summaryrefslogtreecommitdiffstats
path: root/apps
diff options
context:
space:
mode:
Diffstat (limited to 'apps')
-rw-r--r--apps/SOURCES6
-rw-r--r--apps/abrepeat.h33
-rw-r--r--apps/action.c228
-rw-r--r--apps/action.h19
-rw-r--r--apps/alarm_menu.c161
-rw-r--r--apps/apps.make2
-rw-r--r--apps/bookmark.c1264
-rw-r--r--apps/bookmark.h8
-rw-r--r--apps/buffering.c121
-rw-r--r--apps/buffering.h7
-rw-r--r--apps/codec_thread.c12
-rw-r--r--apps/codecs.c5
-rw-r--r--apps/core_keymap.c95
-rw-r--r--apps/core_keymap.h65
-rw-r--r--apps/cuesheet.c194
-rw-r--r--apps/debug_menu.c367
-rw-r--r--apps/enc_config.c12
-rw-r--r--apps/features.txt31
-rw-r--r--apps/filetree.c408
-rw-r--r--apps/filetree.h5
-rw-r--r--apps/filetypes.c400
-rw-r--r--apps/filetypes.h9
-rw-r--r--apps/gui/bitmap/list-skinned.c6
-rw-r--r--apps/gui/bitmap/list.c320
-rw-r--r--apps/gui/color_picker.c26
-rw-r--r--apps/gui/folder_select.c430
-rw-r--r--apps/gui/icon.c80
-rw-r--r--apps/gui/line.c3
-rw-r--r--apps/gui/list.c215
-rw-r--r--apps/gui/list.h97
-rw-r--r--apps/gui/mask_select.c62
-rw-r--r--apps/gui/option_select.c209
-rw-r--r--apps/gui/option_select.h13
-rw-r--r--apps/gui/pitchscreen.c1047
-rw-r--r--apps/gui/quickscreen.c160
-rw-r--r--apps/gui/quickscreen.h10
-rw-r--r--apps/gui/skin_engine/skin_backdrops.c42
-rw-r--r--apps/gui/skin_engine/skin_display.c244
-rw-r--r--apps/gui/skin_engine/skin_display.h9
-rw-r--r--apps/gui/skin_engine/skin_engine.c97
-rw-r--r--apps/gui/skin_engine/skin_engine.h26
-rw-r--r--apps/gui/skin_engine/skin_parser.c490
-rw-r--r--apps/gui/skin_engine/skin_render.c44
-rw-r--r--apps/gui/skin_engine/skin_tokens.c75
-rw-r--r--apps/gui/skin_engine/skin_touchsupport.c50
-rw-r--r--apps/gui/skin_engine/wps_internals.h57
-rw-r--r--apps/gui/splash.c176
-rw-r--r--apps/gui/splash.h11
-rw-r--r--apps/gui/statusbar-skinned.c26
-rw-r--r--apps/gui/statusbar-skinned.h7
-rw-r--r--apps/gui/statusbar.c12
-rw-r--r--apps/gui/statusbar.h3
-rw-r--r--apps/gui/usb_screen.c18
-rw-r--r--apps/gui/viewport.c42
-rw-r--r--apps/gui/viewport.h1
-rw-r--r--apps/gui/wps.c587
-rw-r--r--apps/gui/wps.h37
-rw-r--r--apps/gui/yesno.c294
-rw-r--r--apps/gui/yesno.h11
-rw-r--r--apps/hosted/android/keyboard.c2
-rw-r--r--apps/hosted/android/notification.c1
-rw-r--r--apps/hosted/android/yesno.c11
-rw-r--r--apps/iap/iap-core.c5
-rw-r--r--apps/iap/iap-lingo2.c11
-rw-r--r--apps/iap/iap-lingo4.c21
-rw-r--r--apps/keymaps/keymap-agptekrocker.c22
-rw-r--r--apps/keymaps/keymap-c200.c1
-rw-r--r--apps/keymaps/keymap-clip.c15
-rw-r--r--apps/keymaps/keymap-creativezv.c2
-rw-r--r--apps/keymaps/keymap-creativezvm.c2
-rw-r--r--apps/keymaps/keymap-e200.c1
-rw-r--r--apps/keymaps/keymap-erosq.c7
-rw-r--r--apps/keymaps/keymap-fiiom3k.c131
-rw-r--r--apps/keymaps/keymap-fiiom3klinux.c3
-rw-r--r--apps/keymaps/keymap-fuze.c1
-rw-r--r--apps/keymaps/keymap-fuzeplus.c6
-rw-r--r--apps/keymaps/keymap-gigabeat-s.c2
-rw-r--r--apps/keymaps/keymap-gigabeat.c2
-rw-r--r--apps/keymaps/keymap-h10.c1
-rw-r--r--apps/keymaps/keymap-h1x0_h3x0.c2
-rw-r--r--apps/keymaps/keymap-hdd1630.c2
-rw-r--r--apps/keymaps/keymap-hdd6330.c2
-rw-r--r--apps/keymaps/keymap-hm60x.c3
-rw-r--r--apps/keymaps/keymap-hm801.c3
-rw-r--r--apps/keymaps/keymap-ihifi.c3
-rw-r--r--apps/keymaps/keymap-ihifi770.c1
-rw-r--r--apps/keymaps/keymap-ihifi800.c1
-rw-r--r--apps/keymaps/keymap-ipod.c71
-rw-r--r--apps/keymaps/keymap-m200.c1
-rw-r--r--apps/keymaps/keymap-m3.c4
-rw-r--r--apps/keymaps/keymap-ma.c5
-rw-r--r--apps/keymaps/keymap-meizu-m6sl.c2
-rw-r--r--apps/keymaps/keymap-mini2440.c2
-rw-r--r--apps/keymaps/keymap-mpio-hd200.c6
-rw-r--r--apps/keymaps/keymap-mpio-hd300.c5
-rw-r--r--apps/keymaps/keymap-mr100.c2
-rw-r--r--apps/keymaps/keymap-mr500.c2
-rw-r--r--apps/keymaps/keymap-nwz.c6
-rw-r--r--apps/keymaps/keymap-nwza860.c5
-rw-r--r--apps/keymaps/keymap-ondavx777.c2
-rw-r--r--apps/keymaps/keymap-rk27xx-generic.c2
-rw-r--r--apps/keymaps/keymap-sa9200.c1
-rw-r--r--apps/keymaps/keymap-sansa-connect.c23
-rw-r--r--apps/keymaps/keymap-shanlingq1.c88
-rw-r--r--apps/keymaps/keymap-touchscreen.c6
-rw-r--r--apps/keymaps/keymap-vibe500.c4
-rw-r--r--apps/keymaps/keymap-x5.c4
-rw-r--r--apps/keymaps/keymap-xduoox20.c3
-rw-r--r--apps/keymaps/keymap-xduoox3.c7
-rw-r--r--apps/keymaps/keymap-xduoox3ii.c3
-rw-r--r--apps/keymaps/keymap-yh8xx_yh9xx.c1
-rw-r--r--apps/keymaps/keymap-ypr0.c2
-rw-r--r--apps/keymaps/keymap-ypr1.c2
-rw-r--r--apps/keymaps/keymap-ypz5.c406
-rw-r--r--apps/keymaps/keymap-zen.c4
-rw-r--r--apps/keymaps/keymap-zenxfi2.c2
-rw-r--r--apps/keymaps/keymap-zenxfi3.c1
-rw-r--r--apps/lang/arabic.lang21
-rw-r--r--apps/lang/basque.lang19
-rw-r--r--apps/lang/bulgarian.lang23
-rw-r--r--apps/lang/catala.lang19
-rw-r--r--apps/lang/chinese-simp.lang923
-rw-r--r--apps/lang/chinese-trad.lang3402
-rw-r--r--apps/lang/czech.lang30
-rw-r--r--apps/lang/dansk.lang33
-rw-r--r--apps/lang/deutsch.lang23
-rw-r--r--apps/lang/english-us.lang1094
-rw-r--r--apps/lang/english.lang1101
-rw-r--r--apps/lang/espanol.lang19
-rw-r--r--apps/lang/finnish.lang19
-rw-r--r--apps/lang/francais.lang23
-rw-r--r--apps/lang/galego.lang19
-rw-r--r--apps/lang/greek.lang23
-rw-r--r--apps/lang/hebrew.lang35
-rw-r--r--apps/lang/hrvatski.lang19
-rw-r--r--apps/lang/italiano.lang1084
-rw-r--r--apps/lang/japanese.lang23
-rw-r--r--apps/lang/korean.lang19
-rw-r--r--apps/lang/latviesu.lang17
-rw-r--r--apps/lang/lietuviu.lang19
-rw-r--r--apps/lang/magyar.lang873
-rw-r--r--apps/lang/nederlands.lang962
-rw-r--r--apps/lang/norsk-nynorsk.lang19
-rw-r--r--apps/lang/norsk.lang23
-rw-r--r--apps/lang/polski.lang1089
-rw-r--r--apps/lang/portugues-brasileiro.lang19
-rw-r--r--apps/lang/portugues.lang21
-rw-r--r--apps/lang/romaneste.lang19
-rw-r--r--apps/lang/russian.lang30
-rw-r--r--apps/lang/slovak.lang1146
-rw-r--r--apps/lang/slovenscina.lang19
-rw-r--r--apps/lang/srpski.lang1510
-rw-r--r--apps/lang/svenska.lang21
-rw-r--r--apps/lang/tagalog.lang19
-rw-r--r--apps/lang/thai.lang19
-rw-r--r--apps/lang/turkce.lang6762
-rw-r--r--apps/lang/ukrainian.lang19
-rw-r--r--apps/lang/vlaams.lang25
-rw-r--r--apps/lang/walon.lang19
-rw-r--r--apps/language.c17
-rw-r--r--apps/language.h2
-rw-r--r--apps/logfdisp.c15
-rw-r--r--apps/main.c120
-rw-r--r--apps/menu.c251
-rw-r--r--apps/menu.h53
-rw-r--r--apps/menus/audiohw_eq_menu.c156
-rw-r--r--apps/menus/display_menu.c53
-rw-r--r--apps/menus/eq_menu.c91
-rw-r--r--apps/menus/eq_menu.h3
-rw-r--r--apps/menus/main_menu.c82
-rw-r--r--apps/menus/playback_menu.c126
-rw-r--r--apps/menus/playlist_menu.c62
-rw-r--r--apps/menus/plugin_menu.c63
-rw-r--r--apps/menus/radio_menu.c33
-rw-r--r--apps/menus/recording_menu.c90
-rw-r--r--apps/menus/settings_menu.c91
-rw-r--r--apps/menus/sound_menu.c19
-rw-r--r--apps/menus/theme_menu.c82
-rw-r--r--apps/menus/time_menu.c22
-rw-r--r--apps/misc.c545
-rw-r--r--apps/misc.h49
-rw-r--r--apps/onplay.c1075
-rw-r--r--apps/onplay.h37
-rw-r--r--apps/open_plugin.c475
-rw-r--r--apps/open_plugin.h30
-rw-r--r--apps/pcmbuf.c3
-rw-r--r--apps/playback.c492
-rw-r--r--apps/playback.h5
-rw-r--r--apps/playlist.c5025
-rw-r--r--apps/playlist.h91
-rw-r--r--apps/playlist_catalog.c329
-rw-r--r--apps/playlist_catalog.h15
-rw-r--r--apps/playlist_viewer.c460
-rw-r--r--apps/playlist_viewer.h3
-rw-r--r--apps/plugin.c167
-rw-r--r--apps/plugin.h140
-rw-r--r--apps/plugins/2048.c25
-rw-r--r--apps/plugins/CATEGORIES12
-rw-r--r--apps/plugins/SOURCES24
-rw-r--r--apps/plugins/SOURCES.app_build1
-rw-r--r--apps/plugins/SUBDIRS4
-rw-r--r--apps/plugins/SUBDIRS.app_build1
-rw-r--r--apps/plugins/alarmclock.c12
-rw-r--r--apps/plugins/announce_status.c201
-rw-r--r--apps/plugins/battery_bench.c121
-rw-r--r--apps/plugins/bitmaps/mono/SOURCES3
-rw-r--r--apps/plugins/bitmaps/native/SOURCES68
-rw-r--r--apps/plugins/bitmaps/native/_2048_tiles.26x26x2.bmpbin0 -> 6254 bytes
-rw-r--r--apps/plugins/bitmaps/native/bubbles_background.360x400x16.bmpbin0 -> 432054 bytes
-rw-r--r--apps/plugins/bitmaps/native/bubbles_emblem.360x400x16.bmpbin0 -> 12022 bytes
-rw-r--r--apps/plugins/bitmaps/native/creditslogo.112x30x1.bmpbin0 -> 542 bytes
-rw-r--r--apps/plugins/bitmaps/native/creditslogo.128x40x16.bmpbin0 -> 15414 bytes
-rw-r--r--apps/plugins/bitmaps/native/creditslogo.128x42x1.bmpbin0 -> 736 bytes
-rw-r--r--apps/plugins/bitmaps/native/creditslogo.128x42x2.bmpbin0 -> 2808 bytes
-rw-r--r--apps/plugins/bitmaps/native/creditslogo.132x40x16.bmpbin0 -> 15894 bytes
-rw-r--r--apps/plugins/bitmaps/native/creditslogo.138x46x2.bmpbin0 -> 3392 bytes
-rw-r--r--apps/plugins/bitmaps/native/creditslogo.160x50x16.bmpbin0 -> 24054 bytes
-rw-r--r--apps/plugins/bitmaps/native/creditslogo.160x53x1.bmpbin0 -> 1122 bytes
-rw-r--r--apps/plugins/bitmaps/native/creditslogo.160x53x2.bmpbin0 -> 4312 bytes
-rw-r--r--apps/plugins/bitmaps/native/creditslogo.176x54x16.bmpbin0 -> 28566 bytes
-rw-r--r--apps/plugins/bitmaps/native/creditslogo.220x68x16.bmpbin0 -> 44934 bytes
-rw-r--r--apps/plugins/bitmaps/native/creditslogo.240x74x16.bmpbin0 -> 53334 bytes
-rw-r--r--apps/plugins/bitmaps/native/creditslogo.320x98x16.bmpbin0 -> 94136 bytes
-rw-r--r--apps/plugins/bitmaps/native/creditslogo.480x149x16.bmpbin0 -> 214614 bytes
-rw-r--r--apps/plugins/bitmaps/native/creditslogo.640x198x16.bmpbin0 -> 380214 bytes
-rw-r--r--apps/plugins/bitmaps/native/creditslogo.96x30x16.bmpbin0 -> 8694 bytes
-rw-r--r--apps/plugins/bitmaps/native/invadrox_background.360x400x16.bmpbin0 -> 144634 bytes
-rw-r--r--apps/plugins/bitmaps/native/jewels.360x400x16.bmpbin0 -> 140814 bytes
-rw-r--r--apps/plugins/bitmaps/native/rockblox_background.360x400x16.bmpbin0 -> 432054 bytes
-rw-r--r--apps/plugins/bitmaps/native/sliding_puzzle.360x360x16.bmpbin0 -> 388854 bytes
-rw-r--r--apps/plugins/bitmaps/native/snake2_bottom.360x400x16.bmpbin0 -> 10854 bytes
-rw-r--r--apps/plugins/bitmaps/native/snake2_header1.360x400x16.bmpbin0 -> 41094 bytes
-rw-r--r--apps/plugins/bitmaps/native/snake2_header2.360x400x16.bmpbin0 -> 41094 bytes
-rw-r--r--apps/plugins/bitmaps/native/snake2_left.360x400x16.bmpbin0 -> 6326 bytes
-rw-r--r--apps/plugins/bitmaps/native/snake2_right.360x400x16.bmpbin0 -> 6326 bytes
-rw-r--r--apps/plugins/bitmaps/remote_native/SOURCES7
-rw-r--r--apps/plugins/bitmaps/remote_native/remote_creditslogo.128x42x1.bmpbin0 -> 736 bytes
-rw-r--r--apps/plugins/bitmaps/remote_native/remote_creditslogo.128x42x2.bmpbin0 -> 2808 bytes
-rw-r--r--apps/plugins/blackjack.c20
-rw-r--r--apps/plugins/bounce.c23
-rw-r--r--apps/plugins/brickmania.c28
-rw-r--r--apps/plugins/bubbles.c25
-rw-r--r--apps/plugins/calculator.c9
-rw-r--r--apps/plugins/calendar.c18
-rw-r--r--apps/plugins/chessbox/chessbox.c4
-rw-r--r--apps/plugins/chessbox/chessbox_pgn.c4
-rw-r--r--apps/plugins/chessbox/chessbox_pgn.h5
-rw-r--r--apps/plugins/chessclock.c3
-rw-r--r--apps/plugins/chip8.c3
-rw-r--r--apps/plugins/chopper.c35
-rw-r--r--apps/plugins/clix.c11
-rw-r--r--apps/plugins/clock/clock.c10
-rw-r--r--apps/plugins/clock/clock_menu.c24
-rw-r--r--apps/plugins/codebuster.c7
-rw-r--r--apps/plugins/credits.c101
-rw-r--r--apps/plugins/cube.c15
-rw-r--r--apps/plugins/dart_scorer.c601
-rw-r--r--apps/plugins/db_folder_select.c652
-rw-r--r--apps/plugins/demystify.c32
-rw-r--r--apps/plugins/dice.c9
-rw-r--r--apps/plugins/disktidy.c2
-rw-r--r--apps/plugins/doom/d_deh.c3
-rw-r--r--apps/plugins/doom/i_video.c7
-rw-r--r--apps/plugins/doom/p_ceilng.c3
-rw-r--r--apps/plugins/doom/p_floor.c1
-rw-r--r--apps/plugins/doom/rockdoom.c14
-rw-r--r--apps/plugins/fft/fft.c42
-rw-r--r--apps/plugins/fire.c15
-rw-r--r--apps/plugins/fireworks.c29
-rw-r--r--apps/plugins/flipit.c7
-rw-r--r--apps/plugins/fractals/fractal.h5
-rw-r--r--apps/plugins/goban/goban.c1
-rw-r--r--apps/plugins/imageviewer/gif/gif.c16
-rw-r--r--apps/plugins/imageviewer/gif/gif_decoder.c45
-rw-r--r--apps/plugins/imageviewer/gif/gif_decoder.h1
-rw-r--r--apps/plugins/imageviewer/image_decoder.c5
-rw-r--r--apps/plugins/imageviewer/imageviewer.c80
-rw-r--r--apps/plugins/imageviewer/imageviewer.h14
-rw-r--r--apps/plugins/imageviewer/imageviewer_button.h11
-rw-r--r--apps/plugins/imageviewer/jpeg/jpeg_decoder.c11
-rw-r--r--apps/plugins/imageviewer/jpeg/yuv2rgb.c2
-rw-r--r--apps/plugins/imageviewer/ppm/ppm_decoder.c2
-rw-r--r--apps/plugins/invadrox.c32
-rw-r--r--apps/plugins/jackpot.c9
-rw-r--r--apps/plugins/jewels.c9
-rw-r--r--apps/plugins/keybox.c5
-rw-r--r--apps/plugins/keyremap.c2228
-rw-r--r--apps/plugins/lamp.c22
-rw-r--r--apps/plugins/lastfm_scrobbler.c938
-rw-r--r--apps/plugins/lastfm_scrobbler_viewer.c1033
-rw-r--r--apps/plugins/lib/SOURCES11
-rw-r--r--apps/plugins/lib/action_helper.c1
-rw-r--r--apps/plugins/lib/action_helper.h34
-rwxr-xr-xapps/plugins/lib/action_helper.pl211
-rw-r--r--apps/plugins/lib/arg_helper.c312
-rw-r--r--apps/plugins/lib/arg_helper.h60
-rw-r--r--apps/plugins/lib/bmp_smooth_scale.c2
-rw-r--r--apps/plugins/lib/button_helper.c1
-rw-r--r--apps/plugins/lib/button_helper.h40
-rwxr-xr-xapps/plugins/lib/button_helper.pl103
-rw-r--r--apps/plugins/lib/helper.c54
-rw-r--r--apps/plugins/lib/helper.h22
-rw-r--r--apps/plugins/lib/highscore.c9
-rw-r--r--apps/plugins/lib/icon_helper.c48
-rw-r--r--apps/plugins/lib/icon_helper.h26
-rw-r--r--apps/plugins/lib/id3.c39
-rw-r--r--apps/plugins/lib/id3.h (renamed from apps/scrobbler.h)13
-rw-r--r--apps/plugins/lib/kbd_helper.c8
-rw-r--r--apps/plugins/lib/kbd_helper.h2
-rw-r--r--apps/plugins/lib/keymaps.h12
-rw-r--r--apps/plugins/lib/mul_id3.c174
-rw-r--r--apps/plugins/lib/mul_id3.h27
-rw-r--r--apps/plugins/lib/osd.c8
-rw-r--r--apps/plugins/lib/overlay.c5
-rw-r--r--apps/plugins/lib/playback_control.c20
-rw-r--r--apps/plugins/lib/pluginlib_actions.c2
-rw-r--r--apps/plugins/lib/pluginlib_bmp.c2
-rw-r--r--apps/plugins/lib/printcell_helper.c681
-rw-r--r--apps/plugins/lib/printcell_helper.h87
-rw-r--r--apps/plugins/lib/wrappers.h1
-rw-r--r--apps/plugins/lib/xlcd_scroll.c4
-rw-r--r--apps/plugins/logo.c14
-rw-r--r--apps/plugins/lrcplayer.c41
-rw-r--r--apps/plugins/lua/Makefile2
-rw-r--r--apps/plugins/lua/include_lua/playlist.lua2
-rw-r--r--apps/plugins/lua/lauxlib.c6
-rw-r--r--apps/plugins/lua/lcode.c6
-rw-r--r--apps/plugins/lua/lmathlib.c5
-rw-r--r--apps/plugins/lua/lparser.c2
-rwxr-xr-xapps/plugins/lua/rbdefines_helper.pl3
-rw-r--r--apps/plugins/lua/rockaux.c2
-rw-r--r--apps/plugins/lua/rocklib.c27
-rw-r--r--apps/plugins/lua/rocklib_events.c6
-rw-r--r--apps/plugins/lua/rocklib_img.c2
-rw-r--r--apps/plugins/lua/rocklua.c12
-rw-r--r--apps/plugins/lua/strftime.c4
-rw-r--r--apps/plugins/lua/tlsf_helper.c2
-rw-r--r--apps/plugins/lua_scripts/dbgettags.lua15
-rw-r--r--apps/plugins/lua_scripts/random_playlist.lua558
-rw-r--r--apps/plugins/lua_scripts/return2WPS.lua19
-rw-r--r--apps/plugins/main_menu_config.c4
-rw-r--r--apps/plugins/matrix.c9
-rw-r--r--apps/plugins/maze.c20
-rw-r--r--apps/plugins/mazezam.c9
-rw-r--r--apps/plugins/metronome.c8
-rw-r--r--apps/plugins/midi/midiplay.c3
-rw-r--r--apps/plugins/mikmod/mikmod.c25
-rw-r--r--apps/plugins/minesweeper.c3
-rw-r--r--apps/plugins/mosaique.c11
-rw-r--r--apps/plugins/mp3_encoder.c3
-rw-r--r--apps/plugins/mpegplayer/libmpeg2/header.c1
-rw-r--r--apps/plugins/mpegplayer/libmpeg2/idct_arm.S6
-rw-r--r--apps/plugins/mpegplayer/libmpeg2/idct_armv6.S7
-rw-r--r--apps/plugins/mpegplayer/mpeg_misc.c1
-rw-r--r--apps/plugins/mpegplayer/mpeg_misc.h24
-rw-r--r--apps/plugins/mpegplayer/mpeg_settings.c29
-rw-r--r--apps/plugins/mpegplayer/mpegplayer.c8
-rw-r--r--apps/plugins/mpegplayer/video_out_rockbox.c2
-rw-r--r--apps/plugins/multiboot_select.c358
-rw-r--r--apps/plugins/open_plugins.c102
-rw-r--r--apps/plugins/oscilloscope.c19
-rw-r--r--apps/plugins/otp.c17
-rwxr-xr-xapps/plugins/pacbox/pacbox.c18
-rw-r--r--apps/plugins/pacbox/pacbox.h10
-rw-r--r--apps/plugins/pdbox/PDa/src/m_glob.c2
-rw-r--r--apps/plugins/pdbox/PDa/src/m_obj.c3
-rw-r--r--apps/plugins/pdbox/pdbox.c2
-rw-r--r--apps/plugins/pegbox.c7
-rw-r--r--apps/plugins/periodic_table.c1
-rw-r--r--apps/plugins/pictureflow/pictureflow.c1761
-rw-r--r--apps/plugins/pitch_detector.c11
-rw-r--r--apps/plugins/pitch_screen.c1279
-rw-r--r--apps/plugins/plasma.c17
-rw-r--r--apps/plugins/playing_time.c392
-rw-r--r--apps/plugins/plugin.lds11
-rw-r--r--apps/plugins/plugin_crt0.c3
-rw-r--r--apps/plugins/plugins.make31
-rw-r--r--apps/plugins/pong.c12
-rw-r--r--apps/plugins/properties.c372
-rw-r--r--apps/plugins/puzzles/rockbox.c15
-rw-r--r--apps/plugins/random_folder_advance_config.c54
-rw-r--r--apps/plugins/rb_info.c574
-rw-r--r--apps/plugins/resistor.c24
-rw-r--r--apps/plugins/reversi/reversi-gui.c4
-rw-r--r--apps/plugins/reversi/reversi-gui.h3
-rw-r--r--apps/plugins/robotfindskitten.c6
-rw-r--r--apps/plugins/rockblox.c33
-rw-r--r--apps/plugins/rockblox1d.c7
-rw-r--r--apps/plugins/rockboy/menu.c22
-rw-r--r--apps/plugins/rockboy/rockboy.c8
-rw-r--r--apps/plugins/rocklife.c13
-rw-r--r--apps/plugins/rockpaint.c39
-rw-r--r--apps/plugins/sdl/SDL_mixer/timidity/playmidi.c2
-rw-r--r--apps/plugins/sdl/main.c7
-rw-r--r--apps/plugins/sdl/src/audio/rockbox/SDL_rockboxaudio.c38
-rw-r--r--apps/plugins/sdl/src/thread/rockbox/SDL_systhread.c9
-rw-r--r--apps/plugins/shopper.c3
-rw-r--r--apps/plugins/shortcuts/shortcuts_view.c67
-rw-r--r--apps/plugins/sliding_puzzle.c11
-rw-r--r--apps/plugins/snake.c7
-rw-r--r--apps/plugins/snake2.c21
-rw-r--r--apps/plugins/snow.c8
-rw-r--r--apps/plugins/sokoban.c5
-rw-r--r--apps/plugins/solitaire.c10
-rw-r--r--apps/plugins/spacerocks.c22
-rw-r--r--apps/plugins/speedread.c24
-rw-r--r--apps/plugins/star.c8
-rw-r--r--apps/plugins/starfield.c18
-rw-r--r--apps/plugins/stats.c10
-rw-r--r--apps/plugins/stopwatch.lua2
-rw-r--r--apps/plugins/sudoku/sudoku.c4
-rw-r--r--apps/plugins/sudoku/sudoku.h3
-rw-r--r--apps/plugins/superdom.c3
-rw-r--r--apps/plugins/tagcache/SOURCES2
-rw-r--r--apps/plugins/tagcache/tagcache.c1021
-rw-r--r--apps/plugins/tagcache/tagcache.h25
-rw-r--r--apps/plugins/tagcache/tagcache.make30
-rw-r--r--apps/plugins/test_codec.c2
-rw-r--r--apps/plugins/test_disk.c8
-rw-r--r--apps/plugins/test_fps.c8
-rw-r--r--apps/plugins/test_gfx.c8
-rw-r--r--apps/plugins/test_grey.c8
-rw-r--r--apps/plugins/test_kbd.c46
-rw-r--r--apps/plugins/test_sampr.c2
-rw-r--r--apps/plugins/test_touchscreen.c5
-rw-r--r--apps/plugins/test_usb.c137
-rw-r--r--apps/plugins/test_viewports.c6
-rw-r--r--apps/plugins/text_editor.c3
-rw-r--r--apps/plugins/text_viewer/tv_button.h3
-rw-r--r--apps/plugins/text_viewer/tv_menu.c92
-rw-r--r--apps/plugins/theme_remove.c2
-rw-r--r--apps/plugins/vbrfix.c6
-rw-r--r--apps/plugins/viewers.config8
-rw-r--r--apps/plugins/vu_meter.c22
-rw-r--r--apps/plugins/wormlet.c27
-rw-r--r--apps/plugins/xobox.c14
-rw-r--r--apps/plugins/xworld/engine.c2
-rw-r--r--apps/plugins/xworld/sys.c24
-rw-r--r--apps/plugins/zxbox/keymaps.h3
-rw-r--r--apps/plugins/zxbox/spmain.c12
-rw-r--r--apps/radio/presets.c28
-rw-r--r--apps/radio/radio.c5
-rw-r--r--apps/radio/radio_skin.c16
-rw-r--r--apps/radio/radioart.c4
-rw-r--r--apps/rbcodec_helpers.c15
-rw-r--r--apps/recorder/albumart.c108
-rw-r--r--apps/recorder/albumart.h6
-rw-r--r--apps/recorder/icons.c8
-rw-r--r--apps/recorder/icons.h24
-rw-r--r--apps/recorder/jpeg_common.h4
-rw-r--r--apps/recorder/jpeg_idct_arm.S12
-rw-r--r--apps/recorder/jpeg_load.c15
-rw-r--r--apps/recorder/jpeg_load.h7
-rw-r--r--apps/recorder/keyboard.c234
-rw-r--r--apps/recorder/pcm_record.c60
-rw-r--r--apps/recorder/peakmeter.c15
-rw-r--r--apps/recorder/recording.c278
-rw-r--r--apps/recorder/resize.c2
-rw-r--r--apps/root_menu.c352
-rw-r--r--apps/screen_access.c2
-rw-r--r--apps/screen_access.h1
-rw-r--r--apps/screens.c213
-rw-r--r--apps/screens.h15
-rw-r--r--apps/scrobbler.c287
-rw-r--r--apps/settings.c793
-rw-r--r--apps/settings.h71
-rw-r--r--apps/settings_list.c478
-rw-r--r--apps/settings_list.h21
-rw-r--r--apps/shortcuts.c108
-rw-r--r--apps/status.h2
-rw-r--r--apps/tagcache.c2679
-rw-r--r--apps/tagcache.h109
-rw-r--r--apps/tagnavi.config101
-rw-r--r--apps/tagtree.c759
-rw-r--r--apps/tagtree.h9
-rw-r--r--apps/talk.c497
-rw-r--r--apps/talk.h11
-rw-r--r--apps/tree.c447
-rw-r--r--apps/tree.h32
-rw-r--r--apps/usb_keymaps.c1
-rw-r--r--apps/voice_thread.c13
481 files changed, 43894 insertions, 21034 deletions
diff --git a/apps/SOURCES b/apps/SOURCES
index 5c49f0bbbc..444951bbcb 100644
--- a/apps/SOURCES
+++ b/apps/SOURCES
@@ -7,6 +7,7 @@ alarm_menu.c
#endif
abrepeat.c
bookmark.c
+core_keymap.c
debug_menu.c
filetypes.c
language.c
@@ -57,7 +58,6 @@ tree.c
tagtree.c
#endif
filetree.c
-scrobbler.c
#ifdef IPOD_ACCESSORY_PROTOCOL
iap/iap-core.c
iap/iap-lingo0.c
@@ -272,8 +272,6 @@ keymaps/keymap-ma.c
keymaps/keymap-nwz.c
#elif CONFIG_KEYPAD == SONY_NWZA860_PAD
keymaps/keymap-nwza860.c
-#elif CONFIG_KEYPAD == SAMSUNG_YPZ5_PAD
-keymaps/keymap-ypz5.c
#elif CONFIG_KEYPAD == IHIFI_PAD
keymaps/keymap-ihifi.c
#elif CONFIG_KEYPAD == IHIFI_770_PAD
@@ -298,4 +296,6 @@ keymaps/keymap-fiiom3klinux.c
keymaps/keymap-fiiom3k.c
#elif CONFIG_KEYPAD == EROSQ_PAD
keymaps/keymap-erosq.c
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+keymaps/keymap-shanlingq1.c
#endif
diff --git a/apps/abrepeat.h b/apps/abrepeat.h
index f7ee65247c..4d2c4ea001 100644
--- a/apps/abrepeat.h
+++ b/apps/abrepeat.h
@@ -20,9 +20,38 @@
****************************************************************************/
#ifndef _ABREPEAT_H_
#define _ABREPEAT_H_
-
-#ifdef AB_REPEAT_ENABLE
#include <stdbool.h>
+
+#ifndef AB_REPEAT_ENABLE /* Dummy functions */
+static inline bool ab_repeat_mode_enabled(void)
+{
+ return false;
+}
+static inline bool ab_bool_dummy_marker(unsigned int song_position)
+{
+ (void) song_position;
+ return false;
+}
+static inline void ab_void_dummy_marker(unsigned int song_position)
+{
+ (void) song_position;
+}
+static inline void ab_dummy_voidfn(void){}
+
+#define ab_repeat_init ab_dummy_voidfn
+#define ab_before_A_marker ab_bool_dummy_marker
+#define ab_after_A_marker ab_bool_dummy_marker
+#define ab_jump_to_A_marker ab_dummy_voidfn
+#define ab_reset_markers ab_dummy_voidfn
+#define ab_set_A_marker ab_void_dummy_marker
+#define ab_set_B_marker ab_void_dummy_marker
+#define ab_get_A_marker ab_bool_dummy_marker
+#define ab_get_B_marker ab_bool_dummy_marker
+#define ab_end_of_track_report ab_dummy_voidfn
+#define ab_reached_B_marker ab_bool_dummy_marker
+#define ab_position_report ab_void_dummy_marker
+
+#else /*def AB_REPEAT_ENABLE*/
#include "audio.h"
#include "kernel.h" /* needed for HZ */
diff --git a/apps/action.c b/apps/action.c
index 5533c00241..208fea4a97 100644
--- a/apps/action.c
+++ b/apps/action.c
@@ -34,6 +34,7 @@
#include "button.h"
#include "action.h"
#include "kernel.h"
+#include "core_alloc.h"
#include "splash.h"
#include "settings.h"
@@ -69,6 +70,10 @@ static action_last_t action_last =
.tick = 0,
.wait_for_release = false,
+#ifndef DISABLE_ACTION_REMAP
+ .key_remap = 0,
+#endif
+
#ifdef HAVE_TOUCHSCREEN
.ts_data = 0,
.ts_short_press = false,
@@ -120,7 +125,7 @@ static bool is_action_filtered(int action, unsigned int mask, int context)
{
case ACTION_NONE:
break;
-/*Actions that are not mapped will not turn on the backlight option NOUNMAPPED*/
+ /* Actions that are not mapped will not turn on the backlight */
case ACTION_UNKNOWN:
match = has_flag(mask, SEL_ACTION_NOUNMAPPED);
break;
@@ -128,15 +133,15 @@ static bool is_action_filtered(int action, unsigned int mask, int context)
case ACTION_FM_PLAY:
match = has_flag(mask, SEL_ACTION_PLAY);
break;
- //case ACTION_STD_PREVREPEAT: // seek not exempted outside of WPS
- //case ACTION_STD_NEXTREPEAT:
+ /* case ACTION_STD_PREVREPEAT:*/ /* seek not exempted outside of WPS */
+ /* case ACTION_STD_NEXTREPEAT: */
case ACTION_WPS_SEEKBACK:
case ACTION_WPS_SEEKFWD:
case ACTION_WPS_STOPSEEK:
match = has_flag(mask, SEL_ACTION_SEEK);
break;
- //case ACTION_STD_PREV: // skip/scrollwheel not exempted outside of WPS
- //case ACTION_STD_NEXT:
+ /* case ACTION_STD_PREV: */ /* skip/scrollwheel not */
+ /* case ACTION_STD_NEXT: */ /* exempted outside of WPS */
case ACTION_WPS_SKIPNEXT:
case ACTION_WPS_SKIPPREV:
case ACTION_FM_NEXT_PRESET:
@@ -144,8 +149,8 @@ static bool is_action_filtered(int action, unsigned int mask, int context)
match = has_flag(mask, SEL_ACTION_SKIP);
break;
#ifdef HAVE_VOLUME_IN_LIST
- case ACTION_LIST_VOLUP: // volume exempted outside of WPS if the device supports it
- case ACTION_LIST_VOLDOWN:
+ case ACTION_LIST_VOLUP: /* volume exempted outside of WPS */
+ case ACTION_LIST_VOLDOWN: /* ( if the device supports it )*/
#endif
case ACTION_WPS_VOLUP:
case ACTION_WPS_VOLDOWN:
@@ -405,6 +410,7 @@ static inline bool get_action_touchscreen(action_last_t *last, action_cur_t *cur
}
last->button = cur->button;
+ last->tick = current_tick;
cur->action = ACTION_TOUCHSCREEN;
return true;
}
@@ -498,7 +504,7 @@ static inline int action_code_worker(action_last_t *last,
int *end )
{
int ret = ACTION_UNKNOWN;
- int i = 0;
+ int i = *end;
unsigned int found = 0;
while (cur->items[i].button_code != BUTTON_NONE)
{
@@ -582,21 +588,52 @@ static inline int get_next_context(const struct button_mapping *items, int i)
* for a more in-depth explanation
* places action into current_action
*/
+
static inline void action_code_lookup(action_last_t *last, action_cur_t *cur)
{
- int action = ACTION_NONE;
+ int action, i;
int context = cur->context;
- int i = 0;
-
cur->is_prebutton = false;
-#ifdef HAVE_LOCKED_ACTIONS
+#if !defined(HAS_BUTTON_HOLD) && !defined(BOOTLOADER)
/* This only applies to the first context, to allow locked contexts to
* specify a fall through to their non-locked version */
if (is_keys_locked())
context |= CONTEXT_LOCKED;
#endif
+#ifndef DISABLE_ACTION_REMAP
+ /* attempt to look up the button in user supplied remap */
+ if(last->key_remap && (context & CONTEXT_PLUGIN) == 0)
+ {
+ if ((cur->button & BUTTON_REMOTE) != 0)
+ {
+ context |= CONTEXT_REMOTE;
+ }
+ cur->items = core_get_data(last->key_remap);
+ i = 0;
+ action = ACTION_UNKNOWN;
+ /* check the lut at the beginning for the desired context */
+ while (cur->items[i].button_code != BUTTON_NONE)
+ {
+ if (cur->items[i].action_code == CORE_CONTEXT_REMAP(context))
+ {
+ i = cur->items[i].button_code;
+ action = action_code_worker(last, cur, &i);
+ if (action != ACTION_UNKNOWN)
+ {
+ cur->action = action;
+ return;
+ }
+ }
+ i++;
+ }
+ }
+#endif
+
+ i = 0;
+ action = ACTION_NONE;
+ /* attempt to look up the button in the in-built keymaps */
for(;;)
{
/* logf("context = %x",context); */
@@ -608,9 +645,13 @@ static inline void action_code_lookup(action_last_t *last, action_cur_t *cur)
#endif
if ((context & CONTEXT_PLUGIN) && cur->get_context_map)
+ {
cur->items = cur->get_context_map(context);
+ }
else
+ {
cur->items = get_context_mapping(context);
+ }
if (cur->items != NULL)
{
@@ -684,13 +725,25 @@ static inline int do_auto_softlock(action_last_t *last, action_cur_t *cur)
{
do_key_lock(true);
-#if defined(HAVE_TOUCHPAD)
+#if defined(HAVE_TOUCHPAD) || defined(HAVE_TOUCHSCREEN)
/* if the touchpad is supposed to be off and the current buttonpress
* is from the touchpad, nullify both button and action. */
if (!has_flag(action_last.softlock_mask, SEL_ACTION_ENABLED) ||
has_flag(action_last.softlock_mask, SEL_ACTION_NOTOUCH))
{
+#if defined(HAVE_TOUCHPAD)
cur->button = touchpad_filter(cur->button);
+#endif
+#if defined(HAVE_TOUCHSCREEN)
+ const int touch_fakebuttons =
+ BUTTON_TOPLEFT | BUTTON_TOPMIDDLE | BUTTON_TOPRIGHT |
+ BUTTON_LEFT | BUTTON_CENTER | BUTTON_RIGHT |
+ BUTTON_BOTTOMLEFT | BUTTON_BOTTOMMIDDLE | BUTTON_BOTTOMRIGHT;
+ if (has_flag(cur->button, BUTTON_TOUCHSCREEN))
+ cur->button = BUTTON_NONE;
+ else
+ cur->button &= ~touch_fakebuttons;
+#endif
if (cur->button == BUTTON_NONE)
{
action = ACTION_NONE;
@@ -947,7 +1000,8 @@ static inline int do_backlight(action_last_t *last, action_cur_t *cur, int actio
&& power_input_present());
#endif
/* skip if backlight on | incorrect context | SEL_ACTION_NOEXT + ext pwr */
- if ((cur->context == CONTEXT_FM || cur->context == CONTEXT_WPS) && bl_is_off)
+ if (bl_is_off && (cur->context == CONTEXT_FM || cur->context == CONTEXT_WPS ||
+ cur->context == CONTEXT_MAINMENU))
{
filtered = is_action_filtered(action, last->backlight_mask, cur->context);
bl_activate = !is_action_discarded(cur, filtered, &last->bl_filter_tick);
@@ -1008,6 +1062,7 @@ static int get_action_worker(action_last_t *last, action_cur_t *cur)
if (get_action_touchscreen(last, cur))
{
+ do_softlock(last, cur);
return cur->action;
}
@@ -1136,6 +1191,92 @@ int get_action(int context, int timeout)
return action;
}
+int action_set_keymap(struct button_mapping* core_keymap, int count)
+{
+#ifdef DISABLE_ACTION_REMAP
+ (void)core_keymap;
+ (void)count;
+ return -1;
+#else
+ if (count <= 0 || core_keymap == NULL)
+ return action_set_keymap_handle(0, 0);
+
+ size_t keyremap_buf_size = count * sizeof(struct button_mapping);
+ int handle = core_alloc(keyremap_buf_size);
+ if (handle < 0)
+ return -6;
+
+ memcpy(core_get_data(handle), core_keymap, keyremap_buf_size);
+ return action_set_keymap_handle(handle, count);
+#endif
+}
+
+int action_set_keymap_handle(int handle, int count)
+{
+#ifdef DISABLE_ACTION_REMAP
+ (void)core_keymap;
+ (void)count;
+ return -1;
+#else
+ /* free an existing remap */
+ action_last.key_remap = core_free(action_last.key_remap);
+
+ /* if clearing the remap, we're done */
+ if (count <= 0 || handle <= 0)
+ return 0;
+
+ /* validate the keymap */
+ struct button_mapping* core_keymap = core_get_data(handle);
+ struct button_mapping* entry = &core_keymap[count - 1];
+ if (entry->action_code != (int) CONTEXT_STOPSEARCHING ||
+ entry->button_code != BUTTON_NONE) /* check for sentinel at end*/
+ {
+ /* missing sentinel entry */
+ return -1;
+ }
+
+ /* check the lut at the beginning for invalid offsets */
+ for (int i = 0; i < count; ++i)
+ {
+ entry = &core_keymap[i];
+ if (entry->action_code == (int)CONTEXT_STOPSEARCHING)
+ break;
+
+ if ((entry->action_code & CONTEXT_REMAPPED) == CONTEXT_REMAPPED)
+ {
+ int firstbtn = entry->button_code;
+ int endpos = firstbtn + entry->pre_button_code;
+ if (firstbtn > count || firstbtn < i || endpos > count)
+ {
+ /* offset out of bounds */
+ return -2;
+ }
+
+ if (core_keymap[endpos].button_code != BUTTON_NONE)
+ {
+ /* stop sentinel is not at end of action lut */
+ return -3;
+ }
+ }
+ else
+ {
+ /* something other than a context remap in the lut */
+ return -4;
+ }
+
+ if (i+1 >= count)
+ {
+ /* no sentinel in the lut */
+ return -5;
+ }
+ }
+
+ /* success */
+ action_last.key_remap = handle;
+ return count;
+#endif
+}
+
int get_custom_action(int context,int timeout,
const struct button_mapping* (*get_context_map)(int))
{
@@ -1214,41 +1355,44 @@ void set_selective_softlock_actions(bool selective, unsigned int mask)
}
}
-
-void action_autosoftlock_init(void)
+/* look for an action in the given context, return button which triggers it.
+ * (note: pre_button isn't taken into account here) */
+static int find_button_for_action(int context, int action)
{
- action_cur_t cur;
- int i = 0;
+ const struct button_mapping *items;
+ int i;
- if (action_last.unlock_combo == BUTTON_NONE)
+ do
{
- /* search CONTEXT_WPS, should be here */
- cur.items = get_context_mapping(CONTEXT_WPS);
- while (cur.items[i].button_code != BUTTON_NONE)
+ items = get_context_mapping(context);
+ if (items == NULL)
+ break;
+
+ for (i = 0; items[i].button_code != BUTTON_NONE; ++i)
{
- if (cur.items[i].action_code == ACTION_STD_KEYLOCK)
- {
- action_last.unlock_combo = cur.items[i].button_code;
- break;
- }
- i = i + 1;
+ if (items[i].action_code == action)
+ return items[i].button_code;
}
- /* not there... let's try std
- * I doubt any targets will need this, but... */
- if (action_last.unlock_combo == BUTTON_NONE)
+ /* get chained context, if none it will be CONTEXT_STOPSEARCHING */
+ context = items[i].action_code;
+ } while (context != (int)CONTEXT_STOPSEARCHING);
+
+ return BUTTON_NONE;
+}
+
+void action_autosoftlock_init(void)
+{
+ /* search in WPS and STD contexts for the keylock button combo */
+ static const int contexts[2] = { CONTEXT_WPS, CONTEXT_STD };
+
+ for (int i = 0; i < 2; ++i)
+ {
+ int button = find_button_for_action(contexts[i], ACTION_STD_KEYLOCK);
+ if (button != BUTTON_NONE)
{
- i = 0;
- cur.items = get_context_mapping(CONTEXT_STD);
- while (cur.items[i].button_code != BUTTON_NONE)
- {
- if (cur.items[i].action_code == ACTION_STD_KEYLOCK)
- {
- action_last.unlock_combo = cur.items[i].button_code;
- break;
- }
- i = i + 1;
- }
+ action_last.unlock_combo = button;
+ break;
}
}
diff --git a/apps/action.h b/apps/action.h
index ad91f31535..6e1278b33c 100644
--- a/apps/action.h
+++ b/apps/action.h
@@ -28,13 +28,15 @@
#define TIMEOUT_NOBLOCK 0
#define CONTEXT_STOPSEARCHING 0xFFFFFFFF
+
#define CONTEXT_REMOTE 0x80000000 /* | this against another context to get remote buttons for that context */
#define CONTEXT_CUSTOM 0x40000000 /* | this against anything to get your context number */
#define CONTEXT_CUSTOM2 0x20000000 /* as above */
#define CONTEXT_PLUGIN 0x10000000 /* for plugins using get_custom_action */
-#ifdef HAVE_LOCKED_ACTIONS
+#define CONTEXT_REMAPPED 0x08000000 /* marker for key remap context table */
+#define CORE_CONTEXT_REMAP(context) (CONTEXT_REMAPPED | context)
#define CONTEXT_LOCKED 0x04000000 /* flag to use alternate keymap when screen is locked */
-#endif
+
#define LAST_ITEM_IN_LIST { CONTEXT_STOPSEARCHING, BUTTON_NONE, BUTTON_NONE }
#define LAST_ITEM_IN_LIST__NEXTLIST(a) { a, BUTTON_NONE, BUTTON_NONE }
@@ -92,7 +94,7 @@ void set_selective_backlight_actions(bool selective, unsigned int mask,
enum {
CONTEXT_STD = 0,
/* These CONTEXT_ values were here before me,
- there values may have significance, so dont touch! */
+ their values may have significance, so dont touch! */
CONTEXT_WPS = 1,
CONTEXT_TREE = 2,
CONTEXT_RECORD = 3,
@@ -129,6 +131,7 @@ enum {
CONTEXT_USB_HID_MODE_PRESENTATION,
CONTEXT_USB_HID_MODE_BROWSER,
CONTEXT_USB_HID_MODE_MOUSE,
+ LAST_CONTEXT_PLACEHOLDER,
};
@@ -244,7 +247,6 @@ enum {
ACTION_SETTINGS_DEC,
ACTION_SETTINGS_DECREPEAT,
ACTION_SETTINGS_DECBIGSTEP,
- ACTION_SETTINGS_RESET,
ACTION_SETTINGS_SET, /* Used by touchscreen targets */
/* bookmark screen */
@@ -415,6 +417,10 @@ typedef struct
bool repeated;
bool wait_for_release;
+#ifndef DISABLE_ACTION_REMAP
+ int key_remap;
+#endif
+
#ifdef HAVE_TOUCHSCREEN
bool ts_short_press;
int ts_data;
@@ -441,6 +447,11 @@ bool action_userabort(int timeout);
/* no other code should need this apart from action.c */
const struct button_mapping* get_context_mapping(int context);
+/* load a key map to allow buttons for actions to be remapped see: core_keymap */
+int action_set_keymap(struct button_mapping* core_keymap, int count);
+/* load keymap in a handle: takes ownership of the handle on success */
+int action_set_keymap_handle(int handle, int count);
+
/* returns the status code variable from action.c for the button just pressed
If button != NULL it will be set to the actual button code */
#define ACTION_REMOTE 0x1 /* remote was pressed */
diff --git a/apps/alarm_menu.c b/apps/alarm_menu.c
index 62b54a84bb..67f8d1e8dd 100644
--- a/apps/alarm_menu.c
+++ b/apps/alarm_menu.c
@@ -38,161 +38,56 @@
#include "splash.h"
#include "viewport.h"
-static void speak_time(int hours, int minutes, bool speak_hours, bool enqueue)
-{
- if (global_settings.talk_menu){
- if(speak_hours) {
- talk_value(hours, UNIT_HOUR, enqueue);
- talk_value(minutes, UNIT_MIN, true);
- } else {
- talk_value(minutes, UNIT_MIN, enqueue);
- }
- }
-}
-
int alarm_screen(void)
{
- int h, m;
- bool done = false;
- struct tm *tm;
- int togo;
- int button;
- bool update = true;
- bool hour_wrapped = false;
- struct viewport vp[NB_SCREENS];
- struct viewport * last_vp;
-
- rtc_get_alarm(&h, &m);
+ bool usb, update;
+ struct tm *now = get_time();
+ struct tm atm;
+ memcpy(&atm, now, sizeof(struct tm));
+ rtc_get_alarm(&atm.tm_hour, &atm.tm_min);
/* After a battery change the RTC values are out of range */
- if (m > 60 || h > 24) {
- m = 0;
- h = 12;
- } else {
- m = m / 5 * 5; /* 5 min accuracy should be enough */
- }
- FOR_NB_SCREENS(i)
- {
- viewport_set_defaults(&vp[i], i);
- }
+ if (!valid_time(&atm))
+ memcpy(&atm, now, sizeof(struct tm));
+ atm.tm_sec = 0;
- while(!done) {
- if(update)
- {
- FOR_NB_SCREENS(i)
- {
- screens[i].set_viewport(&vp[i]);
- screens[i].clear_viewport();
- screens[i].puts(0, 4, str(LANG_ALARM_MOD_KEYS));
- }
- /* Talk when entering the wakeup screen */
- speak_time(h, m, true, true);
- update = false;
- }
+ usb = set_time_screen(str(LANG_ALARM_MOD_TIME), &atm, false);
+ update = valid_time(&atm); /* set_time returns invalid time if canceled */
- FOR_NB_SCREENS(i)
- {
- last_vp = screens[i].set_viewport(&vp[i]);
- screens[i].putsf(0, 1, str(LANG_ALARM_MOD_TIME));
- screens[i].putsf(0, 2, "%02d:%02d", h, m);
- screens[i].update_viewport();
- screens[i].set_viewport(last_vp);
- }
- button = get_action(CONTEXT_SETTINGS,HZ);
+ if (!usb && update)
+ {
- switch(button) {
- case ACTION_STD_OK:
+ now = get_time();
+ int nmins = now->tm_min + (now->tm_hour * 60);
+ int amins = atm.tm_min + (atm.tm_hour * 60);
+ int mins_togo = (amins - nmins + 1440) % 1440;
/* prevent that an alarm occurs in the shutdown procedure */
/* accept alarms only if they are in 2 minutes or more */
- tm = get_time();
- togo = (m + h * 60 - tm->tm_min - tm->tm_hour * 60 + 1440) % 1440;
-
- if (togo > 1) {
+ if (mins_togo > 1) {
rtc_init();
- rtc_set_alarm(h,m);
+ rtc_set_alarm(atm.tm_hour,atm.tm_min);
rtc_enable_alarm(true);
if (global_settings.talk_menu)
{
talk_id(LANG_ALARM_MOD_TIME_TO_GO, true);
- talk_value(togo / 60, UNIT_HOUR, true);
- talk_value(togo % 60, UNIT_MIN, true);
+ talk_value(mins_togo / 60, UNIT_HOUR, true);
+ talk_value(mins_togo % 60, UNIT_MIN, true);
talk_force_enqueue_next();
}
splashf(HZ*2, str(LANG_ALARM_MOD_TIME_TO_GO),
- togo / 60, togo % 60);
- done = true;
+ mins_togo / 60, mins_togo % 60);
} else {
splash(HZ, ID2P(LANG_ALARM_MOD_ERROR));
- update = true;
- }
- break;
-
- /* inc(m) */
- case ACTION_SETTINGS_INC:
- case ACTION_SETTINGS_INCREPEAT:
- m += 5;
- if (m == 60) {
- h += 1;
- m = 0;
- hour_wrapped = true;
+ update = false;
}
- if (h == 24)
- h = 0;
-
- speak_time(h, m, hour_wrapped, false);
- break;
-
- /* dec(m) */
- case ACTION_SETTINGS_DEC:
- case ACTION_SETTINGS_DECREPEAT:
- m -= 5;
- if (m == -5) {
- h -= 1;
- m = 55;
- hour_wrapped = true;
- }
- if (h == -1)
- h = 23;
-
- speak_time(h, m, hour_wrapped, false);
- break;
-
- /* inc(h) */
- case ACTION_STD_NEXT:
- case ACTION_STD_NEXTREPEAT:
- h = (h+1) % 24;
-
- if (global_settings.talk_menu)
- talk_value(h, UNIT_HOUR, false);
- break;
-
- /* dec(h) */
- case ACTION_STD_PREV:
- case ACTION_STD_PREVREPEAT:
- h = (h+23) % 24;
-
- if (global_settings.talk_menu)
- talk_value(h, UNIT_HOUR, false);
- break;
+ }
- case ACTION_STD_CANCEL:
- rtc_enable_alarm(false);
+ if (usb || !update)
+ {
+ if (!usb)
splash(HZ*2, ID2P(LANG_ALARM_MOD_DISABLE));
- done = true;
- break;
-
- case ACTION_NONE:
- hour_wrapped = false;
- break;
-
- default:
- if(default_event_handler(button) == SYS_USB_CONNECTED)
- {
- rtc_enable_alarm(false);
- return 1;
- }
- break;
- }
+ rtc_enable_alarm(false);
+ return 1;
}
return 0;
}
diff --git a/apps/apps.make b/apps/apps.make
index 6afcd12b5c..47b015bc92 100644
--- a/apps/apps.make
+++ b/apps/apps.make
@@ -24,7 +24,7 @@ $(BUILDDIR)/apps/features: $(APPSDIR)/features.txt $(BUILDDIR)/firmware/common/
$(call PRINTS,PP $(<F))
$(SILENT)$(CC) $(PPCFLAGS) \
-E -P -imacros "config.h" -imacros "button.h" -x c $< | \
- grep -v "^\#" | grep -v "^ *$$" > $(BUILDDIR)/apps/features; \
+ grep -v "^#" | grep -v "^ *$$" > $(BUILDDIR)/apps/features; \
$(BUILDDIR)/apps/genlang-features: $(BUILDDIR)/apps/features
$(call PRINTS,GEN $(subst $(BUILDDIR)/,,$@))tr \\n : < $< > $@
diff --git a/apps/bookmark.c b/apps/bookmark.c
index 07751c2d4c..2411ddb0ee 100644
--- a/apps/bookmark.c
+++ b/apps/bookmark.c
@@ -43,6 +43,9 @@
#include "file.h"
#include "pathfuncs.h"
+/*#define LOGF_ENABLE*/
+#include "logf.h"
+
#define MAX_BOOKMARKS 10
#define MAX_BOOKMARK_SIZE 350
#define RECENT_BOOKMARK_FILE ROCKBOX_DIR "/most-recent.bmark"
@@ -66,286 +69,289 @@ struct bookmark_list
#define BM_SPEED 0x02
/* bookmark values */
-static struct {
+struct resume_info{
+ const struct mp3entry *id3;
int resume_index;
unsigned long resume_offset;
int resume_seed;
- long resume_time;
+ long resume_elapsed;
int repeat_mode;
bool shuffle;
/* optional values */
int pitch;
- int speed;
-} bm;
-
-static bool add_bookmark(const char* bookmark_file_name, const char* bookmark,
- bool most_recent);
-static char* create_bookmark(void);
-static bool delete_bookmark(const char* bookmark_file_name, int bookmark_id);
-static void say_bookmark(const char* bookmark,
- int bookmark_id, bool show_playlist_name);
-static bool play_bookmark(const char* bookmark);
-static bool generate_bookmark_file_name(const char *in);
-static bool parse_bookmark(const char *bookmark, const bool get_filenames, const bool strip_dir);
-static int buffer_bookmarks(struct bookmark_list* bookmarks, int first_line);
-static const char* get_bookmark_info(int list_index,
- void* data,
- char *buffer,
- size_t buffer_len);
-static int select_bookmark(const char* bookmark_file_name, bool show_dont_resume, char** selected_bookmark);
-static bool write_bookmark(bool create_bookmark_file, const char *bookmark);
-static int get_bookmark_count(const char* bookmark_file_name);
+ int speed;
+};
-#define TEMP_BUF_SIZE (MAX_PATH + 1)
+/* Temp buffer used for reading, create_bookmark and filename creation */
+#define TEMP_BUF_SIZE (MAX(MAX_BOOKMARK_SIZE, MAX_PATH + 1))
static char global_temp_buffer[TEMP_BUF_SIZE];
-/* File name created by generate_bookmark_file_name */
-static char global_bookmark_file_name[MAX_PATH];
-static char global_read_buffer[MAX_BOOKMARK_SIZE];
-/* Bookmark created by create_bookmark*/
-static char global_bookmark[MAX_BOOKMARK_SIZE];
-/* Filename from parsed bookmark (can be made local where needed) */
-static char global_filename[MAX_PATH];
-/* ----------------------------------------------------------------------- */
-/* This is an interface function from the context menu. */
-/* Returns true on successful bookmark creation. */
-/* ----------------------------------------------------------------------- */
-bool bookmark_create_menu(void)
+static inline void get_hash(const char *key, uint32_t *hash, int len)
{
- return write_bookmark(true, create_bookmark());
+ *hash = crc_32(key, len, *hash); /* this is probably sufficient */
}
-/* ----------------------------------------------------------------------- */
-/* This function acts as the load interface from the context menu. */
-/* This function determines the bookmark file name and then loads that file*/
-/* for the user. The user can then select or delete previous bookmarks. */
-/* This function returns BOOKMARK_SUCCESS on the selection of a track to */
-/* resume, BOOKMARK_FAIL if the menu is exited without a selection and */
-/* BOOKMARK_USB_CONNECTED if the menu is forced to exit due to a USB */
-/* connection. */
-/* ----------------------------------------------------------------------- */
-int bookmark_load_menu(void)
+static const char* skip_tokens(const char* s, int ntokens)
{
- char* bookmark;
- int ret = BOOKMARK_FAIL;
-
- push_current_activity(ACTIVITY_BOOKMARKSLIST);
-
- char* name = playlist_get_name(NULL, global_temp_buffer,
- sizeof(global_temp_buffer));
- if (generate_bookmark_file_name(name))
+ for (int i = 0; i < ntokens; i++)
{
- ret = select_bookmark(global_bookmark_file_name, false, &bookmark);
- if (bookmark != NULL)
+ while (*s && *s != ';')
{
- ret = play_bookmark(bookmark) ? BOOKMARK_SUCCESS : BOOKMARK_FAIL;
+ s++;
+ }
+
+ if (*s)
+ {
+ s++;
}
}
+ return s;
+}
- pop_current_activity();
+static int int_token(const char **s)
+{
+ int ret = atoi(*s);
+ *s = skip_tokens(*s, 1);
return ret;
}
-/* ----------------------------------------------------------------------- */
-/* Gives the user a list of the Most Recent Bookmarks. This is an */
-/* interface function */
-/* Returns true on the successful selection of a recent bookmark. */
-/* ----------------------------------------------------------------------- */
-bool bookmark_mrb_load()
+static long long_token(const char **s)
{
- char* bookmark;
- bool ret = false;
+ /* Should be atol, but we don't have it. */
+ return int_token(s);
+}
- push_current_activity(ACTIVITY_BOOKMARKSLIST);
- select_bookmark(RECENT_BOOKMARK_FILE, false, &bookmark);
- if (bookmark != NULL)
+/*-------------------------------------------------------------------------*/
+/* Get the name of the playlist and the name of the track from a bookmark. */
+/* Returns true iff both were extracted. */
+/*-------------------------------------------------------------------------*/
+static bool bookmark_get_playlist_and_track_hash(const char *bookmark,
+ uint32_t *pl_hash,
+ uint32_t *track_hash)
+{
+ *pl_hash = 0;
+ *track_hash = 0;
+ int pl_len;
+ const char *pl_start, *pl_end, *track;
+
+ logf("%s", __func__);
+
+ pl_start = strchr(bookmark,'/');
+ if (!(pl_start))
+ return false;
+
+ pl_end = skip_tokens(pl_start, 1) - 1;
+ pl_len = pl_end - pl_start;
+
+ track = pl_end + 1;
+ get_hash(pl_start, pl_hash, pl_len);
+
+ if (global_settings.usemrb == BOOKMARK_ONE_PER_TRACK)
{
- ret = play_bookmark(bookmark);
+ get_hash(track, track_hash, strlen(track));
}
- pop_current_activity();
- return ret;
+
+ return true;
}
/* ----------------------------------------------------------------------- */
-/* This function handles an autobookmark creation. This is an interface */
-/* function. */
-/* Returns true on successful bookmark creation. */
+/* This function takes a bookmark and parses it. This function also */
+/* validates the bookmark. Valid filenamebuf indicates whether */
+/* the filename tokens are to be extracted. */
+/* Returns true on successful bookmark parse. */
/* ----------------------------------------------------------------------- */
-bool bookmark_autobookmark(bool prompt_ok)
+static bool parse_bookmark(char *filenamebuf,
+ size_t filenamebufsz,
+ const char *bookmark,
+ struct resume_info *resume_info,
+ const bool strip_dir)
{
- char* bookmark;
- bool update;
-
- if (!bookmark_is_bookmarkable_state())
- return false;
+ const char* s = bookmark;
+ const char* end;
- audio_pause(); /* first pause playback */
- update = (global_settings.autoupdatebookmark && bookmark_exists());
- bookmark = create_bookmark();
+#define GET_INT_TOKEN(var) var = int_token(&s)
+#define GET_LONG_TOKEN(var) var = long_token(&s)
+#define GET_BOOL_TOKEN(var) var = (int_token(&s) != 0)
- if (update)
- return write_bookmark(true, bookmark);
+ /* if new format bookmark, extract the optional content flags,
+ otherwise treat as an original format bookmark */
+ int opt_flags = 0;
+ int opt_pitch = 0;
+ int opt_speed = 0;
+ int old_format = ((strchr(s, '>') == s) ? 0 : 1);
+ if (old_format == 0) /* this is a new format bookmark */
+ {
+ s++;
+ GET_INT_TOKEN(opt_flags);
+ opt_pitch = (opt_flags & BM_PITCH) ? 1:0;
+ opt_speed = (opt_flags & BM_SPEED) ? 1:0;
+ }
- switch (global_settings.autocreatebookmark)
+ /* extract all original bookmark tokens */
+ if (resume_info)
{
- case BOOKMARK_YES:
- return write_bookmark(true, bookmark);
+ GET_INT_TOKEN(resume_info->resume_index);
+ GET_LONG_TOKEN(resume_info->resume_offset);
+ GET_INT_TOKEN(resume_info->resume_seed);
- case BOOKMARK_NO:
- return false;
+ s = skip_tokens(s, old_format); /* skip deprecated token */
- case BOOKMARK_RECENT_ONLY_YES:
- return write_bookmark(false, bookmark);
- }
- const char *lines[]={ID2P(LANG_AUTO_BOOKMARK_QUERY)};
- const struct text_message message={lines, 1};
+ GET_LONG_TOKEN(resume_info->resume_elapsed);
+ GET_INT_TOKEN(resume_info->repeat_mode);
+ GET_BOOL_TOKEN(resume_info->shuffle);
- if(prompt_ok && gui_syncyesno_run(&message, NULL, NULL)==YESNO_YES)
+ /* extract all optional bookmark tokens */
+ if (opt_pitch != 0)
+ GET_INT_TOKEN(resume_info->pitch);
+ if (opt_speed != 0)
+ GET_INT_TOKEN(resume_info->speed);
+ }
+ else /* no resume info we just want the file name strings */
{
- if (global_settings.autocreatebookmark == BOOKMARK_RECENT_ONLY_ASK)
- return write_bookmark(false, bookmark);
- else
- return write_bookmark(true, bookmark);
+ #define DEFAULT_BM_TOKENS 6
+ int skipct = DEFAULT_BM_TOKENS + old_format + opt_pitch + opt_speed;
+ s = skip_tokens(s, skipct);
+ #undef DEFAULT_BM_TOKENS
}
- return false;
-}
-/* ----------------------------------------------------------------------- */
-/* This function takes the current current resume information and writes */
-/* that to the beginning of the bookmark file. */
-/* This file will contain N number of bookmarks in the following format: */
-/* resume_index*resume_offset*resume_seed*resume_first_index* */
-/* resume_file*milliseconds*MP3 Title* */
-/* Returns true on successful bookmark write. */
-/* Returns false if any part of the bookmarking process fails. It is */
-/* possible that a bookmark is successfully added to the most recent */
-/* bookmark list but fails to be added to the bookmark file or vice versa. */
-/* ------------------------------------------------------------------------*/
-static bool write_bookmark(bool create_bookmark_file, const char *bookmark)
-{
- bool ret=true;
-
- if (!bookmark)
+ if (*s == 0)
{
- ret = false; /* something didn't happen correctly, do nothing */
+ return false;
}
- else
- {
- if (global_settings.usemrb)
- ret = add_bookmark(RECENT_BOOKMARK_FILE, bookmark, true);
+ end = strchr(s, ';');
- /* writing the bookmark */
- if (create_bookmark_file)
+ /* extract file names */
+ if(filenamebuf)
+ {
+ size_t len = (end == NULL) ? strlen(s) : (size_t) (end - s);
+ len = MIN(TEMP_BUF_SIZE - 1, len);
+ strmemccpy(global_temp_buffer, s, len + 1);
+
+ if (end != NULL)
{
- char* name = playlist_get_name(NULL, global_temp_buffer,
- sizeof(global_temp_buffer));
- if (generate_bookmark_file_name(name))
- {
- ret = ret & add_bookmark(global_bookmark_file_name, bookmark, false);
- }
- else
+ end++;
+ if (strip_dir)
{
- ret = false; /* generating bookmark file failed */
+ s = strrchr(end, '/');
+ if (s)
+ {
+ end = s;
+ end++;
+ }
}
+ strmemccpy(filenamebuf, end, filenamebufsz);
}
- }
-
- splash(HZ, ret ? ID2P(LANG_BOOKMARK_CREATE_SUCCESS)
- : ID2P(LANG_BOOKMARK_CREATE_FAILURE));
+ }
- return ret;
+ return true;
}
-/* Get the name of the playlist and the name of the track from a bookmark. */
-/* Returns true iff both were extracted. */
-static bool get_playlist_and_track(const char *bookmark, char **pl_start,
- char **pl_end, char **track)
+/* ------------------------------------------------------------------------- */
+/* This function takes a filename and appends .tmp. This function also opens */
+/* the resulting file based on oflags, filename will be in buf on return */
+/* Returns file descriptor */
+/* --------------------------------------------------------------------------*/
+static int open_temp_bookmark(char *buf,
+ size_t bufsz,
+ int oflags,
+ const char* filename)
{
- *pl_start = strchr(bookmark,'/');
- if (!(*pl_start))
- return false;
- *pl_end = strrchr(bookmark,';');
- *track = *pl_end + 1;
- return true;
+ if(filename[0] == '/')
+ filename++;
+ /* Opening up a temp bookmark file */
+ int fd = open_pathfmt(buf, bufsz, oflags, "/%s.tmp", filename);
+#ifdef LOGF_ENABLE
+ if (oflags & O_PATH)
+ logf("tempfile path %s", buf);
+ else
+ logf("opening tempfile %s", buf);
+#endif
+ return fd;
}
/* ----------------------------------------------------------------------- */
/* This function adds a bookmark to a file. */
/* Returns true on successful bookmark add. */
/* ------------------------------------------------------------------------*/
-static bool add_bookmark(const char* bookmark_file_name, const char* bookmark,
+static bool add_bookmark(const char* bookmark_file_name,
+ const char* bookmark,
bool most_recent)
{
- int temp_bookmark_file = 0;
- int bookmark_file = 0;
- int bookmark_count = 0;
- char *pl_start = NULL, *bm_pl_start;
- char *pl_end = NULL, *bm_pl_end;
- int pl_len = 0, bm_pl_len;
- char *track = NULL, *bm_track;
- bool comp_playlist = false;
- bool comp_track = false;
- bool equal;
+ char fnamebuf[MAX_PATH];
+ int temp_bookmark_file = 0;
+ int bookmark_file = 0;
+ int bookmark_count = 0;
+ bool comp_playlist = false;
+ bool comp_track = false;
+ bool equal;
+ uint32_t pl_hash, pl_track_hash;
+ uint32_t bm_pl_hash, bm_pl_track_hash;
/* Opening up a temp bookmark file */
- snprintf(global_temp_buffer, sizeof(global_temp_buffer),
- "%s.tmp", bookmark_file_name);
- temp_bookmark_file = open(global_temp_buffer,
- O_WRONLY | O_CREAT | O_TRUNC, 0666);
- if (temp_bookmark_file < 0)
- return false; /* can't open the temp file */
+ temp_bookmark_file = open_temp_bookmark(fnamebuf,
+ sizeof(fnamebuf),
+ O_WRONLY | O_CREAT | O_TRUNC,
+ bookmark_file_name);
+
+ if (temp_bookmark_file < 0 || !bookmark)
+ return false; /* can't open the temp file or no bookmark */
if (most_recent && ((global_settings.usemrb == BOOKMARK_ONE_PER_PLAYLIST)
|| (global_settings.usemrb == BOOKMARK_ONE_PER_TRACK)))
{
- if (get_playlist_and_track(bookmark, &pl_start, &pl_end, &track))
+
+ if (bookmark_get_playlist_and_track_hash(bookmark, &pl_hash, &pl_track_hash))
{
comp_playlist = true;
- pl_len = pl_end - pl_start;
- if (global_settings.usemrb == BOOKMARK_ONE_PER_TRACK)
- comp_track = true;
+ comp_track = (global_settings.usemrb == BOOKMARK_ONE_PER_TRACK);
}
}
+ logf("adding bookmark to %s [%s]", fnamebuf, bookmark);
/* Writing the new bookmark to the begining of the temp file */
write(temp_bookmark_file, bookmark, strlen(bookmark));
write(temp_bookmark_file, "\n", 1);
bookmark_count++;
+ /* WARNING underlying buffer to *bookmrk gets overwritten after this point! */
+
/* Reading in the previous bookmarks and writing them to the temp file */
+ logf("opening old bookmark %s", bookmark_file_name);
bookmark_file = open(bookmark_file_name, O_RDONLY);
if (bookmark_file >= 0)
{
- while (read_line(bookmark_file, global_read_buffer,
- sizeof(global_read_buffer)) > 0)
+ while (read_line(bookmark_file, global_temp_buffer,
+ sizeof(global_temp_buffer)) > 0)
{
/* The MRB has a max of MAX_BOOKMARKS in it */
/* This keeps it from getting too large */
if (most_recent && (bookmark_count >= MAX_BOOKMARKS))
break;
- if (!parse_bookmark(global_read_buffer, false, false))
+ if (!parse_bookmark(NULL, 0, global_temp_buffer, NULL, false))
break;
equal = false;
if (comp_playlist)
{
- if (get_playlist_and_track(global_read_buffer, &bm_pl_start,
- &bm_pl_end, &bm_track))
+ if (bookmark_get_playlist_and_track_hash(global_temp_buffer,
+ &bm_pl_hash, &bm_pl_track_hash))
{
- bm_pl_len = bm_pl_end - bm_pl_start;
- equal = (pl_len == bm_pl_len) && !strncmp(pl_start, bm_pl_start, pl_len);
+ equal = (pl_hash == bm_pl_hash);
if (equal && comp_track)
- equal = !strcmp(track, bm_track);
+ {
+ equal = (pl_track_hash == bm_pl_track_hash);
+ }
}
}
if (!equal)
{
bookmark_count++;
- write(temp_bookmark_file, global_read_buffer,
- strlen(global_read_buffer));
+ /*logf("copying old bookmark [%s]", global_temp_buffer);*/
+ write(temp_bookmark_file, global_temp_buffer,
+ strlen(global_temp_buffer));
write(temp_bookmark_file, "\n", 1);
}
}
@@ -354,180 +360,237 @@ static bool add_bookmark(const char* bookmark_file_name, const char* bookmark,
close(temp_bookmark_file);
remove(bookmark_file_name);
- rename(global_temp_buffer, bookmark_file_name);
+ rename(fnamebuf, bookmark_file_name);
return true;
}
+/* ----------------------------------------------------------------------- */
+/* This function is used by multiple functions and is used to generate a */
+/* bookmark named based off of the input. */
+/* Changing this function could result in how the bookmarks are stored. */
+/* it would be here that the centralized/decentralized bookmark code */
+/* could be placed. */
+/* Returns true if the file name is generated, false if it was too long */
+/* ----------------------------------------------------------------------- */
+static bool generate_bookmark_file_name(char *filenamebuf,
+ size_t filenamebufsz,
+ const char *bmarknamein,
+ size_t bmarknamelen)
+{
+ /* if this is a root dir MP3, rename the bookmark file root_dir.bmark */
+ /* otherwise, name it based on the bmarknamein variable */
+ if (!strncmp("/", bmarknamein, bmarknamelen))
+ strmemccpy(filenamebuf, "/root_dir.bmark", filenamebufsz);
+ else
+ {
+ size_t buflen, len;
+ /* strmemccpy considers the NULL so bmarknamelen is one off */
+ buflen = MIN(filenamebufsz -1 , bmarknamelen);
+ if (buflen >= filenamebufsz)
+ return false;
+
+ strmemccpy(filenamebuf, bmarknamein, buflen + 1);
+
+ len = strlen(filenamebuf);
+
+#ifdef HAVE_MULTIVOLUME
+ /* The "root" of an extra volume need special handling too. */
+ const char *filename;
+ path_strip_volume(filenamebuf, &filename, true);
+ bool volume_root = *filename == '\0';
+#endif
+ if(filenamebuf[len-1] == '/') {
+ filenamebuf[len-1] = '\0';
+ }
+
+ const char *name = ".bmark";
+#ifdef HAVE_MULTIVOLUME
+ if (volume_root)
+ name = "/volume_dir.bmark";
+#endif
+ len = strlcat(filenamebuf, name, filenamebufsz);
+
+ if(len >= filenamebufsz)
+ return false;
+ }
+ logf ("generated name '%s' from '%.*s'",
+ filenamebuf, (int)bmarknamelen, bmarknamein);
+ return true;
+}
+
/* GCC 7 and up complain about the snprintf in create_bookmark() when
compiled with -D_FORTIFY_SOURCE or -Wformat-truncation
This is a false positive, so disable it here only */
-#if __GNUC__ >= 7
+/* SHOULD NO LONGER BE NEEDED --Bilgus 11-2022 */
+#if 0 /* __GNUC__ >= 7 */
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-truncation"
#endif
/* ----------------------------------------------------------------------- */
/* This function takes the system resume data and formats it into a valid */
/* bookmark. */
-/* Returns not NULL on successful bookmark format. */
+/* playlist name and name len are passed back through the name/namelen */
+/* Return is not NULL on successful bookmark format. */
/* ----------------------------------------------------------------------- */
-static char* create_bookmark()
+static char* create_bookmark(char **name,
+ size_t *namelen,
+ struct resume_info *resume_info)
{
- int resume_index = 0;
- char *file;
-
- if (!bookmark_is_bookmarkable_state())
- return NULL; /* something didn't happen correctly, do nothing */
+ const char *file;
+ char *buf = global_temp_buffer;
+ size_t bufsz = sizeof(global_temp_buffer);
- /* grab the currently playing track */
- struct mp3entry *id3 = audio_current_track();
- if(!id3)
+ if(!resume_info->id3)
return NULL;
- /* Get some basic resume information */
- /* queue_resume and queue_resume_index are not used and can be ignored.*/
- playlist_get_resume_info(&resume_index);
-
- /* Get the currently playing file minus the path */
- /* This is used when displaying the available bookmarks */
- file = strrchr(id3->path,'/');
- if(NULL == file)
- return NULL;
-
- /* create the bookmark */
- playlist_get_name(NULL, global_temp_buffer, sizeof(global_temp_buffer));
- if (global_temp_buffer[strlen(global_temp_buffer) - 1] != '/')
- file = id3->path;
- else file++;
- snprintf(global_bookmark, sizeof(global_bookmark),
- /* new optional bookmark token descriptors should be inserted
- just before the "%s;%s" in this line... */
+ size_t bmarksz= snprintf(buf, bufsz,
+ /* new optional bookmark token descriptors should
+ be inserted just after ';"' in this line... */
#if defined(HAVE_PITCHCONTROL)
- ">%d;%d;%ld;%d;%ld;%d;%d;%ld;%ld;%s;%s",
+ ">%d;%d;%ld;%d;%ld;%d;%d;%ld;%ld;",
#else
- ">%d;%d;%ld;%d;%ld;%d;%d;%s;%s",
+ ">%d;%d;%ld;%d;%ld;%d;%d;",
#endif
- /* ... their flags should go here ... */
+ /* ... their flags should go here ... */
#if defined(HAVE_PITCHCONTROL)
- BM_PITCH | BM_SPEED,
+ BM_PITCH | BM_SPEED,
#else
- 0,
+ 0,
#endif
- resume_index,
- id3->offset,
- playlist_get_seed(NULL),
- id3->elapsed,
- global_settings.repeat_mode,
- global_settings.playlist_shuffle,
- /* ...and their values should go here */
+ resume_info->resume_index,
+ resume_info->id3->offset,
+ resume_info->resume_seed,
+ resume_info->id3->elapsed,
+ resume_info->repeat_mode,
+ resume_info->shuffle,
+ /* ...and their values should go here */
#if defined(HAVE_PITCHCONTROL)
- (long)sound_get_pitch(),
- (long)dsp_get_timestretch(),
+ (long)resume_info->pitch,
+ (long)resume_info->speed
#endif
- /* more mandatory tokens */
- global_temp_buffer,
- file);
+ ); /*sprintf*/
+/* mandatory tokens */
+ if (bmarksz >= bufsz) /* include NULL*/
+ return NULL;
+ buf += bmarksz;
+ bufsz -= bmarksz;
+ /* create the bookmark */
+ playlist_get_name(NULL, buf, bufsz);
+ bmarksz = strlen(buf);
+
+ if (bmarksz == 0 || (bmarksz + 1) >= bufsz) /* include the separator & NULL*/
+ return NULL;
+
+ *name = buf; /* return the playlist name through the *pointer */
+ *namelen = bmarksz; /* return the name length through the pointer */
+
+ /* Get the currently playing file minus the path */
+ /* This is used when displaying the available bookmarks */
+ file = strrchr(resume_info->id3->path,'/');
+ if(NULL == file)
+ return NULL;
+
+ if (buf[bmarksz - 1] != '/')
+ file = resume_info->id3->path;
+ else file++;
+
+ buf += bmarksz;
+ bufsz -= (bmarksz + 1);
+ buf[0] = ';';
+ buf[1] = '\0';
+
+ strlcat(buf, file, bufsz);
+
+ logf("%s [%s]", __func__, global_temp_buffer);
/* checking to see if the bookmark is valid */
- if (parse_bookmark(global_bookmark, false, false))
- return global_bookmark;
+ if (parse_bookmark(NULL, 0, global_temp_buffer, NULL, false))
+ return global_temp_buffer;
else
return NULL;
}
-#if __GNUC__ >= 7
+#if 0/* __GNUC__ >= 7*/
#pragma GCC diagnostic pop /* -Wformat-truncation */
#endif
/* ----------------------------------------------------------------------- */
-/* This function will determine if an autoload is necessary. This is an */
-/* interface function. */
-/* Returns true on bookmark load or bookmark selection. */
-/* ------------------------------------------------------------------------*/
-bool bookmark_autoload(const char* file)
+/* This function gets some basic resume information for the current song */
+/* from rockbox, */
+/* ----------------------------------------------------------------------- */
+static void get_track_resume_info(struct resume_info *resume_info)
{
- char* bookmark;
-
- if(global_settings.autoloadbookmark == BOOKMARK_NO)
- return false;
-
- /*Checking to see if a bookmark file exists.*/
- if(!generate_bookmark_file_name(file))
- {
- return false;
- }
-
- if(!file_exists(global_bookmark_file_name))
- return false;
-
- if(global_settings.autoloadbookmark == BOOKMARK_YES)
- {
- return bookmark_load(global_bookmark_file_name, true);
- }
+ if (global_settings.playlist_shuffle)
+ playlist_get_resume_info(&(resume_info->resume_index));
else
- {
- int ret = select_bookmark(global_bookmark_file_name, true, &bookmark);
-
- if (bookmark != NULL)
- {
- if (!play_bookmark(bookmark))
- {
- /* Selected bookmark not found. */
- splash(HZ*2, ID2P(LANG_NOTHING_TO_RESUME));
- }
+ resume_info->resume_index = playlist_get_display_index() - 1;
- /* Act as if autoload was done even if it failed, since the
- * user did make an active selection.
- */
- return true;
- }
-
- return ret != BOOKMARK_SUCCESS;
- }
+ resume_info->resume_seed = playlist_get_seed(NULL);
+ resume_info->id3 = audio_current_track();
+ resume_info->repeat_mode = global_settings.repeat_mode;
+ resume_info->shuffle = global_settings.playlist_shuffle;
+#if defined(HAVE_PITCHCONTROL)
+ resume_info->pitch = sound_get_pitch();
+ resume_info->speed = dsp_get_timestretch();
+#endif
}
/* ----------------------------------------------------------------------- */
-/* This function loads the bookmark information into the resume memory. */
-/* This is an interface function. */
-/* Returns true on successful bookmark load. */
+/* This function takes the current current resume information and writes */
+/* that to the beginning of the bookmark file. */
+/* This file will contain N number of bookmarks in the following format: */
+/* resume_index*resume_offset*resume_seed*resume_first_index* */
+/* resume_file*milliseconds*MP3 Title* */
+/* Returns true on successful bookmark write. */
+/* Returns false if any part of the bookmarking process fails. It is */
+/* possible that a bookmark is successfully added to the most recent */
+/* bookmark list but fails to be added to the bookmark file or vice versa. */
/* ------------------------------------------------------------------------*/
-bool bookmark_load(const char* file, bool autoload)
+static bool write_bookmark(bool create_bookmark_file)
{
- int fd;
- char* bookmark = NULL;
+ logf("%s", __func__);
+ char bm_filename[MAX_PATH];
+ bool ret=true;
- if(autoload)
+ char *name = NULL;
+ size_t namelen = 0;
+ char* bm;
+ struct resume_info resume_info;
+
+ if (bookmark_is_bookmarkable_state())
{
- fd = open(file, O_RDONLY);
- if(fd >= 0)
+ get_track_resume_info(&resume_info);
+ /* writing the most recent bookmark */
+ if (global_settings.usemrb)
{
- if(read_line(fd, global_read_buffer, sizeof(global_read_buffer)) > 0)
- bookmark=global_read_buffer;
- close(fd);
+ /* since we use the same buffer bookmark needs created each time */
+ bm = create_bookmark(&name, &namelen, &resume_info);
+ ret = add_bookmark(RECENT_BOOKMARK_FILE, bm, true);
}
- }
- else
- {
- /* This is not an auto-load, so list the bookmarks */
- select_bookmark(file, false, &bookmark);
- }
- if (bookmark != NULL)
- {
- if (!play_bookmark(bookmark))
+ /* writing the directory bookmark */
+ if (create_bookmark_file)
{
- /* Selected bookmark not found. */
- if (!autoload)
+ bm = create_bookmark(&name, &namelen, &resume_info);
+ if (generate_bookmark_file_name(bm_filename,
+ sizeof(bm_filename), name, namelen))
{
- splash(HZ*2, ID2P(LANG_NOTHING_TO_RESUME));
+ ret &= add_bookmark(bm_filename, bm, false);
+ }
+ else
+ {
+ ret = false; /* generating bookmark file failed */
}
-
- return false;
}
}
+ else
+ ret = false;
- return true;
-}
+ splash(HZ, ret ? ID2P(LANG_BOOKMARK_CREATE_SUCCESS)
+ : ID2P(LANG_BOOKMARK_CREATE_FAILURE));
+ return ret;
+}
static int get_bookmark_count(const char* bookmark_file_name)
{
@@ -537,11 +600,11 @@ static int get_bookmark_count(const char* bookmark_file_name)
if(file < 0)
return -1;
- while(read_line(file, global_read_buffer, sizeof(global_read_buffer)) > 0)
+ while(read_line(file, global_temp_buffer, sizeof(global_temp_buffer)) > 0)
{
read_count++;
}
-
+
close(file);
return read_count;
}
@@ -563,26 +626,26 @@ static int buffer_bookmarks(struct bookmark_list* bookmarks, int first_line)
/* Entire file fits in buffer */
first_line = 0;
}
-
+
bookmarks->start = first_line;
bookmarks->count = 0;
bookmarks->reload = false;
-
- while(read_line(file, global_read_buffer, sizeof(global_read_buffer)) > 0)
+
+ while(read_line(file, global_temp_buffer, sizeof(global_temp_buffer)) > 0)
{
read_count++;
-
+
if (read_count >= first_line)
{
- dest -= strlen(global_read_buffer) + 1;
-
+ dest -= strlen(global_temp_buffer) + 1;
+
if (dest < ((char*) bookmarks) + sizeof(*bookmarks)
+ (sizeof(char*) * (bookmarks->count + 1)))
{
break;
}
-
- strcpy(dest, global_read_buffer);
+
+ strcpy(dest, global_temp_buffer);
bookmarks->items[bookmarks->count] = dest;
bookmarks->count++;
}
@@ -597,6 +660,8 @@ static const char* get_bookmark_info(int list_index,
char *buffer,
size_t buffer_len)
{
+ char fnamebuf[MAX_PATH];
+ struct resume_info resume_info;
struct bookmark_list* bookmarks = (struct bookmark_list*) data;
int index = list_index / 2;
@@ -604,22 +669,22 @@ static const char* get_bookmark_info(int list_index,
{
if (index == 0)
{
- return list_index % 2 == 0
+ return list_index % 2 == 0
? (char*) str(LANG_BOOKMARK_DONT_RESUME) : " ";
}
-
+
index--;
}
- if (bookmarks->reload || (index >= bookmarks->start + bookmarks->count)
+ if (bookmarks->reload || (index >= bookmarks->start + bookmarks->count)
|| (index < bookmarks->start))
{
int read_index = index;
-
+
/* Using count as a guide on how far to move could possibly fail
* sometimes. Use byte count if that is a problem?
*/
-
+
if (read_index != 0)
{
/* Move count * 3 / 4 items in the direction the user is moving,
@@ -627,32 +692,33 @@ static const char* get_bookmark_info(int list_index,
*/
int offset = bookmarks->count;
int max = bookmarks->total_count - (bookmarks->count / 2);
-
+
if (read_index < bookmarks->start)
{
offset *= 3;
}
-
+
read_index = index - offset / 4;
if (read_index > max)
{
read_index = max;
}
-
+
if (read_index < 0)
{
read_index = 0;
}
}
-
+
if (buffer_bookmarks(bookmarks, read_index) <= index)
{
return "";
}
}
-
- if (!parse_bookmark(bookmarks->items[index - bookmarks->start], true, true))
+
+ if (!parse_bookmark(fnamebuf, sizeof(fnamebuf),
+ bookmarks->items[index - bookmarks->start], &resume_info, true))
{
return list_index % 2 == 0 ? (char*) str(LANG_BOOKMARK_INVALID) : " ";
}
@@ -662,12 +728,12 @@ static const char* get_bookmark_info(int list_index,
char *name;
char *format;
int len = strlen(global_temp_buffer);
-
+
if (bookmarks->show_playlist_name && len > 0)
{
name = global_temp_buffer;
len--;
-
+
if (name[len] != '/')
{
strrsplt(name, '.');
@@ -686,25 +752,73 @@ static const char* get_bookmark_info(int list_index,
}
else
{
- name = global_filename;
+ name = fnamebuf;
format = "%s";
}
-
- strrsplt(global_filename, '.');
- snprintf(buffer, buffer_len, format, name, global_filename);
+
+ strrsplt(fnamebuf, '.');
+ snprintf(buffer, buffer_len, format, name, fnamebuf);
return buffer;
}
else
{
char time_buf[32];
- format_time(time_buf, sizeof(time_buf), bm.resume_time);
- snprintf(buffer, buffer_len, "%s, %d%s", time_buf, bm.resume_index + 1,
- bm.shuffle ? (char*) str(LANG_BOOKMARK_SHUFFLE) : "");
+ format_time(time_buf, sizeof(time_buf), resume_info.resume_elapsed);
+ snprintf(buffer, buffer_len, "%s, %d%s", time_buf,
+ resume_info.resume_index + 1,
+ resume_info.shuffle ? (char*) str(LANG_BOOKMARK_SHUFFLE) : "");
return buffer;
}
}
+/* ----------------------------------------------------------------------- */
+/* This function parses a bookmark, says the voice UI part of it. */
+/* ------------------------------------------------------------------------*/
+static void say_bookmark(const char* bookmark,
+ int bookmark_id,
+ bool show_playlist_name)
+{
+ char fnamebuf[MAX_PATH];
+ struct resume_info resume_info;
+ if (!parse_bookmark(fnamebuf, sizeof(fnamebuf), bookmark, &resume_info, false))
+ {
+ talk_id(LANG_BOOKMARK_INVALID, false);
+ return;
+ }
+
+ talk_number(bookmark_id + 1, false);
+
+ bool is_dir = (global_temp_buffer[0]
+ && global_temp_buffer[strlen(global_temp_buffer)-1] == '/');
+
+ /* HWCODEC cannot enqueue voice file entries and .talk thumbnails
+ together, because there is no guarantee that the same mp3
+ parameters are used. */
+ if(show_playlist_name)
+ { /* It's useful to know which playlist this is */
+ if(is_dir)
+ talk_dir_or_spell(global_temp_buffer,
+ TALK_IDARRAY(VOICE_DIR), true);
+ else talk_file_or_spell(NULL, global_temp_buffer,
+ TALK_IDARRAY(LANG_PLAYLIST), true);
+ }
+
+ if(resume_info.shuffle)
+ talk_id(LANG_SHUFFLE, true);
+
+ talk_id(VOICE_BOOKMARK_SELECT_INDEX_TEXT, true);
+ talk_number(resume_info.resume_index + 1, true);
+ talk_id(LANG_TIME, true);
+ talk_value(resume_info.resume_elapsed / 1000, UNIT_TIME, true);
+
+ /* Track filename */
+ if(!is_dir)
+ global_temp_buffer[0] = 0;
+ talk_file_or_spell(global_temp_buffer, fnamebuf,
+ TALK_IDARRAY(VOICE_FILE), true);
+}
+
static int bookmark_list_voice_cb(int list_index, void* data)
{
struct bookmark_list* bookmarks = (struct bookmark_list*) data;
@@ -722,6 +836,57 @@ static int bookmark_list_voice_cb(int list_index, void* data)
}
/* ----------------------------------------------------------------------- */
+/* This function takes a location in a bookmark file and deletes that */
+/* bookmark. */
+/* Returns true on successful bookmark deletion. */
+/* ------------------------------------------------------------------------*/
+static bool delete_bookmark(const char* bookmark_file_name, int bookmark_id)
+{
+ int temp_bookmark_file = 0;
+ int bookmark_file = 0;
+ int bookmark_count = 0;
+
+ /* Opening up a temp bookmark file */
+ temp_bookmark_file = open_temp_bookmark(global_temp_buffer,
+ sizeof(global_temp_buffer),
+ O_WRONLY | O_CREAT | O_TRUNC,
+ bookmark_file_name);
+
+ if (temp_bookmark_file < 0)
+ return false; /* can't open the temp file */
+
+ /* Reading in the previous bookmarks and writing them to the temp file */
+ bookmark_file = open(bookmark_file_name, O_RDONLY);
+ if (bookmark_file >= 0)
+ {
+ while (read_line(bookmark_file, global_temp_buffer,
+ sizeof(global_temp_buffer)) > 0)
+ {
+ if (bookmark_id != bookmark_count)
+ {
+ write(temp_bookmark_file, global_temp_buffer,
+ strlen(global_temp_buffer));
+ write(temp_bookmark_file, "\n", 1);
+ }
+ bookmark_count++;
+ }
+ close(bookmark_file);
+ }
+ close(temp_bookmark_file);
+
+ /* only retrieve the path*/
+ open_temp_bookmark(global_temp_buffer,
+ sizeof(global_temp_buffer),
+ O_PATH,
+ bookmark_file_name);
+
+ remove(bookmark_file_name);
+ rename(global_temp_buffer, bookmark_file_name);
+
+ return true;
+}
+
+/* ----------------------------------------------------------------------- */
/* This displays the bookmarks in a file and allows the user to */
/* select one to play. */
/* *selected_bookmark contains a non NULL value on successful bookmark */
@@ -730,7 +895,9 @@ static int bookmark_list_voice_cb(int list_index, void* data)
/* if no selection was made and BOOKMARK_USB_CONNECTED if the selection */
/* menu is forced to exit due to a USB connection. */
/* ------------------------------------------------------------------------*/
-static int select_bookmark(const char* bookmark_file_name, bool show_dont_resume, char** selected_bookmark)
+static int select_bookmark(const char* bookmark_file_name,
+ bool show_dont_resume,
+ char** selected_bookmark)
{
struct bookmark_list* bookmarks;
struct gui_synclist list;
@@ -747,16 +914,17 @@ static int select_bookmark(const char* bookmark_file_name, bool show_dont_resume
bookmarks->filename = bookmark_file_name;
bookmarks->start = 0;
bookmarks->show_playlist_name
- = strcmp(bookmark_file_name, RECENT_BOOKMARK_FILE) == 0;
- gui_synclist_init(&list, &get_bookmark_info, (void*) bookmarks, false, 2, NULL);
+ = (strcmp(bookmark_file_name, RECENT_BOOKMARK_FILE) == 0);
+
+ gui_synclist_init(&list, &get_bookmark_info,
+ (void*) bookmarks, false, 2, NULL);
+
if(global_settings.talk_menu)
gui_synclist_set_voice_callback(&list, bookmark_list_voice_cb);
- gui_synclist_set_title(&list, str(LANG_BOOKMARK_SELECT_BOOKMARK),
- Icon_Bookmark);
while (!exit)
{
-
+
if (refresh)
{
int count = get_bookmark_count(bookmark_file_name);
@@ -787,14 +955,15 @@ static int select_bookmark(const char* bookmark_file_name, bool show_dont_resume
}
buffer_bookmarks(bookmarks, bookmarks->start);
+ gui_synclist_set_title(&list, str(LANG_BOOKMARK_SELECT_BOOKMARK),
+ Icon_Bookmark);
gui_synclist_draw(&list);
cond_talk_ids_fq(VOICE_EXT_BMARK);
gui_synclist_speak_item(&list);
refresh = false;
}
- list_do_action(CONTEXT_BOOKMARKSCREEN, HZ / 2,
- &list, &action, LIST_WRAP_UNLESS_HELD);
+ list_do_action(CONTEXT_BOOKMARKSCREEN, HZ / 2, &list, &action);
item = gui_synclist_get_sel_pos(&list) / 2;
if (bookmarks->show_dont_resume)
@@ -805,17 +974,17 @@ static int select_bookmark(const char* bookmark_file_name, bool show_dont_resume
if (action == ACTION_STD_CONTEXT)
{
MENUITEM_STRINGLIST(menu_items, ID2P(LANG_BOOKMARK_CONTEXT_MENU),
- NULL, ID2P(LANG_BOOKMARK_CONTEXT_RESUME),
+ NULL, ID2P(LANG_BOOKMARK_CONTEXT_RESUME),
ID2P(LANG_BOOKMARK_CONTEXT_DELETE));
- static const int menu_actions[] =
+ static const int menu_actions[] =
{
ACTION_STD_OK, ACTION_BMS_DELETE
};
int selection = do_menu(&menu_items, NULL, NULL, false);
-
+
refresh = true;
- if (selection >= 0 && selection <=
+ if (selection >= 0 && selection <=
(int) (sizeof(menu_actions) / sizeof(menu_actions[0])))
{
action = menu_actions[selection];
@@ -842,19 +1011,9 @@ static int select_bookmark(const char* bookmark_file_name, bool show_dont_resume
case ACTION_BMS_DELETE:
if (item >= 0)
- {
- const char *lines[]={
- ID2P(LANG_REALLY_DELETE)
- };
- const char *yes_lines[]={
- ID2P(LANG_DELETING)
- };
-
- const struct text_message message={lines, 1};
- const struct text_message yes_message={yes_lines, 1};
-
- if(gui_syncyesno_run(&message, &yes_message, NULL)==YESNO_YES)
- {
+ {
+ if (confirm_delete_yesno("") == YESNO_YES)
+ {
delete_bookmark(bookmark_file_name, item);
bookmarks->reload = true;
}
@@ -879,257 +1038,247 @@ static int select_bookmark(const char* bookmark_file_name, bool show_dont_resume
}
/* ----------------------------------------------------------------------- */
-/* This function takes a location in a bookmark file and deletes that */
-/* bookmark. */
-/* Returns true on successful bookmark deletion. */
+/* This function parses a bookmark and then plays it. */
+/* Returns true on successful bookmark play. */
/* ------------------------------------------------------------------------*/
-static bool delete_bookmark(const char* bookmark_file_name, int bookmark_id)
+static bool play_bookmark(const char* bookmark)
{
- int temp_bookmark_file = 0;
- int bookmark_file = 0;
- int bookmark_count = 0;
-
- /* Opening up a temp bookmark file */
- snprintf(global_temp_buffer, sizeof(global_temp_buffer),
- "%s.tmp", bookmark_file_name);
- temp_bookmark_file = open(global_temp_buffer,
- O_WRONLY | O_CREAT | O_TRUNC, 0666);
-
- if (temp_bookmark_file < 0)
- return false; /* can't open the temp file */
+ char fnamebuf[MAX_PATH];
+ struct resume_info resume_info;
+#if defined(HAVE_PITCHCONTROL)
+ /* preset pitch and speed to 100% in case bookmark doesn't have info */
+ resume_info.pitch = sound_get_pitch();
+ resume_info.speed = dsp_get_timestretch();
+#endif
- /* Reading in the previous bookmarks and writing them to the temp file */
- bookmark_file = open(bookmark_file_name, O_RDONLY);
- if (bookmark_file >= 0)
+ if (parse_bookmark(fnamebuf, sizeof(fnamebuf), bookmark, &resume_info, true))
{
- while (read_line(bookmark_file, global_read_buffer,
- sizeof(global_read_buffer)) > 0)
+ global_settings.repeat_mode = resume_info.repeat_mode;
+ global_settings.playlist_shuffle = resume_info.shuffle;
+#if defined(HAVE_PITCHCONTROL)
+ sound_set_pitch(resume_info.pitch);
+ dsp_set_timestretch(resume_info.speed);
+#endif
+ if (!warn_on_pl_erase())
+ return false;
+ bool success = bookmark_play(global_temp_buffer, resume_info.resume_index,
+ resume_info.resume_elapsed, resume_info.resume_offset,
+ resume_info.resume_seed, fnamebuf);
+ if (success) /* verify we loaded the correct track */
{
- if (bookmark_id != bookmark_count)
+ const struct mp3entry *id3 = audio_current_track();
+ if (id3)
{
- write(temp_bookmark_file, global_read_buffer,
- strlen(global_read_buffer));
- write(temp_bookmark_file, "\n", 1);
+ const char *path;
+ const char *track;
+ path_basename(id3->path, &path);
+ path_basename(fnamebuf, &track);
+ if (strcmp(path, track) == 0)
+ {
+ return true;
+ }
}
- bookmark_count++;
+ audio_stop();
}
- close(bookmark_file);
}
- close(temp_bookmark_file);
- remove(bookmark_file_name);
- rename(global_temp_buffer, bookmark_file_name);
-
- return true;
+ return false;
}
+/*-------------------------------------------------------------------------*/
+/* PUBLIC INTERFACE -------------------------------------------------------*/
+/*-------------------------------------------------------------------------*/
+
+
/* ----------------------------------------------------------------------- */
-/* This function parses a bookmark, says the voice UI part of it. */
-/* ------------------------------------------------------------------------*/
-static void say_bookmark(const char* bookmark,
- int bookmark_id, bool show_playlist_name)
+/* This is an interface function from the context menu. */
+/* Returns true on successful bookmark creation. */
+/* ----------------------------------------------------------------------- */
+bool bookmark_create_menu(void)
{
- if (!parse_bookmark(bookmark, true, false))
+ return write_bookmark(true);
+}
+/* ----------------------------------------------------------------------- */
+/* This function acts as the load interface from the context menu. */
+/* This function determines the bookmark file name and then loads that file*/
+/* for the user. The user can then select or delete previous bookmarks. */
+/* This function returns BOOKMARK_SUCCESS on the selection of a track to */
+/* resume, BOOKMARK_FAIL if the menu is exited without a selection and */
+/* BOOKMARK_USB_CONNECTED if the menu is forced to exit due to a USB */
+/* connection. */
+/* ----------------------------------------------------------------------- */
+int bookmark_load_menu(void)
+{
+ char bm_filename[MAX_PATH];
+ char* bookmark;
+ int ret = BOOKMARK_FAIL;
+
+ push_current_activity(ACTIVITY_BOOKMARKSLIST);
+
+ char* name = playlist_get_name(NULL, global_temp_buffer,
+ sizeof(global_temp_buffer));
+ if (generate_bookmark_file_name(bm_filename, sizeof(bm_filename), name, -1))
{
- talk_id(LANG_BOOKMARK_INVALID, false);
- return;
+ ret = select_bookmark(bm_filename, false, &bookmark);
+ if (bookmark != NULL)
+ {
+ ret = play_bookmark(bookmark) ? BOOKMARK_SUCCESS : BOOKMARK_FAIL;
+ }
}
- talk_number(bookmark_id + 1, false);
+ pop_current_activity();
+ return ret;
+}
- bool is_dir = (global_temp_buffer[0]
- && global_temp_buffer[strlen(global_temp_buffer)-1] == '/');
+/* ----------------------------------------------------------------------- */
+/* Gives the user a list of the Most Recent Bookmarks. This is an */
+/* interface function */
+/* Returns true on the successful selection of a recent bookmark. */
+/* ----------------------------------------------------------------------- */
+bool bookmark_mrb_load()
+{
+ char* bookmark;
+ bool ret = false;
- /* HWCODEC cannot enqueue voice file entries and .talk thumbnails
- together, because there is no guarantee that the same mp3
- parameters are used. */
- if(show_playlist_name)
- { /* It's useful to know which playlist this is */
- if(is_dir)
- talk_dir_or_spell(global_temp_buffer,
- TALK_IDARRAY(VOICE_DIR), true);
- else talk_file_or_spell(NULL, global_temp_buffer,
- TALK_IDARRAY(LANG_PLAYLIST), true);
+ push_current_activity(ACTIVITY_BOOKMARKSLIST);
+ select_bookmark(RECENT_BOOKMARK_FILE, false, &bookmark);
+ if (bookmark != NULL)
+ {
+ ret = play_bookmark(bookmark);
}
- if(bm.shuffle)
- talk_id(LANG_SHUFFLE, true);
-
- talk_id(VOICE_BOOKMARK_SELECT_INDEX_TEXT, true);
- talk_number(bm.resume_index + 1, true);
- talk_id(LANG_TIME, true);
- talk_value(bm.resume_time / 1000, UNIT_TIME, true);
-
- /* Track filename */
- if(!is_dir)
- global_temp_buffer[0] = 0;
- talk_file_or_spell(global_temp_buffer, global_filename,
- TALK_IDARRAY(VOICE_FILE), true);
+ pop_current_activity();
+ return ret;
}
/* ----------------------------------------------------------------------- */
-/* This function parses a bookmark and then plays it. */
-/* Returns true on successful bookmark play. */
-/* ------------------------------------------------------------------------*/
-static bool play_bookmark(const char* bookmark)
+/* This function handles an autobookmark creation. This is an interface */
+/* function. */
+/* Returns true on successful bookmark creation. */
+/* ----------------------------------------------------------------------- */
+bool bookmark_autobookmark(bool prompt_ok)
{
-#if defined(HAVE_PITCHCONTROL)
- /* preset pitch and speed to 100% in case bookmark doesn't have info */
- bm.pitch = sound_get_pitch();
- bm.speed = dsp_get_timestretch();
-#endif
+ logf("%s", __func__);
+ bool update;
+
+ if (!bookmark_is_bookmarkable_state())
+ return false;
+
+ audio_pause(); /* first pause playback */
+ update = (global_settings.autoupdatebookmark && bookmark_exists());
+
+ if (update)
+ return write_bookmark(true);
- if (parse_bookmark(bookmark, true, true))
+ switch (global_settings.autocreatebookmark)
{
- global_settings.repeat_mode = bm.repeat_mode;
- global_settings.playlist_shuffle = bm.shuffle;
-#if defined(HAVE_PITCHCONTROL)
- sound_set_pitch(bm.pitch);
- dsp_set_timestretch(bm.speed);
-#endif
- if (!warn_on_pl_erase())
+ case BOOKMARK_YES:
+ return write_bookmark(true);
+
+ case BOOKMARK_NO:
return false;
- return bookmark_play(global_temp_buffer, bm.resume_index,
- bm.resume_time, bm.resume_offset, bm.resume_seed, global_filename);
+
+ case BOOKMARK_RECENT_ONLY_YES:
+ return write_bookmark(false);
}
+ const char *lines[]={ID2P(LANG_AUTO_BOOKMARK_QUERY)};
+ const struct text_message message={lines, 1};
+ if(prompt_ok && gui_syncyesno_run(&message, NULL, NULL)==YESNO_YES)
+ {
+ if (global_settings.autocreatebookmark == BOOKMARK_RECENT_ONLY_ASK)
+ return write_bookmark(false);
+ else
+ return write_bookmark(true);
+ }
return false;
}
-static const char* skip_token(const char* s)
+/* ----------------------------------------------------------------------- */
+/* This function will determine if an autoload is necessary. This is an */
+/* interface function. */
+/* Returns */
+/* BOOKMARK_DO_RESUME on bookmark load or bookmark selection. */
+/* BOOKMARK_DONT_RESUME if we're not going to resume */
+/* BOOKMARK_CANCEL if user canceled */
+/* ------------------------------------------------------------------------*/
+int bookmark_autoload(const char* file)
{
- while (*s && *s != ';')
+ logf("%s", __func__);
+ char bm_filename[MAX_PATH];
+ char* bookmark;
+
+ if(global_settings.autoloadbookmark == BOOKMARK_NO)
+ return BOOKMARK_DONT_RESUME;
+
+ /*Checking to see if a bookmark file exists.*/
+ if(!generate_bookmark_file_name(bm_filename, sizeof(bm_filename), file, -1))
{
- s++;
+ return BOOKMARK_DONT_RESUME;
}
-
- if (*s)
+
+ if(!file_exists(bm_filename))
+ return BOOKMARK_DONT_RESUME;
+
+ if(global_settings.autoloadbookmark == BOOKMARK_YES)
{
- s++;
+ return (bookmark_load(bm_filename, true)
+ ? BOOKMARK_DO_RESUME : BOOKMARK_DONT_RESUME);
}
-
- return s;
-}
+ else
+ {
+ int ret = select_bookmark(bm_filename, true, &bookmark);
-static const char* int_token(const char* s, int* dest)
-{
- *dest = atoi(s);
- return skip_token(s);
-}
+ if (bookmark != NULL)
+ {
+ if (!play_bookmark(bookmark))
+ return BOOKMARK_CANCEL;
+ return BOOKMARK_DO_RESUME;
+ }
-static const char* long_token(const char* s, long* dest)
-{
- *dest = atoi(s); /* Should be atol, but we don't have it. */
- return skip_token(s);
+ return (ret != BOOKMARK_SUCCESS) ? BOOKMARK_CANCEL : BOOKMARK_DONT_RESUME;
+ }
}
/* ----------------------------------------------------------------------- */
-/* This function takes a bookmark and parses it. This function also */
-/* validates the bookmark. The parse_filenames flag indicates whether */
-/* the filename tokens are to be extracted. */
-/* Returns true on successful bookmark parse. */
-/* ----------------------------------------------------------------------- */
-static bool parse_bookmark(const char *bookmark, const bool parse_filenames, const bool strip_dir)
+/* This function loads the bookmark information into the resume memory. */
+/* This is an interface function. */
+/* Returns true on successful bookmark load. */
+/* ------------------------------------------------------------------------*/
+bool bookmark_load(const char* file, bool autoload)
{
- const char* s = bookmark;
- const char* end;
-
-#define GET_INT_TOKEN(var) s = int_token(s, &var)
-#define GET_LONG_TOKEN(var) s = long_token(s, &var)
-#define GET_BOOL_TOKEN(var) var = (atoi(s)!=0); s = skip_token(s)
-
- /* if new format bookmark, extract the optional content flags,
- otherwise treat as an original format bookmark */
- int opt_flags = 0;
- bool new_format = (strchr(s, '>') == s);
- if (new_format)
+ logf("%s", __func__);
+ int fd;
+ char* bookmark = NULL;
+
+ if(autoload)
{
- s++;
- GET_INT_TOKEN(opt_flags);
+ fd = open(file, O_RDONLY);
+ if(fd >= 0)
+ {
+ if(read_line(fd, global_temp_buffer, sizeof(global_temp_buffer)) > 0)
+ bookmark=global_temp_buffer;
+ close(fd);
+ }
}
-
- /* extract all original bookmark tokens */
- GET_INT_TOKEN(bm.resume_index);
- GET_LONG_TOKEN(bm.resume_offset);
- GET_INT_TOKEN(bm.resume_seed);
- if (!new_format) /* skip deprecated token */
- s = skip_token(s);
- GET_LONG_TOKEN(bm.resume_time);
- GET_INT_TOKEN(bm.repeat_mode);
- GET_BOOL_TOKEN(bm.shuffle);
-
- /* extract all optional bookmark tokens */
- if (opt_flags & BM_PITCH)
- GET_INT_TOKEN(bm.pitch);
- if (opt_flags & BM_SPEED)
- GET_INT_TOKEN(bm.speed);
-
- if (*s == 0)
+ else
{
- return false;
+ /* This is not an auto-load, so list the bookmarks */
+ select_bookmark(file, false, &bookmark);
}
-
- end = strchr(s, ';');
- /* extract file names */
- if (parse_filenames)
+ if (bookmark != NULL)
{
- size_t len = (end == NULL) ? strlen(s) : (size_t) (end - s);
- len = MIN(TEMP_BUF_SIZE - 1, len);
- strlcpy(global_temp_buffer, s, len + 1);
-
- if (end != NULL)
+ if (!play_bookmark(bookmark))
{
- end++;
- if (strip_dir)
+ /* Selected bookmark not found. */
+ if (!autoload)
{
- s = strrchr(end, '/');
- if (s)
- {
- end = s;
- end++;
- }
+ splash(HZ*2, ID2P(LANG_NOTHING_TO_RESUME));
}
- strlcpy(global_filename, end, MAX_PATH);
- }
- }
-
- return true;
-}
-
-/* ----------------------------------------------------------------------- */
-/* This function is used by multiple functions and is used to generate a */
-/* bookmark named based off of the input. */
-/* Changing this function could result in how the bookmarks are stored. */
-/* it would be here that the centralized/decentralized bookmark code */
-/* could be placed. */
-/* Always returns true */
-/* ----------------------------------------------------------------------- */
-static bool generate_bookmark_file_name(const char *in)
-{
- int len = strlen(in);
- /* if this is a root dir MP3, rename the bookmark file root_dir.bmark */
- /* otherwise, name it based on the in variable */
- if (!strcmp("/", in))
- strcpy(global_bookmark_file_name, "/root_dir.bmark");
- else
- {
-#ifdef HAVE_MULTIVOLUME
- /* The "root" of an extra volume need special handling too. */
- const char *filename;
- path_strip_volume(in, &filename, true);
- bool volume_root = *filename == '\0';
-#endif
- strcpy(global_bookmark_file_name, in);
- if(global_bookmark_file_name[len-1] == '/')
- len--;
-#ifdef HAVE_MULTIVOLUME
- if (volume_root)
- strcpy(&global_bookmark_file_name[len], "/volume_dir.bmark");
- else
-#endif
- strcpy(&global_bookmark_file_name[len], ".bmark");
+ return false;
+ }
}
return true;
@@ -1141,13 +1290,15 @@ static bool generate_bookmark_file_name(const char *in)
/* ----------------------------------------------------------------------- */
bool bookmark_exists(void)
{
+ char bm_filename[MAX_PATH];
bool exist=false;
char* name = playlist_get_name(NULL, global_temp_buffer,
sizeof(global_temp_buffer));
- if (generate_bookmark_file_name(name))
+ if (!playlist_dynamic_only() &&
+ generate_bookmark_file_name(bm_filename, sizeof(bm_filename), name, -1))
{
- exist = file_exists(global_bookmark_file_name);
+ exist = file_exists(bm_filename);
}
return exist;
}
@@ -1165,12 +1316,13 @@ bool bookmark_is_bookmarkable_state(void)
/* no track playing */
(playlist_get_resume_info(&resume_index) == -1) ||
/* invalid queue info */
- (playlist_modified(NULL)))
- /* can't bookmark while in the queue */
+ (playlist_modified(NULL)) ||
+ /* can't bookmark playlists modified by user */
+ (playlist_dynamic_only()))
+ /* can't bookmark playlists without associated folder or playlist file */
{
return false;
}
return true;
}
-
diff --git a/apps/bookmark.h b/apps/bookmark.h
index ff7b87c1bf..192e577ce6 100644
--- a/apps/bookmark.h
+++ b/apps/bookmark.h
@@ -29,11 +29,17 @@ enum {
BOOKMARK_USB_CONNECTED = 1
};
+enum {
+ BOOKMARK_CANCEL,
+ BOOKMARK_DONT_RESUME,
+ BOOKMARK_DO_RESUME
+};
+
int bookmark_load_menu(void);
bool bookmark_autobookmark(bool prompt_ok);
bool bookmark_create_menu(void);
bool bookmark_mrb_load(void);
-bool bookmark_autoload(const char* file);
+int bookmark_autoload(const char* file);
bool bookmark_load(const char* file, bool autoload);
bool bookmark_exists(void);
bool bookmark_is_bookmarkable_state(void);
diff --git a/apps/buffering.c b/apps/buffering.c
index 3adbc4a6b9..bf41544c56 100644
--- a/apps/buffering.c
+++ b/apps/buffering.c
@@ -20,7 +20,6 @@
****************************************************************************/
#include "config.h"
#include <string.h>
-#include "strlcpy.h"
#include "system.h"
#include "storage.h"
#include "thread.h"
@@ -71,8 +70,6 @@
/* amount of data to read in one read() call */
#define BUFFERING_DEFAULT_FILECHUNK (1024*32)
-#define BUF_HANDLE_MASK 0x7FFFFFFF
-
enum handle_flags
{
H_CANWRAP = 0x1, /* Handle data may wrap in buffer */
@@ -295,12 +292,11 @@ static int next_handle_id(void)
{
static int cur_handle_id = 0;
- /* Wrap signed int is safe and 0 doesn't happen */
- int next_hid = (cur_handle_id + 1) & BUF_HANDLE_MASK;
- if (next_hid == 0)
- next_hid = 1;
-
- cur_handle_id = next_hid;
+ int next_hid = cur_handle_id + 1;
+ if (next_hid == INT_MAX)
+ cur_handle_id = 0; /* next would overflow; reset the counter */
+ else
+ cur_handle_id = next_hid;
return next_hid;
}
@@ -420,7 +416,8 @@ add_handle(unsigned int flags, size_t data_size, const char *path,
h->signaled = 0; /* Data can be waited for */
/* Save the provided path */
- memcpy(h->path, path, pathsize);
+ if (path)
+ memcpy(h->path, path, pathsize);
/* Return the start of the data area */
*data_out = ringbuf_add(index, handlesize);
@@ -851,8 +848,9 @@ static bool fill_buffer(void)
Return value is the total size (struct + data). */
static int load_image(int fd, const char *path,
struct bufopen_bitmap_data *data,
- size_t bufidx)
+ size_t bufidx, size_t max_size)
{
+ (void)path;
int rc;
struct bitmap *bmp = ringbuf_ptr(bufidx);
struct dim *dim = data->dim;
@@ -866,25 +864,20 @@ static int load_image(int fd, const char *path,
#if (LCD_DEPTH > 1) || defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1)
bmp->maskdata = NULL;
#endif
- int free = (int)MIN(buffer_len - bytes_used(), buffer_len - bufidx)
- - sizeof(struct bitmap);
-
+ const int format = FORMAT_NATIVE | FORMAT_DITHER |
+ FORMAT_RESIZE | FORMAT_KEEP_ASPECT;
#ifdef HAVE_JPEG
if (aa != NULL) {
lseek(fd, aa->pos, SEEK_SET);
- rc = clip_jpeg_fd(fd, aa->size, bmp, free, FORMAT_NATIVE|FORMAT_DITHER|
- FORMAT_RESIZE|FORMAT_KEEP_ASPECT, NULL);
+ rc = clip_jpeg_fd(fd, aa->size, bmp, (int)max_size, format, NULL);
}
else if (strcmp(path + strlen(path) - 4, ".bmp"))
- rc = read_jpeg_fd(fd, bmp, free, FORMAT_NATIVE|FORMAT_DITHER|
- FORMAT_RESIZE|FORMAT_KEEP_ASPECT, NULL);
+ rc = read_jpeg_fd(fd, bmp, (int)max_size, format, NULL);
else
#endif
- rc = read_bmp_fd(fd, bmp, free, FORMAT_NATIVE|FORMAT_DITHER|
- FORMAT_RESIZE|FORMAT_KEEP_ASPECT, NULL);
+ rc = read_bmp_fd(fd, bmp, (int)max_size, format, NULL);
return rc + (rc > 0 ? sizeof(struct bitmap) : 0);
- (void)path;
}
#endif /* HAVE_ALBUMART */
@@ -970,20 +963,36 @@ int bufopen(const char *file, off_t offset, enum data_type type,
size_t size = 0;
#ifdef HAVE_ALBUMART
if (type == TYPE_BITMAP) {
- /* If albumart is embedded, the complete file is not buffered,
- * but only the jpeg part; filesize() would be wrong */
+ /* Bitmaps are resized to the requested dimensions when loaded,
+ * so the file size should not be used as it may be too large
+ * or too small */
struct bufopen_bitmap_data *aa = user_data;
- if (aa->embedded_albumart)
- size = aa->embedded_albumart->size;
- }
+ size = BM_SIZE(aa->dim->width, aa->dim->height, FORMAT_NATIVE, false);
+ size += sizeof(struct bitmap);
+
+#ifdef HAVE_JPEG
+ /* JPEG loading requires extra memory
+ * TODO: don't add unncessary overhead for .bmp images! */
+ size += JPEG_DECODE_OVERHEAD;
#endif
+ /* resize_on_load requires space for 1 line + 2 spare lines */
+#ifdef HAVE_LCD_COLOR
+ size += sizeof(struct uint32_argb) * 3 * aa->dim->width;
+#else
+ size += sizeof(uint32_t) * 3 * aa->dim->width;
+#endif
+ }
+#endif /* HAVE_ALBUMART */
if (size == 0)
size = filesize(fd);
unsigned int hflags = 0;
if (type == TYPE_PACKET_AUDIO || type == TYPE_CODEC)
- hflags = H_CANWRAP;
+ hflags |= H_CANWRAP;
+ /* Bitmaps need their space allocated up front */
+ if (type == TYPE_BITMAP)
+ hflags |= H_ALLOCALL;
size_t adjusted_offset = offset;
if (adjusted_offset > size)
@@ -1034,7 +1043,7 @@ int bufopen(const char *file, off_t offset, enum data_type type,
#ifdef HAVE_ALBUMART
if (type == TYPE_BITMAP) {
/* Bitmap file: we load the data instead of the file */
- int rc = load_image(fd, file, user_data, data);
+ int rc = load_image(fd, file, user_data, data, padded_size);
if (rc <= 0) {
handle_id = ERR_FILE_ERROR;
} else {
@@ -1433,62 +1442,6 @@ ssize_t bufgetdata(int handle_id, size_t size, void **data)
return size;
}
-ssize_t bufgettail(int handle_id, size_t size, void **data)
-{
- if (thread_self() != buffering_thread_id)
- return ERR_WRONG_THREAD; /* only from buffering thread */
-
- /* We don't support tail requests of > guardbuf_size, for simplicity */
- if (size > GUARD_BUFSIZE)
- return ERR_INVALID_VALUE;
-
- const struct memory_handle *h = find_handle(handle_id);
- if (!h)
- return ERR_HANDLE_NOT_FOUND;
-
- if (h->end >= h->filesize) {
- size_t tidx = ringbuf_sub_empty(h->widx, size);
-
- if (tidx + size > buffer_len) {
- size_t copy_n = tidx + size - buffer_len;
- memcpy(guard_buffer, ringbuf_ptr(0), copy_n);
- }
-
- *data = ringbuf_ptr(tidx);
- }
- else {
- size = ERR_HANDLE_NOT_DONE;
- }
-
- return size;
-}
-
-ssize_t bufcuttail(int handle_id, size_t size)
-{
- if (thread_self() != buffering_thread_id)
- return ERR_WRONG_THREAD; /* only from buffering thread */
-
- struct memory_handle *h = find_handle(handle_id);
- if (!h)
- return ERR_HANDLE_NOT_FOUND;
-
- if (h->end >= h->filesize) {
- /* Cannot trim to before read position */
- size_t available = h->end - MAX(h->start, h->pos);
- if (available < size)
- size = available;
-
- h->widx = ringbuf_sub_empty(h->widx, size);
- h->filesize -= size;
- h->end -= size;
- } else {
- size = ERR_HANDLE_NOT_DONE;
- }
-
- return size;
-}
-
-
/*
SECONDARY EXPORTED FUNCTIONS
============================
diff --git a/apps/buffering.h b/apps/buffering.h
index 1a75d865ae..bc47d6b1a1 100644
--- a/apps/buffering.h
+++ b/apps/buffering.h
@@ -46,8 +46,7 @@ enum data_type {
#define ERR_FILE_ERROR -4
#define ERR_HANDLE_NOT_DONE -5
#define ERR_UNSUPPORTED_TYPE -6
-#define ERR_WRONG_THREAD -7
-#define ERR_BITMAP_TOO_LARGE -8
+#define ERR_BITMAP_TOO_LARGE -7
/* Initialise the buffering subsystem */
void buffering_init(void) INIT_ATTR;
@@ -68,8 +67,6 @@ bool buffering_reset(char *buf, size_t buflen);
* bufftell : Return the handle's file read position
* bufread : Copy data from a handle to a buffer
* bufgetdata: Obtain a pointer for linear access to a "size" amount of data
- * bufgettail: Out-of-band get the last size bytes of a handle.
- * bufcuttail: Out-of-band remove the trailing 'size' bytes of a handle.
*
* NOTE: bufread and bufgetdata will block the caller until the requested
* amount of data is ready (unless EOF is reached).
@@ -85,8 +82,6 @@ int bufadvance(int handle_id, off_t offset);
off_t bufftell(int handle_id);
ssize_t bufread(int handle_id, size_t size, void *dest);
ssize_t bufgetdata(int handle_id, size_t size, void **data);
-ssize_t bufgettail(int handle_id, size_t size, void **data);
-ssize_t bufcuttail(int handle_id, size_t size);
/***************************************************************************
* SECONDARY FUNCTIONS
diff --git a/apps/codec_thread.c b/apps/codec_thread.c
index 807c0b6e8e..999b844349 100644
--- a/apps/codec_thread.c
+++ b/apps/codec_thread.c
@@ -183,14 +183,16 @@ const char * get_codec_filename(int cod_spec)
/* Can choose decoder or encoder if one available */
int type = cod_spec & CODEC_TYPE_MASK;
int afmt = cod_spec & CODEC_AFMT_MASK;
-
+ int tmp_fmt = afmt;
if ((unsigned)afmt >= AFMT_NUM_CODECS)
+ {
type = AFMT_UNKNOWN | (type & CODEC_TYPE_MASK);
-
+ tmp_fmt = AFMT_UNKNOWN;
+ }
fname = (type == CODEC_TYPE_ENCODER) ?
- audio_formats[afmt].codec_enc_root_fn :
- audio_formats[afmt].codec_root_fn;
-
+ audio_formats[tmp_fmt].codec_enc_root_fn :
+ audio_formats[tmp_fmt].codec_root_fn;
+
logf("%s: %d - %s",
(type == CODEC_TYPE_ENCODER) ? "Encoder" : "Decoder",
afmt, fname ? fname : "<unknown>");
diff --git a/apps/codecs.c b/apps/codecs.c
index 4d2dd34ce0..9f34d26e14 100644
--- a/apps/codecs.c
+++ b/apps/codecs.c
@@ -203,8 +203,9 @@ static int codec_load_ram(struct codec_api *api)
return CODEC_ERROR;
}
- if (hdr->api_version > CODEC_API_VERSION
- || hdr->api_version < CODEC_MIN_API_VERSION) {
+ if (hdr->api_version != CODEC_API_VERSION ||
+ c_hdr->api_size > sizeof(struct codec_api))
+ {
logf("codec api version error");
lc_close(curr_handle);
curr_handle = NULL;
diff --git a/apps/core_keymap.c b/apps/core_keymap.c
new file mode 100644
index 0000000000..89e7913c33
--- /dev/null
+++ b/apps/core_keymap.c
@@ -0,0 +1,95 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2020 by William Wilgus
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "action.h"
+#include "core_alloc.h"
+#include "core_keymap.h"
+
+/*#define LOGF_ENABLE*/
+#include "logf.h"
+
+#if !defined(__PCTOOL__) || defined(CHECKWPS)
+int core_set_keyremap(struct button_mapping* core_keymap, int count)
+{
+ return action_set_keymap(core_keymap, count);
+}
+
+static int open_key_remap(const char *filename, int *countp)
+{
+ int fd = open(filename, O_RDONLY);
+ if (fd < 0)
+ return fd;
+
+ size_t fsize = filesize(fd);
+ int count = fsize / sizeof(struct button_mapping);
+ if (count == 0 || (size_t)(count * sizeof(struct button_mapping)) != fsize)
+ {
+ logf("core_keyremap: bad filesize %d / %lu", count, (unsigned long)fsize);
+ goto error;
+ }
+
+ struct button_mapping header;
+ if(read(fd, &header, sizeof(header)) != (ssize_t)sizeof(header))
+ {
+ logf("core_keyremap: read error");
+ goto error;
+ }
+
+ if (header.action_code != KEYREMAP_VERSION ||
+ header.button_code != KEYREMAP_HEADERID ||
+ header.pre_button_code != count)
+ {
+ logf("core_keyremap: bad header %d", count);
+ goto error;
+ }
+
+ *countp = count - 1;
+ return fd;
+
+ error:
+ close(fd);
+ return -1;
+}
+
+int core_load_key_remap(const char *filename)
+{
+ int count = 0; /* gcc falsely believes this may be used uninitialized */
+ int fd = open_key_remap(filename, &count);
+ if (fd < 0)
+ return -1;
+
+ size_t bufsize = count * sizeof(struct button_mapping);
+ int handle = core_alloc(bufsize);
+ if (handle > 0)
+ {
+ void *data = core_get_data_pinned(handle);
+
+ if (read(fd, data, bufsize) == (ssize_t)bufsize)
+ count = action_set_keymap_handle(handle, count);
+
+ core_put_data_pinned(data);
+ }
+
+ close(fd);
+ return count;
+}
+
+#endif /* !defined(__PCTOOL__) */
diff --git a/apps/core_keymap.h b/apps/core_keymap.h
new file mode 100644
index 0000000000..2077daa685
--- /dev/null
+++ b/apps/core_keymap.h
@@ -0,0 +1,65 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2020 by William Wilgus
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#ifndef CORE_KEYMAP_H
+#define CORE_KEYMAP_H
+
+#include <stdbool.h>
+#include <inttypes.h>
+#include "config.h"
+#include "action.h"
+#define KEYREMAP_VERSION 1
+#define KEYREMAP_HEADERID (LAST_ACTION_PLACEHOLDER | (TARGET_ID << 8))
+
+/* If exists remap file will be loaded at startup */
+#define CORE_KEYREMAP_FILE ROCKBOX_DIR "/keyremap.kmf"
+
+/* Allocates core buffer, copies keymap to allow buttons for actions to be remapped*/
+int core_set_keyremap(struct button_mapping* core_keymap, int count);
+
+/* load a remap file to allow buttons for actions to be remapped */
+int core_load_key_remap(const char *filename);
+
+/*
+ * entries consist of 3 int [action, button, prebtn]
+ * the header (VERSION, LAST_DEFINED_ACTION, count) is stripped by open_key_remap
+ *
+ * context look up table is at the beginning
+ * action_code contains (context | CONTEXT_REMAPPED)
+ * button_code contains index of first remapped action for the matched context
+ * prebtn_code contains count of actions in this remapped context
+ * [-1] REMAP_VERSION, REMAP_HEADERID, entry count(9) / DISCARDED AFTER LOAD
+ * [0] CORE_CONTEXT_REMAP(ctx1), offset1=(3), count=(1)
+ * [1] CORE_CONTEXT_REMAP(ctx2, offset2=(5), count=(2)
+ * [2] sentinel, 0, 0
+ * [3] act0, btn, 0
+ * [4] sentinel 0, 0
+ * [5] act1, btn, 0
+ * [6] act2, btn1
+ * [7] sentinel, 0, 0
+ *
+ * Note:
+ * last entry of each group is always the sentinel [CONTEXT_STOPSEARCHING, BUTTON_NONE, BUTTON_NONE]
+ * contexts must match exactly -- re-mapped contexts run before the built in w/ fall through contexts
+ * ie. you can't remap std_context and expect it to match std_context actions from the WPS context.
+ */
+
+#endif /* CORE_KEYMAP_H */
+
diff --git a/apps/cuesheet.c b/apps/cuesheet.c
index a9180a70d4..bbdc93746e 100644
--- a/apps/cuesheet.c
+++ b/apps/cuesheet.c
@@ -58,28 +58,28 @@ static bool search_for_cuesheet(const char *path, struct cuesheet_file *cue_file
slash_cuepath = &cuepath[slash - path];
dot = strrchr(slash_cuepath, '.');
if (dot)
- strlcpy(dot, ".cue", MAX_PATH - (dot-cuepath));
+ strmemccpy(dot, ".cue", MAX_PATH - (dot-cuepath));
if (!dot || !file_exists(cuepath))
{
strcpy(cuepath, CUE_DIR);
if (strlcat(cuepath, slash, MAX_PATH) >= MAX_PATH)
- goto skip; /* overflow */
- char *dot = strrchr(cuepath, '.');
+ goto skip; /* overflow */
+ dot = strrchr(cuepath, '.');
strcpy(dot, ".cue");
if (!file_exists(cuepath))
{
skip:
if ((len+4) >= MAX_PATH)
return false;
- strlcpy(cuepath, path, MAX_PATH);
+ strmemccpy(cuepath, path, MAX_PATH);
strlcat(cuepath, ".cue", MAX_PATH);
if (!file_exists(cuepath))
return false;
}
}
- strlcpy(cue_file->path, cuepath, MAX_PATH);
+ strmemccpy(cue_file->path, cuepath, MAX_PATH);
return true;
}
@@ -91,7 +91,7 @@ bool look_for_cuesheet_file(struct mp3entry *track_id3, struct cuesheet_file *cu
cue_file->pos = track_id3->embedded_cuesheet.pos;
cue_file->size = track_id3->embedded_cuesheet.size;
cue_file->encoding = track_id3->embedded_cuesheet.encoding;
- strlcpy(cue_file->path, track_id3->path, MAX_PATH);
+ strmemccpy(cue_file->path, track_id3->path, MAX_PATH);
return true;
}
@@ -118,6 +118,86 @@ static char *get_string(const char *line)
return start;
}
+static unsigned long parse_cue_index(const char *line)
+{
+ /* assumes strncmp(line, "INDEX 01", 8) & NULL terminated string */
+ /* INDEX 01 MM:SS:FF\0 (00:00:00\0 - 99:99:99\0)*/
+ const unsigned field_m[3] = {60 * 1000, 1000, 13}; /* MM:SS:~FF*/
+ const unsigned field_max[3] = {30000, 59, 74}; /* MM, SS, FF */
+ const char f_sep = ':';
+ int field = -1;
+ unsigned long offset = 0; /* ms from start of track */
+ unsigned long value = 0;
+ while (*line)
+ {
+ if (!isdigit(*line)) /* search for numbers */
+ {
+ line++;
+ continue;
+ }
+
+ while (isdigit(*line))
+ {
+ value = 10 * value + (*line - '0');
+ if (field >= 0 && value > field_max[field]) /* Sanity check bail early */
+ return 0;
+ line++;
+ }
+
+ if (field < 0) /*Filter INDEX 01*/
+ {
+ /* safe to assume value == 1 */
+ }
+ else if (field <= 2)
+ {
+ while(*line && *line != f_sep)
+ line++;
+
+ if (*line || field == 2) /* if *line valid we found f_sep */
+ offset += (unsigned long) field_m[field] * value;
+ }
+ else
+ break;
+
+ value = 0;
+ field++;
+ }
+
+ return offset;
+}
+
+enum eCS_SUPPORTED_TAGS {
+ eCS_TRACK = 0, eCS_INDEX_01, eCS_TITLE,
+ eCS_PERFORMER, eCS_SONGWRITER, eCS_FILE,
+ eCS_COUNT_TAGS_COUNT, eCS_NOTFOUND = -1
+};
+
+static enum eCS_SUPPORTED_TAGS cuesheet_tag_get_option(const char *option)
+{
+ #define CS_OPTN(str) {str, sizeof(str)-1}
+ static const struct cs_option_t {
+ const char *str;
+ const int len;
+ } cs_options[eCS_COUNT_TAGS_COUNT + 1] = {
+ [eCS_TRACK] = CS_OPTN("TRACK"),
+ [eCS_INDEX_01] = CS_OPTN("INDEX 01"),
+ [eCS_TITLE] =CS_OPTN("TITLE"),
+ [eCS_PERFORMER] =CS_OPTN("PERFORMER"),
+ [eCS_SONGWRITER] =CS_OPTN("SONGWRITER"),
+ [eCS_FILE] =CS_OPTN("FILE"),
+ [eCS_COUNT_TAGS_COUNT] = {NULL, 0} /*SENTINEL*/
+ };
+
+ const struct cs_option_t *op;
+ for (int i=0; ((op=&cs_options[i]))->str != NULL; i++)
+ {
+ if (strncmp(option, op->str, op->len) == 0)
+ return i;
+ }
+ return eCS_NOTFOUND;
+#undef CS_OPTN
+}
+
/* parse cuesheet "cue_file" and store the information in "cue" */
bool parse_cuesheet(struct cuesheet_file *cue_file, struct cuesheet *cue)
{
@@ -143,22 +223,25 @@ bool parse_cuesheet(struct cuesheet_file *cue_file, struct cuesheet *cue)
/* Look for a Unicode BOM */
unsigned char bom_read = 0;
- read(fd, line, BOM_UTF_8_SIZE);
- if(!memcmp(line, BOM_UTF_8, BOM_UTF_8_SIZE))
- {
- char_enc = CHAR_ENC_UTF_8;
- bom_read = BOM_UTF_8_SIZE;
- }
- else if(!memcmp(line, BOM_UTF_16_LE, BOM_UTF_16_SIZE))
- {
- char_enc = CHAR_ENC_UTF_16_LE;
- bom_read = BOM_UTF_16_SIZE;
- }
- else if(!memcmp(line, BOM_UTF_16_BE, BOM_UTF_16_SIZE))
+ if (read(fd, line, BOM_UTF_8_SIZE) > 0)
{
- char_enc = CHAR_ENC_UTF_16_BE;
- bom_read = BOM_UTF_16_SIZE;
+ if(!memcmp(line, BOM_UTF_8, BOM_UTF_8_SIZE))
+ {
+ char_enc = CHAR_ENC_UTF_8;
+ bom_read = BOM_UTF_8_SIZE;
+ }
+ else if(!memcmp(line, BOM_UTF_16_LE, BOM_UTF_16_SIZE))
+ {
+ char_enc = CHAR_ENC_UTF_16_LE;
+ bom_read = BOM_UTF_16_SIZE;
+ }
+ else if(!memcmp(line, BOM_UTF_16_BE, BOM_UTF_16_SIZE))
+ {
+ char_enc = CHAR_ENC_UTF_16_BE;
+ bom_read = BOM_UTF_16_SIZE;
+ }
}
+
if (bom_read < BOM_UTF_8_SIZE)
lseek(fd, cue_file->pos + bom_read, SEEK_SET);
if (is_embedded)
@@ -198,12 +281,18 @@ bool parse_cuesheet(struct cuesheet_file *cue_file, struct cuesheet *cue)
}
s = skip_whitespace(line);
- if (!strncmp(s, "TRACK", 5))
+/* RECOGNIZED TAGS ***********************
+* eCS_TRACK = 0, eCS_INDEX_01, eCS_TITLE,
+* eCS_PERFORMER, eCS_SONGWRITER, eCS_FILE,
+*/
+ enum eCS_SUPPORTED_TAGS option = cuesheet_tag_get_option(s);
+ if (option == eCS_TRACK)
{
cue->track_count++;
}
- else if (!strncmp(s, "INDEX 01", 8))
+ else if (option == eCS_INDEX_01)
{
+#if 0
s = strchr(s,' ');
s = skip_whitespace(s);
s = strchr(s,' ');
@@ -213,11 +302,11 @@ bool parse_cuesheet(struct cuesheet_file *cue_file, struct cuesheet *cue)
cue->tracks[cue->track_count-1].offset += 1000 * atoi(s);
s = strchr(s,':') + 1;
cue->tracks[cue->track_count-1].offset += 13 * atoi(s);
+#else
+ cue->tracks[cue->track_count-1].offset = parse_cue_index(s);
+#endif
}
- else if (!strncmp(s, "TITLE", 5)
- || !strncmp(s, "PERFORMER", 9)
- || !strncmp(s, "SONGWRITER", 10)
- || !strncmp(s, "FILE", 4))
+ else if (option != eCS_NOTFOUND)
{
char *dest = NULL;
char *string = get_string(s);
@@ -227,24 +316,24 @@ bool parse_cuesheet(struct cuesheet_file *cue_file, struct cuesheet *cue)
size_t count = MAX_NAME*3 + 1;
size_t count8859 = MAX_NAME;
- switch (*s)
+ switch (option)
{
- case 'T': /* TITLE */
+ case eCS_TITLE: /* TITLE */
dest = (cue->track_count <= 0) ? cue->title :
cue->tracks[cue->track_count-1].title;
break;
- case 'P': /* PERFORMER */
+ case eCS_PERFORMER: /* PERFORMER */
dest = (cue->track_count <= 0) ? cue->performer :
cue->tracks[cue->track_count-1].performer;
break;
- case 'S': /* SONGWRITER */
+ case eCS_SONGWRITER: /* SONGWRITER */
dest = (cue->track_count <= 0) ? cue->songwriter :
cue->tracks[cue->track_count-1].songwriter;
break;
- case 'F': /* FILE */
+ case eCS_FILE: /* FILE */
if (is_embedded || cue->track_count > 0)
break;
@@ -252,9 +341,19 @@ bool parse_cuesheet(struct cuesheet_file *cue_file, struct cuesheet *cue)
count = MAX_PATH;
count8859 = MAX_PATH/3;
break;
+ case eCS_TRACK:
+ /*Fall-Through*/
+ case eCS_INDEX_01:
+ /*Fall-Through*/
+ case eCS_COUNT_TAGS_COUNT:
+ /*Fall-Through*/
+ case eCS_NOTFOUND: /*Shouldn't happen*/
+ logf(HZ * 2, "Bad Tag %d @ %s", (int) option, __func__);
+ dest = NULL;
+ break;
}
- if (dest)
+ if (dest)
{
if (char_enc == CHAR_ENC_ISO_8859_1)
{
@@ -264,10 +363,11 @@ bool parse_cuesheet(struct cuesheet_file *cue_file, struct cuesheet *cue)
}
else
{
- strlcpy(dest, string, count);
+ strmemccpy(dest, string, count);
}
- }
+ }
}
+
if (is_embedded)
{
bytes_left -= line_len;
@@ -286,7 +386,7 @@ bool parse_cuesheet(struct cuesheet_file *cue_file, struct cuesheet *cue)
strcpy(cue->file, cue->path);
char *slash = strrchr(cue->file, '/');
if (!slash++) slash = cue->file;
- strlcpy(slash, line, MAX_PATH - (slash - cue->file));
+ strmemccpy(slash, line, MAX_PATH - (slash - cue->file));
}
/* If some songs don't have performer info, we copy the cuesheet performer */
@@ -294,10 +394,10 @@ bool parse_cuesheet(struct cuesheet_file *cue_file, struct cuesheet *cue)
for (i = 0; i < cue->track_count; i++)
{
if (*(cue->tracks[i].performer) == '\0')
- strlcpy(cue->tracks[i].performer, cue->performer, MAX_NAME*3);
+ strmemccpy(cue->tracks[i].performer, cue->performer, MAX_NAME*3);
if (*(cue->tracks[i].songwriter) == '\0')
- strlcpy(cue->tracks[i].songwriter, cue->songwriter, MAX_NAME*3);
+ strmemccpy(cue->tracks[i].songwriter, cue->songwriter, MAX_NAME*3);
}
return true;
@@ -341,7 +441,7 @@ static const char* list_get_name_cb(int selected_item,
struct cuesheet *cue = (struct cuesheet *)data;
if (selected_item & 1)
- strlcpy(buffer, cue->tracks[selected_item/2].title, buffer_len);
+ strmemccpy(buffer, cue->tracks[selected_item/2].title, buffer_len);
else
snprintf(buffer, buffer_len, "%02d. %s", selected_item/2+1,
cue->tracks[selected_item/2].performer);
@@ -381,7 +481,7 @@ void browse_cuesheet(struct cuesheet *cue)
{
gui_synclist_draw(&lists);
action = get_action(CONTEXT_LIST,TIMEOUT_BLOCK);
- if (gui_synclist_do_button(&lists, &action, LIST_WRAP_UNLESS_HELD))
+ if (gui_synclist_do_button(&lists, &action))
continue;
switch (action)
{
@@ -408,7 +508,7 @@ void browse_cuesheet(struct cuesheet *cue)
/* check that this cue is the same one that would be found by
a search from playback */
char file[MAX_PATH];
- strlcpy(file, cue->file, MAX_PATH);
+ strmemccpy(file, cue->file, MAX_PATH);
if (!strcmp(cue->path, file) || /* if embedded */
(search_for_cuesheet(file, &cue_file) &&
@@ -423,6 +523,8 @@ void browse_cuesheet(struct cuesheet *cue)
case ACTION_STD_CANCEL:
done = true;
+ default:
+ break;
}
}
}
@@ -435,7 +537,7 @@ bool display_cuesheet_content(char* filename)
if (!cue || bufsize < sizeof(struct cuesheet))
return false;
- strlcpy(cue_file.path, filename, MAX_PATH);
+ strmemccpy(cue_file.path, filename, MAX_PATH);
cue_file.pos = 0;
cue_file.size = 0;
@@ -454,7 +556,7 @@ bool display_cuesheet_content(char* filename)
bool curr_cuesheet_skip(struct cuesheet *cue, int direction, unsigned long curr_pos)
{
int track = cue_find_current_track(cue, curr_pos);
-
+
if (direction >= 0 && track == cue->track_count - 1)
{
/* we want to get out of the cuesheet */
@@ -465,12 +567,12 @@ bool curr_cuesheet_skip(struct cuesheet *cue, int direction, unsigned long curr_
if (!(direction <= 0 && track == 0))
{
/* If skipping forward, skip to next cuesheet segment. If skipping
- backward before DEFAULT_SKIP_TRESH milliseconds have elapsed, skip
+ backward before DEFAULT_SKIP_THRESH milliseconds have elapsed, skip
to previous cuesheet segment. If skipping backward after
- DEFAULT_SKIP_TRESH seconds have elapsed, skip to the start of the
+ DEFAULT_SKIP_THRESH seconds have elapsed, skip to the start of the
current cuesheet segment */
- if (direction == 1 ||
- ((curr_pos - cue->tracks[track].offset) < DEFAULT_SKIP_TRESH))
+ if (direction == 1 ||
+ ((curr_pos - cue->tracks[track].offset) < DEFAULT_SKIP_THRESH))
{
track += direction;
}
diff --git a/apps/debug_menu.c b/apps/debug_menu.c
index 9bbbfaec99..245cd38627 100644
--- a/apps/debug_menu.c
+++ b/apps/debug_menu.c
@@ -23,13 +23,12 @@
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
-#include <string.h>
+#include <string-extra.h>
#include "lcd.h"
#include "lang.h"
#include "menu.h"
#include "debug_menu.h"
#include "kernel.h"
-#include "structec.h"
#include "action.h"
#include "debug.h"
#include "thread.h"
@@ -39,7 +38,6 @@
#include "audio.h"
#include "settings.h"
#include "list.h"
-#include "statusbar.h"
#include "dir.h"
#include "panic.h"
#include "screens.h"
@@ -112,6 +110,10 @@
#include "pmu-target.h"
#endif
+#ifdef SANSA_CONNECT
+#include "avr-sansaconnect.h"
+#endif
+
#ifdef HAVE_USBSTACK
#include "usb_core.h"
#endif
@@ -124,12 +126,18 @@
#if defined(HAVE_BOOTDATA) && !defined(SIMULATOR)
#include "bootdata.h"
+#include "multiboot.h"
+#include "rbpaths.h"
+#include "pathfuncs.h"
+#include "rb-loader.h"
#endif
+#define SCREEN_MAX_CHARS (LCD_WIDTH / SYSFONT_WIDTH)
+
static const char* threads_getname(int selected_item, void *data,
char *buffer, size_t buffer_len)
{
- (void)data;
+ int *x_offset = (int*) data;
#if NUM_CORES > 1
if (selected_item < (int)NUM_CORES)
@@ -149,36 +157,80 @@ static const char* threads_getname(int selected_item, void *data,
struct thread_debug_info threadinfo;
if (thread_get_debug_info(selected_item, &threadinfo) > 0)
{
- fmtstr = "%2d:" IF_COP(" (%d)") " %s" IF_PRIO(" %d %d")
+ fmtstr = "%2d:" IF_COP(" (%d)") " %s%n" IF_PRIO(" %d %d")
IFN_SDL(" %2d%%") " %s";
}
-
- snprintf(buffer, buffer_len, fmtstr,
+ int status_len;
+ size_t len = snprintf(buffer, buffer_len, fmtstr,
selected_item,
IF_COP(threadinfo.core,)
threadinfo.statusstr,
+ &status_len,
IF_PRIO(threadinfo.base_priority, threadinfo.current_priority,)
IFN_SDL(threadinfo.stack_usage,)
threadinfo.name);
- return buffer;
+ int start = 0;
+#if LCD_WIDTH <= 128
+ if (len >= SCREEN_MAX_CHARS)
+ {
+ int ch_offset = (*x_offset)%(len-1);
+ int rem = SCREEN_MAX_CHARS - (len - ch_offset);
+ if (rem > 0)
+ ch_offset -= rem;
+
+ if (ch_offset > 0)
+ {
+ /* don't scroll the # and status */
+ status_len++;
+ if ((unsigned int)ch_offset + status_len < buffer_len)
+ memmove(&buffer[ch_offset], &buffer[0], status_len);
+ start = ch_offset;
+ }
+ }
+#else
+ (void) x_offset;
+ (void) len;
+#endif
+ return &buffer[start];
}
static int dbg_threads_action_callback(int action, struct gui_synclist *lists)
{
- (void)lists;
+
if (action == ACTION_NONE)
+ {
+ return ACTION_REDRAW;
+ }
+#if LCD_WIDTH <= 128
+ int *x_offset = ((int*) lists->data);
+ if (action == ACTION_STD_OK)
+ {
+ *x_offset += 1;
action = ACTION_REDRAW;
+ }
+ else if (IS_SYSEVENT(action))
+ {
+ return ACTION_REDRAW;
+ }
+ else if (action != ACTION_UNKNOWN)
+ {
+ *x_offset = 0;
+ }
+#else
+ (void) lists;
+#endif
return action;
}
/* Test code!!! */
static bool dbg_os(void)
{
struct simplelist_info info;
+ int xoffset = 0;
+
simplelist_info_init(&info, IF_COP("Core and ") "Stack usage:",
- MAXTHREADS IF_COP( + NUM_CORES ), NULL);
- info.hide_selection = true;
- info.scroll_all = true;
+ MAXTHREADS IF_COP( + NUM_CORES ), &xoffset);
+ info.scroll_all = false;
info.action_callback = dbg_threads_action_callback;
info.get_name = threads_getname;
return simplelist_show_list(&info);
@@ -291,7 +343,6 @@ static bool dbg_cpuinfo(void)
info.get_name = get_cpuinfo;
info.action_callback = cpuinfo_cb;
info.timeout = HZ;
- info.hide_selection = true;
info.scroll_all = true;
return simplelist_show_list(&info);
}
@@ -326,6 +377,13 @@ static bool dbg_buffering_thread(void)
struct buffering_debug d;
size_t filebuflen = audio_get_filebuflen();
/* This is a size_t, but call it a long so it puts a - when it's bad. */
+#if LCD_WIDTH > 96
+ #define STR_DATAREM "data_rem"
+ const char * const fmt_used = "%s: %6ld/%ld";
+#else /* clipzip, ?*/
+ #define STR_DATAREM "remain"
+ const char * const fmt_used = "%s:%ld/%ld";
+#endif
#ifndef CPU_MULTI_FREQUENCY
boost_ticks = 0;
@@ -362,13 +420,13 @@ static bool dbg_buffering_thread(void)
screens[i].clear_display();
- screens[i].putsf(0, line++, "pcm: %6ld/%ld", (long) bufused, (long) bufsize);
+ screens[i].putsf(0, line++, fmt_used, "pcm", (long) bufused, (long) bufsize);
gui_scrollbar_draw(&screens[i],0, line*8, screens[i].lcdwidth, 6,
bufsize, 0, bufused, HORIZONTAL);
line++;
- screens[i].putsf(0, line++, "alloc: %6ld/%ld", audio_filebufused(),
+ screens[i].putsf(0, line++, fmt_used, "alloc", audio_filebufused(),
(long) filebuflen);
#if LCD_HEIGHT > 80 || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_HEIGHT > 80)
@@ -378,7 +436,7 @@ static bool dbg_buffering_thread(void)
filebuflen, 0, audio_filebufused(), HORIZONTAL);
line++;
- screens[i].putsf(0, line++, "real: %6ld/%ld", (long)d.buffered_data,
+ screens[i].putsf(0, line++, fmt_used, "real", (long)d.buffered_data,
(long)filebuflen);
gui_scrollbar_draw(&screens[i],0, line*8, screens[i].lcdwidth, 6,
@@ -387,7 +445,7 @@ static bool dbg_buffering_thread(void)
}
#endif
- screens[i].putsf(0, line++, "usefl: %6ld/%ld", (long)(d.useful_data),
+ screens[i].putsf(0, line++, fmt_used, "usefl", (long)(d.useful_data),
(long)filebuflen);
#if LCD_HEIGHT > 80 || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_HEIGHT > 80)
@@ -399,7 +457,7 @@ static bool dbg_buffering_thread(void)
}
#endif
- screens[i].putsf(0, line++, "data_rem: %ld", (long)d.data_rem);
+ screens[i].putsf(0, line++, "%s: %ld", STR_DATAREM, (long)d.data_rem);
screens[i].putsf(0, line++, "track count: %2u", audio_track_count());
@@ -438,8 +496,10 @@ static bool dbg_buffering_thread(void)
screens[i].setfont(FONT_UI);
return false;
+#undef STR_DATAREM
}
+#ifdef BUFLIB_DEBUG_PRINT
static const char* bf_getname(int selected_item, void *data,
char *buffer, size_t buffer_len)
{
@@ -459,13 +519,12 @@ static int bf_action_cb(int action, struct gui_synclist* list)
else
{
splash(HZ/1, "Attempting a 64k allocation");
- int handle = core_alloc("test", 64<<10);
+ int handle = core_alloc(64<<10);
splash(HZ/2, (handle > 0) ? "Success":"Fail");
/* for some reason simplelist doesn't allow adding items here if
* info.get_name is given, so use normal list api */
gui_synclist_set_nb_items(list, core_get_num_blocks());
- if (handle > 0)
- core_free(handle);
+ core_free(handle);
}
action = ACTION_REDRAW;
}
@@ -481,6 +540,7 @@ static bool dbg_buflib_allocs(void)
info.timeout = HZ;
return simplelist_show_list(&info);
}
+#endif /* BUFLIB_DEBUG_PRINT */
#if (CONFIG_PLATFORM & PLATFORM_NATIVE)
static const char* dbg_partitions_getname(int selected_item, void *data,
@@ -510,7 +570,6 @@ static bool dbg_partitions(void)
struct simplelist_info info;
simplelist_info_init(&info, "Partition Info", NUM_DRIVES * 4, NULL);
info.selection_size = 2;
- info.hide_selection = true;
info.scroll_all = true;
info.get_name = dbg_partitions_getname;
return simplelist_show_list(&info);
@@ -812,8 +871,7 @@ static int tsc2100debug_action_callback(int action, struct gui_synclist *lists)
if (action == ACTION_STD_OK)
{
*page = (*page+1)%3;
- snprintf(lists->title, 32,
- "tsc2100 registers - Page %d", *page);
+ snprintf((char*)lists->title, 32, "tsc2100 registers - Page %d", *page);
return ACTION_REDRAW;
}
return action;
@@ -821,7 +879,8 @@ static int tsc2100debug_action_callback(int action, struct gui_synclist *lists)
static bool tsc2100_debug(void)
{
int page = 0;
- char title[32] = "tsc2100 registers - Page 0";
+ char title[32];
+ snprintf(title, 32, "tsc2100 registers - Page %d", page);
struct simplelist_info info;
simplelist_info_init(&info, title, 32, &page);
info.timeout = HZ/100;
@@ -872,7 +931,7 @@ static bool view_battery(void)
else
grid = 5;
- lcd_putsf(0, 0, "battery %d.%03dV", power_history[0] / 1000,
+ lcd_putsf(0, 0, "%s %d.%03dV", "Battery", power_history[0] / 1000,
power_history[0] % 1000);
lcd_putsf(0, 1, "%d.%03d-%d.%03dV (%2dmV)",
minv / 1000, minv % 1000, maxv / 1000, maxv % 1000,
@@ -883,7 +942,7 @@ static bool view_battery(void)
grid = 10;
else
grid = 1;
- lcd_putsf(0, 0, "battery %d%%", power_history[0]);
+ lcd_putsf(0, 0, "%s %d%%", "Battery", power_history[0]);
lcd_putsf(0, 1, "%d%%-%d%% (%d %%)", minv, maxv, grid);
#endif
@@ -938,16 +997,16 @@ static bool view_battery(void)
lcd_putsf(0, 0, "Pwr status: %s",
charging_state() ? "charging" : "discharging");
#else
- lcd_puts(0, 0, "Power status: unknown");
+ lcd_putsf(0, 0, "Pwr status: %s", "unknown");
#endif
battery_read_info(&y, &z);
if (y > 0)
- lcd_putsf(0, 1, "Battery: %d.%03d V (%d %%)", y / 1000, y % 1000, z);
+ lcd_putsf(0, 1, "%s: %d.%03d V (%d %%)", "Battery", y / 1000, y % 1000, z);
else if (z > 0)
- lcd_putsf(0, 1, "Battery: %d %%", z);
+ lcd_putsf(0, 1, "%s: %d %%", "Battery", z);
#ifdef ADC_EXT_POWER
y = (adc_read(ADC_EXT_POWER) * EXT_SCALE_FACTOR) / 1000;
- lcd_putsf(0, 2, "External: %d.%03d V", y / 1000, y % 1000);
+ lcd_putsf(0, 2, "%s: %d.%03d V", "External", y / 1000, y % 1000);
#endif
#if CONFIG_CHARGING
#if defined IPOD_NANO || defined IPOD_VIDEO
@@ -961,7 +1020,7 @@ static bool view_battery(void)
usb_pwr ? "present" : "absent");
lcd_putsf(0, 4, "EXT pwr: %s",
ext_pwr ? "present" : "absent");
- lcd_putsf(0, 5, "Battery: %s",
+ lcd_putsf(0, 5, "%s: %s", "Battery",
charging ? "charging" : (usb_pwr||ext_pwr) ? "charged" : "discharging");
lcd_putsf(0, 6, "Dock mode: %s",
dock ? "enabled" : "disabled");
@@ -1015,7 +1074,7 @@ static bool view_battery(void)
lcd_putsf(0, line++, "State: %s", chrgstate_strings[y]);
- lcd_putsf(0, line++, "Battery Switch: %s",
+ lcd_putsf(0, line++, "%s Switch: %s", "Battery",
(st & POWER_INPUT_BATTERY) ? "On" : "Off");
y = chrgraw_adc_voltage();
@@ -1038,11 +1097,11 @@ static bool view_battery(void)
y = battery_adc_temp();
if (y != INT_MIN) {
- lcd_putsf(0, line++, "T Battery: %d\u00b0C (%d\u00b0F)", y,
- (9*y + 160) / 5);
+ lcd_putsf(0, line++, "T %s: %d\u00b0C (%d\u00b0F)",
+ "Battery", y, (9*y + 160) / 5);
} else {
/* Conversion disabled */
- lcd_puts(0, line++, "T Battery: ?");
+ lcd_putsf(0, line++, "T %s: ?", "Battery");
}
#elif defined(HAVE_AS3514) && CONFIG_CHARGING
static const char * const chrgstate_strings[] =
@@ -1069,7 +1128,7 @@ static bool view_battery(void)
y = pmu_read_battery_voltage();
lcd_putsf(17, 1, "RAW: %d.%03d V", y / 1000, y % 1000);
y = pmu_read_battery_current();
- lcd_putsf(0, 2, "Battery current: %d mA", y);
+ lcd_putsf(0, 2, "%s current: %d mA", "Battery", y);
lcd_putsf(0, 3, "PWRCON: %08x %08x", PWRCON, PWRCONEXT);
lcd_putsf(0, 4, "CLKCON: %08x %03x %03x", CLKCON, CLKCON2, CLKCON3);
lcd_putsf(0, 5, "PLL: %06x %06x %06x", PLL0PMS, PLL1PMS, PLL2PMS);
@@ -1091,6 +1150,17 @@ static bool view_battery(void)
y = pmu_read(0x2d + (i << 1)) * 100 + 900;
lcd_putsf(0, 10 + i, "LDO%d: %x / %d mV", i + 1, x, y);
}
+#elif defined(SANSA_CONNECT)
+ lcd_putsf(0, 3, "Charger: %s",
+ charger_inserted() ? "present" : "absent");
+ x = (avr_hid_hdq_read_short(HDQ_REG_TEMP) / 4) - 273;
+ lcd_putsf(0, 4, "%s temperature: %d C", "Battery", x);
+ x = (avr_hid_hdq_read_short(HDQ_REG_AI) * 357) / 200;
+ lcd_putsf(0, 5, "%s current: %d.%01d mA", "Battery", x / 10, x % 10);
+ x = (avr_hid_hdq_read_short(HDQ_REG_AP) * 292) / 20;
+ lcd_putsf(0, 6, "Discharge power: %d.%01d mW", x / 10, x % 10);
+ x = (avr_hid_hdq_read_short(HDQ_REG_SAE) * 292) / 2;
+ lcd_putsf(0, 7, "Available energy: %d.%01d mWh", x / 10, x % 10);
#else
lcd_putsf(0, 3, "Charger: %s",
charger_inserted() ? "present" : "absent");
@@ -1124,13 +1194,17 @@ static bool view_battery(void)
power_history[0] % 1000);
#endif
- lcd_putsf(0, 6, "battery level: %d%%", battery_level());
+ lcd_putsf(0, 6, "%s level: %d%%", "Battery", battery_level());
int time_left = battery_time();
if (time_left >= 0)
lcd_putsf(0, 7, "Est. remain: %d m", time_left);
else
lcd_puts(0, 7, "Estimation n/a");
+
+#if (CONFIG_BATTERY_MEASURE & CURRENT_MEASURE)
+ lcd_putsf(0, 8, "%s current: %d mA", "Battery", battery_current());
+#endif
break;
}
@@ -1174,7 +1248,8 @@ static int disk_callback(int btn, struct gui_synclist *lists)
int *cardnum = (int*)lists->data;
unsigned char card_name[6];
unsigned char pbuf[32];
- char *title = lists->title;
+ /* Casting away const is safe; the buffer is defined as non-const. */
+ char *title = (char *)lists->title;
static const unsigned char i_vmin[] = { 0, 1, 5, 10, 25, 35, 60, 100 };
static const unsigned char i_vmax[] = { 1, 5, 10, 25, 35, 45, 80, 200 };
static const unsigned char * const kbit_units[] = { "kBit/s", "MBit/s", "GBit/s" };
@@ -1204,7 +1279,7 @@ static int disk_callback(int btn, struct gui_synclist *lists)
{
card_name[i] = card_extract_bits(card->cid, (103-8*i), 8);
}
- strlcpy(card_name, card_name, sizeof(card_name));
+ strmemccpy(card_name, card_name, sizeof(card_name));
simplelist_addline(
"%s Rev %d.%d", card_name,
(int) card_extract_bits(card->cid, 63, 4),
@@ -1557,7 +1632,8 @@ static int ata_smart_attr_to_string(
if (len >= name_sz) len = name_sz-1;
slen += len;
}
- snprintf(str+slen, size-slen, "%s", buf);
+
+ strmemccpy(str+slen, buf, size-slen);
}
return 1; /* ok */
@@ -1648,7 +1724,6 @@ static bool dbg_ata_smart(void)
struct simplelist_info info;
simplelist_info_init(&info, "S.M.A.R.T. Data [CONTEXT to dump]", 1, NULL);
info.action_callback = ata_smart_callback;
- info.hide_selection = true;
info.scroll_all = true;
return simplelist_show_list(&info);
}
@@ -1680,10 +1755,16 @@ static bool dbg_identify_info(void)
int fd = creat("/identify_info.bin", 0666);
if(fd >= 0)
{
+ const unsigned short *identify_info = ata_get_identify();
#ifdef ROCKBOX_LITTLE_ENDIAN
- ecwrite(fd, ata_get_identify(), SECTOR_SIZE/2, "s", true);
+ /* this is a pointer to a driver buffer so we can't modify it */
+ for (int i = 0; i < SECTOR_SIZE/2; ++i)
+ {
+ unsigned short word = swap16(identify_info[i]);
+ write(fd, &word, 2);
+ }
#else
- write(fd, ata_get_identify(), SECTOR_SIZE);
+ write(fd, identify_info, SECTOR_SIZE);
#endif
close(fd);
}
@@ -1702,7 +1783,6 @@ static bool dbg_disk_info(void)
info.title = title;
#endif
info.action_callback = disk_callback;
- info.hide_selection = true;
info.scroll_all = true;
return simplelist_show_list(&info);
}
@@ -1711,6 +1791,7 @@ static bool dbg_disk_info(void)
#ifdef HAVE_DIRCACHE
static int dircache_callback(int btn, struct gui_synclist *lists)
{
+ (void)lists;
struct dircache_info info;
dircache_get_info(&info);
@@ -1722,6 +1803,7 @@ static int dircache_callback(int btn, struct gui_synclist *lists)
splash(HZ/2, "Rebuilding cache");
dircache_suspend();
*(int *)lists->data = dircache_resume();
+ /* Fallthrough */
case ACTION_UNKNOWN:
btn = ACTION_NONE;
break;
@@ -1761,7 +1843,6 @@ static int dircache_callback(int btn, struct gui_synclist *lists)
btn = ACTION_REDRAW;
return btn;
- (void)lists;
}
static bool dbg_dircache_info(void)
@@ -1770,7 +1851,6 @@ static bool dbg_dircache_info(void)
int syncbuild = 0;
simplelist_info_init(&info, "Dircache Info", 8, &syncbuild);
info.action_callback = dircache_callback;
- info.hide_selection = true;
info.scroll_all = true;
return simplelist_show_list(&info);
}
@@ -1783,6 +1863,7 @@ static int database_callback(int btn, struct gui_synclist *lists)
(void)lists;
struct tagcache_stat *stat = tagcache_get_stat();
static bool synced = false;
+ static int update_entries = 0;
simplelist_set_line_count(0);
@@ -1790,10 +1871,13 @@ static int database_callback(int btn, struct gui_synclist *lists)
stat->initialized ? "Yes" : "No");
simplelist_addline("DB Ready: %s",
stat->ready ? "Yes" : "No");
+ simplelist_addline("DB Path: %s", stat->db_path);
simplelist_addline("RAM Cache: %s",
stat->ramcache ? "Yes" : "No");
simplelist_addline("RAM: %d/%d B",
stat->ramcache_used, stat->ramcache_allocated);
+ simplelist_addline("Total entries: %d",
+ stat->total_entries);
simplelist_addline("Progress: %d%% (%d entries)",
stat->progress, stat->processed_entries);
simplelist_addline("Curfile: %s",
@@ -1815,12 +1899,19 @@ static int database_callback(int btn, struct gui_synclist *lists)
if (!btn && stat->curentry)
{
synced = true;
- return ACTION_REDRAW;
+ if (update_entries <= stat->processed_entries)
+ {
+ update_entries = stat->processed_entries + 100;
+ return ACTION_REDRAW;
+ }
+ return ACTION_NONE;
}
if (btn == ACTION_STD_CANCEL)
+ {
+ update_entries = 0;
tagcache_screensync_enable(false);
-
+ }
return btn;
}
static bool dbg_tagcache_info(void)
@@ -1828,7 +1919,6 @@ static bool dbg_tagcache_info(void)
struct simplelist_info info;
simplelist_info_init(&info, "Database Info", 8, NULL);
info.action_callback = database_callback;
- info.hide_selection = true;
info.scroll_all = true;
/* Don't do nonblock here, must give enough processing time
@@ -2010,12 +2100,12 @@ static int radio_callback(int btn, struct gui_synclist *lists)
tea5767_dbg_info(&nfo);
simplelist_addline("Philips regs:");
simplelist_addline(
- " Read: %02X %02X %02X %02X %02X",
+ " %s: %02X %02X %02X %02X %02X", "Read",
(unsigned)nfo.read_regs[0], (unsigned)nfo.read_regs[1],
(unsigned)nfo.read_regs[2], (unsigned)nfo.read_regs[3],
(unsigned)nfo.read_regs[4]);
simplelist_addline(
- " Write: %02X %02X %02X %02X %02X",
+ " %s: %02X %02X %02X %02X %02X", "Write",
(unsigned)nfo.write_regs[0], (unsigned)nfo.write_regs[1],
(unsigned)nfo.write_regs[2], (unsigned)nfo.write_regs[3],
(unsigned)nfo.write_regs[4]);
@@ -2081,7 +2171,7 @@ static int radio_callback(int btn, struct gui_synclist *lists)
struct tm* time = gmtime(&seconds);
simplelist_addline(
- "CT:%4d-%02d-%02d %02d:%02d",
+ "CT:%4d-%02d-%02d %02d:%02d:%02d",
time->tm_year + 1900, time->tm_mon + 1, time->tm_mday,
time->tm_hour, time->tm_min, time->tm_sec);
}
@@ -2101,7 +2191,6 @@ static bool dbg_fm_radio(void)
radio_hardware_present() ? "yes" : "no");
info.action_callback = radio_hardware_present()?radio_callback : NULL;
- info.hide_selection = true;
return simplelist_show_list(&info);
}
#endif /* CONFIG_TUNER */
@@ -2137,7 +2226,7 @@ static bool dbg_set_memory_guard(void)
};
int mode = system_memory_guard(MEMGUARD_KEEP);
- set_option( "Catch mem accesses", &mode, INT, names, MAXMEMGUARD, NULL);
+ set_option( "Catch mem accesses", &mode, RB_INT, names, MAXMEMGUARD, NULL);
system_memory_guard(mode);
return false;
@@ -2226,6 +2315,47 @@ static bool cpu_boost_log(void)
lcd_setfont(FONT_UI);
return false;
}
+
+static bool cpu_boost_log_dump(void)
+{
+ int fd;
+ int count = cpu_boost_log_getcount();
+ char *str = cpu_boost_log_getlog_first();
+
+ splashf(HZ, "Boost Log File Dumped");
+
+ /* nothing to print ? */
+ if(count == 0)
+ return false;
+
+#if CONFIG_RTC
+ char fname[MAX_PATH];
+ struct tm *nowtm = get_time();
+ fd = open_pathfmt(fname, sizeof(fname), O_CREAT|O_WRONLY|O_TRUNC,
+ "%s/boostlog_%04d%02d%02d%02d%02d%02d.txt", ROCKBOX_DIR,
+ nowtm->tm_year + 1900, nowtm->tm_mon + 1, nowtm->tm_mday,
+ nowtm->tm_hour, nowtm->tm_min, nowtm->tm_sec);
+#else
+ fd = open(ROCKBOX_DIR "/boostlog.txt", O_CREAT|O_WRONLY|O_TRUNC, 0666);
+#endif
+ if(-1 != fd) {
+ for (int i = 0; i < count; i++)
+ {
+ if (!str)
+ str = cpu_boost_log_getlog_next();
+ if (str)
+ {
+ fdprintf(fd, "%s\n", str);
+ str = NULL;
+ }
+ }
+
+ close(fd);
+ return true;
+ }
+
+ return false;
+}
#endif
#if (defined(HAVE_WHEEL_ACCELERATION) && (CONFIG_KEYPAD==IPOD_4G_PAD) \
@@ -2328,7 +2458,6 @@ static bool dbg_talk(void)
else
simplelist_info_init(&list, "Voice Information:", 2, &data);
list.scroll_all = true;
- list.hide_selection = true;
list.timeout = HZ;
list.get_name = dbg_talk_get_name;
@@ -2368,7 +2497,6 @@ static bool dbg_isp1583(void)
isp1583.scroll_all = true;
simplelist_info_init(&isp1583, "ISP1583", dbg_usb_num_items(), NULL);
isp1583.timeout = HZ/100;
- isp1583.hide_selection = true;
isp1583.get_name = dbg_usb_item;
isp1583.action_callback = isp1583_action_callback;
return simplelist_show_list(&isp1583);
@@ -2394,99 +2522,52 @@ static bool dbg_pic(void)
pic.scroll_all = true;
simplelist_info_init(&pic, "PIC", pic_dbg_num_items(), NULL);
pic.timeout = HZ/100;
- pic.hide_selection = true;
pic.get_name = pic_dbg_item;
pic.action_callback = pic_action_callback;
return simplelist_show_list(&pic);
}
#endif
-static bool dbg_skin_engine(void)
-{
- struct simplelist_info info;
- int i, total = 0;
-#if defined(HAVE_BACKDROP_IMAGE)
- int ref_count;
- char *path;
- size_t bytes;
- int path_prefix_len = strlen(ROCKBOX_DIR "/wps/");
-#endif
- simplelist_info_init(&info, "Skin engine usage", 0, NULL);
- simplelist_set_line_count(0);
- info.hide_selection = true;
- FOR_NB_SCREENS(j) {
-#if NB_SCREENS > 1
- simplelist_addline("%s display:",
- j == 0 ? "Main" : "Remote");
-#endif
- for (i = 0; i < skin_get_num_skins(); i++) {
- struct skin_stats *stats = skin_get_stats(i, j);
- if (stats->buflib_handles)
- {
- simplelist_addline("Skin ID: %d, %d allocations",
- i, stats->buflib_handles);
- simplelist_addline("\tskin: %d bytes",
- stats->tree_size);
- simplelist_addline("\tImages: %d bytes",
- stats->images_size);
- simplelist_addline("\tTotal: %d bytes",
- stats->tree_size + stats->images_size);
- total += stats->tree_size + stats->images_size;
- }
- }
- }
- simplelist_addline("Skin total usage: %d bytes", total);
-#if defined(HAVE_BACKDROP_IMAGE)
- simplelist_addline("Backdrop Images:");
- i = 0;
- while (skin_backdrop_get_debug(i++, &path, &ref_count, &bytes)) {
- if (ref_count > 0) {
-
- if (!strncasecmp(path, ROCKBOX_DIR "/wps/", path_prefix_len))
- path += path_prefix_len;
- simplelist_addline("%s", path);
- simplelist_addline("\tref_count: %d", ref_count);
- simplelist_addline("\tsize: %d", bytes);
- total += bytes;
- }
- }
- simplelist_addline("Total usage: %d bytes", total);
-#endif
- return simplelist_show_list(&info);
-}
-
#if defined(HAVE_BOOTDATA) && !defined(SIMULATOR)
static bool dbg_boot_data(void)
{
- unsigned int crc = 0;
struct simplelist_info info;
info.scroll_all = true;
simplelist_info_init(&info, "Boot data", 1, NULL);
simplelist_set_line_count(0);
- crc = crc_32(boot_data.payload, boot_data.length, 0xffffffff);
-#if defined(HAVE_MULTIBOOT)
- int boot_volume = 0;
- if(crc == boot_data.crc)
+
+ if (!boot_data_valid)
{
- boot_volume = boot_data.boot_volume; /* boot volume contained in uint8_t payload */
+ simplelist_addline("Boot data invalid");
+ simplelist_addline("Magic[0]: %08lx", boot_data.magic[0]);
+ simplelist_addline("Magic[1]: %08lx", boot_data.magic[1]);
+ simplelist_addline("Length: %lu", boot_data.length);
}
- simplelist_addline("Boot Volume: <%lu>", boot_volume);
- simplelist_addline("");
-#endif
+ else
+ {
+ simplelist_addline("Boot data valid");
+ simplelist_addline("Version: %d", (int)boot_data.version);
+
+ if (boot_data.version == 0)
+ {
+ simplelist_addline("Boot volume: %d", (int)boot_data._boot_volume);
+ }
+ else if (boot_data.version == 1)
+ {
+ simplelist_addline("Boot drive: %d", (int)boot_data.boot_drive);
+ simplelist_addline("Boot partition: %d", (int)boot_data.boot_partition);
+ }
+ simplelist_addline("Boot path: %s%s/%s", root_realpath(), BOOTDIR, BOOTFILE);
+ }
+
simplelist_addline("Bootdata RAW:");
- if (crc != boot_data.crc)
- simplelist_addline("Magic: %.8s", boot_data.magic);
- simplelist_addline("Length: %lu", boot_data.length);
- simplelist_addline("CRC: %lx", boot_data.crc);
- (crc == boot_data.crc) ? simplelist_addline("CRC: OK!") :
- simplelist_addline("CRC: BAD");
- for (unsigned i = 0; i < boot_data.length; i += 4)
+ for (size_t i = 0; i < boot_data.length; i += 4)
{
- simplelist_addline("%02x: %02x %02x %02x %02x", i, boot_data.payload[i],
- boot_data.payload[i+1], boot_data.payload[i+2], boot_data.payload[i+3]);
+ simplelist_addline("%02x: %02x %02x %02x %02x", i,
+ boot_data.payload[i + 0], boot_data.payload[i + 1],
+ boot_data.payload[i + 2], boot_data.payload[i + 3]);
}
- info.hide_selection = true;
return simplelist_show_list(&info);
}
#endif /* defined(HAVE_BOOTDATA) && !defined(SIMULATOR) */
@@ -2563,7 +2644,9 @@ static const struct {
#ifdef PM_DEBUG
{ "pm histogram", peak_meter_histogram},
#endif /* PM_DEBUG */
+#ifdef BUFLIB_DEBUG_PRINT
{ "View buflib allocs", dbg_buflib_allocs },
+#endif
#ifndef SIMULATOR
#if CONFIG_TUNER
{ "FM Radio", dbg_fm_radio },
@@ -2588,7 +2671,8 @@ static const struct {
#endif
#endif /* HAVE_USBSTACK */
#ifdef CPU_BOOST_LOGGING
- {"cpu_boost log",cpu_boost_log},
+ {"Show cpu_boost log",cpu_boost_log},
+ {"Dump cpu_boost log",cpu_boost_log_dump},
#endif
#if (defined(HAVE_WHEEL_ACCELERATION) && (CONFIG_KEYPAD==IPOD_4G_PAD) \
&& !defined(IPOD_MINI) && !defined(SIMULATOR))
@@ -2629,6 +2713,24 @@ static const char* menu_get_name(int item, void * data,
return menuitems[item].desc;
}
+static int menu_get_talk(int item, void *data)
+{
+ (void)data;
+ if (global_settings.talk_menu && menuitems[item].desc)
+ {
+ talk_number(item + 1, true);
+ talk_id(VOICE_PAUSE, true);
+#if 0 /* no debug items currently have lang ids */
+ long id = P2ID((const unsigned char *)(menuitems[item].desc));
+ if(id>=0)
+ talk_id(id, true);
+ else
+#endif
+ talk_spell(menuitems[item].desc, true);
+ }
+ return 0;
+}
+
int debug_menu(void)
{
struct simplelist_info info;
@@ -2636,6 +2738,7 @@ int debug_menu(void)
simplelist_info_init(&info, "Debug Menu", ARRAYLEN(menuitems), NULL);
info.action_callback = menu_action_callback;
info.get_name = menu_get_name;
+ info.get_talk = menu_get_talk;
return (simplelist_show_list(&info)) ? 1 : 0;
}
diff --git a/apps/enc_config.c b/apps/enc_config.c
index 66aaac22e6..d06ac84b5f 100644
--- a/apps/enc_config.c
+++ b/apps/enc_config.c
@@ -115,7 +115,7 @@ static void mp3_enc_convert_config(struct encoder_config *cfg,
}
else
{
- if ((unsigned)global_settings.mp3_enc_config.bitrate > MP3_ENC_NUM_BITR)
+ if ((unsigned)global_settings.mp3_enc_config.bitrate >= MP3_ENC_NUM_BITR)
global_settings.mp3_enc_config.bitrate = MP3_ENC_BITRATE_CFG_DEFAULT;
cfg->mp3_enc.bitrate = mp3_enc_bitr[global_settings.mp3_enc_config.bitrate];
}
@@ -183,7 +183,7 @@ static bool mp3_enc_bitrate(struct menucallback_data *data)
int index = round_value_to_list32(cfg->mp3_enc.bitrate, rate_list,
n_rates, false);
- bool res = set_option(str(LANG_BITRATE), &index, INT,
+ bool res = set_option(str(LANG_BITRATE), &index, RB_INT,
items, n_rates, NULL);
index = round_value_to_list32(rate_list[index], mp3_enc_bitr,
MP3_ENC_NUM_BITR, false);
@@ -193,9 +193,9 @@ static bool mp3_enc_bitrate(struct menucallback_data *data)
} /* mp3_enc_bitrate */
/* mp3_enc configuration menu */
-MENUITEM_FUNCTION(mp3_bitrate, MENU_FUNC_USEPARAM, ID2P(LANG_BITRATE),
- mp3_enc_bitrate,
- &menu_callback_data, enc_menuitem_callback, Icon_NOICON);
+MENUITEM_FUNCTION_W_PARAM(mp3_bitrate, 0, ID2P(LANG_BITRATE),
+ mp3_enc_bitrate, &menu_callback_data,
+ enc_menuitem_callback, Icon_NOICON);
MAKE_MENU( mp3_enc_menu, ID2P(LANG_ENCODER_SETTINGS),
enc_menuitem_enteritem, Icon_NOICON,
&mp3_bitrate);
@@ -286,7 +286,7 @@ static int enc_menuitem_callback(int action,
{
(void)this_list;
struct menucallback_data *data =
- (struct menucallback_data*)this_item->function->param;
+ (struct menucallback_data*)this_item->function_param->param;
if (action == ACTION_EXIT_MENUITEM)
{
diff --git a/apps/features.txt b/apps/features.txt
index 4e7f986057..a43172932f 100644
--- a/apps/features.txt
+++ b/apps/features.txt
@@ -105,6 +105,10 @@ radio_remote
#endif
#endif
+#if defined(HAVE_RDS_CAP)
+rds
+#endif
+
#if defined(HAVE_RECORDING)
recording
#if defined(HAVE_LINE_IN)
@@ -182,19 +186,23 @@ depth_3d
#endif
/* This should be AUDIOHW_HAVE_FILTER_ROLL_OFF but that is only defined later */
-#if defined(DX50) || defined(HAVE_DF1704_CODEC) || defined(HAVE_PCM1792_CODEC) || defined(HAVE_CS4398) || defined(HAVE_WM8740) || defined(HAVE_ES9018) || defined(HAVE_XDUOO_LINUX_CODEC) || defined(HAVE_FIIO_LINUX_CODEC) || defined(HAVE_AK4376)
+#if defined(DX50) || defined(HAVE_DF1704_CODEC) || defined(HAVE_PCM1792_CODEC) || defined(HAVE_CS4398) || defined(HAVE_WM8740) || defined(HAVE_ES9018)|| defined(HAVE_EROS_QN_CODEC) || defined(HAVE_XDUOO_LINUX_CODEC) || defined(HAVE_FIIO_LINUX_CODEC) || defined(HAVE_AK4376) || defined(HAVE_ES9218)
filter_roll_off
#endif
/* This should be AUDIOHW_HAVE_POWER_MODE but that is not defined yet */
-#if defined(HAVE_AK4376)
+#if defined(HAVE_AK4376) || defined(HAVE_ES9218)
dac_power_mode
#endif
-#if defined(HAVE_ES9018)
+#if defined(HAVE_ES9018) || defined(HAVE_EROS_QN_CODEC)
es9018
#endif
+#if defined(HAVE_ES9218)
+es9218
+#endif
+
/* These features are only used by the manual so they won't break binary
* compatibility
*/
@@ -258,6 +266,8 @@ recording_digital
#if MEMORYSIZE <= 2
lowmem
+#elif MEMORYSIZE > 8
+himem
#endif
#if defined(HAVE_HARDWARE_CLICK)
@@ -279,3 +289,18 @@ multi_boot
#if defined(HIBY_LINUX)
hibylinux
#endif
+
+#if defined(BUTTON_REC) || \
+ (CONFIG_KEYPAD == GIGABEAT_PAD) || \
+ (CONFIG_KEYPAD == IPOD_4G_PAD) || \
+ (CONFIG_KEYPAD == IRIVER_H10_PAD)
+clear_settings_on_hold
+#endif
+
+#if defined(HAVE_PERCEPTUAL_VOLUME)
+perceptual_volume
+#endif
+
+#if defined(CODEC_AAC_SBR_DEC)
+codec_aac_he
+#endif
diff --git a/apps/filetree.c b/apps/filetree.c
index 5c6443cc34..11d562f586 100644
--- a/apps/filetree.c
+++ b/apps/filetree.c
@@ -46,38 +46,78 @@
#include "strnatcmp.h"
#include "keyboard.h"
+#ifdef HAVE_MULTIVOLUME
+#include "mv.h"
+#endif
+
#if CONFIG_TUNER
#include "radio.h"
#endif
#include "wps.h"
-static int compare_sort_dir; /* qsort key for sorting directories */
+static struct compare_data
+{
+ int sort_dir; /* qsort key for sorting directories */
+ int(*_compar)(const char*, const char*, size_t);
+} cmp_data;
+
+/* dummmy functions to allow compatibility with strncmp & strncasecmp */
+static int strnatcmp_n(const char *a, const char *b, size_t n)
+{
+ (void)n;
+ return strnatcmp(a, b);
+}
+static int strnatcasecmp_n(const char *a, const char *b, size_t n)
+{
+ (void)n;
+ return strnatcasecmp(a, b);
+}
int ft_build_playlist(struct tree_context* c, int start_index)
{
int i;
int start=start_index;
+ int res;
+ struct playlist_info *playlist = playlist_get_current();
tree_lock_cache(c);
struct entry *entries = tree_get_entries(c);
- for(i = 0;i < c->filesindir;i++)
+ struct playlist_insert_context pl_context;
+
+ res = playlist_insert_context_create(playlist, &pl_context,
+ PLAYLIST_REPLACE, false, false);
+ if (res >= 0)
{
- if((entries[i].attr & FILE_ATTR_MASK) == FILE_ATTR_AUDIO)
+ cpu_boost(true);
+ for(i = 0;i < c->filesindir;i++)
{
- if (playlist_add(entries[i].name) < 0)
+#if 0 /*only needed if displaying progress */
+ /* user abort */
+ if (action_userabort(TIMEOUT_NOBLOCK))
+ {
break;
+ }
+#endif
+ if((entries[i].attr & FILE_ATTR_MASK) == FILE_ATTR_AUDIO)
+ {
+ res = playlist_insert_context_add(&pl_context, entries[i].name);
+ if (res < 0)
+ break;
+ }
+ else
+ {
+ /* Adjust the start index when se skip non-MP3 entries */
+ if(i < start)
+ start_index--;
+ }
}
- else
- {
- /* Adjust the start index when se skip non-MP3 entries */
- if(i < start)
- start_index--;
- }
+ cpu_boost(false);
}
+
+ playlist_insert_context_release(&pl_context);
tree_unlock_cache(c);
-
return start_index;
}
@@ -89,7 +129,8 @@ int ft_build_playlist(struct tree_context* c, int start_index)
* avoid allocating yet another path buffer on the stack (and save some
* code; the caller typically needs to create the full pathname anyway)...
*/
-bool ft_play_playlist(char* pathname, char* dirname, char* filename, bool skip_dyn_warning)
+bool ft_play_playlist(char* pathname, char* dirname,
+ char* filename, bool skip_warn_and_bookmarks)
{
if (global_settings.party_mode && audio_status())
{
@@ -97,28 +138,19 @@ bool ft_play_playlist(char* pathname, char* dirname, char* filename, bool skip_d
return false;
}
- if (bookmark_autoload(pathname))
+ if (!skip_warn_and_bookmarks)
{
- return false;
+ int res = bookmark_autoload(pathname);
+ if (res == BOOKMARK_CANCEL || res == BOOKMARK_DO_RESUME || !warn_on_pl_erase())
+ return false;
}
splash(0, ID2P(LANG_WAIT));
- /* about to create a new current playlist...
- * allow user to cancel the operation.
- * Do not show if skip_dyn_warning is true */
- if (!skip_dyn_warning)
- {
- if (!warn_on_pl_erase())
- return false;
- }
-
if (playlist_create(dirname, filename) != -1)
{
if (global_settings.playlist_shuffle)
- {
playlist_shuffle(current_tick, -1);
- }
playlist_start(0, 0, 0);
return true;
@@ -198,7 +230,7 @@ static int compare(const void* p1, const void* p2)
if (e1->attr & ATTR_DIRECTORY && e2->attr & ATTR_DIRECTORY)
{ /* two directories */
- criteria = compare_sort_dir;
+ criteria = cmp_data.sort_dir;
#ifdef HAVE_MULTIVOLUME
if (e1->attr & ATTR_VOLUME || e2->attr & ATTR_VOLUME)
@@ -233,41 +265,23 @@ static int compare(const void* p1, const void* p2)
if (t1 != t2) /* if different */
return (t1 - t2) * (criteria == SORT_TYPE_REVERSED ? -1 : 1);
- /* else fall through to alphabetical sorting */
+ /* else alphabetical sorting */
+ return cmp_data._compar(e1->name, e2->name, MAX_PATH);
}
case SORT_DATE:
case SORT_DATE_REVERSED:
- /* Ignore SORT_TYPE */
- if (criteria == SORT_DATE || criteria == SORT_DATE_REVERSED)
- {
- if (e1->time_write != e2->time_write)
- return (e1->time_write - e2->time_write)
- * (criteria == SORT_DATE_REVERSED ? -1 : 1);
- /* else fall through to alphabetical sorting */
- }
-
+ {
+ if (e1->time_write != e2->time_write)
+ return (e1->time_write - e2->time_write)
+ * (criteria == SORT_DATE_REVERSED ? -1 : 1);
+ /* else fall through to alphabetical sorting */
+ }
case SORT_ALPHA:
case SORT_ALPHA_REVERSED:
{
- if (global_settings.sort_case)
- {
- if (global_settings.interpret_numbers == SORT_INTERPRET_AS_NUMBER)
- return strnatcmp(e1->name, e2->name)
- * (criteria == SORT_ALPHA_REVERSED ? -1 : 1);
- else
- return strncmp(e1->name, e2->name, MAX_PATH)
- * (criteria == SORT_ALPHA_REVERSED ? -1 : 1);
- }
- else
- {
- if (global_settings.interpret_numbers == SORT_INTERPRET_AS_NUMBER)
- return strnatcasecmp(e1->name, e2->name)
- * (criteria == SORT_ALPHA_REVERSED ? -1 : 1);
- else
- return strncasecmp(e1->name, e2->name, MAX_PATH)
- * (criteria == SORT_ALPHA_REVERSED ? -1 : 1);
- }
+ return cmp_data._compar(e1->name, e2->name, MAX_PATH) *
+ (criteria == SORT_ALPHA_REVERSED ? -1 : 1);
}
}
@@ -277,12 +291,18 @@ static int compare(const void* p1, const void* p2)
/* load and sort directory into the tree's cache. returns NULL on failure. */
int ft_load(struct tree_context* c, const char* tempdir)
{
+ if (c->out_of_tree > 0) /* something else is loaded */
+ return 0;
+
int files_in_dir = 0;
int name_buffer_used = 0;
struct dirent *entry;
bool (*callback_show_item)(char *, int, struct tree_context *) = NULL;
DIR *dir;
+ if (!c->is_browsing)
+ c->browse = NULL;
+
if (tempdir)
dir = opendir(tempdir);
else
@@ -330,42 +350,43 @@ int ft_load(struct tree_context* c, const char* tempdir)
}
dptr->attr = info.attribute;
+ int dir_attr = (dptr->attr & ATTR_DIRECTORY);
/* check for known file types */
- if ( !(dptr->attr & ATTR_DIRECTORY) )
+ if ( !(dir_attr) )
dptr->attr |= filetype_get_attr((char *)entry->d_name);
+ int file_attr = (dptr->attr & FILE_ATTR_MASK);
+
/* filter out non-visible files */
- if ((!(dptr->attr & ATTR_DIRECTORY) && (
- (*c->dirfilter == SHOW_PLAYLIST &&
- (dptr->attr & FILE_ATTR_MASK) != FILE_ATTR_M3U) ||
- ((*c->dirfilter == SHOW_MUSIC &&
- (dptr->attr & FILE_ATTR_MASK) != FILE_ATTR_AUDIO) &&
- (dptr->attr & FILE_ATTR_MASK) != FILE_ATTR_M3U) ||
+ if ((!(dir_attr) && ((*c->dirfilter == SHOW_PLAYLIST &&
+ file_attr != FILE_ATTR_M3U) ||
+ ((*c->dirfilter == SHOW_MUSIC && file_attr != FILE_ATTR_AUDIO) &&
+ file_attr != FILE_ATTR_M3U) ||
(*c->dirfilter == SHOW_SUPPORTED && !filetype_supported(dptr->attr)))) ||
- (*c->dirfilter == SHOW_WPS && (dptr->attr & FILE_ATTR_MASK) != FILE_ATTR_WPS) ||
- (*c->dirfilter == SHOW_FONT && (dptr->attr & FILE_ATTR_MASK) != FILE_ATTR_FONT) ||
- (*c->dirfilter == SHOW_SBS && (dptr->attr & FILE_ATTR_MASK) != FILE_ATTR_SBS) ||
+ (*c->dirfilter == SHOW_WPS && file_attr != FILE_ATTR_WPS) ||
+ (*c->dirfilter == SHOW_FONT && file_attr != FILE_ATTR_FONT) ||
+ (*c->dirfilter == SHOW_SBS && file_attr != FILE_ATTR_SBS) ||
#if CONFIG_TUNER
- (*c->dirfilter == SHOW_FMS && (dptr->attr & FILE_ATTR_MASK) != FILE_ATTR_FMS) ||
+ (*c->dirfilter == SHOW_FMS && file_attr != FILE_ATTR_FMS) ||
#endif
#ifdef HAVE_REMOTE_LCD
- (*c->dirfilter == SHOW_RWPS && (dptr->attr & FILE_ATTR_MASK) != FILE_ATTR_RWPS) ||
- (*c->dirfilter == SHOW_RSBS && (dptr->attr & FILE_ATTR_MASK) != FILE_ATTR_RSBS) ||
+ (*c->dirfilter == SHOW_RWPS && file_attr != FILE_ATTR_RWPS) ||
+ (*c->dirfilter == SHOW_RSBS && file_attr != FILE_ATTR_RSBS) ||
#if CONFIG_TUNER
- (*c->dirfilter == SHOW_RFMS && (dptr->attr & FILE_ATTR_MASK) != FILE_ATTR_RFMS) ||
+ (*c->dirfilter == SHOW_RFMS && file_attr != FILE_ATTR_RFMS) ||
#endif
#endif
#if CONFIG_TUNER
- (*c->dirfilter == SHOW_FMR && (dptr->attr & FILE_ATTR_MASK) != FILE_ATTR_FMR) ||
+ (*c->dirfilter == SHOW_FMR && file_attr != FILE_ATTR_FMR) ||
#endif
- (*c->dirfilter == SHOW_M3U && (dptr->attr & FILE_ATTR_MASK) != FILE_ATTR_M3U) ||
- (*c->dirfilter == SHOW_CFG && (dptr->attr & FILE_ATTR_MASK) != FILE_ATTR_CFG) ||
- (*c->dirfilter == SHOW_LNG && (dptr->attr & FILE_ATTR_MASK) != FILE_ATTR_LNG) ||
- (*c->dirfilter == SHOW_MOD && (dptr->attr & FILE_ATTR_MASK) != FILE_ATTR_MOD) ||
- (*c->dirfilter == SHOW_PLUGINS && (dptr->attr & FILE_ATTR_MASK) != FILE_ATTR_ROCK &&
- (dptr->attr & FILE_ATTR_MASK) != FILE_ATTR_LUA &&
- (dptr->attr & FILE_ATTR_MASK) != FILE_ATTR_OPX) ||
+ (*c->dirfilter == SHOW_M3U && file_attr != FILE_ATTR_M3U) ||
+ (*c->dirfilter == SHOW_CFG && file_attr != FILE_ATTR_CFG) ||
+ (*c->dirfilter == SHOW_LNG && file_attr != FILE_ATTR_LNG) ||
+ (*c->dirfilter == SHOW_MOD && file_attr != FILE_ATTR_MOD) ||
+ (*c->dirfilter == SHOW_PLUGINS && file_attr != FILE_ATTR_ROCK &&
+ file_attr != FILE_ATTR_LUA &&
+ file_attr != FILE_ATTR_OPX) ||
(callback_show_item && !callback_show_item(entry->d_name, dptr->attr, c)))
{
continue;
@@ -384,14 +405,29 @@ int ft_load(struct tree_context* c, const char* tempdir)
strcpy(dptr->name, (char *)entry->d_name);
name_buffer_used += len + 1;
- if (dptr->attr & ATTR_DIRECTORY) /* count the remaining dirs */
+ if (dir_attr) /* count the remaining dirs */
c->dirsindir++;
}
c->filesindir = files_in_dir;
c->dirlength = files_in_dir;
closedir(dir);
- compare_sort_dir = c->sort_dir;
+ cmp_data.sort_dir = c->sort_dir;
+ if (global_settings.sort_case)
+ {
+ if (global_settings.interpret_numbers == SORT_INTERPRET_AS_NUMBER)
+ cmp_data._compar = strnatcmp_n;
+ else
+ cmp_data._compar = strncmp;
+ }
+ else
+ {
+ if (global_settings.interpret_numbers == SORT_INTERPRET_AS_NUMBER)
+ cmp_data._compar = strnatcasecmp_n;
+ else
+ cmp_data._compar = strncasecmp;
+ }
+
qsort(tree_get_entries(c), files_in_dir, sizeof(struct entry), compare);
/* If thumbnail talking is enabled, make an extra run to mark files with
@@ -432,10 +468,62 @@ static void ft_load_font(char *file)
viewportmanager_theme_changed(THEME_UI_VIEWPORT);
}
+static void ft_apply_skin_file(char *buf, char *file, const int maxlen)
+{
+ splash(0, ID2P(LANG_WAIT));
+ set_file(buf, file, maxlen);
+ settings_apply_skins();
+}
+
+int ft_assemble_path(char *buf, size_t bufsz, const char* currdir, const char* filename)
+{
+ size_t len;
+ if (!filename)
+ filename = "";
+
+#ifdef HAVE_MULTIVOLUME
+ if (currdir && currdir[0] && currdir[1]) /* Not in / */
+ {
+ if (currdir[1] != VOL_START_TOK)
+ {
+ len = path_append(buf, root_realpath(), currdir, bufsz);
+ if (len < bufsz)
+ len = path_append(buf, buf + len, filename, bufsz - len);
+ }
+ len = path_append(buf, currdir, filename, bufsz);
+ }
+ else /* In / */
+ {
+ if (filename[0] != VOL_START_TOK)
+ {
+ len = path_append(buf, root_realpath(), filename, bufsz);
+ }
+ else
+ len = path_append(buf, PATH_SEPSTR, filename, bufsz);
+ }
+#else
+ if (currdir && currdir[0] && currdir[1]) /* Not in / */
+ {
+ len = path_append(buf, root_realpath(), currdir, bufsz);
+ if(len < bufsz)
+ len = path_append(buf, buf + len, filename, bufsz - len);
+ }
+ else /* In / */
+ {
+ len = path_append(buf, root_realpath(), filename, bufsz);
+ }
+#endif
+
+ if (len > bufsz)
+ splash(HZ, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
+ return (int)len;
+}
+
int ft_enter(struct tree_context* c)
{
int rc = GO_TO_PREVIOUS;
char buf[MAX_PATH];
+
struct entry* file = tree_get_entry_at(c, c->selected_item);
if (!file)
{
@@ -444,17 +532,7 @@ int ft_enter(struct tree_context* c)
}
int file_attr = file->attr;
- int len;
-
- if (c->currdir[1])
- {
- len = snprintf(buf,sizeof(buf),"%s/%s",c->currdir, file->name);
- if ((unsigned) len > sizeof(buf))
- splash(HZ, ID2P(LANG_PLAYLIST_ACCESS_ERROR));
- }
- else
- snprintf(buf,sizeof(buf),"/%s",file->name);
-
+ ft_assemble_path(buf, sizeof(buf), c->currdir, file->name);
if (file_attr & ATTR_DIRECTORY) {
memcpy(c->currdir, buf, sizeof(c->currdir));
if ( c->dirlevel < MAX_DIR_LEVELS )
@@ -479,7 +557,9 @@ int ft_enter(struct tree_context* c)
break;
case FILE_ATTR_AUDIO:
- if (bookmark_autoload(c->currdir))
+ {
+ int res = bookmark_autoload(c->currdir);
+ if (res == BOOKMARK_CANCEL || res == BOOKMARK_DO_RESUME)
break;
splash(0, ID2P(LANG_WAIT));
@@ -495,25 +575,30 @@ int ft_enter(struct tree_context* c)
PLAYLIST_INSERT_LAST, true, true);
splash(HZ, ID2P(LANG_QUEUE_LAST));
}
- else if (playlist_create(c->currdir, NULL) != -1)
+ else
{
- start_index = ft_build_playlist(c, c->selected_item);
- if (global_settings.playlist_shuffle)
+ /* use the assembled path sans filename */
+ char * fp = strrchr(buf, PATH_SEPCH);
+ if (fp)
+ *fp = '\0';
+ if (playlist_create(buf, NULL) != -1)
{
- start_index = playlist_shuffle(seed, start_index);
-
- /* when shuffling dir.: play all files
- even if the file selected by user is
- not the first one */
- if (!global_settings.play_selected)
- start_index = 0;
+ start_index = ft_build_playlist(c, c->selected_item);
+ if (global_settings.playlist_shuffle)
+ {
+ start_index = playlist_shuffle(seed, start_index);
+ /* when shuffling dir.: play all files
+ even if the file selected by user is
+ not the first one */
+ if (!global_settings.play_selected)
+ start_index = 0;
+ }
+ playlist_start(start_index, 0, 0);
+ play = true;
}
-
- playlist_start(start_index, 0, 0);
- play = true;
}
break;
-
+ }
#if CONFIG_TUNER
/* fmr preset file */
case FILE_ATTR_FMR:
@@ -538,49 +623,32 @@ int ft_enter(struct tree_context* c)
break;
case FILE_ATTR_FMS:
- splash(0, ID2P(LANG_WAIT));
- set_file(buf, (char *)global_settings.fms_file, MAX_FILENAME);
- settings_apply_skins();
+ ft_apply_skin_file(buf, global_settings.fms_file, MAX_FILENAME);
break;
#ifdef HAVE_REMOTE_LCD
case FILE_ATTR_RFMS:
- splash(0, ID2P(LANG_WAIT));
- set_file(buf, (char *)global_settings.rfms_file, MAX_FILENAME);
- settings_apply_skins();
+ ft_apply_skin_file(buf, global_settings.rfms_file, MAX_FILENAME);
break;
#endif
#endif
-
case FILE_ATTR_SBS:
- splash(0, ID2P(LANG_WAIT));
- set_file(buf, (char *)global_settings.sbs_file, MAX_FILENAME);
- settings_apply_skins();
+ ft_apply_skin_file(buf, global_settings.sbs_file, MAX_FILENAME);
break;
#ifdef HAVE_REMOTE_LCD
case FILE_ATTR_RSBS:
- splash(0, ID2P(LANG_WAIT));
- set_file(buf, (char *)global_settings.rsbs_file, MAX_FILENAME);
- settings_apply_skins();
+ ft_apply_skin_file(buf, global_settings.rsbs_file, MAX_FILENAME);
break;
#endif
/* wps config file */
case FILE_ATTR_WPS:
- splash(0, ID2P(LANG_WAIT));
- set_file(buf, (char *)global_settings.wps_file,
- MAX_FILENAME);
- settings_apply_skins();
+ ft_apply_skin_file(buf, global_settings.wps_file, MAX_FILENAME);
break;
-
#if defined(HAVE_REMOTE_LCD) && (NB_SCREENS > 1)
/* remote-wps config file */
case FILE_ATTR_RWPS:
- splash(0, ID2P(LANG_WAIT));
- set_file(buf, (char *)global_settings.rwps_file,
- MAX_FILENAME);
- settings_apply_skins();
+ ft_apply_skin_file(buf, global_settings.rwps_file, MAX_FILENAME);
break;
#endif
-
case FILE_ATTR_CFG:
splash(0, ID2P(LANG_WAIT));
if (!settings_load_config(buf,true))
@@ -628,32 +696,21 @@ int ft_enter(struct tree_context* c)
rolo_load(buf);
break;
#endif
+ case FILE_ATTR_CUE:
+ display_cuesheet_content(buf);
+ break;
/* plugin file */
case FILE_ATTR_ROCK:
- case FILE_ATTR_LUA:
- case FILE_ATTR_OPX:
{
- char *plugin = buf, *argument = NULL, lua_path[MAX_PATH];
- int ret;
-
- if ((file_attr & FILE_ATTR_MASK) == FILE_ATTR_LUA) {
- snprintf(lua_path, sizeof(lua_path)-1, "%s/lua.rock", VIEWERS_DIR); /* Use a #define here ? */
- plugin = lua_path;
- argument = buf;
- }
- else if ((file_attr & FILE_ATTR_MASK) == FILE_ATTR_OPX) {
- snprintf(lua_path, sizeof(lua_path)-1, "%s/open_plugins.rock", VIEWERS_DIR); /* Use a #define here ? */
- plugin = lua_path;
- argument = buf;
- }
-
+ char *plugin = buf, *argument = NULL;
if (global_settings.party_mode && audio_status()) {
splash(HZ, ID2P(LANG_PARTY_MODE));
break;
}
- ret = plugin_load(plugin, argument);
- switch (ret)
+
+#ifdef PLUGINS_RUN_IN_BROWSER /* Stay in the filetree to run a plugin */
+ switch (plugin_load(plugin, argument))
{
case PLUGIN_GOTO_WPS:
play = true;
@@ -676,32 +733,35 @@ int ft_enter(struct tree_context* c)
default:
break;
}
+#else /* Exit the filetree to run a plugin */
+ plugin_open(plugin, argument);
+ rc = GO_TO_PLUGIN;
+#endif
break;
}
- case FILE_ATTR_CUE:
- display_cuesheet_content(buf);
- break;
default:
{
const char* plugin;
-
+ char plugin_path[MAX_PATH];
+ const char *argument = buf;
if (global_settings.party_mode && audio_status()) {
splash(HZ, ID2P(LANG_PARTY_MODE));
break;
}
- struct entry* file = tree_get_entry_at(c, c->selected_item);
+ file = tree_get_entry_at(c, c->selected_item);
if (!file)
{
splashf(HZ, str(LANG_READ_FAILED), str(LANG_UNKNOWN));
return rc;
}
- plugin = filetype_get_plugin(file);
+ plugin = filetype_get_plugin(file, plugin_path, sizeof(plugin_path));
if (plugin)
{
- switch (plugin_load(plugin,buf))
+#ifdef PLUGINS_RUN_IN_BROWSER /* Stay in the filetree to run a plugin */
+ switch (plugin_load(plugin, argument))
{
case PLUGIN_USB_CONNECTED:
rc = GO_TO_FILEBROWSER;
@@ -719,6 +779,10 @@ int ft_enter(struct tree_context* c)
default:
break;
}
+#else /* Exit the filetree to run a plugin */
+ plugin_open(plugin, argument);
+ rc = GO_TO_PLUGIN;
+#endif
}
break;
}
@@ -745,6 +809,7 @@ int ft_enter(struct tree_context* c)
}
}
}
+
return rc;
}
@@ -753,17 +818,44 @@ int ft_exit(struct tree_context* c)
extern char lastfile[]; /* from tree.c */
char buf[MAX_PATH];
int rc = 0;
- bool exit_func = false;
-
+ bool exit_func = false;
int i = strlen(c->currdir);
+
+ /* strip trailing slashes */
+ while (c->currdir[i-1] == PATH_SEPCH)
+ i--;
+
if (i>1) {
- while (c->currdir[i-1]!='/')
+ while (c->currdir[i-1]!=PATH_SEPCH)
i--;
strcpy(buf,&c->currdir[i]);
if (i==1)
- c->currdir[i]=0;
+ c->currdir[i]='\0';
else
- c->currdir[i-1]=0;
+ c->currdir[i-1]='\0';
+
+#ifdef HAVE_MULTIVOLUME /* un-redirect the realpath */
+ if ((unsigned)c->dirlevel<=2) /* only expect redirect two levels max */
+ {
+ char *currdir = c->currdir;
+ const char *root = root_realpath();
+ int len = i-1;
+ /* compare to the root path bail if they don't match except single '/' */
+ for (; len > 0 && *root != '\0' && *root == *currdir; len--)
+ {
+ root++;
+ currdir++;
+ }
+ if (*root == PATH_SEPCH) /* root may have trailing slash */
+ root++;
+ if (*root == '\0' &&
+ (len == 0 || (len == 1 && *currdir == PATH_SEPCH)))
+ {
+ strcpy(c->currdir, PATH_ROOTSTR);
+ c->dirlevel=1;
+ }
+ }
+#endif
if (*c->dirfilter > NUM_FILTER_MODES && c->dirlevel < 1)
exit_func = true;
@@ -787,5 +879,7 @@ int ft_exit(struct tree_context* c)
if (exit_func)
rc = 3;
+ c->out_of_tree = 0;
+
return rc;
}
diff --git a/apps/filetree.h b/apps/filetree.h
index 178ba0e973..3ec7846d2c 100644
--- a/apps/filetree.h
+++ b/apps/filetree.h
@@ -25,7 +25,10 @@
int ft_load(struct tree_context* c, const char* tempdir);
int ft_enter(struct tree_context* c);
int ft_exit(struct tree_context* c);
+int ft_assemble_path(char *buf, size_t bufsz,
+ const char* currdir, const char* filename);
int ft_build_playlist(struct tree_context* c, int start_index);
-bool ft_play_playlist(char* pathname, char* dirname, char* filename, bool skip_dyn_warning);
+bool ft_play_playlist(char* pathname, char* dirname,
+ char* filename, bool skip_warn_and_bookmarks);
#endif
diff --git a/apps/filetypes.c b/apps/filetypes.c
index 7f7c198cb5..bda7018381 100644
--- a/apps/filetypes.c
+++ b/apps/filetypes.c
@@ -38,6 +38,7 @@
#include "splash.h"
#include "core_alloc.h"
#include "icons.h"
+/*#define LOGF_ENABLE*/
#include "logf.h"
/* max filetypes (plugins & icons stored here) */
@@ -45,111 +46,172 @@
/* max viewer plugins */
#define MAX_VIEWERS 56
+static void read_builtin_types_init(void) INIT_ATTR;
+static void read_viewers_config_init(void) INIT_ATTR;
+static void read_config_init(int fd) INIT_ATTR;
+
/* a table for the known file types */
static const struct filetype inbuilt_filetypes[] = {
- { "mp3", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "mp2", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "mpa", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "mp1", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "ogg", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "oga", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "wma", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "wmv", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "asf", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "wav", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "flac",FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "ac3", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "a52", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "mpc", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "wv", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "m4a", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "m4b", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "mp4", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "mod", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "shn", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "aif", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "aiff",FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "spx" ,FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "opus",FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "sid", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "adx", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "nsf", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "nsfe",FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "spc", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "ape", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "mac", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "sap" ,FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "rm", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "ra", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "rmvb",FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "cmc", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "cm3", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "cmr", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "cms", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "dmc", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "dlt", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "mpt", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "mpd", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "rmt", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "tmc", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "tm8", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "tm2", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "oma", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "aa3", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "at3", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "mmf", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "au", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "snd", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "vox", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "w64", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "tta", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "ay", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "vtx", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "gbs", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "hes", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "sgc", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "vgm", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "vgz", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "kss", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "aac", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "m3u", FILE_ATTR_M3U, Icon_Playlist, LANG_PLAYLIST },
- { "m3u8",FILE_ATTR_M3U, Icon_Playlist, LANG_PLAYLIST },
- { "cfg", FILE_ATTR_CFG, Icon_Config, VOICE_EXT_CFG },
- { "wps", FILE_ATTR_WPS, Icon_Wps, VOICE_EXT_WPS },
+ { "mp3", FILE_ATTR_AUDIO },
+ { "mp2", FILE_ATTR_AUDIO },
+ { "mpa", FILE_ATTR_AUDIO },
+ { "mp1", FILE_ATTR_AUDIO },
+ { "ogg", FILE_ATTR_AUDIO },
+ { "oga", FILE_ATTR_AUDIO },
+ { "wma", FILE_ATTR_AUDIO },
+ { "wmv", FILE_ATTR_AUDIO },
+ { "asf", FILE_ATTR_AUDIO },
+ { "wav", FILE_ATTR_AUDIO },
+ { "flac", FILE_ATTR_AUDIO },
+ { "ac3", FILE_ATTR_AUDIO },
+ { "a52", FILE_ATTR_AUDIO },
+ { "mpc", FILE_ATTR_AUDIO },
+ { "wv", FILE_ATTR_AUDIO },
+ { "m4a", FILE_ATTR_AUDIO },
+ { "m4b", FILE_ATTR_AUDIO },
+ { "mp4", FILE_ATTR_AUDIO },
+ { "mod", FILE_ATTR_AUDIO },
+ { "mpga", FILE_ATTR_AUDIO },
+ { "shn", FILE_ATTR_AUDIO },
+ { "aif", FILE_ATTR_AUDIO },
+ { "aiff", FILE_ATTR_AUDIO },
+ { "spx" , FILE_ATTR_AUDIO },
+ { "opus", FILE_ATTR_AUDIO },
+ { "sid", FILE_ATTR_AUDIO },
+ { "adx", FILE_ATTR_AUDIO },
+ { "nsf", FILE_ATTR_AUDIO },
+ { "nsfe", FILE_ATTR_AUDIO },
+ { "spc", FILE_ATTR_AUDIO },
+ { "ape", FILE_ATTR_AUDIO },
+ { "mac", FILE_ATTR_AUDIO },
+ { "sap" , FILE_ATTR_AUDIO },
+ { "rm", FILE_ATTR_AUDIO },
+ { "ra", FILE_ATTR_AUDIO },
+ { "rmvb", FILE_ATTR_AUDIO },
+ { "cmc", FILE_ATTR_AUDIO },
+ { "cm3", FILE_ATTR_AUDIO },
+ { "cmr", FILE_ATTR_AUDIO },
+ { "cms", FILE_ATTR_AUDIO },
+ { "dmc", FILE_ATTR_AUDIO },
+ { "dlt", FILE_ATTR_AUDIO },
+ { "mpt", FILE_ATTR_AUDIO },
+ { "mpd", FILE_ATTR_AUDIO },
+ { "rmt", FILE_ATTR_AUDIO },
+ { "tmc", FILE_ATTR_AUDIO },
+ { "tm8", FILE_ATTR_AUDIO },
+ { "tm2", FILE_ATTR_AUDIO },
+ { "oma", FILE_ATTR_AUDIO },
+ { "aa3", FILE_ATTR_AUDIO },
+ { "at3", FILE_ATTR_AUDIO },
+ { "mmf", FILE_ATTR_AUDIO },
+ { "au", FILE_ATTR_AUDIO },
+ { "snd", FILE_ATTR_AUDIO },
+ { "vox", FILE_ATTR_AUDIO },
+ { "w64", FILE_ATTR_AUDIO },
+ { "tta", FILE_ATTR_AUDIO },
+ { "ay", FILE_ATTR_AUDIO },
+ { "vtx", FILE_ATTR_AUDIO },
+ { "gbs", FILE_ATTR_AUDIO },
+ { "hes", FILE_ATTR_AUDIO },
+ { "sgc", FILE_ATTR_AUDIO },
+ { "vgm", FILE_ATTR_AUDIO },
+ { "vgz", FILE_ATTR_AUDIO },
+ { "kss", FILE_ATTR_AUDIO },
+ { "aac", FILE_ATTR_AUDIO },
+ { "m3u", FILE_ATTR_M3U },
+ { "m3u8", FILE_ATTR_M3U },
+ { "cfg", FILE_ATTR_CFG },
+ { "wps", FILE_ATTR_WPS },
#ifdef HAVE_REMOTE_LCD
- { "rwps",FILE_ATTR_RWPS, Icon_Wps, VOICE_EXT_RWPS },
+ { "rwps", FILE_ATTR_RWPS },
#endif
#if CONFIG_TUNER
- { "fmr", FILE_ATTR_FMR, Icon_Preset, LANG_FMR },
- { "fms", FILE_ATTR_FMS, Icon_Wps, VOICE_EXT_FMS },
+ { "fmr", FILE_ATTR_FMR },
+ { "fms", FILE_ATTR_FMS },
#endif
- { "lng", FILE_ATTR_LNG, Icon_Language, LANG_LANGUAGE },
- { "rock",FILE_ATTR_ROCK,Icon_Plugin, VOICE_EXT_ROCK },
- { "lua", FILE_ATTR_LUA, Icon_Plugin, VOICE_EXT_ROCK },
- { "opx", FILE_ATTR_OPX, Icon_Plugin, VOICE_EXT_ROCK },
- { "fnt", FILE_ATTR_FONT,Icon_Font, VOICE_EXT_FONT },
- { "kbd", FILE_ATTR_KBD, Icon_Keyboard, VOICE_EXT_KBD },
- { "bmark",FILE_ATTR_BMARK, Icon_Bookmark, VOICE_EXT_BMARK },
- { "cue", FILE_ATTR_CUE, Icon_Bookmark, VOICE_EXT_CUESHEET },
- { "sbs", FILE_ATTR_SBS, Icon_Wps, VOICE_EXT_SBS },
+ { "log", FILE_ATTR_LOG },
+ { "lng", FILE_ATTR_LNG },
+ { "rock", FILE_ATTR_ROCK },
+ { "lua", FILE_ATTR_LUA },
+ { "opx", FILE_ATTR_OPX },
+ { "fnt", FILE_ATTR_FONT },
+ { "kbd", FILE_ATTR_KBD },
+ { "bmark",FILE_ATTR_BMARK },
+ { "cue", FILE_ATTR_CUE },
+ { "sbs", FILE_ATTR_SBS },
#ifdef HAVE_REMOTE_LCD
- { "rsbs", FILE_ATTR_RSBS, Icon_Wps, VOICE_EXT_RSBS },
+ { "rsbs", FILE_ATTR_RSBS },
#if CONFIG_TUNER
- { "rfms", FILE_ATTR_RFMS, Icon_Wps, VOICE_EXT_RFMS },
+ { "rfms", FILE_ATTR_RFMS },
#endif
#endif
#ifdef BOOTFILE_EXT
- { BOOTFILE_EXT, FILE_ATTR_MOD, Icon_Firmware, VOICE_EXT_AJZ },
+ { BOOTFILE_EXT, FILE_ATTR_MOD },
#endif
#ifdef BOOTFILE_EXT2
- { BOOTFILE_EXT2, FILE_ATTR_MOD, Icon_Firmware, VOICE_EXT_AJZ },
+ { BOOTFILE_EXT2, FILE_ATTR_MOD },
#endif
};
-void tree_get_filetypes(const struct filetype** types, int* count)
+struct fileattr_icon_voice {
+ int tree_attr;
+ uint16_t icon;
+ uint16_t voiceclip;
+};
+
+/* a table for the known file types icons & voice clips */
+static const struct fileattr_icon_voice inbuilt_attr_icons_voices[] = {
+ { FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
+ { FILE_ATTR_M3U, Icon_Playlist, LANG_PLAYLIST },
+ { FILE_ATTR_CFG, Icon_Config, VOICE_EXT_CFG },
+ { FILE_ATTR_WPS, Icon_Wps, VOICE_EXT_WPS },
+#ifdef HAVE_REMOTE_LCD
+ {FILE_ATTR_RWPS, Icon_Wps, VOICE_EXT_RWPS },
+#endif
+#if CONFIG_TUNER
+ { FILE_ATTR_FMR, Icon_Preset, LANG_FMR },
+ { FILE_ATTR_FMS, Icon_Wps, VOICE_EXT_FMS },
+#endif
+ { FILE_ATTR_LNG, Icon_Language, LANG_LANGUAGE },
+ { FILE_ATTR_ROCK, Icon_Plugin, VOICE_EXT_ROCK },
+ { FILE_ATTR_LUA, Icon_Plugin, VOICE_EXT_ROCK },
+ { FILE_ATTR_OPX, Icon_Plugin, VOICE_EXT_ROCK },
+ { FILE_ATTR_FONT, Icon_Font, VOICE_EXT_FONT },
+ { FILE_ATTR_KBD, Icon_Keyboard, VOICE_EXT_KBD },
+ { FILE_ATTR_BMARK, Icon_Bookmark, VOICE_EXT_BMARK },
+ { FILE_ATTR_CUE, Icon_Bookmark, VOICE_EXT_CUESHEET },
+ { FILE_ATTR_SBS, Icon_Wps, VOICE_EXT_SBS },
+#ifdef HAVE_REMOTE_LCD
+ { FILE_ATTR_RSBS, Icon_Wps, VOICE_EXT_RSBS },
+#if CONFIG_TUNER
+ { FILE_ATTR_RFMS, Icon_Wps, VOICE_EXT_RFMS },
+#endif
+#endif
+#if defined(BOOTFILE_EXT) || defined(BOOTFILE_EXT2)
+ { FILE_ATTR_MOD, Icon_Firmware, VOICE_EXT_AJZ },
+#endif
+};
+
+long tree_get_filetype_voiceclip(int attr)
{
- *types = inbuilt_filetypes;
- *count = sizeof(inbuilt_filetypes) / sizeof(*inbuilt_filetypes);
+ if (global_settings.talk_filetype)
+ {
+ size_t count = ARRAY_SIZE(inbuilt_attr_icons_voices);
+ /* try to find a voice ID for the extension, if known */
+ attr &= FILE_ATTR_MASK; /* file type */
+
+ for (size_t i = count - 1; i < count; i--)
+ {
+ if (attr == inbuilt_attr_icons_voices[i].tree_attr)
+ {
+ logf("%s found attr %d id %d", __func__, attr,
+ inbuilt_attr_icons_voices[i].voiceclip);
+ return inbuilt_attr_icons_voices[i].voiceclip;
+ }
+ }
+ }
+ logf("%s not found attr %d", __func__, attr);
+ return -1;
}
#define ROCK_EXTENSION "rock"
@@ -185,7 +247,8 @@ static int filetype_count = 0;
static unsigned char highest_attr = 0;
static int viewer_count = 0;
-static int strdup_handle, strdup_bufsize, strdup_cur_idx;
+static int strdup_handle, strdup_cur_idx;
+static size_t strdup_bufsize;
static int move_callback(int handle, void* current, void* new)
{
/*could compare to strdup_handle, but ops is only used once */
@@ -242,8 +305,6 @@ static int find_extension(const char* extension)
return -1;
}
-static void read_builtin_types(void);
-static void read_config(int fd);
#ifdef HAVE_LCD_COLOR
/* Colors file format is similar to icons:
* ext:hex_color
@@ -260,9 +321,9 @@ void read_color_theme_file(void) {
unknown_file.color = -1;
if (!global_settings.colors_file[0] || global_settings.colors_file[0] == '-')
return;
- snprintf(buffer, MAX_PATH, THEME_DIR "/%s.colours",
- global_settings.colors_file);
- fd = open(buffer, O_RDONLY);
+
+ fd = open_pathfmt(buffer, sizeof(buffer), O_RDONLY,
+ THEME_DIR "/%s.colours", global_settings.colors_file);
if (fd < 0)
return;
while (read_line(fd, buffer, MAX_PATH) > 0)
@@ -300,12 +361,12 @@ void read_viewer_theme_file(void)
{
custom_filetype_icons[i] = filetypes[i].icon;
}
-
- snprintf(buffer, MAX_PATH, "%s/%s.icons", ICON_DIR,
- global_settings.viewers_icon_file);
- fd = open(buffer, O_RDONLY);
+
+ fd = open_pathfmt(buffer, sizeof(buffer), O_RDONLY,
+ ICON_DIR "/%s.icons", global_settings.viewers_icon_file);
if (fd < 0)
return;
+
while (read_line(fd, buffer, MAX_PATH) > 0)
{
if (!settings_parseline(buffer, &ext, &icon))
@@ -337,7 +398,30 @@ void read_viewer_theme_file(void)
custom_icons_loaded = true;
}
-void filetype_init(void)
+static void read_viewers_config_init(void)
+{
+ int fd = open(VIEWERS_CONFIG, O_RDONLY);
+ if(fd < 0)
+ return;
+
+ off_t filesz = filesize(fd);
+ if(filesz <= 0)
+ goto out;
+
+ /* estimate bufsize with the filesize, will not be larger */
+ strdup_bufsize = (size_t)filesz;
+ strdup_handle = core_alloc_ex(strdup_bufsize, &ops);
+ if(strdup_handle <= 0)
+ goto out;
+
+ read_config_init(fd);
+ core_shrink(strdup_handle, NULL, strdup_cur_idx);
+
+ out:
+ close(fd);
+}
+
+void filetype_init(void)
{
/* set the directory item first */
filetypes[0].extension = NULL;
@@ -345,29 +429,15 @@ void filetype_init(void)
filetypes[0].attr = 0;
filetypes[0].icon = Icon_Folder;
- /* estimate bufsize with the filesize, will not be larger */
viewer_count = 0;
filetype_count = 1;
- int fd = open(VIEWERS_CONFIG, O_RDONLY);
- if (fd < 0)
- return;
-
- strdup_bufsize = filesize(fd);
- strdup_handle = core_alloc_ex("filetypes", strdup_bufsize, &ops);
- if (strdup_handle <= 0)
- {
- close(fd);
- return;
- }
- read_builtin_types();
- read_config(fd);
- close(fd);
+ read_builtin_types_init();
+ read_viewers_config_init();
read_viewer_theme_file();
#ifdef HAVE_LCD_COLOR
read_color_theme_file();
#endif
- core_shrink(strdup_handle, core_get_data(strdup_handle), strdup_cur_idx);
}
/* remove all white spaces from string */
@@ -386,22 +456,35 @@ static void rm_whitespaces(char* str)
*s = '\0';
}
-static void read_builtin_types(void)
+static void read_builtin_types_init(void)
{
- int count = sizeof(inbuilt_filetypes)/sizeof(*inbuilt_filetypes), i;
- for(i=0; i<count && (filetype_count < MAX_FILETYPES); i++)
+ int tree_attr;
+ size_t count = ARRAY_SIZE(inbuilt_filetypes);
+ size_t icon_count = ARRAY_SIZE(inbuilt_attr_icons_voices);
+ for(size_t i = 0; (i < count) && (filetype_count < MAX_FILETYPES); i++)
{
filetypes[filetype_count].extension = inbuilt_filetypes[i].extension;
filetypes[filetype_count].plugin = NULL;
- filetypes[filetype_count].attr = inbuilt_filetypes[i].tree_attr>>8;
+
+ tree_attr = inbuilt_filetypes[i].tree_attr;
+ filetypes[filetype_count].attr = tree_attr>>8;
if (filetypes[filetype_count].attr > highest_attr)
highest_attr = filetypes[filetype_count].attr;
- filetypes[filetype_count].icon = inbuilt_filetypes[i].icon;
+
+ filetypes[filetype_count].icon = unknown_file.icon;
+ for (size_t j = icon_count - 1; j < icon_count; j--)
+ {
+ if (tree_attr == inbuilt_attr_icons_voices[j].tree_attr)
+ {
+ filetypes[filetype_count].icon = inbuilt_attr_icons_voices[j].icon;
+ break;
+ }
+ }
filetype_count++;
}
}
-static void read_config(int fd)
+static void read_config_init(int fd)
{
char line[64], *s, *e;
char *extension, *plugin;
@@ -521,17 +604,37 @@ int filetype_get_icon(int attr)
return filetypes[index].icon;
}
-char* filetype_get_plugin(const struct entry* file)
+char* filetype_get_plugin(const struct entry* file, char *buffer, size_t buffer_len)
{
- static char plugin_name[MAX_PATH];
int index = find_attr(file->attr);
- if (index < 0)
+ if (index < 0 || !buffer)
return NULL;
- if (filetypes[index].plugin == NULL)
+ struct file_type *ft_indexed = &filetypes[index];
+
+ /* attempt to find a suitable viewer by file extension */
+ if(ft_indexed->plugin == NULL && ft_indexed->extension != NULL)
+ {
+ struct file_type *ft;
+ int i = filetype_count;
+ while (--i > index)
+ {
+ ft = &filetypes[i];
+ if (ft->plugin == NULL || ft->extension == NULL)
+ continue;
+ else if (strcmp(ft->extension, ft_indexed->extension) == 0)
+ {
+ /*splashf(HZ*3, "Found %d %s %s", i, ft->extension, ft->plugin);*/
+ ft_indexed = ft;
+ break;
+ }
+ }
+ }
+ if (ft_indexed->plugin == NULL)
return NULL;
- snprintf(plugin_name, MAX_PATH, "%s/%s.%s",
- PLUGIN_DIR, filetypes[index].plugin, ROCK_EXTENSION);
- return plugin_name;
+
+ snprintf(buffer, buffer_len, "%s/%s." ROCK_EXTENSION,
+ PLUGIN_DIR, ft_indexed->plugin);
+ return buffer;
}
bool filetype_supported(int attr)
@@ -564,39 +667,32 @@ static int openwith_get_talk(int selected_item, void * data)
{
(void)data;
char viewer_filename[MAX_FILENAME];
- snprintf(viewer_filename, MAX_FILENAME, "%s.%s",
- filetypes[viewers[selected_item]].plugin, ROCK_EXTENSION);
+ snprintf(viewer_filename, MAX_FILENAME, "%s." ROCK_EXTENSION,
+ filetypes[viewers[selected_item]].plugin);
talk_file_or_spell(PLUGIN_DIR, viewer_filename,
NULL, false);
return 0;
}
-static int openwith_action_callback(int action, struct gui_synclist *lists)
-{
- struct cb_data *info = (struct cb_data *)lists->data;
- int i;
- if (action == ACTION_STD_OK)
- {
- char plugin[MAX_PATH];
- i = viewers[gui_synclist_get_sel_pos(lists)];
- snprintf(plugin, MAX_PATH, "%s/%s.%s",
- PLUGIN_DIR, filetypes[i].plugin, ROCK_EXTENSION);
- plugin_load(plugin, info->current_file);
- return ACTION_STD_CANCEL;
- }
- return action;
-}
-
int filetype_list_viewers(const char* current_file)
{
struct simplelist_info info;
- struct cb_data data = { current_file };
- simplelist_info_init(&info, str(LANG_ONPLAY_OPEN_WITH), viewer_count, &data);
- info.action_callback = openwith_action_callback;
+ simplelist_info_init(&info, str(LANG_ONPLAY_OPEN_WITH), viewer_count, NULL);
info.get_name = openwith_get_name;
info.get_icon = global_settings.show_icons?openwith_get_icon:NULL;
info.get_talk = openwith_get_talk;
- return simplelist_show_list(&info);
+
+ int ret = simplelist_show_list(&info);
+
+ if (info.selection >= 0) /* run user selected viewer */
+ {
+ char plugin[MAX_PATH];
+ int i = viewers[info.selection];
+ snprintf(plugin, MAX_PATH, "%s/%s." ROCK_EXTENSION,
+ PLUGIN_DIR, filetypes[i].plugin);
+ ret = plugin_load(plugin, current_file);
+ }
+ return ret;
}
int filetype_load_plugin(const char* plugin, const char* file)
@@ -621,7 +717,7 @@ int filetype_load_plugin(const char* plugin, const char* file)
}
if (i >= filetype_count)
return PLUGIN_ERROR;
- snprintf(plugin_name, MAX_PATH, "%s/%s.%s",
- PLUGIN_DIR, filetypes[i].plugin, ROCK_EXTENSION);
+ snprintf(plugin_name, MAX_PATH, "%s/%s." ROCK_EXTENSION,
+ PLUGIN_DIR, filetypes[i].plugin);
return plugin_load(plugin_name, file);
}
diff --git a/apps/filetypes.h b/apps/filetypes.h
index 23f259b3ca..2886fa2850 100644
--- a/apps/filetypes.h
+++ b/apps/filetypes.h
@@ -48,19 +48,20 @@
#define FILE_ATTR_FMS 0x1200 /* FM screen skin file */
#define FILE_ATTR_RFMS 0x1300 /* FM screen skin file */
#define FILE_ATTR_OPX 0x1400 /* open plugins shortcut */
+#define FILE_ATTR_LOG 0x1500 /* log file */
#define FILE_ATTR_MASK 0xFF00 /* which bits tree.c uses for file types */
struct filetype {
char* extension;
int tree_attr;
- enum themable_icons icon;
- int voiceclip;
};
-void tree_get_filetypes(const struct filetype**, int*) INIT_ATTR;
+
+long tree_get_filetype_voiceclip(int attr);
/* init the filetypes structs.
uses audio buffer for storage, so call early in init... */
void filetype_init(void) INIT_ATTR;
+
void read_viewer_theme_file(void);
#ifdef HAVE_LCD_COLOR
void read_color_theme_file(void);
@@ -73,7 +74,7 @@ int filetype_get_color(const char* name, int attr);
#endif
int filetype_get_icon(int attr);
/* return the plugin filename associated with the file */
-char* filetype_get_plugin(const struct entry* file);
+char* filetype_get_plugin(const struct entry* file, char *buffer, size_t buffer_len);
/* returns true if the attr is supported */
bool filetype_supported(int attr);
diff --git a/apps/gui/bitmap/list-skinned.c b/apps/gui/bitmap/list-skinned.c
index c5429738ed..bebff821f8 100644
--- a/apps/gui/bitmap/list-skinned.c
+++ b/apps/gui/bitmap/list-skinned.c
@@ -197,6 +197,9 @@ bool skinlist_draw(struct screen *display, struct gui_synclist *list)
wps.data = listcfg[screen]->data;
display_lines = skinlist_get_line_count(screen, list);
label = (char *)SKINOFFSETTOPTR(get_skin_buffer(wps.data), listcfg[screen]->label);
+ if (!label)
+ return false;
+
display->set_viewport(parent);
display->clear_viewport();
current_item = list->selected_item;
@@ -210,8 +213,7 @@ bool skinlist_draw(struct screen *display, struct gui_synclist *list)
if (list_start_item+cur_line+1 > list->nb_items)
break;
current_drawing_line = list_start_item+cur_line;
- is_selected = list->show_selection_marker &&
- list_start_item+cur_line == list->selected_item;
+ is_selected = list_start_item+cur_line == list->selected_item;
for (viewport = SKINOFFSETTOPTR(get_skin_buffer(wps.data), listcfg[screen]->data->tree);
viewport;
diff --git a/apps/gui/bitmap/list.c b/apps/gui/bitmap/list.c
index 6f6a8c1f7f..c10f0082d3 100644
--- a/apps/gui/bitmap/list.c
+++ b/apps/gui/bitmap/list.c
@@ -50,7 +50,6 @@
static struct viewport list_text[NB_SCREENS], title_text[NB_SCREENS];
#ifdef HAVE_TOUCHSCREEN
-static int y_offset;
static bool hide_selection;
#endif
@@ -91,11 +90,57 @@ static int list_icon_width(enum screen_type screen)
return get_icon_width(screen) + ICON_PADDING * 2;
}
-static bool draw_title(struct screen *display, struct gui_synclist *list)
+static void _default_listdraw_fn(struct list_putlineinfo_t *list_info)
+{
+ struct screen *display = list_info->display;
+ int x = list_info->x;
+ int y = list_info->y;
+ int item_indent = list_info->item_indent;
+ int item_offset = list_info->item_offset;
+ int icon = list_info->icon;
+ bool is_selected = list_info->is_selected;
+ bool is_title = list_info->is_title;
+ bool show_cursor = list_info->show_cursor;
+ bool have_icons = list_info->have_icons;
+ struct line_desc *linedes = list_info->linedes;
+ const char *dsp_text = list_info->dsp_text;
+
+ if (is_title)
+ {
+ if (have_icons)
+ display->put_line(x, y, linedes, "$"ICON_PADDING_S"I$t",
+ icon, dsp_text);
+ else
+ display->put_line(x, y, linedes, "$t", dsp_text);
+ }
+ else if (show_cursor && have_icons)
+ {
+ /* the list can have both, one of or neither of cursor and item icons,
+ * if both don't apply icon padding twice between the icons */
+ display->put_line(x, y,
+ linedes, "$*s$"ICON_PADDING_S"I$i$"ICON_PADDING_S"s$*t",
+ item_indent, is_selected ? Icon_Cursor : Icon_NOICON,
+ icon, item_offset, dsp_text);
+ }
+ else if (show_cursor || have_icons)
+ {
+ display->put_line(x, y, linedes, "$*s$"ICON_PADDING_S"I$*t", item_indent,
+ show_cursor ? (is_selected ? Icon_Cursor:Icon_NOICON):icon,
+ item_offset, dsp_text);
+ }
+ else
+ {
+ display->put_line(x, y, linedes, "$*s$*t", item_indent, item_offset, dsp_text);
+ }
+}
+
+static bool draw_title(struct screen *display,
+ struct gui_synclist *list,
+ list_draw_item *callback_draw_item)
{
const int screen = display->screen_type;
struct viewport *title_text_vp = &title_text[screen];
- struct line_desc line = LINE_DESC_DEFINIT;
+ struct line_desc linedes = LINE_DESC_DEFINIT;
if (sb_set_title_text(list->title, list->title_icon, screen))
return false; /* the sbs is handling the title */
@@ -103,29 +148,41 @@ static bool draw_title(struct screen *display, struct gui_synclist *list)
if (!list_display_title(list, screen))
return false;
*title_text_vp = *(list->parent[screen]);
- line.height = list->line_height[screen];
- title_text_vp->height = line.height;
+ linedes.height = list->line_height[screen];
+ title_text_vp->height = linedes.height;
#if LCD_DEPTH > 1
/* XXX: Do we want to support the separator on remote displays? */
if (display->screen_type == SCREEN_MAIN && global_settings.list_separator_height != 0)
- line.separator_height = abs(global_settings.list_separator_height)
+ linedes.separator_height = abs(global_settings.list_separator_height)
+ (lcd_get_dpi() > 200 ? 2 : 1);
#endif
#ifdef HAVE_LCD_COLOR
if (list->title_color >= 0)
- line.style |= (STYLE_COLORED|list->title_color);
+ linedes.style |= (STYLE_COLORED|list->title_color);
#endif
- line.scroll = true;
+ linedes.scroll = true;
display->set_viewport(title_text_vp);
+ int icon = list->title_icon;
+ int icon_w = list_icon_width(display->screen_type);
+ bool have_icons = false;
+ if (icon != Icon_NOICON && list->show_icons)
+ {
+ have_icons = true;
+ }
- if (list->title_icon != Icon_NOICON && global_settings.show_icons)
- put_line(display, 0, 0, &line, "$"ICON_PADDING_S"I$t",
- list->title_icon, list->title);
- else
- put_line(display, 0, 0, &line, "$t", list->title);
+ struct list_putlineinfo_t list_info =
+ {
+ .x = 0, .y = 0, .item_indent = 0, .item_offset = 0,
+ .line = -1, .icon = icon, .icon_width = icon_w,
+ .display = display, .vp = title_text_vp, .linedes = &linedes, .list = list,
+ .dsp_text = list->title,
+ .is_selected = false, .is_title = true, .show_cursor = false,
+ .have_icons = have_icons
+ };
+ callback_draw_item(&list_info);
return true;
}
@@ -134,23 +191,30 @@ void list_draw(struct screen *display, struct gui_synclist *list)
{
int start, end, item_offset, i;
const int screen = display->screen_type;
+ list_draw_item *callback_draw_item;
+
const int list_start_item = list->start_item[screen];
- const bool scrollbar_in_left = (global_settings.scrollbar == SCROLLBAR_LEFT);
- const bool scrollbar_in_right = (global_settings.scrollbar == SCROLLBAR_RIGHT);
- const bool show_cursor = !global_settings.cursor_style &&
- list->show_selection_marker;
- const bool have_icons = global_settings.show_icons && list->callback_get_item_icon;
+ const bool scrollbar_in_left = (list->scrollbar == SCROLLBAR_LEFT);
+ const bool scrollbar_in_right = (list->scrollbar == SCROLLBAR_RIGHT);
+ const bool show_cursor = (list->cursor_style == SYNCLIST_CURSOR_NOSTYLE);
+ const bool have_icons = list->callback_get_item_icon && list->show_icons;
+
struct viewport *parent = (list->parent[screen]);
struct line_desc linedes = LINE_DESC_DEFINIT;
bool show_title;
struct viewport *list_text_vp = &list_text[screen];
int indent = 0;
+ if (list->callback_draw_item != NULL)
+ callback_draw_item = list->callback_draw_item;
+ else
+ callback_draw_item = _default_listdraw_fn;
+
struct viewport * last_vp = display->set_viewport(parent);
display->clear_viewport();
display->scroll_stop_viewport(list_text_vp);
*list_text_vp = *parent;
- if ((show_title = draw_title(display, list)))
+ if ((show_title = draw_title(display, list, callback_draw_item)))
{
int title_height = title_text[screen].height;
list_text_vp->y += title_height;
@@ -170,12 +234,15 @@ void list_draw(struct screen *display, struct gui_synclist *list)
end = start + nb_lines;
#ifdef HAVE_TOUCHSCREEN
- if (list->selected_item == 0 || (list->nb_items < nb_lines))
- y_offset = 0; /* reset in case it's a new list */
+ /* y_pos needs to be clamped now since it can overflow the maximum
+ * in some cases, and we have no easy way to prevent this beforehand */
+ int max_y_pos = list->nb_items * linedes.height - list_text[screen].height;
+ if (max_y_pos > 0 && list->y_pos > max_y_pos)
+ list->y_pos = max_y_pos;
- int draw_offset = y_offset;
+ int draw_offset = list_start_item * linedes.height - list->y_pos;
/* draw some extra items to not have empty lines at the top and bottom */
- if (y_offset > 0)
+ if (draw_offset > 0)
{
/* make it negative for more consistent apparence when switching
* directions */
@@ -183,31 +250,55 @@ void list_draw(struct screen *display, struct gui_synclist *list)
if (start > 0)
start--;
}
- else if (y_offset < 0)
- end++;
+ else if (draw_offset < 0) {
+ if(end < list->nb_items)
+ end++;
+ }
+
+ /* If the viewport is not an exact multiple of the line height, then
+ * there will be space for one more partial line. */
+ int spare_space = list_text_vp->height - linedes.height * nb_lines;
+ if(nb_lines < list->nb_items && spare_space > 0 && end < list->nb_items)
+ if(end < list->nb_items)
+ end++;
#else
#define draw_offset 0
#endif
/* draw the scrollbar if its needed */
- if (global_settings.scrollbar != SCROLLBAR_OFF)
+ if (list->scrollbar != SCROLLBAR_OFF)
{
/* if the scrollbar is shown the text viewport needs to shrink */
if (nb_lines < list->nb_items)
{
struct viewport vp = *list_text_vp;
vp.width = SCROLLBAR_WIDTH;
+#ifndef HAVE_TOUCHSCREEN
+ /* touchscreens must use full viewport height
+ * due to pixelwise rendering */
vp.height = linedes.height * nb_lines;
+#endif
list_text_vp->width -= SCROLLBAR_WIDTH;
if (scrollbar_in_right)
vp.x += list_text_vp->width;
else /* left */
list_text_vp->x += SCROLLBAR_WIDTH;
struct viewport *last = display->set_viewport(&vp);
+
+#ifndef HAVE_TOUCHSCREEN
+ /* button targets go itemwise */
+ int scrollbar_items = list->nb_items;
+ int scrollbar_min = list_start_item;
+ int scrollbar_max = list_start_item + nb_lines;
+#else
+ /* touchscreens use pixelwise scrolling */
+ int scrollbar_items = list->nb_items * linedes.height;
+ int scrollbar_min = list->y_pos;
+ int scrollbar_max = list->y_pos + list_text_vp->height;
+#endif
gui_scrollbar_draw(display,
(scrollbar_in_left? 0: 1), 0, SCROLLBAR_WIDTH-1, vp.height,
- list->nb_items, list_start_item, list_start_item + nb_lines,
- VERTICAL);
+ scrollbar_items, scrollbar_min, scrollbar_max, VERTICAL);
display->set_viewport(last);
}
/* shift everything a bit in relation to the title */
@@ -218,6 +309,16 @@ void list_draw(struct screen *display, struct gui_synclist *list)
}
display->set_viewport(list_text_vp);
+ int icon_w = list_icon_width(screen);
+ int character_width = display->getcharwidth();
+
+ struct list_putlineinfo_t list_info =
+ {
+ .x = 0, .y = 0, .vp = list_text_vp, .list = list,
+ .icon_width = icon_w, .is_title = false, .show_cursor = show_cursor,
+ .have_icons = have_icons, .linedes = &linedes, .display = display
+ };
+
for (i=start; i<end && i<list->nb_items; i++)
{
/* do the text */
@@ -225,14 +326,16 @@ void list_draw(struct screen *display, struct gui_synclist *list)
unsigned const char *s;
char entry_buffer[MAX_PATH];
unsigned char *entry_name;
- int text_pos = 0;
int line = i - start;
int line_indent = 0;
int style = STYLE_DEFAULT;
bool is_selected = false;
s = list->callback_get_item_name(i, list->data, entry_buffer,
sizeof(entry_buffer));
- entry_name = P2STR(s);
+ if (P2ID((unsigned char *)s) > VOICEONLY_DELIMITER)
+ entry_name = "";
+ else
+ entry_name = P2STR(s);
while (*entry_name == '\t')
{
@@ -241,17 +344,17 @@ void list_draw(struct screen *display, struct gui_synclist *list)
}
if (line_indent)
{
- if (global_settings.show_icons)
- line_indent *= list_icon_width(screen);
+ if (list->show_icons)
+ line_indent *= icon_w;
else
- line_indent *= display->getcharwidth();
+ line_indent *= character_width;
}
line_indent += indent;
/* position the string at the correct offset place */
int item_width,h;
display->getstringsize(entry_name, &item_width, &h);
- item_offset = gui_list_get_item_offset(list, item_width, text_pos,
+ item_offset = gui_list_get_item_offset(list, item_width, indent + (list->show_icons ? icon_w : 0),
display, list_text_vp);
/* draw the selected line */
@@ -261,8 +364,7 @@ void list_draw(struct screen *display, struct gui_synclist *list)
!hide_selection &&
#endif
i >= list->selected_item
- && i < list->selected_item + list->selected_size
- && list->show_selection_marker)
+ && i < list->selected_item + list->selected_size)
{/* The selected item must be displayed scrolling */
#ifdef HAVE_LCD_COLOR
if (list->selection_color)
@@ -275,12 +377,12 @@ void list_draw(struct screen *display, struct gui_synclist *list)
}
else
#endif
- if (global_settings.cursor_style == 1
+ if (list->cursor_style == SYNCLIST_CURSOR_INVERT
#ifdef HAVE_REMOTE_LCD
/* the global_settings.cursor_style check is here to make
* sure if they want the cursor instead of bar it will work
*/
- || (display->depth < 16 && global_settings.cursor_style)
+ || (display->depth < 16 && list->cursor_style)
#endif
)
{
@@ -288,14 +390,14 @@ void list_draw(struct screen *display, struct gui_synclist *list)
style = STYLE_INVERT;
}
#ifdef HAVE_LCD_COLOR
- else if (global_settings.cursor_style == 2)
+ else if (list->cursor_style == SYNCLIST_CURSOR_COLOR)
{
/* Display colour line selector */
style = STYLE_COLORBAR;
linedes.text_color = global_settings.lst_color;
linedes.line_color = global_settings.lss_color;
}
- else if (global_settings.cursor_style == 3)
+ else if (list->cursor_style == SYNCLIST_CURSOR_GRADIENT)
{
/* Display gradient line selector */
style = STYLE_GRADIENT;
@@ -319,27 +421,22 @@ void list_draw(struct screen *display, struct gui_synclist *list)
}
}
#endif
-
linedes.style = style;
linedes.scroll = is_selected ? true : list->scroll_all;
linedes.line = i % list->selected_size;
icon = list->callback_get_item_icon ?
list->callback_get_item_icon(i, list->data) : Icon_NOICON;
- /* the list can have both, one of or neither of cursor and item icons,
- * if both don't apply icon padding twice between the icons */
- if (show_cursor && have_icons)
- put_line(display, 0, line * linedes.height + draw_offset,
- &linedes, "$*s$"ICON_PADDING_S"I$i$"ICON_PADDING_S"s$*t",
- line_indent, is_selected ? Icon_Cursor : Icon_NOICON,
- icon, item_offset, entry_name);
- else if (show_cursor || have_icons)
- put_line(display, 0, line * linedes.height + draw_offset,
- &linedes, "$*s$"ICON_PADDING_S"I$*t", line_indent,
- show_cursor ? (is_selected ? Icon_Cursor:Icon_NOICON):icon,
- item_offset, entry_name);
- else
- put_line(display, 0, line * linedes.height + draw_offset,
- &linedes, "$*s$*t", line_indent, item_offset, entry_name);
+
+
+ list_info.y = line * linedes.height + draw_offset;
+ list_info.is_selected = is_selected;
+ list_info.item_indent = line_indent;
+ list_info.line = i;
+ list_info.icon = icon;
+ list_info.dsp_text = entry_name;
+ list_info.item_offset = item_offset;
+
+ callback_draw_item(&list_info);
}
display->set_viewport(parent);
display->update_viewport();
@@ -359,29 +456,33 @@ static enum {
SCROLL_KINETIC, /* state after releasing swipe */
} scroll_mode;
-static int scrollbar_scroll(struct gui_synclist * gui_list,
- int y)
+static int scrollbar_scroll(struct gui_synclist * gui_list, int y)
{
const int screen = screens[SCREEN_MAIN].screen_type;
const int nb_lines = list_get_nb_lines(gui_list, screen);
- if (nb_lines < gui_list->nb_items)
+ if (nb_lines < gui_list->nb_items)
{
- /* scrollbar scrolling is still line based */
- y_offset = 0;
- int scrollbar_size = nb_lines*gui_list->line_height[screen];
- int actual_y = y - list_text[screen].y;
-
- int new_selection = (actual_y * gui_list->nb_items)
- / scrollbar_size;
+ const int line_height = gui_list->line_height[screen];
- int start_item = new_selection - nb_lines/2;
- if(start_item < 0)
- start_item = 0;
- else if(start_item > gui_list->nb_items - nb_lines)
- start_item = gui_list->nb_items - nb_lines;
+ /* try to position the center of the scrollbar at the touch point */
+ int scrollbar_size = list_text[screen].height;
+ int actual_y = y - list_text[screen].y;
+ int new_y_pos = (actual_y * gui_list->nb_items * line_height) / scrollbar_size;
+ int new_start = (actual_y * gui_list->nb_items) / scrollbar_size;
+
+ new_start -= nb_lines / 2;
+ new_y_pos -= (nb_lines * line_height) / 2;
+ if(new_start < 0) {
+ new_start = 0;
+ new_y_pos = 0;
+ } else if(new_start > gui_list->nb_items - nb_lines) {
+ new_start = gui_list->nb_items - nb_lines;
+ new_y_pos = new_start * line_height;
+ }
- gui_list->start_item[screen] = start_item;
+ gui_list->start_item[screen] = new_start;
+ gui_list->y_pos = new_y_pos;
return ACTION_REDRAW;
}
@@ -471,9 +572,11 @@ static void kinetic_force_stop(void)
/* helper for gui/list.c to cancel scrolling if a normal button event comes
* through dpad or keyboard or whatever */
-void _gui_synclist_stop_kinetic_scrolling(void)
+void _gui_synclist_stop_kinetic_scrolling(struct gui_synclist * gui_list)
{
- y_offset = 0;
+ const enum screen_type screen = screens[SCREEN_MAIN].screen_type;
+ gui_list->y_pos = gui_list->start_item[screen] * gui_list->line_height[screen];
+
if (scroll_mode == SCROLL_KINETIC)
kinetic_force_stop();
scroll_mode = SCROLL_NONE;
@@ -514,23 +617,27 @@ static bool swipe_scroll(struct gui_synclist * gui_list, int difference)
const int old_start = gui_list->start_item[screen];
int new_start_item = -1;
int line_diff = 0;
+ int max_y_pos = gui_list->nb_items * line_height - list_text[screen].height;
- /* don't scroll at the edges of the list */
- if ((old_start == 0 && difference > 0)
- || (old_start == (gui_list->nb_items - nb_lines) && difference < 0))
- {
- y_offset = 0;
- gui_list->start_item[screen] = old_start;
- return scroll_mode != SCROLL_KINETIC; /* stop kinetic at the edges */
- }
+ /* Track whether we hit the end of the list for sake of kinetic scroll */
+ bool hit_end = true;
- /* add up y_offset over time and translate to lines
- * if scrolled enough */
- y_offset += difference;
- if (abs(y_offset) > line_height)
+ /* Move the y position and clamp it (funny things happen otherwise...) */
+ gui_list->y_pos -= difference;
+ if(gui_list->y_pos < 0)
+ gui_list->y_pos = 0;
+ else if(gui_list->y_pos > max_y_pos)
+ gui_list->y_pos = max_y_pos;
+ else
+ hit_end = false;
+
+ /* Get the list y position. When pos_y differs by a line height or more,
+ * we need to scroll the list by adjusting the start item accordingly */
+ int cur_y = gui_list->start_item[screen] * line_height;
+ int diff_y = cur_y - gui_list->y_pos;
+ if (abs(diff_y) >= line_height)
{
- line_diff = y_offset/line_height;
- y_offset -= line_diff * line_height;
+ line_diff = diff_y/line_height;
}
if(line_diff != 0)
@@ -547,9 +654,14 @@ static bool swipe_scroll(struct gui_synclist * gui_list, int difference)
gui_list->start_item[screen] = new_start_item;
/* keep selected item in sync */
gui_list->selected_item = new_start_item + selection_offset;
+ if(gui_list->selected_size > 1)
+ gui_list->selected_item -= (gui_list->selected_item % gui_list->selected_size);
}
- return true;
+ if(hit_end)
+ return scroll_mode != SCROLL_KINETIC;
+ else
+ return true;
}
static int kinetic_callback(struct timeout *tmo)
@@ -644,7 +756,7 @@ static int get_click_location(struct gui_synclist *list, int x, int y)
if (viewport_point_within_vp(title, x, y))
retval = TITLE_TEXT;
/* check the icon too */
- if (list->title_icon != Icon_NOICON && global_settings.show_icons)
+ if (list->title_icon != Icon_NOICON && list->show_icons)
{
int width = list_icon_width(screen);
struct viewport vp = *title;
@@ -662,14 +774,19 @@ static int get_click_location(struct gui_synclist *list, int x, int y)
{
bool on_scrollbar_clicked;
int adj_x = x - parent->x;
- switch (global_settings.scrollbar)
+ switch (list->scrollbar)
{
+ case SCROLLBAR_OFF:
+ /*fall-through*/
+ default:
+ on_scrollbar_clicked = false;
+ break;
case SCROLLBAR_LEFT:
- on_scrollbar_clicked = adj_x <= SCROLLBAR_WIDTH; break;
+ on_scrollbar_clicked = adj_x <= SCROLLBAR_WIDTH;
+ break;
case SCROLLBAR_RIGHT:
- on_scrollbar_clicked = adj_x > (title->x + title->width - SCROLLBAR_WIDTH); break;
- default:
- on_scrollbar_clicked = false; break;
+ on_scrollbar_clicked = adj_x > (title->x + title->width - SCROLLBAR_WIDTH);
+ break;
}
if (on_scrollbar_clicked)
retval = SCROLLBAR;
@@ -730,13 +847,16 @@ unsigned gui_synclist_do_touchscreen(struct gui_synclist * list)
if(!skinlist_get_item(&screens[screen], list, adj_x, adj_y, &line))
{
/* selection needs to be corrected if items are only partially visible */
- line = (adj_y - y_offset) / line_height;
+ int cur_y = list->start_item[screen] * line_height;
+ line = (adj_y - (cur_y - list->y_pos)) / line_height;
if (list_display_title(list, screen))
line -= 1; /* adjust for the list title */
}
- if (line >= list->nb_items)
+ if (list_start_item+line >= list->nb_items)
return ACTION_NONE;
list->selected_item = list_start_item+line;
+ if(list->selected_size > 1)
+ list->selected_item -= (list->selected_item % list->selected_size);
gui_synclist_speak_item(list);
}
@@ -754,7 +874,7 @@ unsigned gui_synclist_do_touchscreen(struct gui_synclist * list)
if (click_loc & LIST)
{
/* held a single line for a while, bring up the context menu */
- gui_synclist_select_item(list, list_start_item + line);
+ gui_synclist_select_item(list, list->selected_item);
/* don't sent context repeatedly */
action_wait_for_release();
initial_touch = true;
@@ -766,7 +886,7 @@ unsigned gui_synclist_do_touchscreen(struct gui_synclist * list)
initial_touch = true;
if (click_loc & LIST)
{ /* release on list item enters it */
- gui_synclist_select_item(list, list_start_item + line);
+ gui_synclist_select_item(list, list->selected_item);
return ACTION_STD_OK;
}
else if (click_loc & TITLE_TEXT)
@@ -835,7 +955,7 @@ unsigned gui_synclist_do_touchscreen(struct gui_synclist * list)
hide_selection = true;
/* similarly to swipe scroll, using the scrollbar grabs
* focus so the click location is irrelevant */
- scrollbar_scroll(list, adj_y);
+ scrollbar_scroll(list, y);
if (action & BUTTON_REL)
scroll_mode = SCROLL_NONE;
break;
diff --git a/apps/gui/color_picker.c b/apps/gui/color_picker.c
index a32f1ee179..ef17c0a230 100644
--- a/apps/gui/color_picker.c
+++ b/apps/gui/color_picker.c
@@ -154,7 +154,6 @@ static void draw_screen(struct screen *display, char *title,
{
unsigned text_color = LCD_BLACK;
unsigned background_color = LCD_WHITE;
- char buf[32];
int i, char_height, line_height;
int max_label_width;
int text_x, text_top;
@@ -253,17 +252,16 @@ static void draw_screen(struct screen *display, char *title,
set_drawinfo(display, mode, fg, bg);
/* Draw label */
- buf[0] = str(LANG_COLOR_RGB_LABELS)[i];
- buf[1] = '\0';
vp.flags &= ~VP_FLAG_ALIGNMENT_MASK;
- display->putsxy(text_x, text_top, buf);
+ display->putsxyf(text_x, text_top, "%c", str(LANG_COLOR_RGB_LABELS)[i]);
/* Draw color value */
+ vp.flags |= VP_FLAG_ALIGN_RIGHT;
if (display->depth >= 24)
- snprintf(buf, 4, "%03d", rgb->rgb_val[i] & 0xFF);
+ display->putsxyf(text_x, text_top, "%03d", rgb->rgb_val[i] & 0xFF);
else
- snprintf(buf, 3, "%02d", rgb->rgb_val[i] & 0x3F);
- vp.flags |= VP_FLAG_ALIGN_RIGHT;
- display->putsxy(text_x, text_top, buf);
+ display->putsxyf(text_x, text_top, "%02d", rgb->rgb_val[i] & 0x3F);
+
+
/* Draw scrollbar */
gui_scrollbar_draw(display, /* screen */
@@ -280,9 +278,6 @@ static void draw_screen(struct screen *display, char *title,
text_top += line_height;
} /* end for */
- /* Format RGB: #rrggbb */
- snprintf(buf, sizeof(buf), str(LANG_COLOR_RGB_VALUE),
- rgb->red, rgb->green, rgb->blue);
vp.flags |= VP_FLAG_ALIGN_CENTER;
if (display->depth >= 16)
{
@@ -301,8 +296,9 @@ static void draw_screen(struct screen *display, char *title,
/* Draw RGB: #rrggbb in middle of swatch */
set_drawinfo(display, DRMODE_FG, get_black_or_white(rgb),
background_color);
-
- display->putsxy(0, top + (height - char_height) / 2, buf);
+ /* Format RGB: #rrggbb */
+ display->putsxyf(0, top + (height - char_height) / 2,
+ str(LANG_COLOR_RGB_VALUE), rgb->red, rgb->green, rgb->blue);
/* Draw border around the rect */
set_drawinfo(display, DRMODE_SOLID, text_color, background_color);
@@ -318,7 +314,9 @@ static void draw_screen(struct screen *display, char *title,
if (height >= char_height)
{
set_drawinfo(display, DRMODE_SOLID, text_color, background_color);
- display->putsxy(0, top + (height - char_height) / 2, buf);
+ /* Format RGB: #rrggbb */
+ display->putsxyf(0, top + (height - char_height) / 2,
+ str(LANG_COLOR_RGB_VALUE), rgb->red, rgb->green, rgb->blue);
}
}
diff --git a/apps/gui/folder_select.c b/apps/gui/folder_select.c
index f2830fb8fd..a76d77562b 100644
--- a/apps/gui/folder_select.c
+++ b/apps/gui/folder_select.c
@@ -8,6 +8,7 @@
*
* Copyright (C) 2012 Jonathan Gordon
* Copyright (C) 2012 Thomas Martitz
+* * Copyright (C) 2021 William Wilgus
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -30,7 +31,11 @@
#include "language.h"
#include "list.h"
#include "plugin.h"
+#include "splash.h"
+/* Define LOGF_ENABLE to enable logf output in this file */
+//#define LOGF_ENABLE
+#include "logf.h"
/*
* Order for changing child states:
@@ -56,18 +61,31 @@ struct child {
struct folder {
char *name;
struct child *children;
- int children_count;
- int depth;
-
struct folder* previous;
+ uint16_t children_count;
+ uint16_t depth;
};
static char *buffer_front, *buffer_end;
+
+static struct
+{
+ int32_t len; /* keeps count versus maxlen to give buffer full notification */
+ uint32_t val; /* hash of all selected items */
+ char buf[3];/* address used as identifier -- only \0 written to it */
+ char maxlen_exceeded; /*0,1*/
+} hashed;
+
+static inline void get_hash(const char *key, uint32_t *hash, int len)
+{
+ *hash = crc_32(key, len, *hash);
+}
+
static char* folder_alloc(size_t size)
{
char* retval;
/* 32-bit aligned */
- size = (size + 3) & ~3;
+ size = ALIGN_UP(size, 4);
if (buffer_front + size > buffer_end)
{
return NULL;
@@ -86,32 +104,57 @@ static char* folder_alloc_from_end(size_t size)
buffer_end -= size;
return buffer_end;
}
-
-static void get_full_path_r(struct folder *start, char* dst)
+#if 0
+/* returns the buffer size required to store the path + \0 */
+static int get_full_pathsz(struct folder *start)
{
- if (start->previous)
- get_full_path_r(start->previous, dst);
-
- if (start->name && start->name[0] && strcmp(start->name, "/"))
+ int reql = 0;
+ struct folder *next = start;
+ do
{
- strlcat(dst, "/", MAX_PATH);
- strlcat(dst, start->name, MAX_PATH);
- }
+ reql += strlen(next->name) + 1;
+ } while ((next = next->previous));
+
+ if (start->name[0] != '/') reql--;
+ if (--reql < 0) reql = 0;
+ return reql;
}
+#endif
-static char* get_full_path(struct folder *start)
+static size_t get_full_path(struct folder *start, char *dst, size_t dst_sz)
{
- static char buffer[MAX_PATH];
-
- if (strcmp(start->name, "/"))
+ size_t pos = 0;
+ struct folder *prev, *cur = NULL, *next = start;
+ dst[0] = '\0'; /* for strlcat to do its thing */
+ /* First traversal R->L mutate nodes->previous to point at child */
+ while (next->previous != NULL) /* stop at the root */
{
- buffer[0] = 0;
- get_full_path_r(start, buffer);
+#define PATHMUTATE() \
+ ({ \
+ prev = cur; \
+ cur = next; \
+ next = cur->previous;\
+ cur->previous = prev; \
+ })
+ PATHMUTATE();
}
- else /* get_full_path_r() does the wrong thing for / */
- return "/";
-
- return buffer;
+ /*swap the next and cur nodes to reverse direction */
+ prev = next;
+ next = cur;
+ cur = prev;
+ /* Second traversal L->R mutate nodes->previous to point back at parent
+ * copy strings to buf as they go by */
+ while (next != NULL)
+ {
+ PATHMUTATE();
+ pos = strlcat(dst, cur->name, dst_sz);
+ /* do not append slash to paths starting with slash */
+ if (cur->name[0] != '/')
+ pos = strlcat(dst, "/", dst_sz);
+ }
+ logf("get_full_path: (%d)[%s]", (int)pos, dst);
+ return pos;
+#undef PATHMUTATE
}
/* support function for qsort() */
@@ -125,47 +168,53 @@ static int compare(const void* p1, const void* p2)
static struct folder* load_folder(struct folder* parent, char *folder)
{
DIR *dir;
- char* path = get_full_path(parent);
char fullpath[MAX_PATH];
+
struct dirent *entry;
- struct folder* this = (struct folder*)folder_alloc(sizeof(struct folder));
int child_count = 0;
char *first_child = NULL;
+ size_t len = 0;
- if (!strcmp(folder,"/"))
- strlcpy(fullpath, folder, 2);
- else
- snprintf(fullpath, MAX_PATH, "%s/%s", parent ? path : "", folder);
+ struct folder* this = (struct folder*)folder_alloc(sizeof(struct folder));
+ if (this == NULL)
+ goto fail;
+
+ if (parent)
+ {
+ len = get_full_path(parent, fullpath, sizeof(fullpath));
+ if (len >= sizeof(fullpath))
+ goto fail;
+ }
+ strmemccpy(&fullpath[len], folder, sizeof(fullpath) - len);
+ logf("load_folder: [%s]", fullpath);
- if (!this)
- return NULL;
dir = opendir(fullpath);
- if (!dir)
- return NULL;
+ if (dir == NULL)
+ goto fail;
this->previous = parent;
this->name = folder;
this->children = NULL;
this->children_count = 0;
- this->depth = parent ? parent->depth + 1 : 0;
+ if (parent)
+ this->depth = parent->depth + 1;
while ((entry = readdir(dir))) {
- int len = strlen((char *)entry->d_name);
- struct dirinfo info;
-
- info = dir_get_info(dir, entry);
-
/* skip anything not a directory */
- if ((info.attribute & ATTR_DIRECTORY) == 0) {
+ if ((dir_get_info(dir, entry).attribute & ATTR_DIRECTORY) == 0) {
continue;
}
- /* skip directories . and .. */
- if ((!strcmp((char *)entry->d_name, ".")) ||
- (!strcmp((char *)entry->d_name, ".."))) {
+ /* skip . and .. */
+ char *dn = entry->d_name;
+ if ((dn[0] == '.') && (dn[1] == '\0' || (dn[1] == '.' && dn[2] == '\0')))
continue;
+ /* copy entry name to end of buffer, save pointer */
+ len = strlen((char *)entry->d_name);
+ char *name = folder_alloc_from_end(len+1); /*for NULL*/
+ if (name == NULL)
+ {
+ closedir(dir);
+ goto fail;
}
- char *name = folder_alloc_from_end(len+1);
- if (!name)
- return NULL;
memcpy(name, (char *)entry->d_name, len+1);
child_count++;
first_child = name;
@@ -174,26 +223,29 @@ static struct folder* load_folder(struct folder* parent, char *folder)
/* now put the names in the array */
this->children = (struct child*)folder_alloc(sizeof(struct child) * child_count);
- if (!this->children)
- return NULL;
+ if (this->children == NULL)
+ goto fail;
+
while (child_count)
{
- this->children[this->children_count].name = first_child;
- this->children[this->children_count].folder = NULL;
- this->children[this->children_count].state = COLLAPSED;
- this->children_count++;
- first_child += strlen(first_child) + 1;
+ struct child *child = &this->children[this->children_count++];
+ child->name = first_child;
+ child->folder = NULL;
+ child->state = COLLAPSED;
+ while(*first_child++ != '\0'){};/* move to next name entry */
child_count--;
}
qsort(this->children, this->children_count, sizeof(struct child), compare);
return this;
+fail:
+ return NULL;
}
struct folder* load_root(void)
{
static struct child root_child;
-
+ /* reset the root for each call */
root_child.name = "/";
root_child.folder = NULL;
root_child.state = COLLAPSED;
@@ -202,7 +254,7 @@ struct folder* load_root(void)
.name = "",
.children = &root_child,
.children_count = 1,
- .depth = -1,
+ .depth = 0,
.previous = NULL,
};
@@ -227,7 +279,6 @@ static int count_items(struct folder *start)
static struct child* find_index(struct folder *start, int index, struct folder **parent)
{
int i = 0;
-
*parent = NULL;
while (i < start->children_count)
@@ -259,22 +310,22 @@ static const char * folder_get_name(int selected_item, void * data,
struct folder *parent;
struct child *this = find_index(root, selected_item , &parent);
- buffer[0] = '\0';
-
- if (parent->depth >= 0)
- for(int i = 0; i <= parent->depth; i++)
- strcat(buffer, "\t");
-
+ char *buf = buffer;
+ if ((int)buffer_len > parent->depth)
+ {
+ int i = parent->depth;
+ while(--i > 0) /* don't indent the parent /folders */
+ *buf++ = '\t';
+ }
+ *buf = '\0';
strlcat(buffer, this->name, buffer_len);
if (this->state == EACCESS)
{ /* append error message to the entry if unaccessible */
- size_t len = strlcat(buffer, " (", buffer_len);
+ size_t len = strlcat(buffer, " ( ", buffer_len);
if (buffer_len > len)
{
- snprintf(&buffer[len], buffer_len - len, str(LANG_READ_FAILED),
- this->name);
- strlcat(buffer, ")", buffer_len);
+ snprintf(&buffer[len], buffer_len - len, str(LANG_READ_FAILED), ")");
}
}
@@ -301,6 +352,23 @@ static enum themable_icons folder_get_icon(int selected_item, void * data)
return Icon_NOICON;
}
+static int child_set_state_expand(struct child *this, struct folder *parent)
+{
+ int newstate = EACCESS;
+ if (this->folder == NULL)
+ this->folder = load_folder(parent, this->name);
+
+ if (this->folder != NULL)
+ {
+ if(this->folder->children_count == 0)
+ newstate = SELECTED;
+ else
+ newstate = EXPANDED;
+ }
+ this->state = newstate;
+ return newstate;
+}
+
static int folder_action_callback(int action, struct gui_synclist *list)
{
struct folder *root = (struct folder*)list->data;
@@ -319,17 +387,13 @@ static int folder_action_callback(int action, struct gui_synclist *list)
this->state = COLLAPSED;
break;
case COLLAPSED:
- if (this->folder == NULL)
- this->folder = load_folder(parent, this->name);
- this->state = this->folder ? (this->folder->children_count == 0 ?
- SELECTED : EXPANDED) : EACCESS;
+ child_set_state_expand(this, parent);
break;
case EACCESS:
/* cannot open, do nothing */
return action;
}
- list->nb_items = count_items(root);
- return ACTION_REDRAW;
+ action = ACTION_REDRAW;
}
else if (action == ACTION_STD_CONTEXT)
{
@@ -339,140 +403,198 @@ static int folder_action_callback(int action, struct gui_synclist *list)
for (i = 0; i < this->folder->children_count; i++)
{
child = &this->folder->children[i];
- if (child->state == SELECTED ||
- child->state == EXPANDED)
- child->state = COLLAPSED;
- else if (child->state == COLLAPSED)
- child->state = SELECTED;
+ switch (child->state)
+ {
+ case SELECTED:
+ case EXPANDED:
+ child->state = COLLAPSED;
+ break;
+ case COLLAPSED:
+ child->state = SELECTED;
+ break;
+ case EACCESS:
+ break;
+ }
}
break;
case SELECTED:
case COLLAPSED:
- if (this->folder == NULL)
- this->folder = load_folder(parent, this->name);
- this->state = this->folder ? (this->folder->children_count == 0 ?
- SELECTED : EXPANDED) : EACCESS;
- if (this->state == EACCESS)
- break;
- for (i = 0; i < this->folder->children_count; i++)
+ if (child_set_state_expand(this, parent) != EACCESS)
{
- child = &this->folder->children[i];
- child->state = SELECTED;
+ for (i = 0; i < (this->folder->children_count); i++)
+ {
+ child = &this->folder->children[i];
+ child->state = SELECTED;
+ }
}
break;
case EACCESS:
/* cannot open, do nothing */
return action;
}
- list->nb_items = count_items(root);
- return ACTION_REDRAW;
+ action = ACTION_REDRAW;
}
-
-
+ if (action == ACTION_REDRAW)
+ list->nb_items = count_items(root);
return action;
}
-static struct child* find_from_filename(char* filename, struct folder *root)
+static struct child* find_from_filename(const char* filename, struct folder *root)
{
- char *slash = strchr(filename, '/');
- int i = 0;
- if (slash)
- *slash = '\0';
if (!root)
return NULL;
-
+ const char *slash = strchr(filename, '/');
struct child *this;
/* filenames beginning with a / are specially treated as the
* loop below can't handle them. they can only occur on the first,
* and not recursive, calls to this function.*/
- if (slash == filename)
+ if (filename[0] == '/') /* in the loop nothing starts with '/' */
{
+ logf("find_from_filename [%s]", filename);
/* filename begins with /. in this case root must be the
* top level folder */
this = &root->children[0];
- if (!slash[1])
+ if (filename[1] == '\0')
{ /* filename == "/" */
return this;
}
- else
- {
- /* filename == "/XXX/YYY". cascade down */
- if (!this->folder)
- this->folder = load_folder(root, this->name);
- this->state = EXPANDED;
- /* recurse with XXX/YYY */
- return find_from_filename(slash+1, this->folder);
- }
+ else /* filename == "/XXX/YYY". cascade down */
+ goto cascade;
}
- while (i < root->children_count)
+ for (int i = 0; i < root->children_count; i++)
{
this = &root->children[i];
- if (!strcasecmp(this->name, filename))
+ /* when slash == NULL n will be really large but \0 stops the compare */
+ if (strncasecmp(this->name, filename, slash - filename) == 0)
{
- if (!slash)
+ if (slash == NULL)
{ /* filename == XXX */
return this;
}
else
- {
- /* filename == XXX/YYY. cascade down */
- if (!this->folder)
- this->folder = load_folder(root, this->name);
- this->state = EXPANDED;
- return find_from_filename(slash+1, this->folder);
- }
+ goto cascade;
}
- i++;
}
return NULL;
+
+cascade:
+ /* filename == XXX/YYY. cascade down */
+ child_set_state_expand(this, root);
+ while (slash[0] == '/') slash++; /* eat slashes */
+ return find_from_filename(slash, this->folder);
}
-/* _modifies_ buf */
-int select_paths(struct folder* root, char* buf)
+static int select_paths(struct folder* root, const char* filenames)
{
- struct child *item = find_from_filename(buf, root);
- if (item)
- item->state = SELECTED;
+ /* Takes a list of filenames in a ':' delimited string
+ splits filenames at the ':' character loads each into buffer
+ selects each file in the folder list
+
+ if last item or only item the rest of the string is copied to the buffer
+ *End the last item WITHOUT the ':' character /.rockbox/eqs:/.rockbox/wps\0*
+ */
+ char buf[MAX_PATH];
+ const int buflen = sizeof(buf);
+
+ const char *fnp = filenames;
+ const char *lastfnp = fnp;
+ const char *sstr;
+ off_t len;
+
+ while (fnp)
+ {
+ fnp = strchr(fnp, ':');
+ if (fnp)
+ {
+ len = fnp - lastfnp;
+ fnp++;
+ }
+ else /* no ':' get the rest of the string */
+ len = strlen(lastfnp);
+
+ sstr = lastfnp;
+ lastfnp = fnp;
+ if (len <= 0 || len + 1 >= buflen)
+ continue;
+ strmemccpy(buf, sstr, len + 1);
+ struct child *item = find_from_filename(buf, root);
+ if (item)
+ item->state = SELECTED;
+ }
+
return 0;
}
-static void save_folders_r(struct folder *root, char* dst, size_t maxlen)
+static void save_folders_r(struct folder *root, char* dst, size_t maxlen, size_t buflen)
{
- int i = 0;
+ size_t len;
+ struct folder *curfolder;
+ char* name;
- while (i < root->children_count)
+ for (int i = 0; i < root->children_count; i++)
{
struct child *this = &root->children[i];
if (this->state == SELECTED)
{
- if (this->folder)
- snprintf(buffer_front, buffer_end - buffer_front,
- "%s:", get_full_path(this->folder));
+ if (this->folder == NULL)
+ {
+ curfolder = root;
+ name = this->name;
+ logf("save_folders_r: this->name[%s]", name);
+ }
else
{
- char *p = get_full_path(root);
- snprintf(buffer_front, buffer_end - buffer_front,
- "%s/%s:", strcmp(p, "/") ? p : "",
- strcmp(this->name, "/") ? this->name : "");
+ curfolder = this->folder->previous;
+ name = this->folder->name;
+ logf("save_folders_r: this->folder->name[%s]", name);
+ }
+
+ len = get_full_path(curfolder, buffer_front, buflen);
+
+ if (len + 2 >= buflen)
+ continue;
+
+ len += snprintf(&buffer_front[len], buflen - len, "%s:", name);
+ logf("save_folders_r: [%s]", buffer_front);
+ if (dst != hashed.buf)
+ {
+ int dlen = strlen(dst);
+ if (dlen + len >= maxlen)
+ continue;
+ strmemccpy(&dst[dlen], buffer_front, maxlen - dlen);
+ }
+ else
+ {
+ if (hashed.len + len >= maxlen)
+ {
+ hashed.maxlen_exceeded = 1;
+ continue;
+ }
+ get_hash(buffer_front, &hashed.val, len);
+ hashed.len += len;
}
- strlcat(dst, buffer_front, maxlen);
}
else if (this->state == EXPANDED)
- save_folders_r(this->folder, dst, maxlen);
- i++;
+ save_folders_r(this->folder, dst, maxlen, buflen);
}
}
-static void save_folders(struct folder *root, char* dst, size_t maxlen)
+static uint32_t save_folders(struct folder *root, char* dst, size_t maxlen)
{
- int len;
+ hashed.len = 0;
+ hashed.val = 0;
+ hashed.maxlen_exceeded = 0;
+ size_t len = buffer_end - buffer_front;
dst[0] = '\0';
- save_folders_r(root, dst, maxlen);
+ save_folders_r(root, dst, maxlen, len);
len = strlen(dst);
/* fix trailing ':' */
if (len > 1) dst[len-1] = '\0';
+ /*Notify - user will probably not see save dialog if nothing new got added*/
+ if (hashed.maxlen_exceeded > 0) splash(HZ *2, ID2P(LANG_SHOWDIR_BUFFER_FULL));
+ return hashed.val;
}
bool folder_select(char* setting, int setting_len)
@@ -480,40 +602,32 @@ bool folder_select(char* setting, int setting_len)
struct folder *root;
struct simplelist_info info;
size_t buf_size;
- /* 32 separate folders should be Enough For Everybody(TM) */
- char *vect[32];
- char copy[setting_len];
- int nb_items;
-
- /* copy onto stack as split_string() modifies it */
- strlcpy(copy, setting, setting_len);
- nb_items = split_string(copy, ':', vect, ARRAYLEN(vect));
buffer_front = plugin_get_buffer(&buf_size);
buffer_end = buffer_front + buf_size;
+ logf("%d bytes free", (int)(buffer_end - buffer_front));
root = load_root();
- if (nb_items > 0)
- {
- for(int i = 0; i < nb_items; i++)
- select_paths(root, vect[i]);
- }
-
+ logf("folders in: %s", setting);
+ /* Load previous selection(s) */
+ select_paths(root, setting);
+ /* get current hash to check for changes later */
+ uint32_t hash = save_folders(root, hashed.buf, setting_len);
simplelist_info_init(&info, str(LANG_SELECT_FOLDER),
count_items(root), root);
info.get_name = folder_get_name;
info.action_callback = folder_action_callback;
info.get_icon = folder_get_icon;
simplelist_show_list(&info);
-
+ logf("%d bytes free", (int)(buffer_end - buffer_front));
/* done editing. check for changes */
- save_folders(root, copy, setting_len);
- if (strcmp(copy, setting))
- { /* prompt for saving changes and commit if yes */
+ if (hash != save_folders(root, hashed.buf, setting_len))
+ { /* prompt for saving changes and commit if yes */
if (yesno_pop(ID2P(LANG_SAVE_CHANGES)))
{
- strcpy(setting, copy);
+ save_folders(root, setting, setting_len);
settings_save();
+ logf("folders out: %s", setting);
return true;
}
}
diff --git a/apps/gui/icon.c b/apps/gui/icon.c
index 25c0b372fa..7a59a72151 100644
--- a/apps/gui/icon.c
+++ b/apps/gui/icon.c
@@ -32,6 +32,7 @@
#include "bmp.h"
#include "filetypes.h"
#include "language.h"
+#include "misc.h"
#include "bitmaps/default_icons.h"
#if defined(HAVE_REMOTE_LCD) && (NB_SCREENS > 1)
@@ -63,7 +64,6 @@ static struct iconset {
struct bitmap bmp;
bool loaded;
int handle;
- int handle_locked;
} iconsets[Iconset_Count][NB_SCREENS];
#define ICON_HEIGHT(screen) (!iconsets[Iconset_user][screen].loaded ? \
@@ -74,13 +74,13 @@ static struct iconset {
(*(inbuilt_iconset[screen])) : iconsets[Iconset_user][screen].bmp).width
/* x,y in letters, not pixles */
-void screen_put_icon(struct screen * display,
+void screen_put_icon(struct screen * display,
int x, int y, enum themable_icons icon)
{
screen_put_icon_with_offset(display, x, y, 0, 0, icon);
}
-void screen_put_icon_with_offset(struct screen * display,
+void screen_put_icon_with_offset(struct screen * display,
int x, int y, int off_x, int off_y,
enum themable_icons icon)
{
@@ -107,7 +107,7 @@ void screen_put_iconxy(struct screen * display,
const int height = ICON_HEIGHT(screen);
const int is_rtl = lang_is_rtl();
const struct bitmap *iconset;
-
+
if (icon <= Icon_NOICON)
{
if (is_rtl)
@@ -119,7 +119,7 @@ void screen_put_iconxy(struct screen * display,
{
iconset = &iconsets[Iconset_viewers][screen].bmp;
icon -= Icon_Last_Themeable;
- if (!iconsets[Iconset_viewers][screen].loaded ||
+ if (!iconsets[Iconset_viewers][screen].loaded ||
(global_status.viewer_icon_count * height > iconset->height) ||
(icon * height + height > iconset->height))
{
@@ -160,8 +160,6 @@ static int buflib_move_callback(int handle, void* current, void* new)
struct iconset *set = &iconsets[i][j];
if (set->bmp.data == current)
{
- if (set->handle_locked > 0)
- return BUFLIB_CB_CANNOT_MOVE;
set->bmp.data = new;
return BUFLIB_CB_OK;
}
@@ -169,48 +167,27 @@ static int buflib_move_callback(int handle, void* current, void* new)
}
return BUFLIB_CB_OK;
}
-static struct buflib_callbacks buflib_ops = {buflib_move_callback, NULL, NULL};
static void load_icons(const char* filename, enum Iconset iconset,
enum screen_type screen)
{
- int size_read;
- int bmpformat = (FORMAT_ANY|FORMAT_DITHER|FORMAT_TRANSPARENT);
+ static struct buflib_callbacks buflib_ops = {buflib_move_callback, NULL, NULL};
+ const int bmpformat = (FORMAT_ANY|FORMAT_DITHER|FORMAT_TRANSPARENT);
struct iconset *ic = &iconsets[iconset][screen];
- int fd;
-
+ ssize_t buf_reqd;
+
ic->loaded = false;
+ ic->handle = CLB_ALOC_ERR;
if (filename[0] && filename[0] != '-')
{
- char path[MAX_PATH];
-
- snprintf(path, sizeof(path), ICON_DIR "/%s.bmp", filename);
- fd = open(path, O_RDONLY);
- if (fd < 0)
- return;
- size_t buf_size = read_bmp_fd(fd, &ic->bmp, 0,
- bmpformat|FORMAT_RETURN_SIZE, NULL);
- ic->handle = core_alloc_ex(filename, buf_size, &buflib_ops);
- if (ic->handle <= 0)
+ char fname[MAX_PATH];
+ snprintf(fname, sizeof(fname), ICON_DIR "/%s.bmp", filename);
+ ic->handle = core_load_bmp(fname, &ic->bmp, bmpformat, &buf_reqd, &buflib_ops);
+ if (ic->handle != CLB_ALOC_ERR)
{
- close(fd);
- return;
- }
- lseek(fd, 0, SEEK_SET);
- ic->bmp.data = core_get_data(ic->handle);
-
- ic->handle_locked = 1;
- size_read = read_bmp_fd(fd, &ic->bmp, buf_size, bmpformat, NULL);
- close(fd);
- ic->handle_locked = 0;
-
- /* free unused alpha channel, if any */
- core_shrink(ic->handle, ic->bmp.data, size_read);
-
- if (size_read <= 0)
- ic->handle = core_free(ic->handle);
- else
+ ic->bmp.data = core_get_data(ic->handle);
ic->loaded = true;
+ }
}
}
@@ -234,31 +211,30 @@ void icons_init(void)
{
load_icons(global_settings.icon_file, Iconset_user, SCREEN_MAIN);
- if (global_settings.viewers_icon_file[0] &&
- global_settings.viewers_icon_file[0] != '-')
+ if (global_settings.viewers_icon_file[0] == '-' ||
+ global_settings.viewers_icon_file[0] == '\0')
{
- load_icons(global_settings.viewers_icon_file,
- Iconset_viewers, SCREEN_MAIN);
- read_viewer_theme_file();
+ load_icons(DEFAULT_VIEWER_BMP, Iconset_viewers, SCREEN_MAIN);
}
else
{
- load_icons(DEFAULT_VIEWER_BMP, Iconset_viewers, SCREEN_MAIN);
+ load_icons(global_settings.viewers_icon_file,
+ Iconset_viewers, SCREEN_MAIN);
+ read_viewer_theme_file();
}
-
#if defined(HAVE_REMOTE_LCD) && (NB_SCREENS > 1)
- load_icons(global_settings.remote_icon_file,
+ load_icons(global_settings.remote_icon_file,
Iconset_user, SCREEN_REMOTE);
-
- if (global_settings.remote_viewers_icon_file[0] &&
- global_settings.remote_viewers_icon_file[0] != '-')
+
+ if (global_settings.remote_viewers_icon_file[0] == '-' ||
+ global_settings.remote_viewers_icon_file[0] == '\0')
{
- load_icons(global_settings.remote_viewers_icon_file,
+ load_icons(DEFAULT_REMOTE_VIEWER_BMP,
Iconset_viewers, SCREEN_REMOTE);
}
else
{
- load_icons(DEFAULT_REMOTE_VIEWER_BMP,
+ load_icons(global_settings.remote_viewers_icon_file,
Iconset_viewers, SCREEN_REMOTE);
}
#endif
diff --git a/apps/gui/line.c b/apps/gui/line.c
index d319ff3c51..7e84aa7b31 100644
--- a/apps/gui/line.c
+++ b/apps/gui/line.c
@@ -264,8 +264,7 @@ next:
else
{
/* any other character here is an erroneous format string */
- snprintf(tempbuf, sizeof(tempbuf), "<E:%c>", ch);
- display->putsxy(xpos, y, tempbuf);
+ display->putsxyf(xpos, y, "<E:%c>", ch);
/* Don't consider going forward, fix the caller */
return;
}
diff --git a/apps/gui/list.c b/apps/gui/list.c
index 36dbe44445..85046ead54 100644
--- a/apps/gui/list.c
+++ b/apps/gui/list.c
@@ -47,12 +47,6 @@
*/
#define FRAMEDROP_TRIGGER 6
-static int offset_step = 16; /* pixels per screen scroll step */
-/* should lines scroll out of the screen */
-static bool offset_out_of_view = false;
-
-static void gui_list_select_at_offset(struct gui_synclist * gui_list,
- int offset);
void list_draw(struct screen *display, struct gui_synclist *list);
static long last_dirty_tick;
@@ -129,6 +123,18 @@ void list_init_item_height(struct gui_synclist *list, enum screen_type screen)
#endif
}
+static void gui_synclist_init_display_settings(struct gui_synclist * list)
+{
+ struct user_settings *gs = &global_settings;
+ list->scrollbar = gs->scrollbar;
+ list->show_icons = gs->show_icons;
+ list->scroll_paginated = gs->scroll_paginated;
+ list->keyclick = gs->keyclick;
+ list->talk_menu = gs->talk_menu;
+ list->wraparound = gs->list_wraparound;
+ list->cursor_style = gs->cursor_style;
+}
+
/*
* Initializes a scrolling list
* - gui_list : the list structure to initialize
@@ -150,8 +156,14 @@ void gui_synclist_init(struct gui_synclist * gui_list,
gui_list->callback_get_item_icon = NULL;
gui_list->callback_get_item_name = callback_get_item_name;
gui_list->callback_speak_item = NULL;
+ gui_list->callback_draw_item = NULL;
gui_list->nb_items = 0;
gui_list->selected_item = 0;
+ gui_synclist_init_display_settings(gui_list);
+
+#ifdef HAVE_TOUCHSCREEN
+ gui_list->y_pos = 0;
+#endif
FOR_NB_SCREENS(i)
{
gui_list->start_item[i] = 0;
@@ -164,7 +176,6 @@ void gui_synclist_init(struct gui_synclist * gui_list,
list_init_viewports(gui_list);
FOR_NB_SCREENS(i)
list_init_item_height(gui_list, i);
- gui_list->limit_scroll = false;
gui_list->data = data;
gui_list->scroll_all = scroll_all;
gui_list->selected_size = selected_size;
@@ -173,7 +184,6 @@ void gui_synclist_init(struct gui_synclist * gui_list,
gui_list->scheduled_talk_tick = gui_list->last_talked_tick = 0;
gui_list->dirty_tick = current_tick;
- gui_list->show_selection_marker = true;
#ifdef HAVE_LCD_COLOR
gui_list->title_color = -1;
@@ -182,13 +192,6 @@ void gui_synclist_init(struct gui_synclist * gui_list,
#endif
}
-/* this toggles the selection bar or cursor */
-void gui_synclist_hide_selection_marker(struct gui_synclist * lists, bool hide)
-{
- lists->show_selection_marker = !hide;
-}
-
-
int gui_list_get_item_offset(struct gui_synclist * gui_list,
int item_width,
int text_pos,
@@ -197,7 +200,7 @@ int gui_list_get_item_offset(struct gui_synclist * gui_list,
{
int item_offset;
- if (offset_out_of_view)
+ if (global_settings.offset_out_of_view)
{
item_offset = gui_list->offset_position[display->screen_type];
}
@@ -251,15 +254,11 @@ static void gui_list_put_selection_on_screen(struct gui_synclist * gui_list,
const int scroll_limit_up = (nb_lines < gui_list->selected_size+2 ? 0:1);
const int scroll_limit_down = (scroll_limit_up+gui_list->selected_size);
- if (gui_list->show_selection_marker == false)
- {
- new_start_item = gui_list->selected_item;
- }
- else if (gui_list->selected_size >= nb_lines)
+ if (gui_list->selected_size >= nb_lines)
{
new_start_item = gui_list->selected_item;
}
- else if (global_settings.scroll_paginated)
+ else if (gui_list->scroll_paginated)
{
nb_lines -= nb_lines%gui_list->selected_size;
if (difference < 0 || difference >= nb_lines)
@@ -282,11 +281,14 @@ static void gui_list_put_selection_on_screen(struct gui_synclist * gui_list,
gui_list->start_item[screen] = bottom;
else
gui_list->start_item[screen] = new_start_item;
+#ifdef HAVE_TOUCHSCREEN
+ gui_list->y_pos = gui_list->start_item[SCREEN_MAIN] * gui_list->line_height[SCREEN_MAIN];
+#endif
}
static void edge_beep(struct gui_synclist * gui_list, bool wrap)
{
- if (global_settings.keyclick)
+ if (gui_list->keyclick)
{
list_speak_item *cb = gui_list->callback_speak_item;
if (!wrap) /* a bounce */
@@ -349,7 +351,7 @@ static void _gui_synclist_speak_item(struct gui_synclist *lists)
void gui_synclist_speak_item(struct gui_synclist *lists)
{
- if (global_settings.talk_menu)
+ if (lists->talk_menu)
{
if (lists->nb_items == 0)
talk_id(VOICE_EMPTY_LIST, true);
@@ -377,7 +379,7 @@ void gui_synclist_select_item(struct gui_synclist * gui_list, int item_number)
}
static void gui_list_select_at_offset(struct gui_synclist * gui_list,
- int offset)
+ int offset, bool allow_wrap)
{
int new_selection;
if (gui_list->selected_size > 1)
@@ -389,37 +391,15 @@ static void gui_list_select_at_offset(struct gui_synclist * gui_list,
if (new_selection >= gui_list->nb_items)
{
- new_selection = gui_list->limit_scroll ?
- gui_list->nb_items - gui_list->selected_size : 0;
- edge_beep(gui_list, !gui_list->limit_scroll);
+ new_selection = allow_wrap ? 0 : gui_list->nb_items - gui_list->selected_size;
+ edge_beep(gui_list, allow_wrap);
}
else if (new_selection < 0)
{
- new_selection = gui_list->limit_scroll ?
- 0 : gui_list->nb_items - gui_list->selected_size;
- edge_beep(gui_list, !gui_list->limit_scroll);
- }
- else if (gui_list->show_selection_marker == false)
- {
- FOR_NB_SCREENS(i)
- {
- int nb_lines = list_get_nb_lines(gui_list, i);
- if (offset > 0)
- {
- int screen_top = MAX(0, gui_list->nb_items - nb_lines);
- gui_list->start_item[i] = MIN(screen_top, gui_list->start_item[i] +
- gui_list->selected_size);
- gui_list->selected_item = gui_list->start_item[i];
- }
- else
- {
- gui_list->start_item[i] = MAX(0, gui_list->start_item[i] -
- gui_list->selected_size);
- gui_list->selected_item = gui_list->start_item[i] + nb_lines;
- }
- }
- return;
+ new_selection = allow_wrap ? gui_list->nb_items - gui_list->selected_size : 0;
+ edge_beep(gui_list, allow_wrap);
}
+
gui_synclist_select_item(gui_list, new_selection);
}
@@ -450,22 +430,12 @@ void gui_synclist_del_item(struct gui_synclist * gui_list)
}
}
-void gui_list_screen_scroll_step(int ofs)
-{
- offset_step = ofs;
-}
-
-void gui_list_screen_scroll_out_of_view(bool enable)
-{
- offset_out_of_view = enable;
-}
-
/*
* Set the title and title icon of the list. Setting title to NULL disables
* both the title and icon. Use NOICON if there is no icon.
*/
void gui_synclist_set_title(struct gui_synclist * gui_list,
- char * title, enum themable_icons icon)
+ const char * title, enum themable_icons icon)
{
gui_list->title = title;
gui_list->title_icon = icon;
@@ -533,26 +503,25 @@ void gui_synclist_set_sel_color(struct gui_synclist * lists,
#endif
static void gui_synclist_select_next_page(struct gui_synclist * lists,
- enum screen_type screen)
+ enum screen_type screen,
+ bool allow_wrap)
{
int nb_lines = list_get_nb_lines(lists, screen);
if (lists->selected_size > 1)
nb_lines = MAX(1, nb_lines/lists->selected_size);
- gui_list_select_at_offset(lists, nb_lines);
+
+ gui_list_select_at_offset(lists, nb_lines, allow_wrap);
}
static void gui_synclist_select_previous_page(struct gui_synclist * lists,
- enum screen_type screen)
+ enum screen_type screen,
+ bool allow_wrap)
{
int nb_lines = list_get_nb_lines(lists, screen);
if (lists->selected_size > 1)
nb_lines = MAX(1, nb_lines/lists->selected_size);
- gui_list_select_at_offset(lists, -nb_lines);
-}
-void gui_synclist_limit_scroll(struct gui_synclist * lists, bool scroll)
-{
- lists->limit_scroll = scroll;
+ gui_list_select_at_offset(lists, -nb_lines, allow_wrap);
}
/*
@@ -567,7 +536,7 @@ static void gui_synclist_scroll_right(struct gui_synclist * lists)
/* FIXME: This is a fake right boundry limiter. there should be some
* callback function to find the longest item on the list in pixels,
* to stop the list from scrolling past that point */
- lists->offset_position[i] += offset_step;
+ lists->offset_position[i] += global_settings.screen_scroll_step;
if (lists->offset_position[i] > 1000)
lists->offset_position[i] = 1000;
}
@@ -581,7 +550,7 @@ static void gui_synclist_scroll_left(struct gui_synclist * lists)
{
FOR_NB_SCREENS(i)
{
- lists->offset_position[i] -= offset_step;
+ lists->offset_position[i] -= global_settings.screen_scroll_step;
if (lists->offset_position[i] < 0)
lists->offset_position[i] = 0;
}
@@ -591,18 +560,24 @@ bool gui_synclist_keyclick_callback(int action, void* data)
{
struct gui_synclist *lists = (struct gui_synclist *)data;
- /* block the beep if we are at the end of the list and we are not wrapping.
- * CAVEAT: mosts lists don't set limit_scroll untill it sees a repeat
- * press at the end of the list so this can cause an extra beep.
- */
- if (lists->limit_scroll == false)
- return true;
+ /* Block the beep if we're at the end of the list and we're not wrapping. */
if (lists->selected_item == 0)
- return (action != ACTION_STD_PREV && action != ACTION_STD_PREVREPEAT);
+ {
+ if (action == ACTION_STD_PREVREPEAT)
+ return false;
+ if (action == ACTION_STD_PREV && !lists->wraparound)
+ return false;
+ }
+
if (lists->selected_item == lists->nb_items - lists->selected_size)
- return (action != ACTION_STD_NEXT && action != ACTION_STD_NEXTREPEAT);
+ {
+ if (action == ACTION_STD_NEXTREPEAT)
+ return false;
+ if (action == ACTION_STD_NEXT && !lists->wraparound)
+ return false;
+ }
- return action != ACTION_NONE;
+ return action != ACTION_NONE && !IS_SYSEVENT(action);
}
/*
@@ -625,8 +600,7 @@ static void _lists_uiviewport_update_callback(unsigned short id, void *data)
gui_synclist_draw(current_lists);
}
-bool gui_synclist_do_button(struct gui_synclist * lists,
- int *actionptr, enum list_wrap wrap)
+bool gui_synclist_do_button(struct gui_synclist * lists, int *actionptr)
{
int action = *actionptr;
static bool pgleft_allow_cancel = false;
@@ -637,6 +611,9 @@ bool gui_synclist_do_button(struct gui_synclist * lists,
static int next_item_modifier = 1;
static int last_accel_tick = 0;
+ if (IS_SYSEVENT(action))
+ return false;
+
if (action != ACTION_TOUCHSCREEN)
{
if (global_settings.list_accel_start_delay)
@@ -667,29 +644,14 @@ bool gui_synclist_do_button(struct gui_synclist * lists,
action = *actionptr = gui_synclist_do_touchscreen(lists);
else if (action > ACTION_TOUCHSCREEN_MODE)
/* cancel kinetic if we got a normal button event */
- _gui_synclist_stop_kinetic_scrolling();
+ _gui_synclist_stop_kinetic_scrolling(lists);
#endif
/* Disable the skin redraw callback */
current_lists = NULL;
- switch (wrap)
- {
- case LIST_WRAP_ON:
- gui_synclist_limit_scroll(lists, false);
- break;
- case LIST_WRAP_OFF:
- gui_synclist_limit_scroll(lists, true);
- break;
- case LIST_WRAP_UNLESS_HELD:
- if (action == ACTION_STD_PREVREPEAT ||
- action == ACTION_STD_NEXTREPEAT ||
- action == ACTION_LISTTREE_PGUP ||
- action == ACTION_LISTTREE_PGDOWN)
- gui_synclist_limit_scroll(lists, true);
- else gui_synclist_limit_scroll(lists, false);
- break;
- };
+ /* repeat actions block list wraparound */
+ bool allow_wrap = lists->wraparound;
switch (action)
{
@@ -699,17 +661,18 @@ bool gui_synclist_do_button(struct gui_synclist * lists,
#ifdef HAVE_VOLUME_IN_LIST
case ACTION_LIST_VOLUP:
- global_settings.volume += sound_steps(SOUND_VOLUME);
- setvol();
+ adjust_volume(1);
return true;
case ACTION_LIST_VOLDOWN:
- global_settings.volume -= sound_steps(SOUND_VOLUME);
- setvol();
+ adjust_volume(-1);
return true;
#endif
- case ACTION_STD_PREV:
case ACTION_STD_PREVREPEAT:
- gui_list_select_at_offset(lists, -next_item_modifier);
+ allow_wrap = false; /* Prevent list wraparound on repeating actions */
+ /*Fallthrough*/
+ case ACTION_STD_PREV:
+
+ gui_list_select_at_offset(lists, -next_item_modifier, allow_wrap);
#ifndef HAVE_WHEEL_ACCELERATION
if (button_queue_count() < FRAMEDROP_TRIGGER)
#endif
@@ -718,9 +681,11 @@ bool gui_synclist_do_button(struct gui_synclist * lists,
*actionptr = ACTION_STD_PREV;
return true;
- case ACTION_STD_NEXT:
case ACTION_STD_NEXTREPEAT:
- gui_list_select_at_offset(lists, next_item_modifier);
+ allow_wrap = false; /* Prevent list wraparound on repeating actions */
+ /*Fallthrough*/
+ case ACTION_STD_NEXT:
+ gui_list_select_at_offset(lists, next_item_modifier, allow_wrap);
#ifndef HAVE_WHEEL_ACCELERATION
if (button_queue_count() < FRAMEDROP_TRIGGER)
#endif
@@ -744,10 +709,11 @@ bool gui_synclist_do_button(struct gui_synclist * lists,
if (lists->offset_position[0] == 0)
{
pgleft_allow_cancel = true;
- *actionptr = ACTION_STD_CANCEL;
+ *actionptr = ACTION_STD_MENU;
return true;
}
*actionptr = ACTION_TREE_PGLEFT;
+ /* fallthrough */
case ACTION_TREE_PGLEFT:
if(pgleft_allow_cancel && (lists->offset_position[0] == 0))
{
@@ -770,7 +736,7 @@ bool gui_synclist_do_button(struct gui_synclist * lists,
SCREEN_REMOTE :
#endif
SCREEN_MAIN;
- gui_synclist_select_previous_page(lists, screen);
+ gui_synclist_select_previous_page(lists, screen, false);
gui_synclist_draw(lists);
yield();
*actionptr = ACTION_STD_NEXT;
@@ -785,7 +751,7 @@ bool gui_synclist_do_button(struct gui_synclist * lists,
SCREEN_REMOTE :
#endif
SCREEN_MAIN;
- gui_synclist_select_next_page(lists, screen);
+ gui_synclist_select_next_page(lists, screen, false);
gui_synclist_draw(lists);
yield();
*actionptr = ACTION_STD_PREV;
@@ -824,8 +790,7 @@ int list_do_action_timeout(struct gui_synclist *lists, int timeout)
}
bool list_do_action(int context, int timeout,
- struct gui_synclist *lists, int *action,
- enum list_wrap wrap)
+ struct gui_synclist *lists, int *action)
/* Combines the get_action() (with possibly overridden timeout) and
gui_synclist_do_button() calls. Returns the list action from
do_button, and places the action from get_action in *action. */
@@ -833,14 +798,7 @@ bool list_do_action(int context, int timeout,
timeout = list_do_action_timeout(lists, timeout);
keyclick_set_callback(gui_synclist_keyclick_callback, lists);
*action = get_action(context, timeout);
- return gui_synclist_do_button(lists, action, wrap);
-}
-
-bool gui_synclist_item_is_onscreen(struct gui_synclist *lists,
- enum screen_type screen, int item)
-{
- int nb_lines = list_get_nb_lines(lists, screen);
- return (unsigned)(item - lists->start_item[screen]) < (unsigned) nb_lines;
+ return gui_synclist_do_button(lists, action);
}
/* Simple use list implementation */
@@ -900,7 +858,6 @@ bool simplelist_show_list(struct simplelist_info *info)
struct gui_synclist lists;
int action, old_line_count = simplelist_line_count;
list_get_name *getname;
- int wrap = LIST_WRAP_UNLESS_HELD;
if (info->get_name)
getname = info->get_name;
else
@@ -925,12 +882,6 @@ bool simplelist_show_list(struct simplelist_info *info)
gui_synclist_set_sel_color(&lists, info->selection_color);
#endif
- if (info->hide_selection)
- {
- gui_synclist_hide_selection_marker(&lists, true);
- wrap = LIST_WRAP_OFF;
- }
-
if (info->action_callback)
info->action_callback(ACTION_REDRAW, &lists);
@@ -949,8 +900,7 @@ bool simplelist_show_list(struct simplelist_info *info)
while(1)
{
- list_do_action(CONTEXT_LIST, info->timeout,
- &lists, &action, wrap);
+ list_do_action(CONTEXT_LIST, info->timeout, &lists, &action);
/* We must yield in this case or no other thread can run */
if (info->timeout == TIMEOUT_NOBLOCK)
@@ -1014,7 +964,6 @@ void simplelist_info_init(struct simplelist_info *info, char* title,
info->title = title;
info->count = count;
info->selection_size = 1;
- info->hide_selection = false;
info->scroll_all = false;
info->hide_theme = false;
info->speak_onshow = true;
diff --git a/apps/gui/list.h b/apps/gui/list.h
index 3a613d0a67..40a27d1061 100644
--- a/apps/gui/list.h
+++ b/apps/gui/list.h
@@ -30,10 +30,12 @@
#define SCROLLBAR_WIDTH global_settings.scrollbar_width
-enum list_wrap {
- LIST_WRAP_ON = 0,
- LIST_WRAP_OFF,
- LIST_WRAP_UNLESS_HELD,
+enum synclist_cursor
+{
+ SYNCLIST_CURSOR_NOSTYLE = 0,
+ SYNCLIST_CURSOR_INVERT,
+ SYNCLIST_CURSOR_COLOR,
+ SYNCLIST_CURSOR_GRADIENT,
};
/*
@@ -69,6 +71,35 @@ typedef enum themable_icons list_get_icon(int selected_item, void * data);
typedef const char * list_get_name(int selected_item, void * data,
char * buffer, size_t buffer_len);
/*
+ * Draw callback
+ * - display : functions supplied depends on the screen call originated from (typ: MAIN)
+ * - list_info : a pointer to an internal struct containing item display information
+ */
+/* owner drawn lists need to know this info */
+struct list_putlineinfo_t {
+ int x;
+ int y;
+ int item_indent;
+ int item_offset;
+ int line;
+
+ int icon;
+ int icon_width;
+
+ struct screen *display;
+ struct viewport *vp;
+ struct line_desc *linedes;
+ struct gui_synclist * list;
+ const char *dsp_text;
+
+ bool is_selected;
+ bool is_title;
+ bool show_cursor;
+ bool have_icons;
+};
+
+typedef void list_draw_item(struct list_putlineinfo_t *list_info);
+/*
* Voice callback
* - selected_item : an integer that tells the number of the item to speak
* - data : a void pointer to the data you gave to the list when you
@@ -110,14 +141,24 @@ struct list_selection_color
struct gui_synclist
{
- /* defines wether the list should stop when reaching the top/bottom
- * or should continue (by going to bottom/top) */
- bool limit_scroll;
- /* wether the text of the whole items of the list have to be
+ /*flags to hold settings show: icons, scrollbar etc..*/
+ int scrollbar;
+ int cursor_style;
+ bool show_icons;
+ bool keyclick;
+ bool talk_menu;
+ bool wraparound;
+ bool scroll_paginated;
+ /* whether the text of the whole items of the list have to be
* scrolled or only for the selected item */
bool scroll_all;
int nb_items;
int selected_item;
+
+#ifdef HAVE_TOUCHSCREEN
+ /* absolute Y coordinate, used for smooth scrolling */
+ int y_pos;
+#endif
int start_item[NB_SCREENS]; /* the item that is displayed at the top of the screen */
/* the number of lines that are selected at the same time */
int selected_size;
@@ -129,14 +170,14 @@ struct gui_synclist
list_get_icon *callback_get_item_icon;
list_get_name *callback_get_item_name;
list_speak_item *callback_speak_item;
+ list_draw_item *callback_draw_item;
/* The data that will be passed to the callback function YOU implement */
void * data;
/* The optional title, set to NULL for none */
- char * title;
+ const char * title;
/* Optional title icon */
enum themable_icons title_icon;
- bool show_selection_marker; /* set to true by default */
#ifdef HAVE_LCD_COLOR
int title_color;
@@ -148,11 +189,6 @@ struct gui_synclist
extern void list_init(void);
-/* parse global setting to static int */
-extern void gui_list_screen_scroll_step(int ofs);
-
-/* parse global setting to static bool */
-extern void gui_list_screen_scroll_out_of_view(bool enable);
extern void gui_synclist_init(
struct gui_synclist * lists,
@@ -181,13 +217,8 @@ extern void gui_synclist_select_item(struct gui_synclist * lists,
int item_number);
extern void gui_synclist_add_item(struct gui_synclist * lists);
extern void gui_synclist_del_item(struct gui_synclist * lists);
-extern void gui_synclist_limit_scroll(struct gui_synclist * lists, bool scroll);
-extern void gui_synclist_set_title(struct gui_synclist * lists, char * title,
+extern void gui_synclist_set_title(struct gui_synclist * lists, const char * title,
enum themable_icons icon);
-extern void gui_synclist_hide_selection_marker(struct gui_synclist *lists,
- bool hide);
-extern bool gui_synclist_item_is_onscreen(struct gui_synclist *lists,
- enum screen_type screen, int item);
extern bool gui_synclist_keyclick_callback(int action, void* data);
/*
@@ -195,20 +226,9 @@ extern bool gui_synclist_keyclick_callback(int action, void* data);
* returns true if the action was handled.
* NOTE: *action may be changed regardless of return value
*/
-extern bool gui_synclist_do_button(struct gui_synclist * lists,
- int *action,
- enum list_wrap);
+extern bool gui_synclist_do_button(struct gui_synclist * lists, int *action);
#if !defined(PLUGIN)
-struct listitem_viewport_cfg {
- struct wps_data *data;
- OFFSETTYPE(char *) label;
- int width;
- int height;
- int xmargin;
- int ymargin;
- bool tile;
- struct skin_viewport selected_item_vp;
-};
+struct listitem_viewport_cfg;
bool skinlist_get_item(struct screen *display, struct gui_synclist *list, int x, int y, int *item);
bool skinlist_draw(struct screen *display, struct gui_synclist *list);
@@ -229,7 +249,7 @@ int skinlist_get_line_count(enum screen_type screen, struct gui_synclist *list);
/* this needs to be fixed if we ever get more than 1 touchscreen on a target */
extern unsigned gui_synclist_do_touchscreen(struct gui_synclist * gui_list);
/* only for private use in gui/list.c */
-extern void _gui_synclist_stop_kinetic_scrolling(void);
+extern void _gui_synclist_stop_kinetic_scrolling(struct gui_synclist * gui_list);
#endif
/* If the list has a pending postponed scheduled announcement, that
@@ -240,8 +260,7 @@ extern int list_do_action_timeout(struct gui_synclist *lists, int timeout);
list_do_action_timeout) with the gui_synclist_do_button call, for
convenience. */
extern bool list_do_action(int context, int timeout,
- struct gui_synclist *lists, int *action,
- enum list_wrap wrap);
+ struct gui_synclist *lists, int *action);
/** Simplelist implementation.
@@ -250,10 +269,9 @@ extern bool list_do_action(int context, int timeout,
**/
struct simplelist_info {
- char *title; /* title to show on the list */
+ const char *title; /* title to show on the list */
int count; /* number of items in the list, each item is selection_size high */
int selection_size; /* list selection size, usually 1 */
- bool hide_selection;
bool scroll_all;
bool hide_theme;
bool speak_onshow; /* list speaks first item or 'empty list' */
@@ -299,7 +317,6 @@ void simplelist_addline(const char *fmt, ...);
/* setup the info struct. members not setup in this function need to be assigned manually
members set in this function:
info.selection_size = 1;
- info.hide_selection = false;
info.scroll_all = false;
info.hide_theme = false;
info.speak_onshow = true;
diff --git a/apps/gui/mask_select.c b/apps/gui/mask_select.c
index e22ba7dd03..c39b3583ac 100644
--- a/apps/gui/mask_select.c
+++ b/apps/gui/mask_select.c
@@ -79,11 +79,14 @@ static int calculate_mask_r(struct category *root, int mask)
while (i < root->children_count)
{
struct child *this = &root->children[i];
- if (this->state == SELECTED)
- mask |= this->key_value;
+ if (this)
+ {
+ if (this->state == SELECTED)
+ mask |= this->key_value;
- else if (this->state == EXPANDED)
- mask = calculate_mask_r(this->category, mask);
+ else if (this->state == EXPANDED)
+ mask = calculate_mask_r(this->category, mask);
+ }
i++;
}
return mask;
@@ -97,7 +100,7 @@ static int count_items(struct category *start)
for (i=0; i<start->children_count; i++)
{
struct child *foo = &start->children[i];
- if (foo->state == EXPANDED)
+ if (foo && foo->state == EXPANDED)
count += count_items(foo->category);
count++;
}
@@ -120,7 +123,7 @@ static struct child* find_index(struct category *start,
return foo;
}
i++;
- if (foo->state == EXPANDED)
+ if (foo && foo->state == EXPANDED)
{
struct child *bar = find_index(foo->category, index - i, parent);
if (bar)
@@ -141,7 +144,7 @@ static int item_action_callback(int action, struct gui_synclist *list)
struct category *parent;
struct child *this = find_index(root, list->selected_item, &parent);
- if (action == ACTION_STD_OK)
+ if (action == ACTION_STD_OK && this)
{
switch (this->state)
{
@@ -194,16 +197,18 @@ static const char * item_get_name(int selected_item, void * data,
for(int i = 0; i <= parent->depth; i++)
strcat(buffer, "\t\0");
- /* state of selection needs icons so if icons are disabled use text*/
- if (!global_settings.show_icons)
- {
- if (this->state == SELECTED)
- strcat(buffer, "+\0");
- else
- strcat(buffer," \0");
- }
- strlcat(buffer, P2STR((const unsigned char *)this->name), buffer_len);
-
+ if (this)
+ {
+ /* state of selection needs icons so if icons are disabled use text*/
+ if (!global_settings.show_icons)
+ {
+ if (this->state == SELECTED)
+ strcat(buffer, "+\0");
+ else
+ strcat(buffer," \0");
+ }
+ strlcat(buffer, P2STR((const unsigned char *)this->name), buffer_len);
+ }
return buffer;
}
@@ -212,7 +217,7 @@ static int item_get_talk(int selected_item, void *data)
struct category *root = (struct category*)data;
struct category *parent;
struct child *this = find_index(root, selected_item , &parent);
- if (global_settings.talk_menu)
+ if (global_settings.talk_menu && this)
{
long id = P2ID((const unsigned char *)(this->name));
if(id>=0)
@@ -234,16 +239,19 @@ static enum themable_icons item_get_icon(int selected_item, void * data)
struct category *parent;
struct child *this = find_index(root, selected_item, &parent);
- switch (this->state)
+ if (this)
{
- case SELECTED:
- return Icon_Submenu;
- case COLLAPSED:
- return Icon_NOICON;
- case EXPANDED:
- return Icon_Submenu_Entered;
- default:
- return Icon_NOICON;
+ switch (this->state)
+ {
+ case SELECTED:
+ return Icon_Submenu;
+ case COLLAPSED:
+ return Icon_NOICON;
+ case EXPANDED:
+ return Icon_Submenu_Entered;
+ default:
+ return Icon_NOICON;
+ }
}
return Icon_NOICON;
}
diff --git a/apps/gui/option_select.c b/apps/gui/option_select.c
index ff257a4925..afc11fc4ee 100644
--- a/apps/gui/option_select.c
+++ b/apps/gui/option_select.c
@@ -36,6 +36,8 @@
#include "menu.h"
#include "quickscreen.h"
+/* HASFLAG compares value to flags returns true if set, false otherwise */
+#define HASFLAG(settings_list, flag) ((settings_list->flags & (flag)) == (flag))
static int selection_to_val(const struct settings_list *setting, int selection);
int option_value_as_int(const struct settings_list *setting)
@@ -60,34 +62,34 @@ static const char *option_get_timestring(char *buf, int buf_len,
/* these two vars are needed so arbitrary values can be added to the
TABLE_SETTING settings if the F_ALLOW_ARBITRARY_VALS flag is set */
static int table_setting_oldval = 0, table_setting_array_position = 0;
-const char *option_get_valuestring(const struct settings_list *setting,
+const char *option_get_valuestring(const struct settings_list *setting,
char *buffer, int buf_len,
intptr_t temp_var)
{
const char* str = buffer;
- if ((setting->flags & F_BOOL_SETTING) == F_BOOL_SETTING)
+ if (HASFLAG(setting, F_BOOL_SETTING))
{
bool val = (bool)temp_var;
- strlcpy(buffer, str(val? setting->bool_setting->lang_yes :
- setting->bool_setting->lang_no), buf_len);
+ strmemccpy(buffer, str(val? setting->bool_setting->lang_yes :
+ setting->bool_setting->lang_no), buf_len);
}
#if 0 /* probably dont need this one */
- else if ((setting->flags & F_FILENAME) == F_FILENAME)
+ else if (HASFLAG(setting, F_FILENAME))
{
struct filename_setting *info = setting->filename_setting;
snprintf(buffer, buf_len, "%s%s%s", info->prefix,
(char*)temp_var, info->suffix);
}
#endif
- else if (((setting->flags & F_INT_SETTING) == F_INT_SETTING) ||
- ((setting->flags & F_TABLE_SETTING) == F_TABLE_SETTING))
+ else if ((HASFLAG(setting, F_INT_SETTING)) ||
+ HASFLAG(setting, F_TABLE_SETTING))
{
const struct int_setting *int_info = setting->int_setting;
const struct table_setting *tbl_info = setting->table_setting;
int info_unit;
const char *str_unit;
const char* (*formatter)(char*, size_t, int, const char*);
- if ((setting->flags & F_INT_SETTING) == F_INT_SETTING)
+ if (HASFLAG(setting, F_INT_SETTING))
{
formatter = int_info->formatter;
info_unit = int_info->unit;
@@ -98,67 +100,68 @@ const char *option_get_valuestring(const struct settings_list *setting,
info_unit = tbl_info->unit;
}
- if ((setting->flags & F_TIME_SETTING) == F_TIME_SETTING)
+ bool is_time_setting = HASFLAG(setting, F_TIME_SETTING);
+ if (is_time_setting)
str = option_get_timestring(buffer, buf_len, (long)temp_var, info_unit);
str_unit = unit_strings_core[info_unit];
+
if (formatter)
str = formatter(buffer, buf_len, (int)temp_var, str_unit);
- else if ((setting->flags & F_TIME_SETTING) != F_TIME_SETTING)
+ else if (!is_time_setting)
snprintf(buffer, buf_len, "%d %s", (int)temp_var, str_unit?str_unit:"");
}
- else if ((setting->flags & F_T_SOUND) == F_T_SOUND)
+ else if (HASFLAG(setting, F_T_SOUND))
{
format_sound_value(buffer, buf_len,
setting->sound_setting->setting,
temp_var);
}
- else if ((setting->flags & F_CHOICE_SETTING) == F_CHOICE_SETTING)
+ else if (HASFLAG(setting, F_CHOICE_SETTING))
{
- if (setting->flags & F_CHOICETALKS)
+ if (HASFLAG(setting, F_CHOICETALKS))
{
- int setting_id;
const struct choice_setting *info = setting->choice_setting;
if (info->talks[(int)temp_var] < LANG_LAST_INDEX_IN_ARRAY)
{
- strlcpy(buffer, str(info->talks[(int)temp_var]), buf_len);
+ strmemccpy(buffer, str(info->talks[(int)temp_var]), buf_len);
}
else
{
- find_setting(setting->setting, &setting_id);
- cfg_int_to_string(setting_id, (int)temp_var, buffer, buf_len);
+ cfg_int_to_string(setting, (int)temp_var, buffer, buf_len);
}
}
else
{
int value = (int)temp_var;
char *val = P2STR(setting->choice_setting->desc[value]);
- strlcpy(buffer, val, buf_len);
+ strmemccpy(buffer, val, buf_len);
}
}
return str;
}
void option_talk_value(const struct settings_list *setting, int value, bool enqueue)
{
- if ((setting->flags & F_BOOL_SETTING) == F_BOOL_SETTING)
+
+ if (HASFLAG(setting, F_BOOL_SETTING))
{
bool val = (value==1);
talk_id(val? setting->bool_setting->lang_yes :
setting->bool_setting->lang_no, enqueue);
}
#if 0 /* probably dont need this one */
- else if ((setting->flags & F_FILENAME) == F_FILENAME)
+ else if (HASFLAG(setting, F_FILENAME))
{
}
#endif
- else if (((setting->flags & F_INT_SETTING) == F_INT_SETTING) ||
- ((setting->flags & F_TABLE_SETTING) == F_TABLE_SETTING))
+ else if ((HASFLAG(setting, F_INT_SETTING)) ||
+ HASFLAG(setting, F_TABLE_SETTING))
{
const struct int_setting *int_info = setting->int_setting;
const struct table_setting *tbl_info = setting->table_setting;
int unit;
int32_t (*get_talk_id)(int, int);
- if ((setting->flags & F_INT_SETTING) == F_INT_SETTING)
+ if (HASFLAG(setting, F_INT_SETTING))
{
unit = int_info->unit;
get_talk_id = int_info->get_talk_id;
@@ -170,12 +173,12 @@ void option_talk_value(const struct settings_list *setting, int value, bool enqu
}
if (get_talk_id)
talk_id(get_talk_id(value, unit), enqueue);
- else if ((setting->flags & F_TIME_SETTING) == F_TIME_SETTING)
+ else if (HASFLAG(setting, F_TIME_SETTING))
talk_time_intervals(value, unit, enqueue);
else
talk_value(value, unit, enqueue);
}
- else if ((setting->flags & F_T_SOUND) == F_T_SOUND)
+ else if (HASFLAG(setting, F_T_SOUND))
{
int talkunit = UNIT_INT;
int sound_setting = setting->sound_setting->setting;
@@ -190,9 +193,9 @@ void option_talk_value(const struct settings_list *setting, int value, bool enqu
talkunit = UNIT_HERTZ;
talk_value_decimal(phys, talkunit, decimals, enqueue);
}
- else if ((setting->flags & F_CHOICE_SETTING) == F_CHOICE_SETTING)
+ else if (HASFLAG(setting, F_CHOICE_SETTING))
{
- if (setting->flags & F_CHOICETALKS)
+ if (HASFLAG(setting, F_CHOICETALKS))
{
talk_id(setting->choice_setting->talks[value], enqueue);
}
@@ -202,7 +205,7 @@ void option_talk_value(const struct settings_list *setting, int value, bool enqu
}
}
}
-
+
static int option_talk(int selected_item, void * data)
{
struct settings_list *setting = (struct settings_list *)data;
@@ -218,14 +221,14 @@ void option_select_next_val(const struct settings_list *setting,
{
int val = 0;
int *value = setting->setting;
- if ((setting->flags & F_BOOL_SETTING) == F_BOOL_SETTING)
+ if (HASFLAG(setting, F_BOOL_SETTING))
{
*(bool*)value = !*(bool*)value;
if (apply && setting->bool_setting->option_callback)
setting->bool_setting->option_callback(*(bool*)value);
return;
}
- else if ((setting->flags & F_INT_SETTING) == F_INT_SETTING)
+ else if (HASFLAG(setting, F_INT_SETTING))
{
struct int_setting *info = (struct int_setting *)setting->int_setting;
bool neg_step = (info->step < 0);
@@ -245,7 +248,7 @@ void option_select_next_val(const struct settings_list *setting,
if (apply && info->option_callback)
info->option_callback(val);
}
- else if ((setting->flags & F_T_SOUND) == F_T_SOUND)
+ else if (HASFLAG(setting, F_T_SOUND))
{
int setting_id = setting->sound_setting->setting;
int steps = sound_steps(setting_id);
@@ -267,7 +270,7 @@ void option_select_next_val(const struct settings_list *setting,
if (apply)
sound_set(setting_id, val);
}
- else if ((setting->flags & F_CHOICE_SETTING) == F_CHOICE_SETTING)
+ else if (HASFLAG(setting, F_CHOICE_SETTING))
{
struct choice_setting *info = (struct choice_setting *)setting->choice_setting;
if (!previous)
@@ -286,7 +289,7 @@ void option_select_next_val(const struct settings_list *setting,
if (apply && info->option_callback)
info->option_callback(val);
}
- else if ((setting->flags & F_TABLE_SETTING) == F_TABLE_SETTING)
+ else if (HASFLAG(setting, F_TABLE_SETTING))
{
const struct table_setting *tbl_info = setting->table_setting;
int i, add;
@@ -314,13 +317,15 @@ static int selection_to_val(const struct settings_list *setting, int selection)
int min = 0;
*/
int max = 0, step = 1;
- if (((setting->flags & F_BOOL_SETTING) == F_BOOL_SETTING) ||
- ((setting->flags & F_CHOICE_SETTING) == F_CHOICE_SETTING))
+ if ((HASFLAG(setting, F_BOOL_SETTING)) ||
+ HASFLAG(setting, F_CHOICE_SETTING))
+ {
return selection;
- else if ((setting->flags & F_TABLE_SETTING) == F_TABLE_SETTING)
+ }
+ else if (HASFLAG(setting, F_TABLE_SETTING))
{
const struct table_setting *info = setting->table_setting;
- if (setting->flags&F_ALLOW_ARBITRARY_VALS &&
+ if (HASFLAG(setting, F_ALLOW_ARBITRARY_VALS) &&
table_setting_array_position != -1 &&
(selection >= table_setting_array_position))
{
@@ -331,37 +336,43 @@ static int selection_to_val(const struct settings_list *setting, int selection)
else
return info->values[selection];
}
- else if ((setting->flags & F_T_SOUND) == F_T_SOUND)
+ else if (HASFLAG(setting, F_T_SOUND))
{
int setting_id = setting->sound_setting->setting;
-#ifndef ASCENDING_INT_SETTINGS
- step = sound_steps(setting_id);
- max = (setting_id == SOUND_VOLUME) ?
- global_settings.volume_limit : sound_max(setting_id);
- /* min = sound_min(setting_id); */
-#else
- step = -sound_steps(setting_id);
- /* min = sound_max(setting_id); */
- max = sound_min(setting_id);
-#endif
+ if(global_settings.list_order == LIST_ORDER_DESCENDING)
+ {
+ step = sound_steps(setting_id);
+ max = (setting_id == SOUND_VOLUME) ?
+ global_settings.volume_limit : sound_max(setting_id);
+ /* min = sound_min(setting_id); */
+ }
+ else
+ {
+ step = -sound_steps(setting_id);
+ /* min = sound_max(setting_id); */
+ max = sound_min(setting_id);
+ }
}
- else if ((setting->flags & F_INT_SETTING) == F_INT_SETTING)
+ else if (HASFLAG(setting, F_INT_SETTING))
{
const struct int_setting *info = setting->int_setting;
-#ifndef ASCENDING_INT_SETTINGS
- /* min = info->min; */
- max = info->max;
- step = info->step;
-#else
- max = info->min;
- /* min = info->max; */
- step = -info->step;
-#endif
+ if(global_settings.list_order == LIST_ORDER_DESCENDING)
+ {
+ /* min = info->min; */
+ max = info->max;
+ step = info->step;
+ }
+ else
+ {
+ max = info->min;
+ /* min = info->max; */
+ step = -info->step;
+ }
}
return max- (selection * step);
}
-static const char * value_setting_get_name_cb(int selected_item,
+static const char * value_setting_get_name_cb(int selected_item,
void * data,
char *buffer,
size_t buffer_len)
@@ -388,13 +399,13 @@ static void val_to_selection(const struct settings_list *setting, int oldvalue,
/* set the number of items and current selection */
if (var_type == F_T_INT || var_type == F_T_UINT)
{
- if (setting->flags&F_CHOICE_SETTING)
+ if (HASFLAG(setting, F_CHOICE_SETTING))
{
*nb_items = setting->choice_setting->count;
*selected = oldvalue;
*function = setting->choice_setting->option_callback;
}
- else if (setting->flags&F_TABLE_SETTING)
+ else if (HASFLAG(setting, F_TABLE_SETTING))
{
const struct table_setting *info = setting->table_setting;
int i;
@@ -403,7 +414,7 @@ static void val_to_selection(const struct settings_list *setting, int oldvalue,
table_setting_array_position = -1;
for (i=0;*selected==-1 && i<*nb_items;i++)
{
- if (setting->flags&F_ALLOW_ARBITRARY_VALS &&
+ if (HASFLAG(setting, F_ALLOW_ARBITRARY_VALS) &&
(oldvalue < info->values[i]))
{
table_setting_oldval = oldvalue;
@@ -416,7 +427,7 @@ static void val_to_selection(const struct settings_list *setting, int oldvalue,
}
*function = info->option_callback;
}
- else if (setting->flags&F_T_SOUND)
+ else if (HASFLAG(setting, F_T_SOUND))
{
int setting_id = setting->sound_setting->setting;
int steps = sound_steps(setting_id);
@@ -424,11 +435,10 @@ static void val_to_selection(const struct settings_list *setting, int oldvalue,
int max = (setting_id == SOUND_VOLUME) ?
global_settings.volume_limit : sound_max(setting_id);
*nb_items = (max-min)/steps + 1;
-#ifndef ASCENDING_INT_SETTINGS
- *selected = (max - oldvalue) / steps;
-#else
- *selected = (oldvalue - min) / steps;
-#endif
+ if (global_settings.list_order == LIST_ORDER_DESCENDING)
+ *selected = (max - oldvalue) / steps;
+ else
+ *selected = (oldvalue - min) / steps;
*function = sound_get_fn(setting_id);
}
else
@@ -439,11 +449,10 @@ static void val_to_selection(const struct settings_list *setting, int oldvalue,
min = info->min;
step = info->step;
*nb_items = (max-min)/step + 1;
-#ifndef ASCENDING_INT_SETTINGS
- *selected = (max - oldvalue) / step;
-#else
- *selected = (oldvalue - min) / step;
-#endif
+ if(global_settings.list_order == LIST_ORDER_DESCENDING)
+ *selected = (max - oldvalue) / step;
+ else
+ *selected = (oldvalue - min) / step;
*function = info->option_callback;
}
}
@@ -466,9 +475,10 @@ bool option_screen(const struct settings_list *setting,
struct gui_synclist lists;
int oldvalue, nb_items = 0, selected = 0, temp_var;
int *variable;
- bool allow_wrap = setting->flags & F_NO_WRAP ? false : true;
- bool cb_on_select_only =
- ((setting->flags & F_CB_ON_SELECT_ONLY) == F_CB_ON_SELECT_ONLY);
+ bool allow_wrap = (!HASFLAG(setting, F_NO_WRAP));
+ bool cb_on_select_only = HASFLAG(setting, F_CB_ON_SELECT_ONLY);
+ bool cb_on_changed = HASFLAG(setting, F_CB_ONLY_IF_CHANGED);
+
int var_type = setting->flags&F_T_MASK;
void (*function)(int) = NULL;
char *title;
@@ -492,25 +502,28 @@ bool option_screen(const struct settings_list *setting,
title = (char*)setting->cfg_vals;
else
title = P2STR(option_title);
-
+
gui_synclist_set_title(&lists, title, Icon_Questionmark);
gui_synclist_set_icon_callback(&lists, NULL);
if(global_settings.talk_menu)
gui_synclist_set_voice_callback(&lists, option_talk);
-
+
val_to_selection(setting, oldvalue, &nb_items, &selected, &function);
gui_synclist_set_nb_items(&lists, nb_items);
gui_synclist_select_item(&lists, selected);
-
- gui_synclist_limit_scroll(&lists, true);
+
gui_synclist_draw(&lists);
/* talk the item */
gui_synclist_speak_item(&lists);
while (!done)
{
+ /* override user wraparound setting; used mainly by EQ settings.
+ * Not sure this is justified? */
+ if (!allow_wrap)
+ lists.wraparound = false;
+
if (list_do_action(CONTEXT_LIST, HZ, /* HZ so the status bar redraws */
- &lists, &action,
- allow_wrap? LIST_WRAP_UNLESS_HELD: LIST_WRAP_OFF))
+ &lists, &action))
{
/* setting changed */
selected = gui_synclist_get_sel_pos(&lists);
@@ -520,7 +533,7 @@ bool option_screen(const struct settings_list *setting,
}
else if (action == ACTION_NONE)
continue;
- else if (action == ACTION_STD_CANCEL)
+ else if (action == ACTION_STD_CANCEL || action == ACTION_STD_MENU)
{
/* setting canceled, restore old value if changed */
if (*variable != oldvalue)
@@ -551,31 +564,38 @@ bool option_screen(const struct settings_list *setting,
{
if (var_type == F_T_INT || var_type == F_T_UINT)
*(int*)setting->setting = *variable;
- else
+ else
*(bool*)setting->setting = (*variable==1);
}
settings_save();
done = true;
- if (cb_on_select_only && function)
- function(*variable);
+ cb_on_select_only = false; /* unset the flag so callback can be called */
}
else if(default_event_handler(action) == SYS_USB_CONNECTED)
+ {
+ pop_current_activity();
return true;
+ }
+
/* callback */
- if (function && !cb_on_select_only)
- function(*variable);
+ if (!cb_on_select_only && function)
+ {
+ if (!cb_on_changed || (*variable != oldvalue))
+ {
+ function(*variable);
+ /* if the volume is changing we need to let the skins know */
+ if (function == sound_get_fn(SOUND_VOLUME))
+ global_status.last_volume_change = current_tick;
+ }
+ }
- /* if the volume is changing we need to let the skins know */
- if (function == sound_get_fn(SOUND_VOLUME))
- global_status.last_volume_change = current_tick;
}
pop_current_activity();
return false;
}
-int get_setting_info_for_bar(int setting_id, int *count, int *val)
+int get_setting_info_for_bar(const struct settings_list *setting, int *count, int *val)
{
- const struct settings_list *setting = &settings[setting_id];
int var_type = setting->flags&F_T_MASK;
void (*function)(int) = NULL;
int oldvalue;
@@ -600,9 +620,8 @@ int get_setting_info_for_bar(int setting_id, int *count, int *val)
}
#ifdef HAVE_TOUCHSCREEN
-void update_setting_value_from_touch(int setting_id, int selection)
+void update_setting_value_from_touch(const struct settings_list *setting, int selection)
{
- const struct settings_list *setting = &settings[setting_id];
int new_val = selection_to_val(setting, selection);
int var_type = setting->flags&F_T_MASK;
diff --git a/apps/gui/option_select.h b/apps/gui/option_select.h
index 476e7b81bd..eabe5825e7 100644
--- a/apps/gui/option_select.h
+++ b/apps/gui/option_select.h
@@ -25,11 +25,10 @@
#include "screen_access.h"
#include "settings.h"
-#if defined (HAVE_SCROLLWHEEL) && !defined(FIIO_M3K)
-/* Define this if your target makes sense to have
- smaller values at the top of the list increasing down the list */
-#define ASCENDING_INT_SETTINGS
-#endif
+enum {
+ LIST_ORDER_DESCENDING = 0,
+ LIST_ORDER_ASCENDING = 1,
+};
bool option_screen(const struct settings_list *setting,
struct viewport parent[NB_SCREENS],
@@ -47,9 +46,9 @@ void option_talk_value(const struct settings_list *setting, int value, bool enqu
/* only use this for int and bool settings */
int option_value_as_int(const struct settings_list *setting);
-int get_setting_info_for_bar(int setting_id, int *count, int *val);
+int get_setting_info_for_bar(const struct settings_list *setting, int *count, int *val);
#ifdef HAVE_TOUCHSCREEN
-void update_setting_value_from_touch(int setting_id, int selection);
+void update_setting_value_from_touch(const struct settings_list *setting, int selection);
#endif
#endif /* _GUI_OPTION_SELECT_H_ */
diff --git a/apps/gui/pitchscreen.c b/apps/gui/pitchscreen.c
index a6e6e42c43..9f42aedb5d 100644
--- a/apps/gui/pitchscreen.c
+++ b/apps/gui/pitchscreen.c
@@ -18,1051 +18,8 @@
* KIND, either express or implied.
*
****************************************************************************/
-
-#include <stdbool.h>
-#include <string.h>
-#include <stdio.h>
-#include <math.h>
-#include <stdlib.h> /* for abs() */
-#include "config.h"
-#include "action.h"
-#include "sound.h"
-#include "pcmbuf.h"
-#include "lang.h"
-#include "icons.h"
-#include "screens.h"
-#include "talk.h"
-#include "viewport.h"
-#include "font.h"
-#include "system.h"
-#include "misc.h"
-#include "pitchscreen.h"
-#include "settings.h"
-#include "tdspeed.h"
-
-#define ICON_BORDER 12 /* icons are currently 7x8, so add ~2 pixels */
- /* on both sides when drawing */
-
-#define PITCH_MAX (200 * PITCH_SPEED_PRECISION)
-#define PITCH_MIN (50 * PITCH_SPEED_PRECISION)
-#define PITCH_SMALL_DELTA (PITCH_SPEED_PRECISION / 10) /* .1% */
-#define PITCH_BIG_DELTA (PITCH_SPEED_PRECISION) /* 1% */
-#define PITCH_NUDGE_DELTA (2 * PITCH_SPEED_PRECISION) /* 2% */
-
-#define SPEED_SMALL_DELTA (PITCH_SPEED_PRECISION / 10) /* .1% */
-#define SPEED_BIG_DELTA (PITCH_SPEED_PRECISION) /* 1% */
-
-#define SEMITONE_SMALL_DELTA (PITCH_SPEED_PRECISION / 10) /* 10 cents */
-#define SEMITONE_BIG_DELTA PITCH_SPEED_PRECISION /* 1 semitone */
-
-enum
-{
- PITCH_TOP = 0,
- PITCH_MID,
- PITCH_BOTTOM,
- PITCH_ITEM_COUNT,
-};
-
-
-/* This is a table of semitone percentage values of the appropriate
- precision (based on PITCH_SPEED_PRECISION). Note that these are
- all constant expressions, which will be evaluated at compile time,
- so no need to worry about how complex the expressions look.
- That's just to get the precision right.
-
- I calculated these values, starting from 50, as
-
- x(n) = 50 * 2^(n/12)
-
- All that math in each entry simply converts the float constant
- to an integer equal to PITCH_SPEED_PRECISION times the float value,
- with as little precision loss as possible (i.e. correctly rounding
- the last digit).
-*/
-#define TO_INT_WITH_PRECISION(x) \
- ( (unsigned short)(((x) * PITCH_SPEED_PRECISION * 10 + 5) / 10) )
-
-static const unsigned short semitone_table[] =
-{
- TO_INT_WITH_PRECISION(50.00000000), /* Octave lower */
- TO_INT_WITH_PRECISION(52.97315472),
- TO_INT_WITH_PRECISION(56.12310242),
- TO_INT_WITH_PRECISION(59.46035575),
- TO_INT_WITH_PRECISION(62.99605249),
- TO_INT_WITH_PRECISION(66.74199271),
- TO_INT_WITH_PRECISION(70.71067812),
- TO_INT_WITH_PRECISION(74.91535384),
- TO_INT_WITH_PRECISION(79.37005260),
- TO_INT_WITH_PRECISION(84.08964153),
- TO_INT_WITH_PRECISION(89.08987181),
- TO_INT_WITH_PRECISION(94.38743127),
- TO_INT_WITH_PRECISION(100.0000000), /* Normal sound */
- TO_INT_WITH_PRECISION(105.9463094),
- TO_INT_WITH_PRECISION(112.2462048),
- TO_INT_WITH_PRECISION(118.9207115),
- TO_INT_WITH_PRECISION(125.9921049),
- TO_INT_WITH_PRECISION(133.4839854),
- TO_INT_WITH_PRECISION(141.4213562),
- TO_INT_WITH_PRECISION(149.8307077),
- TO_INT_WITH_PRECISION(158.7401052),
- TO_INT_WITH_PRECISION(168.1792831),
- TO_INT_WITH_PRECISION(178.1797436),
- TO_INT_WITH_PRECISION(188.7748625),
- TO_INT_WITH_PRECISION(200.0000000) /* Octave higher */
-};
-
-#define NUM_SEMITONES ((int)(sizeof(semitone_table)/sizeof(semitone_table[0])))
-#define SEMITONE_END (NUM_SEMITONES/2)
-#define SEMITONE_START (-SEMITONE_END)
-
-/* A table of values for approximating the cent curve with
- linear interpolation. Multipy the next lowest semitone
- by this much to find the corresponding cent percentage.
-
- These values were calculated as
- x(n) = 100 * 2^(n * 20/1200)
-*/
-
-static const unsigned short cent_interp[] =
-{
- TO_INT_WITH_PRECISION(100.0000000),
- TO_INT_WITH_PRECISION(101.1619440),
- TO_INT_WITH_PRECISION(102.3373892),
- TO_INT_WITH_PRECISION(103.5264924),
- TO_INT_WITH_PRECISION(104.7294123),
- /* this one's the next semitone but we have it here for convenience */
- TO_INT_WITH_PRECISION(105.9463094),
-};
-
-/* Number of cents between entries in the cent_interp table */
-#define CENT_INTERP_INTERVAL 20
-#define CENT_INTERP_NUM ((int)(sizeof(cent_interp)/sizeof(cent_interp[0])))
-
-/* This stores whether the pitch and speed are at their own limits */
-/* or that of the timestretching algorithm */
-static bool at_limit = false;
-
-/*
- *
- * The pitchscreen is divided into 3 viewports (each row is a viewport)
- * Then each viewport is again divided into 3 colums, each showsing some infos
- * Additionally, on touchscreen, each cell represents a button
- *
- * Below a sketch describing what each cell will show (what's drawn on it)
- * --------------------------
- * | | | | <-- pitch up in the middle (text and button)
- * | | | | <-- arrows for mode toggling on the sides for touchscreen
- * |------------------------|
- * | | | | <-- semitone/speed up/down on the sides
- * | | | | <-- reset pitch&speed in the middle
- * |------------------------|
- * | | | | <-- pitch down in the middle
- * | | | | <-- Two "OK" for exit on the sides for touchscreen
- * |------------------------|
- *
- *
- */
-
-static void speak_pitch_mode(bool enqueue)
-{
- bool timestretch_mode = global_settings.pitch_mode_timestretch && dsp_timestretch_available();
- if (timestretch_mode)
- talk_id(VOICE_PITCH_TIMESTRETCH_MODE, enqueue);
- if (global_settings.pitch_mode_semitone)
- talk_id(VOICE_PITCH_SEMITONE_MODE, timestretch_mode ? true : enqueue);
- else
- talk_id(VOICE_PITCH_ABSOLUTE_MODE, timestretch_mode ? true : enqueue);
- return;
-}
-
-/*
- * Fixes the viewports so they represent the 3 rows, and adds a little margin
- * on all sides for the icons (which are drawn outside of the grid
- *
- * The modified viewports need to be passed to the touchscreen handling function
- **/
-static void pitchscreen_fix_viewports(struct viewport *parent,
- struct viewport pitch_viewports[PITCH_ITEM_COUNT])
-{
- int i, font_height;
- font_height = font_get(parent->font)->height;
- for (i = 0; i < PITCH_ITEM_COUNT; i++)
- {
- pitch_viewports[i] = *parent;
- pitch_viewports[i].height = parent->height / PITCH_ITEM_COUNT;
- pitch_viewports[i].x += ICON_BORDER;
- pitch_viewports[i].width -= 2*ICON_BORDER;
- }
- pitch_viewports[PITCH_TOP].y += ICON_BORDER;
- pitch_viewports[PITCH_TOP].height -= ICON_BORDER;
-
- if(pitch_viewports[PITCH_MID].height < font_height * 2)
- pitch_viewports[PITCH_MID].height = font_height * 2;
-
- pitch_viewports[PITCH_MID].y = pitch_viewports[PITCH_TOP].y
- + pitch_viewports[PITCH_TOP].height;
-
- pitch_viewports[PITCH_BOTTOM].y = pitch_viewports[PITCH_MID].y
- + pitch_viewports[PITCH_MID].height;
-
- pitch_viewports[PITCH_BOTTOM].height -= ICON_BORDER;
-}
-
-/* must be called before pitchscreen_draw, or within
- * since it neither clears nor updates the display */
-static void pitchscreen_draw_icons(struct screen *display,
- struct viewport *parent)
-{
- display->set_viewport(parent);
- display->mono_bitmap(bitmap_icons_7x8[Icon_UpArrow],
- parent->width/2 - 3,
- 2, 7, 8);
- display->mono_bitmap(bitmap_icons_7x8[Icon_DownArrow],
- parent->width /2 - 3,
- parent->height - 10, 7, 8);
- display->mono_bitmap(bitmap_icons_7x8[Icon_FastForward],
- parent->width - 10,
- parent->height /2 - 4, 7, 8);
- display->mono_bitmap(bitmap_icons_7x8[Icon_FastBackward],
- 2,
- parent->height /2 - 4, 7, 8);
- display->update_viewport();
-}
-
-static void pitchscreen_draw(struct screen *display, int max_lines,
- struct viewport pitch_viewports[PITCH_ITEM_COUNT],
- int32_t pitch, int32_t semitone
- ,int32_t speed
- )
-{
- const char* ptr;
- char buf[32];
- int w, h;
- bool show_lang_pitch;
- struct viewport *last_vp = NULL;
-
- /* "Pitch up/Pitch down" - hide for a small screen,
- * the text is drawn centered automatically
- *
- * note: this assumes 5 lines always fit on a touchscreen (should be
- * reasonable) */
- if (max_lines >= 5)
- {
- int w, h;
- struct viewport *vp = &pitch_viewports[PITCH_TOP];
- last_vp = display->set_viewport(vp);
- display->clear_viewport();
-#ifdef HAVE_TOUCHSCREEN
- /* two arrows in the top row, left and right column */
- char *arrows[] = { "<", ">"};
- display->getstringsize(arrows[0], &w, &h);
- display->putsxy(0, vp->height/2 - h/2, arrows[0]);
- display->putsxy(vp->width - w, vp->height/2 - h/2, arrows[1]);
-#endif
- /* UP: Pitch Up */
- if (global_settings.pitch_mode_semitone)
- ptr = str(LANG_PITCH_UP_SEMITONE);
- else
- ptr = str(LANG_PITCH_UP);
-
- display->getstringsize(ptr, &w, NULL);
- /* draw text */
- display->putsxy(vp->width/2 - w/2, 0, ptr);
- display->update_viewport();
-
- /* DOWN: Pitch Down */
- vp = &pitch_viewports[PITCH_BOTTOM];
- display->set_viewport(vp);
- display->clear_viewport();
-
-#ifdef HAVE_TOUCHSCREEN
- ptr = str(LANG_KBD_OK);
- display->getstringsize(ptr, &w, &h);
- /* one OK in the middle first column of the vp (at half height) */
- display->putsxy(vp->width/6 - w/2, vp->height/2 - h/2, ptr);
- /* one OK in the middle of the last column of the vp (at half height) */
- display->putsxy(5*vp->width/6 - w/2, vp->height/2 - h/2, ptr);
-#endif
- if (global_settings.pitch_mode_semitone)
- ptr = str(LANG_PITCH_DOWN_SEMITONE);
- else
- ptr = str(LANG_PITCH_DOWN);
- display->getstringsize(ptr, &w, &h);
- /* draw text */
- display->putsxy(vp->width/2 - w/2, vp->height - h, ptr);
- display->update_viewport();
- }
-
- /* Middle section */
- display->set_viewport(&pitch_viewports[PITCH_MID]);
- display->clear_viewport();
- int width_used = 0;
-
- /* Middle section upper line - hide for a small screen */
- if ((show_lang_pitch = (max_lines >= 3)))
- {
- if(global_settings.pitch_mode_timestretch)
- {
- /* Pitch:XXX.X% */
- if(global_settings.pitch_mode_semitone)
- {
- snprintf(buf, sizeof(buf), "%s: %s%d.%02d", str(LANG_PITCH),
- semitone >= 0 ? "+" : "-",
- abs(semitone / PITCH_SPEED_PRECISION),
- abs((semitone % PITCH_SPEED_PRECISION) /
- (PITCH_SPEED_PRECISION / 100))
- );
- }
- else
- {
- snprintf(buf, sizeof(buf), "%s: %ld.%ld%%", str(LANG_PITCH),
- pitch / PITCH_SPEED_PRECISION,
- (pitch % PITCH_SPEED_PRECISION) /
- (PITCH_SPEED_PRECISION / 10));
- }
- }
- else
- {
- /* Rate */
- snprintf(buf, sizeof(buf), "%s:", str(LANG_PLAYBACK_RATE));
- }
- display->getstringsize(buf, &w, &h);
- display->putsxy((pitch_viewports[PITCH_MID].width / 2) - (w / 2),
- (pitch_viewports[PITCH_MID].height / 2) - h, buf);
- if (w > width_used)
- width_used = w;
- }
-
- /* Middle section lower line */
- /* "Speed:XXX%" */
- if(global_settings.pitch_mode_timestretch)
- {
- snprintf(buf, sizeof(buf), "%s: %ld.%ld%%", str(LANG_SPEED),
- speed / PITCH_SPEED_PRECISION,
- (speed % PITCH_SPEED_PRECISION) / (PITCH_SPEED_PRECISION / 10));
- }
- else
- {
- if(global_settings.pitch_mode_semitone)
- {
- snprintf(buf, sizeof(buf), "%s%d.%02d",
- semitone >= 0 ? "+" : "-",
- abs(semitone / PITCH_SPEED_PRECISION),
- abs((semitone % PITCH_SPEED_PRECISION) /
- (PITCH_SPEED_PRECISION / 100))
- );
- }
- else
- {
- snprintf(buf, sizeof(buf), "%ld.%ld%%",
- pitch / PITCH_SPEED_PRECISION,
- (pitch % PITCH_SPEED_PRECISION) / (PITCH_SPEED_PRECISION / 10));
- }
- }
-
- display->getstringsize(buf, &w, &h);
- display->putsxy((pitch_viewports[PITCH_MID].width / 2) - (w / 2),
- show_lang_pitch ? (pitch_viewports[PITCH_MID].height / 2) :
- (pitch_viewports[PITCH_MID].height / 2) - (h / 2),
- buf);
- if (w > width_used)
- width_used = w;
-
- /* "limit" and "timestretch" labels */
- if (max_lines >= 7)
- {
- if(at_limit)
- {
- const char * const p = str(LANG_STRETCH_LIMIT);
- display->getstringsize(p, &w, &h);
- display->putsxy((pitch_viewports[PITCH_MID].width / 2) - (w / 2),
- (pitch_viewports[PITCH_MID].height / 2) + h, p);
- if (w > width_used)
- width_used = w;
- }
- }
-
- /* Middle section left/right labels */
- const char *leftlabel = "-2%";
- const char *rightlabel = "+2%";
- if (global_settings.pitch_mode_timestretch)
- {
- leftlabel = "<<";
- rightlabel = ">>";
- }
-
- /* Only display if they fit */
- display->getstringsize(leftlabel, &w, &h);
- width_used += w;
- display->getstringsize(rightlabel, &w, &h);
- width_used += w;
-
- if (width_used <= pitch_viewports[PITCH_MID].width)
- {
- display->putsxy(0, (pitch_viewports[PITCH_MID].height / 2) - (h / 2),
- leftlabel);
- display->putsxy((pitch_viewports[PITCH_MID].width - w),
- (pitch_viewports[PITCH_MID].height / 2) - (h / 2),
- rightlabel);
- }
- display->update_viewport();
- display->set_viewport(last_vp);
-}
-
-static int32_t pitch_increase(int32_t pitch, int32_t pitch_delta, bool allow_cutoff
- /* need this to maintain correct pitch/speed caps */
- , int32_t speed
- )
-{
- int32_t new_pitch;
- int32_t new_stretch;
- at_limit = false;
-
- if (pitch_delta < 0)
- {
- /* for large jumps, snap up to whole numbers */
- if(allow_cutoff && pitch_delta <= -PITCH_SPEED_PRECISION &&
- (pitch + pitch_delta) % PITCH_SPEED_PRECISION != 0)
- {
- pitch_delta += PITCH_SPEED_PRECISION - ((pitch + pitch_delta) % PITCH_SPEED_PRECISION);
- }
-
- new_pitch = pitch + pitch_delta;
-
- if (new_pitch < PITCH_MIN)
- {
- if (!allow_cutoff)
- {
- return pitch;
- }
- new_pitch = PITCH_MIN;
- at_limit = true;
- }
- }
- else if (pitch_delta > 0)
- {
- /* for large jumps, snap down to whole numbers */
- if(allow_cutoff && pitch_delta >= PITCH_SPEED_PRECISION &&
- (pitch + pitch_delta) % PITCH_SPEED_PRECISION != 0)
- {
- pitch_delta -= (pitch + pitch_delta) % PITCH_SPEED_PRECISION;
- }
-
- new_pitch = pitch + pitch_delta;
-
- if (new_pitch > PITCH_MAX)
- {
- if (!allow_cutoff)
- return pitch;
- new_pitch = PITCH_MAX;
- at_limit = true;
- }
- }
- else
- {
- /* pitch_delta == 0 -> no real change */
- return pitch;
- }
- if (dsp_timestretch_available())
- {
- /* increase the multiple to increase precision of this calculation */
- new_stretch = GET_STRETCH(new_pitch, speed);
- if(new_stretch < STRETCH_MIN)
- {
- /* we have to ignore allow_cutoff, because we can't have the */
- /* stretch go higher than STRETCH_MAX */
- new_pitch = GET_PITCH(speed, STRETCH_MIN);
- }
- else if(new_stretch > STRETCH_MAX)
- {
- /* we have to ignore allow_cutoff, because we can't have the */
- /* stretch go higher than STRETCH_MAX */
- new_pitch = GET_PITCH(speed, STRETCH_MAX);
- }
-
- if(new_stretch >= STRETCH_MAX ||
- new_stretch <= STRETCH_MIN)
- {
- at_limit = true;
- }
- }
-
- sound_set_pitch(new_pitch);
-
- return new_pitch;
-}
-
-static int32_t get_semitone_from_pitch(int32_t pitch)
-{
- int semitone = 0;
- int32_t fractional_index = 0;
-
- while(semitone < NUM_SEMITONES - 1 &&
- pitch >= semitone_table[semitone + 1])
- {
- semitone++;
- }
-
-
- /* now find the fractional part */
- while(pitch > (cent_interp[fractional_index + 1] *
- semitone_table[semitone] / PITCH_SPEED_100))
- {
- /* Check to make sure fractional_index isn't too big */
- /* This should never happen. */
- if(fractional_index >= CENT_INTERP_NUM - 1)
- {
- break;
- }
- fractional_index++;
- }
-
- int32_t semitone_pitch_a = cent_interp[fractional_index] *
- semitone_table[semitone] /
- PITCH_SPEED_100;
- int32_t semitone_pitch_b = cent_interp[fractional_index + 1] *
- semitone_table[semitone] /
- PITCH_SPEED_100;
- /* this will be the integer offset from the cent_interp entry */
- int32_t semitone_frac_ofs = (pitch - semitone_pitch_a) * CENT_INTERP_INTERVAL /
- (semitone_pitch_b - semitone_pitch_a);
- semitone = (semitone + SEMITONE_START) * PITCH_SPEED_PRECISION +
- fractional_index * CENT_INTERP_INTERVAL +
- semitone_frac_ofs;
-
- return semitone;
-}
-
-static int32_t get_pitch_from_semitone(int32_t semitone)
-{
- int32_t adjusted_semitone = semitone - SEMITONE_START * PITCH_SPEED_PRECISION;
-
- /* Find the index into the semitone table */
- int32_t semitone_index = (adjusted_semitone / PITCH_SPEED_PRECISION);
-
- /* set pitch to the semitone's integer part value */
- int32_t pitch = semitone_table[semitone_index];
- /* get the range of the cent modification for future calculation */
- int32_t pitch_mod_a =
- cent_interp[(adjusted_semitone % PITCH_SPEED_PRECISION) /
- CENT_INTERP_INTERVAL];
- int32_t pitch_mod_b =
- cent_interp[(adjusted_semitone % PITCH_SPEED_PRECISION) /
- CENT_INTERP_INTERVAL + 1];
- /* figure out the cent mod amount based on the semitone fractional value */
- int32_t pitch_mod = pitch_mod_a + (pitch_mod_b - pitch_mod_a) *
- (adjusted_semitone % CENT_INTERP_INTERVAL) / CENT_INTERP_INTERVAL;
-
- /* modify pitch based on the mod amount we just calculated */
- return (pitch * pitch_mod + PITCH_SPEED_100 / 2) / PITCH_SPEED_100;
-}
-
-static int32_t pitch_increase_semitone(int32_t pitch,
- int32_t current_semitone,
- int32_t semitone_delta
- , int32_t speed
- )
-{
- int32_t new_semitone = current_semitone;
-
- /* snap to the delta interval */
- if(current_semitone % semitone_delta != 0)
- {
- if(current_semitone > 0 && semitone_delta > 0)
- new_semitone += semitone_delta;
- else if(current_semitone < 0 && semitone_delta < 0)
- new_semitone += semitone_delta;
-
- new_semitone -= new_semitone % semitone_delta;
- }
- else
- new_semitone += semitone_delta;
-
- /* clamp the pitch so it doesn't go beyond the pitch limits */
- if(new_semitone < (SEMITONE_START * PITCH_SPEED_PRECISION))
- {
- new_semitone = SEMITONE_START * PITCH_SPEED_PRECISION;
- at_limit = true;
- }
- else if(new_semitone > (SEMITONE_END * PITCH_SPEED_PRECISION))
- {
- new_semitone = SEMITONE_END * PITCH_SPEED_PRECISION;
- at_limit = true;
- }
-
- int32_t new_pitch = get_pitch_from_semitone(new_semitone);
-
- int32_t new_stretch = GET_STRETCH(new_pitch, speed);
-
- /* clamp the pitch so it doesn't go beyond the stretch limits */
- if( new_stretch > STRETCH_MAX)
- {
- new_pitch = GET_PITCH(speed, STRETCH_MAX);
- new_semitone = get_semitone_from_pitch(new_pitch);
- at_limit = true;
- }
- else if (new_stretch < STRETCH_MIN)
- {
- new_pitch = GET_PITCH(speed, STRETCH_MIN);
- new_semitone = get_semitone_from_pitch(new_pitch);
- at_limit = true;
- }
-
- pitch_increase(pitch, new_pitch - pitch, false
- , speed
- );
-
- return new_semitone;
-}
-
-#ifdef HAVE_TOUCHSCREEN
-/*
- * Check for touchscreen presses as per sketch above in this file
- *
- * goes through each row of the, checks whether the touchscreen
- * was pressed in it. Then it looks the columns of each row for specific actions
- */
-static int pitchscreen_do_touchscreen(struct viewport vps[])
-{
- short x, y;
- struct viewport *this_vp = &vps[PITCH_TOP];
- int ret;
- static bool wait_for_release = false;
- ret = action_get_touchscreen_press_in_vp(&x, &y, this_vp);
-
- /* top row */
- if (ret > ACTION_UNKNOWN)
- { /* press on top row, left or right column
- * only toggle mode if released */
- int column = this_vp->width / 3;
- if ((x < column || x > (2*column)) && (ret == BUTTON_REL))
- return ACTION_PS_TOGGLE_MODE;
-
-
- else if (x >= column && x <= (2*column))
- { /* center column pressed */
- if (ret == BUTTON_REPEAT)
- return ACTION_PS_INC_BIG;
- else if (ret & BUTTON_REL)
- return ACTION_PS_INC_SMALL;
- }
- return ACTION_NONE;
- }
-
- /* now the center row */
- this_vp = &vps[PITCH_MID];
- ret = action_get_touchscreen_press_in_vp(&x, &y, this_vp);
-
- if (ret > ACTION_UNKNOWN)
- {
- int column = this_vp->width / 3;
-
- if (x < column)
- { /* left column */
- if (ret & BUTTON_REL)
- {
- wait_for_release = false;
- return ACTION_PS_NUDGE_LEFTOFF;
- }
- else if (ret & BUTTON_REPEAT)
- return ACTION_PS_SLOWER;
- if (!wait_for_release)
- {
- wait_for_release = true;
- return ACTION_PS_NUDGE_LEFT;
- }
- }
- else if (x > (2*column))
- { /* right column */
- if (ret & BUTTON_REL)
- {
- wait_for_release = false;
- return ACTION_PS_NUDGE_RIGHTOFF;
- }
- else if (ret & BUTTON_REPEAT)
- return ACTION_PS_FASTER;
- if (!wait_for_release)
- {
- wait_for_release = true;
- return ACTION_PS_NUDGE_RIGHT;
- }
- }
- else
- /* center column was pressed */
- return ACTION_PS_RESET;
- }
-
- /* now the bottom row */
- this_vp = &vps[PITCH_BOTTOM];
- ret = action_get_touchscreen_press_in_vp(&x, &y, this_vp);
-
- if (ret > ACTION_UNKNOWN)
- {
- int column = this_vp->width / 3;
-
- /* left or right column is exit */
- if ((x < column || x > (2*column)) && (ret == BUTTON_REL))
- return ACTION_PS_EXIT;
- else if (x >= column && x <= (2*column))
- { /* center column was pressed */
- if (ret & BUTTON_REPEAT)
- return ACTION_PS_DEC_BIG;
- else if (ret & BUTTON_REL)
- return ACTION_PS_DEC_SMALL;
- }
- return ACTION_NONE;
- }
- return ACTION_NONE;
-}
-
-#endif
-/*
- returns:
- 0 on exit
- 1 if USB was connected
-*/
-
+#include "plugin.h"
int gui_syncpitchscreen_run(void)
{
- int button;
- int32_t pitch = sound_get_pitch();
- int32_t semitone;
-
- int32_t new_pitch;
- int32_t pitch_delta;
- bool nudged = false;
- int i, updated = 4, decimals = 0;
- bool exit = false;
- /* should maybe be passed per parameter later, not needed for now */
- struct viewport parent[NB_SCREENS];
- struct viewport pitch_viewports[NB_SCREENS][PITCH_ITEM_COUNT];
- int max_lines[NB_SCREENS];
-
- push_current_activity(ACTIVITY_PITCHSCREEN);
-
- int32_t new_speed = 0, new_stretch;
-
- /* the speed variable holds the apparent speed of the playback */
- int32_t speed;
- if (dsp_timestretch_available())
- {
- speed = GET_SPEED(pitch, dsp_get_timestretch());
- }
- else
- {
- speed = pitch;
- }
-
- /* Figure out whether to be in timestretch mode */
- if (global_settings.pitch_mode_timestretch && !dsp_timestretch_available())
- {
- global_settings.pitch_mode_timestretch = false;
- settings_save();
- }
-
- /* Count decimals for speaking */
- for (i = PITCH_SPEED_PRECISION; i >= 10; i /= 10)
- decimals++;
-
- /* set the semitone index based on the current pitch */
- semitone = get_semitone_from_pitch(pitch);
-
- /* initialize pitchscreen vps */
- FOR_NB_SCREENS(i)
- {
- viewport_set_defaults(&parent[i], i);
- max_lines[i] = viewport_get_nb_lines(&parent[i]);
- pitchscreen_fix_viewports(&parent[i], pitch_viewports[i]);
- screens[i].set_viewport(&parent[i]);
- screens[i].clear_viewport();
-
- /* also, draw the icons now, it's only needed once */
- pitchscreen_draw_icons(&screens[i], &parent[i]);
- }
- pcmbuf_set_low_latency(true);
-
- while (!exit)
- {
- FOR_NB_SCREENS(i)
- pitchscreen_draw(&screens[i], max_lines[i],
- pitch_viewports[i], pitch, semitone
- , speed
- );
- pitch_delta = 0;
- new_speed = 0;
-
- if (global_settings.talk_menu && updated)
- {
- talk_shutup();
- switch (updated)
- {
- case 1:
- if (global_settings.pitch_mode_semitone)
- talk_value_decimal(semitone, UNIT_SIGNED, decimals, false);
- else
- talk_value_decimal(pitch, UNIT_PERCENT, decimals, false);
- break;
- case 2:
- talk_value_decimal(speed, UNIT_PERCENT, decimals, false);
- break;
- case 3:
- speak_pitch_mode(false);
- break;
- case 4:
- if (global_settings.pitch_mode_timestretch && dsp_timestretch_available())
- talk_id(LANG_PITCH, false);
- else
- talk_id(LANG_PLAYBACK_RATE, false);
- talk_value_decimal(pitch, UNIT_PERCENT, decimals, true);
- if (global_settings.pitch_mode_timestretch && dsp_timestretch_available())
- {
- talk_id(LANG_SPEED, true);
- talk_value_decimal(speed, UNIT_PERCENT, decimals, true);
- }
- speak_pitch_mode(true);
- break;
- default:
- break;
- }
- }
- updated = 0;
-
- button = get_action(CONTEXT_PITCHSCREEN, HZ);
-
-#ifdef HAVE_TOUCHSCREEN
- if (button == ACTION_TOUCHSCREEN)
- {
- FOR_NB_SCREENS(i)
- button = pitchscreen_do_touchscreen(pitch_viewports[i]);
- }
-#endif
- switch (button)
- {
- case ACTION_PS_INC_SMALL:
- if(global_settings.pitch_mode_semitone)
- pitch_delta = SEMITONE_SMALL_DELTA;
- else
- pitch_delta = PITCH_SMALL_DELTA;
- updated = 1;
- break;
-
- case ACTION_PS_INC_BIG:
- if(global_settings.pitch_mode_semitone)
- pitch_delta = SEMITONE_BIG_DELTA;
- else
- pitch_delta = PITCH_BIG_DELTA;
- updated = 1;
- break;
-
- case ACTION_PS_DEC_SMALL:
- if(global_settings.pitch_mode_semitone)
- pitch_delta = -SEMITONE_SMALL_DELTA;
- else
- pitch_delta = -PITCH_SMALL_DELTA;
- updated = 1;
- break;
-
- case ACTION_PS_DEC_BIG:
- if(global_settings.pitch_mode_semitone)
- pitch_delta = -SEMITONE_BIG_DELTA;
- else
- pitch_delta = -PITCH_BIG_DELTA;
- updated = 1;
- break;
-
- case ACTION_PS_NUDGE_RIGHT:
- if (!global_settings.pitch_mode_timestretch)
- {
- new_pitch = pitch_increase(pitch, PITCH_NUDGE_DELTA, false
- , speed
- );
- nudged = (new_pitch != pitch);
- pitch = new_pitch;
- semitone = get_semitone_from_pitch(pitch);
- speed = pitch;
- updated = nudged ? 1 : 0;
- break;
- }
- else
- {
- new_speed = speed + SPEED_SMALL_DELTA;
- at_limit = false;
- updated = 2;
- }
- break;
-
- case ACTION_PS_FASTER:
- if (global_settings.pitch_mode_timestretch)
- {
- new_speed = speed + SPEED_BIG_DELTA;
- /* snap to whole numbers */
- if(new_speed % PITCH_SPEED_PRECISION != 0)
- new_speed -= new_speed % PITCH_SPEED_PRECISION;
- at_limit = false;
- updated = 2;
- }
- break;
-
- case ACTION_PS_NUDGE_RIGHTOFF:
- if (nudged)
- {
- pitch = pitch_increase(pitch, -PITCH_NUDGE_DELTA, false
- , speed
- );
- speed = pitch;
- semitone = get_semitone_from_pitch(pitch);
- nudged = false;
- updated = 1;
- }
- break;
-
- case ACTION_PS_NUDGE_LEFT:
- if (!global_settings.pitch_mode_timestretch)
- {
- new_pitch = pitch_increase(pitch, -PITCH_NUDGE_DELTA, false
- , speed
- );
- nudged = (new_pitch != pitch);
- pitch = new_pitch;
- semitone = get_semitone_from_pitch(pitch);
- speed = pitch;
- updated = nudged ? 1 : 0;
- break;
- }
- else
- {
- new_speed = speed - SPEED_SMALL_DELTA;
- at_limit = false;
- updated = 2;
- }
- break;
-
- case ACTION_PS_SLOWER:
- if (global_settings.pitch_mode_timestretch)
- {
- new_speed = speed - SPEED_BIG_DELTA;
- /* snap to whole numbers */
- if(new_speed % PITCH_SPEED_PRECISION != 0)
- new_speed += PITCH_SPEED_PRECISION - speed % PITCH_SPEED_PRECISION;
- at_limit = false;
- updated = 2;
- }
- break;
-
- case ACTION_PS_NUDGE_LEFTOFF:
- if (nudged)
- {
- pitch = pitch_increase(pitch, PITCH_NUDGE_DELTA, false
- , speed
- );
- speed = pitch;
- semitone = get_semitone_from_pitch(pitch);
- nudged = false;
- updated = 1;
- }
- break;
-
- case ACTION_PS_RESET:
- pitch = PITCH_SPEED_100;
- sound_set_pitch(pitch);
- speed = PITCH_SPEED_100;
- if (dsp_timestretch_available())
- {
- dsp_set_timestretch(PITCH_SPEED_100);
- at_limit = false;
- }
- semitone = get_semitone_from_pitch(pitch);
- updated = 4;
- break;
-
- case ACTION_PS_TOGGLE_MODE:
- global_settings.pitch_mode_semitone = !global_settings.pitch_mode_semitone;
-
- if (dsp_timestretch_available() && !global_settings.pitch_mode_semitone)
- {
- global_settings.pitch_mode_timestretch = !global_settings.pitch_mode_timestretch;
- if(!global_settings.pitch_mode_timestretch)
- {
- /* no longer in timestretch mode. Reset speed */
- speed = pitch;
- dsp_set_timestretch(PITCH_SPEED_100);
- }
- }
- settings_save();
- updated = 3;
- break;
-
- case ACTION_PS_EXIT:
- exit = true;
- break;
-
- default:
- if (default_event_handler(button) == SYS_USB_CONNECTED)
- return 1;
- break;
- }
- if (pitch_delta)
- {
- if (global_settings.pitch_mode_semitone)
- {
- semitone = pitch_increase_semitone(pitch, semitone, pitch_delta
- , speed
- );
- pitch = get_pitch_from_semitone(semitone);
- }
- else
- {
- pitch = pitch_increase(pitch, pitch_delta, true
- , speed
- );
- semitone = get_semitone_from_pitch(pitch);
- }
- if (global_settings.pitch_mode_timestretch)
- {
- /* do this to make sure we properly obey the stretch limits */
- new_speed = speed;
- }
- else
- {
- speed = pitch;
- }
- }
-
- if(new_speed)
- {
- new_stretch = GET_STRETCH(pitch, new_speed);
-
- /* limit the amount of stretch */
- if(new_stretch > STRETCH_MAX)
- {
- new_stretch = STRETCH_MAX;
- new_speed = GET_SPEED(pitch, new_stretch);
- }
- else if(new_stretch < STRETCH_MIN)
- {
- new_stretch = STRETCH_MIN;
- new_speed = GET_SPEED(pitch, new_stretch);
- }
-
- new_stretch = GET_STRETCH(pitch, new_speed);
- if(new_stretch >= STRETCH_MAX ||
- new_stretch <= STRETCH_MIN)
- {
- at_limit = true;
- }
-
- /* set the amount of stretch */
- dsp_set_timestretch(new_stretch);
-
- /* update the speed variable with the new speed */
- speed = new_speed;
-
- /* Reset new_speed so we only call dsp_set_timestretch */
- /* when needed */
- new_speed = 0;
- }
- }
- pcmbuf_set_low_latency(false);
- pop_current_activity();
- return 0;
+ return (plugin_load(VIEWERS_DIR"/pitch_screen.rock", NULL) == PLUGIN_USB_CONNECTED);
}
diff --git a/apps/gui/quickscreen.c b/apps/gui/quickscreen.c
index f8bf98d4ee..109414336f 100644
--- a/apps/gui/quickscreen.c
+++ b/apps/gui/quickscreen.c
@@ -39,6 +39,9 @@
#include "option_select.h"
#include "debug.h"
#include "shortcuts.h"
+#ifdef HAVE_ALBUMART
+#include "playback.h"
+#endif
/* 1 top, 1 bottom, 2 on either side, 1 for the icons
* if enough space, top and bottom have 2 lines */
@@ -248,20 +251,21 @@ static void talk_qs_option(const struct settings_list *opt, bool enqueue)
static bool gui_quickscreen_do_button(struct gui_quickscreen * qs, int button)
{
int item;
- bool invert = false;
+ bool previous = false;
switch(button)
{
case ACTION_QS_TOP:
- invert = true;
item = QUICKSCREEN_TOP;
break;
+
case ACTION_QS_LEFT:
- invert = true;
item = QUICKSCREEN_LEFT;
+ previous = true;
break;
case ACTION_QS_DOWN:
item = QUICKSCREEN_BOTTOM;
+ previous = true;
break;
case ACTION_QS_RIGHT:
@@ -271,47 +275,58 @@ static bool gui_quickscreen_do_button(struct gui_quickscreen * qs, int button)
default:
return false;
}
+
if (qs->items[item] == NULL)
return false;
-#ifdef ASCENDING_INT_SETTINGS
- if (((qs->items[item]->flags & F_INT_SETTING) == F_INT_SETTING) &&
- ( button == ACTION_QS_DOWN || button == ACTION_QS_TOP))
- {
- invert = !invert;
- }
-#endif
- option_select_next_val(qs->items[item], invert, true);
+
+ option_select_next_val(qs->items[item], previous, true);
talk_qs_option(qs->items[item], false);
return true;
}
#ifdef HAVE_TOUCHSCREEN
-static int quickscreen_touchscreen_button(const struct viewport
- vps[QUICKSCREEN_ITEM_COUNT])
+static int quickscreen_touchscreen_button(void)
{
short x,y;
- /* only hitting the text counts, everything else is exit */
if (action_get_touchscreen_press(&x, &y) != BUTTON_REL)
return ACTION_NONE;
- else if (viewport_point_within_vp(&vps[QUICKSCREEN_TOP], x, y))
+
+ enum { left=1, right=2, top=4, bottom=8 };
+
+ int bits = 0;
+
+ if(x < LCD_WIDTH/3)
+ bits |= left;
+ else if(x > 2*LCD_WIDTH/3)
+ bits |= right;
+
+ if(y < LCD_HEIGHT/3)
+ bits |= top;
+ else if(y > 2*LCD_HEIGHT/3)
+ bits |= bottom;
+
+ switch(bits) {
+ case top:
return ACTION_QS_TOP;
- else if (viewport_point_within_vp(&vps[QUICKSCREEN_BOTTOM], x, y))
+ case bottom:
return ACTION_QS_DOWN;
- else if (viewport_point_within_vp(&vps[QUICKSCREEN_LEFT], x, y))
+ case left:
return ACTION_QS_LEFT;
- else if (viewport_point_within_vp(&vps[QUICKSCREEN_RIGHT], x, y))
+ case right:
return ACTION_QS_RIGHT;
- return ACTION_STD_CANCEL;
+ default:
+ return ACTION_STD_CANCEL;
+ }
}
#endif
-static bool gui_syncquickscreen_run(struct gui_quickscreen * qs, int button_enter, bool *usb)
+static int gui_syncquickscreen_run(struct gui_quickscreen * qs, int button_enter, bool *usb)
{
int button;
struct viewport parent[NB_SCREENS];
struct viewport vps[NB_SCREENS][QUICKSCREEN_ITEM_COUNT];
struct viewport vp_icons[NB_SCREENS];
- bool changed = false;
+ int ret = QUICKSCREEN_OK;
/* To quit we need either :
* - a second press on the button that made us enter
* - an action taken while pressing the enter button,
@@ -343,7 +358,7 @@ static bool gui_syncquickscreen_run(struct gui_quickscreen * qs, int button_ente
button = get_action(CONTEXT_QUICKSCREEN, HZ/5);
#ifdef HAVE_TOUCHSCREEN
if (button == ACTION_TOUCHSCREEN)
- button = quickscreen_touchscreen_button(vps[SCREEN_MAIN]);
+ button = quickscreen_touchscreen_button();
#endif
if (default_event_handler(button) == SYS_USB_CONNECTED)
{
@@ -352,7 +367,7 @@ static bool gui_syncquickscreen_run(struct gui_quickscreen * qs, int button_ente
}
if (gui_quickscreen_do_button(qs, button))
{
- changed = true;
+ ret |= QUICKSCREEN_CHANGED;
can_quit = true;
FOR_NB_SCREENS(i)
gui_quickscreen_draw(qs, &screens[i], &parent[i],
@@ -363,17 +378,20 @@ static bool gui_syncquickscreen_run(struct gui_quickscreen * qs, int button_ente
else if (button == button_enter)
can_quit = true;
else if (button == ACTION_QS_VOLUP) {
- global_settings.volume += sound_steps(SOUND_VOLUME);
- setvol();
+ adjust_volume(1);
FOR_NB_SCREENS(i)
skin_update(CUSTOM_STATUSBAR, i, SKIN_REFRESH_NON_STATIC);
}
else if (button == ACTION_QS_VOLDOWN) {
- global_settings.volume -= sound_steps(SOUND_VOLUME);
- setvol();
+ adjust_volume(-1);
FOR_NB_SCREENS(i)
skin_update(CUSTOM_STATUSBAR, i, SKIN_REFRESH_NON_STATIC);
}
+ else if (button == ACTION_STD_CONTEXT)
+ {
+ ret |= QUICKSCREEN_GOTO_SHORTCUTS_MENU;
+ break;
+ }
if ((button == button_enter) && can_quit)
break;
@@ -386,73 +404,60 @@ static bool gui_syncquickscreen_run(struct gui_quickscreen * qs, int button_ente
{ /* stop scrolling before exiting */
for (int j = 0; j < QUICKSCREEN_ITEM_COUNT; j++)
screens[i].scroll_stop_viewport(&vps[i][j]);
- viewportmanager_theme_undo(i, true);
+ viewportmanager_theme_undo(i, !(ret & QUICKSCREEN_GOTO_SHORTCUTS_MENU));
}
- pop_current_activity();
- return changed;
-}
+ if (ret & QUICKSCREEN_GOTO_SHORTCUTS_MENU) /* Eliminate flashing of parent during */
+ pop_current_activity_without_refresh(); /* transition to Shortcuts */
+ else
+ pop_current_activity();
-static const struct settings_list *get_setting(int gs_value,
- const struct settings_list *defaultval)
-{
- if (gs_value != -1 && gs_value < nb_settings &&
- is_setting_quickscreenable(&settings[gs_value]))
- return &settings[gs_value];
- return defaultval;
+ return ret;
}
int quick_screen_quick(int button_enter)
{
struct gui_quickscreen qs;
- bool oldshuffle = global_settings.playlist_shuffle;
- int oldrepeat = global_settings.repeat_mode;
+#ifdef HAVE_ALBUMART
+ int old_album_art = global_settings.album_art;
+#endif
bool usb = false;
- if (global_settings.shortcuts_replaces_qs)
- return do_shortcut_menu(NULL);
+ for (int i = 0; i < 4; ++i)
+ {
+ qs.items[i] = global_settings.qs_items[i];
- qs.items[QUICKSCREEN_TOP] =
- get_setting(global_settings.qs_items[QUICKSCREEN_TOP], NULL);
- qs.items[QUICKSCREEN_LEFT] =
- get_setting(global_settings.qs_items[QUICKSCREEN_LEFT],
- find_setting(&global_settings.playlist_shuffle, NULL));
- qs.items[QUICKSCREEN_RIGHT] =
- get_setting(global_settings.qs_items[QUICKSCREEN_RIGHT],
- find_setting(&global_settings.repeat_mode, NULL));
- qs.items[QUICKSCREEN_BOTTOM] =
- get_setting(global_settings.qs_items[QUICKSCREEN_BOTTOM], NULL);
+ if (!is_setting_quickscreenable(qs.items[i]))
+ qs.items[i] = NULL;
+ }
qs.callback = NULL;
- if (gui_syncquickscreen_run(&qs, button_enter, &usb))
+ int ret = gui_syncquickscreen_run(&qs, button_enter, &usb);
+ if (ret & QUICKSCREEN_CHANGED)
{
settings_save();
- /* make sure repeat/shuffle/any other nasty ones get updated */
- if ( oldrepeat != global_settings.repeat_mode &&
- (audio_status() & AUDIO_STATUS_PLAY) )
- {
- audio_flush_and_reload_tracks();
- }
- if (oldshuffle != global_settings.playlist_shuffle
- && audio_status() & AUDIO_STATUS_PLAY)
- {
- replaygain_update();
- if (global_settings.playlist_shuffle)
- playlist_randomise(NULL, current_tick, true);
- else
- playlist_sort(NULL, true);
- }
+#ifdef HAVE_ALBUMART
+ if (old_album_art != global_settings.album_art)
+ set_albumart_mode(global_settings.album_art);
+#endif
}
- return (usb ? 1:0);
+ if (usb)
+ return QUICKSCREEN_IN_USB;
+ return ret & QUICKSCREEN_GOTO_SHORTCUTS_MENU ? QUICKSCREEN_GOTO_SHORTCUTS_MENU :
+ QUICKSCREEN_OK;
}
/* stuff to make the quickscreen configurable */
bool is_setting_quickscreenable(const struct settings_list *setting)
{
+ if (!setting)
+ return true;
+
/* to keep things simple, only settings which have a lang_id set are ok */
- if (setting->lang_id < 0 || (setting->flags&F_BANFROMQS))
+ if (setting->lang_id < 0 || (setting->flags & F_BANFROMQS))
return false;
- switch (setting->flags&F_T_MASK)
+
+ switch (setting->flags & F_T_MASK)
{
case F_T_BOOL:
return true;
@@ -463,16 +468,3 @@ bool is_setting_quickscreenable(const struct settings_list *setting)
return false;
}
}
-
-void set_as_qs_item(const struct settings_list *setting,
- enum quickscreen_item item)
-{
- int i;
- for (i = 0; i < nb_settings; i++)
- {
- if (&settings[i] == setting)
- break;
- }
-
- global_settings.qs_items[item] = i;
-}
diff --git a/apps/gui/quickscreen.h b/apps/gui/quickscreen.h
index 015928ee8a..bd8008bd34 100644
--- a/apps/gui/quickscreen.h
+++ b/apps/gui/quickscreen.h
@@ -36,6 +36,13 @@ enum quickscreen_item {
QUICKSCREEN_ITEM_COUNT,
};
+enum quickscreen_return {
+ QUICKSCREEN_OK = 0,
+ QUICKSCREEN_IN_USB = 0x1,
+ QUICKSCREEN_GOTO_SHORTCUTS_MENU = 0x2,
+ QUICKSCREEN_CHANGED = 0x4,
+};
+
struct gui_quickscreen
{
const struct settings_list *items[QUICKSCREEN_ITEM_COUNT];
@@ -46,7 +53,6 @@ struct gui_quickscreen
extern int quick_screen_quick(int button_enter);
int quickscreen_set_option(void *data);
bool is_setting_quickscreenable(const struct settings_list *setting);
-void set_as_qs_item(const struct settings_list *setting,
- enum quickscreen_item item);
+
#endif /*_GUI_QUICK_SCREEN_H_*/
#endif /* HAVE_QUICKSCREEN */
diff --git a/apps/gui/skin_engine/skin_backdrops.c b/apps/gui/skin_engine/skin_backdrops.c
index 41de1e1f76..eecf5b0433 100644
--- a/apps/gui/skin_engine/skin_backdrops.c
+++ b/apps/gui/skin_engine/skin_backdrops.c
@@ -41,7 +41,6 @@ static struct skin_backdrop {
} backdrops[NB_BDROPS];
#define NB_BDROPS SKINNABLE_SCREENS_COUNT*NB_SCREENS
-static int handle_being_loaded;
static int current_lcd_backdrop[NB_SCREENS];
bool skin_backdrop_get_debug(int index, char **path, int *ref_count, size_t *size)
@@ -65,8 +64,8 @@ bool skin_backdrop_get_debug(int index, char **path, int *ref_count, size_t *siz
static int buflib_move_callback(int handle, void* current, void* new)
{
- if (handle == handle_being_loaded)
- return BUFLIB_CB_CANNOT_MOVE;
+ (void)handle;
+
for (int i=0; i<NB_BDROPS; i++)
{
if (backdrops[i].buffer == current)
@@ -81,8 +80,9 @@ static int buflib_move_callback(int handle, void* current, void* new)
}
static struct buflib_callbacks buflib_ops = {buflib_move_callback, NULL, NULL};
static bool first_go = true;
-void skin_backdrop_init(void)
+bool skin_backdrop_init(void)
{
+ bool go_status = first_go;
if (first_go)
{
for (int i=0; i<NB_BDROPS; i++)
@@ -95,9 +95,9 @@ void skin_backdrop_init(void)
}
FOR_NB_SCREENS(i)
current_lcd_backdrop[i] = -1;
- handle_being_loaded = -1;
first_go = false;
}
+ return go_status;
}
int skin_backdrop_assign(char* backdrop, char *bmpdir,
@@ -137,7 +137,7 @@ int skin_backdrop_assign(char* backdrop, char *bmpdir,
}
if (free >= 0)
{
- strlcpy(backdrops[free].name, filename, MAX_PATH);
+ strmemccpy(backdrops[free].name, filename, MAX_PATH);
backdrops[free].buffer = NULL;
backdrops[free].screen = screen;
backdrops[free].ref_count = 1;
@@ -174,22 +174,22 @@ bool skin_backdrops_preload(void)
}
if (*filename && *filename != '-')
{
- backdrops[i].buflib_handle = core_alloc_ex(filename, buf_size, &buflib_ops);
+ backdrops[i].buflib_handle = core_alloc_ex(buf_size, &buflib_ops);
if (backdrops[i].buflib_handle > 0)
{
backdrops[i].buffer = core_get_data(backdrops[i].buflib_handle);
if (strcmp(filename, BACKDROP_BUFFERNAME))
{
- handle_being_loaded = backdrops[i].buflib_handle;
+ core_pin(backdrops[i].buflib_handle);
backdrops[i].loaded =
screens[screen].backdrop_load(filename, backdrops[i].buffer);
+ core_unpin(backdrops[i].buflib_handle);
if (!backdrops[i].loaded)
{
core_free(backdrops[i].buflib_handle);
backdrops[i].buflib_handle = -1;
retval = false;
}
- handle_being_loaded = -1;
}
else
backdrops[i].loaded = true;
@@ -265,8 +265,7 @@ void skin_backdrop_unload(int backdrop_id)
backdrops[backdrop_id].ref_count--;
if (backdrops[backdrop_id].ref_count <= 0)
{
- if (backdrops[backdrop_id].buflib_handle > 0)
- core_free(backdrops[backdrop_id].buflib_handle);
+ core_free(backdrops[backdrop_id].buflib_handle);
backdrops[backdrop_id].buffer = NULL;
backdrops[backdrop_id].buflib_handle = -1;
backdrops[backdrop_id].loaded = false;
@@ -288,18 +287,17 @@ void skin_backdrop_load_setting(void)
if (backdrops[i].buflib_handle <= 0)
{
backdrops[i].buflib_handle =
- core_alloc_ex(global_settings.backdrop_file,
- LCD_BACKDROP_BYTES, &buflib_ops);
+ core_alloc_ex(LCD_BACKDROP_BYTES, &buflib_ops);
if (backdrops[i].buflib_handle <= 0)
return;
}
bool loaded;
+ core_pin(backdrops[i].buflib_handle);
backdrops[i].buffer = core_get_data(backdrops[i].buflib_handle);
- handle_being_loaded = backdrops[i].buflib_handle;
loaded = screens[SCREEN_MAIN].backdrop_load(
global_settings.backdrop_file,
backdrops[i].buffer);
- handle_being_loaded = -1;
+ core_unpin(backdrops[i].buflib_handle);
backdrops[i].name[2] = loaded ? '.' : '\0';
backdrops[i].loaded = loaded;
return;
@@ -331,8 +329,20 @@ void skin_backdrop_unload(int backdrop_id)
(void)backdrop_id;
}
#else
+static bool first_go = true;
+bool skin_backdrop_init(void)
+{
+ bool go_status = first_go;
+ first_go = false;
+ return go_status;
+}
-void skin_backdrop_init(void)
+void skin_backdrop_load_setting(void)
+{
+}
+
+void skin_backdrop_show(int backdrop_id)
{
+ (void) backdrop_id;
}
#endif
diff --git a/apps/gui/skin_engine/skin_display.c b/apps/gui/skin_engine/skin_display.c
index 4a3dcc2177..913bdcfbc4 100644
--- a/apps/gui/skin_engine/skin_display.c
+++ b/apps/gui/skin_engine/skin_display.c
@@ -46,6 +46,7 @@
#include "tagcache.h"
#include "list.h"
#include "option_select.h"
+#include "buffering.h"
#include "peakmeter.h"
/* Image stuff */
@@ -64,7 +65,7 @@
#endif
#include "root_menu.h"
-
+#include "wps.h"
#include "wps_internals.h"
#include "skin_engine.h"
#include "statusbar-skinned.h"
@@ -82,7 +83,7 @@ void skin_update(enum skinnable_screens skin, enum screen_type screen,
struct gui_wps *gwps = skin_get_gwps(skin, screen);
/* This maybe shouldnt be here,
* This is also safe for skined screen which dont use the id3 */
- struct mp3entry *id3 = skin_get_global_state()->id3;
+ struct mp3entry *id3 = get_wps_state()->id3;
bool cuesheet_update = (id3 != NULL ? cuesheet_subtrack_changed(id3) : false);
if (cuesheet_update)
skin_request_full_update(skin);
@@ -157,11 +158,12 @@ void ab_draw_markers(struct screen * screen, int capacity,
#endif
-void draw_progressbar(struct gui_wps *gwps, int line, struct progressbar *pb)
+void draw_progressbar(struct gui_wps *gwps, struct skin_viewport* skin_viewport,
+ int line, struct progressbar *pb)
{
struct screen *display = gwps->display;
- struct viewport *vp = SKINOFFSETTOPTR(get_skin_buffer(gwps->data), pb->vp);
- struct wps_state *state = skin_get_global_state();
+ struct viewport *vp = &skin_viewport->vp;
+ struct wps_state *state = get_wps_state();
struct mp3entry *id3 = state->id3;
int x = pb->x, y = pb->y, width = pb->width, height = pb->height;
unsigned long length, end;
@@ -183,8 +185,13 @@ void draw_progressbar(struct gui_wps *gwps, int line, struct progressbar *pb)
{
int minvol = sound_min(SOUND_VOLUME);
int maxvol = sound_max(SOUND_VOLUME);
- length = maxvol-minvol;
- end = global_settings.volume-minvol;
+#if defined(HAVE_PERCEPTUAL_VOLUME) || defined(HAVE_TOUCHSCREEN)
+ length = 1000;
+ end = to_normalized_volume(global_settings.volume, minvol, maxvol, length);
+#else
+ length = maxvol - minvol;
+ end = global_settings.volume - minvol;
+#endif
}
else if (pb->type == SKIN_TOKEN_BATTERY_PERCENTBAR)
{
@@ -210,7 +217,7 @@ void draw_progressbar(struct gui_wps *gwps, int line, struct progressbar *pb)
else if (pb->type == SKIN_TOKEN_SETTINGBAR)
{
int val, count;
- get_setting_info_for_bar(pb->setting_id, &count, &val);
+ get_setting_info_for_bar(pb->setting, &count, &val);
length = count - 1;
end = val;
}
@@ -411,8 +418,7 @@ void wps_display_images(struct gui_wps *gwps, struct viewport* vp)
#ifdef HAVE_ALBUMART
/* now draw the AA */
struct skin_albumart *aa = SKINOFFSETTOPTR(get_skin_buffer(data), data->albumart);
- if (aa && SKINOFFSETTOPTR(get_skin_buffer(data), aa->vp) == vp
- && aa->draw_handle >= 0)
+ if (aa && aa->draw_handle >= 0)
{
draw_album_art(gwps, aa->draw_handle, false);
aa->draw_handle = -1;
@@ -499,78 +505,82 @@ void write_line(struct screen *display, struct align_pos *format_align,
/* CASE 1: left and centered string overlap */
/* there is a left string, need to merge left and center */
- if ((left_width != 0 && center_width != 0) &&
- (left_width + space_width > center_xpos)) {
- /* replace the former separator '\0' of left and
- center string with a space */
- *(--format_align->center) = ' ';
- /* calculate the new width and position of the merged string */
- left_width = left_width + space_width + center_width;
- /* there is no centered string anymore */
- center_width = 0;
- }
- /* there is no left string, move center to left */
- if ((left_width == 0 && center_width != 0) &&
- (left_width > center_xpos)) {
- /* move the center string to the left string */
- format_align->left = format_align->center;
- /* calculate the new width and position of the string */
- left_width = center_width;
- /* there is no centered string anymore */
- center_width = 0;
- }
+ if (center_width != 0)
+ {
+ if (left_width != 0 && left_width + space_width > center_xpos) {
+ /* replace the former separator '\0' of left and
+ center string with a space */
+ *(--format_align->center) = ' ';
+ /* calculate the new width and position of the merged string */
+ left_width = left_width + space_width + center_width;
+ /* there is no centered string anymore */
+ center_width = 0;
+ }
+ /* there is no left string, move center to left */
+ else if (left_width == 0 && center_xpos < 0) {
+ /* move the center string to the left string */
+ format_align->left = format_align->center;
+ /* calculate the new width and position of the string */
+ left_width = center_width;
+ /* there is no centered string anymore */
+ center_width = 0;
+ }
+ } /*(center_width != 0)*/
/* CASE 2: centered and right string overlap */
/* there is a right string, need to merge center and right */
- if ((center_width != 0 && right_width != 0) &&
- (center_xpos + center_width + space_width > right_xpos)) {
- /* replace the former separator '\0' of center and
- right string with a space */
- *(--format_align->right) = ' ';
- /* move the center string to the right after merge */
- format_align->right = format_align->center;
- /* calculate the new width and position of the merged string */
- right_width = center_width + space_width + right_width;
- right_xpos = (viewport_width - right_width);
- /* there is no centered string anymore */
- center_width = 0;
- }
- /* there is no right string, move center to right */
- if ((center_width != 0 && right_width == 0) &&
- (center_xpos + center_width > right_xpos)) {
- /* move the center string to the right string */
- format_align->right = format_align->center;
- /* calculate the new width and position of the string */
- right_width = center_width;
- right_xpos = (viewport_width - right_width);
- /* there is no centered string anymore */
- center_width = 0;
- }
+ if (center_width != 0)
+ {
+ int center_left_x = center_xpos + center_width;
+ if (right_width != 0 && center_left_x + space_width > right_xpos) {
+ /* replace the former separator '\0' of center and
+ right string with a space */
+ *(--format_align->right) = ' ';
+ /* move the center string to the right after merge */
+ format_align->right = format_align->center;
+ /* calculate the new width and position of the merged string */
+ right_width = center_width + space_width + right_width;
+ right_xpos = (viewport_width - right_width);
+ /* there is no centered string anymore */
+ center_width = 0;
+ }
+ /* there is no right string, move center to right */
+ else if (right_width == 0 && center_left_x > right_xpos) {
+ /* move the center string to the right string */
+ format_align->right = format_align->center;
+ /* calculate the new width and position of the string */
+ right_width = center_width;
+ right_xpos = (viewport_width - right_width);
+ /* there is no centered string anymore */
+ center_width = 0;
+ }
+ } /*(center_width != 0)*/
/* CASE 3: left and right overlap
There is no center string anymore, either there never
was one or it has been merged in case 1 or 2 */
/* there is a left string, need to merge left and right */
- if ((left_width != 0 && center_width == 0 && right_width != 0) &&
- (left_width + space_width > right_xpos)) {
- /* replace the former separator '\0' of left and
- right string with a space */
- *(--format_align->right) = ' ';
- /* calculate the new width and position of the string */
- left_width = left_width + space_width + right_width;
- /* there is no right string anymore */
- right_width = 0;
- }
- /* there is no left string, move right to left */
- if ((left_width == 0 && center_width == 0 && right_width != 0) &&
- (left_width > right_xpos)) {
- /* move the right string to the left string */
- format_align->left = format_align->right;
- /* calculate the new width and position of the string */
- left_width = right_width;
- /* there is no right string anymore */
- right_width = 0;
- }
+ if (center_width == 0 && right_width != 0)
+ {
+ if (left_width != 0 && left_width + space_width > right_xpos) {
+ /* replace the former separator '\0' of left and
+ right string with a space */
+ *(--format_align->right) = ' ';
+ /* calculate the new width and position of the string */
+ left_width = left_width + space_width + right_width;
+ /* there is no right string anymore */
+ right_width = 0;
+ }
+ /* there is no left string, move right to left */
+ else if (left_width == 0 && right_xpos < 0) {
+ /* move the right string to the left string */
+ format_align->left = format_align->right;
+ /* calculate the new width and position of the string */
+ left_width = right_width;
+ /* there is no right string anymore */
+ right_width = 0;
+ }
+ } /* (center_width == 0 && right_width != 0)*/
if (scroll && ((left_width > scroll_width) ||
(center_width > scroll_width) ||
@@ -601,9 +611,9 @@ void write_line(struct screen *display, struct align_pos *format_align,
char *center = format_align->center ?: "";
char *right = format_align->right ?: "";
- display->put_line(0, line, linedes, "$t$*s$t$*s$t", left,
- center_xpos - left_width, center,
- right_xpos - (center_xpos + center_width), right);
+ display->put_line(0, line, linedes, "$t$*s$t$*s$t", left_width == 0 ? "" : left ,
+ center_xpos - left_width, center_width == 0 ? "" : center,
+ right_xpos - center_xpos - center_width, right_width == 0 ? "" : right);
}
}
@@ -632,14 +642,86 @@ void draw_peakmeters(struct gui_wps *gwps, int line_number,
}
}
-bool skin_has_sbs(enum screen_type screen, struct wps_data *data)
+#ifdef HAVE_ALBUMART
+/* Draw the album art bitmap from the given handle ID onto the given WPS.
+ Call with clear = true to clear the bitmap instead of drawing it. */
+void draw_album_art(struct gui_wps *gwps, int handle_id, bool clear)
+{
+ if (!gwps || !gwps->data || !gwps->display || handle_id < 0)
+ return;
+
+ struct wps_data *data = gwps->data;
+ struct skin_albumart *aa = SKINOFFSETTOPTR(get_skin_buffer(data), data->albumart);
+
+ if (!aa)
+ return;
+
+ struct bitmap *bmp;
+ if (bufgetdata(handle_id, 0, (void *)&bmp) <= 0)
+ return;
+
+ short x = aa->x;
+ short y = aa->y;
+ short width = bmp->width;
+ short height = bmp->height;
+
+ if (aa->width > 0)
+ {
+ /* Crop if the bitmap is too wide */
+ width = MIN(bmp->width, aa->width);
+
+ /* Align */
+ if (aa->xalign & WPS_ALBUMART_ALIGN_RIGHT)
+ x += aa->width - width;
+ else if (aa->xalign & WPS_ALBUMART_ALIGN_CENTER)
+ x += (aa->width - width) / 2;
+ }
+
+ if (aa->height > 0)
+ {
+ /* Crop if the bitmap is too high */
+ height = MIN(bmp->height, aa->height);
+
+ /* Align */
+ if (aa->yalign & WPS_ALBUMART_ALIGN_BOTTOM)
+ y += aa->height - height;
+ else if (aa->yalign & WPS_ALBUMART_ALIGN_CENTER)
+ y += (aa->height - height) / 2;
+ }
+
+ if (!clear)
+ {
+ /* Draw the bitmap */
+ gwps->display->bitmap_part((fb_data*)bmp->data, 0, 0,
+ STRIDE(gwps->display->screen_type,
+ bmp->width, bmp->height),
+ x, y, width, height);
+#ifdef HAVE_LCD_INVERT
+ if (global_settings.invert) {
+ gwps->display->set_drawmode(DRMODE_COMPLEMENT);
+ gwps->display->fillrect(x, y, width, height);
+ gwps->display->set_drawmode(DRMODE_SOLID);
+ }
+#endif
+ }
+ else
+ {
+ /* Clear the bitmap */
+ gwps->display->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
+ gwps->display->fillrect(x, y, width, height);
+ gwps->display->set_drawmode(DRMODE_SOLID);
+ }
+}
+#endif
+
+bool skin_has_sbs(struct gui_wps *gwps)
{
- (void)screen;
- (void)data;
+ struct wps_data *data = gwps->data;
+
bool draw = false;
if (data->wps_sb_tag)
draw = data->show_sb_on_wps;
- else if (statusbar_position(screen) != STATUSBAR_OFF)
+ else if (statusbar_position(gwps->display->screen_type) != STATUSBAR_OFF)
draw = true;
return draw;
}
diff --git a/apps/gui/skin_engine/skin_display.h b/apps/gui/skin_engine/skin_display.h
index de1b0b20b5..56af12aa03 100644
--- a/apps/gui/skin_engine/skin_display.h
+++ b/apps/gui/skin_engine/skin_display.h
@@ -29,7 +29,8 @@
#define _SKIN_DISPLAY_H_
-void draw_progressbar(struct gui_wps *gwps, int line, struct progressbar *pb);
+void draw_progressbar(struct gui_wps *gwps, struct skin_viewport* skin_viewport,
+ int line, struct progressbar *pb);
void draw_playlist_viewer_list(struct gui_wps *gwps, struct playlistviewer *viewer);
/* clears the area where the image was shown */
void clear_image_pos(struct gui_wps *gwps, struct gui_img *img);
@@ -54,4 +55,10 @@ void write_line(struct screen *display, struct align_pos *format_align,
int line, bool scroll, struct line_desc *line_desc);
void draw_peakmeters(struct gui_wps *gwps, int line_number,
struct viewport *viewport);
+#ifdef HAVE_ALBUMART
+/* Draw the album art bitmap from the given handle ID onto the given Skin.
+ Call with clear = true to clear the bitmap instead of drawing it. */
+void draw_album_art(struct gui_wps *gwps, int handle_id, bool clear);
+#endif
+
#endif
diff --git a/apps/gui/skin_engine/skin_engine.c b/apps/gui/skin_engine/skin_engine.c
index ce3401f41c..c007ed35d4 100644
--- a/apps/gui/skin_engine/skin_engine.c
+++ b/apps/gui/skin_engine/skin_engine.c
@@ -34,9 +34,11 @@
#if CONFIG_TUNER
#include "radio.h"
#endif
+#include "gui/list.h"
#include "skin_engine.h"
#include "skin_buffer.h"
#include "statusbar-skinned.h"
+#include "wps_internals.h"
#define FAILSAFENAME "rockbox_failsafe"
@@ -51,7 +53,6 @@ static bool skins_initialised = false;
static char* get_skin_filename(char *buf, size_t buf_size,
enum skinnable_screens skin, enum screen_type screen);
-struct wps_state wps_state = { .id3 = NULL };
static struct gui_skin_helper {
int (*preproccess)(enum screen_type screen, struct wps_data *data);
int (*postproccess)(enum screen_type screen, struct wps_data *data);
@@ -149,13 +150,28 @@ void skin_unload_all(void)
gui_sync_skin_init();
}
+static void skin_reset_buffers(int item, int screen)
+{
+ skin_data_free_buflib_allocs(&skins[item][screen].data);
+#ifdef HAVE_ALBUMART
+ if (skins[item][screen].data.playback_aa_slot >= 0)
+ playback_release_aa_slot(skins[item][screen].data.playback_aa_slot);
+#endif
+#ifdef HAVE_BACKDROP_IMAGE
+ if (skins[item][screen].data.backdrop_id >= 0)
+ skin_backdrop_unload(skins[item][screen].data.backdrop_id);
+#endif
+}
+
void settings_apply_skins(void)
{
int i;
char filename[MAX_PATH];
- static bool first_run = true;
- skin_backdrop_init();
+ if (audio_status() & AUDIO_STATUS_PLAY)
+ audio_stop();
+
+ bool first_run = skin_backdrop_init();
skins_initialised = true;
/* Make sure each skin is loaded */
@@ -167,15 +183,7 @@ void settings_apply_skins(void)
if (!first_run)
{
- skin_data_free_buflib_allocs(&skins[i][j].data);
-#ifdef HAVE_ALBUMART
- if (skins[i][j].data.playback_aa_slot >= 0)
- playback_release_aa_slot(skins[i][j].data.playback_aa_slot);
-#endif
-#ifdef HAVE_BACKDROP_IMAGE
- if (skins[i][j].data.backdrop_id >= 0)
- skin_backdrop_unload(skins[i][j].data.backdrop_id);
-#endif
+ skin_reset_buffers(i, j);
}
gui_skin_reset(&skins[i][j]);
skins[i][j].gui_wps.display = &screens[j];
@@ -183,17 +191,14 @@ void settings_apply_skins(void)
skin_get_gwps(i, j);
}
}
- first_run = false;
-#ifdef HAVE_BACKDROP_IMAGE
+
/* any backdrop that was loaded with "-" has to be reloaded because
* the setting may have changed */
skin_backdrop_load_setting();
-#endif
viewportmanager_theme_changed(THEME_STATUSBAR);
-#ifdef HAVE_BACKDROP_IMAGE
+
FOR_NB_SCREENS(i)
skin_backdrop_show(sb_get_backdrop(i));
-#endif
}
void skin_load(enum skinnable_screens skin, enum screen_type screen,
@@ -304,11 +309,6 @@ struct gui_wps *skin_get_gwps(enum skinnable_screens skin, enum screen_type scre
return &skins[skin][screen].gui_wps;
}
-struct wps_state *skin_get_global_state(void)
-{
- return &wps_state;
-}
-
/* This is called to find out if we the screen needs a full update.
* if true you MUST do a full update as the next call will return false */
bool skin_do_full_update(enum skinnable_screens skin,
@@ -330,3 +330,56 @@ void skin_request_full_update(enum skinnable_screens skin)
FOR_NB_SCREENS(i)
skins[skin][i].needs_full_update = true;
}
+
+bool dbg_skin_engine(void)
+{
+ struct simplelist_info info;
+ int i, total = 0;
+#if defined(HAVE_BACKDROP_IMAGE)
+ int ref_count;
+ char *path;
+ size_t bytes;
+ int path_prefix_len = strlen(ROCKBOX_DIR "/wps/");
+#endif
+ simplelist_info_init(&info, "Skin engine usage", 0, NULL);
+ simplelist_set_line_count(0);
+ FOR_NB_SCREENS(j) {
+#if NB_SCREENS > 1
+ simplelist_addline("%s display:",
+ j == 0 ? "Main" : "Remote");
+#endif
+ for (i = 0; i < skin_get_num_skins(); i++) {
+ struct skin_stats *stats = skin_get_stats(i, j);
+ if (stats->buflib_handles)
+ {
+ simplelist_addline("Skin ID: %d, %d allocations",
+ i, stats->buflib_handles);
+ simplelist_addline("\tskin: %d bytes",
+ stats->tree_size);
+ simplelist_addline("\tImages: %d bytes",
+ stats->images_size);
+ simplelist_addline("\tTotal: %d bytes",
+ stats->tree_size + stats->images_size);
+ total += stats->tree_size + stats->images_size;
+ }
+ }
+ }
+ simplelist_addline("Skin total usage: %d bytes", total);
+#if defined(HAVE_BACKDROP_IMAGE)
+ simplelist_addline("Backdrop Images:");
+ i = 0;
+ while (skin_backdrop_get_debug(i++, &path, &ref_count, &bytes)) {
+ if (ref_count > 0) {
+
+ if (!strncasecmp(path, ROCKBOX_DIR "/wps/", path_prefix_len))
+ path += path_prefix_len;
+ simplelist_addline("%s", path);
+ simplelist_addline("\tref_count: %d", ref_count);
+ simplelist_addline("\tsize: %d", bytes);
+ total += bytes;
+ }
+ }
+ simplelist_addline("Total usage: %d bytes", total);
+#endif
+ return simplelist_show_list(&info);
+}
diff --git a/apps/gui/skin_engine/skin_engine.h b/apps/gui/skin_engine/skin_engine.h
index 3b757a5f8b..d04c873e84 100644
--- a/apps/gui/skin_engine/skin_engine.h
+++ b/apps/gui/skin_engine/skin_engine.h
@@ -26,8 +26,7 @@
#ifndef PLUGIN
#include "tag_table.h"
-
-#include "wps_internals.h" /* TODO: remove this line.. shoudlnt be needed */
+#include "screen_access.h"
enum skinnable_screens {
CUSTOM_STATUSBAR,
@@ -39,30 +38,26 @@ enum skinnable_screens {
SKINNABLE_SCREENS_COUNT
};
+struct skin_stats;
+struct skin_viewport;
+struct gui_wps;
+
#ifdef HAVE_TOUCHSCREEN
-int skin_get_touchaction(struct wps_data *data, int* edge_offset,
- struct touchregion **retregion);
-void skin_disarm_touchregions(struct wps_data *data);
+int skin_get_touchaction(struct gui_wps *gwps, int* edge_offset);
+void skin_disarm_touchregions(struct gui_wps *gwps);
#endif
/* Do a update_type update of the skinned screen */
void skin_update(enum skinnable_screens skin, enum screen_type screen,
unsigned int update_type);
-/*
- * setup up the skin-data from a format-buffer (isfile = false)
- * or from a skinfile (isfile = true)
- */
-bool skin_data_load(enum screen_type screen, struct wps_data *wps_data,
- const char *buf, bool isfile, struct skin_stats *stats);
-
-bool skin_has_sbs(enum screen_type screen, struct wps_data *data);
+bool skin_has_sbs(struct gui_wps *gwps);
/* load a backdrop into the skin buffer.
* reuse buffers if the file is already loaded */
char* skin_backdrop_load(char* backdrop, char *bmpdir, enum screen_type screen);
-void skin_backdrop_init(void);
+bool skin_backdrop_init(void);
int skin_backdrop_assign(char* backdrop, char *bmpdir,
enum screen_type screen);
bool skin_backdrops_preload(void);
@@ -81,7 +76,6 @@ int skin_wait_for_action(enum skinnable_screens skin, int context, int timeout);
void skin_load(enum skinnable_screens skin, enum screen_type screen,
const char *buf, bool isfile);
struct gui_wps *skin_get_gwps(enum skinnable_screens skin, enum screen_type screen);
-struct wps_state *skin_get_global_state(void);
void gui_sync_skin_init(void);
void skin_unload_all(void);
@@ -89,5 +83,7 @@ void skin_unload_all(void);
bool skin_do_full_update(enum skinnable_screens skin, enum screen_type screen);
void skin_request_full_update(enum skinnable_screens skin);
+bool dbg_skin_engine(void);
+
#endif /* !PLUGIN */
#endif
diff --git a/apps/gui/skin_engine/skin_parser.c b/apps/gui/skin_engine/skin_parser.c
index 7f3f4d62e1..39029f79c6 100644
--- a/apps/gui/skin_engine/skin_parser.c
+++ b/apps/gui/skin_engine/skin_parser.c
@@ -62,6 +62,7 @@
#include "settings.h"
#include "settings_list.h"
#include "rbpaths.h"
+#include "pathfuncs.h"
#if CONFIG_TUNER
#include "radio.h"
#include "tuner.h"
@@ -305,7 +306,7 @@ static int get_image_id(int c)
void get_image_filename(const char *start, const char* bmpdir,
char *buf, int buf_size)
{
- snprintf(buf, buf_size, "%s/%s", bmpdir, start);
+ path_append(buf, bmpdir, start, buf_size);
}
static int parse_image_display(struct skin_element *element,
@@ -343,11 +344,12 @@ static int parse_image_display(struct skin_element *element,
if (element->params_count > 1)
{
- if (get_param(element, 1)->type == CODE)
+ struct skin_tag_parameter *param1 = get_param(element, 1);
+ if (param1->type == CODE)
id->token = get_param_code(element, 1)->data;
/* specify a number. 1 being the first subimage (i.e top) NOT 0 */
- else if (get_param(element, 1)->type == INTEGER)
- id->subimage = get_param(element, 1)->data.number - 1;
+ else if (param1->type == INTEGER)
+ id->subimage = param1->data.number - 1;
if (element->params_count > 2)
id->offset = get_param(element, 2)->data.number;
}
@@ -391,14 +393,16 @@ static int parse_image_load(struct skin_element *element,
subimages = get_param(element, 2)->data.number;
else if (element->params_count > 3)
{
- if (get_param(element, 2)->type == PERCENT)
- x = get_param(element, 2)->data.number * curr_vp->vp.width / 1000;
+ struct skin_tag_parameter *param2 = get_param(element, 2);
+ struct skin_tag_parameter *param3 = get_param(element, 3);
+ if (param2->type == PERCENT)
+ x = param2->data.number * curr_vp->vp.width / 1000;
else
- x = get_param(element, 2)->data.number;
- if (get_param(element, 3)->type == PERCENT)
- y = get_param(element, 3)->data.number * curr_vp->vp.height / 1000;
+ x = param2->data.number;
+ if (param3->type == PERCENT)
+ y = param3->data.number * curr_vp->vp.height / 1000;
else
- y = get_param(element, 3)->data.number;
+ y = param3->data.number;
if (element->params_count == 5)
subimages = get_param(element, 4)->data.number;
@@ -423,12 +427,16 @@ static int parse_image_load(struct skin_element *element,
img->buflib_handle = -1;
img->is_9_segment = false;
img->loaded = false;
-
- /* save current viewport */
- img->vp = PTRTOSKINOFFSET(skin_buffer, &curr_vp->vp);
+ img->dither = false;
if (token->type == SKIN_TOKEN_IMAGE_DISPLAY)
+ {
token->value.data = PTRTOSKINOFFSET(skin_buffer, img);
+#ifdef HAVE_BACKDROP_IMAGE
+ if (curr_vp)
+ img->dither = curr_vp->output_to_backdrop_buffer;
+#endif
+ }
if (!strcmp(img->bm.data, "__list_icons__"))
{
@@ -494,7 +502,6 @@ static int parse_playlistview(struct skin_element *element,
struct playlistviewer *viewer = skin_buffer_alloc(sizeof(*viewer));
if (!viewer)
return WPS_ERROR_INVALID_PARAM;
- viewer->vp = PTRTOSKINOFFSET(skin_buffer, &curr_vp->vp);
viewer->show_icons = true;
viewer->start_offset = get_param(element, 0)->data.number;
viewer->line = PTRTOSKINOFFSET(skin_buffer, get_param_code(element, 1));
@@ -561,6 +568,7 @@ static int parse_listitemviewport(struct skin_element *element,
struct wps_data *wps_data)
{
#ifndef __PCTOOL__
+ struct skin_tag_parameter *param;
struct listitem_viewport_cfg *cfg = skin_buffer_alloc(sizeof(*cfg));
if (!cfg)
return -1;
@@ -569,10 +577,15 @@ static int parse_listitemviewport(struct skin_element *element,
cfg->label = PTRTOSKINOFFSET(skin_buffer, get_param_text(element, 0));
cfg->width = -1;
cfg->height = -1;
- if (!isdefault(get_param(element, 1)))
- cfg->width = get_param(element, 1)->data.number;
- if (!isdefault(get_param(element, 2)))
- cfg->height = get_param(element, 2)->data.number;
+
+ param = get_param(element, 1);
+ if (!isdefault(param))
+ cfg->width = param->data.number;
+
+ param = get_param(element, 2);
+ if (!isdefault(param))
+ cfg->height = param->data.number;
+
if (element->params_count > 3 &&
!strcmp(get_param_text(element, 3), "tile"))
cfg->tile = true;
@@ -592,20 +605,26 @@ static int parse_viewporttextstyle(struct skin_element *element,
*line = (struct line_desc)LINE_DESC_DEFINIT;
unsigned colour;
- if (!strcmp(mode, "invert"))
+ static const char * const vp_options[] = { "invert", "color", "colour",
+ "clear", "gradient", NULL};
+
+ int vp_op = string_option(mode, vp_options, false);
+
+ if (vp_op == 0) /*invert*/
{
line->style = STYLE_INVERT;
}
- else if (!strcmp(mode, "colour") || !strcmp(mode, "color"))
+ else if (vp_op == 1 || vp_op == 2) /*color/colour*/
{
if (element->params_count < 2 ||
!parse_color(curr_screen, get_param_text(element, 1), &colour))
return 1;
- line->style = STYLE_COLORED;
+ /* STYLE_COLORED is only a modifier and can't be used on its own */
+ line->style = STYLE_COLORED | STYLE_DEFAULT;
line->text_color = colour;
}
#ifdef HAVE_LCD_COLOR
- else if (!strcmp(mode, "gradient"))
+ else if (vp_op == 4) /*gradient*/
{
int num_lines;
if (element->params_count < 2)
@@ -618,7 +637,7 @@ static int parse_viewporttextstyle(struct skin_element *element,
line->nlines = num_lines;
}
#endif
- else if (!strcmp(mode, "clear"))
+ else if (vp_op == 3) /*clear*/
{
line->style = STYLE_DEFAULT;
}
@@ -634,34 +653,39 @@ static int parse_drawrectangle( struct skin_element *element,
struct wps_data *wps_data)
{
(void)wps_data;
+ struct skin_tag_parameter *param;
struct draw_rectangle *rect = skin_buffer_alloc(sizeof(*rect));
if (!rect)
return -1;
- if (get_param(element, 0)->type == PERCENT)
- rect->x = get_param(element, 0)->data.number * curr_vp->vp.width / 1000;
+ param = get_param(element, 0);
+ if (param->type == PERCENT)
+ rect->x = param->data.number * curr_vp->vp.width / 1000;
else
- rect->x = get_param(element, 0)->data.number;
+ rect->x = param->data.number;
- if (get_param(element, 1)->type == PERCENT)
- rect->y = get_param(element, 1)->data.number * curr_vp->vp.height / 1000;
+ param = get_param(element, 1);
+ if (param->type == PERCENT)
+ rect->y = param->data.number * curr_vp->vp.height / 1000;
else
- rect->y = get_param(element, 1)->data.number;
+ rect->y = param->data.number;
- if (isdefault(get_param(element, 2)))
+ param = get_param(element, 2);
+ if (isdefault(param))
rect->width = curr_vp->vp.width - rect->x;
- else if (get_param(element, 2)->type == PERCENT)
- rect->width = get_param(element, 2)->data.number * curr_vp->vp.width / 1000;
+ else if (param->type == PERCENT)
+ rect->width = param->data.number * curr_vp->vp.width / 1000;
else
- rect->width = get_param(element, 2)->data.number;
+ rect->width = param->data.number;
- if (isdefault(get_param(element, 3)))
+ param = get_param(element, 3);
+ if (isdefault(param))
rect->height = curr_vp->vp.height - rect->y;
- else if (get_param(element, 3)->type == PERCENT)
- rect->height = get_param(element, 3)->data.number * curr_vp->vp.height / 1000;
+ else if (param->type == PERCENT)
+ rect->height = param->data.number * curr_vp->vp.height / 1000;
else
- rect->height = get_param(element, 3)->data.number;
+ rect->height = param->data.number;
rect->start_colour = curr_vp->vp.fg_pattern;
rect->end_colour = curr_vp->vp.fg_pattern;
@@ -695,8 +719,35 @@ static int parse_viewportcolour(struct skin_element *element,
return -1;
if (isdefault(param))
{
- colour->colour = get_viewport_default_colour(curr_screen,
- token->type == SKIN_TOKEN_VIEWPORT_FGCOLOUR);
+ unsigned int fg_color;
+ unsigned int bg_color;
+
+ switch (curr_screen)
+ {
+#if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
+ case SCREEN_REMOTE:
+ fg_color = LCD_REMOTE_DEFAULT_FG;
+ bg_color = LCD_REMOTE_DEFAULT_BG;
+ break;
+#endif
+ default:
+#if defined(HAVE_LCD_COLOR)
+ fg_color = global_settings.fg_color;
+ bg_color = global_settings.bg_color;
+#elif LCD_DEPTH > 1
+ fg_color = LCD_DEFAULT_FG;
+ bg_color = LCD_DEFAULT_BG;
+#else
+ fg_color = 0;
+ bg_color = 0;
+#endif
+ break;
+ }
+
+ if (token->type == SKIN_TOKEN_VIEWPORT_FGCOLOUR)
+ colour->colour = fg_color;
+ else
+ colour->colour = bg_color;
}
else
{
@@ -704,7 +755,6 @@ static int parse_viewportcolour(struct skin_element *element,
&colour->colour))
return -1;
}
- colour->vp = PTRTOSKINOFFSET(skin_buffer, &curr_vp->vp);
token->value.data = PTRTOSKINOFFSET(skin_buffer, colour);
if (element->line == curr_viewport_element->line)
{
@@ -761,14 +811,14 @@ static int parse_setting_and_lang(struct skin_element *element,
*/
(void)wps_data;
char *temp = get_param_text(element, 0);
- int i;
if (token->type == SKIN_TOKEN_TRANSLATEDSTRING)
{
#ifndef __PCTOOL__
- i = lang_english_to_id(temp);
+ int i = lang_english_to_id(temp);
if (i < 0)
- return WPS_ERROR_INVALID_PARAM;
+ i = LANG_LAST_INDEX_IN_ARRAY;
+ token->value.i = i;
#endif
}
else if (element->params_count > 1)
@@ -781,12 +831,13 @@ static int parse_setting_and_lang(struct skin_element *element,
else
{
#ifndef __PCTOOL__
- if (find_setting_by_cfgname(temp, &i) == NULL)
+ const struct settings_list *setting = find_setting_by_cfgname(temp);
+ if (!setting)
return WPS_ERROR_INVALID_PARAM;
+
+ token->value.xdata = (void *)setting;
#endif
}
- /* Store the setting number */
- token->value.i = i;
return 0;
}
@@ -811,19 +862,38 @@ static int parse_logical_if(struct skin_element *element,
token->value.data = PTRTOSKINOFFSET(skin_buffer, lif);
lif->token = get_param_code(element, 0)->data;
- if (!strncmp(op, "=", 1))
- lif->op = IF_EQUALS;
- else if (!strncmp(op, "!=", 2))
- lif->op = IF_NOTEQUALS;
- else if (!strncmp(op, ">=", 2))
- lif->op = IF_GREATERTHAN_EQ;
- else if (!strncmp(op, "<=", 2))
- lif->op = IF_LESSTHAN_EQ;
- else if (!strncmp(op, ">", 2))
- lif->op = IF_GREATERTHAN;
- else if (!strncmp(op, "<", 1))
- lif->op = IF_LESSTHAN;
-
+ /* one or two operator conditionals */
+ #define OPS2VAL(op1, op2) ((int)op1 << 8 | (int)op2)
+ #define CLAUSE(op1, op2, symbol) {OPS2VAL(op1, op2), symbol }
+
+ struct clause_symbol {int value;int symbol;};
+ static const struct clause_symbol get_clause_match[] =
+ {
+ CLAUSE('=', '=', IF_EQUALS),
+ CLAUSE('!', '=', IF_NOTEQUALS),
+ CLAUSE('>', '=', IF_GREATERTHAN_EQ),
+ CLAUSE('<', '=', IF_LESSTHAN_EQ),
+ /*All Single value items @ end */
+ CLAUSE('>', 0, IF_GREATERTHAN),
+ CLAUSE('<', 0, IF_LESSTHAN),
+ CLAUSE('=', 0, IF_EQUALS),
+ };
+
+ int val1 = OPS2VAL(op[0], 0);
+ int val2;
+ if (val1 != 0) /* Empty string ?*/
+ {
+ val2 = OPS2VAL(op[0], op[1]);
+ for (unsigned int i = 0; i < ARRAYLEN(get_clause_match); i++)
+ {
+ const struct clause_symbol *sym = &get_clause_match[i];
+ if(sym->value == val1 || sym->value == val2)
+ {
+ lif->op = sym->symbol;
+ break;
+ }
+ }
+ }
memcpy(&lif->operand, get_param(element, 2), sizeof(lif->operand));
if (element->params_count > 3)
lif->num_options = get_param(element, 3)->data.number;
@@ -904,7 +974,6 @@ static int parse_progressbar_tag(struct skin_element* element,
if (!pb)
return WPS_ERROR_INVALID_PARAM;
- pb->vp = PTRTOSKINOFFSET(skin_buffer, vp);
pb->follow_lang_direction = follow_lang_direction > 0;
pb->nofill = false;
pb->noborder = false;
@@ -912,7 +981,7 @@ static int parse_progressbar_tag(struct skin_element* element,
pb->image = PTRTOSKINOFFSET(skin_buffer, NULL);
pb->slider = PTRTOSKINOFFSET(skin_buffer, NULL);
pb->backdrop = PTRTOSKINOFFSET(skin_buffer, NULL);
- pb->setting_id = -1;
+ pb->setting = NULL;
pb->invert_fill_direction = false;
pb->horizontal = true;
@@ -1009,20 +1078,37 @@ static int parse_progressbar_tag(struct skin_element* element,
}
pb->horizontal = pb->width > pb->height;
+
+ enum
+ {
+ eINVERT = 0, eNOFILL, eNOBORDER, eNOBAR, eSLIDER, eIMAGE,
+ eBACKDROP, eVERTICAL, eHORIZONTAL, eNOTOUCH, eSETTING,
+ e_PB_TAG_COUNT
+ };
+
+ static const char *pb_options[e_PB_TAG_COUNT + 1] = {[eINVERT] = "invert",
+ [eNOFILL] = "nofill", [eNOBORDER] = "noborder", [eNOBAR] = "nobar",
+ [eSLIDER] = "slider", [eIMAGE] = "image", [eBACKDROP] = "backdrop",
+ [eVERTICAL] = "vertical", [eHORIZONTAL] = "horizontal",
+ [eNOTOUCH] = "notouch", [eSETTING] = "setting", [e_PB_TAG_COUNT] = NULL};
+ int pb_op;
+
while (curr_param < element->params_count)
{
char* text;
param++;
text = SKINOFFSETTOPTR(skin_buffer, param->data.text);
- if (!strcmp(text, "invert"))
+
+ pb_op = string_option(text, pb_options, false);
+ if (pb_op == eINVERT)
pb->invert_fill_direction = true;
- else if (!strcmp(text, "nofill"))
+ else if (pb_op == eNOFILL)
pb->nofill = true;
- else if (!strcmp(text, "noborder"))
+ else if (pb_op == eNOBORDER)
pb->noborder = true;
- else if (!strcmp(text, "nobar"))
+ else if (pb_op == eNOBAR)
pb->nobar = true;
- else if (!strcmp(text, "slider"))
+ else if (pb_op == eSLIDER)
{
if (curr_param+1 < element->params_count)
{
@@ -1035,7 +1121,7 @@ static int parse_progressbar_tag(struct skin_element* element,
else /* option needs the next param */
return -1;
}
- else if (!strcmp(text, "image"))
+ else if (pb_op == eIMAGE)
{
if (curr_param+1 < element->params_count)
{
@@ -1046,7 +1132,7 @@ static int parse_progressbar_tag(struct skin_element* element,
else /* option needs the next param */
return -1;
}
- else if (!strcmp(text, "backdrop"))
+ else if (pb_op == eBACKDROP)
{
if (curr_param+1 < element->params_count)
{
@@ -1060,31 +1146,32 @@ static int parse_progressbar_tag(struct skin_element* element,
else /* option needs the next param */
return -1;
}
- else if (!strcmp(text, "vertical"))
+ else if (pb_op == eVERTICAL)
{
pb->horizontal = false;
if (isdefault(get_param(element, 3)))
pb->height = vp->height - pb->y;
}
- else if (!strcmp(text, "horizontal"))
+ else if (pb_op == eHORIZONTAL)
pb->horizontal = true;
#ifdef HAVE_TOUCHSCREEN
- else if (!strcmp(text, "notouch"))
+ else if (pb_op == eNOTOUCH)
suppress_touchregion = true;
#endif
- else if (token->type == SKIN_TOKEN_SETTING && !strcmp(text, "setting"))
- {
+ else if (token->type == SKIN_TOKEN_SETTING && pb_op == eSETTING)
+ {
if (curr_param+1 < element->params_count)
{
curr_param++;
param++;
text = SKINOFFSETTOPTR(skin_buffer, param->data.text);
#ifndef __PCTOOL__
- if (find_setting_by_cfgname(text, &pb->setting_id) == NULL)
- return WPS_ERROR_INVALID_PARAM;
+ pb->setting = find_setting_by_cfgname(text);
+ if (!pb->setting)
+ return WPS_ERROR_INVALID_PARAM;
#endif
- }
- }
+ }
+ }
else if (curr_param == 4)
image_filename = text;
@@ -1114,7 +1201,6 @@ static int parse_progressbar_tag(struct skin_element* element,
img->display = -1;
img->using_preloaded_icons = false;
img->buflib_handle = -1;
- img->vp = PTRTOSKINOFFSET(skin_buffer, &curr_vp->vp);
img->loaded = false;
struct skin_token_list *item = new_skin_token_list_item(NULL, img);
if (!item)
@@ -1148,7 +1234,6 @@ static int parse_progressbar_tag(struct skin_element* element,
{
struct touchregion *region = skin_buffer_alloc(sizeof(*region));
struct skin_token_list *item;
- int wpad, hpad;
if (!region)
return 0;
@@ -1163,24 +1248,24 @@ static int parse_progressbar_tag(struct skin_element* element,
/* try to add some extra space on either end to make pressing the
* full bar easier. ~5% on either side
*/
- wpad = pb->width * 5 / 100;
- if (wpad > 10)
- wpad = 10;
- hpad = pb->height * 5 / 100;
- if (hpad > 10)
- hpad = 10;
-
- region->x = pb->x - wpad;
+ region->wpad = pb->width * 5 / 100;
+ if (region->wpad > 10)
+ region->wpad = 10;
+ region->hpad = pb->height * 5 / 100;
+ if (region->hpad > 10)
+ region->hpad = 10;
+
+ region->x = pb->x;
if (region->x < 0)
region->x = 0;
- region->width = pb->width + 2 * wpad;
+ region->width = pb->width;
if (region->x + region->width > curr_vp->vp.x + curr_vp->vp.width)
region->width = curr_vp->vp.x + curr_vp->vp.width - region->x;
- region->y = pb->y - hpad;
+ region->y = pb->y;
if (region->y < 0)
region->y = 0;
- region->height = pb->height + 2 * hpad;
+ region->height = pb->height;
if (region->y + region->height > curr_vp->vp.y + curr_vp->vp.height)
region->height = curr_vp->vp.y + curr_vp->vp.height - region->y;
@@ -1188,7 +1273,7 @@ static int parse_progressbar_tag(struct skin_element* element,
region->reverse_bar = false;
region->allow_while_locked = false;
region->press_length = PRESS;
- region->last_press = 0xffff;
+ region->last_press = -1;
region->armed = false;
region->bar = PTRTOSKINOFFSET(skin_buffer, pb);
@@ -1220,24 +1305,28 @@ static int parse_albumart_load(struct skin_element* element,
aa->xalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
aa->yalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
- aa->x = get_param(element, 0)->data.number;
- aa->y = get_param(element, 1)->data.number;
- aa->width = get_param(element, 2)->data.number;
- aa->height = get_param(element, 3)->data.number;
+ struct skin_tag_parameter *param0 = get_param(element, 0);
+ struct skin_tag_parameter *param1 = get_param(element, 1);
+ struct skin_tag_parameter *param2 = get_param(element, 2);
+ struct skin_tag_parameter *param3 = get_param(element, 3);
+
+ aa->x = param0->data.number;
+ aa->y = param1->data.number;
+ aa->width = param2->data.number;
+ aa->height = param3->data.number;
- if (!isdefault(get_param(element, 0)) && get_param(element, 0)->type == PERCENT)
- aa->x = get_param(element, 0)->data.number * curr_vp->vp.width / 1000;
+ if (!isdefault(param0) && param0->type == PERCENT)
+ aa->x = param0->data.number * curr_vp->vp.width / 1000;
- if (!isdefault(get_param(element, 1)) && get_param(element, 1)->type == PERCENT)
- aa->y = get_param(element, 1)->data.number * curr_vp->vp.height / 1000;
+ if (!isdefault(param1) && param1->type == PERCENT)
+ aa->y = param1->data.number * curr_vp->vp.height / 1000;
- if (!isdefault(get_param(element, 2)) && get_param(element, 2)->type == PERCENT)
- aa->width = get_param(element, 2)->data.number * curr_vp->vp.width / 1000;
+ if (!isdefault(param2) && param2->type == PERCENT)
+ aa->width = param2->data.number * curr_vp->vp.width / 1000;
- if (!isdefault(get_param(element, 3)) && get_param(element, 3)->type == PERCENT)
- aa->height = get_param(element, 3)->data.number * curr_vp->vp.height / 1000;
+ if (!isdefault(param3) && param3->type == PERCENT)
+ aa->height = param3->data.number * curr_vp->vp.height / 1000;
- aa->vp = PTRTOSKINOFFSET(skin_buffer, &curr_vp->vp);
aa->draw_handle = -1;
/* if we got here, we parsed everything ok .. ! */
@@ -1342,27 +1431,32 @@ static int parse_skinvar( struct skin_element *element,
return 0;
case SKIN_TOKEN_VAR_SET:
{
+ static const char * const sv_options[] = {"touch", "set", "inc", "dec", NULL};
+
struct skin_var_changer *data = skin_buffer_alloc(sizeof(*data));
if (!data)
return WPS_ERROR_INVALID_PARAM;
data->var = PTRTOSKINOFFSET(skin_buffer, var);
+ char *text_param1 = get_param_text(element, 1);
+ int sv_op = string_option(text_param1, sv_options, false);
+
if (!isdefault(get_param(element, 2)))
data->newval = get_param(element, 2)->data.number;
- else if (strcmp(get_param_text(element, 1), "touch"))
+ else if (sv_op != 0) /*!touch*/
return WPS_ERROR_INVALID_PARAM;
data->max = 0;
- if (!strcmp(get_param_text(element, 1), "set"))
+ if (sv_op == 1) /*set*/
data->direct = true;
- else if (!strcmp(get_param_text(element, 1), "inc"))
+ else if (sv_op == 2) /*inc*/
{
data->direct = false;
}
- else if (!strcmp(get_param_text(element, 1), "dec"))
+ else if (sv_op == 3) /*dec*/
{
data->direct = false;
data->newval *= -1;
}
- else if (!strcmp(get_param_text(element, 1), "touch"))
+ else if (sv_op == 0) /*touch*/
{
data->direct = false;
data->newval = 0;
@@ -1404,12 +1498,12 @@ static int parse_lasttouch(struct skin_element *element,
for (i=0; i<element->params_count; i++)
{
- if (get_param(element, i)->type == STRING)
+ struct skin_tag_parameter *param = get_param(element, i);
+ if (param->type == STRING)
region = skin_find_item(get_param_text(element, i),
SKIN_FIND_TOUCHREGION, wps_data);
- else if (get_param(element, i)->type == INTEGER ||
- get_param(element, i)->type == DECIMAL)
- data->timeout = get_param(element, i)->data.number;
+ else if (param->type == INTEGER || param->type == DECIMAL)
+ data->timeout = param->data.number;
}
data->region = PTRTOSKINOFFSET(skin_buffer, region);
@@ -1462,48 +1556,48 @@ static int touchregion_setup_setting(struct skin_element *element, int param_no,
#ifndef __PCTOOL__
int p = param_no;
char *name = get_param_text(element, p++);
- int j;
-
- region->setting_data.setting = find_setting_by_cfgname(name, &j);
- if (region->setting_data.setting == NULL)
+ const struct settings_list *setting = find_setting_by_cfgname(name);
+ if (!setting)
return WPS_ERROR_INVALID_PARAM;
+ region->setting_data.setting = setting;
+
if (region->action == ACTION_SETTINGS_SET)
{
char* text;
int temp;
- struct touchsetting *setting =
+ struct touchsetting *touchsetting =
&region->setting_data;
if (element->params_count < p+1)
return -1;
text = get_param_text(element, p++);
- switch (settings[j].flags&F_T_MASK)
+ switch (setting->flags & F_T_MASK)
{
case F_T_CUSTOM:
- setting->value.text = PTRTOSKINOFFSET(skin_buffer, text);
+ touchsetting->value.text = PTRTOSKINOFFSET(skin_buffer, text);
break;
case F_T_INT:
case F_T_UINT:
- if (settings[j].cfg_vals == NULL)
+ if (setting->cfg_vals == NULL)
{
- setting->value.number = atoi(text);
+ touchsetting->value.number = atoi(text);
}
- else if (cfg_string_to_int(j, &temp, text))
+ else if (cfg_string_to_int(setting, &temp, text))
{
- if (settings[j].flags&F_TABLE_SETTING)
- setting->value.number =
- settings[j].table_setting->values[temp];
+ if (setting->flags & F_TABLE_SETTING)
+ touchsetting->value.number =
+ setting->table_setting->values[temp];
else
- setting->value.number = temp;
+ touchsetting->value.number = temp;
}
else
return -1;
break;
case F_T_BOOL:
- if (cfg_string_to_int(j, &temp, text))
+ if (cfg_string_to_int(setting, &temp, text))
{
- setting->value.number = temp;
+ touchsetting->value.number = temp;
}
else
return -1;
@@ -1541,6 +1635,10 @@ static int parse_touchregion(struct skin_element *element,
/* should probably do some bounds checking here with the viewport... but later */
region->action = ACTION_NONE;
+ /* padding is only for bars, user defined regions have no need of it */
+ region->wpad = 0;
+ region->hpad = 0;
+
if (get_param(element, 0)->type == STRING)
{
region->label = PTRTOSKINOFFSET(skin_buffer, get_param_text(element, 0));
@@ -1599,7 +1697,7 @@ static int parse_touchregion(struct skin_element *element,
region->armed = false;
region->reverse_bar = false;
region->value = 0;
- region->last_press = 0xffff;
+ region->last_press = -1;
region->press_length = PRESS;
region->allow_while_locked = false;
region->bar = PTRTOSKINOFFSET(skin_buffer, NULL);
@@ -1637,16 +1735,21 @@ static int parse_touchregion(struct skin_element *element,
if (region->action == ACTION_NONE)
return WPS_ERROR_INVALID_PARAM;
}
+ static const char * const pm_options[] = {"allow_while_locked", "reverse_bar",
+ "repeat_press", "long_press", NULL};
+ int pm_op;
+
while (p < element->params_count)
{
char* param = get_param_text(element, p++);
- if (!strcmp(param, "allow_while_locked"))
+ pm_op = string_option(param, pm_options, false);
+ if (pm_op == 0)
region->allow_while_locked = true;
- else if (!strcmp(param, "reverse_bar"))
+ else if (pm_op == 1)
region->reverse_bar = true;
- else if (!strcmp(param, "repeat_press"))
+ else if (pm_op == 2)
region->press_length = REPEAT;
- else if (!strcmp(param, "long_press"))
+ else if (pm_op == 3)
region->press_length = LONG_PRESS;
}
struct skin_token_list *item = new_skin_token_list_item(NULL, region);
@@ -1755,9 +1858,7 @@ void skin_data_free_buflib_allocs(struct wps_data *wps_data)
abort:
wps_data->font_ids = PTRTOSKINOFFSET(skin_buffer, NULL); /* Safe if skin_buffer is NULL */
wps_data->images = PTRTOSKINOFFSET(skin_buffer, NULL);
- if (wps_data->buflib_handle > 0)
- core_free(wps_data->buflib_handle);
- wps_data->buflib_handle = -1;
+ wps_data->buflib_handle = core_free(wps_data->buflib_handle);
#endif
}
@@ -1798,13 +1899,11 @@ static void skin_data_reset(struct wps_data *wps_data)
}
#ifndef __PCTOOL__
-static int currently_loading_handle = -1;
static int buflib_move_callback(int handle, void* current, void* new)
{
+ (void)handle;
(void)current;
(void)new;
- if (handle == currently_loading_handle)
- return BUFLIB_CB_CANNOT_MOVE;
/* Any active skins may be scrolling - which means using viewports which
* will be moved after this callback returns. This is a hammer to make that
* safe. TODO: use a screwdriver instead.
@@ -1817,79 +1916,63 @@ static int buflib_move_callback(int handle, void* current, void* new)
return BUFLIB_CB_OK;
}
-static struct buflib_callbacks buflib_ops = {buflib_move_callback, NULL, NULL};
-static void lock_handle(int handle)
-{
- currently_loading_handle = handle;
-}
-static void unlock_handle(void)
-{
- currently_loading_handle = -1;
-}
#endif
-static int load_skin_bmp(struct wps_data *wps_data, struct bitmap *bitmap, char* bmpdir)
+static int load_skin_bmp(struct wps_data *wps_data, struct gui_img *img, char* bmpdir)
{
+
(void)wps_data; /* only needed for remote targets */
char img_path[MAX_PATH];
- int fd;
- int handle;
+ struct bitmap *bitmap = &img->bm;
+
get_image_filename(bitmap->data, bmpdir,
img_path, sizeof(img_path));
- /* load the image */
- int format;
-#ifdef HAVE_REMOTE_LCD
- if (curr_screen == SCREEN_REMOTE)
- format = FORMAT_ANY|FORMAT_REMOTE;
- else
-#endif
- format = FORMAT_ANY|FORMAT_TRANSPARENT;
-
- fd = open(img_path, O_RDONLY);
+#ifdef __PCTOOL__ /* just check if image exists */
+ int fd = open(img_path, O_RDONLY);
if (fd < 0)
{
DEBUGF("Couldn't open %s\n", img_path);
return fd;
}
-#ifndef __PCTOOL__
- size_t buf_size = read_bmp_fd(fd, bitmap, 0,
- format|FORMAT_RETURN_SIZE, NULL);
- handle = core_alloc_ex(bitmap->data, buf_size, &buflib_ops);
- if (handle <= 0)
+ close(fd);
+ return 1;
+#else /* load the image */
+ static struct buflib_callbacks buflib_ops = {buflib_move_callback, NULL, NULL};
+ int handle;
+ int bmpformat;
+ ssize_t buf_reqd;
+#ifdef HAVE_REMOTE_LCD
+ if (curr_screen == SCREEN_REMOTE)
+ bmpformat = FORMAT_ANY|FORMAT_REMOTE;
+ else
+#endif
+ bmpformat = img->dither ? FORMAT_ANY|FORMAT_DITHER|FORMAT_TRANSPARENT :
+ FORMAT_ANY|FORMAT_TRANSPARENT;
+
+ handle = core_load_bmp(img_path, bitmap, bmpformat, &buf_reqd, &buflib_ops);
+ if (handle != CLB_ALOC_ERR)
{
- DEBUGF("Not enough skin buffer: need %zd more.\n",
- buf_size - skin_buffer_freespace());
- close(fd);
+ /* NOTE!: bitmap->data == NULL to force a crash later if the
+ caller doesnt call core_get_data() */
+ _stats->buflib_handles++;
+ _stats->images_size += buf_reqd;
return handle;
}
- _stats->buflib_handles++;
- _stats->images_size += buf_size;
- lseek(fd, 0, SEEK_SET);
- lock_handle(handle);
- bitmap->data = core_get_data(handle);
- int ret = read_bmp_fd(fd, bitmap, buf_size, format, NULL);
- bitmap->data = NULL; /* do this to force a crash later if the
- caller doesnt call core_get_data() */
- unlock_handle();
- close(fd);
- if (ret > 0)
+
+ if (buf_reqd == CLB_READ_ERR)
{
- /* free unused alpha channel, if any */
- core_shrink(handle, core_get_data(handle), ret);
- return handle;
+ /* Abort if we can't load an image */
+ DEBUGF("Couldn't load '%s' (%zu)\n", img_path, buf_reqd);
}
else
{
- /* Abort if we can't load an image */
- DEBUGF("Couldn't load '%s'\n", img_path);
- core_free(handle);
- return -1;
+ DEBUGF("Not enough skin buffer: need %zd more.\n",
+ buf_reqd - skin_buffer_freespace());
}
-#else /* !__PCTOOL__ */
- close(fd);
- return 1;
-#endif
+
+ return -1;
+#endif/* !__PCTOOL__ */
}
static bool load_skin_bitmaps(struct wps_data *wps_data, char *bmpdir)
@@ -1917,14 +2000,15 @@ static bool load_skin_bitmaps(struct wps_data *wps_data, char *bmpdir)
char path[MAX_PATH];
int handle;
strcpy(path, img->bm.data);
- handle = load_skin_bmp(wps_data, &img->bm, bmpdir);
+ handle = load_skin_bmp(wps_data, img, bmpdir);
img->buflib_handle = handle;
- img->loaded = img->buflib_handle >= 0;
+ img->loaded = img->buflib_handle > 0;
if (img->loaded)
{
struct skin_token_list *imglist = SKINOFFSETTOPTR(skin_buffer, list->next);
img->subimage_height = img->bm.height / img->num_subimages;
+ struct bitmap* loaded_bm = &img->bm;
while (imglist)
{
token = SKINOFFSETTOPTR(skin_buffer, imglist->token);
@@ -1934,6 +2018,7 @@ static bool load_skin_bitmaps(struct wps_data *wps_data, char *bmpdir)
{
img->loaded = true;
img->buflib_handle = handle;
+ img->bm = *loaded_bm;
img->subimage_height = img->bm.height / img->num_subimages;
}
}
@@ -2317,13 +2402,6 @@ static int skin_element_callback(struct skin_element* element, void* data)
break;
#endif
#ifdef HAVE_ALBUMART
- case SKIN_TOKEN_ALBUMART_DISPLAY:
- if (SKINOFFSETTOPTR(skin_buffer, wps_data->albumart))
- {
- struct skin_albumart *aa = SKINOFFSETTOPTR(skin_buffer, wps_data->albumart);
- aa->vp = PTRTOSKINOFFSET(skin_buffer, &curr_vp->vp);
- }
- break;
case SKIN_TOKEN_ALBUMART_LOAD:
function = parse_albumart_load;
break;
@@ -2459,8 +2537,9 @@ bool skin_data_load(enum screen_type screen, struct wps_data *wps_data,
skin_buffer = wps_buffer;
wps_buffer = (char*)buf;
}
- skin_buffer = ALIGN_UP(skin_buffer, 4); /* align on 4-byte boundary */
- buffersize -= 3;
+
+ /* align to long */
+ ALIGN_BUFFER(skin_buffer, buffersize, sizeof(long));
#ifdef HAVE_BACKDROP_IMAGE
backdrop_filename = "-";
wps_data->backdrop_id = -1;
@@ -2483,7 +2562,7 @@ bool skin_data_load(enum screen_type screen, struct wps_data *wps_data,
{
/* get the bitmap dir */
char *dot = strrchr(buf, '.');
- strlcpy(bmpdir, buf, dot - buf + 1);
+ strmemccpy(bmpdir, buf, dot - buf + 1);
}
else
{
@@ -2507,8 +2586,7 @@ bool skin_data_load(enum screen_type screen, struct wps_data *wps_data,
}
#endif
#ifndef __PCTOOL__
- wps_data->buflib_handle = core_alloc(isfile ? buf : "failsafe skin",
- skin_buffer_usage());
+ wps_data->buflib_handle = core_alloc(skin_buffer_usage());
if (wps_data->buflib_handle > 0)
{
wps_data->wps_loaded = true;
diff --git a/apps/gui/skin_engine/skin_render.c b/apps/gui/skin_engine/skin_render.c
index 1f777b6672..06f7d9798d 100644
--- a/apps/gui/skin_engine/skin_render.c
+++ b/apps/gui/skin_engine/skin_render.c
@@ -50,6 +50,7 @@
#include "root_menu.h"
#include "misc.h"
#include "list.h"
+#include "wps.h"
#define MAX_LINE 1024
@@ -66,7 +67,6 @@ struct skin_draw_info {
bool no_line_break;
bool line_scrolls;
bool force_redraw;
- bool viewport_change;
char *buf;
size_t buf_size;
@@ -74,6 +74,8 @@ struct skin_draw_info {
int offset; /* used by the playlist viewer */
};
+extern void sb_set_info_vp(enum screen_type screen, OFFSETTYPE(char*) label);
+
typedef bool (*skin_render_func)(struct skin_element* alternator, struct skin_draw_info *info);
bool skin_render_alternator(struct skin_element* alternator, struct skin_draw_info *info);
@@ -93,11 +95,11 @@ get_child(OFFSETTYPE(struct skin_element**) children, int child)
static bool do_non_text_tags(struct gui_wps *gwps, struct skin_draw_info *info,
- struct skin_element *element, struct skin_viewport* skin_vp)
+ struct skin_element *element)
{
struct wps_token *token = (struct wps_token *)SKINOFFSETTOPTR(skin_buffer, element->data);
if (!token) return false;
- struct viewport *vp = &skin_vp->vp;
+ struct skin_viewport *skin_vp = info->skin_vp;
struct wps_data *data = gwps->data;
bool do_refresh = (element->tag->flags & info->refresh_type) > 0;
@@ -108,9 +110,7 @@ static bool do_non_text_tags(struct gui_wps *gwps, struct skin_draw_info *info,
{
struct viewport_colour *col = SKINOFFSETTOPTR(skin_buffer, token->value.data);
if (!col) return false;
- struct viewport *vp = SKINOFFSETTOPTR(skin_buffer, col->vp);
- if (!vp) return false;
- vp->fg_pattern = col->colour;
+ skin_vp->vp.fg_pattern = col->colour;
skin_vp->fgbg_changed = true;
}
break;
@@ -118,9 +118,7 @@ static bool do_non_text_tags(struct gui_wps *gwps, struct skin_draw_info *info,
{
struct viewport_colour *col = SKINOFFSETTOPTR(skin_buffer, token->value.data);
if (!col) return false;
- struct viewport *vp = SKINOFFSETTOPTR(skin_buffer, col->vp);
- if (!vp) return false;
- vp->bg_pattern = col->colour;
+ skin_vp->vp.bg_pattern = col->colour;
skin_vp->fgbg_changed = true;
}
break;
@@ -196,7 +194,7 @@ static bool do_non_text_tags(struct gui_wps *gwps, struct skin_draw_info *info,
case SKIN_TOKEN_PEAKMETER:
data->peak_meter_enabled = true;
if (do_refresh)
- draw_peakmeters(gwps, info->line_number, vp);
+ draw_peakmeters(gwps, info->line_number, &skin_vp->vp);
break;
case SKIN_TOKEN_DRAWRECTANGLE:
if (do_refresh)
@@ -215,13 +213,13 @@ static bool do_non_text_tags(struct gui_wps *gwps, struct skin_draw_info *info,
#endif
{
#if LCD_DEPTH > 1
- unsigned backup = vp->fg_pattern;
- vp->fg_pattern = rect->start_colour;
+ unsigned backup = skin_vp->vp.fg_pattern;
+ skin_vp->vp.fg_pattern = rect->start_colour;
#endif
gwps->display->fillrect(rect->x, rect->y, rect->width,
rect->height);
#if LCD_DEPTH > 1
- vp->fg_pattern = backup;
+ skin_vp->vp.fg_pattern = backup;
#endif
}
}
@@ -239,7 +237,7 @@ static bool do_non_text_tags(struct gui_wps *gwps, struct skin_draw_info *info,
{
struct progressbar *bar = (struct progressbar*)SKINOFFSETTOPTR(skin_buffer, token->value.data);
if (do_refresh)
- draw_progressbar(gwps, info->line_number, bar);
+ draw_progressbar(gwps, info->skin_vp, info->line_number, bar);
}
break;
case SKIN_TOKEN_IMAGE_DISPLAY:
@@ -582,7 +580,7 @@ static bool skin_render_line(struct skin_element* line, struct skin_draw_info *i
{
break;
}
- if (!do_non_text_tags(info->gwps, info, child, info->skin_vp))
+ if (!do_non_text_tags(info->gwps, info, child))
{
static char tempbuf[128];
const char *valuestr = get_token_value(info->gwps, SKINOFFSETTOPTR(skin_buffer, child->data),
@@ -590,10 +588,6 @@ static bool skin_render_line(struct skin_element* line, struct skin_draw_info *i
sizeof(tempbuf), NULL);
if (valuestr)
{
-#if defined(ONDA_VX747) || defined(ONDA_VX747P)
- /* Doesn't redraw (in sim at least) */
- needs_update = true;
-#endif
#if CONFIG_RTC
if (child->tag->flags&SKIN_RTC_REFRESH)
needs_update = needs_update || info->refresh_type&SKIN_REFRESH_DYNAMIC;
@@ -606,10 +600,6 @@ static bool skin_render_line(struct skin_element* line, struct skin_draw_info *i
}
break;
case TEXT:
-#if defined(ONDA_VX747) || defined(ONDA_VX747P)
- /* Doesn't redraw (in sim at least) */
- needs_update = true;
-#endif
strlcat(info->cur_align_start, SKINOFFSETTOPTR(skin_buffer, child->data),
info->buf_size - (info->cur_align_start-info->buf));
needs_update = needs_update ||
@@ -943,7 +933,7 @@ void skin_render_playlistviewer(struct playlistviewer* viewer,
struct align_pos * align = &info.align;
bool needs_update;
int cur_pos, start_item, max;
- int nb_lines = viewport_get_nb_lines(SKINOFFSETTOPTR(skin_buffer, viewer->vp));
+ int nb_lines = viewport_get_nb_lines(&skin_viewport->vp);
#if CONFIG_TUNER
if (get_current_activity() == ACTIVITY_FM)
{
@@ -954,8 +944,8 @@ void skin_render_playlistviewer(struct playlistviewer* viewer,
else
#endif
{
- struct cuesheet *cue = skin_get_global_state()->id3 ?
- skin_get_global_state()->id3->cuesheet : NULL;
+ struct wps_state *state = get_wps_state();
+ struct cuesheet *cue = state->id3 ? state->id3->cuesheet : NULL;
cur_pos = playlist_get_display_index();
max = playlist_amount()+1;
if (cue)
@@ -989,7 +979,7 @@ void skin_render_playlistviewer(struct playlistviewer* viewer,
/* only update if the line needs to be, and there is something to write */
if (refresh_type && needs_update)
{
- struct viewport *vp = SKINOFFSETTOPTR(skin_buffer, viewer->vp);
+ struct viewport *vp = &skin_viewport->vp;
if (!info.force_redraw)
display->scroll_stop_viewport_rect(vp,
0, info.line_number*display->getcharheight(),
diff --git a/apps/gui/skin_engine/skin_tokens.c b/apps/gui/skin_engine/skin_tokens.c
index a0de45d3e3..12277aa0ea 100644
--- a/apps/gui/skin_engine/skin_tokens.c
+++ b/apps/gui/skin_engine/skin_tokens.c
@@ -66,11 +66,10 @@
#include "fixedpoint.h"
#endif
#include "list.h"
+#include "wps.h"
#define NOINLINE __attribute__ ((noinline))
-extern struct wps_state wps_state;
-
static const char* get_codectype(const struct mp3entry* id3)
{
if (id3 && id3->codectype < AFMT_NUM_CODECS) {
@@ -88,6 +87,7 @@ static const char* get_codectype(const struct mp3entry* id3)
* level - what to extract. 0 is file name, 1 is parent of file, 2 is
* parent of parent, etc.
*
+ * path does not need to be absolute, ignores multiple slashes
* Returns buf if the desired level was found, NULL otherwise.
*/
char* get_dir(char* buf, int buf_size, const char* path, int level)
@@ -95,7 +95,7 @@ char* get_dir(char* buf, int buf_size, const char* path, int level)
const char* sep;
const char* last_sep;
int len;
-
+ buf[0] = '\0';
sep = path + strlen(path);
last_sep = sep;
@@ -106,16 +106,20 @@ char* get_dir(char* buf, int buf_size, const char* path, int level)
if (!level)
break;
- level--;
last_sep = sep - 1;
+ if (*last_sep != '/') /* ignore multiple separators */
+ level--;
}
}
- if (level || (last_sep <= sep))
+ if (level || (last_sep <= sep)) /* level was not found */
return NULL;
- len = MIN(last_sep - sep, buf_size - 1);
- strlcpy(buf, sep + 1, len + 1);
+ if (sep == path && *sep != '/') /* for paths without leading separator */
+ sep = path - 1;
+
+ len = MIN((last_sep - sep), buf_size - 1);
+ strmemccpy(buf, sep + 1, len + 1);
return buf;
}
@@ -228,7 +232,7 @@ static const char* get_filename_token(struct wps_token *token, char* filename,
const char *get_id3_token(struct wps_token *token, struct mp3entry *id3,
char *filename, char *buf, int buf_size, int limit, int *intval)
{
- struct wps_state *state = &wps_state;
+ struct wps_state *state = get_wps_state();
if (id3)
{
unsigned long length = id3->length;
@@ -540,7 +544,7 @@ const char *get_radio_token(struct wps_token *token, int preset_offset,
static struct mp3entry* get_mp3entry_from_offset(int offset, char **filename)
{
struct mp3entry* pid3 = NULL;
- struct wps_state *state = skin_get_global_state();
+ struct wps_state *state = get_wps_state();
struct cuesheet *cue = state->id3 ? state->id3->cuesheet : NULL;
const char *fname = NULL;
if (cue && cue->curr_track_idx + offset < cue->track_count)
@@ -678,7 +682,7 @@ const char *get_token_value(struct gui_wps *gwps,
return NULL;
struct wps_data *data = gwps->data;
- struct wps_state *state = skin_get_global_state();
+ struct wps_state *state = get_wps_state();
struct mp3entry *id3; /* Think very carefully about using this.
maybe get_id3_token() is the better place? */
const char *out_text = NULL;
@@ -821,7 +825,8 @@ const char *get_token_value(struct gui_wps *gwps,
return (char*)SKINOFFSETTOPTR(get_skin_buffer(data), token->value.data);
case SKIN_TOKEN_TRANSLATEDSTRING:
- return (char*)P2STR(ID2P(token->value.i));
+ return token->value.i < LANG_LAST_INDEX_IN_ARRAY ?
+ (char*)P2STR(ID2P(token->value.i)) : "<ERR>";
case SKIN_TOKEN_PLAYLIST_ENTRIES:
numeric_ret = playlist_amount();
@@ -1039,37 +1044,19 @@ const char *get_token_value(struct gui_wps *gwps,
case SKIN_TOKEN_PLAYBACK_STATUS:
{
int status = current_playmode();
- /* music */
- int mode = 1; /* stop */
- if (status == STATUS_PLAY)
- mode = 2; /* play */
- if (state->is_fading ||
- (status == STATUS_PAUSE && !status_get_ffmode()))
- mode = 3; /* pause */
- else
- { /* ff / rwd */
- if (status_get_ffmode() == STATUS_FASTFORWARD)
- mode = 4;
- if (status_get_ffmode() == STATUS_FASTBACKWARD)
- mode = 5;
+ switch (status) {
+ case STATUS_STOP:
+ numeric_ret = 1;
+ break;
+ case STATUS_PLAY:
+ numeric_ret = 2;
+ break;
+ default:
+ numeric_ret = status + 1;
+ break;
}
-#ifdef HAVE_RECORDING
- /* recording */
- if (status == STATUS_RECORD)
- mode = 6;
- else if (status == STATUS_RECORD_PAUSE)
- mode = 7;
-#endif
-#if CONFIG_TUNER
- /* radio */
- if (status == STATUS_RADIO)
- mode = 8;
- else if (status == STATUS_RADIO_PAUSE)
- mode = 9;
-#endif
- numeric_ret = mode;
- snprintf(buf, buf_size, "%d", mode-1);
+ snprintf(buf, buf_size, "%d", numeric_ret-1);
numeric_buf = buf;
goto gtv_ret_numeric_tag_info;
}
@@ -1366,7 +1353,7 @@ const char *get_token_value(struct gui_wps *gwps,
case SKIN_TOKEN_LASTTOUCH:
{
#ifdef HAVE_TOUCHSCREEN
- unsigned int last_touch = touchscreen_last_touch();
+ long last_touch = touchscreen_last_touch();
char *skin_base = get_skin_buffer(data);
struct touchregion_lastpress *data = SKINOFFSETTOPTR(skin_base, token->value.data);
if (!data) return NULL;
@@ -1374,7 +1361,7 @@ const char *get_token_value(struct gui_wps *gwps,
if (region)
last_touch = region->last_press;
- if (last_touch != 0xffff &&
+ if (last_touch != -1 &&
TIME_BEFORE(current_tick, data->timeout + last_touch))
return "t";
#endif
@@ -1389,7 +1376,7 @@ const char *get_token_value(struct gui_wps *gwps,
case SKIN_TOKEN_SETTING:
{
- const struct settings_list *s = settings+token->value.i;
+ const struct settings_list *s = token->value.xdata;
if (intval)
{
/* Handle contionals */
@@ -1460,7 +1447,7 @@ const char *get_token_value(struct gui_wps *gwps,
goto gtv_ret_numeric_tag_info;
}
}
- cfg_to_string(token->value.i,buf,buf_size);
+ cfg_to_string(s, buf, buf_size);
numeric_buf = buf;
goto gtv_ret_numeric_tag_info;
}
diff --git a/apps/gui/skin_engine/skin_touchsupport.c b/apps/gui/skin_engine/skin_touchsupport.c
index 7a03e83c36..b952709562 100644
--- a/apps/gui/skin_engine/skin_touchsupport.c
+++ b/apps/gui/skin_engine/skin_touchsupport.c
@@ -35,8 +35,9 @@
#include "dsp_misc.h"
/** Disarms all touchregions. */
-void skin_disarm_touchregions(struct wps_data *data)
+void skin_disarm_touchregions(struct gui_wps *gwps)
{
+ struct wps_data *data = gwps->data;
char* skin_buffer = get_skin_buffer(data);
struct skin_token_list *regions = SKINOFFSETTOPTR(skin_buffer, data->touchregions);
while (regions)
@@ -52,9 +53,9 @@ void skin_disarm_touchregions(struct wps_data *data)
* egde_offset is a percentage value for the position of the touch
* inside the bar for regions which arnt WPS_TOUCHREGION_ACTION type.
*/
-int skin_get_touchaction(struct wps_data *data, int* edge_offset,
- struct touchregion **retregion)
+int skin_get_touchaction(struct gui_wps *gwps, int* edge_offset)
{
+ struct wps_data *data = gwps->data;
int returncode = ACTION_NONE;
short x,y;
short vx, vy;
@@ -92,6 +93,18 @@ int skin_get_touchaction(struct wps_data *data, int* edge_offset,
* are relative to a preceding viewport */
vx = x - wvp->vp.x;
vy = y - wvp->vp.y;
+
+ /* project touches in the padding region so they clamp to the
+ * edge of the region instead */
+ if(r->x - r->wpad <= vx && vx < r->x)
+ vx = r->x;
+ else if(r->x + r->width <= vx && vx < r->x + r->width + r->wpad)
+ vx = r->x + r->width - 1;
+ if(r->y - r->hpad <= vy && vy < r->y)
+ vy = r->y;
+ else if(r->y + r->height <= vy && vy < r->y + r->height + r->hpad)
+ vy = r->y + r->height - 1;
+
/* now see if the point is inside this region */
if (vx >= r->x && vx < r->x+r->width &&
vy >= r->y && vy < r->y+r->height)
@@ -109,12 +122,21 @@ int skin_get_touchaction(struct wps_data *data, int* edge_offset,
{
struct progressbar *bar =
SKINOFFSETTOPTR(skin_buffer, r->bar);
- if(r->width > r->height)
- *edge_offset = vx*100/r->width;
- else /* vertical bars are bottom-up by default */
- *edge_offset = 100 - vy*100/r->height;
+ if(r->width > r->height) {
+ if(r->width > 1)
+ *edge_offset = vx*1000/(r->width - 1);
+ else
+ *edge_offset = 0;
+ } else {
+ /* vertical bars are bottom-up by default */
+ if(r->height > 1)
+ *edge_offset = 1000 - vy*1000/(r->height - 1);
+ else
+ *edge_offset = 0;
+ }
+
if (r->reverse_bar || (bar && bar->invert_fill_direction))
- *edge_offset = 100 - *edge_offset;
+ *edge_offset = 1000 - *edge_offset;
}
temp = r;
returncode = r->action;
@@ -141,9 +163,7 @@ int skin_get_touchaction(struct wps_data *data, int* edge_offset,
/* On release, all regions are disarmed. */
if (released)
- skin_disarm_touchregions(data);
- if (retregion && temp)
- *retregion = temp;
+ skin_disarm_touchregions(gwps);
if (temp && temp->press_length == LONG_PRESS)
temp->armed = false;
@@ -280,7 +300,7 @@ int skin_get_touchaction(struct wps_data *data, int* edge_offset,
case ACTION_TOUCH_REPMODE: /* cycle the repeat mode setting */
{
const struct settings_list *rep_setting =
- find_setting(&global_settings.repeat_mode, NULL);
+ find_setting(&global_settings.repeat_mode);
option_select_next_val(rep_setting, false, true);
audio_flush_and_reload_tracks();
returncode = ACTION_REDRAW;
@@ -293,9 +313,9 @@ int skin_get_touchaction(struct wps_data *data, int* edge_offset,
if (bar && edge_offset)
{
int val, count;
- get_setting_info_for_bar(bar->setting_id, &count, &val);
- val = *edge_offset * count / 100;
- update_setting_value_from_touch(bar->setting_id, val);
+ get_setting_info_for_bar(bar->setting, &count, &val);
+ val = *edge_offset * count / 1000;
+ update_setting_value_from_touch(bar->setting, val);
}
}
break;
diff --git a/apps/gui/skin_engine/wps_internals.h b/apps/gui/skin_engine/wps_internals.h
index dcad598dab..8ad8325e66 100644
--- a/apps/gui/skin_engine/wps_internals.h
+++ b/apps/gui/skin_engine/wps_internals.h
@@ -31,6 +31,8 @@
#include "core_alloc.h"
#endif
+struct wps_data;
+
struct skin_stats {
size_t buflib_handles;
size_t tree_size;
@@ -42,6 +44,13 @@ struct skin_stats *skin_get_stats(int number, int screen);
#define skin_clear_stats(stats) memset(stats, 0, sizeof(struct skin_stats))
bool skin_backdrop_get_debug(int index, char **path, int *ref_count, size_t *size);
+/*
+ * setup up the skin-data from a format-buffer (isfile = false)
+ * or from a skinfile (isfile = true)
+ */
+bool skin_data_load(enum screen_type screen, struct wps_data *wps_data,
+ const char *buf, bool isfile, struct skin_stats *stats);
+
/* Timeout unit expressed in HZ. In WPS, all timeouts are given in seconds
(possibly with a decimal fraction) but stored as integer values.
E.g. 2.5 is stored as 25. This means 25 tenth of a second, i.e. 25 units.
@@ -64,6 +73,7 @@ struct wps_token {
unsigned short i;
long l;
OFFSETTYPE(void*) data;
+ void *xdata;
} value;
enum skin_token_type type; /* enough to store the token type */
@@ -81,7 +91,6 @@ struct skin_token_list {
};
struct gui_img {
- OFFSETTYPE(struct viewport*) vp; /* The viewport to display this image in */
short int x; /* x-pos */
short int y; /* y-pos */
short int num_subimages; /* number of sub-images */
@@ -93,6 +102,7 @@ struct gui_img {
int display;
bool using_preloaded_icons; /* using the icon system instead of a bmp */
bool is_9_segment;
+ bool dither;
};
struct image_display {
@@ -104,7 +114,6 @@ struct image_display {
struct progressbar {
enum skin_token_type type;
- OFFSETTYPE(struct viewport *) vp;
/* regular pb */
short x;
/* >=0: explicitly set in the tag -> y-coord within the viewport
@@ -124,7 +133,7 @@ struct progressbar {
OFFSETTYPE(struct gui_img *) slider;
bool horizontal;
OFFSETTYPE(struct gui_img *) backdrop;
- int setting_id; /* for the setting bar type */
+ const struct settings_list *setting;
};
@@ -189,7 +198,6 @@ struct skin_viewport {
#endif
};
struct viewport_colour {
- OFFSETTYPE(struct viewport *) vp;
unsigned colour;
};
@@ -201,6 +209,8 @@ struct touchregion {
short int y; /* y-pos */
short int width; /* width */
short int height; /* height */
+ short int wpad; /* padding to width */
+ short int hpad; /* padding to height */
bool reverse_bar; /* if true 0% is the left or top */
bool allow_while_locked;
enum {
@@ -234,7 +244,6 @@ struct touchregion_lastpress {
#endif
struct playlistviewer {
- OFFSETTYPE(struct viewport *) vp;
bool show_icons;
int start_offset;
OFFSETTYPE(struct skin_element *) line;
@@ -265,7 +274,6 @@ struct skin_albumart {
unsigned char yalign; /* WPS_ALBUMART_ALIGN_TOP, _CENTER, _BOTTOM */
unsigned char state; /* WPS_ALBUMART_NONE, _CHECK, _LOAD */
- OFFSETTYPE(struct viewport *) vp;
int draw_handle;
};
#endif
@@ -311,6 +319,17 @@ struct listitem {
short offset;
};
+struct listitem_viewport_cfg {
+ struct wps_data *data;
+ OFFSETTYPE(char *) label;
+ int width;
+ int height;
+ int xmargin;
+ int ymargin;
+ bool tile;
+ struct skin_viewport selected_item_vp;
+};
+
#ifdef HAVE_SKIN_VARIABLES
struct skin_var {
OFFSETTYPE(const char *) label;
@@ -370,7 +389,7 @@ struct wps_data
#ifndef __PCTOOL__
static inline char* get_skin_buffer(struct wps_data* data)
{
- if (data->buflib_handle >= 0)
+ if (data->buflib_handle > 0)
return core_get_data(data->buflib_handle);
return NULL;
}
@@ -380,30 +399,6 @@ static inline char* get_skin_buffer(struct wps_data* data)
/* wps_data end */
-/* wps_state
- holds the data which belongs to the current played track,
- the track which will be played afterwards, current path to the track
- and some status infos */
-struct wps_state
-{
- struct mp3entry* id3;
- struct mp3entry* nid3;
- int ff_rewind_count;
- bool ff_rewind;
- bool paused;
- bool is_fading;
-};
-
-/* change the ff/rew-status
- if ff_rew = true then we are in skipping mode
- else we are in normal mode */
-/* void wps_state_update_ff_rew(bool ff_rew); Currently unused */
-
-/* change the tag-information of the current played track
- and the following track */
-/* void wps_state_update_id3_nid3(struct mp3entry *id3, struct mp3entry *nid3); Currently unused */
-/* wps_state end*/
-
/* gui_wps
defines a wps with its data, state,
and the screen on which the wps-content should be drawn */
diff --git a/apps/gui/splash.c b/apps/gui/splash.c
index b85e4693aa..d0f1fbb67c 100644
--- a/apps/gui/splash.c
+++ b/apps/gui/splash.c
@@ -29,71 +29,84 @@
#include "talk.h"
#include "splash.h"
#include "viewport.h"
-#include "strtok_r.h"
+#include "strptokspn_r.h"
+#include "scrollbar.h"
+#include "font.h"
+
+static long progress_next_tick = 0;
#define MAXLINES (LCD_HEIGHT/6)
#define MAXBUFFER 512
#define RECT_SPACING 2
#define SPLASH_MEMORY_INTERVAL (HZ)
-static void splash_internal(struct screen * screen, const char *fmt, va_list ap)
+static bool splash_internal(struct screen * screen, const char *fmt, va_list ap,
+ struct viewport *vp, int addl_lines)
{
char splash_buf[MAXBUFFER];
- char *lines[MAXLINES];
-
- char *next;
- char *lastbreak = NULL;
- char *store = NULL;
+ struct splash_lines {
+ const char *str;
+ size_t len;
+ } lines[MAXLINES];
+ const char *next;
+ const char *lastbreak = NULL;
+ const char *store = NULL;
int line = 0;
int x = 0;
int y, i;
- int space_w, w, h;
- struct viewport vp;
+ int space_w, w, chr_h;
int width, height;
int maxw = 0;
+ int fontnum = vp->font;
- viewport_set_defaults(&vp, screen->screen_type);
- struct viewport *last_vp = screen->set_viewport(&vp);
-
- screen->getstringsize(" ", &space_w, &h);
- y = h;
+ char lastbrkchr;
+ size_t len, next_len;
+ const char matchstr[] = "\r\n\f\v\t ";
+ font_getstringsize(" ", &space_w, &chr_h, fontnum);
+ y = chr_h + (addl_lines * chr_h);
vsnprintf(splash_buf, sizeof(splash_buf), fmt, ap);
va_end(ap);
/* break splash string into display lines, doing proper word wrap */
-
- next = strtok_r(splash_buf, " ", &store);
+ next = strptokspn_r(splash_buf, matchstr, &next_len, &store);
if (!next)
- goto end; /* nothing to display */
+ return false; /* nothing to display */
- lines[0] = next;
+ lines[line].len = next_len;
+ lines[line].str = next;
while (true)
{
- screen->getstringsize(next, &w, NULL);
+ w = font_getstringnsize(next, next_len, NULL, NULL, fontnum);
if (lastbreak)
{
- if (x + (next - lastbreak) * space_w + w
- > vp.width - RECT_SPACING*2)
- { /* too wide, wrap */
+ len = next - lastbreak;
+ int next_w = len * space_w;
+ if (x + next_w + w > vp->width - RECT_SPACING*2 || lastbrkchr != ' ')
+ { /* too wide, or control character wrap */
if (x > maxw)
maxw = x;
- if ((y + h > vp.height) || (line >= (MAXLINES-1)))
+ if ((y + chr_h * 2 > vp->height) || (line >= (MAXLINES-1)))
break; /* screen full or out of lines */
x = 0;
- y += h;
- lines[++line] = next;
+ y += chr_h;
+ lines[++line].len = next_len;
+ lines[line].str = next;
}
else
{
/* restore & calculate spacing */
- *lastbreak = ' ';
- x += (next - lastbreak) * space_w;
+ lines[line].len += next_len + 1;
+ x += next_w;
}
}
x += w;
- lastbreak = next + strlen(next);
- next = strtok_r(NULL, " ", &store);
+
+ lastbreak = next + next_len;
+ lastbrkchr = *lastbreak;
+
+ next = strptokspn_r(NULL, matchstr, &next_len, &store);
+
if (!next)
{ /* no more words */
if (x > maxw)
@@ -111,53 +124,48 @@ static void splash_internal(struct screen * screen, const char *fmt, va_list ap)
width = maxw + 2*RECT_SPACING;
height = y + 2*RECT_SPACING;
- if (width > vp.width)
- width = vp.width;
- if (height > vp.height)
- height = vp.height;
+ if (width > vp->width)
+ width = vp->width;
+ if (height > vp->height)
+ height = vp->height;
- vp.x += (vp.width - width) / 2;
- vp.y += (vp.height - height) / 2;
- vp.width = width;
- vp.height = height;
+ vp->x += (vp->width - width) / 2;
+ vp->y += (vp->height - height) / 2;
+ vp->width = width;
+ vp->height = height;
- vp.flags |= VP_FLAG_ALIGN_CENTER;
+ vp->flags |= VP_FLAG_ALIGN_CENTER;
#if LCD_DEPTH > 1
if (screen->depth > 1)
{
- vp.drawmode = DRMODE_FG;
- /* can't do vp.fg_pattern here, since set_foreground does a bit more on
+ vp->drawmode = DRMODE_FG;
+ /* can't do vp->fg_pattern here, since set_foreground does a bit more on
* greyscale */
screen->set_foreground(SCREEN_COLOR_TO_NATIVE(screen, LCD_LIGHTGRAY));
}
else
#endif
- vp.drawmode = (DRMODE_SOLID|DRMODE_INVERSEVID);
+ vp->drawmode = (DRMODE_SOLID|DRMODE_INVERSEVID);
screen->fill_viewport();
#if LCD_DEPTH > 1
if (screen->depth > 1)
- /* can't do vp.fg_pattern here, since set_foreground does a bit more on
+ /* can't do vp->fg_pattern here, since set_foreground does a bit more on
* greyscale */
screen->set_foreground(SCREEN_COLOR_TO_NATIVE(screen, LCD_BLACK));
else
#endif
- vp.drawmode = DRMODE_SOLID;
+ vp->drawmode = DRMODE_SOLID;
screen->draw_border_viewport();
- /* prepare putting the text */
- y = RECT_SPACING;
-
/* print the message to screen */
- for (i = 0; i <= line; i++, y+=h)
+ for(i = 0, y = RECT_SPACING; i <= line; i++, y+= chr_h)
{
- screen->putsxy(0, y, lines[i]);
+ screen->putsxyf(0, y, "%.*s", lines[i].len, lines[i].str);
}
- screen->update_viewport();
-end:
- screen->set_viewport(last_vp);
+ return true; /* needs update */
}
void splashf(int ticks, const char *fmt, ...)
@@ -169,9 +177,17 @@ void splashf(int ticks, const char *fmt, ...)
fmt = P2STR((unsigned char *)fmt);
FOR_NB_SCREENS(i)
{
+ struct screen * screen = &(screens[i]);
+ struct viewport vp;
+ viewport_set_defaults(&vp, screen->screen_type);
+ struct viewport *last_vp = screen->set_viewport(&vp);
+
va_start(ap, fmt);
- splash_internal(&(screens[i]), fmt, ap);
+ if (splash_internal(screen, fmt, ap, &vp, 0))
+ screen->update_viewport();
va_end(ap);
+
+ screen->set_viewport(last_vp);
}
if (ticks)
sleep(ticks);
@@ -189,3 +205,59 @@ void splash(int ticks, const char *str)
#endif
splashf(ticks, "%s", P2STR((const unsigned char*)str));
}
+
+/* set delay before progress meter is shown */
+void splash_progress_set_delay(long delay_ticks)
+{
+ progress_next_tick = current_tick + delay_ticks;
+}
+
+/* splash a progress meter */
+void splash_progress(int current, int total, const char *fmt, ...)
+{
+ va_list ap;
+ int vp_flag = VP_FLAG_VP_DIRTY;
+ /* progress update tick */
+ long now = current_tick;
+
+ if (current < total)
+ {
+ if(TIME_BEFORE(now, progress_next_tick))
+ return;
+ /* limit to 20fps */
+ progress_next_tick = now + HZ/20;
+ vp_flag = 0; /* don't mark vp dirty to prevent flashing */
+ }
+
+ /* If fmt is a lang ID then get the corresponding string (which
+ still might contain % place holders). */
+ fmt = P2STR((unsigned char *)fmt);
+ FOR_NB_SCREENS(i)
+ {
+ struct screen * screen = &(screens[i]);
+ struct viewport vp;
+ viewport_set_defaults(&vp, screen->screen_type);
+ struct viewport *last_vp = screen->set_viewport_ex(&vp, vp_flag);
+
+ va_start(ap, fmt);
+ if (splash_internal(screen, fmt, ap, &vp, 1))
+ {
+ int size = screen->getcharheight();
+ int x = RECT_SPACING;
+ int y = vp.height - size - RECT_SPACING;
+ int w = vp.width - RECT_SPACING * 2;
+ int h = size;
+#ifdef HAVE_LCD_COLOR
+ const int sb_flags = HORIZONTAL | FOREGROUND;
+#else
+ const int sb_flags = HORIZONTAL;
+#endif
+ gui_scrollbar_draw(screen, x, y, w, h, total, 0, current, sb_flags);
+
+ screen->update_viewport();
+ }
+ va_end(ap);
+
+ screen->set_viewport(last_vp);
+ }
+}
diff --git a/apps/gui/splash.h b/apps/gui/splash.h
index 76b4c16d0c..f7ff44e00b 100644
--- a/apps/gui/splash.h
+++ b/apps/gui/splash.h
@@ -39,4 +39,15 @@ extern void splashf(int ticks, const char *fmt, ...) ATTRIBUTE_PRINTF(2, 3);
* it will be voiced
*/
extern void splash(int ticks, const char *str);
+
+/* set a delay before displaying the progress meter the first time */
+extern void splash_progress_set_delay(long delay_ticks);
+/*
+ * Puts a splash message centered on all the screens with a progressbar
+ * - current : current progress increment
+ * - total : total increments
+ * - fmt : what to say *printf style
+ * updates limited internally to 20 fps - call repeatedly to update progress
+ */
+extern void splash_progress(int current, int total, const char *fmt, ...) ATTRIBUTE_PRINTF(3, 4);
#endif /* _GUI_ICON_H_ */
diff --git a/apps/gui/statusbar-skinned.c b/apps/gui/statusbar-skinned.c
index 9b10ad4dce..8dd66641dd 100644
--- a/apps/gui/statusbar-skinned.c
+++ b/apps/gui/statusbar-skinned.c
@@ -27,7 +27,6 @@
#include "appevents.h"
#include "screens.h"
#include "screen_access.h"
-#include "strlcpy.h"
#include "skin_parser.h"
#include "skin_buffer.h"
#include "skin_engine/skin_engine.h"
@@ -49,11 +48,13 @@
static int update_delay = DEFAULT_UPDATE_DELAY;
static bool sbs_has_title[NB_SCREENS];
-static char* sbs_title[NB_SCREENS];
+static const char* sbs_title[NB_SCREENS];
static enum themable_icons sbs_icon[NB_SCREENS];
static bool sbs_loaded[NB_SCREENS] = { false };
-bool sb_set_title_text(char* title, enum themable_icons icon, enum screen_type screen)
+void sb_set_info_vp(enum screen_type screen, OFFSETTYPE(char*) label);
+
+bool sb_set_title_text(const char* title, enum themable_icons icon, enum screen_type screen)
{
sbs_title[screen] = title;
/* Icon_NOICON == -1 which the skin engine wants at position 1, so + 2 */
@@ -156,7 +157,12 @@ int sb_get_backdrop(enum screen_type screen)
else
return -1;
}
-
+#else
+int sb_get_backdrop(enum screen_type screen)
+{
+ (void) screen;
+ return -1;
+}
#endif
static bool force_waiting = false;
void sb_skin_update(enum screen_type screen, bool force)
@@ -270,6 +276,7 @@ char* sb_create_from_settings(enum screen_type screen)
{
case STATUSBAR_TOP:
y = STATUSBAR_HEIGHT;
+ /* Fallthrough */
case STATUSBAR_BOTTOM:
height = screens[screen].lcdheight - STATUSBAR_HEIGHT;
break;
@@ -299,18 +306,17 @@ void sb_bypass_touchregions(bool enable)
int sb_touch_to_button(int context)
{
- struct touchregion *region;
static int last_context = -1;
int button, offset;
if (bypass_sb_touchregions)
return ACTION_TOUCHSCREEN;
-
+
+ struct gui_wps *gwps = skin_get_gwps(CUSTOM_STATUSBAR, SCREEN_MAIN);
if (last_context != context)
- skin_disarm_touchregions(skin_get_gwps(CUSTOM_STATUSBAR, SCREEN_MAIN)->data);
+ skin_disarm_touchregions(gwps);
last_context = context;
- button = skin_get_touchaction(skin_get_gwps(CUSTOM_STATUSBAR, SCREEN_MAIN)->data,
- &offset, &region);
-
+
+ button = skin_get_touchaction(gwps, &offset);
switch (button)
{
#ifdef HAVE_VOLUME_IN_LIST
diff --git a/apps/gui/statusbar-skinned.h b/apps/gui/statusbar-skinned.h
index ad102bef47..e8fa14e676 100644
--- a/apps/gui/statusbar-skinned.h
+++ b/apps/gui/statusbar-skinned.h
@@ -30,16 +30,15 @@
#include "icon.h"
#include "skin_engine/skin_engine.h"
-void sb_skin_data_load(enum screen_type screen, const char *buf, bool isfile);
+struct wps_data;
char* sb_create_from_settings(enum screen_type screen);
void sb_skin_init(void) INIT_ATTR;
-void sb_set_info_vp(enum screen_type screen, OFFSETTYPE(char*) label);
struct viewport *sb_skin_get_info_vp(enum screen_type screen);
void sb_skin_update(enum screen_type screen, bool force);
void sb_skin_set_update_delay(int delay);
-bool sb_set_title_text(char* title, enum themable_icons icon, enum screen_type screen);
+bool sb_set_title_text(const char* title, enum themable_icons icon, enum screen_type screen);
void sb_skin_has_title(enum screen_type screen);
const char* sb_get_title(enum screen_type screen);
enum themable_icons sb_get_icon(enum screen_type screen);
@@ -49,9 +48,7 @@ void sb_bypass_touchregions(bool enable);
int sb_touch_to_button(int context);
#endif
-#if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1)
int sb_get_backdrop(enum screen_type screen);
-#endif
int sb_preproccess(enum screen_type screen, struct wps_data *data);
int sb_postproccess(enum screen_type screen, struct wps_data *data);
diff --git a/apps/gui/statusbar.c b/apps/gui/statusbar.c
index a961d191b2..74da14bd3f 100644
--- a/apps/gui/statusbar.c
+++ b/apps/gui/statusbar.c
@@ -725,18 +725,6 @@ void gui_syncstatusbar_init(struct gui_syncstatusbar * bars)
}
}
-void gui_syncstatusbar_draw(struct gui_syncstatusbar * bars,
- bool force_redraw)
-{
- if(!global_settings.statusbar)
- return;
- struct viewport viewport;
- FOR_NB_SCREENS(i) {
- GET_RECT(viewport,statusbar_position(i),&screens[i]);
- gui_statusbar_draw( &(bars->statusbars[i]), force_redraw, &viewport );
- }
-}
-
#ifdef HAVE_REMOTE_LCD
enum statusbar_values statusbar_position(int screen)
diff --git a/apps/gui/statusbar.h b/apps/gui/statusbar.h
index a676c7b143..c62434155d 100644
--- a/apps/gui/statusbar.h
+++ b/apps/gui/statusbar.h
@@ -99,8 +99,7 @@ struct gui_syncstatusbar
};
extern void gui_syncstatusbar_init(struct gui_syncstatusbar * bars) INIT_ATTR;
-extern void gui_syncstatusbar_draw(struct gui_syncstatusbar * bars,
- bool force_redraw);
+
#if !defined(HAVE_REMOTE_LCD) || defined(__PCTOOL__)
#include "settings.h"
#define statusbar_position(a) ((enum statusbar_values)global_settings.statusbar)
diff --git a/apps/gui/usb_screen.c b/apps/gui/usb_screen.c
index 31321ec005..fb59f820b6 100644
--- a/apps/gui/usb_screen.c
+++ b/apps/gui/usb_screen.c
@@ -41,6 +41,7 @@
#include "skin_engine/skin_engine.h"
#include "playlist.h"
#include "misc.h"
+#include "icons.h"
#include "bitmaps/usblogo.h"
@@ -155,7 +156,24 @@ static void usb_screen_fix_viewports(struct screen *screen,
*logo = *parent;
logo->x = parent->x + parent->width - logo_width;
+#ifdef HAVE_LCD_SPLIT
+ switch (statusbar_position(screen))
+ {
+ /* start beyond split */
+ case STATUSBAR_OFF:
+ logo->y = parent->y + LCD_SPLIT_POS;
+ break;
+ case STATUSBAR_TOP:
+ logo->y = parent->y + LCD_SPLIT_POS - STATUSBAR_HEIGHT;
+ break;
+ /* start at the top for maximum space */
+ default:
+ logo->y = parent->y;
+ break;
+ }
+#else
logo->y = parent->y + (parent->height - logo_height) / 2;
+#endif
logo->width = logo_width;
logo->height = logo_height;
diff --git a/apps/gui/viewport.c b/apps/gui/viewport.c
index 3dd8bca979..e90426a132 100644
--- a/apps/gui/viewport.c
+++ b/apps/gui/viewport.c
@@ -233,7 +233,7 @@ static void viewportmanager_redraw(unsigned short id, void* data)
}
}
-void viewportmanager_init()
+void viewportmanager_init(void)
{
FOR_NB_SCREENS(i)
{
@@ -332,7 +332,7 @@ void viewport_set_defaults(struct viewport *vp,
const enum screen_type screen)
{
vp->buffer = NULL; /* use default frame_buffer */
-
+ vp->flags = VP_DEFAULT_FLAGS;
#if !defined(__PCTOOL__)
struct viewport *sbs_area = NULL;
if (!is_theme_enabled(screen))
@@ -348,41 +348,3 @@ void viewport_set_defaults(struct viewport *vp,
#endif /* !__PCTOOL__ */
viewport_set_fullscreen(vp, screen);
}
-
-
-int get_viewport_default_colour(enum screen_type screen, bool fgcolour)
-{
- (void)screen; (void)fgcolour;
-#if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1)
- int colour;
- if (fgcolour)
- {
-#if (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1)
- if (screen == SCREEN_REMOTE)
- colour = REMOTE_FG_FALLBACK;
- else
-#endif
-#if defined(HAVE_LCD_COLOR)
- colour = global_settings.fg_color;
-#else
- colour = FG_FALLBACK;
-#endif
- }
- else
- {
-#if (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1)
- if (screen == SCREEN_REMOTE)
- colour = REMOTE_BG_FALLBACK;
- else
-#endif
-#if defined(HAVE_LCD_COLOR)
- colour = global_settings.bg_color;
-#else
- colour = BG_FALLBACK;
-#endif
- }
- return colour;
-#else
- return 0;
-#endif /* LCD_DEPTH > 1 || LCD_REMOTE_DEPTH > 1 */
-}
diff --git a/apps/gui/viewport.h b/apps/gui/viewport.h
index 2810be2ac3..c57a58b232 100644
--- a/apps/gui/viewport.h
+++ b/apps/gui/viewport.h
@@ -43,7 +43,6 @@ void viewport_set_defaults(struct viewport *vp,
const enum screen_type screen);
void viewport_set_fullscreen(struct viewport *vp,
const enum screen_type screen);
-int get_viewport_default_colour(enum screen_type screen, bool fgcolour);
#ifndef __PCTOOL__
diff --git a/apps/gui/wps.c b/apps/gui/wps.c
index b42e386a91..260730c4a1 100644
--- a/apps/gui/wps.c
+++ b/apps/gui/wps.c
@@ -53,6 +53,7 @@
#include "root_menu.h"
#include "backdrop.h"
#include "quickscreen.h"
+#include "shortcuts.h"
#include "pitchscreen.h"
#include "appevents.h"
#include "viewport.h"
@@ -61,14 +62,15 @@
#include "playlist_viewer.h"
#include "wps.h"
#include "statusbar-skinned.h"
-
-#define RESTORE_WPS_INSTANTLY 0l
-#define RESTORE_WPS_NEXT_SECOND ((long)(HZ+current_tick))
+#include "skin_engine/wps_internals.h"
+#include "open_plugin.h"
#define FF_REWIND_MAX_PERCENT 3 /* cap ff/rewind step size at max % of file */
/* 3% of 30min file == 54s step size */
#define MIN_FF_REWIND_STEP 500
+static struct wps_state wps_state;
+
/* initial setup of wps_data */
static void wps_state_init(void);
static void track_info_callback(unsigned short id, void *param);
@@ -114,7 +116,7 @@ static void update_non_static(void)
skin_update(WPS, i, SKIN_REFRESH_NON_STATIC);
}
-void pause_action(bool may_fade, bool updatewps)
+void pause_action(bool updatewps)
{
/* Do audio first, then update, unless skin were to use its local
status in which case, reverse it */
@@ -131,11 +133,9 @@ void pause_action(bool may_fade, bool updatewps)
- global_settings.pause_rewind * 1000;
audio_ff_rewind(newpos > 0 ? newpos : 0);
}
-
- (void)may_fade;
}
-void unpause_action(bool may_fade, bool updatewps)
+void unpause_action(bool updatewps)
{
/* Do audio first, then update, unless skin were to use its local
status in which case, reverse it */
@@ -143,24 +143,15 @@ void unpause_action(bool may_fade, bool updatewps)
if (updatewps)
update_non_static();
-
- (void)may_fade;
-}
-
-static bool update_onvol_change(enum screen_type screen)
-{
- skin_update(WPS, screen, SKIN_REFRESH_NON_STATIC);
-
- return false;
}
-
#ifdef HAVE_TOUCHSCREEN
-static int skintouch_to_wps(struct wps_data *data)
+static int skintouch_to_wps(void)
{
int offset = 0;
- struct touchregion *region;
- int button = skin_get_touchaction(data, &offset, &region);
+ struct wps_state *gstate = get_wps_state();
+ struct gui_wps *gwps = skin_get_gwps(WPS, SCREEN_MAIN);
+ int button = skin_get_touchaction(gwps, &offset);
switch (button)
{
case ACTION_STD_PREV:
@@ -182,16 +173,18 @@ static int skintouch_to_wps(struct wps_data *data)
return ACTION_WPS_HOTKEY;
#endif
case ACTION_TOUCH_SCROLLBAR:
- skin_get_global_state()->id3->elapsed = skin_get_global_state()->id3->length*offset/100;
+ gstate->id3->elapsed = gstate->id3->length*offset/1000;
audio_pre_ff_rewind();
- audio_ff_rewind(skin_get_global_state()->id3->elapsed);
+ audio_ff_rewind(gstate->id3->elapsed);
return ACTION_TOUCHSCREEN;
case ACTION_TOUCH_VOLUME:
{
const int min_vol = sound_min(SOUND_VOLUME);
const int max_vol = sound_max(SOUND_VOLUME);
- global_settings.volume = (offset * (max_vol - min_vol)) / 100;
- global_settings.volume += min_vol;
+ const int step_vol = sound_steps(SOUND_VOLUME);
+
+ global_settings.volume = from_normalized_volume(offset, min_vol, max_vol, 1000);
+ global_settings.volume -= (global_settings.volume % step_vol);
setvol();
}
return ACTION_TOUCHSCREEN;
@@ -200,7 +193,7 @@ static int skintouch_to_wps(struct wps_data *data)
}
#endif /* HAVE_TOUCHSCREEN */
-bool ffwd_rew(int button)
+static bool ffwd_rew(int button, bool seek_from_end)
{
unsigned int step = 0; /* current ff/rewind step */
unsigned int max_step = 0; /* maximum ff/rewind step */
@@ -208,7 +201,10 @@ bool ffwd_rew(int button)
int direction = -1; /* forward=1 or backward=-1 */
bool exit = false;
bool usb = false;
+ bool ff_rewind = false;
const long ff_rw_accel = (global_settings.ff_rewind_accel + 3);
+ struct wps_state *gstate = get_wps_state();
+ struct mp3entry *old_id3 = gstate->id3;
if (button == ACTION_NONE)
{
@@ -217,26 +213,37 @@ bool ffwd_rew(int button)
}
while (!exit)
{
+ struct mp3entry *id3 = gstate->id3;
+ if (id3 != old_id3)
+ {
+ ff_rewind = false;
+ ff_rewind_count = 0;
+ old_id3 = id3;
+ }
+ if (id3 && seek_from_end)
+ id3->elapsed = id3->length;
+
switch ( button )
{
case ACTION_WPS_SEEKFWD:
direction = 1;
+ /* Fallthrough */
case ACTION_WPS_SEEKBACK:
- if (skin_get_global_state()->ff_rewind)
+ if (ff_rewind)
{
if (direction == 1)
{
/* fast forwarding, calc max step relative to end */
- max_step = (skin_get_global_state()->id3->length -
- (skin_get_global_state()->id3->elapsed +
+ max_step = (id3->length -
+ (id3->elapsed +
ff_rewind_count)) *
FF_REWIND_MAX_PERCENT / 100;
}
else
{
/* rewinding, calc max step relative to start */
- max_step = (skin_get_global_state()->id3->elapsed + ff_rewind_count) *
- FF_REWIND_MAX_PERCENT / 100;
+ max_step = (id3->elapsed + ff_rewind_count) *
+ FF_REWIND_MAX_PERCENT / 100;
}
max_step = MAX(max_step, MIN_FF_REWIND_STEP);
@@ -251,8 +258,7 @@ bool ffwd_rew(int button)
}
else
{
- if ( (audio_status() & AUDIO_STATUS_PLAY) &&
- skin_get_global_state()->id3 && skin_get_global_state()->id3->length )
+ if ((audio_status() & AUDIO_STATUS_PLAY) && id3 && id3->length )
{
audio_pre_ff_rewind();
if (direction > 0)
@@ -260,7 +266,7 @@ bool ffwd_rew(int button)
else
status_set_ffmode(STATUS_FASTBACKWARD);
- skin_get_global_state()->ff_rewind = true;
+ ff_rewind = true;
step = 1000 * global_settings.ff_rewind_min_step;
}
@@ -269,19 +275,17 @@ bool ffwd_rew(int button)
}
if (direction > 0) {
- if ((skin_get_global_state()->id3->elapsed + ff_rewind_count) >
- skin_get_global_state()->id3->length)
- ff_rewind_count = skin_get_global_state()->id3->length -
- skin_get_global_state()->id3->elapsed;
+ if ((id3->elapsed + ff_rewind_count) > id3->length)
+ ff_rewind_count = id3->length - id3->elapsed;
}
else {
- if ((int)(skin_get_global_state()->id3->elapsed + ff_rewind_count) < 0)
- ff_rewind_count = -skin_get_global_state()->id3->elapsed;
+ if ((int)(id3->elapsed + ff_rewind_count) < 0)
+ ff_rewind_count = -id3->elapsed;
}
/* set the wps state ff_rewind_count so the progess info
displays corectly */
- skin_get_global_state()->ff_rewind_count = ff_rewind_count;
+ gstate->ff_rewind_count = ff_rewind_count;
FOR_NB_SCREENS(i)
{
@@ -293,10 +297,10 @@ bool ffwd_rew(int button)
break;
case ACTION_WPS_STOPSEEK:
- skin_get_global_state()->id3->elapsed = skin_get_global_state()->id3->elapsed+ff_rewind_count;
- audio_ff_rewind(skin_get_global_state()->id3->elapsed);
- skin_get_global_state()->ff_rewind_count = 0;
- skin_get_global_state()->ff_rewind = false;
+ id3->elapsed = id3->elapsed + ff_rewind_count;
+ audio_ff_rewind(id3->elapsed);
+ gstate->ff_rewind_count = 0;
+ ff_rewind = false;
status_set_ffmode(0);
exit = true;
break;
@@ -314,20 +318,21 @@ bool ffwd_rew(int button)
button = get_action(CONTEXT_WPS|ALLOW_SOFTLOCK,TIMEOUT_BLOCK);
#ifdef HAVE_TOUCHSCREEN
if (button == ACTION_TOUCHSCREEN)
- button = skintouch_to_wps(skin_get_gwps(WPS, SCREEN_MAIN)->data);
+ button = skintouch_to_wps();
#endif
- if (button != ACTION_WPS_SEEKFWD &&
- button != ACTION_WPS_SEEKBACK)
+ if (button != ACTION_WPS_SEEKFWD
+ && button != ACTION_WPS_SEEKBACK
+ && button != 0 && !IS_SYSEVENT(button))
button = ACTION_WPS_STOPSEEK;
}
}
return usb;
}
-#if defined(HAVE_BACKLIGHT) || defined(HAVE_REMOTE_LCD)
static void gwps_caption_backlight(struct wps_state *state)
{
- if (state && state->id3)
+#if defined(HAVE_BACKLIGHT) || defined(HAVE_REMOTE_LCD)
+ if (state->id3)
{
#ifdef HAVE_BACKLIGHT
if (global_settings.caption_backlight)
@@ -363,9 +368,10 @@ static void gwps_caption_backlight(struct wps_state *state)
}
#endif
}
+#else
+ (void) state;
+#endif /* def HAVE_BACKLIGHT || def HAVE_REMOTE_LCD */
}
-#endif
-
static void change_dir(int direction)
{
@@ -382,7 +388,7 @@ static void change_dir(int direction)
static void prev_track(unsigned long skip_thresh)
{
- struct wps_state *state = skin_get_global_state();
+ struct wps_state *state = get_wps_state();
if (state->id3->elapsed < skip_thresh)
{
audio_prev();
@@ -403,7 +409,7 @@ static void prev_track(unsigned long skip_thresh)
static void next_track(void)
{
- struct wps_state *state = skin_get_global_state();
+ struct wps_state *state = get_wps_state();
/* take care of if we're playing a cuesheet */
if (state->id3->cuesheet)
{
@@ -420,7 +426,7 @@ static void next_track(void)
static void play_hop(int direction)
{
- struct wps_state *state = skin_get_global_state();
+ struct wps_state *state = get_wps_state();
struct cuesheet *cue = state->id3->cuesheet;
long step = global_settings.skip_length*1000;
long elapsed = state->id3->elapsed;
@@ -439,26 +445,40 @@ static void play_hop(int direction)
{
if (direction < 0)
{
- prev_track(DEFAULT_SKIP_TRESH);
+ prev_track(DEFAULT_SKIP_THRESH);
return;
}
- else if (remaining < DEFAULT_SKIP_TRESH*2)
+ else if (remaining < DEFAULT_SKIP_THRESH*2)
{
next_track();
return;
}
else
- elapsed += (remaining - DEFAULT_SKIP_TRESH*2);
+ elapsed += (remaining - DEFAULT_SKIP_THRESH*2);
}
else if (!global_settings.prevent_skip &&
(!step ||
(direction > 0 && step >= remaining) ||
- (direction < 0 && elapsed < DEFAULT_SKIP_TRESH)))
+ (direction < 0 && elapsed < DEFAULT_SKIP_THRESH)))
{ /* Do normal track skipping */
if (direction > 0)
next_track();
else if (direction < 0)
- prev_track(DEFAULT_SKIP_TRESH);
+ {
+ if (step > 0 && global_settings.rewind_across_tracks && elapsed < DEFAULT_SKIP_THRESH && playlist_check(-1))
+ {
+ bool audio_paused = (audio_status() & AUDIO_STATUS_PAUSE)?true:false;
+ if (!audio_paused)
+ audio_pause();
+ audio_prev();
+ audio_ff_rewind(-step);
+ if (!audio_paused)
+ audio_resume();
+ return;
+ }
+
+ prev_track(DEFAULT_SKIP_THRESH);
+ }
return;
}
else if (direction == 1 && step >= remaining)
@@ -482,7 +502,6 @@ static void play_hop(int direction)
audio_ff_rewind(elapsed);
}
-
#if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
/*
* If the user is unable to see the wps, because the display is deactivated,
@@ -499,16 +518,19 @@ static void wps_lcd_activation_hook(unsigned short id, void *param)
}
#endif
-static void gwps_leave_wps(void)
+static void gwps_leave_wps(bool theme_enabled)
{
FOR_NB_SCREENS(i)
{
- skin_get_gwps(WPS, i)->display->scroll_stop();
+ struct gui_wps *gwps = skin_get_gwps(WPS, i);
+ gwps->display->scroll_stop();
+ if (theme_enabled)
+ {
#ifdef HAVE_BACKDROP_IMAGE
- skin_backdrop_show(sb_get_backdrop(i));
+ skin_backdrop_show(sb_get_backdrop(i));
#endif
- viewportmanager_theme_undo(i, skin_has_sbs(i, skin_get_gwps(WPS, i)->data));
-
+ viewportmanager_theme_undo(i, skin_has_sbs(gwps));
+ }
}
#if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
@@ -522,19 +544,30 @@ static void gwps_leave_wps(void)
#endif
}
+static void restore_theme(void)
+{
+ FOR_NB_SCREENS(i)
+ {
+ struct gui_wps *gwps = skin_get_gwps(WPS, i);
+ struct screen *display = gwps->display;
+ display->scroll_stop();
+ viewportmanager_theme_enable(i, skin_has_sbs(gwps), NULL);
+ }
+}
+
/*
* display the wps on entering or restoring */
-static void gwps_enter_wps(void)
+static void gwps_enter_wps(bool theme_enabled)
{
struct gui_wps *gwps;
struct screen *display;
+ if (theme_enabled)
+ restore_theme();
FOR_NB_SCREENS(i)
{
gwps = skin_get_gwps(WPS, i);
display = gwps->display;
display->scroll_stop();
- viewportmanager_theme_enable(i, skin_has_sbs(i, skin_get_gwps(WPS, i)->data), NULL);
-
/* Update the values in the first (default) viewport - in case the user
has modified the statusbar or colour settings */
#if LCD_DEPTH > 1
@@ -560,7 +593,7 @@ static void gwps_enter_wps(void)
}
#ifdef HAVE_TOUCHSCREEN
gwps = skin_get_gwps(WPS, SCREEN_MAIN);
- skin_disarm_touchregions(gwps->data);
+ skin_disarm_touchregions(gwps);
if (gwps->data->touchregions < 0)
touchscreen_set_mode(TOUCHSCREEN_BUTTON);
#endif
@@ -570,23 +603,74 @@ static void gwps_enter_wps(void)
void wps_do_playpause(bool updatewps)
{
- struct wps_state *state = skin_get_global_state();
+ struct wps_state *state = get_wps_state();
if ( state->paused )
{
state->paused = false;
- unpause_action(true, updatewps);
+ unpause_action(updatewps);
}
else
{
state->paused = true;
- pause_action(true, updatewps);
+ pause_action(updatewps);
settings_save();
-#if !defined(HAVE_RTC_RAM) && !defined(HAVE_SW_POWEROFF)
+#if !defined(HAVE_SW_POWEROFF)
call_storage_idle_notifys(true); /* make sure resume info is saved */
#endif
}
}
+static long do_wps_exit(long action, bool bookmark)
+{
+ audio_pause();
+ update_non_static();
+ if (bookmark)
+ bookmark_autobookmark(true);
+ audio_stop();
+
+ ab_reset_markers();
+
+ gwps_leave_wps(true);
+#ifdef HAVE_RECORDING
+ if (action == ACTION_WPS_REC)
+ return GO_TO_RECSCREEN;
+#else
+ (void)action;
+#endif
+ if (global_settings.browse_current)
+ return GO_TO_PREVIOUS_BROWSER;
+ return GO_TO_PREVIOUS;
+}
+
+static long do_party_mode(long action)
+{
+ if (global_settings.party_mode)
+ {
+ switch (action)
+ {
+#ifdef ACTION_WPSAB_SINGLE
+ case ACTION_WPSAB_SINGLE:
+ if (!ab_repeat_mode_enabled())
+ break;
+ /* Note: currently all targets use ACTION_WPS_BROWSE
+ * if mapped to any of below actions this will cause problems */
+#endif
+ case ACTION_WPS_PLAY:
+ case ACTION_WPS_SEEKFWD:
+ case ACTION_WPS_SEEKBACK:
+ case ACTION_WPS_SKIPPREV:
+ case ACTION_WPS_SKIPNEXT:
+ case ACTION_WPS_ABSETB_NEXTDIR:
+ case ACTION_WPS_ABSETA_PREVDIR:
+ case ACTION_WPS_STOP:
+ return ACTION_NONE;
+ break;
+ default:
+ break;
+ }
+ }
+ return action;
+}
/* The WPS can be left in two ways:
* a) call a function, which draws over the wps. In this case, the wps
@@ -594,31 +678,28 @@ void wps_do_playpause(bool updatewps)
* b) return with a value evaluated by root_menu.c, in this case the wps
* is really left, and root_menu will handle the next screen
*
- * In either way, call gwps_leave_wps(), in order to restore the correct
+ * In either way, call gwps_leave_wps(true), in order to restore the correct
* "main screen" backdrops and statusbars
*/
long gui_wps_show(void)
{
long button = 0;
bool restore = true;
- long restoretimer = RESTORE_WPS_INSTANTLY; /* timer to delay screen redraw temporarily */
bool exit = false;
bool bookmark = false;
bool update = false;
- bool vol_changed = false;
+ bool theme_enabled = true;
long last_left = 0, last_right = 0;
- struct wps_state *state = skin_get_global_state();
+ struct wps_state *state = get_wps_state();
-#ifdef AB_REPEAT_ENABLE
ab_repeat_init();
ab_reset_markers();
-#endif
- wps_state_init();
+ wps_state_init();
while ( 1 )
{
+ bool hotkey = false;
bool audio_paused = (audio_status() & AUDIO_STATUS_PAUSE)?true:false;
-
/* did someone else (i.e power thread) change audio pause mode? */
if (state->paused != audio_paused) {
state->paused = audio_paused;
@@ -627,13 +708,58 @@ long gui_wps_show(void)
about to shut down. lets save the settings. */
if (state->paused) {
settings_save();
-#if !defined(HAVE_RTC_RAM) && !defined(HAVE_SW_POWEROFF)
+#if !defined(HAVE_SW_POWEROFF)
call_storage_idle_notifys(true);
#endif
}
}
- button = skin_wait_for_action(WPS, CONTEXT_WPS|ALLOW_SOFTLOCK,
- restore ? 1 : HZ/5);
+
+ if (restore)
+ {
+ restore = false;
+#if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
+ add_event(LCD_EVENT_ACTIVATION, wps_lcd_activation_hook);
+#endif
+ /* we remove the update delay since it's not very usable in the wps,
+ * e.g. during volume changing or ffwd/rewind */
+ sb_skin_set_update_delay(0);
+ skin_request_full_update(WPS);
+ update = true;
+ gwps_enter_wps(theme_enabled);
+ theme_enabled = true;
+ }
+ else
+ {
+ gwps_caption_backlight(state);
+
+ FOR_NB_SCREENS(i)
+ {
+#if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
+ /* currently, all remotes are readable without backlight
+ * so still update those */
+ if (lcd_active() || (i != SCREEN_MAIN))
+#endif
+ {
+ bool full_update = skin_do_full_update(WPS, i);
+ if (update || full_update)
+ {
+ skin_update(WPS, i, full_update ?
+ SKIN_REFRESH_ALL : SKIN_REFRESH_NON_STATIC);
+ }
+ }
+ }
+ update = false;
+ }
+
+ if (exit)
+ {
+ return do_wps_exit(button, bookmark);
+ }
+
+ if (button && !IS_SYSEVENT(button) )
+ storage_spin();
+
+ button = skin_wait_for_action(WPS, CONTEXT_WPS|ALLOW_SOFTLOCK, HZ/5);
/* Exit if audio has stopped playing. This happens e.g. at end of
playlist or if using the sleep timer. */
@@ -641,46 +767,64 @@ long gui_wps_show(void)
exit = true;
#ifdef HAVE_TOUCHSCREEN
if (button == ACTION_TOUCHSCREEN)
- button = skintouch_to_wps(skin_get_gwps(WPS, SCREEN_MAIN)->data);
+ button = skintouch_to_wps();
#endif
+ button = do_party_mode(button); /* block select actions in party mode */
+
/* The iPods/X5/M5 use a single button for the A-B mode markers,
defined as ACTION_WPSAB_SINGLE in their config files. */
#ifdef ACTION_WPSAB_SINGLE
- if (!global_settings.party_mode && ab_repeat_mode_enabled())
+ static int wps_ab_state = 0;
+ if (button == ACTION_WPSAB_SINGLE && ab_repeat_mode_enabled())
{
- static int wps_ab_state = 0;
- if (button == ACTION_WPSAB_SINGLE)
+ switch (wps_ab_state)
{
- switch (wps_ab_state)
- {
- case 0: /* set the A spot */
- button = ACTION_WPS_ABSETA_PREVDIR;
- break;
- case 1: /* set the B spot */
- button = ACTION_WPS_ABSETB_NEXTDIR;
- break;
- case 2:
- button = ACTION_WPS_ABRESET;
- break;
- }
- wps_ab_state = (wps_ab_state+1) % 3;
+ case 0: /* set the A spot */
+ button = ACTION_WPS_ABSETA_PREVDIR;
+ break;
+ case 1: /* set the B spot */
+ button = ACTION_WPS_ABSETB_NEXTDIR;
+ break;
+ case 2:
+ button = ACTION_WPS_ABRESET;
+ break;
}
+ wps_ab_state = (wps_ab_state+1) % 3;
}
-#endif
+#endif /* def ACTION_WPSAB_SINGLE */
+
switch(button)
{
#ifdef HAVE_HOTKEY
case ACTION_WPS_HOTKEY:
+ {
+ hotkey = true;
if (!global_settings.hotkey_wps)
break;
- /* fall through */
-#endif
+ if (get_hotkey(global_settings.hotkey_wps)->flags & HOTKEY_FLAG_NOSBS)
+ {
+ /* leave WPS without re-enabling theme */
+ theme_enabled = false;
+ gwps_leave_wps(theme_enabled);
+ onplay(state->id3->path,
+ FILE_ATTR_AUDIO, CONTEXT_WPS, hotkey);
+ if (!audio_status())
+ {
+ /* re-enable theme since we're returning to SBS */
+ gwps_leave_wps(true);
+ return GO_TO_ROOT;
+ }
+ restore = true;
+ break;
+ }
+ }
+ /* fall through */
+#endif /* def HAVE_HOTKEY */
case ACTION_WPS_CONTEXT:
{
- bool hotkey = button == ACTION_WPS_HOTKEY;
- gwps_leave_wps();
+ gwps_leave_wps(true);
int retval = onplay(state->id3->path,
- FILE_ATTR_AUDIO, CONTEXT_WPS, hotkey);
+ FILE_ATTR_AUDIO, CONTEXT_WPS, hotkey);
/* if music is stopped in the context menu we want to exit the wps */
if (retval == ONPLAY_MAINMENU
|| !audio_status())
@@ -688,39 +832,46 @@ long gui_wps_show(void)
else if (retval == ONPLAY_PLAYLIST)
return GO_TO_PLAYLIST_VIEWER;
else if (retval == ONPLAY_PLUGIN)
- return GO_TO_PLUGIN;
+ {
+ restore_theme();
+ theme_enabled = false;
+ open_plugin_run(ID2P(LANG_OPEN_PLUGIN_SET_WPS_CONTEXT_PLUGIN));
+ }
+
restore = true;
}
break;
case ACTION_WPS_BROWSE:
- gwps_leave_wps();
+ gwps_leave_wps(true);
return GO_TO_PREVIOUS_BROWSER;
break;
/* play/pause */
case ACTION_WPS_PLAY:
- if (global_settings.party_mode)
- break;
wps_do_playpause(true);
break;
- case ACTION_WPS_VOLUP:
- global_settings.volume += sound_steps(SOUND_VOLUME);
- vol_changed = true;
- break;
+ case ACTION_WPS_VOLUP: /* fall through */
case ACTION_WPS_VOLDOWN:
- global_settings.volume -= sound_steps(SOUND_VOLUME);
- vol_changed = true;
+ if (button == ACTION_WPS_VOLUP)
+ adjust_volume(1);
+ else
+ adjust_volume(-1);
+
+ setvol();
+ FOR_NB_SCREENS(i)
+ {
+ skin_update(WPS, i, SKIN_REFRESH_NON_STATIC);
+ }
+ update = false;
break;
/* fast forward
OR next dir if this is straight after ACTION_WPS_SKIPNEXT */
case ACTION_WPS_SEEKFWD:
- if (global_settings.party_mode)
- break;
if (current_tick -last_right < HZ)
{
- if (state->id3->cuesheet)
+ if (state->id3->cuesheet && playlist_check(1))
{
audio_next();
}
@@ -730,37 +881,42 @@ long gui_wps_show(void)
}
}
else
- ffwd_rew(ACTION_WPS_SEEKFWD);
+ ffwd_rew(ACTION_WPS_SEEKFWD, false);
last_right = last_left = 0;
break;
/* fast rewind
OR prev dir if this is straight after ACTION_WPS_SKIPPREV,*/
case ACTION_WPS_SEEKBACK:
- if (global_settings.party_mode)
- break;
- if (current_tick -last_left < HZ)
+ if (current_tick - last_left < HZ)
{
- if (state->id3->cuesheet)
+ if (state->id3->cuesheet && playlist_check(-1))
{
- audio_pre_ff_rewind();
- audio_ff_rewind(0);
+ audio_prev();
}
else
{
change_dir(-1);
}
+ } else if (global_settings.rewind_across_tracks
+ && get_wps_state()->id3->elapsed < DEFAULT_SKIP_THRESH
+ && playlist_check(-1))
+ {
+ if (!audio_paused)
+ audio_pause();
+ audio_prev();
+ ffwd_rew(ACTION_WPS_SEEKBACK, true);
+ if (!audio_paused)
+ audio_resume();
}
else
- ffwd_rew(ACTION_WPS_SEEKBACK);
+ ffwd_rew(ACTION_WPS_SEEKBACK, false);
last_left = last_right = 0;
break;
/* prev / restart */
case ACTION_WPS_SKIPPREV:
- if (global_settings.party_mode)
- break;
last_left = current_tick;
-#ifdef AB_REPEAT_ENABLE
+
/* if we're in A/B repeat mode and the current position
is past the A marker, jump back to the A marker... */
if ( ab_repeat_mode_enabled() && ab_after_A_marker(state->id3->elapsed) )
@@ -768,19 +924,15 @@ long gui_wps_show(void)
ab_jump_to_A_marker();
break;
}
- else
- /* ...otherwise, do it normally */
-#endif
+ else /* ...otherwise, do it normally */
play_hop(-1);
break;
/* next
OR if skip length set, hop by predetermined amount. */
case ACTION_WPS_SKIPNEXT:
- if (global_settings.party_mode)
- break;
last_right = current_tick;
-#ifdef AB_REPEAT_ENABLE
+
/* if we're in A/B repeat mode and the current position is
before the A marker, jump to the A marker... */
if ( ab_repeat_mode_enabled() )
@@ -791,43 +943,33 @@ long gui_wps_show(void)
break;
}
}
- else
- /* ...otherwise, do it normally */
-#endif
+ else /* ...otherwise, do it normally */
play_hop(1);
break;
/* next / prev directories */
/* and set A-B markers if in a-b mode */
case ACTION_WPS_ABSETB_NEXTDIR:
- if (global_settings.party_mode)
- break;
-#if defined(AB_REPEAT_ENABLE)
if (ab_repeat_mode_enabled())
{
ab_set_B_marker(state->id3->elapsed);
ab_jump_to_A_marker();
}
else
-#endif
{
change_dir(1);
}
break;
case ACTION_WPS_ABSETA_PREVDIR:
- if (global_settings.party_mode)
- break;
-#if defined(AB_REPEAT_ENABLE)
if (ab_repeat_mode_enabled())
ab_set_A_marker(state->id3->elapsed);
else
-#endif
{
change_dir(-1);
}
break;
/* menu key functions */
case ACTION_WPS_MENU:
- gwps_leave_wps();
+ gwps_leave_wps(true);
return GO_TO_ROOT;
break;
@@ -835,16 +977,25 @@ long gui_wps_show(void)
#ifdef HAVE_QUICKSCREEN
case ACTION_WPS_QUICKSCREEN:
{
- gwps_leave_wps();
- if (global_settings.shortcuts_replaces_qs)
+ gwps_leave_wps(true);
+ bool enter_shortcuts_menu = global_settings.shortcuts_replaces_qs;
+ if (!enter_shortcuts_menu)
{
- global_status.last_screen = GO_TO_SHORTCUTMENU;
int ret = quick_screen_quick(button);
+ if (ret == QUICKSCREEN_IN_USB)
+ return GO_TO_ROOT;
+ else if (ret == QUICKSCREEN_GOTO_SHORTCUTS_MENU)
+ enter_shortcuts_menu = true;
+ else
+ restore = true;
+ }
+
+ if (enter_shortcuts_menu) /* enter_shortcuts_menu */
+ {
+ global_status.last_screen = GO_TO_SHORTCUTMENU;
+ int ret = do_shortcut_menu(NULL);
return (ret == GO_TO_PREVIOUS ? GO_TO_WPS : ret);
}
- else if (quick_screen_quick(button) > 0)
- return GO_TO_ROOT;
- restore = true;
}
break;
#endif /* HAVE_QUICKSCREEN */
@@ -855,7 +1006,7 @@ long gui_wps_show(void)
#ifdef HAVE_PITCHCONTROL
case ACTION_WPS_PITCHSCREEN:
{
- gwps_leave_wps();
+ gwps_leave_wps(true);
if (1 == gui_syncpitchscreen_run())
return GO_TO_ROOT;
restore = true;
@@ -863,7 +1014,6 @@ long gui_wps_show(void)
break;
#endif /* HAVE_PITCHCONTROL */
-#ifdef AB_REPEAT_ENABLE
/* reset A&B markers */
case ACTION_WPS_ABRESET:
if (ab_repeat_mode_enabled())
@@ -872,18 +1022,15 @@ long gui_wps_show(void)
update = true;
}
break;
-#endif /* AB_REPEAT_ENABLE */
/* stop and exit wps */
case ACTION_WPS_STOP:
- if (global_settings.party_mode)
- break;
bookmark = true;
exit = true;
break;
case ACTION_WPS_LIST_BOOKMARKS:
- gwps_leave_wps();
+ gwps_leave_wps(true);
if (bookmark_load_menu() == BOOKMARK_USB_CONNECTED)
{
return GO_TO_ROOT;
@@ -892,15 +1039,17 @@ long gui_wps_show(void)
break;
case ACTION_WPS_CREATE_BOOKMARK:
- gwps_leave_wps();
+ gwps_leave_wps(true);
bookmark_create_menu();
restore = true;
break;
case ACTION_WPS_ID3SCREEN:
{
- gwps_leave_wps();
- if (browse_id3())
+ gwps_leave_wps(true);
+ if (browse_id3(audio_current_track(),
+ playlist_get_display_index(),
+ playlist_amount(), NULL, 1))
return GO_TO_ROOT;
restore = true;
}
@@ -912,7 +1061,7 @@ long gui_wps_show(void)
break;
case ACTION_NONE: /* Timeout, do a partial update */
update = true;
- ffwd_rew(button); /* hopefully fix the ffw/rwd bug */
+ ffwd_rew(button, false); /* hopefully fix the ffw/rwd bug */
break;
#ifdef HAVE_RECORDING
case ACTION_WPS_REC:
@@ -920,7 +1069,7 @@ long gui_wps_show(void)
break;
#endif
case ACTION_WPS_VIEW_PLAYLIST:
- gwps_leave_wps();
+ gwps_leave_wps(true);
return GO_TO_PLAYLIST_VIEWER;
break;
default:
@@ -929,99 +1078,25 @@ long gui_wps_show(void)
case SYS_USB_CONNECTED:
case SYS_CALL_INCOMING:
case BUTTON_MULTIMEDIA_STOP:
- gwps_leave_wps();
+ gwps_leave_wps(true);
return GO_TO_ROOT;
}
update = true;
break;
}
-
- if (vol_changed)
- {
- bool res = false;
- vol_changed = false;
- setvol();
- FOR_NB_SCREENS(i)
- {
- if(update_onvol_change(i))
- res = true;
- }
- if (res) {
- restore = true;
- restoretimer = RESTORE_WPS_NEXT_SECOND;
- }
- }
-
-
- if (restore &&
- ((restoretimer == RESTORE_WPS_INSTANTLY) ||
- TIME_AFTER(current_tick, restoretimer)))
- {
- restore = false;
- restoretimer = RESTORE_WPS_INSTANTLY;
-#if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
- add_event(LCD_EVENT_ACTIVATION, wps_lcd_activation_hook);
-#endif
- /* we remove the update delay since it's not very usable in the wps,
- * e.g. during volume changing or ffwd/rewind */
- sb_skin_set_update_delay(0);
- skin_request_full_update(WPS);
- update = true;
- gwps_enter_wps();
- }
- else
- {
-#if defined(HAVE_BACKLIGHT) || defined(HAVE_REMOTE_LCD)
- gwps_caption_backlight(state);
-#endif
- FOR_NB_SCREENS(i)
- {
-#if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
- /* currently, all remotes are readable without backlight
- * so still update those */
- if (lcd_active() || (i != SCREEN_MAIN))
-#endif
- {
- bool full_update = skin_do_full_update(WPS, i);
- if (update || full_update)
- {
- skin_update(WPS, i, full_update ?
- SKIN_REFRESH_ALL : SKIN_REFRESH_NON_STATIC);
- }
- }
- }
- update = false;
- }
-
- if (exit) {
- audio_pause();
- update_non_static();
- if (bookmark)
- bookmark_autobookmark(true);
- audio_stop();
-#ifdef AB_REPEAT_ENABLE
- ab_reset_markers();
-#endif
- gwps_leave_wps();
-#ifdef HAVE_RECORDING
- if (button == ACTION_WPS_REC)
- return GO_TO_RECSCREEN;
-#endif
- if (global_settings.browse_current)
- return GO_TO_PREVIOUS_BROWSER;
- return GO_TO_PREVIOUS;
- }
-
- if (button && !IS_SYSEVENT(button) )
- storage_spin();
}
return GO_TO_ROOT; /* unreachable - just to reduce compiler warnings */
}
+struct wps_state *get_wps_state(void)
+{
+ return &wps_state;
+}
+
/* this is called from the playback thread so NO DRAWING! */
static void track_info_callback(unsigned short id, void *param)
{
- struct wps_state *state = skin_get_global_state();
+ struct wps_state *state = get_wps_state();
if (id == PLAYBACK_EVENT_TRACK_CHANGE || id == PLAYBACK_EVENT_CUR_TRACK_READY)
{
@@ -1037,14 +1112,13 @@ static void track_info_callback(unsigned short id, void *param)
state->id3 = audio_current_track();
}
#endif
- skin_get_global_state()->nid3 = audio_next_track();
+ state->nid3 = audio_next_track();
skin_request_full_update(WPS);
}
static void wps_state_init(void)
{
- struct wps_state *state = skin_get_global_state();
- state->ff_rewind = false;
+ struct wps_state *state = get_wps_state();
state->paused = false;
if(audio_status() & AUDIO_STATUS_PLAY)
{
@@ -1068,16 +1142,3 @@ static void wps_state_init(void)
add_event(PLAYBACK_EVENT_TRACK_SKIP, track_info_callback);
#endif
}
-
-
-#ifdef IPOD_ACCESSORY_PROTOCOL
-bool is_wps_fading(void)
-{
- return skin_get_global_state()->is_fading;
-}
-
-int wps_get_ff_rewind_count(void)
-{
- return skin_get_global_state()->ff_rewind_count;
-}
-#endif
diff --git a/apps/gui/wps.h b/apps/gui/wps.h
index a463b0e9bb..001c112a4d 100644
--- a/apps/gui/wps.h
+++ b/apps/gui/wps.h
@@ -20,35 +20,30 @@
****************************************************************************/
#ifndef _WPS_H_
#define _WPS_H_
+
#include <stdbool.h>
-#include "config.h"
-#include "screen_access.h"
-
-long gui_wps_show(void);
-
-/* wrapper for the wps to load the skin (.wps/.rwps) files */
-void wps_data_load(enum screen_type, const char *, bool);
-void gui_sync_wps_init(void) INIT_ATTR;
+struct mp3entry;
-/* fade (if enabled) and pause the audio, optionally rewind a little */
-void pause_action(bool may_fade, bool updatewps);
-void unpause_action(bool may_fade, bool updatewps);
+/* Please don't add anything else to here... */
+struct wps_state
+{
+ struct mp3entry *id3;
+ struct mp3entry *nid3;
+ int ff_rewind_count;
+ bool paused;
+};
-/* fades the volume, e.g. on pause or stop */
-void fade(bool fade_in, bool updatewps);
+long gui_wps_show(void);
-bool ffwd_rew(int button);
+/* fade (if enabled) and pause the audio, optionally rewind a little */
+void pause_action(bool updatewps);
+void unpause_action(bool updatewps);
void wps_do_playpause(bool updatewps);
-#ifdef IPOD_ACCESSORY_PROTOCOL
-/* whether the wps is fading the volume due to pausing/stopping */
-bool is_wps_fading(void);
-/* return length of the current ff or rewin action, IAP needs this */
-int wps_get_ff_rewind_count(void);
-#endif /* IPOD_ACCESSORY_PROTOCOL */
+struct wps_state *get_wps_state(void);
/* in milliseconds */
-#define DEFAULT_SKIP_TRESH 3000l
+#define DEFAULT_SKIP_THRESH 3000l
#endif /* _WPS_H_ */
diff --git a/apps/gui/yesno.c b/apps/gui/yesno.c
index 669f8ca09a..e3b22115df 100644
--- a/apps/gui/yesno.c
+++ b/apps/gui/yesno.c
@@ -28,30 +28,31 @@
#include "talk.h"
#include "settings.h"
#include "viewport.h"
-
+#include "appevents.h"
+#include "splash.h"
+#include "backlight.h"
struct gui_yesno
{
+ struct viewport vp;
const struct text_message * main_message;
- const struct text_message * result_message[2];
-
- struct viewport *vp;
struct screen * display;
+ int vp_lines;
+ /* timeout data */
+ long end_tick;
+ enum yesno_res tmo_default_res;
};
static void talk_text_message(const struct text_message * message, bool enqueue)
{
int line;
- if(message)
+ for(line=0; line < message->nb_lines; line++)
{
- for(line=0; line<message->nb_lines; line++)
+ long id = P2ID((unsigned char *)message->message_lines[line]);
+ if(id>=0)
{
- long id = P2ID((unsigned char *)message->message_lines[line]);
- if(id>=0)
- {
- talk_id(id, enqueue);
- enqueue = true;
- }
+ talk_id(id, enqueue);
+ enqueue = true;
}
}
}
@@ -61,7 +62,8 @@ static int put_message(struct screen *display,
int start, int max_y)
{
int i;
- for(i=0; i<message->nb_lines && i+start<max_y; i++)
+ int ct = MIN(message->nb_lines, max_y - start);
+ for(i=0; i < ct; i++)
{
display->puts_scroll(0, i+start,
P2STR((unsigned char *)message->message_lines[i]));
@@ -76,44 +78,92 @@ static int put_message(struct screen *display,
static void gui_yesno_draw(struct gui_yesno * yn)
{
struct screen * display=yn->display;
- struct viewport *vp = yn->vp;
- int nb_lines, vp_lines, line_shift=0;
- struct viewport *last_vp;
+ struct viewport *vp = &yn->vp;
+ int vp_lines = yn->vp_lines;
+ enum yesno_res def_res = yn->tmo_default_res;
+ const struct text_message *main_message = yn->main_message;
+ int line_shift = 0;
+ struct viewport *last_vp = display->set_viewport_ex(vp, VP_FLAG_VP_SET_CLEAN);
- last_vp = display->set_viewport(vp);
- display->clear_viewport();
- nb_lines = yn->main_message->nb_lines;
- vp_lines = viewport_get_nb_lines(vp);
+ /* do our own clear to avoid stopping scrolling */
+ int oldmode = vp->drawmode;
+ vp->drawmode ^= DRMODE_INVERSEVID;
+ vp->drawmode |= DRMODE_SOLID;
+ display->fillrect(0, 0, vp->width, vp->height);
+ vp->drawmode = oldmode;
- if(nb_lines+3< vp_lines)
- line_shift=1;
+ if(main_message->nb_lines + 3 < vp_lines)
+ line_shift = 1;
+
+ line_shift += put_message(display, main_message, line_shift, vp_lines);
- line_shift += put_message(display, yn->main_message,
- line_shift, vp_lines);
#ifdef HAVE_TOUCHSCREEN
if (display->screen_type == SCREEN_MAIN)
{
- int w,h;
+ int w,h,tmo_w;
+ int tm_rem = 0;
+ const char *btn_fmt;
int rect_w = vp->width/2, rect_h = vp->height/2;
int old_pattern = vp->fg_pattern;
vp->fg_pattern = LCD_RGBPACK(0,255,0);
display->drawrect(0, rect_h, rect_w, rect_h);
display->getstringsize(str(LANG_SET_BOOL_YES), &w, &h);
- display->putsxy((rect_w-w)/2, rect_h+(rect_h-h)/2, str(LANG_SET_BOOL_YES));
+
+ if (def_res == YESNO_YES)
+ {
+ display->getstringsize(" (00)", &tmo_w, NULL);
+ tm_rem = ((yn->end_tick - current_tick) / 100);
+ btn_fmt = "%s (%02d)";
+ }
+ else
+ {
+ btn_fmt = "%s\0%d";
+ tmo_w = 0;
+ }
+
+ display->putsxyf((rect_w-(w+tmo_w))/2, rect_h+(rect_h-h)/2,
+ btn_fmt, str(LANG_SET_BOOL_YES), tm_rem);
+
vp->fg_pattern = LCD_RGBPACK(255,0,0);
display->drawrect(rect_w, rect_h, rect_w, rect_h);
display->getstringsize(str(LANG_SET_BOOL_NO), &w, &h);
- display->putsxy(rect_w + (rect_w-w)/2, rect_h+(rect_h-h)/2, str(LANG_SET_BOOL_NO));
+
+ if (def_res == YESNO_NO)
+ {
+ display->getstringsize(" (00)", &tmo_w, NULL);
+ tm_rem = ((yn->end_tick - current_tick) / 100);
+ btn_fmt = "%s (%02d)";
+ }
+ else
+ {
+ btn_fmt = "%s\0%d";
+ tmo_w = 0;
+ }
+
+ display->putsxyf(rect_w + (rect_w-(w+tmo_w))/2, rect_h+(rect_h-h)/2,
+ btn_fmt, str(LANG_SET_BOOL_NO), tm_rem);
+
vp->fg_pattern = old_pattern;
}
#else
/* Space remaining for yes / no text ? */
- if(line_shift+2 <= vp_lines)
+ if(line_shift + 2 <= vp_lines)
{
- if(line_shift+3 <= vp_lines)
+ if(line_shift + 3 <= vp_lines)
line_shift++;
display->puts(0, line_shift, str(LANG_CONFIRM_WITH_BUTTON));
display->puts(0, line_shift+1, str(LANG_CANCEL_WITH_ANY));
+
+ if (def_res == YESNO_YES || def_res == YESNO_NO)
+ {
+ int tm_rem = ((yn->end_tick - current_tick) / 100);
+ if (def_res == YESNO_YES)
+ display->putsf(0, line_shift, "%s (%02d)",
+ str(LANG_CONFIRM_WITH_BUTTON), tm_rem);
+ else
+ display->putsf(0, line_shift+1, "%s (%02d)",
+ str(LANG_CANCEL_WITH_ANY), tm_rem);
+ }
}
#endif
display->update_viewport();
@@ -123,72 +173,120 @@ static void gui_yesno_draw(struct gui_yesno * yn)
/*
* Draws the yesno result
* - yn : the yesno structure
- * - result : the result tha must be displayed :
+ * - result : the result to be displayed :
* YESNO_NO if no
* YESNO_YES if yes
*/
-static bool gui_yesno_draw_result(struct gui_yesno * yn, enum yesno_res result)
+static void gui_yesno_draw_result(struct gui_yesno * yn, const struct text_message * message)
{
- const struct text_message * message=yn->result_message[result];
- struct viewport *vp = yn->vp;
+ struct viewport *vp = &yn->vp;
struct screen * display=yn->display;
- if(message==NULL)
- return false;
- struct viewport *last_vp = display->set_viewport(vp);
+ struct viewport *last_vp = display->set_viewport_ex(vp, VP_FLAG_VP_SET_CLEAN);
+
display->clear_viewport();
- put_message(yn->display, message, 0, viewport_get_nb_lines(vp));
+ put_message(display, message, 0, yn->vp_lines);
display->update_viewport();
display->set_viewport(last_vp);
- return(true);
}
+#if 0
+static void gui_yesno_ui_update(unsigned short id, void *event_data, void *user_data)
+{
+ (void)id;
+ (void)event_data;
-enum yesno_res gui_syncyesno_run(const struct text_message * main_message,
- const struct text_message * yes_message,
- const struct text_message * no_message)
+ struct gui_yesno* yn = (struct gui_yesno*)user_data;
+ FOR_NB_SCREENS(i)
+ {
+ gui_yesno_draw(&yn[i]);
+ }
+}
+#endif
+/* Display a YES_NO prompt to the user
+ *
+ * ticks < HZ will be ignored and the prompt will be blocking
+ * tmo_default_res is the answer that is returned when the timeout expires
+ * a default result of YESNO_TMO will also make the prompt blocking
+ * if tmo_default_res is YESNO_YES or YESNO_NO a seconds countdown will
+ * be present next to the default option
+ *
+ * ticks - timeout if (>=HZ) otherwise ignored
+ * default_res - result returned on timeout YESNO_TMO creates a blocking prompt
+ * main_message - prompt to the user
+ * yes_message - displayed when YESNO_YES is choosen
+ * no_message - displayed when YESNO_NO is choosen
+*/
+enum yesno_res gui_syncyesno_run_w_tmo(int ticks, enum yesno_res tmo_default_res,
+ const struct text_message * main_message,
+ const struct text_message * yes_message,
+ const struct text_message * no_message)
{
- int button;
- int result=-1;
- bool result_displayed;
+ #define YESNO_NONE (-1)
+ int action;
+ bool backlight_on;
+ bool talk_menu = global_settings.talk_menu;
+ int result = YESNO_NONE;
struct gui_yesno yn[NB_SCREENS];
- struct viewport vp[NB_SCREENS];
- long talked_tick = 0;
+ long talked_tick = current_tick - 1;
+ long end_tick = current_tick + ticks;
+
+ if (ticks < HZ) /* Display a prompt with NO timeout to the user */
+ {
+ tmo_default_res = YESNO_TMO;
+ }
+
FOR_NB_SCREENS(i)
{
+ yn[i].end_tick = end_tick;
+ yn[i].tmo_default_res = tmo_default_res;
yn[i].main_message=main_message;
- yn[i].result_message[YESNO_YES]=yes_message;
- yn[i].result_message[YESNO_NO]=no_message;
yn[i].display=&screens[i];
- yn[i].vp = &vp[i];
- viewportmanager_theme_enable(i, true, yn[i].vp);
screens[i].scroll_stop();
- gui_yesno_draw(&(yn[i]));
+ viewportmanager_theme_enable(i, true, &(yn[i].vp));
+ yn[i].vp_lines = viewport_get_nb_lines(&(yn[i].vp));
}
+#ifdef HAVE_TOUCHSCREEN
+ /* switch to point mode because that's more intuitive */
+ enum touchscreen_mode old_mode = touchscreen_get_mode();
+ touchscreen_set_mode(TOUCHSCREEN_POINT);
+#endif
+
/* make sure to eat any extranous keypresses */
action_wait_for_release();
button_clear_queue();
- while (result==-1)
+ /* hook into UI update events to avoid the dialog disappearing
+ * in case the skin decides to do a full refresh */
+ /*add_event_ex(GUI_EVENT_NEED_UI_UPDATE, false, gui_yesno_ui_update, &yn[0]);*/
+ /* probably no longer needed --Bilgus 2023*/
+
+ while (result==YESNO_NONE)
{
+
+ FOR_NB_SCREENS(i)
+ gui_yesno_draw(&yn[i]);
+
/* Repeat the question every 5secs (more or less) */
- if (global_settings.talk_menu
- && (talked_tick==0 || TIME_AFTER(current_tick, talked_tick+HZ*5)))
+ if (talk_menu && TIME_AFTER(current_tick, talked_tick))
{
- talked_tick = current_tick;
+ talked_tick = current_tick + (HZ*5);
talk_text_message(main_message, false);
}
- button = get_action(CONTEXT_YESNOSCREEN, HZ*5);
- switch (button)
+ backlight_on = is_backlight_on(false);
+ action = get_action(CONTEXT_YESNOSCREEN, HZ / 2); /* for statubar and tmo */
+ switch (action)
{
#ifdef HAVE_TOUCHSCREEN
case ACTION_TOUCHSCREEN:
{
+ int btn;
short int x, y;
- if (action_get_touchscreen_press_in_vp(&x, &y, yn[0].vp) == BUTTON_REL)
+ btn = action_get_touchscreen_press_in_vp(&x, &y, &(yn[0].vp));
+ if (btn == BUTTON_REL)
{
- if (y > yn[0].vp->height/2)
+ if (y > yn[0].vp.height/2)
{
- if (x <= yn[0].vp->width/2)
+ if (x <= yn[0].vp.width/2)
result = YESNO_YES;
else
result = YESNO_NO;
@@ -198,49 +296,81 @@ enum yesno_res gui_syncyesno_run(const struct text_message * main_message,
break;
#endif
case ACTION_YESNO_ACCEPT:
- result=YESNO_YES;
+ result = YESNO_YES;
break;
case ACTION_NONE:
+ if(tmo_default_res != YESNO_TMO && TIME_AFTER(current_tick, end_tick))
+ {
+ splash(HZ/2, ID2P(LANG_TIMEOUT));
+ result = tmo_default_res;
+ goto exit;
+ }
+ /*fall-through*/
case ACTION_UNKNOWN:
- case SYS_CHARGER_DISCONNECTED:
- case SYS_BATTERY_UPDATE:
- case SYS_TIMEOUT:
-#if CONFIG_CHARGING
- case SYS_CHARGER_CONNECTED:
-#endif
- /* ignore some SYS events that can happen */
+ case ACTION_REDRAW:
continue;
default:
- if(default_event_handler(button) == SYS_USB_CONNECTED)
- return(YESNO_USB);
- result = YESNO_NO;
+ if(default_event_handler(action) == SYS_USB_CONNECTED) {
+ result = YESNO_USB;
+ goto exit;
+ }
+ if (!IS_SYSEVENT(action)) /* ignore SYS events that can happen */
+ result = YESNO_NO;
}
+
+ if (!backlight_on)
+ result = YESNO_NONE; /* don't allow results if the screen is off */
}
- FOR_NB_SCREENS(i)
- result_displayed=gui_yesno_draw_result(&(yn[i]), result);
+exit:
+
+ /*remove_event_ex(GUI_EVENT_NEED_UI_UPDATE, gui_yesno_ui_update, &yn[0]);*/
- if (global_settings.talk_menu)
+ if (result == YESNO_YES || result == YESNO_NO)
{
- talk_text_message((result == YESNO_YES) ? yes_message
- : no_message, false);
- talk_force_enqueue_next();
+ const struct text_message * resmsg;
+ if (result == YESNO_YES)
+ resmsg = yes_message;
+ else
+ resmsg = no_message;
+
+ if (resmsg != NULL)
+ {
+ FOR_NB_SCREENS(i)
+ gui_yesno_draw_result(&(yn[i]), resmsg);
+
+ if (talk_menu)
+ {
+ talk_text_message(resmsg, false);
+ talk_force_enqueue_next();
+ }
+
+ sleep(HZ);
+ }
}
- if(result_displayed)
- sleep(HZ);
FOR_NB_SCREENS(i)
{
- screens[i].scroll_stop_viewport(yn[i].vp);
+ screens[i].scroll_stop_viewport(&(yn[i].vp));
viewportmanager_theme_undo(i, true);
}
- return(result);
+#ifdef HAVE_TOUCHSCREEN
+ touchscreen_set_mode(old_mode);
+#endif
+ return result;
}
+enum yesno_res gui_syncyesno_run(const struct text_message * main_message,
+ const struct text_message * yes_message,
+ const struct text_message * no_message)
+{
+ return gui_syncyesno_run_w_tmo(TIMEOUT_BLOCK, YESNO_TMO,
+ main_message, yes_message, no_message);
+}
/* Function to manipulate all yesno dialogues.
- This function needs the output text as an argument. */
+ This function needs the prompt text as an argument. */
bool yesno_pop(const char* text)
{
const char *lines[]={text};
diff --git a/apps/gui/yesno.h b/apps/gui/yesno.h
index 61bcf8d3c0..5f67733d79 100644
--- a/apps/gui/yesno.h
+++ b/apps/gui/yesno.h
@@ -27,7 +27,8 @@ enum yesno_res
{
YESNO_YES,
YESNO_NO,
- YESNO_USB
+ YESNO_USB,
+ YESNO_TMO
};
struct text_message
@@ -48,7 +49,13 @@ extern enum yesno_res gui_syncyesno_run(
const struct text_message * main_message,
const struct text_message * yes_message,
const struct text_message * no_message);
-
+
+extern enum yesno_res gui_syncyesno_run_w_tmo(
+ int ticks, enum yesno_res tmo_default_res,
+ const struct text_message * main_message,
+ const struct text_message * yes_message,
+ const struct text_message * no_message);
+
bool yesno_pop(const char* text);
#endif /* _GUI_YESNO_H_ */
diff --git a/apps/hosted/android/keyboard.c b/apps/hosted/android/keyboard.c
index eda951a7c9..b74f67e782 100644
--- a/apps/hosted/android/keyboard.c
+++ b/apps/hosted/android/keyboard.c
@@ -100,7 +100,7 @@ int kbd_input(char* text, int buflen, unsigned short *kbd)
if (accepted)
{
utf8_string = e->GetStringUTFChars(env_ptr, new_string, 0);
- strlcpy(text, utf8_string, buflen);
+ strmemccpy(text, utf8_string, buflen);
e->ReleaseStringUTFChars(env_ptr, new_string, utf8_string);
e->DeleteGlobalRef(env_ptr, new_string);
}
diff --git a/apps/hosted/android/notification.c b/apps/hosted/android/notification.c
index 39c8b07737..a5b60c1013 100644
--- a/apps/hosted/android/notification.c
+++ b/apps/hosted/android/notification.c
@@ -29,6 +29,7 @@
#include "misc.h"
#include "thread.h"
#include "debug.h"
+#include "audio.h"
extern JNIEnv *env_ptr;
extern jclass RockboxService_class;
diff --git a/apps/hosted/android/yesno.c b/apps/hosted/android/yesno.c
index bc8672ad76..af7f5a5c7b 100644
--- a/apps/hosted/android/yesno.c
+++ b/apps/hosted/android/yesno.c
@@ -117,6 +117,17 @@ enum yesno_res gui_syncyesno_run(const struct text_message * main_message,
return ret ? YESNO_YES : YESNO_NO;
}
+enum yesno_res gui_syncyesno_run_w_tmo(int ticks, enum yesno_res tmo_default_res,
+ const struct text_message * main_message,
+ const struct text_message * yes_message,
+ const struct text_message * no_message)
+{
+ /* FIXME: create a prompt with timeout for android */
+ (void)ticks;
+ (void)tmo_default_res;
+ return gui_syncyesno_run(main_message, yes_message, no_message);
+}
+
#endif
/* Function to manipulate all yesno dialogues.
diff --git a/apps/iap/iap-core.c b/apps/iap/iap-core.c
index 885ba2c188..da04a67311 100644
--- a/apps/iap/iap-core.c
+++ b/apps/iap/iap-core.c
@@ -19,8 +19,8 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
-#include <string.h>
+#include "string-extra.h"
#include "panic.h"
#include "iap-core.h"
#include "iap-lingo.h"
@@ -364,6 +364,7 @@ static void iap_thread(void)
/* Handle poweroff message */
case SYS_POWEROFF:
+ case SYS_REBOOT:
{
iap_shutdown = true;
break;
@@ -452,7 +453,7 @@ static void iap_malloc(void)
return;
#ifdef IAP_MALLOC_DYNAMIC
- iap_buffer_handle = core_alloc_ex("iap", IAP_MALLOC_SIZE, &iap_buflib_callbacks);
+ iap_buffer_handle = core_alloc_ex(IAP_MALLOC_SIZE, &iap_buflib_callbacks);
if (iap_buffer_handle < 0)
panicf("Could not allocate buffer memory");
iap_buffers = core_get_data(iap_buffer_handle);
diff --git a/apps/iap/iap-lingo2.c b/apps/iap/iap-lingo2.c
index 946e51222d..51f951c6d9 100644
--- a/apps/iap/iap-lingo2.c
+++ b/apps/iap/iap-lingo2.c
@@ -187,6 +187,17 @@ void iap_handlepkt_mode2(const unsigned int len, const unsigned char *buf)
REMOTE_BUTTON(BUTTON_RC_RIGHT);
if(buf[4] & 32) /* frwd */
REMOTE_BUTTON(BUTTON_RC_LEFT);
+ if(buf[4] & 64) /* menu */
+ REMOTE_BUTTON(BUTTON_RC_MENU);
+ if(buf[4] & 128) /* select */
+ REMOTE_BUTTON(BUTTON_RC_SELECT);
+ }
+ else if(len >= 6 && buf[5] != 0)
+ {
+ if(buf[5] & 1) /* up */
+ REMOTE_BUTTON(BUTTON_RC_UP);
+ if (buf[5] & 2) /* down */
+ REMOTE_BUTTON(BUTTON_RC_DOWN);
}
/* power on released */
diff --git a/apps/iap/iap-lingo4.c b/apps/iap/iap-lingo4.c
index 4ec5c462a1..eb629407f2 100644
--- a/apps/iap/iap-lingo4.c
+++ b/apps/iap/iap-lingo4.c
@@ -24,6 +24,7 @@
#include "filetree.h"
#include "wps.h"
#include "playback.h"
+#include "string-extra.h"
/*
* This macro is meant to be used inside an IAP mode message handler.
@@ -87,7 +88,7 @@ static void get_playlist_name(unsigned char *dest,
}
}
if (playlist_file != NULL) {
- strlcpy(dest, playlist_file->d_name, max_length);
+ strmemccpy(dest, playlist_file->d_name, max_length);
}
closedir(dp);
}
@@ -1218,7 +1219,7 @@ void iap_handlepkt_mode4(const unsigned int len, const unsigned char *buf)
{
memcpy(cur_dbrecord, buf + 3, 5);
- int paused = (is_wps_fading() || (audio_status() & AUDIO_STATUS_PAUSE));
+ int paused = !!(audio_status() & AUDIO_STATUS_PAUSE);
uint32_t index;
uint32_t trackcount;
index = get_u32(&cur_dbrecord[1]);
@@ -1430,7 +1431,6 @@ void iap_handlepkt_mode4(const unsigned int len, const unsigned char *buf)
unsigned int number_of_playlists = nbr_total_playlists();
uint32_t trackcount;
trackcount = playlist_amount();
- size_t len;
if ((buf[3] == 0x05) && ((start_index + read_count ) > trackcount))
{
@@ -1465,22 +1465,21 @@ void iap_handlepkt_mode4(const unsigned int len, const unsigned char *buf)
switch(buf[3])
{
case 0x05:
- len = strlcpy((char *)&data[7], id3.title,64);
+ strmemccpy((char *)&data[7], id3.title,64);
break;
case 0x02:
- len = strlcpy((char *)&data[7], id3.artist,64);
+ strmemccpy((char *)&data[7], id3.artist,64);
break;
case 0x03:
- len = strlcpy((char *)&data[7], id3.album,64);
+ strmemccpy((char *)&data[7], id3.album,64);
break;
case 0x04:
case 0x06:
- len = strlcpy((char *)&data[7], "Not Supported",14);
+ strmemccpy((char *)&data[7], "Not Supported",14);
break;
}
break;
}
- (void)len; /* Shut up, compiler */
put_u32(&data[3], start_index+counter);
iap_send_pkt(data, 7 + strlen(data+7) + 1);
yield();
@@ -2003,7 +2002,7 @@ void iap_handlepkt_mode4(const unsigned int len, const unsigned char *buf)
*
*/
{
- int paused = (is_wps_fading() || (audio_status() & AUDIO_STATUS_PAUSE));
+ int paused = !!(audio_status() & AUDIO_STATUS_PAUSE);
uint32_t index;
uint32_t trackcount;
index = get_u32(&buf[3]);
@@ -2821,7 +2820,7 @@ void iap_handlepkt_mode4(const unsigned int len, const unsigned char *buf)
*
*/
{
- int paused = (is_wps_fading() || (audio_status() & AUDIO_STATUS_PAUSE));
+ int paused = !!(audio_status() & AUDIO_STATUS_PAUSE);
long tracknum = get_u32(&buf[3]);
audio_pause();
@@ -2977,7 +2976,7 @@ void iap_handlepkt_mode4(const unsigned int len, const unsigned char *buf)
{
memcpy(cur_dbrecord, buf + 3, 5);
- int paused = (is_wps_fading() || (audio_status() & AUDIO_STATUS_PAUSE));
+ int paused = !!(audio_status() & AUDIO_STATUS_PAUSE);
unsigned int number_of_playlists = nbr_total_playlists();
uint32_t index;
uint32_t trackcount;
diff --git a/apps/keymaps/keymap-agptekrocker.c b/apps/keymaps/keymap-agptekrocker.c
index 4d7139ffd5..5ca834ab51 100644
--- a/apps/keymaps/keymap-agptekrocker.c
+++ b/apps/keymaps/keymap-agptekrocker.c
@@ -96,7 +96,6 @@ static const struct button_mapping button_context_settings[] = {
{ ACTION_SETTINGS_DEC, BUTTON_VOLDOWN, BUTTON_NONE },
{ ACTION_SETTINGS_DECREPEAT, BUTTON_VOLDOWN|BUTTON_REPEAT, BUTTON_NONE },
/* ACTION_SETTINGS_DECBIGSTEP */
- { ACTION_SETTINGS_RESET, BUTTON_SELECT|BUTTON_REPEAT, BUTTON_SELECT },
/* ACTION_SETTINGS_SET, Used by touchscreen targets */
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD),
}; /* button_context_settings */
@@ -140,15 +139,16 @@ static const struct button_mapping button_context_yesno[] = {
}; /* button_context_settings_yesno */
static const struct button_mapping button_context_quickscreen[] = {
- { ACTION_QS_TOP, BUTTON_UP|BUTTON_REL, BUTTON_NONE },
- { ACTION_QS_TOP, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE },
- { ACTION_QS_DOWN, BUTTON_DOWN|BUTTON_REL, BUTTON_NONE },
- { ACTION_QS_DOWN, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE },
- { ACTION_QS_LEFT, BUTTON_LEFT|BUTTON_REL, BUTTON_NONE },
- { ACTION_QS_LEFT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
- { ACTION_QS_RIGHT, BUTTON_RIGHT|BUTTON_REL, BUTTON_NONE },
- { ACTION_QS_RIGHT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
- { ACTION_STD_CANCEL, BUTTON_SELECT, BUTTON_NONE },
+ { ACTION_QS_TOP, BUTTON_UP|BUTTON_REL, BUTTON_NONE },
+ { ACTION_QS_TOP, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE },
+ { ACTION_QS_DOWN, BUTTON_DOWN|BUTTON_REL, BUTTON_NONE },
+ { ACTION_QS_DOWN, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE },
+ { ACTION_QS_LEFT, BUTTON_LEFT|BUTTON_REL, BUTTON_NONE },
+ { ACTION_QS_LEFT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
+ { ACTION_QS_RIGHT, BUTTON_RIGHT|BUTTON_REL, BUTTON_NONE },
+ { ACTION_QS_RIGHT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
+ { ACTION_STD_CONTEXT, BUTTON_SELECT|BUTTON_REPEAT, BUTTON_SELECT },
+ { ACTION_STD_CANCEL, BUTTON_SELECT|BUTTON_REL, BUTTON_SELECT },
LAST_ITEM_IN_LIST
}; /* button_context_quickscreen */
@@ -221,7 +221,7 @@ static const struct button_mapping button_context_bmark[] = {
/* get_context_mapping returns a pointer to one of the above defined arrays depending on the context */
const struct button_mapping* get_context_mapping(int context)
{
- switch (context)
+ switch (context & ~CONTEXT_LOCKED)
{
case CONTEXT_STD:
return button_context_standard;
diff --git a/apps/keymaps/keymap-c200.c b/apps/keymaps/keymap-c200.c
index 2e26f424cc..6ca1702e5f 100644
--- a/apps/keymaps/keymap-c200.c
+++ b/apps/keymaps/keymap-c200.c
@@ -88,7 +88,6 @@ static const struct button_mapping button_context_wps[] = {
static const struct button_mapping button_context_settings[] = {
{ ACTION_STD_CANCEL, BUTTON_POWER, BUTTON_NONE },
- { ACTION_SETTINGS_RESET, BUTTON_SELECT, BUTTON_NONE },
{ ACTION_SETTINGS_INC, BUTTON_UP, BUTTON_NONE },
{ ACTION_SETTINGS_INCREPEAT,BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE },
diff --git a/apps/keymaps/keymap-clip.c b/apps/keymaps/keymap-clip.c
index 7c8fb69471..59ee080364 100644
--- a/apps/keymaps/keymap-clip.c
+++ b/apps/keymaps/keymap-clip.c
@@ -84,9 +84,9 @@ static const struct button_mapping button_context_wps[] = {
{ ACTION_WPS_ID3SCREEN, BUTTON_SELECT|BUTTON_DOWN, BUTTON_SELECT },
#ifdef HAVE_HOTKEY /* down|repeat doesn't work in the file browser */
- { ACTION_WPS_HOTKEY, BUTTON_DOWN, BUTTON_NONE },
+ { ACTION_WPS_HOTKEY, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_DOWN },
#endif
- { ACTION_WPS_VIEW_PLAYLIST, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE },
+ { ACTION_WPS_VIEW_PLAYLIST, BUTTON_DOWN|BUTTON_REL, BUTTON_DOWN },
#ifndef HAS_BUTTON_HOLD /* Clip+ */
{ ACTION_STD_KEYLOCK, BUTTON_HOME|BUTTON_SELECT, BUTTON_NONE },
@@ -98,7 +98,6 @@ static const struct button_mapping button_context_wps[] = {
static const struct button_mapping button_context_settings[] = {
{ ACTION_STD_OK, BUTTON_HOME|BUTTON_REL, BUTTON_HOME },
{ ACTION_STD_CANCEL, BUTTON_POWER, BUTTON_NONE },
- { ACTION_SETTINGS_RESET, BUTTON_SELECT, BUTTON_NONE },
{ ACTION_SETTINGS_INC, BUTTON_UP, BUTTON_NONE },
{ ACTION_SETTINGS_INCREPEAT,BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE },
@@ -180,7 +179,7 @@ static const struct button_mapping button_context_quickscreen[] = {
{ ACTION_NONE, BUTTON_LEFT, BUTTON_NONE },
{ ACTION_STD_CANCEL, BUTTON_POWER|BUTTON_REL, BUTTON_NONE },
{ ACTION_STD_CANCEL, BUTTON_HOME, BUTTON_NONE },
- { ACTION_STD_CANCEL, BUTTON_SELECT, BUTTON_NONE },
+ { ACTION_STD_CANCEL, BUTTON_SELECT|BUTTON_REL, BUTTON_SELECT },
{ ACTION_QS_TOP, BUTTON_UP|BUTTON_REL, BUTTON_NONE },
{ ACTION_QS_TOP, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE },
{ ACTION_QS_DOWN, BUTTON_DOWN|BUTTON_REL, BUTTON_NONE },
@@ -213,9 +212,11 @@ static const struct button_mapping button_context_pitchscreen[] = {
{ ACTION_PS_NUDGE_LEFTOFF, BUTTON_LEFT|BUTTON_REL, BUTTON_NONE },
{ ACTION_PS_NUDGE_RIGHT, BUTTON_RIGHT, BUTTON_NONE },
{ ACTION_PS_NUDGE_RIGHTOFF, BUTTON_RIGHT|BUTTON_REL, BUTTON_NONE },
- { ACTION_PS_TOGGLE_MODE, BUTTON_HOME|BUTTON_REL, BUTTON_HOME },
- { ACTION_PS_RESET, BUTTON_SELECT, BUTTON_NONE },
+ { ACTION_PS_TOGGLE_MODE, BUTTON_SELECT|BUTTON_REL, BUTTON_SELECT },
+ { ACTION_PS_RESET, BUTTON_HOME|BUTTON_REPEAT, BUTTON_HOME },
+ { ACTION_PS_RESET, BUTTON_SELECT|BUTTON_REPEAT, BUTTON_SELECT },
{ ACTION_PS_EXIT, BUTTON_POWER, BUTTON_NONE },
+ { ACTION_PS_EXIT, BUTTON_HOME|BUTTON_REL, BUTTON_HOME },
{ ACTION_PS_SLOWER, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
{ ACTION_PS_FASTER, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD),
@@ -398,7 +399,7 @@ static const struct button_mapping button_context_usb_hid_mode_mouse[] = {
/* get_context_mapping returns a pointer to one of the above defined arrays depending on the context */
const struct button_mapping* get_context_mapping(int context)
{
- switch (context)
+ switch (context & ~CONTEXT_LOCKED)
{
case CONTEXT_STD:
return button_context_standard;
diff --git a/apps/keymaps/keymap-creativezv.c b/apps/keymaps/keymap-creativezv.c
index f2bcfd80f7..3242e8924f 100644
--- a/apps/keymaps/keymap-creativezv.c
+++ b/apps/keymaps/keymap-creativezv.c
@@ -134,7 +134,6 @@ static const struct button_mapping button_context_settings[] = {
{ ACTION_STD_PREVREPEAT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_LEFT },
{ ACTION_STD_NEXT, BUTTON_RIGHT, BUTTON_NONE },
{ ACTION_STD_NEXTREPEAT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_RIGHT },
- { ACTION_SETTINGS_RESET, BUTTON_PLAY, BUTTON_NONE },
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
}; /* button_context_settings */
@@ -147,7 +146,6 @@ static const struct button_mapping button_context_settings_right_is_inc[] = {
{ ACTION_STD_PREVREPEAT, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE },
{ ACTION_STD_NEXT, BUTTON_DOWN, BUTTON_NONE },
{ ACTION_STD_NEXTREPEAT, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE },
- { ACTION_SETTINGS_RESET, BUTTON_BACK, BUTTON_NONE },
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
}; /* button_context_settingsgraphical */
diff --git a/apps/keymaps/keymap-creativezvm.c b/apps/keymaps/keymap-creativezvm.c
index 7a0cb5965a..47b24bf8b3 100644
--- a/apps/keymaps/keymap-creativezvm.c
+++ b/apps/keymaps/keymap-creativezvm.c
@@ -133,7 +133,6 @@ static const struct button_mapping button_context_settings[] = {
{ ACTION_STD_PREVREPEAT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_LEFT },
{ ACTION_STD_NEXT, BUTTON_RIGHT, BUTTON_NONE },
{ ACTION_STD_NEXTREPEAT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_RIGHT },
- { ACTION_SETTINGS_RESET, BUTTON_PLAY, BUTTON_NONE },
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
}; /* button_context_settings */
@@ -146,7 +145,6 @@ static const struct button_mapping button_context_settings_right_is_inc[] = {
{ ACTION_STD_PREVREPEAT, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE },
{ ACTION_STD_NEXT, BUTTON_DOWN, BUTTON_NONE },
{ ACTION_STD_NEXTREPEAT, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE },
- { ACTION_SETTINGS_RESET, BUTTON_BACK, BUTTON_NONE },
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
}; /* button_context_settingsgraphical */
diff --git a/apps/keymaps/keymap-e200.c b/apps/keymaps/keymap-e200.c
index 538cd7cb8a..20cc16f2dc 100644
--- a/apps/keymaps/keymap-e200.c
+++ b/apps/keymaps/keymap-e200.c
@@ -100,7 +100,6 @@ static const struct button_mapping button_context_settings[] = {
{ ACTION_STD_PREVREPEAT, BUTTON_UP|BUTTON_REPEAT, BUTTON_UP },
{ ACTION_STD_NEXT, BUTTON_DOWN|BUTTON_REL, BUTTON_DOWN },
{ ACTION_STD_NEXTREPEAT, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_DOWN },
- { ACTION_SETTINGS_RESET, BUTTON_SELECT, BUTTON_NONE },
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD),
}; /* button_context_settings */
diff --git a/apps/keymaps/keymap-erosq.c b/apps/keymaps/keymap-erosq.c
index 59b70d24ca..054da96201 100644
--- a/apps/keymaps/keymap-erosq.c
+++ b/apps/keymaps/keymap-erosq.c
@@ -87,7 +87,6 @@ static const struct button_mapping button_context_settings[] = {
{ ACTION_SETTINGS_INCBIGSTEP, BUTTON_VOL_UP, BUTTON_NONE },
{ ACTION_SETTINGS_DEC, BUTTON_SCROLL_BACK, BUTTON_NONE },
{ ACTION_SETTINGS_DECBIGSTEP, BUTTON_VOL_DOWN, BUTTON_NONE },
- { ACTION_SETTINGS_RESET, BUTTON_PLAY|BUTTON_REPEAT, BUTTON_PLAY },
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD),
}; /* button_context_settings */
@@ -131,6 +130,7 @@ static const struct button_mapping button_context_quickscreen[] = {
{ ACTION_QS_DOWN, BUTTON_NEXT|BUTTON_REL, BUTTON_NONE },
{ ACTION_QS_DOWN, BUTTON_NEXT|BUTTON_REPEAT, BUTTON_NONE },
{ ACTION_STD_CANCEL, BUTTON_BACK, BUTTON_NONE },
+ { ACTION_STD_CONTEXT,BUTTON_MENU|BUTTON_REPEAT, BUTTON_MENU },
LAST_ITEM_IN_LIST
}; /* button_context_quickscreen */
@@ -194,7 +194,7 @@ static const struct button_mapping button_context_bmark[] = {
/* get_context_mapping returns a pointer to one of the above defined arrays depending on the context */
const struct button_mapping* get_context_mapping(int context)
{
- switch (context)
+ switch (context & ~CONTEXT_LOCKED)
{
case CONTEXT_STD:
return button_context_standard;
@@ -212,7 +212,8 @@ const struct button_mapping* get_context_mapping(int context)
return button_context_list;
case CONTEXT_SETTINGS:
- case CONTEXT_SETTINGS_EQ:
+ case CONTEXT_SETTINGS_EQ:
+ case CONTEXT_SETTINGS_COLOURCHOOSER:
return button_context_settings;
case CONTEXT_SETTINGS_TIME:
diff --git a/apps/keymaps/keymap-fiiom3k.c b/apps/keymaps/keymap-fiiom3k.c
index 94d2dbc172..1ccf7e956f 100644
--- a/apps/keymaps/keymap-fiiom3k.c
+++ b/apps/keymaps/keymap-fiiom3k.c
@@ -40,39 +40,41 @@ static const struct button_mapping button_context_standard[] = {
{ACTION_STD_OK, BUTTON_SELECT|BUTTON_REL, BUTTON_SELECT},
{ACTION_STD_CANCEL, BUTTON_BACK|BUTTON_REL, BUTTON_BACK},
{ACTION_STD_CONTEXT, BUTTON_SELECT|BUTTON_REPEAT, BUTTON_SELECT},
- {ACTION_STD_CONTEXT, BUTTON_MENU|BUTTON_REL, BUTTON_MENU},
- {ACTION_STD_MENU, BUTTON_BACK|BUTTON_REPEAT, BUTTON_BACK},
- {ACTION_STD_QUICKSCREEN, BUTTON_MENU|BUTTON_REPEAT, BUTTON_MENU},
+ {ACTION_STD_QUICKSCREEN, BUTTON_MENU|BUTTON_REL, BUTTON_MENU},
+ {ACTION_STD_MENU, BUTTON_MENU|BUTTON_REPEAT, BUTTON_MENU},
{ACTION_STD_KEYLOCK, BUTTON_POWER|BUTTON_REL, BUTTON_POWER},
- {ACTION_STD_HOTKEY, BUTTON_PLAY|BUTTON_REL, BUTTON_PLAY},
LAST_ITEM_IN_LIST
}; /* button_context_standard */
static const struct button_mapping button_context_wps[] = {
{ACTION_WPS_PLAY, BUTTON_PLAY|BUTTON_REL, BUTTON_PLAY},
- {ACTION_WPS_PLAY, BUTTON_SELECT|BUTTON_REL, BUTTON_SELECT},
+ {ACTION_WPS_VIEW_PLAYLIST, BUTTON_SELECT|BUTTON_REL, BUTTON_SELECT},
{ACTION_WPS_STOP, BUTTON_POWER|BUTTON_REPEAT, BUTTON_POWER},
{ACTION_WPS_VOLUP, BUTTON_VOL_UP, BUTTON_NONE},
{ACTION_WPS_VOLUP, BUTTON_VOL_UP|BUTTON_REPEAT, BUTTON_NONE},
{ACTION_WPS_VOLDOWN, BUTTON_VOL_DOWN, BUTTON_NONE},
{ACTION_WPS_VOLDOWN, BUTTON_VOL_DOWN|BUTTON_REPEAT, BUTTON_NONE},
+ {ACTION_WPS_VOLUP, BUTTON_SCROLL_BACK, BUTTON_NONE},
+ {ACTION_WPS_VOLUP, BUTTON_SCROLL_BACK|BUTTON_REPEAT, BUTTON_NONE},
+ {ACTION_WPS_VOLDOWN, BUTTON_SCROLL_FWD, BUTTON_NONE},
+ {ACTION_WPS_VOLDOWN, BUTTON_SCROLL_FWD|BUTTON_REPEAT, BUTTON_NONE},
+ {ACTION_WPS_VOLUP, BUTTON_UP|BUTTON_REL, BUTTON_UP},
+ {ACTION_WPS_VOLDOWN, BUTTON_DOWN|BUTTON_REL, BUTTON_DOWN},
{ACTION_WPS_SKIPNEXT, BUTTON_RIGHT|BUTTON_REL, BUTTON_RIGHT},
{ACTION_WPS_SKIPPREV, BUTTON_LEFT|BUTTON_REL, BUTTON_LEFT},
{ACTION_WPS_SEEKFWD, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE},
{ACTION_WPS_STOPSEEK, BUTTON_RIGHT|BUTTON_REL, BUTTON_RIGHT|BUTTON_REPEAT},
{ACTION_WPS_SEEKBACK, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE},
{ACTION_WPS_STOPSEEK, BUTTON_LEFT|BUTTON_REL, BUTTON_LEFT|BUTTON_REPEAT},
- {ACTION_WPS_BROWSE, BUTTON_BACK|BUTTON_REPEAT, BUTTON_BACK},
- {ACTION_WPS_MENU, BUTTON_BACK|BUTTON_REL, BUTTON_BACK},
- {ACTION_WPS_CONTEXT, BUTTON_MENU|BUTTON_REL, BUTTON_MENU},
- {ACTION_WPS_QUICKSCREEN, BUTTON_MENU|BUTTON_REPEAT, BUTTON_MENU},
+ {ACTION_WPS_BROWSE, BUTTON_BACK|BUTTON_REL, BUTTON_BACK},
+ {ACTION_WPS_QUICKSCREEN, BUTTON_MENU|BUTTON_REL, BUTTON_MENU},
+ {ACTION_WPS_MENU, BUTTON_MENU|BUTTON_REPEAT, BUTTON_MENU},
{ACTION_STD_KEYLOCK, BUTTON_POWER|BUTTON_REL, BUTTON_POWER},
{ACTION_WPS_HOTKEY, BUTTON_PLAY|BUTTON_REPEAT, BUTTON_PLAY},
- {ACTION_WPS_VIEW_PLAYLIST, BUTTON_SCROLL_FWD, BUTTON_NONE},
- {ACTION_WPS_VIEW_PLAYLIST, BUTTON_SCROLL_BACK, BUTTON_NONE},
+ {ACTION_WPS_CONTEXT, BUTTON_SELECT|BUTTON_REPEAT, BUTTON_SELECT},
{ACTION_WPS_ABSETA_PREVDIR, BUTTON_UP|BUTTON_REPEAT, BUTTON_UP},
{ACTION_WPS_ABSETB_NEXTDIR, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_DOWN},
- {ACTION_WPS_ABRESET, BUTTON_SELECT|BUTTON_REPEAT, BUTTON_SELECT},
+ {ACTION_WPS_ABRESET, BUTTON_BACK|BUTTON_REPEAT, BUTTON_BACK},
LAST_ITEM_IN_LIST
}; /* button_context_wps */
@@ -87,11 +89,25 @@ static const struct button_mapping button_context_wps_locked[] = {
}; /* button_context_wps_locked */
static const struct button_mapping button_context_tree[] = {
- {ACTION_TREE_STOP, BUTTON_PLAY|BUTTON_REPEAT, BUTTON_PLAY},
- {ACTION_TREE_WPS, BUTTON_BACK|BUTTON_REPEAT, BUTTON_BACK},
+ {ACTION_TREE_WPS, BUTTON_PLAY|BUTTON_REL, BUTTON_PLAY },
+ {ACTION_TREE_HOTKEY, BUTTON_PLAY|BUTTON_REPEAT, BUTTON_PLAY},
+ {ACTION_TREE_STOP, BUTTON_POWER|BUTTON_REPEAT, BUTTON_POWER},
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_LIST)
}; /* button_context_tree */
+
+static const struct button_mapping button_context_tree_scroll_lr[] = {
+ { ACTION_TREE_ROOT_INIT, BUTTON_MENU|BUTTON_REPEAT, BUTTON_MENU },
+ { ACTION_TREE_PGLEFT, BUTTON_MENU|BUTTON_REPEAT, BUTTON_NONE },
+ { ACTION_TREE_PGRIGHT, BUTTON_BACK|BUTTON_REPEAT, BUTTON_NONE },
+ LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_CUSTOM|CONTEXT_TREE),
+}; /* button_context_tree_scroll_lr */
+
+static const struct button_mapping button_context_bmark[] = {
+ {ACTION_BMS_DELETE, BUTTON_PLAY, BUTTON_NONE},
+ LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_TREE),
+}; /* button_context_bmark */
+
static const struct button_mapping button_context_list[] = {
{ACTION_LISTTREE_PGUP, BUTTON_LEFT, BUTTON_NONE},
{ACTION_LISTTREE_PGUP, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE},
@@ -115,7 +131,6 @@ static const struct button_mapping button_context_settings[] = {
{ACTION_SETTINGS_DEC, BUTTON_SCROLL_FWD, BUTTON_NONE},
{ACTION_SETTINGS_DECREPEAT, BUTTON_SCROLL_FWD|BUTTON_REPEAT, BUTTON_NONE},
{ACTION_SETTINGS_DECBIGSTEP, BUTTON_VOL_DOWN, BUTTON_NONE},
- {ACTION_SETTINGS_RESET, BUTTON_SELECT|BUTTON_REPEAT, BUTTON_SELECT},
{ACTION_STD_NEXT, BUTTON_RIGHT, BUTTON_NONE},
{ACTION_STD_NEXTREPEAT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE},
{ACTION_STD_PREV, BUTTON_LEFT, BUTTON_NONE},
@@ -123,6 +138,16 @@ static const struct button_mapping button_context_settings[] = {
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
}; /* button_context_settings */
+static const struct button_mapping button_context_settings_rectrigger[] = {
+ {ACTION_SETTINGS_INC, BUTTON_RIGHT, BUTTON_NONE},
+ {ACTION_SETTINGS_INCREPEAT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE},
+ {ACTION_SETTINGS_INCBIGSTEP, BUTTON_VOL_UP, BUTTON_NONE},
+ {ACTION_SETTINGS_DEC, BUTTON_LEFT, BUTTON_NONE},
+ {ACTION_SETTINGS_DECREPEAT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE},
+ {ACTION_SETTINGS_DECBIGSTEP, BUTTON_VOL_DOWN, BUTTON_NONE},
+ LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
+}; /* button_context_settings_rectrigger */
+
static const struct button_mapping button_context_settings_eq[] = {
{ACTION_SETTINGS_INC, BUTTON_RIGHT, BUTTON_NONE},
{ACTION_SETTINGS_INCREPEAT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE},
@@ -135,14 +160,26 @@ static const struct button_mapping button_context_settings_eq[] = {
static const struct button_mapping button_context_quickscreen[] = {
{ACTION_QS_TOP, BUTTON_UP, BUTTON_NONE},
+ {ACTION_QS_TOP, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE},
+ {ACTION_QS_TOP, BUTTON_SCROLL_BACK, BUTTON_NONE},
+ {ACTION_QS_TOP, BUTTON_SCROLL_BACK|BUTTON_REPEAT, BUTTON_NONE},
{ACTION_QS_DOWN, BUTTON_DOWN, BUTTON_NONE},
+ {ACTION_QS_DOWN, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE},
+ {ACTION_QS_DOWN, BUTTON_SCROLL_FWD, BUTTON_NONE},
+ {ACTION_QS_DOWN, BUTTON_SCROLL_FWD|BUTTON_REPEAT, BUTTON_NONE},
{ACTION_QS_LEFT, BUTTON_LEFT, BUTTON_NONE},
+ {ACTION_QS_LEFT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE},
{ACTION_QS_RIGHT, BUTTON_RIGHT, BUTTON_NONE},
+ {ACTION_QS_RIGHT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE},
{ACTION_QS_VOLUP, BUTTON_VOL_UP, BUTTON_NONE},
+ {ACTION_QS_VOLUP, BUTTON_VOL_UP|BUTTON_REPEAT, BUTTON_NONE},
{ACTION_QS_VOLDOWN, BUTTON_VOL_DOWN, BUTTON_NONE},
- {ACTION_STD_CANCEL, BUTTON_SELECT, BUTTON_NONE},
+ {ACTION_QS_VOLDOWN, BUTTON_VOL_DOWN|BUTTON_REPEAT, BUTTON_NONE},
+ {ACTION_STD_CONTEXT, BUTTON_SELECT|BUTTON_REPEAT, BUTTON_SELECT},
+ {ACTION_STD_CANCEL, BUTTON_SELECT|BUTTON_REL, BUTTON_SELECT},
{ACTION_STD_CANCEL, BUTTON_POWER, BUTTON_NONE},
{ACTION_STD_CANCEL, BUTTON_BACK, BUTTON_NONE},
+ {ACTION_STD_CANCEL, BUTTON_MENU, BUTTON_NONE},
LAST_ITEM_IN_LIST
}; /* button_context_quickscreen */
@@ -167,18 +204,51 @@ static const struct button_mapping button_context_pitchscreen[] = {
static const struct button_mapping button_context_yesnoscreen[] = {
{ACTION_YESNO_ACCEPT, BUTTON_PLAY, BUTTON_NONE},
- {ACTION_YESNO_ACCEPT, BUTTON_SELECT, BUTTON_NONE},
+ {ACTION_STD_CANCEL, BUTTON_SELECT, BUTTON_NONE},
{ACTION_STD_CANCEL, BUTTON_BACK, BUTTON_NONE},
{ACTION_STD_CANCEL, BUTTON_POWER, BUTTON_NONE},
- LAST_ITEM_IN_LIST
+ {ACTION_STD_CANCEL, BUTTON_RIGHT, BUTTON_NONE},
+ {ACTION_STD_CANCEL, BUTTON_LEFT, BUTTON_NONE},
+ {ACTION_STD_CANCEL, BUTTON_VOL_UP, BUTTON_NONE},
+ {ACTION_STD_CANCEL, BUTTON_VOL_DOWN, BUTTON_NONE},
+ LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
}; /* button_context_yesnoscreen */
+static const struct button_mapping button_context_recscreen[] = {
+ {ACTION_REC_PAUSE, BUTTON_PLAY, BUTTON_NONE},
+ {ACTION_REC_NEWFILE, BUTTON_SELECT, BUTTON_NONE},
+ {ACTION_STD_MENU, BUTTON_MENU, BUTTON_NONE},
+ {ACTION_STD_CANCEL, BUTTON_BACK, BUTTON_NONE},
+ {ACTION_STD_CANCEL, BUTTON_POWER, BUTTON_NONE},
+ {ACTION_STD_PREV, BUTTON_UP, BUTTON_NONE},
+ {ACTION_STD_PREVREPEAT, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE},
+ {ACTION_STD_NEXT, BUTTON_DOWN, BUTTON_NONE},
+ {ACTION_STD_NEXTREPEAT, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE},
+ {ACTION_STD_PREV, BUTTON_SCROLL_BACK, BUTTON_NONE},
+ {ACTION_STD_PREVREPEAT, BUTTON_SCROLL_BACK|BUTTON_REPEAT, BUTTON_NONE},
+ {ACTION_STD_NEXT, BUTTON_SCROLL_FWD, BUTTON_NONE},
+ {ACTION_STD_NEXTREPEAT, BUTTON_SCROLL_FWD|BUTTON_REPEAT, BUTTON_NONE},
+ {ACTION_SETTINGS_INC, BUTTON_VOL_UP, BUTTON_NONE},
+ {ACTION_SETTINGS_INCREPEAT, BUTTON_VOL_UP|BUTTON_REPEAT, BUTTON_NONE},
+ {ACTION_SETTINGS_DEC, BUTTON_VOL_DOWN, BUTTON_NONE},
+ {ACTION_SETTINGS_DECREPEAT, BUTTON_VOL_DOWN|BUTTON_REPEAT, BUTTON_NONE},
+ {ACTION_SETTINGS_INC, BUTTON_RIGHT, BUTTON_NONE},
+ {ACTION_SETTINGS_INCREPEAT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE},
+ {ACTION_SETTINGS_DEC, BUTTON_LEFT, BUTTON_NONE},
+ {ACTION_SETTINGS_DECREPEAT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE},
+ LAST_ITEM_IN_LIST
+}; /* button_context_recscreen */
+
static const struct button_mapping button_context_keyboard[] = {
{ACTION_KBD_UP, BUTTON_UP, BUTTON_NONE},
{ACTION_KBD_DOWN, BUTTON_DOWN, BUTTON_NONE},
{ACTION_KBD_LEFT, BUTTON_LEFT, BUTTON_NONE},
{ACTION_KBD_RIGHT, BUTTON_RIGHT, BUTTON_NONE},
{ACTION_KBD_UP, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE},
+ {ACTION_KBD_UP, BUTTON_SCROLL_BACK, BUTTON_NONE},
+ {ACTION_KBD_UP, BUTTON_SCROLL_BACK|BUTTON_REPEAT, BUTTON_NONE},
+ {ACTION_KBD_DOWN, BUTTON_SCROLL_FWD, BUTTON_NONE},
+ {ACTION_KBD_DOWN, BUTTON_SCROLL_FWD|BUTTON_REPEAT, BUTTON_NONE},
{ACTION_KBD_DOWN, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE},
{ACTION_KBD_LEFT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE},
{ACTION_KBD_RIGHT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE},
@@ -252,32 +322,31 @@ static const struct button_mapping button_context_usb_hid_mode_browser[] = {
const struct button_mapping* get_context_mapping(int context)
{
- switch (context)
- {
- case CONTEXT_WPS|CONTEXT_LOCKED:
- return button_context_wps_locked;
- default:
- context &= ~CONTEXT_LOCKED;
- break;
- }
-
- switch (context)
+ switch (context & ~CONTEXT_LOCKED)
{
default:
case CONTEXT_STD:
return button_context_standard;
case CONTEXT_WPS:
+ if (context & CONTEXT_LOCKED)
+ return button_context_wps_locked;
return button_context_wps;
case CONTEXT_TREE:
case CONTEXT_MAINMENU:
- case CONTEXT_BOOKMARKSCREEN:
+ if (global_settings.hold_lr_for_scroll_in_list)
+ return button_context_tree_scroll_lr;
+ /* else fall through to CUSTOM|CONTEXT_TREE */
+ case CONTEXT_CUSTOM|CONTEXT_TREE:
return button_context_tree;
+ case CONTEXT_BOOKMARKSCREEN:
+ return button_context_bmark;
case CONTEXT_LIST:
return button_context_list;
case CONTEXT_SETTINGS:
case CONTEXT_SETTINGS_TIME:
- case CONTEXT_SETTINGS_RECTRIGGER:
return button_context_settings;
+ case CONTEXT_SETTINGS_RECTRIGGER:
+ return button_context_settings_rectrigger;
case CONTEXT_SETTINGS_EQ:
case CONTEXT_SETTINGS_COLOURCHOOSER:
return button_context_settings_eq;
@@ -287,6 +356,8 @@ const struct button_mapping* get_context_mapping(int context)
return button_context_pitchscreen;
case CONTEXT_YESNOSCREEN:
return button_context_yesnoscreen;
+ case CONTEXT_RECSCREEN:
+ return button_context_recscreen;
case CONTEXT_KEYBOARD:
return button_context_keyboard;
case CONTEXT_USB_HID:
diff --git a/apps/keymaps/keymap-fiiom3klinux.c b/apps/keymaps/keymap-fiiom3klinux.c
index 7769e2da04..79a7687501 100644
--- a/apps/keymaps/keymap-fiiom3klinux.c
+++ b/apps/keymaps/keymap-fiiom3klinux.c
@@ -150,7 +150,6 @@ static const struct button_mapping button_context_quickscreen[] = {
/** Settings - General Mappings **/
static const struct button_mapping button_context_settings[] = {
- { ACTION_SETTINGS_RESET, BUTTON_POWER|BUTTON_REL, BUTTON_POWER },
{ ACTION_STD_PREV, BUTTON_PREV, BUTTON_NONE },
{ ACTION_STD_PREVREPEAT, BUTTON_PREV|BUTTON_REPEAT, BUTTON_NONE },
{ ACTION_STD_NEXT, BUTTON_NEXT, BUTTON_NONE },
@@ -193,7 +192,7 @@ static const struct button_mapping button_context_yesnoscreen[] = {
/* get_context_mapping returns a pointer to one of the above defined arrays depending on the context */
const struct button_mapping* get_context_mapping(int context)
{
- switch (context)
+ switch (context & ~CONTEXT_LOCKED)
{
case CONTEXT_LIST:
return button_context_list;
diff --git a/apps/keymaps/keymap-fuze.c b/apps/keymaps/keymap-fuze.c
index c5538a6c49..ef0bed1099 100644
--- a/apps/keymaps/keymap-fuze.c
+++ b/apps/keymaps/keymap-fuze.c
@@ -99,7 +99,6 @@ static const struct button_mapping button_context_settings[] = {
{ ACTION_STD_PREVREPEAT, BUTTON_UP|BUTTON_REPEAT, BUTTON_UP },
{ ACTION_STD_NEXT, BUTTON_DOWN|BUTTON_REL, BUTTON_DOWN },
{ ACTION_STD_NEXTREPEAT, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_DOWN },
- { ACTION_SETTINGS_RESET, BUTTON_SELECT, BUTTON_NONE },
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD),
}; /* button_context_settings */
diff --git a/apps/keymaps/keymap-fuzeplus.c b/apps/keymaps/keymap-fuzeplus.c
index 0933916b4a..052f11085e 100644
--- a/apps/keymaps/keymap-fuzeplus.c
+++ b/apps/keymaps/keymap-fuzeplus.c
@@ -128,7 +128,8 @@ static const struct button_mapping button_context_keyboard[] = {
}; /* button_context_keyboard */
static const struct button_mapping button_context_quickscreen[] = {
- { ACTION_STD_CANCEL, BUTTON_SELECT, BUTTON_NONE },
+ { ACTION_STD_CONTEXT, BUTTON_SELECT|BUTTON_REPEAT, BUTTON_SELECT },
+ { ACTION_STD_CANCEL, BUTTON_SELECT|BUTTON_REL, BUTTON_SELECT },
{ ACTION_STD_CANCEL, BUTTON_BACK, BUTTON_NONE },
{ ACTION_STD_CANCEL, BUTTON_PLAYPAUSE, BUTTON_NONE },
{ ACTION_STD_CANCEL, BUTTON_BOTTOMRIGHT, BUTTON_NONE },
@@ -225,7 +226,6 @@ static const struct button_mapping button_context_settings[] = {
{ ACTION_SETTINGS_DECBIGSTEP, BUTTON_BOTTOMLEFT|BUTTON_REPEAT, BUTTON_NONE },
{ ACTION_STD_OK, BUTTON_SELECT|BUTTON_REL, BUTTON_SELECT },
- { ACTION_SETTINGS_RESET, BUTTON_SELECT|BUTTON_REPEAT, BUTTON_NONE },
{ ACTION_STD_CANCEL, BUTTON_BACK, BUTTON_NONE },
@@ -407,7 +407,7 @@ static const struct button_mapping button_context_usb_hid_mode_mouse[] = {
/* get_context_mapping returns a pointer to one of the above defined arrays depending on the context */
const struct button_mapping* get_context_mapping(int context)
{
- switch (context)
+ switch (context & ~CONTEXT_LOCKED)
{
case CONTEXT_STD:
return button_context_standard;
diff --git a/apps/keymaps/keymap-gigabeat-s.c b/apps/keymaps/keymap-gigabeat-s.c
index 4cd76184ff..35cecb7476 100644
--- a/apps/keymaps/keymap-gigabeat-s.c
+++ b/apps/keymaps/keymap-gigabeat-s.c
@@ -167,7 +167,6 @@ static const struct button_mapping button_context_settings[] = {
{ ACTION_STD_PREVREPEAT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
{ ACTION_STD_NEXT, BUTTON_RIGHT, BUTTON_NONE },
{ ACTION_STD_NEXTREPEAT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
- { ACTION_SETTINGS_RESET, BUTTON_BACK, BUTTON_NONE },
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
}; /* button_context_settings */
@@ -181,7 +180,6 @@ static const struct button_mapping button_context_settings_right_is_inc[] = {
{ ACTION_STD_PREVREPEAT, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE },
{ ACTION_STD_NEXT, BUTTON_DOWN, BUTTON_NONE },
{ ACTION_STD_NEXTREPEAT, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE },
- { ACTION_SETTINGS_RESET, BUTTON_BACK, BUTTON_NONE },
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
}; /* button_context_settingsgraphical */
diff --git a/apps/keymaps/keymap-gigabeat.c b/apps/keymaps/keymap-gigabeat.c
index 31fd0eaf0d..913763af18 100644
--- a/apps/keymaps/keymap-gigabeat.c
+++ b/apps/keymaps/keymap-gigabeat.c
@@ -160,7 +160,6 @@ static const struct button_mapping button_context_settings[] = {
{ ACTION_STD_PREVREPEAT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
{ ACTION_STD_NEXT, BUTTON_RIGHT, BUTTON_NONE },
{ ACTION_STD_NEXTREPEAT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
- { ACTION_SETTINGS_RESET, BUTTON_A, BUTTON_NONE },
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
}; /* button_context_settings */
@@ -174,7 +173,6 @@ static const struct button_mapping button_context_settings_right_is_inc[] = {
{ ACTION_STD_PREVREPEAT, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE },
{ ACTION_STD_NEXT, BUTTON_DOWN, BUTTON_NONE },
{ ACTION_STD_NEXTREPEAT, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE },
- { ACTION_SETTINGS_RESET, BUTTON_A, BUTTON_NONE },
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
}; /* button_context_settingsgraphical */
diff --git a/apps/keymaps/keymap-h10.c b/apps/keymaps/keymap-h10.c
index 825590e6f4..7df2b61d05 100644
--- a/apps/keymaps/keymap-h10.c
+++ b/apps/keymaps/keymap-h10.c
@@ -124,7 +124,6 @@ static const struct button_mapping button_context_settings[] = {
{ ACTION_STD_PREVREPEAT, BUTTON_FF|BUTTON_REPEAT, BUTTON_NONE },
{ ACTION_STD_NEXT, BUTTON_REW, BUTTON_NONE },
{ ACTION_STD_NEXTREPEAT, BUTTON_REW|BUTTON_REPEAT, BUTTON_NONE },
- { ACTION_SETTINGS_RESET, BUTTON_PLAY, BUTTON_NONE },
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD),
}; /* button_context_settings */
diff --git a/apps/keymaps/keymap-h1x0_h3x0.c b/apps/keymaps/keymap-h1x0_h3x0.c
index d4f098ce1e..422af7502c 100644
--- a/apps/keymaps/keymap-h1x0_h3x0.c
+++ b/apps/keymaps/keymap-h1x0_h3x0.c
@@ -145,7 +145,6 @@ static const struct button_mapping button_context_settings[] = {
{ ACTION_STD_PREVREPEAT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
{ ACTION_STD_NEXT, BUTTON_RIGHT, BUTTON_NONE },
{ ACTION_STD_NEXTREPEAT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
- { ACTION_SETTINGS_RESET, BUTTON_ON, BUTTON_NONE },
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
}; /* button_context_settings */
@@ -159,7 +158,6 @@ static const struct button_mapping button_context_settings_right_is_inc[] = {
{ ACTION_STD_PREVREPEAT, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE },
{ ACTION_STD_NEXT, BUTTON_DOWN, BUTTON_NONE },
{ ACTION_STD_NEXTREPEAT, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE },
- { ACTION_SETTINGS_RESET, BUTTON_ON, BUTTON_NONE },
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
}; /* button_context_settingsgraphical */
diff --git a/apps/keymaps/keymap-hdd1630.c b/apps/keymaps/keymap-hdd1630.c
index ca55f7fc26..7afcaeec6d 100644
--- a/apps/keymaps/keymap-hdd1630.c
+++ b/apps/keymaps/keymap-hdd1630.c
@@ -160,7 +160,6 @@ static const struct button_mapping button_context_settings[] = {
{ ACTION_STD_PREVREPEAT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
{ ACTION_STD_NEXT, BUTTON_RIGHT, BUTTON_NONE },
{ ACTION_STD_NEXTREPEAT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
- { ACTION_SETTINGS_RESET, BUTTON_VIEW, BUTTON_NONE },
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
}; /* button_context_settings */
@@ -174,7 +173,6 @@ static const struct button_mapping button_context_settings_right_is_inc[] = {
{ ACTION_STD_PREVREPEAT, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE },
{ ACTION_STD_NEXT, BUTTON_DOWN, BUTTON_NONE },
{ ACTION_STD_NEXTREPEAT, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE },
- { ACTION_SETTINGS_RESET, BUTTON_VIEW, BUTTON_NONE },
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
}; /* button_context_settingsgraphical */
diff --git a/apps/keymaps/keymap-hdd6330.c b/apps/keymaps/keymap-hdd6330.c
index ed197af41c..18ef2b7c8f 100644
--- a/apps/keymaps/keymap-hdd6330.c
+++ b/apps/keymaps/keymap-hdd6330.c
@@ -142,7 +142,6 @@ static const struct button_mapping button_context_settings[] = {
{ ACTION_STD_PREVREPEAT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
{ ACTION_STD_NEXT, BUTTON_RIGHT, BUTTON_NONE },
{ ACTION_STD_NEXTREPEAT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
- { ACTION_SETTINGS_RESET, BUTTON_VIEW, BUTTON_NONE },
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
}; /* button_context_settings */
@@ -156,7 +155,6 @@ static const struct button_mapping button_context_settings_right_is_inc[] = {
{ ACTION_STD_PREVREPEAT, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE },
{ ACTION_STD_NEXT, BUTTON_DOWN, BUTTON_NONE },
{ ACTION_STD_NEXTREPEAT, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE },
- { ACTION_SETTINGS_RESET, BUTTON_VIEW, BUTTON_NONE },
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
}; /* button_context_settingsgraphical */
diff --git a/apps/keymaps/keymap-hm60x.c b/apps/keymaps/keymap-hm60x.c
index 1050ec7f31..fa8783de6d 100644
--- a/apps/keymaps/keymap-hm60x.c
+++ b/apps/keymaps/keymap-hm60x.c
@@ -128,7 +128,6 @@ static const struct button_mapping button_context_quickscreen[] = {
/** Settings - General Mappings **/
static const struct button_mapping button_context_settings[] = {
- { ACTION_SETTINGS_RESET, BUTTON_SELECT, BUTTON_NONE },
{ ACTION_SETTINGS_INC, BUTTON_UP, BUTTON_NONE },
{ ACTION_SETTINGS_INCREPEAT, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE },
{ ACTION_SETTINGS_DEC, BUTTON_DOWN, BUTTON_NONE },
@@ -176,7 +175,7 @@ static const struct button_mapping button_context_tree_scroll_lr[] = {
/** Yes/No Screen **/
static const struct button_mapping button_context_yesnoscreen[] = {
{ ACTION_YESNO_ACCEPT, BUTTON_SELECT, BUTTON_NONE },
- LAST_ITEM_IN_LIST
+ LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
}; /* button_context_settings_yesnoscreen */
/* get_context_mapping returns a pointer to one of the above defined arrays depending on the context */
diff --git a/apps/keymaps/keymap-hm801.c b/apps/keymaps/keymap-hm801.c
index b69656a3f1..210f521178 100644
--- a/apps/keymaps/keymap-hm801.c
+++ b/apps/keymaps/keymap-hm801.c
@@ -154,7 +154,6 @@ static const struct button_mapping button_context_quickscreen[] = {
/** Settings - General Mappings **/
static const struct button_mapping button_context_settings[] = {
{ ACTION_STD_CANCEL, BUTTON_POWER|BUTTON_REL, BUTTON_POWER},
- { ACTION_SETTINGS_RESET, BUTTON_SELECT, BUTTON_NONE },
{ ACTION_SETTINGS_INC, BUTTON_UP, BUTTON_NONE },
{ ACTION_SETTINGS_INCREPEAT, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE },
{ ACTION_SETTINGS_DEC, BUTTON_DOWN, BUTTON_NONE },
@@ -202,7 +201,7 @@ static const struct button_mapping button_context_tree_scroll_lr[] = {
/** Yes/No Screen **/
static const struct button_mapping button_context_yesnoscreen[] = {
{ ACTION_YESNO_ACCEPT, BUTTON_SELECT, BUTTON_NONE },
- LAST_ITEM_IN_LIST
+ LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
}; /* button_context_settings_yesnoscreen */
/* get_context_mapping returns a pointer to one of the above defined arrays depending on the context */
diff --git a/apps/keymaps/keymap-ihifi.c b/apps/keymaps/keymap-ihifi.c
index 0d574c19e6..0c28f812f1 100644
--- a/apps/keymaps/keymap-ihifi.c
+++ b/apps/keymaps/keymap-ihifi.c
@@ -127,7 +127,6 @@ static const struct button_mapping button_context_quickscreen[] = {
/** Settings - General Mappings **/
static const struct button_mapping button_context_settings[] = {
- { ACTION_SETTINGS_RESET, BUTTON_PLAY, BUTTON_NONE },
{ ACTION_SETTINGS_INC, BUTTON_BWD, BUTTON_NONE },
{ ACTION_SETTINGS_INCREPEAT, BUTTON_BWD|BUTTON_REPEAT, BUTTON_NONE },
{ ACTION_SETTINGS_DEC, BUTTON_FWD, BUTTON_NONE },
@@ -175,7 +174,7 @@ static const struct button_mapping button_context_tree_scroll_lr[] = {
/** Yes/No Screen **/
static const struct button_mapping button_context_yesnoscreen[] = {
{ ACTION_YESNO_ACCEPT, BUTTON_PLAY, BUTTON_NONE },
- LAST_ITEM_IN_LIST
+ LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
}; /* button_context_settings_yesnoscreen */
/* get_context_mapping returns a pointer to one of the above defined arrays depending on the context */
diff --git a/apps/keymaps/keymap-ihifi770.c b/apps/keymaps/keymap-ihifi770.c
index c5671bd650..a5f37cdb7d 100644
--- a/apps/keymaps/keymap-ihifi770.c
+++ b/apps/keymaps/keymap-ihifi770.c
@@ -139,7 +139,6 @@ static const struct button_mapping button_context_settings[] = {
{ ACTION_STD_NEXT, BUTTON_NEXT, BUTTON_NONE },
{ ACTION_STD_NEXTREPEAT, BUTTON_NEXT|BUTTON_REPEAT, BUTTON_NONE },
{ ACTION_STD_OK, BUTTON_PLAY|BUTTON_REL, BUTTON_PLAY },
- { ACTION_SETTINGS_RESET, BUTTON_PLAY|BUTTON_REPEAT, BUTTON_NONE },
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
}; /* button_context_settings */
diff --git a/apps/keymaps/keymap-ihifi800.c b/apps/keymaps/keymap-ihifi800.c
index b56bd018eb..4dfceff965 100644
--- a/apps/keymaps/keymap-ihifi800.c
+++ b/apps/keymaps/keymap-ihifi800.c
@@ -143,7 +143,6 @@ static const struct button_mapping button_context_settings[] = {
{ ACTION_STD_NEXTREPEAT, BUTTON_NEXT|BUTTON_REPEAT, BUTTON_NONE },
{ ACTION_STD_OK, BUTTON_POWER|BUTTON_REL, BUTTON_POWER },
{ ACTION_STD_OK, BUTTON_PLAY|BUTTON_REL, BUTTON_PLAY },
- { ACTION_SETTINGS_RESET, BUTTON_PLAY|BUTTON_REPEAT, BUTTON_NONE },
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
}; /* button_context_settings */
diff --git a/apps/keymaps/keymap-ipod.c b/apps/keymaps/keymap-ipod.c
index ca52b6b216..aea0c444a2 100644
--- a/apps/keymaps/keymap-ipod.c
+++ b/apps/keymaps/keymap-ipod.c
@@ -114,7 +114,8 @@ static const struct button_mapping button_context_settings[] = {
static const struct button_mapping button_context_yesno[] = {
{ ACTION_YESNO_ACCEPT, BUTTON_SELECT, BUTTON_NONE },
- LAST_ITEM_IN_LIST
+ { ACTION_STD_CANCEL, BUTTON_PLAY, BUTTON_NONE },
+ LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
}; /* button_context_yesno */
static const struct button_mapping button_context_bmark[] = {
@@ -131,7 +132,7 @@ static const struct button_mapping button_context_quickscreen[] = {
{ ACTION_QS_LEFT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
{ ACTION_QS_RIGHT, BUTTON_RIGHT, BUTTON_NONE },
{ ACTION_QS_RIGHT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
- { ACTION_STD_CANCEL, BUTTON_SELECT, BUTTON_NONE },
+ { ACTION_STD_CANCEL, BUTTON_SELECT|BUTTON_REL, BUTTON_SELECT },
{ ACTION_QS_VOLDOWN, BUTTON_SCROLL_BACK, BUTTON_NONE },
{ ACTION_QS_VOLDOWN, BUTTON_SCROLL_BACK|BUTTON_REPEAT, BUTTON_NONE },
{ ACTION_QS_VOLUP, BUTTON_SCROLL_FWD, BUTTON_NONE },
@@ -160,7 +161,7 @@ static const struct button_mapping button_context_pitchscreen[] = {
static const struct button_mapping button_context_keyboard[] = {
{ ACTION_KBD_LEFT, BUTTON_LEFT, BUTTON_NONE },
- { ACTION_KBD_LEFT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
+ { ACTION_KBD_LEFT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
{ ACTION_KBD_RIGHT, BUTTON_RIGHT, BUTTON_NONE },
{ ACTION_KBD_RIGHT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
{ ACTION_KBD_SELECT, BUTTON_SELECT, BUTTON_NONE },
@@ -203,7 +204,7 @@ const struct button_mapping button_context_recscreen[] = {
{ ACTION_FM_STOP, BUTTON_PLAY | BUTTON_REPEAT, BUTTON_PLAY },
{ ACTION_FM_MODE, BUTTON_SELECT, BUTTON_NONE },
{ ACTION_FM_EXIT, BUTTON_MENU | BUTTON_REL, BUTTON_MENU },
- { ACTION_FM_PLAY, BUTTON_PLAY | BUTTON_REL, BUTTON_PLAY },
+ { ACTION_FM_PLAY, BUTTON_PLAY | BUTTON_REL, BUTTON_PLAY },
{ ACTION_SETTINGS_INC, BUTTON_SCROLL_FWD, BUTTON_NONE },
{ ACTION_SETTINGS_INCREPEAT,BUTTON_SCROLL_FWD|BUTTON_REPEAT, BUTTON_NONE },
{ ACTION_SETTINGS_DEC, BUTTON_SCROLL_BACK, BUTTON_NONE },
@@ -308,11 +309,20 @@ static const struct button_mapping button_context_usb_hid_mode_mouse[] = {
*****************************************************************************/
static const struct button_mapping remote_button_context_standard[] = {
+ { ACTION_STD_PREV, BUTTON_RC_UP, BUTTON_NONE },
+ { ACTION_STD_PREVREPEAT, BUTTON_RC_UP|BUTTON_REPEAT, BUTTON_NONE },
+ { ACTION_STD_NEXT, BUTTON_RC_DOWN, BUTTON_NONE },
+ { ACTION_STD_NEXTREPEAT, BUTTON_RC_DOWN|BUTTON_REPEAT, BUTTON_NONE },
+
{ ACTION_STD_PREV, BUTTON_RC_LEFT, BUTTON_NONE },
{ ACTION_STD_NEXT, BUTTON_RC_RIGHT, BUTTON_NONE },
{ ACTION_STD_CANCEL, BUTTON_RC_STOP, BUTTON_NONE },
{ ACTION_STD_OK, BUTTON_RC_PLAY, BUTTON_NONE },
+ { ACTION_STD_MENU, BUTTON_RC_MENU|BUTTON_REL, BUTTON_RC_MENU },
+ { ACTION_STD_QUICKSCREEN, BUTTON_RC_MENU|BUTTON_REPEAT, BUTTON_RC_MENU },
+ { ACTION_STD_CONTEXT, BUTTON_RC_SELECT|BUTTON_REPEAT, BUTTON_RC_SELECT },
+
LAST_ITEM_IN_LIST
}; /* remote_button_context_standard */
@@ -321,7 +331,7 @@ static const struct button_mapping remote_button_context_wps[] = {
{ ACTION_WPS_VOLDOWN, BUTTON_RC_VOL_DOWN|BUTTON_REPEAT, BUTTON_NONE },
{ ACTION_WPS_VOLUP, BUTTON_RC_VOL_UP, BUTTON_NONE },
{ ACTION_WPS_VOLUP, BUTTON_RC_VOL_UP|BUTTON_REPEAT, BUTTON_NONE },
-
+ // RC_UP and RC_DOWN?
{ ACTION_WPS_PLAY, BUTTON_RC_PLAY|BUTTON_REL, BUTTON_RC_PLAY },
{ ACTION_WPS_STOP, BUTTON_RC_PLAY|BUTTON_REPEAT, BUTTON_NONE },
{ ACTION_WPS_SKIPNEXT, BUTTON_RC_RIGHT|BUTTON_REL, BUTTON_RC_RIGHT },
@@ -330,6 +340,12 @@ static const struct button_mapping remote_button_context_wps[] = {
{ ACTION_WPS_SKIPPREV, BUTTON_RC_LEFT|BUTTON_REL, BUTTON_RC_LEFT },
{ ACTION_WPS_SEEKBACK, BUTTON_RC_LEFT|BUTTON_REPEAT, BUTTON_NONE },
{ ACTION_WPS_STOPSEEK, BUTTON_RC_LEFT|BUTTON_REL, BUTTON_RC_LEFT|BUTTON_REPEAT },
+ { ACTION_WPS_BROWSE, BUTTON_RC_SELECT|BUTTON_REL, BUTTON_RC_SELECT },
+ { ACTION_WPS_CONTEXT, BUTTON_RC_SELECT|BUTTON_REPEAT, BUTTON_RC_SELECT },
+ { ACTION_WPS_HOTKEY, BUTTON_RC_SELECT|BUTTON_PLAY, BUTTON_NONE },
+ { ACTION_WPS_MENU, BUTTON_RC_MENU|BUTTON_REL, BUTTON_RC_MENU },
+ { ACTION_WPS_QUICKSCREEN, BUTTON_RC_MENU|BUTTON_REPEAT, BUTTON_RC_MENU },
+
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
}; /* remote_button_context_wps */
@@ -337,23 +353,57 @@ static const struct button_mapping remote_button_context_wps[] = {
static const struct button_mapping remote_button_context_tree[] = {
{ ACTION_TREE_WPS, BUTTON_RC_PLAY|BUTTON_REL, BUTTON_RC_PLAY },
{ ACTION_TREE_STOP, BUTTON_RC_PLAY|BUTTON_REPEAT, BUTTON_RC_PLAY },
-
+ { ACTION_TREE_HOTKEY, BUTTON_RC_SELECT|BUTTON_PLAY, BUTTON_NONE },
+
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
}; /* remote_button_context_tree */
+static const struct button_mapping remote_button_context_quickscreen[] = {
+ { ACTION_QS_TOP, BUTTON_RC_MENU, BUTTON_NONE },
+ { ACTION_QS_TOP, BUTTON_RC_MENU|BUTTON_REPEAT, BUTTON_NONE },
+ { ACTION_QS_DOWN, BUTTON_RC_PLAY, BUTTON_NONE },
+ { ACTION_QS_DOWN, BUTTON_RC_PLAY|BUTTON_REPEAT, BUTTON_NONE },
+ { ACTION_QS_LEFT, BUTTON_RC_LEFT, BUTTON_NONE },
+ { ACTION_QS_LEFT, BUTTON_RC_LEFT|BUTTON_REPEAT, BUTTON_NONE },
+ { ACTION_QS_RIGHT, BUTTON_RC_RIGHT, BUTTON_NONE },
+ { ACTION_QS_RIGHT, BUTTON_RC_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
+ { ACTION_STD_CANCEL, BUTTON_RC_SELECT|BUTTON_REL, BUTTON_RC_SELECT },
+ { ACTION_QS_VOLDOWN, BUTTON_RC_VOL_DOWN, BUTTON_NONE },
+ { ACTION_QS_VOLDOWN, BUTTON_RC_VOL_DOWN|BUTTON_REPEAT, BUTTON_NONE },
+ { ACTION_QS_VOLUP, BUTTON_RC_VOL_UP, BUTTON_NONE },
+ { ACTION_QS_VOLUP, BUTTON_RC_VOL_UP|BUTTON_REPEAT, BUTTON_NONE },
+
+ LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
+}; /* button_context_quickscreen */
+
#if CONFIG_TUNER
static const struct button_mapping remote_button_context_radio[] = {
+ { ACTION_FM_MENU, BUTTON_RC_SELECT | BUTTON_REPEAT, BUTTON_NONE },
{ ACTION_FM_STOP, BUTTON_RC_PLAY | BUTTON_REPEAT, BUTTON_NONE },
- { ACTION_FM_PLAY, BUTTON_RC_PLAY | BUTTON_REL, BUTTON_RC_PLAY },
+ { ACTION_FM_MODE, BUTTON_RC_SELECT, BUTTON_NONE },
+ { ACTION_FM_EXIT, BUTTON_RC_MENU | BUTTON_REL, BUTTON_RC_MENU },
+
+ { ACTION_FM_PLAY, BUTTON_RC_PLAY | BUTTON_REL, BUTTON_RC_PLAY },
+ { ACTION_SETTINGS_INC, BUTTON_RC_UP, BUTTON_NONE },
+ { ACTION_SETTINGS_INCREPEAT,BUTTON_RC_UP|BUTTON_REPEAT, BUTTON_NONE },
+ { ACTION_SETTINGS_DEC, BUTTON_RC_DOWN, BUTTON_NONE },
+ { ACTION_SETTINGS_DECREPEAT,BUTTON_RC_DOWN|BUTTON_REPEAT,BUTTON_NONE },
+
{ ACTION_STD_NEXT, BUTTON_RC_RIGHT|BUTTON_REL, BUTTON_RC_RIGHT },
{ ACTION_STD_NEXTREPEAT, BUTTON_RC_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
{ ACTION_STD_PREV, BUTTON_RC_LEFT|BUTTON_REL, BUTTON_RC_LEFT },
{ ACTION_STD_PREVREPEAT, BUTTON_RC_LEFT|BUTTON_REPEAT, BUTTON_NONE },
-
+
LAST_ITEM_IN_LIST
}; /* remote_button_context_radio */
#endif
+static const struct button_mapping remote_button_context_yesno[] = {
+ { ACTION_YESNO_ACCEPT, BUTTON_RC_SELECT, BUTTON_NONE },
+ { ACTION_STD_CANCEL, BUTTON_RC_PLAY, BUTTON_NONE },
+ LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
+}; /* button_context_yesno */
+
static const struct button_mapping* get_context_mapping_remote( int context )
{
context ^= CONTEXT_REMOTE;
@@ -363,13 +413,18 @@ static const struct button_mapping* get_context_mapping_remote( int context )
case CONTEXT_WPS:
return remote_button_context_wps;
case CONTEXT_TREE:
+ case CONTEXT_MAINMENU:
case CONTEXT_CUSTOM|CONTEXT_TREE:
return remote_button_context_tree;
+ case CONTEXT_QUICKSCREEN:
+ return remote_button_context_quickscreen;
#if CONFIG_TUNER
case CONTEXT_FM:
return remote_button_context_radio;
#endif
+ case CONTEXT_YESNOSCREEN:
+ return remote_button_context_yesno;
default:
return remote_button_context_standard;
}
diff --git a/apps/keymaps/keymap-m200.c b/apps/keymaps/keymap-m200.c
index f055d648ef..be202d21e6 100644
--- a/apps/keymaps/keymap-m200.c
+++ b/apps/keymaps/keymap-m200.c
@@ -89,7 +89,6 @@ static const struct button_mapping button_context_wps[] = {
static const struct button_mapping button_context_settings[] = {
{ ACTION_STD_CANCEL, BUTTON_POWER, BUTTON_NONE },
- { ACTION_SETTINGS_RESET, BUTTON_SELECT, BUTTON_NONE },
{ ACTION_SETTINGS_INC, BUTTON_UP, BUTTON_NONE },
{ ACTION_SETTINGS_INCREPEAT,BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE },
diff --git a/apps/keymaps/keymap-m3.c b/apps/keymaps/keymap-m3.c
index 6192d7ff6b..bcd96d664b 100644
--- a/apps/keymaps/keymap-m3.c
+++ b/apps/keymaps/keymap-m3.c
@@ -379,12 +379,12 @@ static const struct button_mapping remote_button_context_wps[] = {
/** Yes/No Screen **/
static const struct button_mapping button_context_yesnoscreen[] = {
{ ACTION_YESNO_ACCEPT, BUTTON_PLAY, BUTTON_NONE },
- LAST_ITEM_IN_LIST
+ LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
}; /* button_context_yesnoscreen */
static const struct button_mapping remote_button_context_yesnoscreen[] = {
{ ACTION_YESNO_ACCEPT, BUTTON_RC_PLAY, BUTTON_NONE },
- LAST_ITEM_IN_LIST
+ LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
}; /* remote_button_context_yesnoscreen */
diff --git a/apps/keymaps/keymap-ma.c b/apps/keymaps/keymap-ma.c
index e1740b697b..aca006d071 100644
--- a/apps/keymaps/keymap-ma.c
+++ b/apps/keymaps/keymap-ma.c
@@ -135,7 +135,6 @@ static const struct button_mapping button_context_quickscreen[] = {
/** Settings - General Mappings **/
static const struct button_mapping button_context_settings[] = {
- { ACTION_SETTINGS_RESET, BUTTON_PLAY, BUTTON_NONE },
{ ACTION_SETTINGS_INC, BUTTON_UP, BUTTON_NONE },
{ ACTION_SETTINGS_INCREPEAT, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE },
{ ACTION_SETTINGS_DEC, BUTTON_DOWN, BUTTON_NONE },
@@ -183,13 +182,13 @@ static const struct button_mapping button_context_tree_scroll_lr[] = {
/** Yes/No Screen **/
static const struct button_mapping button_context_yesnoscreen[] = {
{ ACTION_YESNO_ACCEPT, BUTTON_PLAY, BUTTON_NONE },
- LAST_ITEM_IN_LIST
+ LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
}; /* button_context_settings_yesnoscreen */
/* get_context_mapping returns a pointer to one of the above defined arrays depending on the context */
const struct button_mapping* get_context_mapping(int context)
{
- switch (context)
+ switch (context & ~CONTEXT_LOCKED)
{
/* anything that uses button_context_standard */
case CONTEXT_LIST:
diff --git a/apps/keymaps/keymap-meizu-m6sl.c b/apps/keymaps/keymap-meizu-m6sl.c
index 35e44da905..31dbc88432 100644
--- a/apps/keymaps/keymap-meizu-m6sl.c
+++ b/apps/keymaps/keymap-meizu-m6sl.c
@@ -147,7 +147,6 @@ static const struct button_mapping button_context_settings[] = {
{ ACTION_STD_PREVREPEAT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
{ ACTION_STD_NEXT, BUTTON_RIGHT, BUTTON_NONE },
{ ACTION_STD_NEXTREPEAT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
- { ACTION_SETTINGS_RESET, BUTTON_PLAY, BUTTON_NONE },
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
}; /* button_context_settings */
@@ -161,7 +160,6 @@ static const struct button_mapping button_context_settings_right_is_inc[] = {
{ ACTION_STD_PREVREPEAT, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE },
{ ACTION_STD_NEXT, BUTTON_DOWN, BUTTON_NONE },
{ ACTION_STD_NEXTREPEAT, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE },
- { ACTION_SETTINGS_RESET, BUTTON_PLAY, BUTTON_NONE },
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
}; /* button_context_settingsgraphical */
diff --git a/apps/keymaps/keymap-mini2440.c b/apps/keymaps/keymap-mini2440.c
index 70f63aef72..3fe0698fa0 100644
--- a/apps/keymaps/keymap-mini2440.c
+++ b/apps/keymaps/keymap-mini2440.c
@@ -162,7 +162,6 @@ static const struct button_mapping button_context_settings[] = {
{ ACTION_STD_PREVREPEAT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
{ ACTION_STD_NEXT, BUTTON_RIGHT, BUTTON_NONE },
{ ACTION_STD_NEXTREPEAT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
- { ACTION_SETTINGS_RESET, BUTTON_A, BUTTON_NONE },
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
}; /* button_context_settings */
@@ -176,7 +175,6 @@ static const struct button_mapping button_context_settings_right_is_inc[] = {
{ ACTION_STD_PREVREPEAT, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE },
{ ACTION_STD_NEXT, BUTTON_DOWN, BUTTON_NONE },
{ ACTION_STD_NEXTREPEAT, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE },
- { ACTION_SETTINGS_RESET, BUTTON_A, BUTTON_NONE },
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
}; /* button_context_settingsgraphical */
diff --git a/apps/keymaps/keymap-mpio-hd200.c b/apps/keymaps/keymap-mpio-hd200.c
index 71b3190384..019d4bfac4 100644
--- a/apps/keymaps/keymap-mpio-hd200.c
+++ b/apps/keymaps/keymap-mpio-hd200.c
@@ -105,8 +105,10 @@ static const struct button_mapping button_context_settings[] = {
static const struct button_mapping button_context_yesno[] = {
{ ACTION_YESNO_ACCEPT, BUTTON_FUNC, BUTTON_NONE },
{ ACTION_YESNO_ACCEPT, BUTTON_PLAY, BUTTON_NONE },
+ { ACTION_STD_CANCEL, BUTTON_VOL_UP, BUTTON_NONE },
+ { ACTION_STD_CANCEL, BUTTON_VOL_DOWN, BUTTON_NONE },
- LAST_ITEM_IN_LIST
+ LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
}; /* button_context_yesno */
static const struct button_mapping button_context_bmark[] = {
@@ -269,7 +271,7 @@ static const struct button_mapping button_rc_context_yesno[] = {
{ ACTION_YESNO_ACCEPT, BUTTON_RC_FUNC, BUTTON_NONE },
{ ACTION_YESNO_ACCEPT, BUTTON_RC_PLAY, BUTTON_NONE },
- LAST_ITEM_IN_LIST
+ LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
}; /* button_context_yesno */
static const struct button_mapping button_rc_context_radio[] = {
diff --git a/apps/keymaps/keymap-mpio-hd300.c b/apps/keymaps/keymap-mpio-hd300.c
index cdb617bed0..7941d1704b 100644
--- a/apps/keymaps/keymap-mpio-hd300.c
+++ b/apps/keymaps/keymap-mpio-hd300.c
@@ -109,8 +109,11 @@ static const struct button_mapping button_context_settings[] = {
static const struct button_mapping button_context_yesno[] = {
{ ACTION_YESNO_ACCEPT, BUTTON_ENTER, BUTTON_NONE },
{ ACTION_YESNO_ACCEPT, BUTTON_PLAY, BUTTON_NONE },
+ { ACTION_STD_CANCEL, BUTTON_REW, BUTTON_NONE },
+ { ACTION_STD_CANCEL, BUTTON_FF, BUTTON_NONE },
+ { ACTION_STD_CANCEL, BUTTON_REC, BUTTON_NONE },
- LAST_ITEM_IN_LIST
+ LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
}; /* button_context_yesno */
static const struct button_mapping button_context_bmark[] = {
diff --git a/apps/keymaps/keymap-mr100.c b/apps/keymaps/keymap-mr100.c
index d551d434be..0a677efc00 100644
--- a/apps/keymaps/keymap-mr100.c
+++ b/apps/keymaps/keymap-mr100.c
@@ -153,7 +153,6 @@ static const struct button_mapping button_context_settings[] = {
{ ACTION_STD_PREVREPEAT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
{ ACTION_STD_NEXT, BUTTON_RIGHT, BUTTON_NONE },
{ ACTION_STD_NEXTREPEAT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
- { ACTION_SETTINGS_RESET, BUTTON_DISPLAY, BUTTON_NONE },
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
}; /* button_context_settings */
@@ -167,7 +166,6 @@ static const struct button_mapping button_context_settings_right_is_inc[] = {
{ ACTION_STD_PREVREPEAT, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE },
{ ACTION_STD_NEXT, BUTTON_DOWN, BUTTON_NONE },
{ ACTION_STD_NEXTREPEAT, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE },
- { ACTION_SETTINGS_RESET, BUTTON_DISPLAY, BUTTON_NONE },
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
}; /* button_context_settingsgraphical */
diff --git a/apps/keymaps/keymap-mr500.c b/apps/keymaps/keymap-mr500.c
index b35c9c1dfc..41e6d21827 100644
--- a/apps/keymaps/keymap-mr500.c
+++ b/apps/keymaps/keymap-mr500.c
@@ -118,7 +118,6 @@ static const struct button_mapping button_context_settings[] = {
{ ACTION_STD_PREVREPEAT, BUTTON_RC_REW|BUTTON_REPEAT, BUTTON_NONE },
{ ACTION_STD_NEXT, BUTTON_RC_FF, BUTTON_NONE },
{ ACTION_STD_NEXTREPEAT, BUTTON_RC_FF|BUTTON_REPEAT, BUTTON_NONE },
- { ACTION_SETTINGS_RESET, BUTTON_RC_MODE, BUTTON_NONE },
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
}; /* button_context_settings */
@@ -132,7 +131,6 @@ static const struct button_mapping button_context_settings_right_is_inc[] = {
{ ACTION_STD_PREVREPEAT, BUTTON_RC_PLAY|BUTTON_REPEAT, BUTTON_NONE },
{ ACTION_STD_NEXT, BUTTON_RC_DOWN, BUTTON_NONE },
{ ACTION_STD_NEXTREPEAT, BUTTON_RC_DOWN|BUTTON_REPEAT, BUTTON_NONE },
- { ACTION_SETTINGS_RESET, BUTTON_RC_MODE, BUTTON_NONE },
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
}; /* button_context_settingsgraphical */
diff --git a/apps/keymaps/keymap-nwz.c b/apps/keymaps/keymap-nwz.c
index 66bdd1b0db..36675566d3 100644
--- a/apps/keymaps/keymap-nwz.c
+++ b/apps/keymaps/keymap-nwz.c
@@ -112,7 +112,8 @@ static const struct button_mapping button_context_keyboard[] = {
}; /* button_context_keyboard */
static const struct button_mapping button_context_quickscreen[] = {
- { ACTION_STD_CANCEL, BUTTON_PLAY, BUTTON_NONE },
+ { ACTION_STD_CONTEXT, BUTTON_PLAY|BUTTON_REPEAT, BUTTON_PLAY },
+ { ACTION_STD_CANCEL, BUTTON_PLAY|BUTTON_REL, BUTTON_PLAY },
{ ACTION_STD_CANCEL, BUTTON_BACK, BUTTON_NONE },
{ ACTION_STD_CANCEL, BUTTON_POWER, BUTTON_NONE },
{ ACTION_QS_TOP, BUTTON_UP, BUTTON_NONE },
@@ -199,7 +200,6 @@ static const struct button_mapping button_context_settings[] = {
{ ACTION_STD_NEXTREPEAT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
{ ACTION_STD_OK, BUTTON_PLAY|BUTTON_REL, BUTTON_PLAY },
- { ACTION_SETTINGS_RESET, BUTTON_PLAY|BUTTON_REPEAT, BUTTON_NONE },
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
}; /* button_context_settings */
@@ -357,7 +357,7 @@ static const struct button_mapping button_context_usb_hid_mode_mouse[] = {
/* get_context_mapping returns a pointer to one of the above defined arrays depending on the context */
const struct button_mapping* get_context_mapping(int context)
{
- switch (context)
+ switch (context & ~CONTEXT_LOCKED)
{
case CONTEXT_STD:
return button_context_standard;
diff --git a/apps/keymaps/keymap-nwza860.c b/apps/keymaps/keymap-nwza860.c
index f1a3bb6f85..899796552e 100644
--- a/apps/keymaps/keymap-nwza860.c
+++ b/apps/keymaps/keymap-nwza860.c
@@ -74,7 +74,8 @@ static const struct button_mapping button_context_keyboard[] = {
}; /* button_context_keyboard */
static const struct button_mapping button_context_quickscreen[] = {
- { ACTION_STD_CANCEL, BUTTON_PLAY, BUTTON_NONE },
+ { ACTION_STD_CONTEXT, BUTTON_PLAY|BUTTON_REPEAT, BUTTON_PLAY },
+ { ACTION_STD_CANCEL, BUTTON_PLAY|BUTTON_REL, BUTTON_PLAY },
{ ACTION_STD_CANCEL, BUTTON_BACK, BUTTON_NONE },
LAST_ITEM_IN_LIST
@@ -125,8 +126,6 @@ static const struct button_mapping button_context_settings[] = {
{ ACTION_SETTINGS_DEC, BUTTON_VOL_DOWN, BUTTON_NONE },
{ ACTION_SETTINGS_DECREPEAT, BUTTON_VOL_DOWN|BUTTON_REPEAT, BUTTON_NONE },
- { ACTION_SETTINGS_RESET, BUTTON_PLAY|BUTTON_REPEAT, BUTTON_NONE },
-
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
}; /* button_context_settings */
diff --git a/apps/keymaps/keymap-ondavx777.c b/apps/keymaps/keymap-ondavx777.c
index ed23350666..c4b68f1ef2 100644
--- a/apps/keymaps/keymap-ondavx777.c
+++ b/apps/keymaps/keymap-ondavx777.c
@@ -127,7 +127,7 @@ static const struct button_mapping button_context_usb_hid[] = {
const struct button_mapping* target_get_context_mapping(int context)
{
- switch (context)
+ switch (context & ~CONTEXT_LOCKED)
{
case CONTEXT_STD:
return button_context_standard;
diff --git a/apps/keymaps/keymap-rk27xx-generic.c b/apps/keymaps/keymap-rk27xx-generic.c
index d837c608fc..4c07384810 100644
--- a/apps/keymaps/keymap-rk27xx-generic.c
+++ b/apps/keymaps/keymap-rk27xx-generic.c
@@ -164,7 +164,7 @@ static const struct button_mapping button_context_usb_hid_mode_mouse[] = {
/* get_context_mapping returns a pointer to one of the above defined arrays depending on the context */
const struct button_mapping* get_context_mapping(int context)
{
- switch (context)
+ switch (context & ~CONTEXT_LOCKED)
{
case CONTEXT_STD:
return button_context_standard;
diff --git a/apps/keymaps/keymap-sa9200.c b/apps/keymaps/keymap-sa9200.c
index d29557e222..2dcf301f0f 100644
--- a/apps/keymaps/keymap-sa9200.c
+++ b/apps/keymaps/keymap-sa9200.c
@@ -155,7 +155,6 @@ static const struct button_mapping button_context_listtree_scroll_without_combo[
static const struct button_mapping button_context_settings[] = {
{ ACTION_STD_CANCEL, BUTTON_POWER, BUTTON_NONE },
- { ACTION_SETTINGS_RESET, BUTTON_PLAY, BUTTON_NONE },
{ ACTION_SETTINGS_INC, BUTTON_UP, BUTTON_NONE },
{ ACTION_SETTINGS_INCREPEAT,BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE },
diff --git a/apps/keymaps/keymap-sansa-connect.c b/apps/keymaps/keymap-sansa-connect.c
index 27790d2bfd..b339744b0f 100644
--- a/apps/keymaps/keymap-sansa-connect.c
+++ b/apps/keymaps/keymap-sansa-connect.c
@@ -70,9 +70,26 @@ static const struct button_mapping button_context_wps[] = {
static const struct button_mapping button_context_yesno[] = {
{ACTION_YESNO_ACCEPT, BUTTON_SELECT, BUTTON_NONE},
- LAST_ITEM_IN_LIST
+ {ACTION_STD_CANCEL, BUTTON_PREV, BUTTON_NONE},
+ {ACTION_STD_CANCEL, BUTTON_NEXT, BUTTON_NONE},
+ {ACTION_STD_CANCEL, BUTTON_VOL_UP, BUTTON_NONE},
+ {ACTION_STD_CANCEL, BUTTON_VOL_DOWN, BUTTON_NONE},
+ {ACTION_STD_CANCEL, BUTTON_POWER, BUTTON_NONE},
+ LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
}; /* button_context_yesno */
+static const struct button_mapping button_context_settings_time[] = {
+ {ACTION_STD_PREV, BUTTON_PREV|BUTTON_REL, BUTTON_PREV},
+ {ACTION_STD_PREVREPEAT, BUTTON_PREV|BUTTON_REPEAT, BUTTON_PREV},
+ {ACTION_STD_NEXT, BUTTON_NEXT|BUTTON_REL, BUTTON_NEXT},
+ {ACTION_STD_NEXTREPEAT, BUTTON_NEXT|BUTTON_REPEAT, BUTTON_NEXT},
+ {ACTION_STD_CANCEL, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_LEFT},
+ {ACTION_STD_OK, BUTTON_SELECT|BUTTON_REL, BUTTON_SELECT},
+ {ACTION_SETTINGS_INC, BUTTON_SCROLL_FWD, BUTTON_NONE},
+ {ACTION_SETTINGS_DEC, BUTTON_SCROLL_BACK, BUTTON_NONE},
+ LAST_ITEM_IN_LIST
+}; /* button_context_settings_time */
+
static const struct button_mapping button_context_keyboard[] = {
{ACTION_KBD_LEFT, BUTTON_LEFT, BUTTON_NONE},
{ACTION_KBD_LEFT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE},
@@ -113,7 +130,9 @@ const struct button_mapping* get_context_mapping(int context)
case CONTEXT_KEYBOARD:
case CONTEXT_MORSE_INPUT:
return button_context_keyboard;
-
+ case CONTEXT_SETTINGS_TIME:
+ return button_context_settings_time;
+
case CONTEXT_TREE:
case CONTEXT_LIST:
case CONTEXT_MAINMENU:
diff --git a/apps/keymaps/keymap-shanlingq1.c b/apps/keymaps/keymap-shanlingq1.c
new file mode 100644
index 0000000000..f8fefe5ff3
--- /dev/null
+++ b/apps/keymaps/keymap-shanlingq1.c
@@ -0,0 +1,88 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+/* Button Code Definitions for Shanling Q1 target */
+
+#include "config.h"
+#include "action.h"
+#include "button.h"
+#include "settings.h"
+
+/* {Action Code, Button code, Prereq button code } */
+
+static const struct button_mapping button_context_standard[] = {
+ {ACTION_STD_PREV, BUTTON_PREV, BUTTON_NONE},
+ {ACTION_STD_PREVREPEAT, BUTTON_PREV|BUTTON_REPEAT, BUTTON_NONE},
+ {ACTION_STD_NEXT, BUTTON_NEXT, BUTTON_NONE},
+ {ACTION_STD_NEXTREPEAT, BUTTON_NEXT|BUTTON_REPEAT, BUTTON_NONE},
+ {ACTION_STD_OK, BUTTON_PLAY|BUTTON_REL, BUTTON_PLAY},
+ {ACTION_STD_CONTEXT, BUTTON_PLAY|BUTTON_REPEAT, BUTTON_PLAY},
+ {ACTION_STD_CANCEL, BUTTON_POWER|BUTTON_REL, BUTTON_POWER},
+ LAST_ITEM_IN_LIST
+}; /* button_context_standard */
+
+static const struct button_mapping button_context_wps[] = {
+ {ACTION_WPS_PLAY, BUTTON_PLAY|BUTTON_REL, BUTTON_PLAY},
+ {ACTION_WPS_STOP, BUTTON_PLAY|BUTTON_REPEAT, BUTTON_NONE},
+ {ACTION_WPS_VOLUP, BUTTON_VOL_UP|BUTTON_REL, BUTTON_NONE},
+ {ACTION_WPS_VOLDOWN, BUTTON_VOL_DOWN|BUTTON_REL, BUTTON_NONE},
+ {ACTION_WPS_SKIPNEXT, BUTTON_NEXT|BUTTON_REL, BUTTON_NEXT},
+ {ACTION_WPS_SKIPPREV, BUTTON_PREV|BUTTON_REL, BUTTON_PREV},
+ {ACTION_WPS_SEEKFWD, BUTTON_NEXT|BUTTON_REPEAT, BUTTON_NONE},
+ {ACTION_WPS_STOPSEEK, BUTTON_NEXT|BUTTON_REL, BUTTON_NEXT|BUTTON_REPEAT},
+ {ACTION_WPS_SEEKBACK, BUTTON_PREV|BUTTON_REPEAT, BUTTON_NONE},
+ {ACTION_WPS_STOPSEEK, BUTTON_PREV|BUTTON_REL, BUTTON_PREV|BUTTON_REPEAT},
+ {ACTION_STD_KEYLOCK, BUTTON_POWER|BUTTON_REL, BUTTON_POWER},
+ LAST_ITEM_IN_LIST
+}; /* button_context_wps */
+
+static const struct button_mapping button_context_list[] = {
+ {ACTION_LIST_VOLUP, BUTTON_VOL_UP|BUTTON_REL, BUTTON_NONE},
+ {ACTION_LIST_VOLDOWN, BUTTON_VOL_DOWN|BUTTON_REL, BUTTON_NONE},
+ LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
+}; /* button_context_list */
+
+static const struct button_mapping button_context_yesno[] = {
+ /* note: touchscreen buttons are usable in addition to physical keys */
+ {ACTION_YESNO_ACCEPT, BUTTON_PLAY, BUTTON_NONE},
+ {ACTION_STD_CANCEL, BUTTON_POWER, BUTTON_NONE},
+ LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
+}; /* button_context_yesno */
+
+const struct button_mapping* target_get_context_mapping(int context)
+{
+ switch (context & ~CONTEXT_LOCKED)
+ {
+ default:
+ case CONTEXT_STD:
+ return button_context_standard;
+ case CONTEXT_WPS:
+ return button_context_wps;
+ case CONTEXT_TREE:
+ case CONTEXT_CUSTOM|CONTEXT_TREE:
+ case CONTEXT_MAINMENU:
+ case CONTEXT_BOOKMARKSCREEN:
+ case CONTEXT_LIST:
+ return button_context_list;
+ case CONTEXT_YESNOSCREEN:
+ return button_context_yesno;
+ }
+}
diff --git a/apps/keymaps/keymap-touchscreen.c b/apps/keymaps/keymap-touchscreen.c
index 6163cb054c..07710be78d 100644
--- a/apps/keymaps/keymap-touchscreen.c
+++ b/apps/keymaps/keymap-touchscreen.c
@@ -146,7 +146,6 @@ static const struct button_mapping button_context_settings[] = {
{ ACTION_SETTINGS_INCREPEAT, BUTTON_TOPMIDDLE|BUTTON_REPEAT, BUTTON_NONE },
{ ACTION_SETTINGS_DEC, BUTTON_BOTTOMMIDDLE, BUTTON_NONE },
{ ACTION_SETTINGS_DECREPEAT, BUTTON_BOTTOMMIDDLE|BUTTON_REPEAT, BUTTON_NONE },
- { ACTION_SETTINGS_RESET, BUTTON_CENTER, BUTTON_NONE },
{ ACTION_STD_PREV, BUTTON_MIDLEFT, BUTTON_NONE },
{ ACTION_STD_PREVREPEAT, BUTTON_MIDLEFT|BUTTON_REPEAT, BUTTON_NONE },
@@ -164,7 +163,6 @@ static const struct button_mapping button_context_settings_right_is_inc[] = {
{ ACTION_SETTINGS_INCREPEAT, BUTTON_MIDRIGHT|BUTTON_REPEAT, BUTTON_NONE },
{ ACTION_SETTINGS_DEC, BUTTON_MIDLEFT, BUTTON_NONE },
{ ACTION_SETTINGS_DECREPEAT, BUTTON_MIDLEFT|BUTTON_REPEAT, BUTTON_NONE },
- { ACTION_SETTINGS_RESET, BUTTON_CENTER, BUTTON_NONE },
{ ACTION_STD_CANCEL, BUTTON_TOPLEFT, BUTTON_NONE },
@@ -179,8 +177,6 @@ static const struct button_mapping button_context_mainmenu[] =
static const struct button_mapping button_context_yesno[] = {
- { ACTION_YESNO_ACCEPT, BUTTON_TOPRIGHT, BUTTON_NONE },
- { ACTION_YESNO_ACCEPT, BUTTON_BOTTOMLEFT, BUTTON_NONE },
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_CUSTOM2|CONTEXT_YESNOSCREEN)
}; /* button_context_settings_yesno */
@@ -387,7 +383,7 @@ const struct button_mapping* get_context_mapping(int context)
return target_get_context_mapping(context & ~CONTEXT_CUSTOM2);
}
- switch (context)
+ switch (context & ~CONTEXT_LOCKED)
{
case CONTEXT_STD:
return button_context_standard;
diff --git a/apps/keymaps/keymap-vibe500.c b/apps/keymaps/keymap-vibe500.c
index 49f77628ce..5116ec8575 100644
--- a/apps/keymaps/keymap-vibe500.c
+++ b/apps/keymaps/keymap-vibe500.c
@@ -113,7 +113,9 @@ static const struct button_mapping button_context_settings[] = {
static const struct button_mapping button_context_yesno[] = {
{ ACTION_YESNO_ACCEPT, BUTTON_OK, BUTTON_NONE },
- LAST_ITEM_IN_LIST
+ { ACTION_STD_CANCEL, BUTTON_PLAY, BUTTON_NONE },
+ { ACTION_STD_CANCEL, BUTTON_REC, BUTTON_NONE },
+ LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
}; /* button_context_yesno */
static const struct button_mapping button_context_bmark[] = {
diff --git a/apps/keymaps/keymap-x5.c b/apps/keymaps/keymap-x5.c
index fb2fbfa605..249df7bf3c 100644
--- a/apps/keymaps/keymap-x5.c
+++ b/apps/keymaps/keymap-x5.c
@@ -405,12 +405,12 @@ static const struct button_mapping remote_button_context_wps[] = {
/** Yes/No Screen **/
static const struct button_mapping button_context_yesnoscreen[] = {
{ ACTION_YESNO_ACCEPT, BUTTON_SELECT, BUTTON_NONE },
- LAST_ITEM_IN_LIST
+ LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
}; /* button_context_settings_yesnoscreen */
static const struct button_mapping remote_button_context_yesnoscreen[] = {
{ ACTION_YESNO_ACCEPT, BUTTON_RC_PLAY, BUTTON_NONE },
- LAST_ITEM_IN_LIST
+ LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
}; /* remote_button_context_settings_yesnoscreen */
diff --git a/apps/keymaps/keymap-xduoox20.c b/apps/keymaps/keymap-xduoox20.c
index c49410cd03..fa4076e27d 100644
--- a/apps/keymaps/keymap-xduoox20.c
+++ b/apps/keymaps/keymap-xduoox20.c
@@ -145,7 +145,6 @@ static const struct button_mapping button_context_quickscreen[] = {
/** Settings - General Mappings **/
static const struct button_mapping button_context_settings[] = {
- { ACTION_SETTINGS_RESET, BUTTON_POWER|BUTTON_REL, BUTTON_POWER },
{ ACTION_STD_PREV, BUTTON_NEXT, BUTTON_NONE },
{ ACTION_STD_PREVREPEAT, BUTTON_NEXT|BUTTON_REPEAT, BUTTON_NONE },
{ ACTION_STD_NEXT, BUTTON_PREV, BUTTON_NONE },
@@ -185,7 +184,7 @@ static const struct button_mapping button_context_yesnoscreen[] = {
/* get_context_mapping returns a pointer to one of the above defined arrays depending on the context */
const struct button_mapping* get_context_mapping(int context)
{
- switch (context)
+ switch (context & ~CONTEXT_LOCKED)
{
case CONTEXT_LIST:
return button_context_list;
diff --git a/apps/keymaps/keymap-xduoox3.c b/apps/keymaps/keymap-xduoox3.c
index 124ca06c9d..bf588411eb 100644
--- a/apps/keymaps/keymap-xduoox3.c
+++ b/apps/keymaps/keymap-xduoox3.c
@@ -86,10 +86,12 @@ static const struct button_mapping button_context_wps[] = {
{ ACTION_WPS_VOLDOWN, BUTTON_VOL_DOWN|BUTTON_REPEAT, BUTTON_NONE },
{ ACTION_WPS_BROWSE, BUTTON_HOME|BUTTON_REL, BUTTON_HOME },
{ ACTION_WPS_CONTEXT, BUTTON_PLAY|BUTTON_REPEAT, BUTTON_PLAY },
- { ACTION_WPS_MENU, BUTTON_OPTION|BUTTON_REL, BUTTON_OPTION },
- { ACTION_WPS_QUICKSCREEN, BUTTON_OPTION|BUTTON_REPEAT, BUTTON_OPTION },
{ ACTION_WPS_HOTKEY, BUTTON_HOME|BUTTON_REPEAT, BUTTON_HOME },
+ { ACTION_WPS_MENU, BUTTON_OPTION|BUTTON_REL, BUTTON_OPTION },
+ { ACTION_WPS_QUICKSCREEN, BUTTON_OPTION|BUTTON_REPEAT, BUTTON_OPTION },
+ { ACTION_WPS_PITCHSCREEN, BUTTON_OPTION|BUTTON_HOME|BUTTON_REPEAT, BUTTON_NONE },
+
{ ACTION_WPS_ABSETB_NEXTDIR, BUTTON_PWRALT|BUTTON_NEXT, BUTTON_POWER },
{ ACTION_WPS_ABSETA_PREVDIR, BUTTON_PWRALT|BUTTON_PREV, BUTTON_POWER },
{ ACTION_WPS_ABRESET, BUTTON_PWRALT|BUTTON_PLAY, BUTTON_POWER },
@@ -172,7 +174,6 @@ static const struct button_mapping button_context_quickscreen[] = {
/** Settings - General Mappings **/
static const struct button_mapping button_context_settings[] = {
- { ACTION_SETTINGS_RESET, BUTTON_POWER|BUTTON_REL, BUTTON_POWER },
{ ACTION_STD_PREV, BUTTON_PREV, BUTTON_NONE },
{ ACTION_STD_PREVREPEAT, BUTTON_PREV|BUTTON_REPEAT, BUTTON_NONE },
{ ACTION_STD_NEXT, BUTTON_NEXT, BUTTON_NONE },
diff --git a/apps/keymaps/keymap-xduoox3ii.c b/apps/keymaps/keymap-xduoox3ii.c
index c8be8ce13e..c8724ec1f0 100644
--- a/apps/keymaps/keymap-xduoox3ii.c
+++ b/apps/keymaps/keymap-xduoox3ii.c
@@ -145,7 +145,6 @@ static const struct button_mapping button_context_quickscreen[] = {
/** Settings - General Mappings **/
static const struct button_mapping button_context_settings[] = {
- { ACTION_SETTINGS_RESET, BUTTON_POWER|BUTTON_REL, BUTTON_POWER },
{ ACTION_STD_PREV, BUTTON_NEXT, BUTTON_NONE },
{ ACTION_STD_PREVREPEAT, BUTTON_NEXT|BUTTON_REPEAT, BUTTON_NONE },
{ ACTION_STD_NEXT, BUTTON_PREV, BUTTON_NONE },
@@ -185,7 +184,7 @@ static const struct button_mapping button_context_yesnoscreen[] = {
/* get_context_mapping returns a pointer to one of the above defined arrays depending on the context */
const struct button_mapping* get_context_mapping(int context)
{
- switch (context)
+ switch (context & ~CONTEXT_LOCKED)
{
case CONTEXT_LIST:
return button_context_list;
diff --git a/apps/keymaps/keymap-yh8xx_yh9xx.c b/apps/keymaps/keymap-yh8xx_yh9xx.c
index 32623e1a72..9332c9fe98 100644
--- a/apps/keymaps/keymap-yh8xx_yh9xx.c
+++ b/apps/keymaps/keymap-yh8xx_yh9xx.c
@@ -201,7 +201,6 @@ static const struct button_mapping button_context_settings_right_is_inc[] = {
{ ACTION_SETTINGS_INCREPEAT,BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
{ ACTION_SETTINGS_DEC, BUTTON_LEFT, BUTTON_NONE },
{ ACTION_SETTINGS_DECREPEAT,BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
- { ACTION_SETTINGS_RESET, BUTTON_REW|BUTTON_REL, BUTTON_REW },
{ ACTION_STD_PREV, BUTTON_UP, BUTTON_NONE },
{ ACTION_STD_PREVREPEAT, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE },
diff --git a/apps/keymaps/keymap-ypr0.c b/apps/keymaps/keymap-ypr0.c
index 2d6be5b7ec..6e493ddf61 100644
--- a/apps/keymaps/keymap-ypr0.c
+++ b/apps/keymaps/keymap-ypr0.c
@@ -255,7 +255,7 @@ static const struct button_mapping button_context_radio[] = {
const struct button_mapping* get_context_mapping(int context)
{
- switch (context)
+ switch (context & ~CONTEXT_LOCKED)
{
case CONTEXT_STD:
return button_context_standard;
diff --git a/apps/keymaps/keymap-ypr1.c b/apps/keymaps/keymap-ypr1.c
index d47047912b..67f42f07c6 100644
--- a/apps/keymaps/keymap-ypr1.c
+++ b/apps/keymaps/keymap-ypr1.c
@@ -132,7 +132,7 @@ static const struct button_mapping button_context_radio[] = {
const struct button_mapping* target_get_context_mapping(int context)
{
- switch (context)
+ switch (context & ~CONTEXT_LOCKED)
{
case CONTEXT_STD:
return button_context_standard;
diff --git a/apps/keymaps/keymap-ypz5.c b/apps/keymaps/keymap-ypz5.c
deleted file mode 100644
index edeebbac3f..0000000000
--- a/apps/keymaps/keymap-ypz5.c
+++ /dev/null
@@ -1,406 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id$
- *
- * Copyright (C) 2013 Lorenzo Miori
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ****************************************************************************/
-
-/* Button Code Definitions for the Creative Zen Vision target */
-/* Copied from ZVM target for now... */
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-
-#include "config.h"
-#include "action.h"
-#include "button.h"
-#include "settings.h"
-
-/*
- * The format of the list is as follows
- * { Action Code, Button code, Prereq button code }
- * if there's no need to check the previous button's value, use BUTTON_NONE
- * Insert LAST_ITEM_IN_LIST at the end of each mapping
- */
-
-/* CONTEXT_CUSTOM's used in this file...
-
-CONTEXT_CUSTOM|CONTEXT_TREE = the standard list/tree defines (without directions)
-CONTEXT_CUSTOM|CONTEXT_SETTINGS = the direction keys for the eq/col picker screens
- i.e where up/down is inc/dec
- CONTEXT_SETTINGS = up/down is prev/next, l/r is inc/dec
-
-*/
-
-static const struct button_mapping button_context_standard[] = {
- { ACTION_STD_PREV, BUTTON_UP|BUTTON_REL, BUTTON_NONE },
- { ACTION_STD_PREVREPEAT, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE },
- { ACTION_STD_NEXT, BUTTON_DOWN|BUTTON_REL, BUTTON_NONE },
- { ACTION_STD_NEXTREPEAT, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE },
-
- { ACTION_STD_PREV, BUTTON_FF|BUTTON_REL, BUTTON_NONE },
- { ACTION_STD_PREVREPEAT, BUTTON_FF|BUTTON_REPEAT, BUTTON_NONE },
- { ACTION_STD_NEXT, BUTTON_REW|BUTTON_REL, BUTTON_NONE },
- { ACTION_STD_NEXTREPEAT, BUTTON_REW|BUTTON_REPEAT, BUTTON_NONE },
-
- { ACTION_STD_CANCEL, BUTTON_BACK|BUTTON_REL, BUTTON_NONE },
-
- { ACTION_STD_CONTEXT, BUTTON_SELECT|BUTTON_REPEAT, BUTTON_SELECT },
-
- //{ ACTION_STD_QUICKSCREEN, BUTTON_BACK|BUTTON_REPEAT, BUTTON_BACK },
- { ACTION_STD_MENU, BUTTON_BACK|BUTTON_REPEAT, BUTTON_NONE },
-
- { ACTION_STD_OK, BUTTON_SELECT|BUTTON_REL, BUTTON_SELECT },
-// { ACTION_STD_OK, BUTTON_RIGHT, BUTTON_NONE },
-
- LAST_ITEM_IN_LIST
-}; /* button_context_standard */
-
-
-static const struct button_mapping button_context_wps[] = {
- { ACTION_WPS_PLAY, BUTTON_POWER|BUTTON_REL, BUTTON_POWER },
- //{ ACTION_WPS_STOP, BUTTON_POWER|BUTTON_REPEAT, BUTTON_NONE },
-
- { ACTION_WPS_SKIPNEXT, BUTTON_RIGHT|BUTTON_REL, BUTTON_RIGHT },
- { ACTION_WPS_SKIPPREV, BUTTON_LEFT|BUTTON_REL, BUTTON_LEFT },
-
- { ACTION_WPS_SEEKBACK, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
- { ACTION_WPS_SEEKFWD, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
- { ACTION_WPS_STOPSEEK, BUTTON_LEFT|BUTTON_REL, BUTTON_LEFT|BUTTON_REPEAT },
- { ACTION_WPS_STOPSEEK, BUTTON_RIGHT|BUTTON_REL, BUTTON_RIGHT|BUTTON_REPEAT },
-
- { ACTION_WPS_VOLDOWN, BUTTON_VOL_DOWN|BUTTON_REPEAT, BUTTON_NONE },
- { ACTION_WPS_VOLDOWN, BUTTON_VOL_DOWN, BUTTON_NONE },
- { ACTION_WPS_VOLUP, BUTTON_VOL_UP|BUTTON_REPEAT, BUTTON_NONE },
- { ACTION_WPS_VOLUP, BUTTON_VOL_UP, BUTTON_NONE },
-
- //{ ACTION_WPS_PITCHSCREEN, BUTTON_BACK|BUTTON_REPEAT, BUTTON_BACK },
-
- { ACTION_WPS_QUICKSCREEN, BUTTON_BACK|BUTTON_REPEAT, BUTTON_BACK },
- { ACTION_WPS_MENU, BUTTON_BACK|BUTTON_REL, BUTTON_BACK },
- { ACTION_WPS_VIEW_PLAYLIST, BUTTON_POWER, BUTTON_NONE },
- { ACTION_WPS_CONTEXT, BUTTON_SELECT|BUTTON_REPEAT, BUTTON_SELECT },
-
- { ACTION_WPS_HOTKEY, BUTTON_BACK|BUTTON_REL, BUTTON_NONE },
- { ACTION_WPS_BROWSE, BUTTON_SELECT|BUTTON_REL, BUTTON_BACK },
- LAST_ITEM_IN_LIST
-}; /* button_context_wps */
-
-static const struct button_mapping button_context_list[] = {
-#ifdef HAVE_VOLUME_IN_LIST
- { ACTION_LIST_VOLUP, BUTTON_VOL_UP|BUTTON_REPEAT, BUTTON_NONE },
- { ACTION_LIST_VOLUP, BUTTON_VOL_UP, BUTTON_NONE },
- { ACTION_LIST_VOLDOWN, BUTTON_VOL_DOWN, BUTTON_NONE },
- { ACTION_LIST_VOLDOWN, BUTTON_VOL_DOWN|BUTTON_REPEAT, BUTTON_NONE },
-#endif
- LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
-}; /* button_context_list */
-
-static const struct button_mapping button_context_tree[] = {
- //{ ACTION_TREE_WPS, BUTTON_POWER|BUTTON_REL, BUTTON_POWER },
- //{ ACTION_TREE_STOP, BUTTON_POWER, BUTTON_NONE },
- //{ ACTION_TREE_STOP, BUTTON_POWER|BUTTON_REL, BUTTON_POWER },
- //{ ACTION_TREE_STOP, BUTTON_POWER|BUTTON_REPEAT, BUTTON_NONE },
- //{ ACTION_TREE_HOTKEY, BUTTON_BACK|BUTTON_REL, BUTTON_NONE },
-
- LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_LIST)
-}; /* button_context_tree */
-
-static const struct button_mapping button_context_listtree_scroll_without_combo[] = {
- { ACTION_NONE, BUTTON_LEFT, BUTTON_NONE },
- { ACTION_STD_CANCEL, BUTTON_BACK|BUTTON_REL, BUTTON_BACK },
- { ACTION_TREE_ROOT_INIT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_LEFT },
- { ACTION_TREE_PGLEFT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
- { ACTION_NONE, BUTTON_RIGHT, BUTTON_NONE },
- { ACTION_STD_OK, BUTTON_RIGHT|BUTTON_REL, BUTTON_RIGHT },
- { ACTION_TREE_PGRIGHT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
- LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_CUSTOM|CONTEXT_TREE),
-};
-
-static const struct button_mapping button_context_settings[] = {
- { ACTION_SETTINGS_INC, BUTTON_UP, BUTTON_NONE },
- { ACTION_SETTINGS_INCREPEAT, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE },
- { ACTION_SETTINGS_DEC, BUTTON_DOWN, BUTTON_NONE },
- { ACTION_SETTINGS_DECREPEAT, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE },
- { ACTION_STD_PREV, BUTTON_LEFT, BUTTON_NONE },
- { ACTION_STD_PREVREPEAT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_LEFT },
- { ACTION_STD_NEXT, BUTTON_RIGHT, BUTTON_NONE },
- { ACTION_STD_NEXTREPEAT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_RIGHT },
- { ACTION_SETTINGS_RESET, BUTTON_POWER, BUTTON_NONE },
- LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
-}; /* button_context_settings */
-
-static const struct button_mapping button_context_settings_right_is_inc[] = {
- { ACTION_SETTINGS_INC, BUTTON_RIGHT, BUTTON_NONE },
- { ACTION_SETTINGS_INCREPEAT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
- { ACTION_SETTINGS_DEC, BUTTON_LEFT, BUTTON_NONE },
- { ACTION_SETTINGS_DECREPEAT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
- { ACTION_STD_PREV, BUTTON_UP, BUTTON_NONE },
- { ACTION_STD_PREVREPEAT, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE },
- { ACTION_STD_NEXT, BUTTON_DOWN, BUTTON_NONE },
- { ACTION_STD_NEXTREPEAT, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE },
- { ACTION_SETTINGS_RESET, BUTTON_BACK, BUTTON_NONE },
- LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
-}; /* button_context_settingsgraphical */
-
-static const struct button_mapping button_context_yesno[] = {
- { ACTION_YESNO_ACCEPT, BUTTON_SELECT, BUTTON_NONE },
- LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
-}; /* button_context_settings_yesno */
-
-static const struct button_mapping button_context_colorchooser[] = {
- { ACTION_STD_OK, BUTTON_BACK|BUTTON_REL, BUTTON_NONE },
- LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_CUSTOM|CONTEXT_SETTINGS),
-}; /* button_context_colorchooser */
-
-static const struct button_mapping button_context_eq[] = {
- { ACTION_STD_OK, BUTTON_SELECT|BUTTON_REL, BUTTON_NONE },
- LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_CUSTOM|CONTEXT_SETTINGS),
-}; /* button_context_eq */
-
-/** Bookmark Screen **/
-static const struct button_mapping button_context_bmark[] = {
- { ACTION_BMS_DELETE, BUTTON_BACK, BUTTON_NONE },
- LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_LIST),
-}; /* button_context_bmark */
-
-static const struct button_mapping button_context_time[] = {
- LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_SETTINGS),
-}; /* button_context_time */
-
-static const struct button_mapping button_context_quickscreen[] = {
- { ACTION_QS_TOP, BUTTON_UP, BUTTON_NONE },
- { ACTION_QS_TOP, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE },
- { ACTION_QS_DOWN, BUTTON_DOWN, BUTTON_NONE },
- { ACTION_QS_DOWN, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE },
- { ACTION_QS_LEFT, BUTTON_LEFT, BUTTON_NONE },
- { ACTION_QS_LEFT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
- { ACTION_QS_RIGHT, BUTTON_RIGHT, BUTTON_NONE },
- { ACTION_QS_RIGHT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
- { ACTION_STD_CANCEL, BUTTON_BACK, BUTTON_NONE },
- LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
-}; /* button_context_quickscreen */
-
-static const struct button_mapping button_context_pitchscreen[] = {
-
- LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
-}; /* button_context_pitchcreen */
-
-static const struct button_mapping button_context_keyboard[] = {
- { ACTION_KBD_LEFT, BUTTON_LEFT, BUTTON_NONE },
- { ACTION_KBD_LEFT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
- { ACTION_KBD_RIGHT, BUTTON_RIGHT, BUTTON_NONE },
- { ACTION_KBD_RIGHT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
- { ACTION_KBD_CURSOR_RIGHT, BUTTON_POWER, BUTTON_NONE },
- { ACTION_KBD_CURSOR_RIGHT, BUTTON_POWER|BUTTON_REPEAT, BUTTON_NONE },
- { ACTION_KBD_SELECT, BUTTON_SELECT, BUTTON_NONE },
- { ACTION_KBD_PAGE_FLIP, BUTTON_BACK|BUTTON_BACK, BUTTON_NONE },
- { ACTION_KBD_DONE, BUTTON_POWER|BUTTON_REL, BUTTON_POWER },
- { ACTION_KBD_ABORT, BUTTON_BACK|BUTTON_REL, BUTTON_BACK },
- { ACTION_KBD_BACKSPACE, BUTTON_BACK, BUTTON_NONE },
- { ACTION_KBD_BACKSPACE, BUTTON_BACK|BUTTON_REPEAT, BUTTON_NONE },
- { ACTION_KBD_UP, BUTTON_UP, BUTTON_NONE },
- { ACTION_KBD_UP, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE },
- { ACTION_KBD_DOWN, BUTTON_DOWN, BUTTON_NONE },
- { ACTION_KBD_DOWN, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE },
- { ACTION_KBD_MORSE_SELECT, BUTTON_SELECT|BUTTON_REL, BUTTON_NONE },
- LAST_ITEM_IN_LIST
-}; /* button_context_keyboard */
-
-/** FM Radio Screen **/
-#if CONFIG_TUNER
-static const struct button_mapping button_context_radio[] = {
- { ACTION_FM_MENU, BUTTON_SELECT | BUTTON_REPEAT, BUTTON_SELECT },
- { ACTION_FM_MODE, BUTTON_SELECT, BUTTON_SELECT },
-// { ACTION_FM_PRESET, BUTTON_STOP, BUTTON_NONE },
- { ACTION_FM_PLAY, BUTTON_POWER | BUTTON_REL, BUTTON_NONE },
- { ACTION_FM_STOP, BUTTON_POWER | BUTTON_REPEAT, BUTTON_NONE },
- { ACTION_FM_EXIT, BUTTON_BACK, BUTTON_NONE },
-
- //{ ACTION_SETTINGS_INC, BUTTON_VOL_UP, BUTTON_NONE },
- //{ ACTION_SETTINGS_INCREPEAT,BUTTON_VOL_UP|BUTTON_REPEAT, BUTTON_NONE },
- //{ ACTION_SETTINGS_DEC, BUTTON_VOL_DOWN, BUTTON_NONE },
- //{ ACTION_SETTINGS_DECREPEAT,BUTTON_VOL_DOWN|BUTTON_REPEAT, BUTTON_NONE },
-
- LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_SETTINGS)
-}; /* button_context_radio */
-#endif
-
-#ifdef HAVE_RECORDING
-static const struct button_mapping button_context_recscreen[] = {
-
- { ACTION_REC_PAUSE, BUTTON_SELECT, BUTTON_NONE },
- { ACTION_SETTINGS_INC, BUTTON_RIGHT, BUTTON_NONE },
- { ACTION_SETTINGS_INCREPEAT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
- { ACTION_SETTINGS_DEC, BUTTON_LEFT, BUTTON_NONE },
- { ACTION_SETTINGS_DECREPEAT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
-
- LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
-}; /* button_context_recscreen */
-#endif
-
-#ifdef USB_ENABLE_HID
-static const struct button_mapping button_context_usb_hid[] = {
- { ACTION_USB_HID_MODE_SWITCH_NEXT, BUTTON_BACK|BUTTON_REL, BUTTON_BACK },
- { ACTION_USB_HID_MODE_SWITCH_PREV, BUTTON_BACK|BUTTON_REPEAT, BUTTON_BACK },
-
- LAST_ITEM_IN_LIST
-}; /* button_context_usb_hid */
-
-static const struct button_mapping button_context_usb_hid_mode_multimedia[] = {
-
- { ACTION_USB_HID_MULTIMEDIA_VOLUME_DOWN, BUTTON_VOL_DOWN, BUTTON_NONE },
- { ACTION_USB_HID_MULTIMEDIA_VOLUME_DOWN, BUTTON_VOL_DOWN|BUTTON_REPEAT, BUTTON_NONE },
- { ACTION_USB_HID_MULTIMEDIA_VOLUME_UP, BUTTON_VOL_UP, BUTTON_NONE },
- { ACTION_USB_HID_MULTIMEDIA_VOLUME_UP, BUTTON_VOL_UP|BUTTON_REPEAT, BUTTON_NONE },
- { ACTION_USB_HID_MULTIMEDIA_VOLUME_MUTE, BUTTON_SELECT|BUTTON_REL, BUTTON_SELECT },
- { ACTION_USB_HID_MULTIMEDIA_PLAYBACK_PLAY_PAUSE, BUTTON_POWER|BUTTON_REL, BUTTON_POWER },
- { ACTION_USB_HID_MULTIMEDIA_PLAYBACK_STOP, BUTTON_POWER|BUTTON_REPEAT, BUTTON_POWER },
- { ACTION_USB_HID_MULTIMEDIA_PLAYBACK_TRACK_PREV, BUTTON_REW|BUTTON_REL, BUTTON_REW },
- { ACTION_USB_HID_MULTIMEDIA_PLAYBACK_TRACK_NEXT, BUTTON_FF|BUTTON_REL, BUTTON_FF },
-
- LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_USB_HID)
-}; /* button_context_usb_hid_mode_multimedia */
-
-
-static const struct button_mapping button_context_usb_hid_mode_presentation[] = {
- { ACTION_USB_HID_PRESENTATION_SLIDESHOW_START, BUTTON_POWER|BUTTON_REL, BUTTON_POWER },
- { ACTION_USB_HID_PRESENTATION_SLIDESHOW_LEAVE, BUTTON_POWER|BUTTON_REPEAT, BUTTON_POWER },
- { ACTION_USB_HID_PRESENTATION_SLIDE_PREV, BUTTON_LEFT|BUTTON_REL, BUTTON_LEFT },
- { ACTION_USB_HID_PRESENTATION_SLIDE_NEXT, BUTTON_RIGHT|BUTTON_REL, BUTTON_RIGHT },
- { ACTION_USB_HID_PRESENTATION_SLIDE_FIRST, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
- { ACTION_USB_HID_PRESENTATION_SLIDE_LAST, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
- //{ ACTION_USB_HID_PRESENTATION_SCREEN_BLACK, BUTTON_BOTTOMRIGHT|BUTTON_REPEAT, BUTTON_BOTTOMRIGHT },
- //{ ACTION_USB_HID_PRESENTATION_SCREEN_WHITE, BUTTON_BOTTOMLEFT|BUTTON_REPEAT, BUTTON_BOTTOMLEFT },
- { ACTION_USB_HID_PRESENTATION_LINK_PREV, BUTTON_UP, BUTTON_NONE },
- { ACTION_USB_HID_PRESENTATION_LINK_PREV, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE },
- { ACTION_USB_HID_PRESENTATION_LINK_NEXT, BUTTON_DOWN, BUTTON_NONE },
- { ACTION_USB_HID_PRESENTATION_LINK_NEXT, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE },
- { ACTION_USB_HID_PRESENTATION_MOUSE_CLICK, BUTTON_SELECT, BUTTON_SELECT },
- { ACTION_USB_HID_PRESENTATION_MOUSE_OVER, BUTTON_SELECT|BUTTON_REPEAT, BUTTON_SELECT },
-
- LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_USB_HID)
-}; /* button_context_usb_hid_mode_presentation */
-
-static const struct button_mapping button_context_usb_hid_mode_browser[] = {
- { ACTION_USB_HID_BROWSER_SCROLL_UP, BUTTON_UP, BUTTON_NONE },
- { ACTION_USB_HID_BROWSER_SCROLL_UP, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE },
- { ACTION_USB_HID_BROWSER_SCROLL_DOWN, BUTTON_DOWN, BUTTON_NONE },
- { ACTION_USB_HID_BROWSER_SCROLL_DOWN, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE },
- { ACTION_USB_HID_BROWSER_ZOOM_IN, BUTTON_VOL_UP, BUTTON_NONE },
- { ACTION_USB_HID_BROWSER_ZOOM_IN, BUTTON_VOL_UP|BUTTON_REPEAT, BUTTON_NONE },
- { ACTION_USB_HID_BROWSER_ZOOM_OUT, BUTTON_VOL_DOWN, BUTTON_NONE },
- { ACTION_USB_HID_BROWSER_ZOOM_OUT, BUTTON_VOL_DOWN|BUTTON_REPEAT, BUTTON_NONE },
- { ACTION_USB_HID_BROWSER_ZOOM_RESET, BUTTON_POWER|BUTTON_REPEAT, BUTTON_NONE },
- { ACTION_USB_HID_BROWSER_TAB_PREV, BUTTON_LEFT|BUTTON_REL, BUTTON_LEFT },
- { ACTION_USB_HID_BROWSER_TAB_NEXT, BUTTON_RIGHT|BUTTON_REL, BUTTON_RIGHT },
- { ACTION_USB_HID_BROWSER_TAB_CLOSE, BUTTON_BACK|BUTTON_REPEAT, BUTTON_BACK },
- { ACTION_USB_HID_BROWSER_HISTORY_BACK, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_LEFT },
- { ACTION_USB_HID_BROWSER_HISTORY_FORWARD, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_RIGHT },
- { ACTION_USB_HID_BROWSER_VIEW_FULL_SCREEN, BUTTON_SELECT|BUTTON_REPEAT, BUTTON_SELECT },
-
- LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_USB_HID)
-}; /* button_context_usb_hid_mode_browser */
-
-#ifdef HAVE_USB_HID_MOUSE
-static const struct button_mapping button_context_usb_hid_mode_mouse[] = {
- { ACTION_USB_HID_MOUSE_UP, BUTTON_UP, BUTTON_NONE },
- { ACTION_USB_HID_MOUSE_UP_REP, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE },
- { ACTION_USB_HID_MOUSE_DOWN, BUTTON_DOWN, BUTTON_NONE },
- { ACTION_USB_HID_MOUSE_DOWN_REP, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE },
- { ACTION_USB_HID_MOUSE_LEFT, BUTTON_LEFT, BUTTON_NONE },
- { ACTION_USB_HID_MOUSE_LEFT_REP, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
- { ACTION_USB_HID_MOUSE_RIGHT, BUTTON_RIGHT, BUTTON_NONE },
- { ACTION_USB_HID_MOUSE_RIGHT_REP, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
- { ACTION_USB_HID_MOUSE_BUTTON_LEFT, BUTTON_SELECT, BUTTON_NONE },
- { ACTION_USB_HID_MOUSE_BUTTON_LEFT_REL, BUTTON_SELECT|BUTTON_REL, BUTTON_NONE },
- { ACTION_USB_HID_MOUSE_BUTTON_RIGHT, BUTTON_POWER, BUTTON_NONE },
- { ACTION_USB_HID_MOUSE_BUTTON_RIGHT_REL, BUTTON_POWER|BUTTON_REL, BUTTON_POWER },
-
- { ACTION_USB_HID_MOUSE_WHEEL_SCROLL_UP, BUTTON_FF, BUTTON_NONE },
- { ACTION_USB_HID_MOUSE_WHEEL_SCROLL_UP, BUTTON_FF|BUTTON_REPEAT, BUTTON_NONE },
- { ACTION_USB_HID_MOUSE_WHEEL_SCROLL_DOWN, BUTTON_REW, BUTTON_NONE },
- { ACTION_USB_HID_MOUSE_WHEEL_SCROLL_DOWN, BUTTON_REW|BUTTON_REPEAT, BUTTON_NONE },
-
- LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_USB_HID)
-}; /* button_context_usb_hid_mode_mouse */
-#endif
-#endif
-
-const struct button_mapping* get_context_mapping(int context)
-{
- switch (context)
- {
- case CONTEXT_STD:
- return button_context_standard;
- case CONTEXT_WPS:
- return button_context_wps;
- case CONTEXT_LIST:
- return button_context_list;
- case CONTEXT_MAINMENU:
- case CONTEXT_TREE:
- return button_context_listtree_scroll_without_combo;
- case CONTEXT_CUSTOM|CONTEXT_TREE:
- return button_context_tree;
-#if CONFIG_TUNER
- case CONTEXT_FM:
- return button_context_radio;
-#endif
- case CONTEXT_SETTINGS:
- return button_context_settings;
- case CONTEXT_CUSTOM|CONTEXT_SETTINGS:
- return button_context_settings_right_is_inc;
- case CONTEXT_SETTINGS_COLOURCHOOSER:
- return button_context_colorchooser;
- case CONTEXT_SETTINGS_EQ:
- return button_context_eq;
- case CONTEXT_SETTINGS_TIME:
- return button_context_time;
- case CONTEXT_YESNOSCREEN:
- return button_context_yesno;
- case CONTEXT_BOOKMARKSCREEN:
- return button_context_bmark;
- case CONTEXT_QUICKSCREEN:
- return button_context_quickscreen;
- case CONTEXT_PITCHSCREEN:
- return button_context_pitchscreen;
- case CONTEXT_KEYBOARD:
- case CONTEXT_MORSE_INPUT:
- return button_context_keyboard;
-#ifdef HAVE_RECORDING
- case CONTEXT_SETTINGS_RECTRIGGER:
- return button_context_settings_right_is_inc;
- case CONTEXT_RECSCREEN:
- return button_context_recscreen;
-#endif
-#ifdef USB_ENABLE_HID
- case CONTEXT_USB_HID:
- return button_context_usb_hid;
- case CONTEXT_USB_HID_MODE_MULTIMEDIA:
- return button_context_usb_hid_mode_multimedia;
- case CONTEXT_USB_HID_MODE_PRESENTATION:
- return button_context_usb_hid_mode_presentation;
- case CONTEXT_USB_HID_MODE_BROWSER:
- return button_context_usb_hid_mode_browser;
-#ifdef HAVE_USB_HID_MOUSE
- case CONTEXT_USB_HID_MODE_MOUSE:
- return button_context_usb_hid_mode_mouse;
-#endif
-#endif
- }
- return button_context_standard;
-}
diff --git a/apps/keymaps/keymap-zen.c b/apps/keymaps/keymap-zen.c
index ab0f32edb4..04b65b3980 100644
--- a/apps/keymaps/keymap-zen.c
+++ b/apps/keymaps/keymap-zen.c
@@ -145,7 +145,8 @@ static const struct button_mapping button_context_keyboard[] = {
}; /* button_context_keyboard */
static const struct button_mapping button_context_quickscreen[] = {
- { ACTION_STD_CANCEL, BUTTON_SELECT, BUTTON_NONE },
+ { ACTION_STD_CONTEXT, BUTTON_SELECT|BUTTON_REPEAT, BUTTON_SELECT },
+ { ACTION_STD_CANCEL, BUTTON_SELECT|BUTTON_REL, BUTTON_SELECT },
{ ACTION_STD_CANCEL, BUTTON_BACK, BUTTON_NONE },
{ ACTION_STD_CANCEL, BUTTON_PLAYPAUSE, BUTTON_NONE },
{ ACTION_QS_TOP, BUTTON_UP, BUTTON_NONE },
@@ -235,7 +236,6 @@ static const struct button_mapping button_context_settings[] = {
{ ACTION_STD_NEXTREPEAT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
{ ACTION_STD_OK, BUTTON_SELECT|BUTTON_REL, BUTTON_SELECT },
- { ACTION_SETTINGS_RESET, BUTTON_SELECT|BUTTON_REPEAT, BUTTON_NONE },
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
}; /* button_context_settings */
diff --git a/apps/keymaps/keymap-zenxfi2.c b/apps/keymaps/keymap-zenxfi2.c
index c98a4a8405..ba25592246 100644
--- a/apps/keymaps/keymap-zenxfi2.c
+++ b/apps/keymaps/keymap-zenxfi2.c
@@ -137,7 +137,7 @@ static const struct button_mapping button_context_radio[] = {
const struct button_mapping* target_get_context_mapping(int context)
{
- switch (context)
+ switch (context & ~CONTEXT_LOCKED)
{
case CONTEXT_STD:
return button_context_standard;
diff --git a/apps/keymaps/keymap-zenxfi3.c b/apps/keymaps/keymap-zenxfi3.c
index 191b36a1ca..d19a407b78 100644
--- a/apps/keymaps/keymap-zenxfi3.c
+++ b/apps/keymaps/keymap-zenxfi3.c
@@ -163,7 +163,6 @@ static const struct button_mapping button_context_settings[] = {
{ ACTION_STD_NEXTREPEAT, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE },
{ ACTION_STD_OK, BUTTON_PLAY|BUTTON_REL, BUTTON_PLAY },
- { ACTION_SETTINGS_RESET, BUTTON_PLAY|BUTTON_REPEAT, BUTTON_NONE },
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
}; /* button_context_settings */
diff --git a/apps/lang/arabic.lang b/apps/lang/arabic.lang
index ecf8f8fb1f..c9b295956b 100644
--- a/apps/lang/arabic.lang
+++ b/apps/lang/arabic.lang
@@ -14,6 +14,8 @@
# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
# KIND, either express or implied.
#
+# LANGUAGE_IS_RTL
+#
# Arabic language file, translated by:
# - Mohamed Tarek
# - Raafat Akkad
@@ -764,20 +766,17 @@
</voice>
</phrase>
<phrase>
- id: LANG_RECORDING_FORMAT
- desc: audio format item in recording menu
+ id: LANG_FORMAT
+ desc: audio format
user: core
<source>
- *: none
- recording: "Format"
+ *: "Format"
</source>
<dest>
- *: none
- recording: "الصيغة"
+ *: "الصيغة"
</dest>
<voice>
- *: none
- recording: "Format"
+ *: "Format"
</voice>
</phrase>
<phrase>
@@ -4043,15 +4042,15 @@
user: core
<source>
*: none
- gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
</source>
<dest>
*: none
- gigabeatfx,sansafuzeplus: "حساسية اللمس"
+ fiiom3k,gigabeatfx,sansafuzeplus: "حساسية اللمس"
</dest>
<voice>
*: none
- gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
</voice>
</phrase>
<phrase>
diff --git a/apps/lang/basque.lang b/apps/lang/basque.lang
index 59ab84b04e..05fae692e8 100644
--- a/apps/lang/basque.lang
+++ b/apps/lang/basque.lang
@@ -5263,20 +5263,17 @@
</voice>
</phrase>
<phrase>
- id: LANG_RECORDING_FORMAT
- desc: audio format item in recording menu
+ id: LANG_FORMAT
+ desc: audio format
user: core
<source>
- *: none
- recording: "Format"
+ *: "Format"
</source>
<dest>
- *: none
- recording: "Formatua"
+ *: "Formatua"
</dest>
<voice>
- *: none
- recording: "Formatua"
+ *: "Formatua"
</voice>
</phrase>
<phrase>
@@ -10311,15 +10308,15 @@
user: core
<source>
*: none
- gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
</source>
<dest>
*: none
- gigabeatfx,sansafuzeplus: "Taktilaren Sentikortasuna"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Taktilaren Sentikortasuna"
</dest>
<voice>
*: none
- gigabeatfx,sansafuzeplus: "Taktilaren Sentikortasuna"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Taktilaren Sentikortasuna"
</voice>
</phrase>
<phrase>
diff --git a/apps/lang/bulgarian.lang b/apps/lang/bulgarian.lang
index a66df1ba51..77f2d58361 100644
--- a/apps/lang/bulgarian.lang
+++ b/apps/lang/bulgarian.lang
@@ -252,7 +252,7 @@
*: "PLAY = Yes"
archosplayer: "(PLAY/STOP)"
cowond2*: "MENU, or top-right = Yes"
- creativezen*: "Select = Yes"
+ creativezen*: "SELECT = Yes"
gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT = Yes"
iriverh100,iriverh120,iriverh300: "NAVI = Yes"
mrobe500: "PLAY, POWER, or top-right = Yes"
@@ -262,7 +262,7 @@
*: "PLAY = Да"
archosplayer: "(PLAY/STOP)"
cowond2*: "MENU или горе лÑво = Да"
- creativezen*: "Select = Да"
+ creativezen*: "SELECT = Да"
gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT = Да"
iriverh100,iriverh120,iriverh300: "NAVI = Да"
mrobe500: "PLAY, POWER или горе дÑÑно = Да"
@@ -5268,20 +5268,17 @@
</voice>
</phrase>
<phrase>
- id: LANG_RECORDING_FORMAT
- desc: audio format item in recording menu
+ id: LANG_FORMAT
+ desc: audio format
user: core
<source>
- *: none
- recording: "Format"
+ *: "Format"
</source>
<dest>
- *: none
- recording: "Формат"
+ *: "Формат"
</dest>
<voice>
- *: none
- recording: "Формат"
+ *: "Формат"
</voice>
</phrase>
<phrase>
@@ -10316,15 +10313,15 @@
user: core
<source>
*: none
- gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
</source>
<dest>
*: none
- gigabeatfx,sansafuzeplus: "ЧувÑтвителноÑÑ‚ на тъчпада"
+ fiiom3k,gigabeatfx,sansafuzeplus: "ЧувÑтвителноÑÑ‚ на тъчпада"
</dest>
<voice>
*: none
- gigabeatfx,sansafuzeplus: "ЧувÑтвителноÑÑ‚ на тъчпада"
+ fiiom3k,gigabeatfx,sansafuzeplus: "ЧувÑтвителноÑÑ‚ на тъчпада"
</voice>
</phrase>
<phrase>
diff --git a/apps/lang/catala.lang b/apps/lang/catala.lang
index 9c29513ca5..38fd6d0644 100644
--- a/apps/lang/catala.lang
+++ b/apps/lang/catala.lang
@@ -5257,20 +5257,17 @@
</voice>
</phrase>
<phrase>
- id: LANG_RECORDING_FORMAT
- desc: audio format item in recording menu
+ id: LANG_FORMAT
+ desc: audio format
user: core
<source>
- *: none
- recording: "Format"
+ *: "Format"
</source>
<dest>
- *: none
- recording: "Format"
+ *: "Format"
</dest>
<voice>
- *: none
- recording: "Format"
+ *: "Format"
</voice>
</phrase>
<phrase>
@@ -10305,15 +10302,15 @@
user: core
<source>
*: none
- gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
</source>
<dest>
*: none
- gigabeatfx,sansafuzeplus: "Sensibilitat del touchpad"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Sensibilitat del touchpad"
</dest>
<voice>
*: none
- gigabeatfx,sansafuzeplus: "Sensibilitat del Touchpad"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Sensibilitat del Touchpad"
</voice>
</phrase>
<phrase>
diff --git a/apps/lang/chinese-simp.lang b/apps/lang/chinese-simp.lang
index c1829298be..b7702ee3c7 100644
--- a/apps/lang/chinese-simp.lang
+++ b/apps/lang/chinese-simp.lang
@@ -13,12 +13,43 @@
# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
# KIND, either express or implied.
#
+# LANGUAGE_UNITS_FIRST
+#
# Simplified Chinese language file, translated by:
# - Ye Wei
# - Xinlu Huang
# - Harry Tu
# - Jun Gu
# - Purling Nayuki
+#------
+<phrase>
+ id: LANG_LIST_SEPARATOR
+ desc: line between lines in lists
+ user: core
+ <source>
+ *: "Line Separator"
+ </source>
+ <dest>
+ *: "行分隔线"
+ </dest>
+ <voice>
+ *: "行分隔线"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_LIST_SEPARATOR_COLOR
+ desc: line between lines in lists
+ user: core
+ <source>
+ *: "Line Separator Colour"
+ </source>
+ <dest>
+ *: "行分隔线颜色"
+ </dest>
+ <voice>
+ *: "Color of Line Separator"
+ </voice>
+</phrase>
<phrase>
id: LANG_SET_BOOL_YES
desc: bool true representation
@@ -27,7 +58,7 @@
*: "Yes"
</source>
<dest>
- *: "√"
+ *: "是"
</dest>
<voice>
*: "是"
@@ -41,7 +72,7 @@
*: "No"
</source>
<dest>
- *: "×"
+ *: "å¦"
</dest>
<voice>
*: "å¦"
@@ -104,23 +135,6 @@
</voice>
</phrase>
<phrase>
- id: LANG_CONFIRM_SHUTDOWN
- desc: in shutdown screen
- user: core
- <source>
- *: none
- soft_shutdown: "Press OFF to shut down"
- </source>
- <dest>
- *: none
- soft_shutdown: "按下OFF关机"
- </dest>
- <voice>
- *: none
- soft_shutdown: ""
- </voice>
-</phrase>
-<phrase>
id: LANG_SHUTTINGDOWN
desc: in main menu
user: core
@@ -128,27 +142,10 @@
*: "Shutting down..."
</source>
<dest>
- *: "关机中..."
+ *: "关闭中..."
</dest>
<voice>
- *: "关机中"
- </voice>
-</phrase>
-<phrase>
- id: LANG_REMOVE_MMC
- desc: before acknowledging usb in case an MMC is inserted (Ondio)
- user: core
- <source>
- *: none
- archosondio*: "Please remove inserted MMC"
- </source>
- <dest>
- *: none
- archosondio*: "请å–出MMCå¡"
- </dest>
- <voice>
- *: none
- archosondio*: "请å–出MMCå¡"
+ *: "关闭中"
</voice>
</phrase>
<phrase>
@@ -201,7 +198,7 @@
*: "General Settings"
</source>
<dest>
- *: "一般设置"
+ *: "常规设置"
</dest>
<voice>
*: "一般设置"
@@ -262,11 +259,11 @@
</source>
<dest>
*: none
- recording: "录音界é¢"
+ recording: "录音"
</dest>
<voice>
*: none
- recording: "录音界é¢"
+ recording: "录音"
</voice>
</phrase>
<phrase>
@@ -284,23 +281,6 @@
</voice>
</phrase>
<phrase>
- id: LANG_SHUTDOWN
- desc: in main menu
- user: core
- <source>
- *: none
- soft_shutdown: "Shut down"
- </source>
- <dest>
- *: none
- soft_shutdown: "关机"
- </dest>
- <voice>
- *: none
- soft_shutdown: "关机"
- </voice>
-</phrase>
-<phrase>
id: LANG_VOLUME
desc: in sound_settings
user: core
@@ -455,159 +435,6 @@
</voice>
</phrase>
<phrase>
- id: LANG_LOUDNESS
- desc: in sound_settings
- user: core
- <source>
- *: none
- masf: "Loudness"
- </source>
- <dest>
- *: none
- masf: "å“度"
- </dest>
- <voice>
- *: none
- masf: "å“度"
- </voice>
-</phrase>
-<phrase>
- id: LANG_AUTOVOL
- desc: in sound_settings
- user: core
- <source>
- *: none
- masf: "Auto Volume"
- </source>
- <dest>
- *: none
- masf: "自动音é‡è°ƒæ•´"
- </dest>
- <voice>
- *: none
- masf: "自动音é‡è°ƒæ•´"
- </voice>
-</phrase>
-<phrase>
- id: LANG_DECAY
- desc: in sound_settings
- user: core
- <source>
- *: none
- masf: "AV Decay Time"
- </source>
- <dest>
- *: none
- masf: "自动音é‡è°ƒæ•´è¡°æ¸›æ—¶é—´"
- </dest>
- <voice>
- *: none
- masf: ""
- </voice>
-</phrase>
-<phrase>
- id: LANG_SUPERBASS
- desc: in sound settings
- user: core
- <source>
- *: none
- masf: "Super Bass"
- </source>
- <dest>
- *: none
- masf: "超低音"
- </dest>
- <voice>
- *: none
- masf: "超低音"
- </voice>
-</phrase>
-<phrase>
- id: LANG_MDB_ENABLE
- desc: in sound settings
- user: core
- <source>
- *: none
- masf: "MDB Enable"
- </source>
- <dest>
- *: none
- masf: "å¯ç”¨MDB"
- </dest>
- <voice>
- *: none
- masf: "å¯ç”¨MDB"
- </voice>
-</phrase>
-<phrase>
- id: LANG_MDB_STRENGTH
- desc: in sound settings
- user: core
- <source>
- *: none
- masf: "MDB Strength"
- </source>
- <dest>
- *: none
- masf: "MDB强度"
- </dest>
- <voice>
- *: none
- masf: "MDB的强度"
- </voice>
-</phrase>
-<phrase>
- id: LANG_MDB_HARMONICS
- desc: in sound settings
- user: core
- <source>
- *: none
- masf: "MDB Harmonics"
- </source>
- <dest>
- *: none
- masf: "MDB泛音"
- </dest>
- <voice>
- *: none
- masf: "MDB的泛音"
- </voice>
-</phrase>
-<phrase>
- id: LANG_MDB_CENTER
- desc: in sound settings
- user: core
- <source>
- *: none
- masf: "MDB Centre Frequency"
- </source>
- <dest>
- *: none
- masf: "MDB中心频率"
- </dest>
- <voice>
- *: none
- masf: "MDB的中心频率"
- </voice>
-</phrase>
-<phrase>
- id: LANG_MDB_SHAPE
- desc: in sound settings
- user: core
- <source>
- *: none
- masf: "MDB Shape"
- </source>
- <dest>
- *: none
- masf: "MDB形状"
- </dest>
- <voice>
- *: none
- masf: "MDB的形状"
- </voice>
-</phrase>
-<phrase>
id: LANG_CROSSFEED
desc: in sound settings
user: core
@@ -929,7 +756,7 @@
*: "%dHz频段增益"
</dest>
<voice>
- *: "赫兹频段增益"
+ *: "%d赫兹频段增益"
</voice>
</phrase>
<phrase>
@@ -957,7 +784,7 @@
*: "带通滤波器%d"
</dest>
<voice>
- *: "带通滤波器"
+ *: "带通滤波器%d"
</voice>
</phrase>
<phrase>
@@ -1726,15 +1553,12 @@
user: core
<source>
*: "Peak Meter"
- masd: none
</source>
<dest>
*: "峰值电平表"
- masd: none
</dest>
<voice>
*: "峰值电平表"
- masd: none
</voice>
</phrase>
<phrase>
@@ -2142,20 +1966,6 @@
</voice>
</phrase>
<phrase>
- id: LANG_BOOKMARK_SETTINGS_UNIQUE_ONLY
- desc: Save only on bookmark for each playlist in recent bookmarks
- user: core
- <source>
- *: "Unique only"
- </source>
- <dest>
- *: "åªä¿å­˜å”¯ä¸€ä¸€ä»½"
- </dest>
- <voice>
- *: "åªä¿å­˜å”¯ä¸€ä¸€ä»½"
- </voice>
-</phrase>
-<phrase>
id: LANG_VOICE_MENU
desc: item of voice menu, enable/disable the voice UI
user: core
@@ -2240,23 +2050,6 @@
</voice>
</phrase>
<phrase>
- id: LANG_RECORDING_QUALITY
- desc: in the recording settings
- user: core
- <source>
- *: none
- recording_hwcodec: "Quality"
- </source>
- <dest>
- *: none
- recording_hwcodec: "å“è´¨"
- </dest>
- <voice>
- *: none
- recording_hwcodec: "å“è´¨"
- </voice>
-</phrase>
-<phrase>
id: LANG_FREQUENCY
desc: in recording and playback settings
user: core
@@ -2328,23 +2121,6 @@
</voice>
</phrase>
<phrase>
- id: LANG_RECORDING_EDITABLE
- desc: Editable recordings setting
- user: core
- <source>
- *: none
- recording_hwcodec: "Independent Frames"
- </source>
- <dest>
- *: none
- recording_hwcodec: "独立框架"
- </dest>
- <voice>
- *: none
- recording_hwcodec: "独立框架"
- </voice>
-</phrase>
-<phrase>
id: LANG_RECORD_TIMESPLIT
desc: Record split menu
user: core
@@ -3182,23 +2958,6 @@
</voice>
</phrase>
<phrase>
- id: LANG_BUTTON_BAR
- desc: in settings menu
- user: core
- <source>
- *: none
- recorder_pad: "Button Bar"
- </source>
- <dest>
- *: none
- recorder_pad: "按键æ¡"
- </dest>
- <voice>
- *: none
- recorder_pad: "按键æ¡"
- </voice>
-</phrase>
-<phrase>
id: LANG_VOLUME_DISPLAY
desc: Volume type title
user: core
@@ -3260,15 +3019,12 @@
user: core
<source>
*: "Peak Release"
- masd: none
</source>
<dest>
*: "峰值释放速度"
- masd: none
</dest>
<voice>
*: "峰值释放速度"
- masd: none
</voice>
</phrase>
<phrase>
@@ -3277,15 +3033,12 @@
user: core
<source>
*: "Peak Hold Time"
- masd: none
</source>
<dest>
*: "峰值ä¿æŒæ—¶é•¿"
- masd: none
</dest>
<voice>
*: "峰值ä¿æŒæ—¶é•¿"
- masd: none
</voice>
</phrase>
<phrase>
@@ -3294,15 +3047,12 @@
user: core
<source>
*: "Clip Hold Time"
- masd: none
</source>
<dest>
*: "削波ä¿æŒæ—¶é•¿"
- masd: none
</dest>
<voice>
*: "削波ä¿æŒæ—¶é•¿"
- masd: none
</voice>
</phrase>
<phrase>
@@ -3311,15 +3061,12 @@
user: core
<source>
*: "Eternal"
- masd: none
</source>
<dest>
*: "外部"
- masd: none
</dest>
<voice>
*: "外部"
- masd: none
</voice>
</phrase>
<phrase>
@@ -3328,15 +3075,12 @@
user: core
<source>
*: "Scale"
- masd: none
</source>
<dest>
*: "标尺类型"
- masd: none
</dest>
<voice>
*: "标尺类型"
- masd: none
</voice>
</phrase>
<phrase>
@@ -3345,15 +3089,12 @@
user: core
<source>
*: "Logarithmic (dB)"
- masd: none
</source>
<dest>
*: "对数(dB)åæ ‡"
- masd: none
</dest>
<voice>
*: "对数åæ ‡"
- masd: none
</voice>
</phrase>
<phrase>
@@ -3362,15 +3103,12 @@
user: core
<source>
*: "Linear (%)"
- masd: none
</source>
<dest>
*: "线性(%)åæ ‡"
- masd: none
</dest>
<voice>
*: "线性åæ ‡"
- masd: none
</voice>
</phrase>
<phrase>
@@ -3379,15 +3117,12 @@
user: core
<source>
*: "Minimum Of Range"
- masd: none
</source>
<dest>
*: "é‡ç¨‹æœ€å°å€¼"
- masd: none
</dest>
<voice>
*: "é‡ç¨‹æœ€å°å€¼"
- masd: none
</voice>
</phrase>
<phrase>
@@ -3396,15 +3131,12 @@
user: core
<source>
*: "Maximum Of Range"
- masd: none
</source>
<dest>
*: "é‡ç¨‹æœ€å¤§å€¼"
- masd: none
</dest>
<voice>
*: "é‡ç¨‹æœ€å¤§å€¼"
- masd: none
</voice>
</phrase>
<phrase>
@@ -3999,7 +3731,7 @@
*: "æœç´¢ä¸­...已找到%d个(%s)"
</dest>
<voice>
- *: ""
+ *: "æœç´¢ä¸­...已找到%d个(%s)"
</voice>
</phrase>
<phrase>
@@ -4062,23 +3794,6 @@
</voice>
</phrase>
<phrase>
- id: LANG_BATTERY_TOPOFF_CHARGE
- desc: in info display, shows that top off charge is running Only for V1 archosrecorder
- user: core
- <source>
- *: none
- archosrecorder: "Battery: Top-Off Chg"
- </source>
- <dest>
- *: none
- archosrecorder: "电池:æ’æµå¿«é€Ÿå……电"
- </dest>
- <voice>
- *: none
- archosrecorder: "电池 æ’æµå¿«é€Ÿå……电"
- </voice>
-</phrase>
-<phrase>
id: LANG_BATTERY_TRICKLE_CHARGE
desc: in info display, shows that trickle charge is running
user: core
@@ -4102,12 +3817,10 @@
<source>
*: "Battery: %d%% %dh %dm"
ipodmini1g,ipodmini2g,iriverh10: "Batt: %d%% %dh %dm"
- iriverifp7xx: "%d%% %dh %dm"
</source>
<dest>
*: "剩余电é‡:%d%% %dh %dm"
ipodmini1g,ipodmini2g,iriverh10: "电é‡:%d%% %dh %dm"
- iriverifp7xx: "%d%% %dh %dm"
</dest>
<voice>
*: "剩余电é‡"
@@ -4147,38 +3860,38 @@
user: core
<source>
*: "Int:"
+ hibylinux: "mSD:"
xduoox3: "mSD1:"
</source>
<dest>
*: "内部:"
+ hibylinux: "mSD:"
xduoox3: "mSD1:"
</dest>
<voice>
*: "内部大å°ä¸º"
+ hibylinux: "micro S D"
xduoox3: "mSDå¡1"
</voice>
</phrase>
<phrase>
id: LANG_DISK_NAME_MMC
- desc: in info menu; name for external disk with multivolume (Ondio; keep short!)
+ desc: in info menu; name for external disk with multivolume (keep short!)
user: core
<source>
*: none
- archosondio*: "MMC:"
multivolume: "HD1"
sansac200*,sansaclipplus,sansae200*,sansafuze*: "mSD:"
xduoox3: "mSD2:"
</source>
<dest>
*: none
- archosondio*: "MMC:"
multivolume: "ç£ç›˜1"
sansac200*,sansaclipplus,sansae200*,sansafuze*: "mSD:"
xduoox3: "mSD2:"
</dest>
<voice>
*: none
- archosondio*: "MMCå¡"
multivolume: "ç£ç›˜1"
sansac200*,sansaclipplus,sansae200*,sansafuze*: "mSDå¡"
xduoox3: "mSDå¡2"
@@ -4339,7 +4052,8 @@
vibe500: "CANCEL:å–消"
</dest>
<voice>
- *: none
+ *: ""
+ gigabeat*,gogearsa9200,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh100,iriverh10_5gb,iriverh120,iriverh300,mrobe100,samsungyh*,sansac200*,sansae200*: ""
</voice>
</phrase>
<phrase>
@@ -4398,6 +4112,7 @@
</dest>
<voice>
*: none
+ iaudiom5,iaudiox5,iriverh100,iriverh120,iriverh300,recording,samsungyh*,sansac200*,sansae200*,vibe500: ""
</voice>
</phrase>
<phrase>
@@ -4516,7 +4231,7 @@
</dest>
<voice>
*: none
- alarm: "离闹钟å“起还有"
+ alarm: "离闹钟å“起还有%d:%02d"
</voice>
</phrase>
<phrase>
@@ -4579,7 +4294,7 @@
</dest>
<voice>
*: none
- alarm,gigabeats,ipod*,iriverh10,iriverh10_5gb: ""
+ alarm,ipod*: ""
</voice>
</phrase>
<phrase>
@@ -4787,7 +4502,7 @@
*: "(VBR)"
</dest>
<voice>
- *: "VBR"
+ *: "(VBR)"
</voice>
</phrase>
<phrase>
@@ -4857,7 +4572,7 @@
*: "<æ— ä¿¡æ¯>"
</dest>
<voice>
- *: "æ— ä¿¡æ¯"
+ *: "<æ— ä¿¡æ¯>"
</voice>
</phrase>
<phrase>
@@ -5834,48 +5549,6 @@
</voice>
</phrase>
<phrase>
- id: LANG_KILOBYTE
- desc: a unit postfix, also voiced
- user: core
- <source>
- *: "KB"
- </source>
- <dest>
- *: "KB"
- </dest>
- <voice>
- *: "B"
- </voice>
-</phrase>
-<phrase>
- id: LANG_MEGABYTE
- desc: a unit postfix, also voiced
- user: core
- <source>
- *: "MB"
- </source>
- <dest>
- *: "MB"
- </dest>
- <voice>
- *: "MB"
- </voice>
-</phrase>
-<phrase>
- id: LANG_GIGABYTE
- desc: a unit postfix, also voiced
- user: core
- <source>
- *: "GB"
- </source>
- <dest>
- *: "GB"
- </dest>
- <voice>
- *: "GB"
- </voice>
-</phrase>
-<phrase>
id: LANG_POINT
desc: decimal separator for composing numbers
user: core
@@ -6477,7 +6150,7 @@
*: "æ’入了%d首曲目(%s)"
</dest>
<voice>
- *: "首曲目 æ’入了"
+ *: "æ’入了%d首曲目%s"
</voice>
</phrase>
<phrase>
@@ -6491,7 +6164,7 @@
*: "列入了%d首曲目(%s)"
</dest>
<voice>
- *: "首曲目 列入了"
+ *: "列入了%d首曲目%s"
</voice>
</phrase>
<phrase>
@@ -6505,7 +6178,7 @@
*: "ä¿å­˜äº†%d首曲目(%s)"
</dest>
<voice>
- *: "首曲目 ä¿å­˜äº†"
+ *: "ä¿å­˜äº†%d首曲目%s"
</voice>
</phrase>
<phrase>
@@ -6723,91 +6396,6 @@
</voice>
</phrase>
<phrase>
- id: LANG_BUTTONBAR_MENU
- desc: in button bar
- user: core
- <source>
- *: none
- radio_screen_button_bar: "Menu"
- </source>
- <dest>
- *: none
- radio_screen_button_bar: "选å•"
- </dest>
- <voice>
- *: none
- radio_screen_button_bar: ""
- </voice>
-</phrase>
-<phrase>
- id: LANG_FM_BUTTONBAR_EXIT
- desc: in radio screen
- user: core
- <source>
- *: none
- radio_screen_button_bar: "Exit"
- </source>
- <dest>
- *: none
- radio_screen_button_bar: "退出"
- </dest>
- <voice>
- *: none
- radio_screen_button_bar: ""
- </voice>
-</phrase>
-<phrase>
- id: LANG_FM_BUTTONBAR_ACTION
- desc: in radio screen
- user: core
- <source>
- *: none
- radio_screen_button_bar: "Action"
- </source>
- <dest>
- *: none
- radio_screen_button_bar: "动作"
- </dest>
- <voice>
- *: none
- radio_screen_button_bar: ""
- </voice>
-</phrase>
-<phrase>
- id: LANG_FM_BUTTONBAR_ADD
- desc: in radio screen
- user: core
- <source>
- *: none
- radio_screen_button_bar: "Add"
- </source>
- <dest>
- *: none
- radio_screen_button_bar: "增加"
- </dest>
- <voice>
- *: none
- radio_screen_button_bar: ""
- </voice>
-</phrase>
-<phrase>
- id: LANG_FM_BUTTONBAR_RECORD
- desc: in radio screen
- user: core
- <source>
- *: none
- radio_screen_button_bar: "Record"
- </source>
- <dest>
- *: none
- radio_screen_button_bar: "录音"
- </dest>
- <voice>
- *: none
- radio_screen_button_bar: ""
- </voice>
-</phrase>
-<phrase>
id: LANG_FM_MONO_MODE
desc: in radio screen
user: core
@@ -7012,7 +6600,7 @@
</phrase>
<phrase>
id: LANG_OFF_ABORT
- desc: Used on archosrecorder models
+ desc: Used on many models
user: core
<source>
*: "OFF to abort"
@@ -7031,6 +6619,7 @@
ipod*: "按PLAY/PAUSEé”®å–消"
iriverh10,iriverh10_5gb,sansac200*,sansae200*,vibe500: "按PREVé”®å–消"
iriverh100,iriverh120,iriverh300: "按STOPé”®å–消"
+ sansafuzeplus: "按BACKé”®å–消"
</dest>
<voice>
*: ""
@@ -7178,7 +6767,7 @@
</phrase>
<phrase>
id: LANG_PLUGIN_WRONG_MODEL
- desc: The plugin is not compatible with the archos model trying to run it
+ desc: The plugin is not compatible with the player model trying to run it
user: core
<source>
*: "Incompatible model"
@@ -7187,7 +6776,7 @@
*: "ä¸å…¼å®¹åž‹å·çš„æ’件"
</dest>
<voice>
- *: ""
+ *: "ä¸å…¼å®¹åž‹å·çš„æ’件"
</voice>
</phrase>
<phrase>
@@ -7201,7 +6790,7 @@
*: "ä¸å…¼å®¹ç‰ˆæœ¬çš„æ’件"
</dest>
<voice>
- *: ""
+ *: "ä¸å…¼å®¹ç‰ˆæœ¬çš„æ’件"
</voice>
</phrase>
<phrase>
@@ -7215,7 +6804,7 @@
*: "æ’件返回错误"
</dest>
<voice>
- *: ""
+ *: "æ’件返回错误"
</voice>
</phrase>
<phrase>
@@ -7475,159 +7064,6 @@
</voice>
</phrase>
<phrase>
- id: LANG_SYSFONT_CHANNEL_STEREO
- desc: in sound_settings
- user: core
- <source>
- *: none
- recording: "Stereo"
- </source>
- <dest>
- *: none
- recording: "Stereo"
- </dest>
- <voice>
- *: none
- recording: "Stereo"
- </voice>
-</phrase>
-<phrase>
- id: LANG_SYSFONT_CHANNEL_MONO
- desc: in sound_settings
- user: core
- <source>
- *: none
- recording: "Mono"
- </source>
- <dest>
- *: none
- recording: "Mono"
- </dest>
- <voice>
- *: none
- recording: "Mono"
- </voice>
-</phrase>
-<phrase>
- id: LANG_SYSFONT_RECORDING_SOURCE
- desc: in the recording settings
- user: core
- <source>
- *: none
- recording: "Source"
- </source>
- <dest>
- *: none
- recording: "æ¥æº"
- </dest>
- <voice>
- *: none
- recording: "æ¥æº"
- </voice>
-</phrase>
-<phrase>
- id: LANG_SYSFONT_RECORDING_SRC_MIC
- desc: in the recording settings
- user: core
- <source>
- *: none
- recording: "Int. Mic"
- </source>
- <dest>
- *: none
- recording: "内部麦克风"
- </dest>
- <voice>
- *: none
- recording: "内部麦克风"
- </voice>
-</phrase>
-<phrase>
- id: LANG_SYSFONT_RECORDING_SRC_DIGITAL
- desc: in the recording settings
- user: core
- <source>
- *: none
- recording: "Digital"
- </source>
- <dest>
- *: none
- recording: "æ•°ç "
- </dest>
- <voice>
- *: none
- recording: "æ•°ç "
- </voice>
-</phrase>
-<phrase>
- id: LANG_SYSFONT_RECORD_TRIGGER
- desc: in recording settings_menu
- user: core
- <source>
- *: none
- recording: "Trigger"
- </source>
- <dest>
- *: none
- recording: "触å‘"
- </dest>
- <voice>
- *: none
- recording: "触å‘"
- </voice>
-</phrase>
-<phrase>
- id: LANG_SYSFONT_DIRBROWSE_F1
- desc: in dir browser, F1 button bar text
- user: core
- <source>
- *: none
- recorder_pad: "Menu"
- </source>
- <dest>
- *: none
- recorder_pad: "选å•"
- </dest>
- <voice>
- *: none
- recorder_pad: ""
- </voice>
-</phrase>
-<phrase>
- id: LANG_SYSFONT_DIRBROWSE_F2
- desc: in dir browser, F2 button bar text
- user: core
- <source>
- *: none
- recorder_pad: "Option"
- </source>
- <dest>
- *: none
- recorder_pad: "选项"
- </dest>
- <voice>
- *: none
- recorder_pad: ""
- </voice>
-</phrase>
-<phrase>
- id: LANG_SYSFONT_DIRBROWSE_F3
- desc: in dir browser, F3 button bar text
- user: core
- <source>
- *: none
- recorder_pad: "LCD"
- </source>
- <dest>
- *: none
- recorder_pad: "å±å¹•"
- </dest>
- <voice>
- *: none
- recorder_pad: ""
- </voice>
-</phrase>
-<phrase>
id: LANG_AFMT_PCM_WAV
desc: audio format description
user: core
@@ -7641,7 +7077,7 @@
</dest>
<voice>
*: none
- recording: "PCM Wave"
+ recording: "PCM Wave(.wav)"
</voice>
</phrase>
<phrase>
@@ -7677,33 +7113,13 @@
desc: in lcd settings
user: core
<source>
- *: none
- lcd_sleep: "Never"
+ *: "Never"
</source>
<dest>
- *: none
- lcd_sleep: "从ä¸"
+ *: "从ä¸"
</dest>
<voice>
- *: none
- lcd_sleep: "从ä¸"
- </voice>
-</phrase>
-<phrase>
- id: LANG_SYSFONT_LINE_IN
- desc: in the recording settings
- user: core
- <source>
- *: none
- recording: "Line In"
- </source>
- <dest>
- *: none
- recording: "线路输入"
- </dest>
- <voice>
- *: none
- recording: "线路输入"
+ *: "从ä¸"
</voice>
</phrase>
<phrase>
@@ -7712,7 +7128,6 @@
user: core
<source>
*: "Building database... %d found (OFF to return)"
- archosplayer: "Building DB %d found"
gigabeat*,iaudiom5,iaudiox5,mrobe100,samsungyh*: "Building database... %d found (LEFT to return)"
gogearsa9200: "Building database... %d found (REW to return)"
ipod*,iriverh10,iriverh10_5gb,sansac200*,sansae200*,sansafuze*,vibe500: "Building database... %d found (PREV to return)"
@@ -7720,7 +7135,6 @@
</source>
<dest>
*: "æ•°æ®åº“更新中...已找到%d个(按OFF键返回)"
- archosplayer: "æ•°æ®åº“更新中...已找到%d个"
gigabeat*,iaudiom5,iaudiox5,mrobe100,samsungyh*: "æ•°æ®åº“更新中...已找到%d个(按LEFT键返回)"
gogearsa9200: "æ•°æ®åº“更新中...已找到%d个(按REW键返回)"
ipod*,iriverh10,iriverh10_5gb,sansac200*,sansae200*,sansafuze*,vibe500: "æ•°æ®åº“更新中...已找到%d个(按PREV键返回)"
@@ -8075,20 +7489,17 @@
</voice>
</phrase>
<phrase>
- id: LANG_RECORDING_FORMAT
- desc: audio format item in recording menu
+ id: LANG_FORMAT
+ desc: audio format
user: core
<source>
- *: none
- recording: "Format"
+ *: "Format"
</source>
<dest>
- *: none
- recording: "æ ¼å¼"
+ *: "æ ¼å¼"
</dest>
<voice>
- *: none
- recording: "æ ¼å¼"
+ *: "æ ¼å¼"
</voice>
</phrase>
<phrase>
@@ -8142,11 +7553,9 @@
user: core
<source>
*: "Buffer:"
- archosplayer: "Buf:"
</source>
<dest>
*: "缓冲区:"
- archosplayer: "Buf:"
</dest>
<voice>
*: "缓冲区大å°ä¸º"
@@ -8206,8 +7615,8 @@
user: core
<source>
*: "PLAY = Yes"
- archosplayer: "(PLAY/STOP)"
cowond2*: "MENU, or top-right = Yes"
+ creativezen*: "SELECT = Yes"
gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT = Yes"
iriverh100,iriverh120,iriverh300: "NAVI = Yes"
mrobe500: "PLAY, POWER, or top-right = Yes"
@@ -8215,9 +7624,8 @@
</source>
<dest>
*: "PLAY:是"
- archosplayer: "(PLAY/STOP)"
cowond2*: "MENU或TOP-RIGHT:是"
- gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT:是"
+ creativezen*,gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT:是"
iriverh100,iriverh120,iriverh300: "NAVI=是"
mrobe500: "PLAY,POWER或TOP-RIGHT:是"
vibe500: "OK:是"
@@ -8252,6 +7660,7 @@
</dest>
<voice>
*: none
+ gigabeat*,gogearsa9200,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh100,iriverh10_5gb,iriverh120,iriverh300,mrobe100,samsungyh*,sansac200*,sansae200*: ""
</voice>
</phrase>
<phrase>
@@ -8260,11 +7669,9 @@
user: core
<source>
*: "Any Other = No"
- archosplayer: none
</source>
<dest>
*: "Any Other=å¦"
- archosplayer: none
</dest>
<voice>
*: ""
@@ -8427,7 +7834,7 @@
*: "Main Menu"
</source>
<dest>
- *: "主选å•"
+ *: "主èœå•"
</dest>
<voice>
*: "主选å•"
@@ -8453,15 +7860,12 @@
user: core
<source>
*: "End of Song List"
- archosplayer: "End of List"
</source>
<dest>
*: "曲目列表结æŸ"
- archosplayer: "列表结æŸ"
</dest>
<voice>
*: "曲目列表结æŸ"
- archosplayer: "列表结æŸ"
</voice>
</phrase>
<phrase>
@@ -8561,20 +7965,6 @@
</voice>
</phrase>
<phrase>
- id: LANG_SYSFONT_MODE
- desc: in wps F2 pressed
- user: core
- <source>
- *: "Mode:"
- </source>
- <dest>
- *: "模å¼:"
- </dest>
- <voice>
- *: ""
- </voice>
-</phrase>
-<phrase>
id: LANG_AGC_VOICE
desc: AGC preset
user: core
@@ -8785,16 +8175,16 @@
</phrase>
<phrase>
id: LANG_REPLACE
- desc: in onplay menu. Replace the current playlist with a new one.
+ desc: deprecated
user: core
<source>
- *: "Play Next"
+ *: ""
</source>
<dest>
- *: "播放下一个"
+ *: ""
</dest>
<voice>
- *: "播放下一个"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -9036,7 +8426,7 @@
*: "Context Menu"
</source>
<dest>
- *: "相关选å•"
+ *: "相关èœå•"
</dest>
<voice>
*: "相关选å•"
@@ -9123,10 +8513,10 @@
*: "Show Filename Extensions"
</source>
<dest>
- *: "显示文件ååŽç¼€"
+ *: "显示åŽç¼€å"
</dest>
<voice>
- *: "显示文件ååŽç¼€"
+ *: "显示åŽç¼€å"
</voice>
</phrase>
<phrase>
@@ -9140,7 +8530,7 @@
*: "读å–中...已读å–%d%%(%s)"
</dest>
<voice>
- *: ""
+ *: "读å–中...已读å–%d%%(%s)"
</voice>
</phrase>
<phrase>
@@ -9266,7 +8656,7 @@
</phrase>
<phrase>
id: LANG_DITHERING
- desc: in the sound settings menu
+ desc: in the sound settings and some other menus
user: core
<source>
*: "Dithering"
@@ -9473,23 +8863,6 @@
</voice>
</phrase>
<phrase>
- id: LANG_SYSFONT_CHANNELS
- desc: in the recording settings
- user: core
- <source>
- *: none
- recording: "Channels"
- </source>
- <dest>
- *: none
- recording: "声é“"
- </dest>
- <voice>
- *: none
- recording: "声é“"
- </voice>
-</phrase>
-<phrase>
id: LANG_PLAYLISTS
desc: in the file view setting
user: core
@@ -9657,7 +9030,7 @@
*: "%sä¸å­˜åœ¨"
</dest>
<voice>
- *: "ä¸å­˜åœ¨"
+ *: "%sä¸å­˜åœ¨"
</voice>
</phrase>
<phrase>
@@ -10146,11 +9519,11 @@
</source>
<dest>
*: none
- accessory_supply: "给附件供电"
+ accessory_supply: "为附件供电"
</dest>
<voice>
*: none
- accessory_supply: "给附件供电"
+ accessory_supply: "为附件供电"
</voice>
</phrase>
<phrase>
@@ -10161,10 +9534,10 @@
*: "Unknown"
</source>
<dest>
- *: "ä¸çŸ¥é“"
+ *: "未知"
</dest>
<voice>
- *: "ä¸çŸ¥é“"
+ *: "未知"
</voice>
</phrase>
<phrase>
@@ -10207,15 +9580,15 @@
user: core
<source>
*: none
- gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
</source>
<dest>
*: none
- gigabeatfx,sansafuzeplus: "触摸æ¿çµæ•åº¦"
+ fiiom3k,gigabeatfx,sansafuzeplus: "触摸æ¿çµæ•åº¦"
</dest>
<voice>
*: none
- gigabeatfx,sansafuzeplus: "触摸æ¿çµæ•åº¦"
+ fiiom3k,gigabeatfx,sansafuzeplus: "触摸æ¿çµæ•åº¦"
</voice>
</phrase>
<phrase>
@@ -10914,10 +10287,10 @@
*: "Threshold"
</source>
<dest>
- *: "阀值"
+ *: "阈值"
</dest>
<voice>
- *: "阀值"
+ *: "阈值"
</voice>
</phrase>
<phrase>
@@ -11004,10 +10377,10 @@
*: "Knee"
</source>
<dest>
- *: "柔和度"
+ *: "æ‹ç‚¹"
</dest>
<voice>
- *: "柔和度"
+ *: "æ‹ç‚¹"
</voice>
</phrase>
<phrase>
@@ -11046,10 +10419,10 @@
*: "Soft Knee"
</source>
<dest>
- *: "柔和"
+ *: "软æ‹ç‚¹"
</dest>
<voice>
- *: "柔和"
+ *: "软æ‹ç‚¹"
</voice>
</phrase>
<phrase>
@@ -11105,10 +10478,10 @@
*: "Hard Knee"
</source>
<dest>
- *: "é”利"
+ *: "硬æ‹ç‚¹"
</dest>
<voice>
- *: "é”利"
+ *: "硬æ‹ç‚¹"
</voice>
</phrase>
<phrase>
@@ -11125,7 +10498,7 @@
</dest>
<voice>
*: none
- quickscreen: "Set as Top Quickscreen Item"
+ quickscreen: "设为快å±ä¸Šé”®"
</voice>
</phrase>
<phrase>
@@ -11483,10 +10856,10 @@
*: "Timestretch"
</source>
<dest>
- *: "音调/音速"
+ *: "时间伸缩"
</dest>
<voice>
- *: "音调/音速"
+ *: "时间伸缩"
</voice>
</phrase>
<phrase>
@@ -11726,15 +11099,15 @@
user: core
<source>
*: none
- gigabeats: "Band %d Gain"
+ gigabeats,samsungypr1: "Band %d Gain"
</source>
<dest>
*: none
- gigabeats: "频段 %d 增益"
+ gigabeats,samsungypr1: "频段 %d 增益"
</dest>
<voice>
*: none
- gigabeats: "频段 增益"
+ gigabeats,samsungypr1: "频段 %d 增益"
</voice>
</phrase>
<phrase>
@@ -11870,15 +11243,15 @@
user: core
<source>
*: none
- creativezenxfi2,creativezenxfi3,gigabeats,mpiohd200,mpiohd300,sansafuzeplus: "3-D Enhancement"
+ depth_3d: "3-D Enhancement"
</source>
<dest>
*: none
- creativezenxfi2,creativezenxfi3,gigabeats,mpiohd200,mpiohd300,sansafuzeplus: "3-D增强"
+ depth_3d: "3-D增强"
</dest>
<voice>
*: none
- creativezenxfi2,creativezenxfi3,gigabeats,mpiohd200,mpiohd300,sansafuzeplus: "三维增强"
+ depth_3d: "三维增强"
</voice>
</phrase>
<phrase>
@@ -11918,15 +11291,15 @@
user: core
<source>
*: none
- gigabeats: "Band %d Frequency"
+ gigabeats,samsungypr1: "Band %d Frequency"
</source>
<dest>
*: none
- gigabeats: "频段 %d 频率"
+ gigabeats,samsungypr1: "频段 %d 频率"
</dest>
<voice>
*: none
- gigabeats: "频段 的频率"
+ gigabeats,samsungypr1: "频段 %d 的频率"
</voice>
</phrase>
<phrase>
@@ -11935,15 +11308,15 @@
user: core
<source>
*: none
- gigabeats: "Band %d Width"
+ gigabeats,samsungypr1: "Band %d Width"
</source>
<dest>
*: none
- gigabeats: "频段 %d 宽度"
+ gigabeats,samsungypr1: "频段 %d 宽度"
</dest>
<voice>
*: none
- gigabeats: "频段 的宽度"
+ gigabeats,samsungypr1: "频段 %d 的宽度"
</voice>
</phrase>
<phrase>
@@ -12031,15 +11404,15 @@
user: core
<source>
*: none
- gigabeats: "Wide"
+ gigabeats,samsungypr1: "Wide"
</source>
<dest>
*: none
- gigabeats: "宽度"
+ gigabeats,samsungypr1: "宽度"
</dest>
<voice>
*: none
- gigabeats: "宽度"
+ gigabeats,samsungypr1: "宽度"
</voice>
</phrase>
<phrase>
@@ -12076,15 +11449,15 @@
user: core
<source>
*: none
- gigabeats: "Narrow"
+ gigabeats,samsungypr1: "Narrow"
</source>
<dest>
*: none
- gigabeats: "窄"
+ gigabeats,samsungypr1: "窄"
</dest>
<voice>
*: none
- gigabeats: "窄"
+ gigabeats,samsungypr1: "窄"
</voice>
</phrase>
<phrase>
@@ -12093,15 +11466,15 @@
user: core
<source>
*: none
- gigabeats: "Advanced Tone Control Settings"
+ gigabeats,samsungypr1: "Advanced Tone Control Settings"
</source>
<dest>
*: none
- gigabeats: "高级音调控制设置"
+ gigabeats,samsungypr1: "高级硬件å‡è¡¡å™¨"
</dest>
<voice>
*: none
- gigabeats: "高级音调控制设置"
+ gigabeats,samsungypr1: "高级硬件å‡è¡¡å™¨"
</voice>
</phrase>
<phrase>
@@ -12155,15 +11528,15 @@
user: core
<source>
*: none
- gigabeats: "Tone Controls"
+ gigabeats,samsungypr1: "Tone Controls"
</source>
<dest>
*: none
- gigabeats: "音调控制"
+ gigabeats,samsungypr1: "硬件å‡è¡¡å™¨"
</dest>
<voice>
*: none
- gigabeats: "音调控制"
+ gigabeats,samsungypr1: "硬件å‡è¡¡å™¨"
</voice>
</phrase>
<phrase>
@@ -12549,6 +11922,20 @@
</voice>
</phrase>
<phrase>
+ id: LANG_SELECT_FOLDER
+ desc: in settings_menu
+ user: core
+ <source>
+ *: "Select one or more directories"
+ </source>
+ <dest>
+ *: "请选择一个或多个文件夹:"
+ </dest>
+ <voice>
+ *: "请选择一个或多个文件夹:"
+ </voice>
+</phrase>
+<phrase>
id: LANG_SELECT_DATABASE_DIRS
desc: in settings_menu
user: core
@@ -12559,7 +11946,7 @@
*: "仅扫æ指定文件夹"
</dest>
<voice>
- *: "扫æ媒体时扫æ指定文件夹"
+ *: "仅扫æ指定文件夹"
</voice>
</phrase>
<phrase>
@@ -12577,6 +11964,20 @@
</voice>
</phrase>
<phrase>
+ id: LANG_SURROUND
+ desc: in the sound settings menu
+ user: core
+ <source>
+ *: "Haas Surround"
+ </source>
+ <dest>
+ *: "哈斯环绕"
+ </dest>
+ <voice>
+ *: "哈斯环绕音效"
+ </voice>
+</phrase>
+<phrase>
id: LANG_FILTER_ROLL_OFF
desc: in sound settings
user: core
@@ -12656,20 +12057,6 @@
</voice>
</phrase>
<phrase>
- id: LANG_SURROUND
- desc: in the sound settings menu
- user: core
- <source>
- *: "Haas Surround"
- </source>
- <dest>
- *: "哈斯环绕"
- </dest>
- <voice>
- *: "哈斯环绕音效"
- </voice>
-</phrase>
-<phrase>
id: LANG_SURROUND_FX1
desc: in sound settings
user: core
diff --git a/apps/lang/chinese-trad.lang b/apps/lang/chinese-trad.lang
index 5af129590b..e7ee7ae607 100644
--- a/apps/lang/chinese-trad.lang
+++ b/apps/lang/chinese-trad.lang
@@ -13,11 +13,14 @@
# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
# KIND, either express or implied.
#
+# LANGUAGE_UNITS_FIRST
+#
# Traditional Chinese language file, translated by:
# - Wenbin Leo
# - Xinlu Huang
# - Harry Tu
# - Tse-Hsien Chiang
+# - papaloco
<phrase>
id: LANG_SET_BOOL_YES
desc: bool true representation
@@ -57,7 +60,7 @@
*: "é–‹å•Ÿ"
</dest>
<voice>
- *: "On"
+ *: "é–‹å•Ÿ"
</voice>
</phrase>
<phrase>
@@ -71,7 +74,7 @@
*: "關閉"
</dest>
<voice>
- *: "Off"
+ *: "關閉"
</voice>
</phrase>
<phrase>
@@ -85,7 +88,7 @@
*: "æ°¸é "
</dest>
<voice>
- *: "Always"
+ *: "æ°¸é "
</voice>
</phrase>
<phrase>
@@ -99,24 +102,7 @@
*: "讀å–中..."
</dest>
<voice>
- *: ""
- </voice>
-</phrase>
-<phrase>
- id: LANG_CONFIRM_SHUTDOWN
- desc: in shutdown screen
- user: core
- <source>
- *: none
- soft_shutdown: "Press OFF to shut down"
- </source>
- <dest>
- *: none
- soft_shutdown: "按下 OFF 關機"
- </dest>
- <voice>
- *: none
- soft_shutdown: ""
+ *: "讀å–中..."
</voice>
</phrase>
<phrase>
@@ -130,24 +116,7 @@
*: "關機中..."
</dest>
<voice>
- *: ""
- </voice>
-</phrase>
-<phrase>
- id: LANG_REMOVE_MMC
- desc: before acknowledging usb in case an MMC is inserted (Ondio)
- user: core
- <source>
- *: none
- archosondio*: "Please remove inserted MMC"
- </source>
- <dest>
- *: none
- archosondio*: "請移除æ’入的 MMC å¡"
- </dest>
- <voice>
- *: "Please remove multimedia card"
- archosondio*: "Please remove multimedia card"
+ *: "關機中..."
</voice>
</phrase>
<phrase>
@@ -161,7 +130,7 @@
*: "失敗"
</dest>
<voice>
- *: ""
+ *: "失敗"
</voice>
</phrase>
<phrase>
@@ -175,7 +144,7 @@
*: "最近的書籤"
</dest>
<voice>
- *: "Recent Bookmarks"
+ *: "最近的書籤"
</voice>
</phrase>
<phrase>
@@ -203,7 +172,7 @@
*: "一般設定"
</dest>
<voice>
- *: "General Settings"
+ *: "一般設定"
</voice>
</phrase>
<phrase>
@@ -217,7 +186,7 @@
*: "管ç†è¨­å®š"
</dest>
<voice>
- *: "Manage Settings"
+ *: "管ç†è¨­å®š"
</voice>
</phrase>
<phrase>
@@ -248,7 +217,7 @@
</dest>
<voice>
*: none
- radio: "FM Radio"
+ radio: "æ”¶è½ FM 廣播"
</voice>
</phrase>
<phrase>
@@ -265,7 +234,7 @@
</dest>
<voice>
*: none
- recording: "Recording"
+ recording: "錄音"
</voice>
</phrase>
<phrase>
@@ -279,24 +248,7 @@
*: "æ’件"
</dest>
<voice>
- *: "Plugins"
- </voice>
-</phrase>
-<phrase>
- id: LANG_SHUTDOWN
- desc: in main menu
- user: core
- <source>
- *: none
- soft_shutdown: "Shut down"
- </source>
- <dest>
- *: none
- soft_shutdown: "關機"
- </dest>
- <voice>
- *: none
- soft_shutdown: "Shut down"
+ *: "æ’件"
</voice>
</phrase>
<phrase>
@@ -310,7 +262,7 @@
*: "音é‡"
</dest>
<voice>
- *: "Volume"
+ *: "音é‡"
</voice>
</phrase>
<phrase>
@@ -324,7 +276,7 @@
*: "低頻音é‡"
</dest>
<voice>
- *: "Bass"
+ *: "低頻音é‡"
</voice>
</phrase>
<phrase>
@@ -338,7 +290,7 @@
*: "高頻音é‡"
</dest>
<voice>
- *: "Treble"
+ *: "高頻音é‡"
</voice>
</phrase>
<phrase>
@@ -352,7 +304,7 @@
*: "平衡"
</dest>
<voice>
- *: "Balance"
+ *: "平衡"
</voice>
</phrase>
<phrase>
@@ -366,7 +318,7 @@
*: "ç«‹é«”è²"
</dest>
<voice>
- *: "Stereo"
+ *: "ç«‹é«”è²"
</voice>
</phrase>
<phrase>
@@ -380,7 +332,7 @@
*: "å–®è²é“"
</dest>
<voice>
- *: "Mono"
+ *: "å–®è²é“"
</voice>
</phrase>
<phrase>
@@ -394,7 +346,7 @@
*: "自訂"
</dest>
<voice>
- *: "Custom"
+ *: "自訂"
</voice>
</phrase>
<phrase>
@@ -408,7 +360,7 @@
*: "å–®è²é“ (å·¦)"
</dest>
<voice>
- *: "Mono Left"
+ *: "å–®è²é“ (å·¦)"
</voice>
</phrase>
<phrase>
@@ -422,7 +374,7 @@
*: "å–®è²é“ (å³)"
</dest>
<voice>
- *: "Mono Right"
+ *: "å–®è²é“ (å³)"
</voice>
</phrase>
<phrase>
@@ -436,7 +388,7 @@
*: "å¡å•¦ OK"
</dest>
<voice>
- *: "Karaoke"
+ *: "å¡å•¦ OK"
</voice>
</phrase>
<phrase>
@@ -450,160 +402,7 @@
*: "ç«‹é«”è²å¯¬åº¦"
</dest>
<voice>
- *: "Stereo Width"
- </voice>
-</phrase>
-<phrase>
- id: LANG_LOUDNESS
- desc: in sound_settings
- user: core
- <source>
- *: none
- masf: "Loudness"
- </source>
- <dest>
- *: none
- masf: "響度"
- </dest>
- <voice>
- *: none
- masf: ""
- </voice>
-</phrase>
-<phrase>
- id: LANG_AUTOVOL
- desc: in sound_settings
- user: core
- <source>
- *: none
- masf: "Auto Volume"
- </source>
- <dest>
- *: none
- masf: "自動音é‡èª¿æ•´"
- </dest>
- <voice>
- *: none
- masf: ""
- </voice>
-</phrase>
-<phrase>
- id: LANG_DECAY
- desc: in sound_settings
- user: core
- <source>
- *: none
- masf: "AV Decay Time"
- </source>
- <dest>
- *: none
- masf: "自動音é‡èª¿æ•´è¡°æ¸›æ™‚é–“"
- </dest>
- <voice>
- *: none
- masf: ""
- </voice>
-</phrase>
-<phrase>
- id: LANG_SUPERBASS
- desc: in sound settings
- user: core
- <source>
- *: none
- masf: "Super Bass"
- </source>
- <dest>
- *: none
- masf: "超低音"
- </dest>
- <voice>
- *: none
- masf: ""
- </voice>
-</phrase>
-<phrase>
- id: LANG_MDB_ENABLE
- desc: in sound settings
- user: core
- <source>
- *: none
- masf: "MDB Enable"
- </source>
- <dest>
- *: none
- masf: "MDB 有效"
- </dest>
- <voice>
- *: none
- masf: ""
- </voice>
-</phrase>
-<phrase>
- id: LANG_MDB_STRENGTH
- desc: in sound settings
- user: core
- <source>
- *: none
- masf: "MDB Strength"
- </source>
- <dest>
- *: none
- masf: "MDB 強度"
- </dest>
- <voice>
- *: none
- masf: ""
- </voice>
-</phrase>
-<phrase>
- id: LANG_MDB_HARMONICS
- desc: in sound settings
- user: core
- <source>
- *: none
- masf: "MDB Harmonics"
- </source>
- <dest>
- *: none
- masf: "MDB 泛音"
- </dest>
- <voice>
- *: none
- masf: ""
- </voice>
-</phrase>
-<phrase>
- id: LANG_MDB_CENTER
- desc: in sound settings
- user: core
- <source>
- *: none
- masf: "MDB Centre Frequency"
- </source>
- <dest>
- *: none
- masf: "MDB 中心頻率"
- </dest>
- <voice>
- *: none
- masf: ""
- </voice>
-</phrase>
-<phrase>
- id: LANG_MDB_SHAPE
- desc: in sound settings
- user: core
- <source>
- *: none
- masf: "MDB Shape"
- </source>
- <dest>
- *: none
- masf: "MDB 形狀"
- </dest>
- <voice>
- *: none
- masf: ""
+ *: "ç«‹é«”è²å¯¬åº¦"
</voice>
</phrase>
<phrase>
@@ -617,7 +416,7 @@
*: "交å‰ä¾›éŸ³"
</dest>
<voice>
- *: ""
+ *: "交å‰ä¾›éŸ³"
</voice>
</phrase>
<phrase>
@@ -631,7 +430,7 @@
*: "等化器"
</dest>
<voice>
- *: ""
+ *: "等化器"
</voice>
</phrase>
<phrase>
@@ -659,7 +458,7 @@
*: "檔案檢視設定"
</dest>
<voice>
- *: "File View"
+ *: "檔案檢視設定"
</voice>
</phrase>
<phrase>
@@ -673,7 +472,7 @@
*: "顯示設定"
</dest>
<voice>
- *: "Display"
+ *: "顯示設定"
</voice>
</phrase>
<phrase>
@@ -687,7 +486,7 @@
*: "系統設定"
</dest>
<voice>
- *: "System"
+ *: "系統設定"
</voice>
</phrase>
<phrase>
@@ -701,7 +500,7 @@
*: "書籤設定"
</dest>
<voice>
- *: "Bookmarking"
+ *: "書籤設定"
</voice>
</phrase>
<phrase>
@@ -715,7 +514,7 @@
*: "語言設定"
</dest>
<voice>
- *: "Language"
+ *: "語言設定"
</voice>
</phrase>
<phrase>
@@ -729,7 +528,7 @@
*: "語音"
</dest>
<voice>
- *: "Voice"
+ *: "語音"
</voice>
</phrase>
<phrase>
@@ -743,7 +542,7 @@
*: "ç€è¦½ .cfg 檔案"
</dest>
<voice>
- *: "Browse configuration files"
+ *: "ç€è¦½ .cfg 檔案"
</voice>
</phrase>
<phrase>
@@ -757,7 +556,7 @@
*: "é‡æ–°è¨­å®š"
</dest>
<voice>
- *: "Reset Settings"
+ *: "é‡æ–°è¨­å®š"
</voice>
</phrase>
<phrase>
@@ -771,7 +570,7 @@
*: "已清除"
</dest>
<voice>
- *: ""
+ *: "已清除"
</voice>
</phrase>
<phrase>
@@ -802,7 +601,7 @@
</dest>
<voice>
*: none
- recording: ""
+ recording: "錄音設定"
</voice>
</phrase>
<phrase>
@@ -816,7 +615,7 @@
*: "開啟等化器"
</dest>
<voice>
- *: ""
+ *: "開啟等化器"
</voice>
</phrase>
<phrase>
@@ -830,7 +629,7 @@
*: "圖解等化器"
</dest>
<voice>
- *: ""
+ *: "圖解等化器"
</voice>
</phrase>
<phrase>
@@ -844,7 +643,7 @@
*: "é åˆ‡"
</dest>
<voice>
- *: ""
+ *: "é åˆ‡"
</voice>
</phrase>
<phrase>
@@ -858,7 +657,7 @@
*: "簡易等化器設定"
</dest>
<voice>
- *: ""
+ *: "簡易等化器設定"
</voice>
</phrase>
<phrase>
@@ -872,7 +671,7 @@
*: "進階等化器設定"
</dest>
<voice>
- *: ""
+ *: "進階等化器設定"
</voice>
</phrase>
<phrase>
@@ -886,7 +685,7 @@
*: "儲存等化器設定"
</dest>
<voice>
- *: ""
+ *: "儲存等化器設定"
</voice>
</phrase>
<phrase>
@@ -900,7 +699,7 @@
*: "ç€è¦½ç­‰åŒ–器設定"
</dest>
<voice>
- *: ""
+ *: "ç€è¦½ç­‰åŒ–器設定"
</voice>
</phrase>
<phrase>
@@ -928,7 +727,7 @@
*: "%d Hz 頻帶增益"
</dest>
<voice>
- *: ""
+ *: "%d Hz 頻帶增益"
</voice>
</phrase>
<phrase>
@@ -942,7 +741,7 @@
*: "低峰值éŽæ¿¾"
</dest>
<voice>
- *: ""
+ *: "低峰值éŽæ¿¾"
</voice>
</phrase>
<phrase>
@@ -956,7 +755,7 @@
*: "峰值éŽæ¿¾ %d"
</dest>
<voice>
- *: ""
+ *: "峰值éŽæ¿¾ %d"
</voice>
</phrase>
<phrase>
@@ -970,7 +769,7 @@
*: "高峰值éŽæ¿¾"
</dest>
<voice>
- *: ""
+ *: "高峰值éŽæ¿¾"
</voice>
</phrase>
<phrase>
@@ -984,7 +783,7 @@
*: "截斷頻率"
</dest>
<voice>
- *: ""
+ *: "截斷頻率"
</voice>
</phrase>
<phrase>
@@ -998,7 +797,7 @@
*: "中心頻率"
</dest>
<voice>
- *: ""
+ *: "中心頻率"
</voice>
</phrase>
<phrase>
@@ -1026,7 +825,7 @@
*: "建立播放清單"
</dest>
<voice>
- *: "Create Playlist"
+ *: "建立播放清單"
</voice>
</phrase>
<phrase>
@@ -1040,7 +839,7 @@
*: "檢視ç¾åœ¨çš„播放清單"
</dest>
<voice>
- *: "View Current Playlist"
+ *: "檢視ç¾åœ¨çš„播放清單"
</voice>
</phrase>
<phrase>
@@ -1054,7 +853,7 @@
*: "儲存ç¾åœ¨çš„播放清單"
</dest>
<voice>
- *: "Save Current Playlist"
+ *: "儲存ç¾åœ¨çš„播放清單"
</voice>
</phrase>
<phrase>
@@ -1068,7 +867,7 @@
*: "éžè¿´æ’入目錄"
</dest>
<voice>
- *: "Recursively Insert Directories"
+ *: "éžè¿´æ’入目錄"
</voice>
</phrase>
<phrase>
@@ -1082,7 +881,7 @@
*: "消除動態播放清單時警示"
</dest>
<voice>
- *: ""
+ *: "消除動態播放清單時警示"
</voice>
</phrase>
<phrase>
@@ -1110,7 +909,7 @@
*: "除錯 (請勿進入!)"
</dest>
<voice>
- *: "Debug, keep out!"
+ *: "除錯 (請勿進入!)"
</voice>
</phrase>
<phrase>
@@ -1124,7 +923,7 @@
*: "亂åºæ’­æ”¾"
</dest>
<voice>
- *: "Shuffle"
+ *: "亂åºæ’­æ”¾"
</voice>
</phrase>
<phrase>
@@ -1138,7 +937,7 @@
*: "é‡è¦†"
</dest>
<voice>
- *: "Repeat"
+ *: "é‡è¦†"
</voice>
</phrase>
<phrase>
@@ -1152,7 +951,7 @@
*: "一首"
</dest>
<voice>
- *: "One"
+ *: "一首"
</voice>
</phrase>
<phrase>
@@ -1180,7 +979,7 @@
*: "先播放é¸æ“‡çš„檔案"
</dest>
<voice>
- *: "Play Selected File First"
+ *: "先播放é¸æ“‡çš„檔案"
</voice>
</phrase>
<phrase>
@@ -1194,7 +993,7 @@
*: "快轉/倒轉"
</dest>
<voice>
- *: "Fast forward and Rewind"
+ *: "快轉/倒轉"
</voice>
</phrase>
<phrase>
@@ -1210,7 +1009,7 @@
flash_storage: none
</dest>
<voice>
- *: "Anti-Skip Buffer"
+ *: "防止中斷緩è¡å€"
flash_storage: none
</voice>
</phrase>
@@ -1225,7 +1024,7 @@
*: "åœæ­¢/æš«åœæ™‚淡出"
</dest>
<voice>
- *: "Fade On Stop and Pause"
+ *: "åœæ­¢/æš«åœæ™‚淡出"
</voice>
</phrase>
<phrase>
@@ -1239,7 +1038,7 @@
*: "æ´¾å°æ¨¡å¼"
</dest>
<voice>
- *: "Party Mode"
+ *: "æ´¾å°æ¨¡å¼"
</voice>
</phrase>
<phrase>
@@ -1256,7 +1055,7 @@
</dest>
<voice>
*: none
- crossfade: "Crossfade"
+ crossfade: "交å‰æ·¡è®Š"
</voice>
</phrase>
<phrase>
@@ -1267,10 +1066,10 @@
*: "Replaygain"
</source>
<dest>
- *: "Replaygain"
+ *: "播放增益"
</dest>
<voice>
- *: "Replaygain"
+ *: "播放增益"
</voice>
</phrase>
<phrase>
@@ -1298,7 +1097,7 @@
*: "å¼±"
</dest>
<voice>
- *: ""
+ *: "å¼±"
</voice>
</phrase>
<phrase>
@@ -1312,7 +1111,7 @@
*: "中"
</dest>
<voice>
- *: ""
+ *: "中"
</voice>
</phrase>
<phrase>
@@ -1326,7 +1125,7 @@
*: "å¼·"
</dest>
<voice>
- *: ""
+ *: "å¼·"
</voice>
</phrase>
<phrase>
@@ -1343,7 +1142,7 @@
</dest>
<voice>
*: none
- spdif_power: ""
+ spdif_power: "光纖輸出"
</voice>
</phrase>
<phrase>
@@ -1357,7 +1156,7 @@
*: "自動替æ›ç›®éŒ„"
</dest>
<voice>
- *: "Auto-Change Directory"
+ *: "自動替æ›ç›®éŒ„"
</voice>
</phrase>
<phrase>
@@ -1371,7 +1170,7 @@
*: "資料庫"
</dest>
<voice>
- *: ""
+ *: "資料庫"
</voice>
</phrase>
<phrase>
@@ -1388,7 +1187,7 @@
</dest>
<voice>
*: none
- tc_ramcache: ""
+ tc_ramcache: "載入到記憶體"
</voice>
</phrase>
<phrase>
@@ -1402,7 +1201,7 @@
*: "å³åˆ»åˆå§‹åŒ–"
</dest>
<voice>
- *: "Initialize Now"
+ *: "å³åˆ»åˆå§‹åŒ–"
</voice>
</phrase>
<phrase>
@@ -1416,7 +1215,7 @@
*: "在背景更新中"
</dest>
<voice>
- *: ""
+ *: "在背景更新中"
</voice>
</phrase>
<phrase>
@@ -1430,7 +1229,7 @@
*: "處ç†è³‡æ–™åº«"
</dest>
<voice>
- *: ""
+ *: "處ç†è³‡æ–™åº«"
</voice>
</phrase>
<phrase>
@@ -1444,7 +1243,7 @@
*: "收集é‹è¡Œæ™‚資料"
</dest>
<voice>
- *: "Gather Runtime Data"
+ *: "收集é‹è¡Œæ™‚資料"
</voice>
</phrase>
<phrase>
@@ -1458,7 +1257,7 @@
*: "排åºæ™‚å€åˆ¥å¤§å°å¯«"
</dest>
<voice>
- *: "Sort Case Sensitive"
+ *: "排åºæ™‚å€åˆ¥å¤§å°å¯«"
</voice>
</phrase>
<phrase>
@@ -1472,7 +1271,7 @@
*: "排åºç›®éŒ„"
</dest>
<voice>
- *: "sort directories"
+ *: "排åºç›®éŒ„"
</voice>
</phrase>
<phrase>
@@ -1486,7 +1285,7 @@
*: "排åºæª”案"
</dest>
<voice>
- *: "sort files"
+ *: "排åºæª”案"
</voice>
</phrase>
<phrase>
@@ -1500,7 +1299,7 @@
*: "å­—æ¯é †åº"
</dest>
<voice>
- *: "Alphabetical"
+ *: "å­—æ¯é †åº"
</voice>
</phrase>
<phrase>
@@ -1514,7 +1313,7 @@
*: "ä¾æ—¥æœŸ"
</dest>
<voice>
- *: "By Date"
+ *: "ä¾æ—¥æœŸ"
</voice>
</phrase>
<phrase>
@@ -1528,7 +1327,7 @@
*: "ä¾æœ€æ–°æ—¥æœŸ"
</dest>
<voice>
- *: "By Newest Date"
+ *: "ä¾æœ€æ–°æ—¥æœŸ"
</voice>
</phrase>
<phrase>
@@ -1542,7 +1341,7 @@
*: "ä¾é¡žåž‹"
</dest>
<voice>
- *: "By Type"
+ *: "ä¾é¡žåž‹"
</voice>
</phrase>
<phrase>
@@ -1556,7 +1355,7 @@
*: "顯示檔案"
</dest>
<voice>
- *: "Show Files"
+ *: "顯示檔案"
</voice>
</phrase>
<phrase>
@@ -1570,7 +1369,7 @@
*: "支æ´çš„"
</dest>
<voice>
- *: "Supported"
+ *: "支æ´çš„"
</voice>
</phrase>
<phrase>
@@ -1584,7 +1383,7 @@
*: "音樂"
</dest>
<voice>
- *: "Music"
+ *: "音樂"
</voice>
</phrase>
<phrase>
@@ -1598,7 +1397,7 @@
*: "跟隨播放清單"
</dest>
<voice>
- *: "Follow Playlist"
+ *: "跟隨播放清單"
</voice>
</phrase>
<phrase>
@@ -1612,7 +1411,7 @@
*: "顯示圖示"
</dest>
<voice>
- *: "Show Icons"
+ *: "顯示圖示"
</voice>
</phrase>
<phrase>
@@ -1626,7 +1425,7 @@
*: "ç€è¦½å­—å½¢"
</dest>
<voice>
- *: "å­—å½¢"
+ *: "ç€è¦½å­—å½¢"
</voice>
</phrase>
<phrase>
@@ -1657,7 +1456,7 @@
</dest>
<voice>
*: none
- remote: ""
+ remote: "ç€è¦½ .rwps 檔案"
</voice>
</phrase>
<phrase>
@@ -1671,7 +1470,7 @@
*: "液晶顯示設定"
</dest>
<voice>
- *: "LCD Settings"
+ *: "液晶顯示設定"
</voice>
</phrase>
<phrase>
@@ -1688,7 +1487,7 @@
</dest>
<voice>
*: none
- remote: ""
+ remote: "線控液晶顯示設定"
</voice>
</phrase>
<phrase>
@@ -1702,7 +1501,7 @@
*: "æ²å‹•è¨­å®š"
</dest>
<voice>
- *: "Scrolling"
+ *: "æ²å‹•è¨­å®š"
</voice>
</phrase>
<phrase>
@@ -1716,7 +1515,7 @@
*: "狀態列/æ²å‹•è»¸"
</dest>
<voice>
- *: ""
+ *: "狀態列/æ²å‹•è»¸"
</voice>
</phrase>
<phrase>
@@ -1725,15 +1524,12 @@
user: core
<source>
*: "Peak Meter"
- masd: none
</source>
<dest>
*: "高峰顯示表"
- masd: none
</dest>
<voice>
- *: ""
- masd: none
+ *: "高峰顯示表"
</voice>
</phrase>
<phrase>
@@ -1747,7 +1543,7 @@
*: "é è¨­ç·¨ç¢¼"
</dest>
<voice>
- *: "Default codepage"
+ *: "é è¨­ç·¨ç¢¼"
</voice>
</phrase>
<phrase>
@@ -1873,7 +1669,7 @@
*: "日文(SJIS)"
</dest>
<voice>
- *: "Japanese"
+ *: "日文(SJIS)"
</voice>
</phrase>
<phrase>
@@ -1887,7 +1683,7 @@
*: "簡體中文 (GB2312)"
</dest>
<voice>
- *: ""
+ *: "簡體中文 (GB2312)"
</voice>
</phrase>
<phrase>
@@ -1901,7 +1697,7 @@
*: "韓文 (KSX1001)"
</dest>
<voice>
- *: ""
+ *: "韓文 (KSX1001)"
</voice>
</phrase>
<phrase>
@@ -1915,7 +1711,7 @@
*: "正體中文 (BIG5)"
</dest>
<voice>
- *: ""
+ *: "正體中文 (BIG5)"
</voice>
</phrase>
<phrase>
@@ -1929,7 +1725,7 @@
*: "è¬åœ‹ç¢¼ (UTF-8)"
</dest>
<voice>
- *: "Unicode"
+ *: "è¬åœ‹ç¢¼ (UTF-8)"
</voice>
</phrase>
<phrase>
@@ -1943,7 +1739,7 @@
*: "電池"
</dest>
<voice>
- *: "Battery"
+ *: "電池"
</voice>
</phrase>
<phrase>
@@ -1957,7 +1753,7 @@
*: "硬碟"
</dest>
<voice>
- *: "Disk"
+ *: "硬碟"
</voice>
</phrase>
<phrase>
@@ -1974,7 +1770,7 @@
</dest>
<voice>
*: none
- rtc: ""
+ rtc: "時間與日期"
</voice>
</phrase>
<phrase>
@@ -1988,7 +1784,7 @@
*: "閒置關閉電æº"
</dest>
<voice>
- *: "Idle Poweroff"
+ *: "閒置關閉電æº"
</voice>
</phrase>
<phrase>
@@ -2002,7 +1798,7 @@
*: "ç¡çœ å®šæ™‚"
</dest>
<voice>
- *: "Sleep Timer"
+ *: "ç¡çœ å®šæ™‚"
</voice>
</phrase>
<phrase>
@@ -2019,7 +1815,7 @@
</dest>
<voice>
*: none
- alarm: ""
+ alarm: "鬧鈴"
</voice>
</phrase>
<phrase>
@@ -2033,7 +1829,7 @@
*: "é™åˆ¶"
</dest>
<voice>
- *: "Limits"
+ *: "é™åˆ¶"
</voice>
</phrase>
<phrase>
@@ -2050,7 +1846,7 @@
</dest>
<voice>
*: none
- recording: ""
+ recording: "è²éŸ³è¼¸å…¥"
</voice>
</phrase>
<phrase>
@@ -2067,7 +1863,7 @@
</dest>
<voice>
*: none
- charging: ""
+ charging: "車用變壓器模å¼"
</voice>
</phrase>
<phrase>
@@ -2081,7 +1877,7 @@
*: "åœæ­¢æ™‚建立書籤"
</dest>
<voice>
- *: "Bookmark on Stop"
+ *: "åœæ­¢æ™‚建立書籤"
</voice>
</phrase>
<phrase>
@@ -2095,7 +1891,7 @@
*: "是 - åªå­˜åœ¨æœ€è¿‘書籤"
</dest>
<voice>
- *: "Yes - Recent only"
+ *: "是 - åªå­˜åœ¨æœ€è¿‘書籤"
</voice>
</phrase>
<phrase>
@@ -2109,7 +1905,7 @@
*: "è©¢å• - åªå­˜åœ¨æœ€è¿‘書籤"
</dest>
<voice>
- *: "Ask - Recent only"
+ *: "è©¢å• - åªå­˜åœ¨æœ€è¿‘書籤"
</voice>
</phrase>
<phrase>
@@ -2123,7 +1919,7 @@
*: "載入最近書籤"
</dest>
<voice>
- *: "Load Last Bookmark"
+ *: "載入最近書籤"
</voice>
</phrase>
<phrase>
@@ -2137,21 +1933,7 @@
*: "維護最近書籤清單?"
</dest>
<voice>
- *: "Maintain a List of Recent Bookmarks?"
- </voice>
-</phrase>
-<phrase>
- id: LANG_BOOKMARK_SETTINGS_UNIQUE_ONLY
- desc: Save only on bookmark for each playlist in recent bookmarks
- user: core
- <source>
- *: "Unique only"
- </source>
- <dest>
- *: "åªæœ‰å”¯ä¸€çš„"
- </dest>
- <voice>
- *: "Unique only"
+ *: "維護最近書籤清單?"
</voice>
</phrase>
<phrase>
@@ -2165,7 +1947,7 @@
*: "語音é¸å–®"
</dest>
<voice>
- *: "Voice Menus"
+ *: "語音é¸å–®"
</voice>
</phrase>
<phrase>
@@ -2179,7 +1961,7 @@
*: "語音目錄"
</dest>
<voice>
- *: "Voice Directories"
+ *: "語音目錄"
</voice>
</phrase>
<phrase>
@@ -2193,7 +1975,7 @@
*: "語音檔案å稱"
</dest>
<voice>
- *: "Voice Filenames"
+ *: "語音檔案å稱"
</voice>
</phrase>
<phrase>
@@ -2207,7 +1989,7 @@
*: "數字"
</dest>
<voice>
- *: "Numbers"
+ *: "數字"
</voice>
</phrase>
<phrase>
@@ -2221,7 +2003,7 @@
*: "拼字"
</dest>
<voice>
- *: "Spell"
+ *: "拼字"
</voice>
</phrase>
<phrase>
@@ -2239,23 +2021,6 @@
</voice>
</phrase>
<phrase>
- id: LANG_RECORDING_QUALITY
- desc: in the recording settings
- user: core
- <source>
- *: none
- recording_hwcodec: "Quality"
- </source>
- <dest>
- *: none
- recording_hwcodec: "å“質"
- </dest>
- <voice>
- *: none
- recording_hwcodec: ""
- </voice>
-</phrase>
-<phrase>
id: LANG_FREQUENCY
desc: in recording and playback settings
user: core
@@ -2269,7 +2034,7 @@
</dest>
<voice>
*: none
- play_frequency,recording: ""
+ play_frequency,recording: "頻率"
</voice>
</phrase>
<phrase>
@@ -2286,7 +2051,7 @@
</dest>
<voice>
*: none
- recording: ""
+ recording: "來æº"
</voice>
</phrase>
<phrase>
@@ -2305,7 +2070,8 @@
</dest>
<voice>
*: none
- iriverh100,iriverh120,iriverh300,recording: ""
+ iriverh100,iriverh120,iriverh300: "內置麥克風"
+ recording: "麥克風"
</voice>
</phrase>
<phrase>
@@ -2322,24 +2088,7 @@
</dest>
<voice>
*: none
- recording: ""
- </voice>
-</phrase>
-<phrase>
- id: LANG_RECORDING_EDITABLE
- desc: Editable recordings setting
- user: core
- <source>
- *: none
- recording_hwcodec: "Independent Frames"
- </source>
- <dest>
- *: none
- recording_hwcodec: "ç¨ç«‹æ¡†æž¶"
- </dest>
- <voice>
- *: none
- recording_hwcodec: ""
+ recording: "數ä½"
</voice>
</phrase>
<phrase>
@@ -2356,7 +2105,7 @@
</dest>
<voice>
*: none
- recording: ""
+ recording: "檔案分割é¸é …"
</voice>
</phrase>
<phrase>
@@ -2373,7 +2122,7 @@
</dest>
<voice>
*: none
- recording: ""
+ recording: "é éŒ„時間"
</voice>
</phrase>
<phrase>
@@ -2390,7 +2139,7 @@
</dest>
<voice>
*: none
- recording: ""
+ recording: "目錄"
</voice>
</phrase>
<phrase>
@@ -2407,7 +2156,7 @@
</dest>
<voice>
*: none
- recording: ""
+ recording: "觸發"
</voice>
</phrase>
<phrase>
@@ -2424,7 +2173,7 @@
</dest>
<voice>
*: none
- recording: ""
+ recording: "削波背光"
</voice>
</phrase>
<phrase>
@@ -2441,7 +2190,7 @@
</dest>
<voice>
*: none
- remote: ""
+ remote: "åªæœ‰ä¸»æ©Ÿ"
</voice>
</phrase>
<phrase>
@@ -2458,7 +2207,7 @@
</dest>
<voice>
*: none
- remote: ""
+ remote: "åªæœ‰ç·šæŽ§"
</voice>
</phrase>
<phrase>
@@ -2475,7 +2224,7 @@
</dest>
<voice>
*: none
- remote: ""
+ remote: "主機與線控"
</voice>
</phrase>
<phrase>
@@ -2489,7 +2238,7 @@
*: "快轉/倒轉最å°é–“éš”"
</dest>
<voice>
- *: "Minimum Step"
+ *: "快轉/倒轉最å°é–“éš”"
</voice>
</phrase>
<phrase>
@@ -2503,7 +2252,7 @@
*: "快轉/倒轉加速"
</dest>
<voice>
- *: "Acceleration"
+ *: "快轉/倒轉加速"
</voice>
</phrase>
<phrase>
@@ -2520,7 +2269,7 @@
</dest>
<voice>
*: none
- crossfade: ""
+ crossfade: "é–‹å•ŸCrossfade"
</voice>
</phrase>
<phrase>
@@ -2537,7 +2286,7 @@
</dest>
<voice>
*: none
- crossfade: "Manual Track Skip Only"
+ crossfade: "åªåœ¨è·³åˆ°å¦ä¸€é¦–時"
</voice>
</phrase>
<phrase>
@@ -2554,7 +2303,7 @@
</dest>
<voice>
*: none
- crossfade: "Fade-In Delay"
+ crossfade: "淡入延é²"
</voice>
</phrase>
<phrase>
@@ -2571,7 +2320,7 @@
</dest>
<voice>
*: none
- crossfade: "Fade-In Duration"
+ crossfade: "淡入期間"
</voice>
</phrase>
<phrase>
@@ -2588,7 +2337,7 @@
</dest>
<voice>
*: none
- crossfade: "Fade-Out Delay"
+ crossfade: "淡出延é²"
</voice>
</phrase>
<phrase>
@@ -2605,7 +2354,7 @@
</dest>
<voice>
*: none
- crossfade: "Fade-Out Duration"
+ crossfade: "淡出期間"
</voice>
</phrase>
<phrase>
@@ -2622,7 +2371,7 @@
</dest>
<voice>
*: none
- crossfade: "Fade-Out Mode"
+ crossfade: "淡出模å¼"
</voice>
</phrase>
<phrase>
@@ -2639,7 +2388,7 @@
</dest>
<voice>
*: none
- crossfade: "Mix"
+ crossfade: "æ··åˆ"
</voice>
</phrase>
<phrase>
@@ -2653,7 +2402,7 @@
*: "防止削波"
</dest>
<voice>
- *: "Prevent Clipping"
+ *: "防止削波"
</voice>
</phrase>
<phrase>
@@ -2664,10 +2413,10 @@
*: "Replaygain Type"
</source>
<dest>
- *: "Replaygain 種類"
+ *: "播放增益種類"
</dest>
<voice>
- *: "Replaygain Type"
+ *: "播放增益種類"
</voice>
</phrase>
<phrase>
@@ -2678,10 +2427,10 @@
*: "Album Gain"
</source>
<dest>
- *: "Album Gain"
+ *: "專輯增益"
</dest>
<voice>
- *: "Album Gain"
+ *: "專輯增益"
</voice>
</phrase>
<phrase>
@@ -2692,10 +2441,10 @@
*: "Track Gain"
</source>
<dest>
- *: "Track Gain"
+ *: "單曲增益"
</dest>
<voice>
- *: "Track Gain"
+ *: "單曲增益"
</voice>
</phrase>
<phrase>
@@ -2706,10 +2455,10 @@
*: "Track Gain if Shuffling"
</source>
<dest>
- *: "亂åºæ’­æ”¾æ™‚使用 Track Gain"
+ *: "亂åºæ’­æ”¾æ™‚使用單曲增益"
</dest>
<voice>
- *: "Track Gain if Shuffling"
+ *: "亂åºæ’­æ”¾æ™‚使用單曲增益"
</voice>
</phrase>
<phrase>
@@ -2720,10 +2469,10 @@
*: "Pre-amp"
</source>
<dest>
- *: "å‰ç½®æ”¾å¤§"
+ *: "å‰ç´šæ”¾å¤§å™¨"
</dest>
<voice>
- *: "Preamp"
+ *: "å‰ç´šæ”¾å¤§å™¨"
</voice>
</phrase>
<phrase>
@@ -2737,7 +2486,7 @@
*: "背光"
</dest>
<voice>
- *: "Backlight"
+ *: "背光"
</voice>
</phrase>
<phrase>
@@ -2754,7 +2503,7 @@
</dest>
<voice>
*: none
- charging: "Backlight (While Plugged In)"
+ charging: "背光 (使用外部電æºæ™‚)"
</voice>
</phrase>
<phrase>
@@ -2768,7 +2517,7 @@
*: "標題背光"
</dest>
<voice>
- *: "Caption Backlight"
+ *: "標題背光"
</voice>
</phrase>
<phrase>
@@ -2785,7 +2534,7 @@
</dest>
<voice>
*: none
- backlight_fade*: "Backlight Fade In"
+ backlight_fade*: "背光淡入"
</voice>
</phrase>
<phrase>
@@ -2802,7 +2551,7 @@
</dest>
<voice>
*: none
- backlight_fade*: "Backlight Fade Out"
+ backlight_fade*: "背光淡出"
</voice>
</phrase>
<phrase>
@@ -2819,7 +2568,7 @@
</dest>
<voice>
*: none
- backlight_brightness: "Brightness"
+ backlight_brightness: "亮度"
</voice>
</phrase>
<phrase>
@@ -2833,7 +2582,7 @@
*: "å°æ¯”"
</dest>
<voice>
- *: "Contrast"
+ *: "å°æ¯”"
</voice>
</phrase>
<phrase>
@@ -2864,7 +2613,7 @@
</dest>
<voice>
*: none
- lcd_invert,remote_lcd_invert: "LCD Mode"
+ lcd_invert,remote_lcd_invert: "液晶顯示模å¼"
</voice>
</phrase>
<phrase>
@@ -2881,7 +2630,7 @@
</dest>
<voice>
*: none
- lcd_invert,remote_lcd_invert: "Inverse"
+ lcd_invert,remote_lcd_invert: "å轉"
</voice>
</phrase>
<phrase>
@@ -2895,7 +2644,7 @@
*: "上下å轉"
</dest>
<voice>
- *: "Upside Down"
+ *: "上下å轉"
</voice>
</phrase>
<phrase>
@@ -2923,7 +2672,7 @@
*: "指示器"
</dest>
<voice>
- *: "Pointer"
+ *: "指示器"
</voice>
</phrase>
<phrase>
@@ -2937,7 +2686,7 @@
*: "æ•´è¡Œå白"
</dest>
<voice>
- *: "Inverse Bar"
+ *: "æ•´è¡Œå白"
</voice>
</phrase>
<phrase>
@@ -2954,7 +2703,7 @@
</dest>
<voice>
*: none
- lcd_non-mono: "Clear Backdrop"
+ lcd_non-mono: "清除背景"
</voice>
</phrase>
<phrase>
@@ -2971,7 +2720,7 @@
</dest>
<voice>
*: none
- lcd_color: "Background Colour"
+ lcd_color: "背景é¡è‰²"
</voice>
</phrase>
<phrase>
@@ -2988,7 +2737,7 @@
</dest>
<voice>
*: none
- lcd_color: "Foreground Colour"
+ lcd_color: "å‰æ™¯é¡è‰²"
</voice>
</phrase>
<phrase>
@@ -3005,7 +2754,7 @@
</dest>
<voice>
*: none
- lcd_color: "Reset Colours"
+ lcd_color: "é‡è¨­é¡è‰²"
</voice>
</phrase>
<phrase>
@@ -3022,7 +2771,7 @@
</dest>
<voice>
*: none
- remote_ticking: "Reduce Ticking"
+ remote_ticking: "減少滴答è²"
</voice>
</phrase>
<phrase>
@@ -3036,7 +2785,7 @@
*: "æ²å‹•é€Ÿåº¦"
</dest>
<voice>
- *: "Scroll Speed"
+ *: "æ²å‹•é€Ÿåº¦"
</voice>
</phrase>
<phrase>
@@ -3064,7 +2813,7 @@
*: "æ²å‹•é–‹å§‹å»¶é²"
</dest>
<voice>
- *: "Scroll Start Delay"
+ *: "æ²å‹•é–‹å§‹å»¶é²"
</voice>
</phrase>
<phrase>
@@ -3078,7 +2827,7 @@
*: "æ²å‹•é–“éš”"
</dest>
<voice>
- *: "Scroll Step Size"
+ *: "æ²å‹•é–“éš”"
</voice>
</phrase>
<phrase>
@@ -3106,7 +2855,7 @@
*: "é›™å‘æ²å‹•é™åˆ¶"
</dest>
<voice>
- *: "Bidirectional Scroll Limit"
+ *: "é›™å‘æ²å‹•é™åˆ¶"
</voice>
</phrase>
<phrase>
@@ -3120,7 +2869,7 @@
*: "ç•«é¢ç¯„åœå¤–æ²å‹•"
</dest>
<voice>
- *: "Screen Scrolls Out Of View"
+ *: "ç•«é¢ç¯„åœå¤–æ²å‹•"
</voice>
</phrase>
<phrase>
@@ -3134,7 +2883,7 @@
*: "ç•«é¢æ²å‹•é–“éš”"
</dest>
<voice>
- *: "Screen Scroll Step Size"
+ *: "ç•«é¢æ²å‹•é–“éš”"
</voice>
</phrase>
<phrase>
@@ -3148,7 +2897,7 @@
*: "é æ²å‹•"
</dest>
<voice>
- *: "Paged scrolling"
+ *: "é æ²å‹•"
</voice>
</phrase>
<phrase>
@@ -3162,7 +2911,7 @@
*: "æ²å‹•è»¸"
</dest>
<voice>
- *: "Scroll Bar"
+ *: "æ²å‹•è»¸"
</voice>
</phrase>
<phrase>
@@ -3176,24 +2925,7 @@
*: "狀態列"
</dest>
<voice>
- *: "Status Bar"
- </voice>
-</phrase>
-<phrase>
- id: LANG_BUTTON_BAR
- desc: in settings menu
- user: core
- <source>
- *: none
- recorder_pad: "Button Bar"
- </source>
- <dest>
- *: none
- recorder_pad: "按鈕列"
- </dest>
- <voice>
- *: none
- recorder_pad: "Button Bar"
+ *: "狀態列"
</voice>
</phrase>
<phrase>
@@ -3207,7 +2939,7 @@
*: "音é‡é¡¯ç¤º"
</dest>
<voice>
- *: "Volume Display"
+ *: "音é‡é¡¯ç¤º"
</voice>
</phrase>
<phrase>
@@ -3221,7 +2953,7 @@
*: "電池顯示"
</dest>
<voice>
- *: "Battery Display"
+ *: "電池顯示"
</voice>
</phrase>
<phrase>
@@ -3235,7 +2967,7 @@
*: "圖形"
</dest>
<voice>
- *: "Graphic"
+ *: "圖形"
</voice>
</phrase>
<phrase>
@@ -3249,7 +2981,7 @@
*: "數值"
</dest>
<voice>
- *: "Numeric"
+ *: "數值"
</voice>
</phrase>
<phrase>
@@ -3258,15 +2990,12 @@
user: core
<source>
*: "Peak Release"
- masd: none
</source>
<dest>
- *: "Peak Release"
- masd: none
+ *: "峰值釋放速度"
</dest>
<voice>
- *: "Peak Release"
- masd: none
+ *: "峰值釋放速度"
</voice>
</phrase>
<phrase>
@@ -3275,15 +3004,12 @@
user: core
<source>
*: "Peak Hold Time"
- masd: none
</source>
<dest>
*: "高峰記號åœç•™æ™‚é–“"
- masd: none
</dest>
<voice>
- *: "Peak Hold Time"
- masd: none
+ *: "高峰記號åœç•™æ™‚é–“"
</voice>
</phrase>
<phrase>
@@ -3292,15 +3018,12 @@
user: core
<source>
*: "Clip Hold Time"
- masd: none
</source>
<dest>
*: "削波記號åœç•™æ™‚é–“"
- masd: none
</dest>
<voice>
- *: "Clip Hold Time"
- masd: none
+ *: "削波記號åœç•™æ™‚é–“"
</voice>
</phrase>
<phrase>
@@ -3309,15 +3032,12 @@
user: core
<source>
*: "Eternal"
- masd: none
</source>
<dest>
*: "永久"
- masd: none
</dest>
<voice>
- *: "Eternal"
- masd: none
+ *: "永久"
</voice>
</phrase>
<phrase>
@@ -3326,15 +3046,12 @@
user: core
<source>
*: "Scale"
- masd: none
</source>
<dest>
*: "比例"
- masd: none
</dest>
<voice>
- *: "Scale"
- masd: none
+ *: "比例"
</voice>
</phrase>
<phrase>
@@ -3343,15 +3060,12 @@
user: core
<source>
*: "Logarithmic (dB)"
- masd: none
</source>
<dest>
*: "å°æ•¸ (dB)"
- masd: none
</dest>
<voice>
- *: "Logarithmic decibel"
- masd: none
+ *: "å°æ•¸ (dB)"
</voice>
</phrase>
<phrase>
@@ -3360,15 +3074,12 @@
user: core
<source>
*: "Linear (%)"
- masd: none
</source>
<dest>
*: "線性 (%)"
- masd: none
</dest>
<voice>
- *: "Linear percent"
- masd: none
+ *: "線性 (%)"
</voice>
</phrase>
<phrase>
@@ -3377,15 +3088,12 @@
user: core
<source>
*: "Minimum Of Range"
- masd: none
</source>
<dest>
*: "最å°ç¯„åœ"
- masd: none
</dest>
<voice>
- *: "Minimum Of Range"
- masd: none
+ *: "最å°ç¯„åœ"
</voice>
</phrase>
<phrase>
@@ -3394,15 +3102,12 @@
user: core
<source>
*: "Maximum Of Range"
- masd: none
</source>
<dest>
*: "最大範åœ"
- masd: none
</dest>
<voice>
- *: "Maximum Of Range"
- masd: none
+ *: "最大範åœ"
</voice>
</phrase>
<phrase>
@@ -3416,7 +3121,7 @@
*: "電池容é‡"
</dest>
<voice>
- *: "Battery Capacity"
+ *: "電池容é‡"
</voice>
</phrase>
<phrase>
@@ -3433,41 +3138,7 @@
</dest>
<voice>
*: none
- battery_types: "Battery type"
- </voice>
-</phrase>
-<phrase>
- id: LANG_BATTERY_TYPE_1
- desc: in battery settings
- user: core
- <source>
- *: none
- battery_types: "Alkaline"
- </source>
- <dest>
- *: none
- battery_types: "鹼性"
- </dest>
- <voice>
- *: none
- battery_types: "Alkaline"
- </voice>
-</phrase>
-<phrase>
- id: LANG_BATTERY_TYPE_2
- desc: in battery settings
- user: core
- <source>
- *: none
- battery_types: "NiMH"
- </source>
- <dest>
- *: none
- battery_types: "鎳氫"
- </dest>
- <voice>
- *: none
- battery_types: "Nickel metal hydride"
+ battery_types: "電池類型"
</voice>
</phrase>
<phrase>
@@ -3483,7 +3154,7 @@
flash_storage: none
</dest>
<voice>
- *: "Disk Spindown"
+ *: "硬碟迴轉åœæ­¢"
flash_storage: none
</voice>
</phrase>
@@ -3501,7 +3172,7 @@
</dest>
<voice>
*: none
- dircache: "Directory Cache"
+ dircache: "目錄快å–"
</voice>
</phrase>
<phrase>
@@ -3529,7 +3200,7 @@
*: "時間格å¼"
</dest>
<voice>
- *: "Time Format"
+ *: "時間格å¼"
</voice>
</phrase>
<phrase>
@@ -3543,7 +3214,7 @@
*: "12 å°æ™‚制"
</dest>
<voice>
- *: "12 Hour Clock"
+ *: "12 å°æ™‚制"
</voice>
</phrase>
<phrase>
@@ -3557,7 +3228,7 @@
*: "24 å°æ™‚制"
</dest>
<voice>
- *: "24 Hour Clock"
+ *: "24 å°æ™‚制"
</voice>
</phrase>
<phrase>
@@ -3585,7 +3256,7 @@
*: "最大播放清單歌曲數"
</dest>
<voice>
- *: "Maximum playlist size"
+ *: "最大播放清單歌曲數"
</voice>
</phrase>
<phrase>
@@ -3599,7 +3270,7 @@
*: "播放清單"
</dest>
<voice>
- *: "Playlist"
+ *: "播放清單"
</voice>
</phrase>
<phrase>
@@ -3613,7 +3284,7 @@
*: "書籤"
</dest>
<voice>
- *: "Bookmarks"
+ *: "書籤"
</voice>
</phrase>
<phrase>
@@ -3641,7 +3312,7 @@
*: "設定歌曲評分"
</dest>
<voice>
- *: "Set Song Rating"
+ *: "設定歌曲評分"
</voice>
</phrase>
<phrase>
@@ -3655,7 +3326,7 @@
*: "æ›´å"
</dest>
<voice>
- *: "Rename"
+ *: "æ›´å"
</voice>
</phrase>
<phrase>
@@ -3669,7 +3340,7 @@
*: "剪下"
</dest>
<voice>
- *: "Cut"
+ *: "剪下"
</voice>
</phrase>
<phrase>
@@ -3683,7 +3354,7 @@
*: "複製"
</dest>
<voice>
- *: "Copy"
+ *: "複製"
</voice>
</phrase>
<phrase>
@@ -3697,7 +3368,7 @@
*: "貼上"
</dest>
<voice>
- *: "Paste"
+ *: "貼上"
</voice>
</phrase>
<phrase>
@@ -3711,7 +3382,7 @@
*: "檔案/目錄已存在. 覆寫?"
</dest>
<voice>
- *: ""
+ *: "檔案/目錄已存在. 覆寫?"
</voice>
</phrase>
<phrase>
@@ -3725,7 +3396,7 @@
*: "刪除"
</dest>
<voice>
- *: "Delete"
+ *: "刪除"
</voice>
</phrase>
<phrase>
@@ -3742,7 +3413,7 @@
</dest>
<voice>
*: none
- lcd_non-mono: "Set As Backdrop"
+ lcd_non-mono: "設定為背景"
</voice>
</phrase>
<phrase>
@@ -3756,7 +3427,7 @@
*: "刪除目錄"
</dest>
<voice>
- *: "delete directory"
+ *: "刪除目錄"
</voice>
</phrase>
<phrase>
@@ -3770,7 +3441,7 @@
*: "刪除?"
</dest>
<voice>
- *: ""
+ *: "刪除?"
</voice>
</phrase>
<phrase>
@@ -3784,7 +3455,7 @@
*: "已刪除"
</dest>
<voice>
- *: ""
+ *: "已刪除"
</voice>
</phrase>
<phrase>
@@ -3798,7 +3469,7 @@
*: "開啟檔案..."
</dest>
<voice>
- *: "open with"
+ *: "開啟檔案..."
</voice>
</phrase>
<phrase>
@@ -3812,7 +3483,7 @@
*: "建立目錄"
</dest>
<voice>
- *: "Create Directory"
+ *: "建立目錄"
</voice>
</phrase>
<phrase>
@@ -3829,7 +3500,7 @@
</dest>
<voice>
*: none
- pitchscreen: "Pitch"
+ pitchscreen: "音調"
</voice>
</phrase>
<phrase>
@@ -3843,7 +3514,7 @@
*: "檢視"
</dest>
<voice>
- *: "View"
+ *: "檢視"
</voice>
</phrase>
<phrase>
@@ -3857,7 +3528,7 @@
*: "é‡æ–°äº‚åº"
</dest>
<voice>
- *: "Reshuffle"
+ *: "é‡æ–°äº‚åº"
</voice>
</phrase>
<phrase>
@@ -3871,7 +3542,7 @@
*: "æ’å…¥"
</dest>
<voice>
- *: "Insert"
+ *: "æ’å…¥"
</voice>
</phrase>
<phrase>
@@ -3885,7 +3556,7 @@
*: "æ’入下一個"
</dest>
<voice>
- *: "Insert Next"
+ *: "æ’入下一個"
</voice>
</phrase>
<phrase>
@@ -3899,7 +3570,7 @@
*: "æ’入到最後"
</dest>
<voice>
- *: "Insert Last"
+ *: "æ’入到最後"
</voice>
</phrase>
<phrase>
@@ -3913,7 +3584,7 @@
*: "亂åºæ’å…¥"
</dest>
<voice>
- *: "Insert Shuffled"
+ *: "亂åºæ’å…¥"
</voice>
</phrase>
<phrase>
@@ -3927,7 +3598,7 @@
*: "隊列"
</dest>
<voice>
- *: "Queue"
+ *: "隊列"
</voice>
</phrase>
<phrase>
@@ -3941,7 +3612,7 @@
*: "隊列中下一個"
</dest>
<voice>
- *: "Queue Next"
+ *: "隊列中下一個"
</voice>
</phrase>
<phrase>
@@ -3955,7 +3626,7 @@
*: "隊列最後"
</dest>
<voice>
- *: "Queue Last"
+ *: "隊列最後"
</voice>
</phrase>
<phrase>
@@ -3969,7 +3640,7 @@
*: "隨機隊列"
</dest>
<voice>
- *: "Queue Shuffled"
+ *: "隨機隊列"
</voice>
</phrase>
<phrase>
@@ -3983,7 +3654,7 @@
*: "在播放清單內æœå°‹"
</dest>
<voice>
- *: "Search In Playlist"
+ *: "在播放清單內æœå°‹"
</voice>
</phrase>
<phrase>
@@ -3997,7 +3668,7 @@
*: "æœå°‹ä¸­... 找到 %d 件 (%s)"
</dest>
<voice>
- *: ""
+ *: "æœå°‹ä¸­... 找到 %d 件 (%s)"
</voice>
</phrase>
<phrase>
@@ -4011,7 +3682,7 @@
*: "建立書籤"
</dest>
<voice>
- *: "Create Bookmark"
+ *: "建立書籤"
</voice>
</phrase>
<phrase>
@@ -4025,7 +3696,7 @@
*: "列舉書籤"
</dest>
<voice>
- *: "List Bookmarks"
+ *: "列舉書籤"
</voice>
</phrase>
<phrase>
@@ -4039,7 +3710,7 @@
*: "Rockbox 資訊"
</dest>
<voice>
- *: ""
+ *: "Rockbox 資訊"
</voice>
</phrase>
<phrase>
@@ -4056,24 +3727,7 @@
</dest>
<voice>
*: none
- charging: ""
- </voice>
-</phrase>
-<phrase>
- id: LANG_BATTERY_TOPOFF_CHARGE
- desc: in info display, shows that top off charge is running Only for V1 archosrecorder
- user: core
- <source>
- *: none
- archosrecorder: "Battery: Top-Off Chg"
- </source>
- <dest>
- *: none
- archosrecorder: "電池: 完æˆå……é›»"
- </dest>
- <voice>
- *: none
- archosrecorder: ""
+ charging: "電池: 充電中"
</voice>
</phrase>
<phrase>
@@ -4090,7 +3744,7 @@
</dest>
<voice>
*: none
- charging: ""
+ charging: "電池: 涓æµå……é›»"
</voice>
</phrase>
<phrase>
@@ -4100,16 +3754,13 @@
<source>
*: "Battery: %d%% %dh %dm"
ipodmini1g,ipodmini2g,iriverh10: "Batt: %d%% %dh %dm"
- iriverifp7xx: "%d%% %dh %dm"
</source>
<dest>
*: "電池: %d%% %då°æ™‚ %d分"
- archosondiofm,iriverifp7xx: "%d%% %dh %dm"
ipodmini1g,ipodmini2g,iriverh10: "電池: %d%% %dh %dm"
</dest>
<voice>
- *: "Battery level"
- ipodmini1g,ipodmini2g,iriverh10,iriverifp7xx: ""
+ *: "電池: %d%% %då°æ™‚ %d分"
</voice>
</phrase>
<phrase>
@@ -4123,7 +3774,7 @@
*: "硬碟:"
</dest>
<voice>
- *: ""
+ *: "硬碟:"
</voice>
</phrase>
<phrase>
@@ -4137,7 +3788,7 @@
*: "剩餘容é‡:"
</dest>
<voice>
- *: "Free diskspace:"
+ *: "剩餘容é‡:"
</voice>
</phrase>
<phrase>
@@ -4146,41 +3797,41 @@
user: core
<source>
*: "Int:"
+ hibylinux: "mSD:"
xduoox3: "mSD1:"
</source>
<dest>
- *: "內建:"
- xduoox3: "Multimedia å¡1:"
+ *: "Int:"
+ hibylinux: "mSD:"
+ xduoox3: "mSD1:"
</dest>
<voice>
*: "Internal"
- xduoox3: "Multimedia å¡1"
+ hibylinux: "micro S D"
+ xduoox3: "micro S D 1"
</voice>
</phrase>
<phrase>
id: LANG_DISK_NAME_MMC
- desc: in info menu; name for external disk with multivolume (Ondio; keep short!)
+ desc: in info menu; name for external disk with multivolume (keep short!)
user: core
<source>
*: none
- archosondio*: "MMC:"
multivolume: "HD1"
sansac200*,sansaclipplus,sansae200*,sansafuze*: "mSD:"
xduoox3: "mSD2:"
</source>
<dest>
*: none
- archosondio*: "MMC å¡:"
multivolume: "ç£ç›¤1"
sansac200*,sansaclipplus,sansae200*,sansafuze*: "Multimedia å¡:"
- xduoox3: "Multimedia å¡2:"
+ xduoox3: "mSD2:"
</dest>
<voice>
*: none
- archosondio*: "MMC å¡"
multivolume: "ç£ç›¤1"
sansac200*,sansaclipplus,sansae200*,sansafuze*: "Multimedia å¡"
- xduoox3: "Multimedia å¡2"
+ xduoox3: "micro S D 2"
</voice>
</phrase>
<phrase>
@@ -4193,7 +3844,7 @@
</source>
<dest>
*: none
- rtc: none
+ rtc: ""
</dest>
<voice>
*: none
@@ -4245,7 +3896,7 @@
*: "æš«åœ"
</dest>
<voice>
- *: ""
+ *: "æš«åœ"
</voice>
</phrase>
<phrase>
@@ -4259,7 +3910,7 @@
*: "建立書籤?"
</dest>
<voice>
- *: ""
+ *: "建立書籤?"
</voice>
</phrase>
<phrase>
@@ -4273,7 +3924,7 @@
*: "已建立書籤"
</dest>
<voice>
- *: ""
+ *: "已建立書籤"
</voice>
</phrase>
<phrase>
@@ -4287,7 +3938,7 @@
*: "建立書籤失敗!"
</dest>
<voice>
- *: ""
+ *: "建立書籤失敗!"
</voice>
</phrase>
<phrase>
@@ -4301,7 +3952,7 @@
*: "書籤為空"
</dest>
<voice>
- *: ""
+ *: "書籤為空"
</voice>
</phrase>
<phrase>
@@ -4326,19 +3977,19 @@
*: none
gigabeatfx: "POWER = 返回"
gigabeats,sansafuzeplus: "BACK = 返回"
- gogearsa9200: "LEFT = 返回"
+ gogearsa9200,ipod*,mpiohd300,sansac200*: "MENU = 返回"
iaudiom5,iaudiox5: "RECORD = 回去"
- ipod*,mpiohd300,sansac200*: "MENU = 回去"
iriverh10,iriverh10_5gb,sansae200*,sansafuze*: "PREV = 返回"
iriverh100,iriverh120,iriverh300: "STOP = 返回"
mrobe100: "DISPLAY = 返回"
- mrobe500: "POWER = 回去"
+ mrobe500: "POWER = Revert"
rtc: "OFF = 返回"
samsungyh*: "REW = 返回"
vibe500: "CANCEL = 返回"
</dest>
<voice>
- *: none
+ *: ""
+ gigabeat*,gogearsa9200,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh100,iriverh10_5gb,iriverh120,iriverh300,mrobe100,samsungyh*,sansac200*,sansae200*: ""
</voice>
</phrase>
<phrase>
@@ -4397,7 +4048,7 @@
</dest>
<voice>
*: none
- iaudiom5,iaudiox5,iriverh100,iriverh120,iriverh300,recording,sansac200*,sansae200*: ""
+ iaudiom5,iaudiox5,iriverh100,iriverh120,iriverh300,recording,samsungyh*,sansac200*,sansae200*,vibe500: ""
</voice>
</phrase>
<phrase>
@@ -4414,7 +4065,7 @@
</dest>
<voice>
*: none
- recording: ""
+ recording: "一次"
</voice>
</phrase>
<phrase>
@@ -4431,7 +4082,7 @@
</dest>
<voice>
*: none
- recording: ""
+ recording: "在之上開始"
</voice>
</phrase>
<phrase>
@@ -4448,7 +4099,7 @@
</dest>
<voice>
*: none
- recording: ""
+ recording: "在之下åœæ­¢"
</voice>
</phrase>
<phrase>
@@ -4465,7 +4116,7 @@
</dest>
<voice>
*: none
- recording: ""
+ recording: "先設分割的間隔"
</voice>
</phrase>
<phrase>
@@ -4482,7 +4133,7 @@
</dest>
<voice>
*: none
- recording: ""
+ recording: "minus infinity"
</voice>
</phrase>
<phrase>
@@ -4516,7 +4167,7 @@
</dest>
<voice>
*: none
- alarm: ""
+ alarm: "在 %d:%02d 內關閉鬧鈴"
</voice>
</phrase>
<phrase>
@@ -4533,7 +4184,7 @@
</dest>
<voice>
*: none
- alarm: ""
+ alarm: "已設定鬧鈴"
</voice>
</phrase>
<phrase>
@@ -4550,7 +4201,7 @@
</dest>
<voice>
*: none
- alarm: ""
+ alarm: "鬧鈴設定時間ä¸æ­£ç¢º!"
</voice>
</phrase>
<phrase>
@@ -4579,7 +4230,7 @@
</dest>
<voice>
*: none
- alarm,gigabeats,ipod*,iriverh10,iriverh10_5gb: ""
+ alarm,ipod*: ""
</voice>
</phrase>
<phrase>
@@ -4596,7 +4247,7 @@
</dest>
<voice>
*: none
- alarm: ""
+ alarm: "鬧鈴關閉"
</voice>
</phrase>
<phrase>
@@ -4787,7 +4438,7 @@
*: " (變動ä½å…ƒçŽ‡)"
</dest>
<voice>
- *: "變動ä½å…ƒçŽ‡"
+ *: " (變動ä½å…ƒçŽ‡)"
</voice>
</phrase>
<phrase>
@@ -4815,7 +4466,7 @@
*: "Track Gain"
</dest>
<voice>
- *: "Track Gain"
+ *: "Track gain"
</voice>
</phrase>
<phrase>
@@ -4829,7 +4480,7 @@
*: "Album Gain"
</dest>
<voice>
- *: "Album Gain"
+ *: "Album gain"
</voice>
</phrase>
<phrase>
@@ -4857,7 +4508,7 @@
*: "<無資訊>"
</dest>
<voice>
- *: "無資訊"
+ *: "<無資訊>"
</voice>
</phrase>
<phrase>
@@ -4990,7 +4641,7 @@
*: "1月"
</dest>
<voice>
- *: "January"
+ *: "1月"
</voice>
</phrase>
<phrase>
@@ -5004,7 +4655,7 @@
*: "2月"
</dest>
<voice>
- *: "February"
+ *: "2月"
</voice>
</phrase>
<phrase>
@@ -5018,7 +4669,7 @@
*: "3月"
</dest>
<voice>
- *: "March"
+ *: "3月"
</voice>
</phrase>
<phrase>
@@ -5032,7 +4683,7 @@
*: "4月"
</dest>
<voice>
- *: "April"
+ *: "4月"
</voice>
</phrase>
<phrase>
@@ -5046,7 +4697,7 @@
*: "5月"
</dest>
<voice>
- *: "May"
+ *: "5月"
</voice>
</phrase>
<phrase>
@@ -5060,7 +4711,7 @@
*: "6月"
</dest>
<voice>
- *: "June"
+ *: "6月"
</voice>
</phrase>
<phrase>
@@ -5074,7 +4725,7 @@
*: "7月"
</dest>
<voice>
- *: "July"
+ *: "7月"
</voice>
</phrase>
<phrase>
@@ -5088,7 +4739,7 @@
*: "8月"
</dest>
<voice>
- *: "August"
+ *: "8月"
</voice>
</phrase>
<phrase>
@@ -5102,7 +4753,7 @@
*: "9月"
</dest>
<voice>
- *: "September"
+ *: "9月"
</voice>
</phrase>
<phrase>
@@ -5116,7 +4767,7 @@
*: "10月"
</dest>
<voice>
- *: "October"
+ *: "10月"
</voice>
</phrase>
<phrase>
@@ -5130,7 +4781,7 @@
*: "11月"
</dest>
<voice>
- *: "November"
+ *: "11月"
</voice>
</phrase>
<phrase>
@@ -5144,7 +4795,7 @@
*: "12月"
</dest>
<voice>
- *: "December"
+ *: "12月"
</voice>
</phrase>
<phrase>
@@ -5834,48 +5485,6 @@
</voice>
</phrase>
<phrase>
- id: LANG_KILOBYTE
- desc: a unit postfix, also voiced
- user: core
- <source>
- *: "KB"
- </source>
- <dest>
- *: "KB"
- </dest>
- <voice>
- *: "kilobyte"
- </voice>
-</phrase>
-<phrase>
- id: LANG_MEGABYTE
- desc: a unit postfix, also voiced
- user: core
- <source>
- *: "MB"
- </source>
- <dest>
- *: "MB"
- </dest>
- <voice>
- *: "megabyte"
- </voice>
-</phrase>
-<phrase>
- id: LANG_GIGABYTE
- desc: a unit postfix, also voiced
- user: core
- <source>
- *: "GB"
- </source>
- <dest>
- *: "GB"
- </dest>
- <voice>
- *: "gigabyte"
- </voice>
-</phrase>
-<phrase>
id: LANG_POINT
desc: decimal separator for composing numbers
user: core
@@ -6417,7 +6026,7 @@
</source>
<dest>
*: none
- remote: none
+ remote: ""
</dest>
<voice>
*: none
@@ -6449,7 +6058,7 @@
*: "播放清單緩è¡å€å·²æ»¿"
</dest>
<voice>
- *: ""
+ *: "播放清單緩è¡å€å·²æ»¿"
</voice>
</phrase>
<phrase>
@@ -6477,7 +6086,7 @@
*: "æ’å…¥ %d 首歌曲 (%s)"
</dest>
<voice>
- *: ""
+ *: "æ’å…¥ %d 首歌曲 (%s)"
</voice>
</phrase>
<phrase>
@@ -6491,7 +6100,7 @@
*: "列入了 %d 首歌曲 (%s)"
</dest>
<voice>
- *: ""
+ *: "列入了 %d 首歌曲 (%s)"
</voice>
</phrase>
<phrase>
@@ -6505,7 +6114,7 @@
*: "儲存 %d 首歌曲 (%s)"
</dest>
<voice>
- *: ""
+ *: "儲存 %d 首歌曲 (%s)"
</voice>
</phrase>
<phrase>
@@ -6519,7 +6128,7 @@
*: "éžè¿´å­ç›®éŒ„?"
</dest>
<voice>
- *: ""
+ *: "éžè¿´å­ç›®éŒ„?"
</voice>
</phrase>
<phrase>
@@ -6533,7 +6142,7 @@
*: "消除動態播放清單?"
</dest>
<voice>
- *: ""
+ *: "消除動態播放清單?"
</voice>
</phrase>
<phrase>
@@ -6547,7 +6156,7 @@
*: "無法繼續上次播放"
</dest>
<voice>
- *: ""
+ *: "無法繼續上次播放"
</voice>
</phrase>
<phrase>
@@ -6561,7 +6170,7 @@
*: "更新播放清單控制檔案錯誤"
</dest>
<voice>
- *: ""
+ *: "更新播放清單控制檔案錯誤"
</voice>
</phrase>
<phrase>
@@ -6575,7 +6184,7 @@
*: "å­˜å–播放清單檔案錯誤"
</dest>
<voice>
- *: ""
+ *: "å­˜å–播放清單檔案錯誤"
</voice>
</phrase>
<phrase>
@@ -6589,7 +6198,7 @@
*: "å­˜å–播放清單控制檔案錯誤"
</dest>
<voice>
- *: ""
+ *: "å­˜å–播放清單控制檔案錯誤"
</voice>
</phrase>
<phrase>
@@ -6603,7 +6212,7 @@
*: "å­˜å–目錄錯誤"
</dest>
<voice>
- *: ""
+ *: "å­˜å–目錄錯誤"
</voice>
</phrase>
<phrase>
@@ -6617,7 +6226,7 @@
*: "無效的播放清單控制檔"
</dest>
<voice>
- *: ""
+ *: "無效的播放清單控制檔"
</voice>
</phrase>
<phrase>
@@ -6634,7 +6243,7 @@
</dest>
<voice>
*: none
- radio: ""
+ radio: "沒有é è¨­"
</voice>
</phrase>
<phrase>
@@ -6651,7 +6260,7 @@
</dest>
<voice>
*: none
- radio: "Add Preset"
+ radio: "增加é è¨­"
</voice>
</phrase>
<phrase>
@@ -6668,7 +6277,7 @@
</dest>
<voice>
*: none
- radio: ""
+ radio: "編輯é è¨­"
</voice>
</phrase>
<phrase>
@@ -6685,7 +6294,7 @@
</dest>
<voice>
*: none
- radio: ""
+ radio: "移除é è¨­"
</voice>
</phrase>
<phrase>
@@ -6702,7 +6311,7 @@
</dest>
<voice>
*: none
- radio: ""
+ radio: "儲存é è¨­å¤±æ•—"
</voice>
</phrase>
<phrase>
@@ -6719,92 +6328,7 @@
</dest>
<voice>
*: none
- radio: ""
- </voice>
-</phrase>
-<phrase>
- id: LANG_BUTTONBAR_MENU
- desc: in button bar
- user: core
- <source>
- *: none
- radio_screen_button_bar: "Menu"
- </source>
- <dest>
- *: none
- radio_screen_button_bar: "é¸å–®"
- </dest>
- <voice>
- *: none
- radio_screen_button_bar: ""
- </voice>
-</phrase>
-<phrase>
- id: LANG_FM_BUTTONBAR_EXIT
- desc: in radio screen
- user: core
- <source>
- *: none
- radio_screen_button_bar: "Exit"
- </source>
- <dest>
- *: none
- radio_screen_button_bar: "離開"
- </dest>
- <voice>
- *: none
- radio_screen_button_bar: ""
- </voice>
-</phrase>
-<phrase>
- id: LANG_FM_BUTTONBAR_ACTION
- desc: in radio screen
- user: core
- <source>
- *: none
- radio_screen_button_bar: "Action"
- </source>
- <dest>
- *: none
- radio_screen_button_bar: "動作"
- </dest>
- <voice>
- *: none
- radio_screen_button_bar: ""
- </voice>
-</phrase>
-<phrase>
- id: LANG_FM_BUTTONBAR_ADD
- desc: in radio screen
- user: core
- <source>
- *: none
- radio_screen_button_bar: "Add"
- </source>
- <dest>
- *: none
- radio_screen_button_bar: "增加"
- </dest>
- <voice>
- *: none
- radio_screen_button_bar: ""
- </voice>
-</phrase>
-<phrase>
- id: LANG_FM_BUTTONBAR_RECORD
- desc: in radio screen
- user: core
- <source>
- *: none
- radio_screen_button_bar: "Record"
- </source>
- <dest>
- *: none
- radio_screen_button_bar: "錄音"
- </dest>
- <voice>
- *: none
- radio_screen_button_bar: ""
+ radio: "é è¨­æ¸…單已滿"
</voice>
</phrase>
<phrase>
@@ -6821,7 +6345,7 @@
</dest>
<voice>
*: none
- radio: ""
+ radio: "強迫使用單è²é“"
</voice>
</phrase>
<phrase>
@@ -6855,7 +6379,7 @@
</dest>
<voice>
*: none
- radio: "Auto scan presets"
+ radio: "自動掃æé è¨­"
</voice>
</phrase>
<phrase>
@@ -6872,7 +6396,7 @@
</dest>
<voice>
*: none
- radio: ""
+ radio: "清除ç¾åœ¨é è¨­?"
</voice>
</phrase>
<phrase>
@@ -6923,7 +6447,7 @@
</dest>
<voice>
*: none
- radio: ""
+ radio: "掃æ"
</voice>
</phrase>
<phrase>
@@ -6937,7 +6461,7 @@
*: "目錄緩è¡å€å·²æ»¿!"
</dest>
<voice>
- *: ""
+ *: "目錄緩è¡å€å·²æ»¿!"
</voice>
</phrase>
<phrase>
@@ -6951,7 +6475,7 @@
*: "新語言"
</dest>
<voice>
- *: ""
+ *: "新語言"
</voice>
</phrase>
<phrase>
@@ -6965,7 +6489,7 @@
*: "設定已載入"
</dest>
<voice>
- *: ""
+ *: "設定已載入"
</voice>
</phrase>
<phrase>
@@ -6979,7 +6503,7 @@
*: "設定已儲存"
</dest>
<voice>
- *: ""
+ *: "設定已儲存"
</voice>
</phrase>
<phrase>
@@ -6993,7 +6517,7 @@
*: "開機檔已改變"
</dest>
<voice>
- *: ""
+ *: "開機檔已改變"
</voice>
</phrase>
<phrase>
@@ -7007,12 +6531,12 @@
*: "ç¾åœ¨è¦é‡æ–°é–‹æ©Ÿ?"
</dest>
<voice>
- *: ""
+ *: "ç¾åœ¨è¦é‡æ–°é–‹æ©Ÿ?"
</voice>
</phrase>
<phrase>
id: LANG_OFF_ABORT
- desc: Used on archosrecorder models
+ desc: Used on many models
user: core
<source>
*: "OFF to abort"
@@ -7047,7 +6571,7 @@
*: "沒有檔案"
</dest>
<voice>
- *: ""
+ *: "沒有檔案"
</voice>
</phrase>
<phrase>
@@ -7061,7 +6585,7 @@
*: "æ–°éµç›¤"
</dest>
<voice>
- *: ""
+ *: "æ–°éµç›¤"
</voice>
</phrase>
<phrase>
@@ -7075,7 +6599,7 @@
*: "移動"
</dest>
<voice>
- *: "Move"
+ *: "移動"
</voice>
</phrase>
<phrase>
@@ -7089,7 +6613,7 @@
*: "顯示索引"
</dest>
<voice>
- *: "Show Indices"
+ *: "顯示索引"
</voice>
</phrase>
<phrase>
@@ -7103,7 +6627,7 @@
*: "曲目顯示"
</dest>
<voice>
- *: "Track Display"
+ *: "曲目顯示"
</voice>
</phrase>
<phrase>
@@ -7117,7 +6641,7 @@
*: "åªé¡¯ç¤ºæ›²å"
</dest>
<voice>
- *: "Track Name Only"
+ *: "åªé¡¯ç¤ºæ›²å"
</voice>
</phrase>
<phrase>
@@ -7131,7 +6655,7 @@
*: "完整路徑"
</dest>
<voice>
- *: "Full Path"
+ *: "完整路徑"
</voice>
</phrase>
<phrase>
@@ -7145,7 +6669,7 @@
*: "移除"
</dest>
<voice>
- *: "Remove"
+ *: "移除"
</voice>
</phrase>
<phrase>
@@ -7178,7 +6702,7 @@
</phrase>
<phrase>
id: LANG_PLUGIN_WRONG_MODEL
- desc: The plugin is not compatible with the archos model trying to run it
+ desc: The plugin is not compatible with the player model trying to run it
user: core
<source>
*: "Incompatible model"
@@ -7187,7 +6711,7 @@
*: "ä¸ç›¸å®¹æ©Ÿåž‹"
</dest>
<voice>
- *: ""
+ *: "ä¸ç›¸å®¹æ©Ÿåž‹"
</voice>
</phrase>
<phrase>
@@ -7201,7 +6725,7 @@
*: "ä¸ç›¸å®¹ç‰ˆæœ¬"
</dest>
<voice>
- *: ""
+ *: "ä¸ç›¸å®¹ç‰ˆæœ¬"
</voice>
</phrase>
<phrase>
@@ -7215,7 +6739,7 @@
*: "æ’件回傳錯誤"
</dest>
<voice>
- *: ""
+ *: "æ’件回傳錯誤"
</voice>
</phrase>
<phrase>
@@ -7229,7 +6753,7 @@
*: "檔案類型陣列已滿"
</dest>
<voice>
- *: ""
+ *: "檔案類型陣列已滿"
</voice>
</phrase>
<phrase>
@@ -7246,7 +6770,7 @@
</dest>
<voice>
*: none
- radio: "Load Preset List"
+ radio: "載入é è¨­æ¸…å–®"
</voice>
</phrase>
<phrase>
@@ -7263,7 +6787,7 @@
</dest>
<voice>
*: none
- radio: "Save Preset List"
+ radio: "儲存é è¨­æ¸…å–®"
</voice>
</phrase>
<phrase>
@@ -7280,7 +6804,7 @@
</dest>
<voice>
*: none
- radio: "Clear Preset List"
+ radio: "清除é è¨­æ¸…å–®"
</voice>
</phrase>
<phrase>
@@ -7297,7 +6821,7 @@
</dest>
<voice>
*: none
- radio: "Preset List"
+ radio: "é è¨­æ¸…å–®"
</voice>
</phrase>
<phrase>
@@ -7314,7 +6838,7 @@
</dest>
<voice>
*: none
- radio: ""
+ radio: "找ä¸åˆ°è¨­å®š, 自動掃æ?"
</voice>
</phrase>
<phrase>
@@ -7328,7 +6852,7 @@
*: "直接增加"
</dest>
<voice>
- *: "Direct gain"
+ *: "直接增加"
</voice>
</phrase>
<phrase>
@@ -7342,7 +6866,7 @@
*: "交å‰å¢žåŠ "
</dest>
<voice>
- *: "Cross gain"
+ *: "交å‰å¢žåŠ "
</voice>
</phrase>
<phrase>
@@ -7356,7 +6880,7 @@
*: "高頻衰減"
</dest>
<voice>
- *: "High-frequency attenuation"
+ *: "高頻衰減"
</voice>
</phrase>
<phrase>
@@ -7370,7 +6894,7 @@
*: "高頻截斷"
</dest>
<voice>
- *: "High-frequency cutoff"
+ *: "高頻截斷"
</voice>
</phrase>
<phrase>
@@ -7398,7 +6922,7 @@
*: "<所有曲目>"
</dest>
<voice>
- *: "All tracks"
+ *: "<所有曲目>"
</voice>
</phrase>
<phrase>
@@ -7412,7 +6936,7 @@
*: "無效的檔å!"
</dest>
<voice>
- *: "Invalid Filename"
+ *: "無效的檔å!"
</voice>
</phrase>
<phrase>
@@ -7429,7 +6953,7 @@
</dest>
<voice>
*: none
- remote: "Remote Scrolling Options"
+ remote: "線控æ²å‹•è¨­å®š"
</voice>
</phrase>
<phrase>
@@ -7475,193 +6999,6 @@
</voice>
</phrase>
<phrase>
- id: LANG_SYSFONT_CHANNEL_STEREO
- desc: in sound_settings
- user: core
- <source>
- *: none
- recording: "Stereo"
- </source>
- <dest>
- *: none
- recording: "Stereo"
- </dest>
- <voice>
- *: none
- recording: "Stereo"
- </voice>
-</phrase>
-<phrase>
- id: LANG_SYSFONT_CHANNEL_MONO
- desc: in sound_settings
- user: core
- <source>
- *: none
- recording: "Mono"
- </source>
- <dest>
- *: none
- recording: "Mono"
- </dest>
- <voice>
- *: none
- recording: "Mono"
- </voice>
-</phrase>
-<phrase>
- id: LANG_SYSFONT_RECORDING_QUALITY
- desc: in the recording settings
- user: core
- <source>
- *: none
- recording_hwcodec: "Quality"
- </source>
- <dest>
- *: none
- recording_hwcodec: "Quality"
- </dest>
- <voice>
- *: none
- recording_hwcodec: "Quality"
- </voice>
-</phrase>
-<phrase>
- id: LANG_SYSFONT_RECORDING_FREQUENCY
- desc: in the recording settings
- user: core
- <source>
- *: none
- recording: "Frequency"
- </source>
- <dest>
- *: none
- recording: "Frequency"
- </dest>
- <voice>
- *: none
- recording: "Frequency"
- </voice>
-</phrase>
-<phrase>
- id: LANG_SYSFONT_RECORDING_SOURCE
- desc: in the recording settings
- user: core
- <source>
- *: none
- recording: "Source"
- </source>
- <dest>
- *: none
- recording: "Source"
- </dest>
- <voice>
- *: none
- recording: "Source"
- </voice>
-</phrase>
-<phrase>
- id: LANG_SYSFONT_RECORDING_SRC_MIC
- desc: in the recording settings
- user: core
- <source>
- *: none
- recording: "Int. Mic"
- </source>
- <dest>
- *: none
- recording: "Mic"
- </dest>
- <voice>
- *: none
- recording: "Microphone"
- </voice>
-</phrase>
-<phrase>
- id: LANG_SYSFONT_RECORDING_SRC_DIGITAL
- desc: in the recording settings
- user: core
- <source>
- *: none
- recording: "Digital"
- </source>
- <dest>
- *: none
- recording: "Digital"
- </dest>
- <voice>
- *: none
- recording: "Digital"
- </voice>
-</phrase>
-<phrase>
- id: LANG_SYSFONT_RECORD_TRIGGER
- desc: in recording settings_menu
- user: core
- <source>
- *: none
- recording: "Trigger"
- </source>
- <dest>
- *: none
- recording: "Trigger"
- </dest>
- <voice>
- *: none
- recording: "Trigger"
- </voice>
-</phrase>
-<phrase>
- id: LANG_SYSFONT_DIRBROWSE_F1
- desc: in dir browser, F1 button bar text
- user: core
- <source>
- *: none
- recorder_pad: "Menu"
- </source>
- <dest>
- *: none
- recorder_pad: "Menu"
- </dest>
- <voice>
- *: none
- recorder_pad: ""
- </voice>
-</phrase>
-<phrase>
- id: LANG_SYSFONT_DIRBROWSE_F2
- desc: in dir browser, F2 button bar text
- user: core
- <source>
- *: none
- recorder_pad: "Option"
- </source>
- <dest>
- *: none
- recorder_pad: "Option"
- </dest>
- <voice>
- *: none
- recorder_pad: ""
- </voice>
-</phrase>
-<phrase>
- id: LANG_SYSFONT_DIRBROWSE_F3
- desc: in dir browser, F3 button bar text
- user: core
- <source>
- *: none
- recorder_pad: "LCD"
- </source>
- <dest>
- *: none
- recorder_pad: "LCD"
- </dest>
- <voice>
- *: none
- recorder_pad: ""
- </voice>
-</phrase>
-<phrase>
id: LANG_AFMT_PCM_WAV
desc: audio format description
user: core
@@ -7711,33 +7048,13 @@
desc: in lcd settings
user: core
<source>
- *: none
- lcd_sleep: "Never"
- </source>
- <dest>
- *: none
- lcd_sleep: "æ°¸ä¸"
- </dest>
- <voice>
- *: none
- lcd_sleep: "Never"
- </voice>
-</phrase>
-<phrase>
- id: LANG_SYSFONT_LINE_IN
- desc: in the recording settings
- user: core
- <source>
- *: none
- recording: "Line In"
+ *: "Never"
</source>
<dest>
- *: none
- recording: "Line In"
+ *: "Never"
</dest>
<voice>
- *: none
- recording: "Line In"
+ *: "Never"
</voice>
</phrase>
<phrase>
@@ -7746,7 +7063,6 @@
user: core
<source>
*: "Building database... %d found (OFF to return)"
- archosplayer: "Building DB %d found"
gigabeat*,iaudiom5,iaudiox5,mrobe100,samsungyh*: "Building database... %d found (LEFT to return)"
gogearsa9200: "Building database... %d found (REW to return)"
ipod*,iriverh10,iriverh10_5gb,sansac200*,sansae200*,sansafuze*,vibe500: "Building database... %d found (PREV to return)"
@@ -7754,14 +7070,13 @@
</source>
<dest>
*: "建立資料庫... 找到 %d é … (OFF: 回上一畫é¢)"
- archosplayer: "建立資料庫... 找到 %d"
gigabeat*,iaudiom5,iaudiox5,mrobe100,samsungyh*: "建立資料庫... 找到 %d é … (LEFT: 回上一畫é¢)"
- gogearsa9200: "建立資料庫... 找到 %d é … (REW: 回上一畫é¢)"
- ipod*,iriverh10,iriverh10_5gb,sansac200*,sansae200*,sansafuze*,vibe500: "建立資料庫... 找到 %d é … (PREV: 回上一畫é¢)"
+ gogearsa9200,ipod*,iriverh10,iriverh10_5gb,sansac200*,sansae200*,vibe500: "建立資料庫... 找到 %d é … (PREV: 回上一畫é¢)"
iriverh100,iriverh120,iriverh300: "建立資料庫... 找到 %d é … (STOP: 回上一畫é¢)"
+ sansafuze*: "Building database... %d found (PREV to return)"
</dest>
<voice>
- *: "entries found for database"
+ *: "建立資料庫... 找到 %d é … (OFF: 回上一畫é¢)"
</voice>
</phrase>
<phrase>
@@ -7775,7 +7090,7 @@
*: "掃æ文件..."
</dest>
<voice>
- *: "Scanning disk"
+ *: "掃æ文件..."
</voice>
</phrase>
<phrase>
@@ -7789,7 +7104,7 @@
*: "檔案"
</dest>
<voice>
- *: "Files"
+ *: "檔案"
</voice>
</phrase>
<phrase>
@@ -7820,7 +7135,7 @@
</dest>
<voice>
*: none
- agc: "DJ set (slow)"
+ agc: "DJ 設定 (慢)"
</voice>
</phrase>
<phrase>
@@ -7834,7 +7149,7 @@
*: "匯入更動"
</dest>
<voice>
- *: "Import Modifications"
+ *: "匯入更動"
</voice>
</phrase>
<phrase>
@@ -7868,7 +7183,7 @@
</dest>
<voice>
*: none
- lcd_sleep: "Sleep after backlight off"
+ lcd_sleep: "ç¡çœ  (在背光關閉後)"
</voice>
</phrase>
<phrase>
@@ -7882,7 +7197,7 @@
*: "添加"
</dest>
<voice>
- *: "Gain"
+ *: "添加"
</voice>
</phrase>
<phrase>
@@ -7896,7 +7211,7 @@
*: "刪除中..."
</dest>
<voice>
- *: "Deleting"
+ *: "刪除中..."
</voice>
</phrase>
<phrase>
@@ -7913,7 +7228,7 @@
</dest>
<voice>
*: none
- usb_charging_enable: "Charge During U S B Connection"
+ usb_charging_enable: "在 USB 連線時充電"
</voice>
</phrase>
<phrase>
@@ -7930,7 +7245,7 @@
</dest>
<voice>
*: none
- remote: "Remote Display OFF"
+ remote: "線控顯示關閉"
</voice>
</phrase>
<phrase>
@@ -7947,7 +7262,7 @@
</dest>
<voice>
*: none
- recording: "Same As Source"
+ recording: "(與來æºç›¸åŒ)"
</voice>
</phrase>
<phrase>
@@ -7964,7 +7279,7 @@
</dest>
<voice>
*: none
- headphone_detection: "Disable resume on startup if phones unplugged"
+ headphone_detection: "如沒耳機æ’入開機時ä¸æ’­æ”¾"
</voice>
</phrase>
<phrase>
@@ -7978,7 +7293,7 @@
*: "æ¢å¾©æ’­ç™¼"
</dest>
<voice>
- *: "Resume"
+ *: "æ¢å¾©æ’­ç™¼"
</voice>
</phrase>
<phrase>
@@ -7995,7 +7310,7 @@
</dest>
<voice>
*: none
- radio: "Europe"
+ radio: "æ­æ´²"
</voice>
</phrase>
<phrase>
@@ -8009,7 +7324,7 @@
*: "警告! 電池電é‡ä½Ž!"
</dest>
<voice>
- *: "WARNING! Low Battery!"
+ *: "警告! 電池電é‡ä½Ž!"
</voice>
</phrase>
<phrase>
@@ -8026,7 +7341,7 @@
</dest>
<voice>
*: none
- recording: "Encoder Settings"
+ recording: "編碼設定"
</voice>
</phrase>
<phrase>
@@ -8040,7 +7355,7 @@
*: "播放清單設置"
</dest>
<voice>
- *: "Playlist Viewer Settings"
+ *: "播放清單設置"
</voice>
</phrase>
<phrase>
@@ -8057,7 +7372,7 @@
</dest>
<voice>
*: none
- rtc: "Set Time and Date"
+ rtc: "設置時間/日期"
</voice>
</phrase>
<phrase>
@@ -8071,7 +7386,7 @@
*: "正在播放"
</dest>
<voice>
- *: "Now Playing"
+ *: "正在播放"
</voice>
</phrase>
<phrase>
@@ -8088,7 +7403,7 @@
</dest>
<voice>
*: none
- recording: "Stop recording"
+ recording: "åœæ­¢éŒ„音"
</voice>
</phrase>
<phrase>
@@ -8104,25 +7419,22 @@
wheel_acceleration: none
</dest>
<voice>
- *: "List Acceleration Speed"
+ *: "列表加速速度"
wheel_acceleration: none
</voice>
</phrase>
<phrase>
- id: LANG_RECORDING_FORMAT
- desc: audio format item in recording menu
+ id: LANG_FORMAT
+ desc: audio format
user: core
<source>
- *: none
- recording: "Format"
+ *: "Format"
</source>
<dest>
- *: none
- recording: "æ ¼å¼"
+ *: "æ ¼å¼"
</dest>
<voice>
- *: none
- recording: "Format"
+ *: "æ ¼å¼"
</voice>
</phrase>
<phrase>
@@ -8136,7 +7448,7 @@
*: "書簽動作"
</dest>
<voice>
- *: "Bookmark Actions"
+ *: "書簽動作"
</voice>
</phrase>
<phrase>
@@ -8153,7 +7465,7 @@
</dest>
<voice>
*: none
- crossfade: "Shuffle or Manual Track Skip"
+ crossfade: "亂åºæ’­æ”¾èˆ‡è·³éŽæ›²ç›®"
</voice>
</phrase>
<phrase>
@@ -8167,7 +7479,7 @@
*: "正常"
</dest>
<voice>
- *: "Normal"
+ *: "正常"
</voice>
</phrase>
<phrase>
@@ -8176,15 +7488,12 @@
user: core
<source>
*: "Buffer:"
- archosplayer: "Buf:"
</source>
<dest>
*: "ç·©è¡:"
- archosplayer: "Buf:"
</dest>
<voice>
- *: ""
- archosplayer: ""
+ *: "ç·©è¡:"
</voice>
</phrase>
<phrase>
@@ -8201,7 +7510,7 @@
</dest>
<voice>
*: none
- headphone_detection: "Pause and Resume"
+ headphone_detection: "æš«åœå’Œæ¢å¾©æ’­æ”¾"
</voice>
</phrase>
<phrase>
@@ -8218,7 +7527,7 @@
</dest>
<voice>
*: none
- recording: "Can't write to recording directory"
+ recording: "ä¸èƒ½å¯«å…¥éŒ„音目錄"
</voice>
</phrase>
<phrase>
@@ -8229,10 +7538,10 @@
*: "Channel Configuration"
</source>
<dest>
- *: "è²é“設置"
+ *: "è²é“"
</dest>
<voice>
- *: "Channel Configuration"
+ *: "è²é“"
</voice>
</phrase>
<phrase>
@@ -8241,9 +7550,8 @@
user: core
<source>
*: "PLAY = Yes"
- archosplayer: "(PLAY/STOP)"
cowond2*: "MENU, or top-right = Yes"
- creativezen*: "Select = Yes"
+ creativezen*: "SELECT = Yes"
gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT = Yes"
iriverh100,iriverh120,iriverh300: "NAVI = Yes"
mrobe500: "PLAY, POWER, or top-right = Yes"
@@ -8251,12 +7559,12 @@
</source>
<dest>
*: "PLAY = 是"
- archosplayer: "(PLAY/STOP)"
- cowond2*: "MENU, or top-right = 是"
- creativezen*: "Select = 是"
- gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT = 是"
+ cowond2*: "MENU, or top-right = Yes"
+ creativezen*: "SELECT = Yes"
+ gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansae200*: "SELECT = 是"
iriverh100,iriverh120,iriverh300: "NAVI = 是"
- mrobe500: "PLAY, POWER, or top-right = 是"
+ mrobe500: "PLAY, POWER, or top-right = Yes"
+ sansaclip*,sansaconnect,sansafuze*: "SELECT = Yes"
vibe500: "OK = 是"
</dest>
<voice>
@@ -8279,16 +7587,18 @@
</source>
<dest>
*: none
- gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT = 設定"
+ gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansae200*: "SELECT = 設定"
gogearsa9200,samsungyh*: "PLAY = 設定"
iriverh100,iriverh120,iriverh300: "NAVI = 設定"
mpiohd300: "ENTER = 設定"
- mrobe500: "HEART = 設定"
+ mrobe500: "HEART = Set"
rtc: "ON = 設定"
+ sansaclip*,sansaconnect,sansafuze*: "SELECT = Set"
vibe500: "OK = 設定"
</dest>
<voice>
- *: none
+ *: ""
+ gigabeat*,gogearsa9200,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh100,iriverh10_5gb,iriverh120,iriverh300,mrobe100,samsungyh*,sansac200*,sansae200*: ""
</voice>
</phrase>
<phrase>
@@ -8297,15 +7607,12 @@
user: core
<source>
*: "Any Other = No"
- archosplayer: none
</source>
<dest>
*: "Any Other = å¦"
- archosplayer: none
</dest>
<voice>
*: ""
- archosplayer: none
</voice>
</phrase>
<phrase>
@@ -8319,7 +7626,7 @@
*: "ç€è¦½ Cuesheet"
</dest>
<voice>
- *: "Browse Cuesheet"
+ *: "ç€è¦½ Cuesheet"
</voice>
</phrase>
<phrase>
@@ -8333,7 +7640,7 @@
*: "樣å“程åº"
</dest>
<voice>
- *: "Demos"
+ *: "樣å“程åº"
</voice>
</phrase>
<phrase>
@@ -8350,7 +7657,7 @@
</dest>
<voice>
*: none
- recording: "Bitrate"
+ recording: "ä½å…ƒçŽ‡"
</voice>
</phrase>
<phrase>
@@ -8364,7 +7671,7 @@
*: "最高時間"
</dest>
<voice>
- *: "Top Time"
+ *: "最高時間"
</voice>
</phrase>
<phrase>
@@ -8378,7 +7685,7 @@
*: "使用 .talk 文件"
</dest>
<voice>
- *: "Use File .talk Clips"
+ *: "使用 .talk 文件"
</voice>
</phrase>
<phrase>
@@ -8392,7 +7699,7 @@
*: "亂數"
</dest>
<voice>
- *: "Random"
+ *: "亂數"
</voice>
</phrase>
<phrase>
@@ -8406,7 +7713,7 @@
*: "全部"
</dest>
<voice>
- *: "All"
+ *: "全部"
</voice>
</phrase>
<phrase>
@@ -8423,7 +7730,7 @@
</dest>
<voice>
*: none
- recording: "New file"
+ recording: "新檔案"
</voice>
</phrase>
<phrase>
@@ -8434,10 +7741,10 @@
*: "Add to Shortcuts"
</source>
<dest>
- *: "添加到快æ·"
+ *: "添加到æ·å¾‘中"
</dest>
<voice>
- *: "Add to Shortcuts"
+ *: "添加到æ·å¾‘中"
</voice>
</phrase>
<phrase>
@@ -8454,7 +7761,7 @@
</dest>
<voice>
*: none
- radio: ""
+ radio: "é è¨­"
</voice>
</phrase>
<phrase>
@@ -8468,7 +7775,7 @@
*: "主é¸å–®"
</dest>
<voice>
- *: "Main Menu"
+ *: "主é¸å–®"
</voice>
</phrase>
<phrase>
@@ -8482,7 +7789,7 @@
*: "éŠæˆ²"
</dest>
<voice>
- *: "Games"
+ *: "éŠæˆ²"
</voice>
</phrase>
<phrase>
@@ -8491,15 +7798,12 @@
user: core
<source>
*: "End of Song List"
- archosplayer: "End of List"
</source>
<dest>
*: "列表çµæŸ"
- archosplayer: "列表çµæŸ"
</dest>
<voice>
- *: "End of Song List"
- archosplayer: "End of List"
+ *: "列表çµæŸ"
</voice>
</phrase>
<phrase>
@@ -8516,7 +7820,7 @@
</dest>
<voice>
*: none
- alarm: "Alarm Wake up Screen"
+ alarm: "鬧鈴啟動畫é¢"
</voice>
</phrase>
<phrase>
@@ -8533,7 +7837,7 @@
</dest>
<voice>
*: none
- recording: "Clear Recording Directory"
+ recording: "清除錄音目錄"
</voice>
</phrase>
<phrase>
@@ -8564,7 +7868,7 @@
</dest>
<voice>
*: none
- agc: "Live (slow)"
+ agc: "ç¾å ´ (æ…¢)"
</voice>
</phrase>
<phrase>
@@ -8578,7 +7882,7 @@
*: "儲存佈景主題設定"
</dest>
<voice>
- *: "Save Theme Settings"
+ *: "儲存佈景主題設定"
</voice>
</phrase>
<phrase>
@@ -8599,20 +7903,6 @@
</voice>
</phrase>
<phrase>
- id: LANG_SYSFONT_MODE
- desc: in wps F2 pressed
- user: core
- <source>
- *: "Mode:"
- </source>
- <dest>
- *: "Mode:"
- </dest>
- <voice>
- *: ""
- </voice>
-</phrase>
-<phrase>
id: LANG_AGC_VOICE
desc: AGC preset
user: core
@@ -8626,7 +7916,7 @@
</dest>
<voice>
*: none
- agc: "Voice (fast)"
+ agc: "語音 (快)"
</voice>
</phrase>
<phrase>
@@ -8643,7 +7933,7 @@
</dest>
<voice>
*: none
- recording: "No settings available"
+ recording: "(無設定)"
</voice>
</phrase>
<phrase>
@@ -8660,7 +7950,7 @@
</dest>
<voice>
*: none
- recording: ""
+ recording: "最少"
</voice>
</phrase>
<phrase>
@@ -8677,7 +7967,7 @@
</dest>
<voice>
*: none
- recording: "Split Filesize"
+ recording: "分割檔案大å°"
</voice>
</phrase>
<phrase>
@@ -8691,7 +7981,7 @@
*: "è²é“"
</dest>
<voice>
- *: "Channels"
+ *: "è²é“"
</voice>
</phrase>
<phrase>
@@ -8708,7 +7998,7 @@
</dest>
<voice>
*: none
- remote: "(Vol- : Re-enable)"
+ remote: "(Vol- : é‡æ–°é–‹å•Ÿ)"
</voice>
</phrase>
<phrase>
@@ -8722,7 +8012,7 @@
*: "æ¯æ¬¡æå•"
</dest>
<voice>
- *: "Ask"
+ *: "æ¯æ¬¡æå•"
</voice>
</phrase>
<phrase>
@@ -8753,7 +8043,7 @@
</dest>
<voice>
*: none
- agc: "Medium"
+ agc: "中等"
</voice>
</phrase>
<phrase>
@@ -8787,7 +8077,7 @@
</dest>
<voice>
*: none
- agc: "Safety (clip)"
+ agc: "安全 (clip)"
</voice>
</phrase>
<phrase>
@@ -8801,7 +8091,7 @@
*: "繼續播放"
</dest>
<voice>
- *: "Resume Playback"
+ *: "繼續播放"
</voice>
</phrase>
<phrase>
@@ -8818,21 +8108,21 @@
</dest>
<voice>
*: none
- headphone_detection: "Pause on Headphone Unplug"
+ headphone_detection: "耳機拔出後暫åœ"
</voice>
</phrase>
<phrase>
id: LANG_REPLACE
- desc: in onplay menu. Replace the current playlist with a new one.
+ desc: deprecated
user: core
<source>
- *: "Play Next"
+ *: ""
</source>
<dest>
- *: "播放下一首"
+ *: ""
</dest>
<voice>
- *: "Play Next"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -8863,7 +8153,7 @@
</dest>
<voice>
*: none
- recording: "Split Measure"
+ recording: "分割ä¾æ“š"
</voice>
</phrase>
<phrase>
@@ -8891,7 +8181,7 @@
*: "電池已空! 充電!"
</dest>
<voice>
- *: "Battery empty! RECHARGE!"
+ *: "電池已空! 充電!"
</voice>
</phrase>
<phrase>
@@ -8908,7 +8198,7 @@
</dest>
<voice>
*: none
- radio: "Korea"
+ radio: "韓國"
</voice>
</phrase>
<phrase>
@@ -8922,7 +8212,7 @@
*: "é‹ä½œæ™‚é–“"
</dest>
<voice>
- *: "Running Time"
+ *: "é‹ä½œæ™‚é–“"
</voice>
</phrase>
<phrase>
@@ -8939,7 +8229,7 @@
</dest>
<voice>
*: none
- radio: "US and Canada"
+ radio: "美國 / 加拿大"
</voice>
</phrase>
<phrase>
@@ -8956,7 +8246,7 @@
</dest>
<voice>
*: none
- radio: "Region"
+ radio: "å€åŸŸ"
</voice>
</phrase>
<phrase>
@@ -9001,7 +8291,7 @@
*: "增加到新的播放清單"
</dest>
<voice>
- *: "Add to New Playlist"
+ *: "增加到新的播放清單"
</voice>
</phrase>
<phrase>
@@ -9018,7 +8308,7 @@
</dest>
<voice>
*: none
- recording: "Stop"
+ recording: "åœæ­¢"
</voice>
</phrase>
<phrase>
@@ -9049,7 +8339,7 @@
</dest>
<voice>
*: none
- recording: "Split Time"
+ recording: "分割時間"
</voice>
</phrase>
<phrase>
@@ -9063,7 +8353,7 @@
*: "è«‹é‡é–‹æ©Ÿä»¥å•Ÿç”¨"
</dest>
<voice>
- *: "Please reboot to enable"
+ *: "è«‹é‡é–‹æ©Ÿä»¥å•Ÿç”¨"
</voice>
</phrase>
<phrase>
@@ -9077,7 +8367,7 @@
*: "有關é¸å–®"
</dest>
<voice>
- *: "Context Menu"
+ *: "有關é¸å–®"
</voice>
</phrase>
<phrase>
@@ -9091,7 +8381,7 @@
*: "å…ˆå‰ç•«é¢"
</dest>
<voice>
- *: "Previous Screen"
+ *: "å…ˆå‰ç•«é¢"
</voice>
</phrase>
<phrase>
@@ -9105,7 +8395,7 @@
*: "增加到播放清單"
</dest>
<voice>
- *: "Add to Playlist"
+ *: "增加到播放清單"
</voice>
</phrase>
<phrase>
@@ -9122,7 +8412,7 @@
</dest>
<voice>
*: none
- buttonlight_brightness: "Button Light Brightness"
+ buttonlight_brightness: "按鈕光度"
</voice>
</phrase>
<phrase>
@@ -9136,7 +8426,7 @@
*: "Cuesheet 支æ´"
</dest>
<voice>
- *: "Cuesheet Support"
+ *: "Cuesheet 支æ´"
</voice>
</phrase>
<phrase>
@@ -9178,7 +8468,7 @@
*: "載入中... %d%% å®Œæˆ (%s)"
</dest>
<voice>
- *: ""
+ *: "載入中... %d%% å®Œæˆ (%s)"
</voice>
</phrase>
<phrase>
@@ -9195,7 +8485,7 @@
</dest>
<voice>
*: none
- recording: "Start new file"
+ recording: "開新檔案"
</voice>
</phrase>
<phrase>
@@ -9212,7 +8502,7 @@
</dest>
<voice>
*: none
- recording: "What to do when Splitting"
+ recording: "分割時行為"
</voice>
</phrase>
<phrase>
@@ -9226,7 +8516,7 @@
*: "é–‹æ©Ÿç•«é¢"
</dest>
<voice>
- *: "Start Screen"
+ *: "é–‹æ©Ÿç•«é¢"
</voice>
</phrase>
<phrase>
@@ -9240,7 +8530,7 @@
*: "<ä¸è¦æ¢å¾©æ›¸ç°½>"
</dest>
<voice>
- *: "Do not resume"
+ *: "<ä¸è¦æ¢å¾©æ›¸ç°½>"
</voice>
</phrase>
<phrase>
@@ -9254,7 +8544,7 @@
*: "刪除"
</dest>
<voice>
- *: "Delete"
+ *: "刪除"
</voice>
</phrase>
<phrase>
@@ -9271,7 +8561,7 @@
</dest>
<voice>
*: none
- radio: "Japan"
+ radio: "日本"
</voice>
</phrase>
<phrase>
@@ -9285,7 +8575,7 @@
*: "顯示路徑"
</dest>
<voice>
- *: "Show Path"
+ *: "顯示路徑"
</voice>
</phrase>
<phrase>
@@ -9299,12 +8589,12 @@
*: "é¸æ“‡æ›¸ç°½"
</dest>
<voice>
- *: "Select Bookmark"
+ *: "é¸æ“‡æ›¸ç°½"
</voice>
</phrase>
<phrase>
id: LANG_DITHERING
- desc: in the sound settings menu
+ desc: in the sound settings and some other menus
user: core
<source>
*: "Dithering"
@@ -9313,7 +8603,7 @@
*: "添加抖動"
</dest>
<voice>
- *: "Dithering"
+ *: "添加抖動"
</voice>
</phrase>
<phrase>
@@ -9327,7 +8617,7 @@
*: "應用程åº"
</dest>
<voice>
- *: "Applications"
+ *: "應用程åº"
</voice>
</phrase>
<phrase>
@@ -9343,7 +8633,7 @@
wheel_acceleration: none
</dest>
<voice>
- *: "List Acceleration Start Delay"
+ *: "列表加速緩è¡"
wheel_acceleration: none
</voice>
</phrase>
@@ -9358,7 +8648,7 @@
*: "正在移動..."
</dest>
<voice>
- *: "Moving"
+ *: "正在移動..."
</voice>
</phrase>
<phrase>
@@ -9372,7 +8662,7 @@
*: "使用 .talk 文件夾"
</dest>
<voice>
- *: "Use Directory .talk Clips"
+ *: "使用 .talk 文件夾"
</voice>
</phrase>
<phrase>
@@ -9386,7 +8676,7 @@
*: "屬性"
</dest>
<voice>
- *: "Properties"
+ *: "屬性"
</voice>
</phrase>
<phrase>
@@ -9442,7 +8732,7 @@
*: "<無效書簽>"
</dest>
<voice>
- *: "Invalid Bookmark"
+ *: "<無效書簽>"
</voice>
</phrase>
<phrase>
@@ -9462,7 +8752,7 @@
<voice>
*: none
button_light: "Button Light Timeout"
- sansae200*,sansafuze*: "Wheel Light Timeout"
+ sansae200*,sansafuze*: "轉盤燈自動關時間"
</voice>
</phrase>
<phrase>
@@ -9507,24 +8797,7 @@
*: "åªæœ‰ç›®å‰ç›®éŒ„"
</dest>
<voice>
- *: "Current Directory Only"
- </voice>
-</phrase>
-<phrase>
- id: LANG_SYSFONT_CHANNELS
- desc: in the recording settings
- user: core
- <source>
- *: none
- recording: "Channels"
- </source>
- <dest>
- *: none
- recording: "è²é“"
- </dest>
- <voice>
- *: none
- recording: "Channels"
+ *: "åªæœ‰ç›®å‰ç›®éŒ„"
</voice>
</phrase>
<phrase>
@@ -9538,7 +8811,7 @@
*: "播放清單"
</dest>
<voice>
- *: "Playlists"
+ *: "播放清單"
</voice>
</phrase>
<phrase>
@@ -9594,7 +8867,7 @@
*: "清除時間?"
</dest>
<voice>
- *: "Clear Time?"
+ *: "清除時間?"
</voice>
</phrase>
<phrase>
@@ -9608,7 +8881,7 @@
*: "Last.fm 紀錄"
</dest>
<voice>
- *: "Last.fm Log"
+ *: "Last.fm 紀錄"
</voice>
</phrase>
<phrase>
@@ -9625,7 +8898,7 @@
</dest>
<voice>
*: none
- hold_button: "Backlight on hold"
+ hold_button: "背光 (Hold 時)"
</voice>
</phrase>
<phrase>
@@ -9639,7 +8912,7 @@
*: "設置"
</dest>
<voice>
- *: "Settings"
+ *: "設置"
</voice>
</phrase>
<phrase>
@@ -9653,7 +8926,7 @@
*: "å·²å–消"
</dest>
<voice>
- *: "Cancelled"
+ *: "å·²å–消"
</voice>
</phrase>
<phrase>
@@ -9681,7 +8954,7 @@
*: "你肯定嗎?"
</dest>
<voice>
- *: "Are You Sure?"
+ *: "你肯定嗎?"
</voice>
</phrase>
<phrase>
@@ -9695,7 +8968,7 @@
*: "%s ä¸å­˜åœ¨"
</dest>
<voice>
- *: ""
+ *: "%s ä¸å­˜åœ¨"
</voice>
</phrase>
<phrase>
@@ -9709,7 +8982,7 @@
*: "沒有播放清單"
</dest>
<voice>
- *: ""
+ *: "沒有播放清單"
</voice>
</phrase>
<phrase>
@@ -9723,7 +8996,7 @@
*: "複製中..."
</dest>
<voice>
- *: "Copying"
+ *: "複製中..."
</voice>
</phrase>
<phrase>
@@ -9757,7 +9030,7 @@
</dest>
<voice>
*: none
- recording: "Set As Recording Directory"
+ recording: "設為錄音目錄"
</voice>
</phrase>
<phrase>
@@ -9774,7 +9047,7 @@
</dest>
<voice>
*: none
- radio: "FM Radio Menu"
+ radio: "FM 收音é¸å–®"
</voice>
</phrase>
<phrase>
@@ -9791,7 +9064,7 @@
</dest>
<voice>
*: none
- recording: "Clip Counter"
+ recording: "削波屢計"
</voice>
</phrase>
<phrase>
@@ -9861,7 +9134,7 @@
*: "讀出文件格å¼"
</dest>
<voice>
- *: "Say File Type"
+ *: "讀出文件格å¼"
</voice>
</phrase>
<phrase>
@@ -9878,7 +9151,7 @@
</dest>
<voice>
*: none
- multivolume: "Not present"
+ multivolume: "ä¸å­˜åœ¨"
</voice>
</phrase>
<phrase>
@@ -9895,7 +9168,7 @@
</dest>
<voice>
*: none
- gigabeatfx,ipod6g,ipodvideo,mpiohd200,mpiohd300,mrobe100: "Bass Cutoff"
+ gigabeatfx,ipod6g,ipodvideo,mpiohd200,mpiohd300,mrobe100: "低音截斷"
</voice>
</phrase>
<phrase>
@@ -9912,7 +9185,7 @@
</dest>
<voice>
*: none
- lcd_color: "Primary Colour"
+ lcd_color: "é …ç›®é¸æ“‡æ¢ä¸»è¦é¡è‰²"
</voice>
</phrase>
<phrase>
@@ -9940,7 +9213,7 @@
*: "讀出電池剩餘容é‡"
</dest>
<voice>
- *: "Announce Battery Level"
+ *: "讀出電池剩餘容é‡"
</voice>
</phrase>
<phrase>
@@ -9957,7 +9230,7 @@
</dest>
<voice>
*: none
- lcd_color: "Line Selector Colours"
+ lcd_color: "é …ç›®é¸æ“‡æ¢é¡è‰²"
</voice>
</phrase>
<phrase>
@@ -9988,7 +9261,7 @@
</dest>
<voice>
*: none
- lcd_color: "Text Colour"
+ lcd_color: "å­—é«”é¡è‰²"
</voice>
</phrase>
<phrase>
@@ -10002,7 +9275,7 @@
*: "主題設定"
</dest>
<voice>
- *: "Theme Settings"
+ *: "主題設定"
</voice>
</phrase>
<phrase>
@@ -10033,7 +9306,7 @@
</dest>
<voice>
*: none
- lcd_color: "Bar (Gradient Colour)"
+ lcd_color: "漸變é¡è‰²æ¢"
</voice>
</phrase>
<phrase>
@@ -10050,7 +9323,7 @@
</dest>
<voice>
*: none
- lcd_color: "Bar (Solid Colour)"
+ lcd_color: "實色æ¢"
</voice>
</phrase>
<phrase>
@@ -10067,7 +9340,7 @@
</dest>
<voice>
*: none
- lcd_color: "Colours"
+ lcd_color: "é¡è‰²"
</voice>
</phrase>
<phrase>
@@ -10084,7 +9357,7 @@
</dest>
<voice>
*: none
- lcd_color: "Secondary Colour"
+ lcd_color: "é …ç›®é¸æ“‡æ¢æ¬¡è¦é¡è‰²"
</voice>
</phrase>
<phrase>
@@ -10097,11 +9370,11 @@
</source>
<dest>
*: none
- gigabeatfx,ipod6g,ipodvideo,mpiohd200,mpiohd300,mrobe100: "高è²æˆªæ–·"
+ gigabeatfx,ipod6g,ipodvideo,mpiohd200,mpiohd300,mrobe100: "高音截斷"
</dest>
<voice>
*: none
- gigabeatfx,ipod6g,ipodvideo,mpiohd200,mpiohd300,mrobe100: "Treble Cutoff"
+ gigabeatfx,ipod6g,ipodvideo,mpiohd200,mpiohd300,mrobe100: "高音截斷"
</voice>
</phrase>
<phrase>
@@ -10115,7 +9388,7 @@
*: "æ­æ´²ä¸­éƒ¨ (CP1250)"
</dest>
<voice>
- *: "Central European"
+ *: "æ­æ´²ä¸­éƒ¨ (CP1250)"
</voice>
</phrase>
<phrase>
@@ -10262,15 +9535,15 @@
user: core
<source>
*: none
- gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
</source>
<dest>
*: none
- gigabeatfx,sansafuzeplus: "觸摸æ¿éˆæ•åº¦"
+ fiiom3k, fiiom3k,gigabeatfx,sansafuzeplus: "觸摸æ¿éˆæ•åº¦"
</dest>
<voice>
*: none
- gigabeatfx,sansafuzeplus: "觸摸æ¿éˆæ•åº¦"
+ fiiom3k, fiiom3k,gigabeatfx,sansafuzeplus: "觸摸æ¿éˆæ•åº¦"
</voice>
</phrase>
<phrase>
@@ -10847,11 +10120,11 @@
</source>
<dest>
*: none
- touchscreen: "那個地方"
+ touchscreen: "å…·é«”ä½ç½®"
</dest>
<voice>
*: none
- touchscreen: "那個地方"
+ touchscreen: "å…·é«”ä½ç½®"
</voice>
</phrase>
<phrase>
@@ -10881,11 +10154,62 @@
</source>
<dest>
*: none
- touchscreen: "觸控模å¼"
+ touchscreen: "觸控é¢æ¿æ“作模å¼"
+ </dest>
+ <voice>
+ *: none
+ touchscreen: "觸控é¢æ¿æ“作模å¼"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_TOUCHSCREEN_CALIBRATE
+ desc: in Settings -> General -> Display -> Touchscreen Settings
+ user: core
+ <source>
+ *: none
+ touchscreen: "Calibrate"
+ </source>
+ <dest>
+ *: none
+ touchscreen: "觸控é¢æ¿æ ¡æ­£"
+ </dest>
+ <voice>
+ *: none
+ touchscreen: "觸控é¢æ¿æ ¡æ­£"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_TOUCHSCREEN_RESET_CALIBRATION
+ desc: in Settings -> General -> Display -> Touchscreen Settings
+ user: core
+ <source>
+ *: none
+ touchscreen: "Reset Calibration"
+ </source>
+ <dest>
+ *: none
+ touchscreen: "é‡æ–°æ ¡æ­£è§¸æŽ§"
</dest>
<voice>
*: none
- touchscreen: "Touchscreen é¸æ“‡"
+ touchscreen: "é‡æ–°æ ¡æ­£è§¸æŽ§"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_USE_SHORTCUTS_INSTEAD_OF_QS
+ desc: in settings_menu.
+ user: core
+ <source>
+ *: none
+ quickscreen: "Use Shortcuts Menu Instead of Quick Screen"
+ </source>
+ <dest>
+ *: none
+ quickscreen: "使用快æ·é¸å–®æ›¿ä»£å¿«æ·ç†’幕"
+ </dest>
+ <voice>
+ *: none
+ quickscreen: "使用快æ·é¸å–®æ›¿ä»£å¿«æ·ç†’幕"
</voice>
</phrase>
<phrase>
@@ -10903,17 +10227,17 @@
</voice>
</phrase>
<phrase>
- id: LANG_VOLUME_LIMIT
- desc: in sound_settings
+ id: LANG_GLYPHS
+ desc: in settings_menu
user: core
<source>
- *: "Maximum Volume Limit"
+ *: "Glyphs To Cache"
</source>
<dest>
- *: "音é‡é™åˆ¶"
+ *: "å¿«å–字型容ç´æ•¸é‡"
</dest>
<voice>
- *: "音é‡é™åˆ¶"
+ *: "å¿«å–字型容ç´æ•¸é‡"
</voice>
</phrase>
<phrase>
@@ -10941,35 +10265,35 @@
*: "å•“å‹•ç¡çœ è¨ˆæ™‚器"
</dest>
<voice>
- *: "Start Sleep Timer"
+ *: "å•“å‹•ç¡çœ è¨ˆæ™‚器"
</voice>
</phrase>
<phrase>
- id: LANG_SLEEP_TIMER_CANCEL_CURRENT
- desc: shown instead of sleep timer when it's running
+ id: LANG_SLEEP_TIMER_DURATION
+ desc: default sleep timer duration in minutes
user: core
<source>
- *: "Cancel Sleep Timer"
+ *: "Default Sleep Timer Duration"
</source>
<dest>
- *: "åœæ­¢ç¡çœ è¨ˆæ™‚器"
+ *: "ç¡çœ å€’計時長度"
</dest>
<voice>
- *: "åœæ­¢ç¡çœ è¨ˆæ™‚器"
+ *: "ç¡çœ å€’計時長度"
</voice>
</phrase>
<phrase>
- id: LANG_SLEEP_TIMER_DURATION
- desc: default sleep timer duration in minutes
+ id: LANG_KEYPRESS_RESTARTS_SLEEP_TIMER
+ desc: whether to restart running sleep timer on keypress
user: core
<source>
- *: "Default Sleep Timer Duration"
+ *: "Restart Sleep Timer On Keypress"
</source>
<dest>
- *: "ç¡çœ å€’計時長度"
+ *: "åµæ¸¬åˆ°æŒ‰éµæ™‚é‡ç½®ç¡çœ è¨ˆæ™‚器"
</dest>
<voice>
- *: "ç¡çœ å€’計時長度"
+ *: "åµæ¸¬åˆ°æŒ‰éµæ™‚é‡ç½®ç¡çœ è¨ˆæ™‚器"
</voice>
</phrase>
<phrase>
@@ -10987,17 +10311,17 @@
</voice>
</phrase>
<phrase>
- id: LANG_KEYPRESS_RESTARTS_SLEEP_TIMER
- desc: whether to restart running sleep timer on keypress
+ id: LANG_SHORTCUTS
+ desc: Title in the shortcuts menu
user: core
<source>
- *: "Restart Sleep Timer On Keypress"
+ *: "Shortcuts"
</source>
<dest>
- *: "åµæ¸¬åˆ°æŒ‰éµæ™‚é‡ç½®ç¡çœ è¨ˆæ™‚器"
+ *: "æ·å¾‘"
</dest>
<voice>
- *: "åµæ¸¬åˆ°æŒ‰éµæ™‚é‡ç½®ç¡çœ è¨ˆæ™‚器"
+ *: "æ·å¾‘"
</voice>
</phrase>
<phrase>
@@ -11043,478 +10367,244 @@
</voice>
</phrase>
<phrase>
- id: LANG_SET_AS_START_DIR
- desc: used in the onplay menu to set a starting browser dir
- user: core
- <source>
- *: "Start File Browser Here"
- </source>
- <dest>
- *: "設爲起始檔案ç€è¦½ä½ç½®"
- </dest>
- <voice>
- *: "設爲起始檔案ç€è¦½ä½ç½®"
- </voice>
-</phrase>
-<phrase>
- id: LANG_RESET_START_DIR
- desc: reset the browser start directory
- user: core
- <source>
- *: "Start File Browser at /"
- </source>
- <dest>
- *: "復原檔案ç€è¦½èµ·å§‹ä½ç½®"
- </dest>
- <voice>
- *: "復原檔案ç€è¦½èµ·å§‹ä½ç½®"
- </voice>
-</phrase>
-<phrase>
- id: LANG_SELECT_DATABASE_DIRS
- desc: in settings_menu
- user: core
- <source>
- *: "Select directories to scan"
- </source>
- <dest>
- *: "自定è¦æŽƒæ的資料夾"
- </dest>
- <voice>
- *: "自定資料庫è¦é€²è¡Œåª’體掃æ的資料夾"
- </voice>
-</phrase>
-<phrase>
- id: LANG_USE_SHORTCUTS_INSTEAD_OF_QS
- desc: in settings_menu.
- user: core
- <source>
- *: none
- quickscreen: "Use Shortcuts Menu Instead of Quick Screen"
- </source>
- <dest>
- *: none
- quickscreen: "使用快æ·é¸å–®æ›¿ä»£å¿«æ·ç†’幕"
- </dest>
- <voice>
- *: none
- quickscreen: "使用快æ·é¸å–®æ›¿ä»£å¿«æ·ç†’幕"
- </voice>
-</phrase>
-<phrase>
- id: LANG_RADIOSCREEN
- desc: in the theme menu
- user: core
- <source>
- *: none
- radio: "Radio Screen"
- </source>
- <dest>
- *: none
- radio: "收音機接å£ä¸»é¡Œ"
- </dest>
- <voice>
- *: none
- radio: "收音機接å£ä¸»é¡Œ"
- </voice>
-</phrase>
-<phrase>
- id: LANG_BASE_SKIN
- desc: browse for the base skin in theme settings
- user: core
- <source>
- *: "Base Skin"
- </source>
- <dest>
- *: "狀態列佈景主題"
- </dest>
- <voice>
- *: "狀態列佈景主題"
- </voice>
-</phrase>
-<phrase>
- id: LANG_SCROLLBAR_WIDTH
- desc: in Settings -> General -> Display -> Status-/Scrollbar
- user: core
- <source>
- *: "Scroll Bar Width"
- </source>
- <dest>
- *: "æ²å‹•è»¸å¯¬åº¦"
- </dest>
- <voice>
- *: "æ²å‹•è»¸å¯¬åº¦"
- </voice>
-</phrase>
-<phrase>
- id: LANG_PAUSE_REWIND
- desc: Seconds to rewind when rewind on pause is enabled.
- user: core
- <source>
- *: "Rewind on Pause"
- </source>
- <dest>
- *: "æš«åœæ™‚自動倒退"
- </dest>
- <voice>
- *: "æš«åœæ™‚自動倒退"
- </voice>
-</phrase>
-<phrase>
- id: LANG_RESUME_REWIND
- desc: in playback settings menu
- user: core
- <source>
- *: "Rewind Before Resume"
- </source>
- <dest>
- *: "æš«åœæ™‚自動倒退長度"
- </dest>
- <voice>
- *: "æš«åœæ™‚自動倒退長度"
- </voice>
-</phrase>
-<phrase>
- id: LANG_FILTER_ROLL_OFF
+ id: LANG_COMPRESSOR
desc: in sound settings
user: core
<source>
- *: none
- filter_roll_off: "DAC's filter roll-off"
+ *: "Compressor"
</source>
<dest>
- *: none
- filter_roll_off: "DAC濾波器滾é™"
+ *: "壓縮器"
</dest>
<voice>
- *: none
- filter_roll_off: "DAC濾波器滾é™"
+ *: "壓縮器"
</voice>
</phrase>
<phrase>
- id: LANG_FILTER_SHARP
+ id: LANG_COMPRESSOR_RATIO
desc: in sound settings
user: core
<source>
- *: none
- filter_roll_off: "Sharp"
+ *: "Ratio"
</source>
<dest>
- *: none
- filter_roll_off: "快速"
+ *: "壓縮比例"
</dest>
<voice>
- *: none
- filter_roll_off: "快速滾é™"
+ *: "壓縮比例"
</voice>
</phrase>
<phrase>
- id: LANG_FILTER_SLOW
+ id: LANG_COMPRESSOR_RELEASE
desc: in sound settings
user: core
<source>
- *: none
- filter_roll_off: "Slow"
+ *: "Release Time"
</source>
<dest>
- *: none
- filter_roll_off: "平緩"
+ *: "壓縮釋放時間"
</dest>
<voice>
- *: none
- filter_roll_off: "平緩滾é™"
+ *: "壓縮釋放時間"
</voice>
</phrase>
<phrase>
- id: LANG_PBE
+ id: LANG_COMPRESSOR_THRESHOLD
desc: in sound settings
user: core
<source>
- *: "Perceptual Bass Enhancement"
+ *: "Threshold"
</source>
<dest>
- *: "感知性低音增強"
+ *: "閥值"
</dest>
<voice>
- *: "感知性低音增強音效"
+ *: "閥值"
</voice>
</phrase>
<phrase>
- id: LANG_AFR
+ id: LANG_COMPRESSOR_GAIN
desc: in sound settings
user: core
<source>
- *: "Auditory Fatigue Reduction"
- </source>
- <dest>
- *: "è²å­¸æŠ—疲勞"
- </dest>
- <voice>
- *: "è²å­¸æŠ—疲勞音效"
- </voice>
-</phrase>
-<phrase>
- id: LANG_SURROUND
- desc: in the sound settings menu
- user: core
- <source>
- *: "Haas Surround"
+ *: "Makeup Gain"
</source>
<dest>
- *: "哈斯環繞"
+ *: "增益"
</dest>
<voice>
- *: "哈斯環繞音效"
+ *: "增益"
</voice>
</phrase>
<phrase>
- id: LANG_SURROUND_FX1
+ id: LANG_COMPRESSOR_KNEE
desc: in sound settings
user: core
<source>
- *: "f(x1)"
+ *: "Knee"
</source>
<dest>
- *: "f(x1)"
+ *: "æ‹é»ž"
</dest>
<voice>
- *: "f(x1)"
+ *: "æ‹é»ž"
</voice>
</phrase>
<phrase>
- id: LANG_SURROUND_FX2
+ id: LANG_COMPRESSOR_SOFT_KNEE
desc: in sound settings
user: core
<source>
- *: "f(x2)"
+ *: "Soft Knee"
</source>
<dest>
- *: "f(x2)"
+ *: "軟æ‹é»ž"
</dest>
<voice>
- *: "f(x2)"
+ *: "軟æ‹é»ž"
</voice>
</phrase>
<phrase>
- id: LANG_SURROUND_METHOD2
+ id: LANG_COMPRESSOR_HARD_KNEE
desc: in sound settings
user: core
<source>
- *: "SIDE ONLY"
+ *: "Hard Knee"
</source>
<dest>
- *: "SIDE ONLY"
+ *: "硬æ‹é»ž"
</dest>
<voice>
- *: "SIDE ONLY"
+ *: "硬æ‹é»ž"
</voice>
</phrase>
<phrase>
- id: LANG_SURROUND_MIX
+ id: LANG_AUTO
desc: in sound settings
user: core
<source>
- *: "Dry / Wet Mix"
- </source>
- <dest>
- *: "乾濕混åˆåº¦"
- </dest>
- <voice>
- *: "乾濕混åˆåº¦"
- </voice>
-</phrase>
-<phrase>
- id: LANG_IBASSO_FREQ_SCALING_GOVERNOR
- desc: in Settings -> General -> System -> Freq Scaling Governor
- user: core
- <source>
- *: none
- ibassodx50,ibassodx90: "Freq Scaling Governor"
- </source>
- <dest>
- *: none
- ibassodx50,ibassodx90: "CPU頻率管ç†å™¨"
- </dest>
- <voice>
- *: none
- ibassodx50,ibassodx90: "CPU頻率管ç†å™¨"
- </voice>
-</phrase>
-<phrase>
- id: LANG_USB_MODE
- desc: in Settings -> General -> System -> USB Mode
- user: core
- <source>
- *: "USB Mode"
- </source>
- <dest>
- *: "USB模å¼"
- </dest>
- <voice>
- *: "USB模å¼"
- </voice>
-</phrase>
-<phrase>
- id: LANG_USB_MODE_MASS_STORAGE
- desc: in Settings -> General -> System -> USB Mode: Mass Storage
- user: core
- <source>
- *: "Mass Storage"
- </source>
- <dest>
- *: "大容é‡å­˜å„²"
- </dest>
- <voice>
- *: "大容é‡å­˜å„²"
- </voice>
-</phrase>
-<phrase>
- id: LANG_USB_MODE_CHARGE
- desc: in Settings -> General -> System -> USB Mode: Charge Only
- user: core
- <source>
- *: "Charge Only"
- </source>
- <dest>
- *: "僅充電"
- </dest>
- <voice>
- *: "僅充電"
- </voice>
-</phrase>
-<phrase>
- id: LANG_USB_MODE_ADB
- desc: in Settings -> General -> System -> USB Mode: Android Debug Bridge
- user: core
- <source>
- *: "Android Debug Bridge"
+ *: "Auto"
</source>
<dest>
- *: "ADBåµéŒ¯"
+ *: "自動"
</dest>
<voice>
- *: "ADBåµéŒ¯"
+ *: "自動"
</voice>
</phrase>
<phrase>
- id: LANG_STATUSBAR_BOTTOM
- desc: in Settings -> General -> Display -> statusbar
+ id: LANG_TIMESTRETCH
+ desc: timestretch enable
user: core
<source>
- *: "Bottom"
+ *: "Timestretch"
</source>
<dest>
- *: "底部"
+ *: "時間伸縮"
</dest>
<voice>
- *: "Bottom"
+ *: "時間伸縮"
</voice>
</phrase>
<phrase>
- id: LANG_LEFT
- desc: Generic use of 'left'
+ id: LANG_RESUME_REWIND
+ desc: in playback settings menu
user: core
<source>
- *: "Left"
+ *: "Rewind Before Resume"
</source>
<dest>
- *: "Left"
+ *: "æš«åœæ™‚自動倒退長度"
</dest>
<voice>
- *: "Left"
+ *: "æš«åœæ™‚自動倒退長度"
</voice>
</phrase>
<phrase>
- id: LANG_LIST_SEPARATOR
- desc: line between lines in lists
+ id: LANG_PAUSE_REWIND
+ desc: Seconds to rewind when rewind on pause is enabled.
user: core
<source>
- *: "Line Separator"
+ *: "Rewind on Pause"
</source>
<dest>
- *: "分隔行"
+ *: "æš«åœæ™‚自動倒退"
</dest>
<voice>
- *: "Line Separator"
+ *: "æš«åœæ™‚自動倒退"
</voice>
</phrase>
<phrase>
- id: LANG_SKIP_OUTRO
- desc: skipping to the 5 seconds before the end of a track
+ id: LANG_REPLAYGAIN
+ desc: in replaygain
user: core
<source>
- *: "Skip to Outro"
+ *: "Replaygain"
</source>
<dest>
- *: "快轉"
+ *: "播放增益"
</dest>
<voice>
- *: "Skip to Outro"
+ *: "播放增益"
</voice>
</phrase>
<phrase>
- id: LANG_COMPRESSOR_THRESHOLD
- desc: in sound settings
+ id: LANG_RESET_START_DIR
+ desc: reset the browser start directory
user: core
<source>
- *: "Threshold"
+ *: "Start File Browser at /"
</source>
<dest>
- *: "é–¾"
+ *: "復原檔案ç€è¦½èµ·å§‹ä½ç½®"
</dest>
<voice>
- *: "Threshold"
+ *: "復原檔案ç€è¦½èµ·å§‹ä½ç½®"
</voice>
</phrase>
<phrase>
- id: LANG_FM_STATION_HEADER
- desc: in radio screen
+ id: LANG_SELECT_DATABASE_DIRS
+ desc: in settings_menu
user: core
<source>
- *: none
- radio: "Station:"
+ *: "Select directories to scan"
</source>
<dest>
- *: none
- radio: "Station:"
+ *: "自定è¦æŽƒæ的資料夾"
</dest>
<voice>
- *: none
- radio: ""
+ *: "自定資料庫è¦é€²è¡Œåª’體掃æ的資料夾"
</voice>
</phrase>
<phrase>
- id: LANG_HW_EQ_WIDTH_WIDE
- desc: in sound_menu, hardware equalizer tone controls wide bandwidth setting
+ id: LANG_PM_RELEASE
+ desc: in the peak meter menu
user: core
<source>
- *: none
- gigabeats,samsungypr1: "Wide"
+ *: "Peak Release"
</source>
<dest>
- *: none
- gigabeats,samsungypr1: "寬"
+ *: "峰值釋放速度"
</dest>
<voice>
- *: none
- gigabeats,samsungypr1: "Wide"
+ *: "峰值釋放速度"
</voice>
</phrase>
<phrase>
- id: LANG_TOP_QS_ITEM
- desc: used for the submenu name for the quickscreen items
+ id: LANG_TOUCHSCREEN_SETTINGS
+ desc: in Settings -> General -> Display menu
user: core
<source>
*: none
- quickscreen: "Set as Top Quickscreen Item"
+ touchscreen: "Touchscreen Settings"
</source>
<dest>
*: none
- quickscreen: "Set as Top Quickscreen Item"
+ touchscreen: "觸控é¢æ¿è¨­å®š"
</dest>
<voice>
*: none
- quickscreen: "Set as Top Quickscreen Item"
+ touchscreen: "觸控é¢æ¿è¨­å®š"
</voice>
</phrase>
<phrase>
@@ -11527,546 +10617,132 @@
</source>
<dest>
*: none
- touchscreen: "Line Padding in Lists"
- </dest>
- <voice>
- *: none
- touchscreen: "Line Padding in Lists"
- </voice>
-</phrase>
-<phrase>
- id: LANG_SEMITONE
- desc:
- user: core
- <source>
- *: none
- pitchscreen: "Semitone"
- </source>
- <dest>
- *: none
- pitchscreen: "åŠéŸ³"
- </dest>
- <voice>
- *: none
- pitchscreen: "Semitone"
- </voice>
-</phrase>
-<phrase>
- id: LANG_QUEUE_LAST_SHUFFLED
- desc: in onplay menu. queue a playlist randomly at end of dynamic playlist
- user: core
- <source>
- *: "Queue Last Shuffled"
- </source>
- <dest>
- *: "Queue Last Shuffled"
- </dest>
- <voice>
- *: "Queue Last Shuffled"
- </voice>
-</phrase>
-<phrase>
- id: LANG_AUTOMATIC
- desc: generic automatic
- user: core
- <source>
- *: "Automatic"
- </source>
- <dest>
- *: "自動"
- </dest>
- <voice>
- *: "Automatic"
- </voice>
-</phrase>
-<phrase>
- id: LANG_REMOTE_BASE_SKIN
- desc: browse for the base skin in theme settings
- user: core
- <source>
- *: "Remote Base Skin"
- </source>
- <dest>
- *: "Remote Base Skin"
- </dest>
- <voice>
- *: "Remote Base Skin"
- </voice>
-</phrase>
-<phrase>
- id: LANG_KBD_OK
- desc: in keyboard
- user: core
- <source>
- *: none
- touchscreen: "OK"
- </source>
- <dest>
- *: none
- touchscreen: "OK"
+ touchscreen: "列表行間填充"
</dest>
<voice>
*: none
- touchscreen: "OK"
- </voice>
-</phrase>
-<phrase>
- id: LANG_NEXT_TRACK
- desc: Shown in WPS
- user: core
- <source>
- *: "Next Track:"
- </source>
- <dest>
- *: "下一首:"
- </dest>
- <voice>
- *: "Next Track:"
+ touchscreen: "列表行間填充"
</voice>
</phrase>
<phrase>
- id: LANG_REMOTE_SCREEN
- desc: in the main menu
- user: core
- <source>
- *: none
- remote: "Remote Screen"
- </source>
- <dest>
- *: none
- remote: "Remote Screen"
- </dest>
- <voice>
- *: none
- remote: "Remote Screen"
- </voice>
-</phrase>
-<phrase>
- id: LANG_DEADZONE
- desc: touchpad deadzone setting
- user: core
- <source>
- *: none
- sansafuzeplus: "Touchpad Dead Zone"
- </source>
- <dest>
- *: none
- sansafuzeplus: "觸控æ¿ç›²å€"
- </dest>
- <voice>
- *: none
- sansafuzeplus: "Touchpad Dead Zone"
- </voice>
-</phrase>
-<phrase>
- id: LANG_INSERT_LAST_SHUFFLED
- desc: in onplay menu. insert a playlist randomly at end of dynamic playlist
- user: core
- <source>
- *: "Insert Last Shuffled"
- </source>
- <dest>
- *: "Insert Last Shuffled"
- </dest>
- <voice>
- *: "Insert Last Shuffled"
- </voice>
-</phrase>
-<phrase>
- id: LANG_COMPRESSOR_RATIO_LIMIT
- desc: in sound settings
- user: core
- <source>
- *: "Limit"
- </source>
- <dest>
- *: "Limit"
- </dest>
- <voice>
- *: "Limit"
- </voice>
-</phrase>
-<phrase>
- id: LANG_SYSFONT_EQUALIZER_BAND_Q
- desc: in the equalizer settings menu
- user: core
- <source>
- *: "Q"
- </source>
- <dest>
- *: "å“質因數"
- </dest>
- <voice>
- *: "Q"
- </voice>
-</phrase>
-<phrase>
- id: LANG_COMPRESSOR
- desc: in sound settings
- user: core
- <source>
- *: "Compressor"
- </source>
- <dest>
- *: "壓縮"
- </dest>
- <voice>
- *: "Compressor"
- </voice>
-</phrase>
-<phrase>
- id: LANG_CROSSFEED_MEIER
- desc: in sound settings
- user: core
- <source>
- *: "Simple (Meier)"
- </source>
- <dest>
- *: "Simple (Meier)"
- </dest>
- <voice>
- *: "Simple"
- </voice>
-</phrase>
-<phrase>
- id: LANG_HW_EQ_FREQUENCY
- desc: in sound_menu, hardware equalizer tone controls shelf filter cutoff frequency
- user: core
- <source>
- *: none
- gigabeats,samsungypr1: "Band %d Frequency"
- </source>
- <dest>
- *: none
- gigabeats,samsungypr1: "Band %d 頻率"
- </dest>
- <voice>
- *: none
- gigabeats,samsungypr1: "Band Frequency"
- </voice>
-</phrase>
-<phrase>
- id: VOICE_EXT_RFMS
- desc: spoken only, for file extension
- user: core
- <source>
- *: none
- radio_remote: ""
- </source>
- <dest>
- *: none
- radio_remote: ""
- </dest>
- <voice>
- *: none
- radio_remote: "remote radio screen skin"
- </voice>
-</phrase>
-<phrase>
- id: VOICE_EXT_RSBS
- desc: spoken only, for file extension
+ id: LANG_LIST_SEPARATOR
+ desc: line between lines in lists
user: core
<source>
- *: none
- remote: ""
+ *: "Line Separator"
</source>
<dest>
- *: none
- remote: ""
+ *: "行分隔線"
</dest>
<voice>
- *: none
- remote: "remote statusbar skin"
+ *: "行分隔線"
</voice>
</phrase>
<phrase>
- id: LANG_COMPRESSOR_RATIO_10
- desc: in sound settings
+ id: LANG_LIST_SEPARATOR_COLOR
+ desc: line between lines in lists
user: core
<source>
- *: "10:1"
+ *: "Line Separator Colour"
</source>
<dest>
- *: "10:1"
+ *: "行分隔線é¡è‰²"
</dest>
<voice>
- *: "10 to 1"
+ *: "Color of Line Separator"
</voice>
</phrase>
<phrase>
- id: LANG_SCROLLBAR_POSITION
+ id: LANG_SCROLLBAR_WIDTH
desc: in Settings -> General -> Display -> Status-/Scrollbar
user: core
<source>
- *: "Scroll Bar Position"
- </source>
- <dest>
- *: "滾動æ¢ä½ç½®"
- </dest>
- <voice>
- *: "Scroll bar position"
- </voice>
-</phrase>
-<phrase>
- id: LANG_COMPRESSOR_KNEE
- desc: in sound settings
- user: core
- <source>
- *: "Knee"
- </source>
- <dest>
- *: "Knee"
- </dest>
- <voice>
- *: "Knee"
- </voice>
-</phrase>
-<phrase>
- id: LANG_STATUSBAR_TOP
- desc: in Settings -> General -> Display -> statusbar
- user: core
- <source>
- *: "Top"
- </source>
- <dest>
- *: "頂部"
- </dest>
- <voice>
- *: "Top"
- </voice>
-</phrase>
-<phrase>
- id: LANG_COMPRESSOR_RATIO_4
- desc: in sound settings
- user: core
- <source>
- *: "4:1"
- </source>
- <dest>
- *: "4:1"
- </dest>
- <voice>
- *: "4 to 1"
- </voice>
-</phrase>
-<phrase>
- id: LANG_KBD_DELETE
- desc: in keyboard
- user: core
- <source>
- *: none
- touchscreen: "Del"
- </source>
- <dest>
- *: none
- touchscreen: "刪除"
- </dest>
- <voice>
- *: none
- touchscreen: "Del"
- </voice>
-</phrase>
-<phrase>
- id: LANG_COMPRESSOR_SOFT_KNEE
- desc: in sound settings
- user: core
- <source>
- *: "Soft Knee"
- </source>
- <dest>
- *: "軟æ‹é»ž"
- </dest>
- <voice>
- *: "Soft Knee"
- </voice>
-</phrase>
-<phrase>
- id: LANG_FM_OTHER
- desc: Catch-all FM region. Select if none of the others work
- user: core
- <source>
- *: none
- radio: "Other"
- </source>
- <dest>
- *: none
- radio: "其他"
- </dest>
- <voice>
- *: none
- radio: "Other"
- </voice>
-</phrase>
-<phrase>
- id: LANG_HW_EQ_TONE_CONTROLS_ADVANCED
- desc: in sound_menu, advanced settings for hardware equalizer tone controls
- user: core
- <source>
- *: none
- gigabeats,samsungypr1: "Advanced Tone Control Settings"
- </source>
- <dest>
- *: none
- gigabeats,samsungypr1: "進階音調控制設置"
- </dest>
- <voice>
- *: none
- gigabeats,samsungypr1: "Advanced Tone Control Settings"
- </voice>
-</phrase>
-<phrase>
- id: LANG_CODEPAGE_WESTERN_EUROPEAN
- desc: in codepage setting menu
- user: core
- <source>
- *: "Western European (CP1252)"
- </source>
- <dest>
- *: "è¥¿æ­ (CP1252)"
- </dest>
- <voice>
- *: "Western European"
- </voice>
-</phrase>
-<phrase>
- id: LANG_COMPRESSOR_GAIN
- desc: in sound settings
- user: core
- <source>
- *: "Makeup Gain"
- </source>
- <dest>
- *: "Makeup Gain"
- </dest>
- <voice>
- *: "Makeup Gain"
- </voice>
-</phrase>
-<phrase>
- id: LANG_HW_EQ_WIDTH_NARROW
- desc: in sound_menu, hardware equalizer tone controls narrow bandwith setting
- user: core
- <source>
- *: none
- gigabeats,samsungypr1: "Narrow"
- </source>
- <dest>
- *: none
- gigabeats,samsungypr1: "Narrow"
- </dest>
- <voice>
- *: none
- gigabeats,samsungypr1: "Narrow"
- </voice>
-</phrase>
-<phrase>
- id: LANG_SAVE_CHANGES
- desc: When you try to exit screens to confirm save
- user: core
- <source>
- *: "Save Changes?"
+ *: "Scroll Bar Width"
</source>
<dest>
- *: "儲存變更?"
+ *: "æ²å‹•è»¸å¯¬åº¦"
</dest>
<voice>
- *: "Save Changes?"
+ *: "æ²å‹•è»¸å¯¬åº¦"
</voice>
</phrase>
<phrase>
- id: LANG_MAIN_SCREEN
- desc: in the main menu
+ id: LANG_CONSTRAIN_NEXT_FOLDER
+ desc: in settings_menu. Whether LANG_NEXT_FOLDER should be constrained to directories within LANG_SET_AS_START_DIR
user: core
<source>
- *: none
- remote: "Main Screen"
+ *: "Constrain Auto-Change"
</source>
<dest>
- *: none
- remote: "主畫é¢"
+ *: "é™åˆ¶è‡ªå‹•è·³è½‰"
</dest>
<voice>
- *: none
- remote: "Main Screen"
+ *: "é™åˆ¶è‡ªå‹•è·³è½‰"
</voice>
</phrase>
<phrase>
- id: LANG_HISTOGRAM_INTERVAL
- desc: in record settings menu
+ id: LANG_HOTKEY
+ desc: hotkey menu
user: core
<source>
*: none
- histogram: "Histogram interval"
+ hotkey: "Hotkey"
</source>
<dest>
*: none
- histogram: "Histogram interval"
+ hotkey: "熱éµ"
</dest>
<voice>
*: none
- histogram: "Histogram interval"
+ hotkey: "熱éµ"
</voice>
</phrase>
<phrase>
- id: LANG_STATUSBAR_CUSTOM
- desc: if this translation is compatible with LANG_CHANNEL_CUSTOM, then please use the same translation. it can be combined later then
- user: core
- <source>
- *: "Custom"
- </source>
- <dest>
- *: "自訂"
- </dest>
- <voice>
- *: "Custom"
- </voice>
-</phrase>
-<phrase>
- id: LANG_KBD_CANCEL
- desc: in keyboard
+ id: LANG_HOTKEY_FILE_BROWSER
+ desc: hotkey menu
user: core
<source>
*: none
- touchscreen: "Cancel"
+ hotkey: "File Browser Hotkey"
</source>
<dest>
*: none
- touchscreen: "å–消"
+ hotkey: "檔案ç€è¦½å™¨çš„熱éµ"
</dest>
<voice>
*: none
- touchscreen: "Cancel"
+ hotkey: "檔案ç€è¦½å™¨çš„熱éµ"
</voice>
</phrase>
<phrase>
- id: VOICE_EXT_FMS
- desc: spoken only, for file extension
+ id: LANG_HOTKEY_WPS
+ desc: hotkey menu
user: core
<source>
*: none
- radio: ""
+ hotkey: "WPS Hotkey"
</source>
<dest>
*: none
- radio: ""
+ hotkey: "音樂播放介é¢ç†±éµ"
</dest>
<voice>
*: none
- radio: "radio screen skin"
+ hotkey: "音樂播放介é¢ç†±éµ"
</voice>
</phrase>
<phrase>
- id: LANG_COMPRESSOR_RATIO_2
- desc: in sound settings
+ id: LANG_SET_AS_START_DIR
+ desc: used in the onplay menu to set a starting browser dir
user: core
<source>
- *: "2:1"
+ *: "Start File Browser Here"
</source>
<dest>
- *: "2:1"
+ *: "設爲起始檔案ç€è¦½ä½ç½®"
</dest>
<voice>
- *: "2 to 1"
+ *: "設爲起始檔案ç€è¦½ä½ç½®"
</voice>
</phrase>
<phrase>
@@ -12077,877 +10753,489 @@
*: "Current Playlist"
</source>
<dest>
- *: "當å‰æ’­æ”¾åˆ—表"
+ *: "當å‰æ’­æ”¾æ¸…å–®"
</dest>
<voice>
- *: "Current Playlist"
+ *: "當å‰æ’­æ”¾æ¸…å–®"
</voice>
</phrase>
<phrase>
- id: LANG_TAGNAVI_UNTAGGED
- desc: "<untagged>" entry in tag browser
- user: core
- <source>
- *: "<Untagged>"
- </source>
- <dest>
- *: "<Untagged>"
- </dest>
- <voice>
- *: "Untagged"
- </voice>
-</phrase>
-<phrase>
- id: LANG_BROWSER_MODE
- desc: in settings_menu
- user: core
- <source>
- *: none
- usb_hid: "Browser"
- </source>
- <dest>
- *: none
- usb_hid: "ç€è¦½"
- </dest>
- <voice>
- *: none
- usb_hid: "Browser"
- </voice>
-</phrase>
-<phrase>
- id: LANG_COMPRESSOR_HARD_KNEE
- desc: in sound settings
- user: core
- <source>
- *: "Hard Knee"
- </source>
- <dest>
- *: "硬æ‹é»ž"
- </dest>
- <voice>
- *: "Hard Knee"
- </voice>
-</phrase>
-<phrase>
- id: LANG_REMOTE_RADIOSCREEN
- desc: in the theme menu
- user: core
- <source>
- *: none
- radio_remote: "Remote Radio Screen"
- </source>
- <dest>
- *: none
- radio_remote: "Remote Radio Screen"
- </dest>
- <voice>
- *: none
- radio_remote: "Remote Radio Screen"
- </voice>
-</phrase>
-<phrase>
- id: VOICE_EXT_SBS
- desc: spoken only, for file extension
+ id: LANG_INSERT_LAST_SHUFFLED
+ desc: in onplay menu. insert a playlist randomly at end of dynamic playlist
user: core
<source>
- *: ""
+ *: "Insert Last Shuffled"
</source>
<dest>
- *: ""
+ *: "隨機æ’入到播放清單最後ä½ç½®"
</dest>
<voice>
- *: "statusbar skin"
+ *: "隨機æ’入到播放清單最後ä½ç½®"
</voice>
</phrase>
<phrase>
- id: LANG_USB_HID
- desc: in settings_menu
+ id: LANG_QUEUE_LAST_SHUFFLED
+ desc: in onplay menu. queue a playlist randomly at end of dynamic playlist
user: core
<source>
- *: none
- usb_hid: "USB HID"
+ *: "Queue Last Shuffled"
</source>
<dest>
- *: none
- usb_hid: "USB人性化介é¢"
+ *: "清單播放完畢後é‡æ–°éš¨æ©ŸéšŠåˆ—"
</dest>
<voice>
- *: none
- usb_hid: "USB Human Interface Device"
+ *: "清單播放完畢後é‡æ–°éš¨æ©ŸéšŠåˆ—"
</voice>
</phrase>
<phrase>
- id: LANG_TOUCHSCREEN_CALIBRATE
- desc: in Settings -> General -> Display -> Touchscreen Settings
+ id: LANG_SET_AS_PLAYLISTCAT_DIR
+ desc: used in the onplay menu to set a playlist catalogue dir
user: core
<source>
- *: none
- touchscreen: "Calibrate"
+ *: "Set As Playlist Catalogue Directory"
</source>
<dest>
- *: none
- touchscreen: "校準"
+ *: "設定為播放清單目錄ä½ç½®"
</dest>
<voice>
- *: none
- touchscreen: "Calibrate"
+ *: "設定為播放清單目錄ä½ç½®"
</voice>
</phrase>
<phrase>
- id: LANG_PLAYBACK_RATE
- desc: "rate" in pitch screen
+ id: LANG_VOLUME_LIMIT
+ desc: in sound_settings
user: core
<source>
- *: none
- pitchscreen: "Rate"
+ *: "Maximum Volume Limit"
</source>
<dest>
- *: none
- pitchscreen: "速率"
+ *: "音é‡é™åˆ¶"
</dest>
<voice>
- *: none
- pitchscreen: "Rate"
+ *: "音é‡é™åˆ¶"
</voice>
</phrase>
<phrase>
- id: LANG_MOUSE_MODE
- desc: in settings_menu
+ id: LANG_STARTUP_SHUTDOWN
+ desc: in the general settings menu
user: core
<source>
- *: none
- usb_hid: "Mouse"
+ *: "Startup/Shutdown"
</source>
<dest>
- *: none
- usb_hid: "滑鼠"
+ *: "開機/關機"
</dest>
<voice>
- *: none
- usb_hid: "Mouse"
+ *: "開機和關機"
</voice>
</phrase>
<phrase>
- id: LANG_COMPRESSOR_ATTACK
- desc: in sound settings
+ id: LANG_SLEEP_TIMER_START_CURRENT
+ desc: shown when a sleep timer isn't running
user: core
<source>
- *: "Attack Time"
+ *: "Start Sleep Timer"
</source>
<dest>
- *: "Attack Time"
+ *: "å•“å‹•ç¡çœ è¨ˆæ™‚器"
</dest>
<voice>
- *: "Attack Time"
+ *: "å•“å‹•ç¡çœ è¨ˆæ™‚器"
</voice>
</phrase>
<phrase>
- id: LANG_MORSE_INPUT
- desc: in Settings -> System
+ id: LANG_SLEEP_TIMER_CANCEL_CURRENT
+ desc: shown instead of sleep timer when it's running
user: core
<source>
- *: none
- morse_input: "Use Morse Code Input"
+ *: "Cancel Sleep Timer"
</source>
<dest>
- *: none
- morse_input: "Use Morse Code Input"
+ *: "åœæ­¢ç¡çœ è¨ˆæ™‚器"
</dest>
<voice>
- *: none
- morse_input: "Use Morse Code Input"
+ *: "åœæ­¢ç¡çœ è¨ˆæ™‚器"
</voice>
</phrase>
<phrase>
- id: LANG_FM_RSSI
- desc: Signal strength of a received FM station
+ id: LANG_SLEEP_TIMER_DURATION
+ desc: default sleep timer duration in minutes
user: core
<source>
- *: none
- radio: "Signal strength:"
+ *: "Default Sleep Timer Duration"
</source>
<dest>
- *: none
- radio: "信號強度:"
+ *: "ç¡çœ å€’計時長度"
</dest>
<voice>
- *: none
- radio: "Signal strength:"
+ *: "ç¡çœ å€’計時長度"
</voice>
</phrase>
<phrase>
- id: LANG_COMPRESSOR_RELEASE
- desc: in sound settings
+ id: LANG_SLEEP_TIMER_ON_POWER_UP
+ desc: whether sleep timer starts on power up
user: core
<source>
- *: "Release Time"
+ *: "Start Sleep Timer On Boot"
</source>
<dest>
- *: "釋放時間"
+ *: "é–‹æ©Ÿå³å•“å‹•ç¡çœ è¨ˆæ™‚"
</dest>
<voice>
- *: "Release Time"
+ *: "開機後立å³å•“å‹•ç¡çœ è¨ˆæ™‚"
</voice>
</phrase>
<phrase>
- id: LANG_FM_ITALY
- desc: fm region Italy
+ id: LANG_KEYPRESS_RESTARTS_SLEEP_TIMER
+ desc: whether to restart running sleep timer on keypress
user: core
<source>
- *: none
- radio: "Italy"
+ *: "Restart Sleep Timer On Keypress"
</source>
<dest>
- *: none
- radio: "義大利"
+ *: "åµæ¸¬åˆ°æŒ‰éµæ™‚é‡ç½®ç¡çœ è¨ˆæ™‚器"
</dest>
<voice>
- *: none
- radio: "Italy"
+ *: "åµæ¸¬åˆ°æŒ‰éµæ™‚é‡ç½®ç¡çœ è¨ˆæ™‚器"
</voice>
</phrase>
<phrase>
- id: LANG_FORCE
- desc: alternative to yes/no for tristate settings
+ id: LANG_AUTORESUME
+ desc: resume settings menu
user: core
<source>
- *: "Force"
+ *: "Automatic resume"
</source>
<dest>
- *: "Force"
+ *: "單檔案斷點續撥"
</dest>
<voice>
- *: "Force"
+ *: "爲æ¯å€‹æª”案ä¿å­˜æ–·é»žçºŒæ’¥"
</voice>
</phrase>
<phrase>
- id: LANG_HW_EQ_TONE_CONTROLS
- desc: in sound_menu, hardware equalizer tone controls
+ id: LANG_AUTORESUME_AUTOMATIC
+ desc: resume on automatic track change
user: core
<source>
- *: none
- gigabeats,samsungypr1: "Tone Controls"
+ *: "Resume on automatic track change"
</source>
<dest>
- *: none
- gigabeats,samsungypr1: "音調控制"
+ *: "曲目自動跳轉時斷點續撥"
</dest>
<voice>
- *: none
- gigabeats,samsungypr1: "Tone Controls"
+ *: "曲目自動跳轉時斷點續撥"
</voice>
</phrase>
<phrase>
- id: LANG_AUTOTRACKSKIP
- desc: in crossfade settings
+ id: LANG_AUTORESUME_CUSTOM
+ desc: enable customization of resume on automatic track change
user: core
<source>
- *: none
- crossfade: "Automatic Track Change Only"
+ *: "In custom directories only"
</source>
<dest>
- *: none
- crossfade: "Automatic Track Change Only"
+ *: "僅é‡å°è‡ªå®šè³‡æ–™å¤¾ä¸­çš„檔案"
</dest>
<voice>
- *: none
- crossfade: "Automatic Track Change Only"
+ *: "僅é‡å°è‡ªå®šè³‡æ–™å¤¾ä¸­çš„檔案"
</voice>
</phrase>
<phrase>
- id: LANG_USB_KEYPAD_MODE
- desc: in settings_menu
+ id: LANG_SET_AS_START_DIR
+ desc: used in the onplay menu to set a starting browser dir
user: core
<source>
- *: none
- usb_hid: "USB Keypad Mode"
+ *: "Start File Browser Here"
</source>
<dest>
- *: none
- usb_hid: "USB Keypad Mode"
+ *: "設爲起始檔案ç€è¦½ä½ç½®"
</dest>
<voice>
- *: none
- usb_hid: "USB Keypad Mode"
+ *: "設爲起始檔案ç€è¦½ä½ç½®"
</voice>
</phrase>
<phrase>
- id: LANG_SELECT_FOLDER
- desc: in settings_menu
+ id: LANG_RESET_START_DIR
+ desc: reset the browser start directory
user: core
<source>
- *: "Select one or more directories"
+ *: "Start File Browser at /"
</source>
<dest>
- *: "é¸æ“‡ä¸€å€‹æˆ–多個目錄"
+ *: "復原檔案ç€è¦½èµ·å§‹ä½ç½®"
</dest>
<voice>
- *: "Select one or more directories"
+ *: "復原檔案ç€è¦½èµ·å§‹ä½ç½®"
</voice>
</phrase>
<phrase>
- id: LANG_USB_SKIP_FIRST_DRIVE
+ id: LANG_SELECT_DATABASE_DIRS
desc: in settings_menu
user: core
<source>
- *: none
- multidrive_usb: "USB Hide Internal Drive"
- </source>
- <dest>
- *: none
- multidrive_usb: "USB Hide Internal Drive"
- </dest>
- <voice>
- *: none
- multidrive_usb: "USB Hide Internal Drive"
- </voice>
-</phrase>
-<phrase>
- id: LANG_HW_EQ_WIDTH
- desc: in sound_menu, hardware equalizer tone controls peak bandwith setting
- user: core
- <source>
- *: none
- gigabeats,samsungypr1: "Band %d Width"
- </source>
- <dest>
- *: none
- gigabeats,samsungypr1: "Band %d 寬"
- </dest>
- <voice>
- *: none
- gigabeats,samsungypr1: "Band Width"
- </voice>
-</phrase>
-<phrase>
- id: LANG_CONSTRAIN_NEXT_FOLDER
- desc: in settings_menu. Whether LANG_NEXT_FOLDER should be constrained to directories within LANG_SET_AS_START_DIR
- user: core
- <source>
- *: "Constrain Auto-Change"
- </source>
- <dest>
- *: "Constrain Auto-Change"
- </dest>
- <voice>
- *: "Constrain Auto-Change"
- </voice>
-</phrase>
-<phrase>
- id: LANG_HW_EQ_GAIN
- desc: in sound_menu, hardware equalizer tone controls filter gain
- user: core
- <source>
- *: none
- gigabeats,samsungypr1: "Band %d Gain"
- </source>
- <dest>
- *: none
- gigabeats,samsungypr1: "Band %d 增益"
- </dest>
- <voice>
- *: none
- gigabeats,samsungypr1: "Band Gain"
- </voice>
-</phrase>
-<phrase>
- id: LANG_SPEED
- desc: timestretch speed
- user: core
- <source>
- *: "Speed"
- </source>
- <dest>
- *: "速度"
- </dest>
- <voice>
- *: ""
- </voice>
-</phrase>
-<phrase>
- id: LANG_TIMESTRETCH
- desc: timestretch enable
- user: core
- <source>
- *: "Timestretch"
+ *: "Select directories to scan"
</source>
<dest>
- *: "Timestretch"
+ *: "自定è¦æŽƒæ的資料夾"
</dest>
<voice>
- *: "Timestretch"
+ *: "自定資料庫è¦é€²è¡Œåª’體掃æ的資料夾"
</voice>
</phrase>
<phrase>
- id: LANG_STRETCH_LIMIT
- desc: "limit" in pitch screen
+ id: LANG_USE_SHORTCUTS_INSTEAD_OF_QS
+ desc: in settings_menu.
user: core
<source>
*: none
- pitchscreen: "Limit"
+ quickscreen: "Use Shortcuts Menu Instead of Quick Screen"
</source>
<dest>
*: none
- pitchscreen: "Limit"
+ quickscreen: "使用快æ·é¸å–®æ›¿ä»£å¿«æ·ç†’幕"
</dest>
<voice>
*: none
- pitchscreen: "Limit"
+ quickscreen: "使用快æ·é¸å–®æ›¿ä»£å¿«æ·ç†’幕"
</voice>
</phrase>
<phrase>
- id: LANG_TOUCHSCREEN_RESET_CALIBRATION
- desc: in Settings -> General -> Display -> Touchscreen Settings
+ id: LANG_RADIOSCREEN
+ desc: in the theme menu
user: core
<source>
*: none
- touchscreen: "Reset Calibration"
+ radio: "Radio Screen"
</source>
<dest>
*: none
- touchscreen: "復ä½æ ¡æº–"
+ radio: "收音機接å£ä¸»é¡Œ"
</dest>
<voice>
*: none
- touchscreen: "Reset Calibration"
- </voice>
-</phrase>
-<phrase>
- id: LANG_SHORTCUTS
- desc: Title in the shortcuts menu
- user: core
- <source>
- *: "Shortcuts"
- </source>
- <dest>
- *: "å¿«æ·"
- </dest>
- <voice>
- *: "Shortcuts"
- </voice>
-</phrase>
-<phrase>
- id: LANG_ONPLAY_PICTUREFLOW
- desc: Onplay pictureflow
- user: core
- <source>
- *: "PictureFlow"
- </source>
- <dest>
- *: "PictureFlow"
- </dest>
- <voice>
- *: "open picture flow"
- </voice>
-</phrase>
-<phrase>
- id: LANG_RIGHT
- desc: Generic use of 'right'
- user: core
- <source>
- *: "Right"
- </source>
- <dest>
- *: "right"
- </dest>
- <voice>
- *: "Right"
+ radio: "收音機接å£ä¸»é¡Œ"
</voice>
</phrase>
<phrase>
- id: LANG_HOTKEY_FILE_BROWSER
- desc: hotkey menu
+ id: LANG_BASE_SKIN
+ desc: browse for the base skin in theme settings
user: core
<source>
- *: none
- hotkey: "File Browser Hotkey"
+ *: "Base Skin"
</source>
<dest>
- *: none
- hotkey: "檔案ç€è¦½å™¨å¿«æ·éµ"
+ *: "狀態列佈景主題"
</dest>
<voice>
- *: none
- hotkey: "File Browser Hotkey"
+ *: "狀態列佈景主題"
</voice>
</phrase>
<phrase>
- id: LANG_ID3_COMPOSER
- desc: in tag viewer
+ id: LANG_SCROLLBAR_WIDTH
+ desc: in Settings -> General -> Display -> Status-/Scrollbar
user: core
<source>
- *: "Composer"
+ *: "Scroll Bar Width"
</source>
<dest>
- *: "作曲家"
+ *: "æ²å‹•è»¸å¯¬åº¦"
</dest>
<voice>
- *: ""
+ *: "æ²å‹•è»¸å¯¬åº¦"
</voice>
</phrase>
<phrase>
- id: LANG_SYSFONT_GAIN
- desc: in the equalizer settings menu
+ id: LANG_PAUSE_REWIND
+ desc: Seconds to rewind when rewind on pause is enabled.
user: core
<source>
- *: "Gain"
+ *: "Rewind on Pause"
</source>
<dest>
- *: "增益"
+ *: "æš«åœæ™‚自動倒退"
</dest>
<voice>
- *: "Gain"
+ *: "æš«åœæ™‚自動倒退"
</voice>
</phrase>
<phrase>
- id: LANG_SYSFONT_EQUALIZER_EDIT_MODE
- desc: in the equalizer settings menu
+ id: LANG_RESUME_REWIND
+ desc: in playback settings menu
user: core
<source>
- *: "Edit mode: %s %s"
+ *: "Rewind Before Resume"
</source>
<dest>
- *: "編輯設定: %s %s"
+ *: "æš«åœæ™‚自動倒退長度"
</dest>
<voice>
- *: ""
+ *: "æš«åœæ™‚自動倒退長度"
</voice>
</phrase>
<phrase>
- id: LANG_SYSFONT_EQUALIZER_BAND_CUTOFF
- desc: in the equalizer settings menu
+ id: LANG_SURROUND
+ desc: in the sound settings menu
user: core
<source>
- *: "Cutoff"
+ *: "Haas Surround"
</source>
<dest>
- *: "Cutoff"
+ *: "哈斯環繞"
</dest>
<voice>
- *: "Cutoff Frequency"
+ *: "哈斯環繞音效"
</voice>
</phrase>
<phrase>
- id: LANG_MULTIMEDIA_MODE
- desc: in settings_menu
+ id: LANG_FILTER_ROLL_OFF
+ desc: in sound settings
user: core
<source>
*: none
- usb_hid: "Multimedia"
+ filter_roll_off: "DAC's filter roll-off"
</source>
<dest>
*: none
- usb_hid: "多媒體"
+ filter_roll_off: "DAC濾波器滾é™"
</dest>
<voice>
*: none
- usb_hid: "Multimedia"
+ filter_roll_off: "DAC濾波器滾é™"
</voice>
</phrase>
<phrase>
- id: LANG_CROSSFEED_CUSTOM
+ id: LANG_FILTER_SHARP
desc: in sound settings
user: core
<source>
- *: "Custom"
- </source>
- <dest>
- *: "自訂"
- </dest>
- <voice>
- *: "Custom"
- </voice>
-</phrase>
-<phrase>
- id: LANG_PRESENTATION_MODE
- desc: in settings_menu
- user: core
- <source>
*: none
- usb_hid: "Presentation"
+ filter_roll_off: "Sharp"
</source>
<dest>
*: none
- usb_hid: "介é¢"
+ filter_roll_off: "快速"
</dest>
<voice>
*: none
- usb_hid: "Presentation"
+ filter_roll_off: "快速滾é™"
</voice>
</phrase>
<phrase>
- id: LANG_KEYCLICK_SOFTWARE
- desc: in keyclick settings menu
+ id: LANG_FILTER_SLOW
+ desc: in sound settings
user: core
<source>
*: none
- hardware_click: "Headphone Keyclick"
+ filter_roll_off: "Slow"
</source>
<dest>
*: none
- hardware_click: "耳機線控"
+ filter_roll_off: "平緩"
</dest>
<voice>
*: none
- hardware_click: "Headphone Keyclick"
- </voice>
-</phrase>
-<phrase>
- id: LANG_SET_AS_PLAYLISTCAT_DIR
- desc: used in the onplay menu to set a playlist catalogue dir
- user: core
- <source>
- *: "Set As Playlist Catalogue Directory"
- </source>
- <dest>
- *: "分類目錄添加於播放列表"
- </dest>
- <voice>
- *: "Set As Playlist Catalogue Directory"
+ filter_roll_off: "平緩滾é™"
</voice>
</phrase>
<phrase>
- id: LANG_COMPRESSOR_RATIO_6
+ id: LANG_PBE
desc: in sound settings
user: core
<source>
- *: "6:1"
+ *: "Perceptual Bass Enhancement"
</source>
<dest>
- *: "6:1"
+ *: "感知性低音增強"
</dest>
<voice>
- *: "6 to 1"
+ *: "感知性低音增強音效"
</voice>
</phrase>
<phrase>
- id: LANG_COMPRESSOR_RATIO
+ id: LANG_AFR
desc: in sound settings
user: core
<source>
- *: "Ratio"
- </source>
- <dest>
- *: "比率"
- </dest>
- <voice>
- *: "Ratio"
- </voice>
-</phrase>
-<phrase>
- id: LANG_BOOKMARK_SETTINGS_AUTOUPDATE
- desc: prompt for user to decide whether to update bookmarks
- user: core
- <source>
- *: "Update on Stop"
- </source>
- <dest>
- *: "Update on Stop"
- </dest>
- <voice>
- *: "Update on Stop"
- </voice>
-</phrase>
-<phrase>
- id: LANG_HOTKEY
- desc: hotkey menu
- user: core
- <source>
- *: none
- hotkey: "Hotkey"
- </source>
- <dest>
- *: none
- hotkey: "å¿«æ·éµ"
- </dest>
- <voice>
- *: none
- hotkey: "Hotkey"
- </voice>
-</phrase>
-<phrase>
- id: LANG_RESET_PLAYLISTCAT_DIR
- desc:
- user: core
- <source>
- *: "Reset Playlist Catalogue Directory"
- </source>
- <dest>
- *: "é‡ç½®æ’­æ”¾åˆ—表目錄"
- </dest>
- <voice>
- *: "Reset Playlist Catalogue Directory"
- </voice>
-</phrase>
-<phrase>
- id: LANG_OF
- desc: Shown in WPS: X of Y (tracks)
- user: core
- <source>
- *: "of"
- </source>
- <dest>
- *: "of"
- </dest>
- <voice>
- *: "of"
- </voice>
-</phrase>
-<phrase>
- id: LANG_NEXT
- desc: Shown in WPS (short form of Next Track)
- user: core
- <source>
- *: "Next:"
- </source>
- <dest>
- *: "下一首:"
- </dest>
- <voice>
- *: "Next:"
- </voice>
-</phrase>
-<phrase>
- id: LANG_GLYPHS
- desc: in settings_menu
- user: core
- <source>
- *: "Glyphs To Cache"
- </source>
- <dest>
- *: "字形緩存"
- </dest>
- <voice>
- *: "Glyphs To Cache"
- </voice>
-</phrase>
-<phrase>
- id: LANG_REMOTE_STATUSBAR
- desc: in Settings -> General -> Display -> statusbar
- user: core
- <source>
- *: none
- remote: "Remote Statusbar"
- </source>
- <dest>
- *: none
- remote: "Remote Statusbar"
- </dest>
- <voice>
- *: none
- remote: "Remote Statusbar"
- </voice>
-</phrase>
-<phrase>
- id: LANG_LIST_SEPARATOR_COLOR
- desc: line between lines in lists
- user: core
- <source>
- *: "Line Separator Colour"
- </source>
- <dest>
- *: "Line Separator Colour"
- </dest>
- <voice>
- *: "Line Separator Colour"
- </voice>
-</phrase>
-<phrase>
- id: LANG_DEPTH_3D
- desc: in sound_menu, amount of 3D enhancement effect
- user: core
- <source>
- *: none
- depth_3d: "3-D Enhancement"
+ *: "Auditory Fatigue Reduction"
</source>
<dest>
- *: none
- depth_3d: "3D音效增強"
+ *: "è²å­¸æŠ—疲勞"
</dest>
<voice>
- *: none
- depth_3d: "3-D Enhancement"
+ *: "è²å­¸æŠ—疲勞音效"
</voice>
</phrase>
<phrase>
- id: LANG_AUTO
+ id: LANG_SURROUND_FX1
desc: in sound settings
user: core
<source>
- *: "Auto"
+ *: "f(x1)"
</source>
<dest>
- *: "自動"
+ *: "f(x1)"
</dest>
<voice>
- *: "Auto"
+ *: "f(x1)"
</voice>
</phrase>
<phrase>
- id: LANG_HOTKEY_WPS
- desc: hotkey menu
+ id: LANG_SURROUND_FX2
+ desc: in sound settings
user: core
<source>
- *: none
- hotkey: "WPS Hotkey"
+ *: "f(x2)"
</source>
<dest>
- *: none
- hotkey: "WPSå¿«æ·éµ"
+ *: "f(x2)"
</dest>
<voice>
- *: none
- hotkey: "WPS Hotkey"
+ *: "f(x2)"
</voice>
</phrase>
<phrase>
- id: LANG_KEYCLICK_HARDWARE
- desc: in keyclick settings menu
+ id: LANG_SURROUND_METHOD2
+ desc: in sound settings
user: core
<source>
- *: none
- hardware_click: "Speaker Keyclick"
+ *: "SIDE ONLY"
</source>
<dest>
- *: none
- hardware_click: "Speaker Keyclick"
+ *: "SIDE ONLY"
</dest>
<voice>
- *: none
- hardware_click: "Speaker Keyclick"
+ *: "SIDE ONLY"
</voice>
</phrase>
<phrase>
- id: LANG_LINEOUT_ONOFF
- desc: in system settings menu
+ id: LANG_SURROUND_MIX
+ desc: in sound settings
user: core
<source>
- *: none
- lineout_poweroff: "Line Out"
+ *: "Dry / Wet Mix"
</source>
<dest>
- *: none
- lineout_poweroff: "輸出"
+ *: "乾濕混åˆåº¦"
</dest>
<voice>
- *: none
- lineout_poweroff: "Line Out"
+ *: "乾濕混åˆåº¦"
</voice>
</phrase>
<phrase>
- id: LANG_TOUCHSCREEN_SETTINGS
- desc: in Settings -> General -> Display menu
+ id: LANG_IBASSO_FREQ_SCALING_GOVERNOR
+ desc: in Settings -> General -> System -> Freq Scaling Governor
user: core
<source>
*: none
- touchscreen: "Touchscreen Settings"
+ ibassodx50,ibassodx90: "Freq Scaling Governor"
</source>
<dest>
*: none
- touchscreen: "觸控設定"
+ ibassodx50,ibassodx90: "CPU頻率管ç†å™¨"
</dest>
<voice>
*: none
- touchscreen: "Touchscreen Settings"
+ ibassodx50,ibassodx90: "CPU頻率管ç†å™¨"
</voice>
</phrase>
diff --git a/apps/lang/czech.lang b/apps/lang/czech.lang
index c2f01db7d3..b2d5e6e8e2 100644
--- a/apps/lang/czech.lang
+++ b/apps/lang/czech.lang
@@ -252,7 +252,7 @@
*: "PLAY = Yes"
archosplayer: "(PLAY/STOP)"
cowond2*: "MENU, or top-right = Yes"
- creativezen*: "Select = Yes"
+ creativezen*: "SELECT = Yes"
gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT = Yes"
iriverh100,iriverh120,iriverh300: "NAVI = Yes"
mrobe500: "PLAY, POWER, or top-right = Yes"
@@ -262,7 +262,7 @@
*: "PLAY = Ano"
archosplayer: "(PLAY/STOP)"
cowond2*: "MENU, nebo vpravo nahoře = Ano"
- creativezen*: "Select = Ano"
+ creativezen*: "SELECT = Ano"
gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT = Ano"
iriverh100,iriverh120,iriverh300: "NAVI = Ano"
mrobe500: "PLAY, POWER, nebo vpravo nahoře = Ano"
@@ -5254,20 +5254,17 @@
</voice>
</phrase>
<phrase>
- id: LANG_RECORDING_FORMAT
- desc: audio format item in recording menu
+ id: LANG_FORMAT
+ desc: audio format
user: core
<source>
- *: none
- recording: "Format"
+ *: "Format"
</source>
<dest>
- *: none
- recording: "Formát"
+ *: "Formát"
</dest>
<voice>
- *: none
- recording: "Formát"
+ *: "Formát"
</voice>
</phrase>
<phrase>
@@ -10328,11 +10325,11 @@
user: core
<source>
*: none
- gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
</source>
<dest>
*: none
- gigabeatfx,sansafuzeplus: "Citlivost touchpadu"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Citlivost touchpadu"
</dest>
<voice>
*: none
@@ -12898,16 +12895,13 @@
desc: used in track x of y constructs
user: core
<source>
- *: none
- hotkey: "Track"
+ *: "Track"
</source>
<dest>
- *: none
- hotkey: "Skladba"
+ *: "Skladba"
</dest>
<voice>
- *: none
- hotkey: "Skladba"
+ *: "Skladba"
</voice>
</phrase>
<phrase>
diff --git a/apps/lang/dansk.lang b/apps/lang/dansk.lang
index 536ecb28ec..8a01d45efc 100644
--- a/apps/lang/dansk.lang
+++ b/apps/lang/dansk.lang
@@ -8326,20 +8326,17 @@
</voice>
</phrase>
<phrase>
- id: LANG_RECORDING_FORMAT
- desc: audio format item in recording menu
+ id: LANG_FORMAT
+ desc: audio format
user: core
<source>
- *: none
- recording: "Format"
+ *: "Format"
</source>
<dest>
- *: none
- recording: "Format"
+ *: "Format"
</dest>
<voice>
- *: none
- recording: "Format"
+ *: "Format"
</voice>
</phrase>
<phrase>
@@ -10232,15 +10229,15 @@
user: core
<source>
*: none
- gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
</source>
<dest>
*: none
- gigabeatfx,sansafuzeplus: "Touchpad følsomhed"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Touchpad følsomhed"
</dest>
<voice>
*: none
- gigabeatfx,sansafuzeplus: "Touchpad følsomhed"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Touchpad følsomhed"
</voice>
</phrase>
<phrase>
@@ -12652,3 +12649,17 @@
*: "Start auto-sluk ved opstart"
</voice>
</phrase>
+<phrase>
+ id: VOICE_NUMERIC_TENS_SWAP_SEPARATOR
+ desc: voice only, for speaking numbers in languages that swap the tens and ones fields. Leave blank for languages that do not need it, such as English ("231" => "two hundred thirty one") but other languages may speak it as "two hundred one [AND] thirty"
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: "og"
+ </voice>
+</phrase>
diff --git a/apps/lang/deutsch.lang b/apps/lang/deutsch.lang
index 131f342a5d..ef7ee7fe6a 100644
--- a/apps/lang/deutsch.lang
+++ b/apps/lang/deutsch.lang
@@ -269,7 +269,7 @@
*: "PLAY = Yes"
archosplayer: "(PLAY/STOP)"
cowond2*: "MENU, or top-right = Yes"
- creativezen*: "Select = Yes"
+ creativezen*: "SELECT = Yes"
gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT = Yes"
iriverh100,iriverh120,iriverh300: "NAVI = Yes"
mrobe500: "PLAY, POWER, or top-right = Yes"
@@ -279,7 +279,7 @@
*: "WIEDERGABE = Ja"
archosplayer: "(WIEDERGABE/STOP)"
cowond2*: "MENÜ oder rechts oben= Ja"
- creativezen*: "Select = Ja"
+ creativezen*: "SELECT = Ja"
gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT = Ja"
iriverh100,iriverh120,iriverh300: "NAVI = Ja"
mrobe500: "WIEDERGABE, POWER oder rechts oben = Ja"
@@ -5240,20 +5240,17 @@
</voice>
</phrase>
<phrase>
- id: LANG_RECORDING_FORMAT
- desc: audio format item in recording menu
+ id: LANG_FORMAT
+ desc: audio format
user: core
<source>
- *: none
- recording: "Format"
+ *: "Format"
</source>
<dest>
- *: none
- recording: "Format"
+ *: "Format"
</dest>
<voice>
- *: none
- recording: "Format"
+ *: "Format"
</voice>
</phrase>
<phrase>
@@ -10187,15 +10184,15 @@
user: core
<source>
*: none
- gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
</source>
<dest>
*: none
- gigabeatfx,sansafuzeplus: "Touchpad-Empfindlichkeit"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Touchpad-Empfindlichkeit"
</dest>
<voice>
*: none
- gigabeatfx,sansafuzeplus: "Touchpad-Empfindlichkeit"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Touchpad-Empfindlichkeit"
</voice>
</phrase>
<phrase>
diff --git a/apps/lang/english-us.lang b/apps/lang/english-us.lang
index 9439236b9c..5a3eb38ba3 100644
--- a/apps/lang/english-us.lang
+++ b/apps/lang/english-us.lang
@@ -246,8 +246,7 @@
<source>
*: "PLAY = Yes"
cowond2*: "MENU, or top-right = Yes"
- creativezen*: "Select = Yes"
- gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT = Yes"
+ creativezen*,gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT = Yes"
iriverh100,iriverh120,iriverh300: "NAVI = Yes"
mrobe500: "PLAY, POWER, or top-right = Yes"
vibe500: "OK = Yes"
@@ -255,8 +254,7 @@
<dest>
*: "PLAY = Yes"
cowond2*: "MENU, or top-right = Yes"
- creativezen*: "Select = Yes"
- gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT = Yes"
+ creativezen*,gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT = Yes"
iriverh100,iriverh120,iriverh300: "NAVI = Yes"
mrobe500: "PLAY, POWER, or top-right = Yes"
vibe500: "OK = Yes"
@@ -1300,6 +1298,20 @@
</voice>
</phrase>
<phrase>
+ id: LANG_SINGLE_MODE
+ desc: single mode
+ user: core
+ <source>
+ *: "Single Mode"
+ </source>
+ <dest>
+ *: "Single Mode"
+ </dest>
+ <voice>
+ *: "Single Mode"
+ </voice>
+</phrase>
+<phrase>
id: LANG_PARTY_MODE
desc: party mode
user: core
@@ -1684,10 +1696,10 @@
</phrase>
<phrase>
id: LANG_AUDIOSCROBBLER
- desc: "Last.fm Log" in the playback menu
+ desc: "Last.fm Logger" in Plugin/apps/scrobbler
user: core
<source>
- *: "Last.fm Log"
+ *: "Last.fm Logger"
</source>
<dest>
*: "Last.fm Log"
@@ -2254,20 +2266,25 @@
charging: "Backlight (While Plugged In)"
</voice>
</phrase>
+### The <source> section for 'LANG_BACKLIGHT_ON_BUTTON_HOLD:*' differs from the english!
+### the previously used one is commented below:
+### Backlight on Lock
+### The <dest> section for 'LANG_BACKLIGHT_ON_BUTTON_HOLD:*' is missing! Copying from english!
+### The <voice> section for 'LANG_BACKLIGHT_ON_BUTTON_HOLD:*' is missing! Copying from english!
<phrase>
id: LANG_BACKLIGHT_ON_BUTTON_HOLD
desc: in lcd settings
user: core
<source>
- *: none
+ *: "Backlight on Lock"
hold_button: "Backlight on Hold"
</source>
<dest>
- *: none
+ *: "Backlight on Lock"
hold_button: "Backlight on Hold"
</dest>
<voice>
- *: none
+ *: "Backlight on Lock"
hold_button: "Backlight on hold"
</voice>
</phrase>
@@ -4095,31 +4112,16 @@
</phrase>
<phrase>
id: LANG_ALARM_MOD_KEYS
- desc: Shown key functions in alarm menu (for the RTC alarm mod).
+ desc: deprecated
user: core
<source>
- *: none
- alarm: "PLAY=Set OFF=Cancel"
- gigabeats: "SELECT=Set POWER=Cancel"
- ipod*: "SELECT=Set MENU=Cancel"
- iriverh10,iriverh10_5gb: "SELECT=Set PREV=Cancel"
- mpiohd300: "ENTER=Set MENU=Cancel"
- sansafuzeplus: "SELECT=Set BACK=Cancel"
- vibe500: "OK=Set C=Cancel"
+ *: ""
</source>
<dest>
- *: none
- alarm: "PLAY=Set OFF=Cancel"
- gigabeats: "SELECT=Set POWER=Cancel"
- ipod*: "SELECT=Set MENU=Cancel"
- iriverh10,iriverh10_5gb: "SELECT=Set PREV=Cancel"
- mpiohd300: "ENTER=Set MENU=Cancel"
- sansafuzeplus: "SELECT=Set BACK=Cancel"
- vibe500: "OK=Set C=Cancel"
+ *: ""
</dest>
<voice>
- *: none
- alarm,ipod*: ""
+ *: ""
</voice>
</phrase>
<phrase>
@@ -4946,20 +4948,17 @@
</voice>
</phrase>
<phrase>
- id: LANG_RECORDING_FORMAT
- desc: audio format item in recording menu
+ id: LANG_FORMAT
+ desc: audio format
user: core
<source>
- *: none
- recording: "Format"
+ *: "Format"
</source>
<dest>
- *: none
- recording: "Format"
+ *: "Format"
</dest>
<voice>
- *: none
- recording: "Format"
+ *: "Format"
</voice>
</phrase>
<phrase>
@@ -5341,19 +5340,19 @@
</phrase>
<phrase>
id: LANG_SET_AS_REC_DIR
- desc: used in the onplay menu to set a recording dir
+ desc: deprecated
user: core
<source>
*: none
- recording: "Set As Recording Directory"
+ recording: ""
</source>
<dest>
*: none
- recording: "Set As Recording Directory"
+ recording: ""
</dest>
<voice>
*: none
- recording: "Set As Recording Directory"
+ recording: ""
</voice>
</phrase>
<phrase>
@@ -6055,18 +6054,21 @@
user: core
<source>
*: none
- multivolume: "HD1"
+ hibylinux: "USB:"
+ multivolume: "HD1:"
sansac200*,sansaclipplus,sansae200*,sansafuze*: "mSD:"
xduoox3: "mSD2:"
</source>
<dest>
*: none
+ hibylinux: "USB:"
multivolume: "HD1"
sansac200*,sansaclipplus,sansae200*,sansafuze*: "mSD:"
xduoox3: "mSD2:"
</dest>
<voice>
*: none
+ hibylinux: "U S B"
multivolume: "H D 1"
sansac200*,sansaclipplus,sansae200*,sansafuze*: "micro S D"
xduoox3: "micro S D 2"
@@ -6158,58 +6160,58 @@
</phrase>
<phrase>
id: LANG_INSERT
- desc: in onplay menu. insert a track/playlist into dynamic playlist.
+ desc: deprecated
user: core
<source>
- *: "Insert"
+ *: ""
</source>
<dest>
- *: "Insert"
+ *: ""
</dest>
<voice>
- *: "Insert"
+ *: ""
</voice>
</phrase>
<phrase>
id: LANG_INSERT_FIRST
- desc: in onplay menu. insert a track/playlist into dynamic playlist.
+ desc: deprecated
user: core
<source>
- *: "Insert Next"
+ *: ""
</source>
<dest>
- *: "Insert Next"
+ *: ""
</dest>
<voice>
- *: "Insert Next"
+ *: ""
</voice>
</phrase>
<phrase>
id: LANG_INSERT_LAST
- desc: in onplay menu. append a track/playlist into dynamic playlist.
+ desc: deprecated
user: core
<source>
- *: "Insert Last"
+ *: ""
</source>
<dest>
- *: "Insert Last"
+ *: ""
</dest>
<voice>
- *: "Insert Last"
+ *: ""
</voice>
</phrase>
<phrase>
id: LANG_INSERT_SHUFFLED
- desc: in onplay menu. insert a track/playlist randomly into dynamic playlist
+ desc: deprecated
user: core
<source>
- *: "Insert Shuffled"
+ *: ""
</source>
<dest>
- *: "Insert Shuffled"
+ *: ""
</dest>
<voice>
- *: "Insert Shuffled"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -6382,16 +6384,16 @@
</phrase>
<phrase>
id: LANG_CATALOG_ADD_TO
- desc: in onplay playlist catalogue submenu
+ desc: deprecated
user: core
<source>
- *: "Add to Playlist"
+ *: ""
</source>
<dest>
- *: "Add to Playlist"
+ *: ""
</dest>
<voice>
- *: "Add to Playlist"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -10882,16 +10884,16 @@
</phrase>
<phrase>
id: LANG_INSERT_LAST_SHUFFLED
- desc: in onplay menu. insert a playlist randomly at end of dynamic playlist
+ desc: deprecated
user: core
<source>
- *: "Insert Last Shuffled"
+ *: ""
</source>
<dest>
- *: "Insert Last Shuffled"
+ *: ""
</dest>
<voice>
- *: "Insert Last Shuffled"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -11489,16 +11491,16 @@
</phrase>
<phrase>
id: LANG_SET_AS_START_DIR
- desc: used in the onplay menu to set a starting browser dir
+ desc: deprecated
user: core
<source>
- *: "Start File Browser Here"
+ *: ""
</source>
<dest>
- *: "Start File Browser Here"
+ *: ""
</dest>
<voice>
- *: "Start File Browser Here"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -11618,16 +11620,16 @@
</phrase>
<phrase>
id: LANG_SET_AS_PLAYLISTCAT_DIR
- desc: used in the onplay menu to set a playlist catalogue dir
+ desc: deprecated
user: core
<source>
- *: "Set As Playlist Catalogue Directory"
+ *: ""
</source>
<dest>
- *: "Set As Playlist Catalog Directory"
+ *: ""
</dest>
<voice>
- *: "Set As Playlist Catalog Directory"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -11694,16 +11696,16 @@
</phrase>
<phrase>
id: LANG_AUTOMATIC
- desc: generic automatic
+ desc: deprecated
user: core
<source>
- *: "Automatic"
+ *: ""
</source>
<dest>
- *: "Automatic"
+ *: ""
</dest>
<voice>
- *: "Automatic"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -12019,14 +12021,17 @@
<source>
*: none
dac_power_mode: "DAC's power mode"
+ es9218: "DAC's output level"
</source>
<dest>
*: none
dac_power_mode: "DAC power mode"
+ es9218: "DAC's output level"
</dest>
<voice>
*: none
dac_power_mode: "DAC power mode"
+ es9218: "DAC's output level"
</voice>
</phrase>
<phrase>
@@ -12036,14 +12041,17 @@
<source>
*: none
dac_power_mode: "High performance"
+ es9218: "High Gain (2 Vrms)"
</source>
<dest>
*: none
dac_power_mode: "High performance"
+ es9218: "High Gain (2 Vrms)"
</dest>
<voice>
*: none
dac_power_mode: "High performance"
+ es9218: "High Gain (2 Vrms)"
</voice>
</phrase>
<phrase>
@@ -12053,14 +12061,17 @@
<source>
*: none
dac_power_mode: "Save battery"
+ es9218: "Low Gain (1 Vrms)"
</source>
<dest>
*: none
dac_power_mode: "Save battery"
+ es9218: "Low Gain (1 Vrms)"
</dest>
<voice>
*: none
dac_power_mode: "Save battery"
+ es9218: "Low Gain (1 Vrms)"
</voice>
</phrase>
<phrase>
@@ -12545,10 +12556,10 @@
*: "Exempt Seek"
</source>
<dest>
- *: "Seek"
+ *: "Exempt Seek"
</dest>
<voice>
- *: "Seek"
+ *: "Exempt Seek"
</voice>
</phrase>
<phrase>
@@ -12618,10 +12629,10 @@
*: "Exempt Play"
</source>
<dest>
- *: "Play"
+ *: "Exempt Play"
</dest>
<voice>
- *: "Play"
+ *: "Exempt Play"
</voice>
</phrase>
<phrase>
@@ -12907,10 +12918,10 @@
*: "Exempt Skip"
</source>
<dest>
- *: "Skip"
+ *: "Exempt Skip"
</dest>
<voice>
- *: "Skip"
+ *: "Exempt Skip"
</voice>
</phrase>
<phrase>
@@ -13870,10 +13881,10 @@
*: "Disable Locked Reminders"
</source>
<dest>
- *: "Disable Notify"
+ *: "Disable Locked Reminders"
</dest>
<voice>
- *: "Disable Notify"
+ *: "Disable Locked Reminders"
</voice>
</phrase>
<phrase>
@@ -14288,16 +14299,16 @@
</phrase>
<phrase>
id: LANG_PROPERTIES_TITLE
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Title]"
+ *: ""
</source>
<dest>
- *: "[Title]"
+ *: ""
</dest>
<voice>
- *: "Title"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -14414,16 +14425,16 @@
</phrase>
<phrase>
id: LANG_PROPERTIES_DURATION
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Duration]"
+ *: ""
</source>
<dest>
- *: "[Duration]"
+ *: ""
</dest>
<voice>
- *: "Duration"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -14470,16 +14481,16 @@
</phrase>
<phrase>
id: LANG_PROPERTIES_ARTIST
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Artist]"
+ *: ""
</source>
<dest>
- *: "[Artist]"
+ *: ""
</dest>
<voice>
- *: "Artist"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -14526,16 +14537,16 @@
</phrase>
<phrase>
id: LANG_PROPERTIES_ALBUM
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Album]"
+ *: ""
</source>
<dest>
- *: "[Album]"
+ *: ""
</dest>
<voice>
- *: "Album"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -14596,16 +14607,16 @@
</phrase>
<phrase>
id: LANG_CLEAR_PLAYLIST
- desc: in the pictureflow main menu
+ desc: deprecated
user: core
<source>
- *: "Clear playlist"
+ *: ""
</source>
<dest>
- *: "Clear playlist"
+ *: ""
</dest>
<voice>
- *: "Clear playlist"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -14902,7 +14913,7 @@
</phrase>
<phrase>
id: LANG_DIRECT
- desc: in the pictureflow settings
+ desc: in the pictureflow settings, also a volume adjustment mode
user: core
<source>
*: "Direct"
@@ -15195,16 +15206,13 @@
desc: used in track x of y constructs
user: core
<source>
- *: none
- hotkey: "Track"
+ *: "Track"
</source>
<dest>
- *: none
- hotkey: "Track"
+ *: "Track"
</dest>
<voice>
- *: none
- hotkey: "Track"
+ *: "Track"
</voice>
</phrase>
<phrase>
@@ -15578,16 +15586,16 @@
</phrase>
<phrase>
id: LANG_CLEAR_LIST_AND_PLAY_NEXT
- desc: in onplay menu. Replace current playlist with selected tracks
+ desc: deprecated
user: core
<source>
- *: "Clear List & Play Next"
+ *: ""
</source>
<dest>
- *: "Clear List & Play Next"
+ *: ""
</dest>
<voice>
- *: "Clear List & Play Next"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -15648,16 +15656,16 @@
</phrase>
<phrase>
id: LANG_CLEAR_LIST_AND_PLAY_SHUFFLED
- desc: in onplay menu. Replace current playlist with selected tracks in random order.
+ desc: deprecated
user: core
<source>
- *: "Clear List & Play Shuffled"
+ *: ""
</source>
<dest>
- *: "Clear List & Play Shuffled"
+ *: ""
</dest>
<voice>
- *: "Clear List & Play Shuffled"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -15716,3 +15724,849 @@
*: "Reload After Saving"
</voice>
</phrase>
+<phrase>
+ id: LANG_FILTER_SUPER_SLOW
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ filter_roll_off: "Super Slow"
+ </source>
+ <dest>
+ *: none
+ filter_roll_off: "Super Slow"
+ </dest>
+ <voice>
+ *: none
+ filter_roll_off: "Super Slow"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FILTER_LINEAR_FAST
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ es9218: "Linear Fast"
+ </source>
+ <dest>
+ *: none
+ es9218: "Linear Fast"
+ </dest>
+ <voice>
+ *: none
+ es9218: "Linear Fast"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FILTER_LINEAR_SLOW
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ es9218: "Linear Slow"
+ </source>
+ <dest>
+ *: none
+ es9218: "Linear Slow"
+ </dest>
+ <voice>
+ *: none
+ es9218: "Linear Slow"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FILTER_MINIMUM_FAST
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ es9218: "Minimum Fast"
+ </source>
+ <dest>
+ *: none
+ es9218: "Minimum Fast"
+ </dest>
+ <voice>
+ *: none
+ es9218: "Minimum Fast"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FILTER_MINIMUM_SLOW
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ es9218: "Minimum Slow"
+ </source>
+ <dest>
+ *: none
+ es9218: "Minimum Slow"
+ </dest>
+ <voice>
+ *: none
+ es9218: "Minimum Slow"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FILTER_APODIZING_1
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ es9218: "Apodizing type 1"
+ </source>
+ <dest>
+ *: none
+ es9218: "Apodizing type 1"
+ </dest>
+ <voice>
+ *: none
+ es9218: "Apodizing type 1"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FILTER_APODIZING_2
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ es9218: "Apodizing type 2"
+ </source>
+ <dest>
+ *: none
+ es9218: "Apodizing type 2"
+ </dest>
+ <voice>
+ *: none
+ es9218: "Apodizing type 2"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FILTER_HYBRID_FAST
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ es9218: "Hybrid Fast"
+ </source>
+ <dest>
+ *: none
+ es9218: "Hybrid Fast"
+ </dest>
+ <voice>
+ *: none
+ es9218: "Hybrid Fast"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FILTER_BRICK_WALL
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ es9218: "Brick Wall"
+ </source>
+ <dest>
+ *: none
+ es9218: "Brick Wall"
+ </dest>
+ <voice>
+ *: none
+ es9218: "Brick Wall"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_ALBUMARTIST
+ desc: deprecated
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_GENRE
+ desc: deprecated
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_COMMENT
+ desc: deprecated
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_COMPOSER
+ desc: deprecated
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_YEAR
+ desc: deprecated
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_TRACKNUM
+ desc: deprecated
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_DISCNUM
+ desc: deprecated
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_FREQUENCY
+ desc: deprecated
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_BITRATE
+ desc: deprecated
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: VOICE_NUMERIC_TENS_SWAP_SEPARATOR
+ desc: voice only, for speaking numbers in languages that swap the tens and ones fields. Leave blank for languages that do not need it, such as English ("231" => "two hundred thirty one") but other languages may speak it as "two hundred one [AND] thirty"
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_VOICED_DATE_FORMAT
+ desc: format string for how dates will be read back. Y == 4-digit year, A == month name, m == numeric month, d == numeric day. For example, "AdY" will read "January 21 2021"
+ user: core
+ <source>
+ *: "dAY"
+ </source>
+ <dest>
+ *: "AdY"
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_LIST_WRAPAROUND
+ desc: in Settings
+ user: core
+ <source>
+ *: "List Wraparound"
+ </source>
+ <dest>
+ *: "List Wraparound"
+ </dest>
+ <voice>
+ *: "List Wraparound"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_SHOW_SHUTDOWN_MESSAGE
+ desc: in Settings
+ user: core
+ <source>
+ *: "Show Shutdown Message"
+ </source>
+ <dest>
+ *: "Show Shutdown Message"
+ </dest>
+ <voice>
+ *: "Show Shutdown Message"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_LIST_ORDER
+ desc: in Settings
+ user: core
+ <source>
+ *: "List Order"
+ </source>
+ <dest>
+ *: "List Order"
+ </dest>
+ <voice>
+ *: "List Order"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_ASCENDING
+ desc: in Settings
+ user: core
+ <source>
+ *: "Ascending"
+ </source>
+ <dest>
+ *: "Ascending"
+ </dest>
+ <voice>
+ *: "Ascending"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_DESCENDING
+ desc: in Settings
+ user: core
+ <source>
+ *: "Descending"
+ </source>
+ <dest>
+ *: "Descending"
+ </dest>
+ <voice>
+ *: "Descending"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_ALBUM_ART
+ desc: in Settings
+ user: core
+ <source>
+ *: "Album Art"
+ </source>
+ <dest>
+ *: "Album Art"
+ </dest>
+ <voice>
+ *: "Album Art"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PREFER_EMBEDDED
+ desc: in Settings
+ user: core
+ <source>
+ *: "Prefer Embedded"
+ </source>
+ <dest>
+ *: "Prefer Embedded"
+ </dest>
+ <voice>
+ *: "Prefer Embedded"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PREFER_IMAGE_FILE
+ desc: in Settings
+ user: core
+ <source>
+ *: "Prefer Image File"
+ </source>
+ <dest>
+ *: "Prefer Image File"
+ </dest>
+ <voice>
+ *: "Prefer Image File"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FM_SYNC_RDS_TIME
+ desc: in radio screen and Settings
+ user: core
+ <source>
+ *: none
+ rds: "Sync RDS Time"
+ </source>
+ <dest>
+ *: none
+ rds: "Sync RDS Time"
+ </dest>
+ <voice>
+ *: none
+ rds: "Sync RDS Time"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_SORT_ALBUMS_BY
+ desc: in Settings
+ user: core
+ <source>
+ *: "Sort albums by"
+ </source>
+ <dest>
+ *: "Sort albums by"
+ </dest>
+ <voice>
+ *: "Sort albums by"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_ARTIST_PLUS_NAME
+ desc: in Settings
+ user: core
+ <source>
+ *: "Artist + Name"
+ </source>
+ <dest>
+ *: "Artist + Name"
+ </dest>
+ <voice>
+ *: "Artist And Name"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_ARTIST_PLUS_YEAR
+ desc: in Settings
+ user: core
+ <source>
+ *: "Artist + Year"
+ </source>
+ <dest>
+ *: "Artist + Year"
+ </dest>
+ <voice>
+ *: "Artist And Year"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_YEAR_SORT_ORDER
+ desc: in Settings
+ user: core
+ <source>
+ *: "Year sort order"
+ </source>
+ <dest>
+ *: "Year sort order"
+ </dest>
+ <voice>
+ *: "Year sort order"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_SHOW_YEAR_IN_ALBUM_TITLE
+ desc: in Settings
+ user: core
+ <source>
+ *: "Show year in album title"
+ </source>
+ <dest>
+ *: "Show year in album title"
+ </dest>
+ <voice>
+ *: "Show year in album title"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_WAIT_FOR_CACHE
+ desc: in Settings
+ user: core
+ <source>
+ *: "Cache needs to finish updating first!"
+ </source>
+ <dest>
+ *: "Cache needs to finish updating first!"
+ </dest>
+ <voice>
+ *: "Cache needs to finish updating first!"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_TRACK_INFO
+ desc: Track Info Title
+ user: core
+ <source>
+ *: "Track Info"
+ </source>
+ <dest>
+ *: "Track Info"
+ </dest>
+ <voice>
+ *: "Track Info"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PLAY
+ desc: play selected file/directory, in playlist context menu
+ user: core
+ <source>
+ *: "Play"
+ </source>
+ <dest>
+ *: "Play"
+ </dest>
+ <voice>
+ *: "Play"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PLAY_SHUFFLED
+ desc: play selected files in shuffled order, in playlist context menu
+ user: core
+ <source>
+ *: "Play Shuffled"
+ </source>
+ <dest>
+ *: "Play Shuffled"
+ </dest>
+ <voice>
+ *: "Play Shuffled"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_KEEP_CURRENT_TRACK_ON_REPLACE
+ desc: used in the playlist settings menu
+ user: core
+ <source>
+ *: "Keep Current Track When Replacing Playlist"
+ </source>
+ <dest>
+ *: "Keep Current Track When Replacing Playlist"
+ </dest>
+ <voice>
+ *: "Keep Current Track When Replacing Playlist"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_CLEAR_SETTINGS_ON_HOLD
+ desc: in the system sub menu
+ user: core
+ <source>
+ *: none
+ clear_settings_on_hold,iriverh10: "Clear settings when reset button is held during startup"
+ ipod4g,ipodcolor,ipodmini1g,ipodmini2g,ipodnano1g,ipodvideo: "Clear settings when hold switch is on during startup"
+ </source>
+ <dest>
+ *: none
+ clear_settings_on_hold,iriverh10: "Clear settings when reset button is held during startup"
+ ipod4g,ipodcolor,ipodmini1g,ipodmini2g,ipodnano1g,ipodvideo: "Clear settings when hold switch is on during startup"
+ </dest>
+ <voice>
+ *: none
+ clear_settings_on_hold,iriverh10: "Clear settings when reset button is held during startup"
+ ipod4g,ipodcolor,ipodmini1g,ipodmini2g,ipodnano1g,ipodvideo: "Clear settings when hold switch is on during startup"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_REWIND_ACROSS_TRACKS
+ desc: in playback settings menu
+ user: core
+ <source>
+ *: "Rewind Across Tracks"
+ </source>
+ <dest>
+ *: "Rewind Across Tracks"
+ </dest>
+ <voice>
+ *: "Rewind across tracks"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_SET_AS
+ desc: used in the onplay menu
+ user: core
+ <source>
+ *: "Set As..."
+ </source>
+ <dest>
+ *: "Set As..."
+ </dest>
+ <voice>
+ *: "Set As..."
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PLAYLIST_DIR
+ desc: used in the onplay menu
+ user: core
+ <source>
+ *: "Playlist Directory"
+ </source>
+ <dest>
+ *: "Playlist Directory"
+ </dest>
+ <voice>
+ *: "Playlist Directory"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_START_DIR
+ desc: used in the onplay menu
+ user: core
+ <source>
+ *: "Start Directory"
+ </source>
+ <dest>
+ *: "Start Directory"
+ </dest>
+ <voice>
+ *: "Start Directory"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_RECORDING_DIR
+ desc: used in the onplay menu
+ user: core
+ <source>
+ *: none
+ recording: "Recording Directory"
+ </source>
+ <dest>
+ *: none
+ recording: "Recording Directory"
+ </dest>
+ <voice>
+ *: none
+ recording: "Recording Directory"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_ADD_TO_PL
+ desc: used in the onplay menu
+ user: core
+ <source>
+ *: "Add to Playlist..."
+ </source>
+ <dest>
+ *: "Add to Playlist..."
+ </dest>
+ <voice>
+ *: "Add to Playlist..."
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_ADD_TO_EXISTING_PL
+ desc: used in the onplay menu
+ user: core
+ <source>
+ *: "Add to Existing Playlist"
+ </source>
+ <dest>
+ *: "Add to Existing Playlist"
+ </dest>
+ <voice>
+ *: "Add to Existing Playlist"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PLAYING_NEXT
+ desc: used in the onplay menu
+ user: core
+ <source>
+ *: "Playing Next..."
+ </source>
+ <dest>
+ *: "Playing Next..."
+ </dest>
+ <voice>
+ *: "Playing Next..."
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PLAY_NEXT
+ desc: used in the onplay menu
+ user: core
+ <source>
+ *: "Play Next"
+ </source>
+ <dest>
+ *: "Play Next"
+ </dest>
+ <voice>
+ *: "Play Next"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_ADD_SHUFFLED
+ desc: used in the onplay menu
+ user: core
+ <source>
+ *: "Add Shuffled"
+ </source>
+ <dest>
+ *: "Add Shuffled"
+ </dest>
+ <voice>
+ *: "Add Shuffled"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PLAY_LAST
+ desc: used in the onplay menu
+ user: core
+ <source>
+ *: "Play Last"
+ </source>
+ <dest>
+ *: "Play Last"
+ </dest>
+ <voice>
+ *: "Play Last"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PLAY_LAST_SHUFFLED
+ desc: used in the onplay menu
+ user: core
+ <source>
+ *: "Play Last Shuffled"
+ </source>
+ <dest>
+ *: "Play Last Shuffled"
+ </dest>
+ <voice>
+ *: "Play Last Shuffled"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_VOLUME_ADJUST_MODE
+ desc: in system settings
+ user: core
+ <source>
+ *: none
+ perceptual_volume: "Volume Adjustment Mode"
+ </source>
+ <dest>
+ *: none
+ perceptual_volume: "Volume Adjustment Mode"
+ </dest>
+ <voice>
+ *: none
+ perceptual_volume: "Volume Adjustment Mode"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_VOLUME_ADJUST_NORM_STEPS
+ desc: in system settings
+ user: core
+ <source>
+ *: none
+ perceptual_volume: "Number of Volume Steps"
+ </source>
+ <dest>
+ *: none
+ perceptual_volume: "Number of Volume Steps"
+ </dest>
+ <voice>
+ *: none
+ perceptual_volume: "Number of Volume Steps"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PERCEPTUAL
+ desc: in system settings -> volume adjustment mode
+ user: core
+ <source>
+ *: none
+ perceptual_volume: "Perceptual"
+ </source>
+ <dest>
+ *: none
+ perceptual_volume: "Perceptual"
+ </dest>
+ <voice>
+ *: none
+ perceptual_volume: "Perceptual"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_SHOW_TRACKS_WHILE_BROWSING
+ desc: in PictureFlow Main Menu
+ user: core
+ <source>
+ *: "Show Tracks While Browsing"
+ </source>
+ <dest>
+ *: "Show Tracks While Browsing"
+ </dest>
+ <voice>
+ *: "Show Tracks While Browsing"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_GOTO_LAST_ALBUM
+ desc: in PictureFlow Main Menu
+ user: core
+ <source>
+ *: "Go to Last Album"
+ </source>
+ <dest>
+ *: "Go to Last Album"
+ </dest>
+ <voice>
+ *: "Go to Last Album"
+ </voice>
+</phrase>
diff --git a/apps/lang/english.lang b/apps/lang/english.lang
index 4cbc6f30f6..1287a26c39 100644
--- a/apps/lang/english.lang
+++ b/apps/lang/english.lang
@@ -322,8 +322,7 @@
<source>
*: "PLAY = Yes"
cowond2*: "MENU, or top-right = Yes"
- creativezen*: "Select = Yes"
- gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT = Yes"
+ creativezen*,gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT = Yes"
iriverh100,iriverh120,iriverh300: "NAVI = Yes"
mrobe500: "PLAY, POWER, or top-right = Yes"
vibe500: "OK = Yes"
@@ -331,8 +330,7 @@
<dest>
*: "PLAY = Yes"
cowond2*: "MENU, or top-right = Yes"
- creativezen*: "Select = Yes"
- gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT = Yes"
+ creativezen*,gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT = Yes"
iriverh100,iriverh120,iriverh300: "NAVI = Yes"
mrobe500: "PLAY, POWER, or top-right = Yes"
vibe500: "OK = Yes"
@@ -1376,6 +1374,20 @@
</voice>
</phrase>
<phrase>
+ id: LANG_SINGLE_MODE
+ desc: single mode
+ user: core
+ <source>
+ *: "Single Mode"
+ </source>
+ <dest>
+ *: "Single Mode"
+ </dest>
+ <voice>
+ *: "Single Mode"
+ </voice>
+</phrase>
+<phrase>
id: LANG_PARTY_MODE
desc: party mode
user: core
@@ -1760,16 +1772,16 @@
</phrase>
<phrase>
id: LANG_AUDIOSCROBBLER
- desc: "Last.fm Log" in the playback menu
+ desc: "Last.fm Logger" in Plugin/apps/scrobbler
user: core
<source>
- *: "Last.fm Log"
+ *: "Last.fm Logger"
</source>
<dest>
- *: "Last.fm Log"
+ *: "Last.fm Logger"
</dest>
<voice>
- *: "Last.fm Log"
+ *: "Last.fm Logger"
</voice>
</phrase>
<phrase>
@@ -2335,15 +2347,15 @@
desc: in lcd settings
user: core
<source>
- *: none
+ *: "Backlight on Lock"
hold_button: "Backlight on Hold"
</source>
<dest>
- *: none
+ *: "Backlight on Lock"
hold_button: "Backlight on Hold"
</dest>
<voice>
- *: none
+ *: "Backlight on Lock"
hold_button: "Backlight on hold"
</voice>
</phrase>
@@ -3633,22 +3645,22 @@
user: core
<source>
*: none
+ rtc: "ON = Set"
gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT = Set"
gogearsa9200,samsungyh*: "PLAY = Set"
iriverh100,iriverh120,iriverh300: "NAVI = Set"
mpiohd300: "ENTER = Set"
mrobe500: "HEART = Set"
- rtc: "ON = Set"
vibe500: "OK = Set"
</source>
<dest>
*: none
+ rtc: "ON = Set"
gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT = Set"
gogearsa9200,samsungyh*: "PLAY = Set"
iriverh100,iriverh120,iriverh300: "NAVI = Set"
mpiohd300: "ENTER = Set"
mrobe500: "HEART = Set"
- rtc: "ON = Set"
vibe500: "OK = Set"
</dest>
<voice>
@@ -3662,6 +3674,7 @@
user: core
<source>
*: none
+ rtc: "OFF = Revert"
gigabeatfx,mrobe500: "POWER = Revert"
gigabeats,sansafuzeplus: "BACK = Revert"
gogearsa9200: "LEFT = Revert"
@@ -3670,12 +3683,12 @@
iriverh10,iriverh10_5gb,sansae200*,sansafuze*: "PREV = Revert"
iriverh100,iriverh120,iriverh300: "STOP = Revert"
mrobe100: "DISPLAY = Revert"
- rtc: "OFF = Revert"
samsungyh*: "REW = Revert"
vibe500: "CANCEL = Revert"
</source>
<dest>
*: none
+ rtc: "OFF = Revert"
gigabeatfx,mrobe500: "POWER = Revert"
gigabeats,sansafuzeplus: "BACK = Revert"
gogearsa9200: "LEFT = Revert"
@@ -3684,7 +3697,6 @@
iriverh10,iriverh10_5gb,sansae200*,sansafuze*: "PREV = Revert"
iriverh100,iriverh120,iriverh300: "STOP = Revert"
mrobe100: "DISPLAY = Revert"
- rtc: "OFF = Revert"
samsungyh*: "REW = Revert"
vibe500: "CANCEL = Revert"
</dest>
@@ -4171,31 +4183,16 @@
</phrase>
<phrase>
id: LANG_ALARM_MOD_KEYS
- desc: Shown key functions in alarm menu (for the RTC alarm mod).
+ desc: deprecated
user: core
<source>
- *: none
- alarm: "PLAY=Set OFF=Cancel"
- gigabeats: "SELECT=Set POWER=Cancel"
- ipod*: "SELECT=Set MENU=Cancel"
- iriverh10,iriverh10_5gb: "SELECT=Set PREV=Cancel"
- mpiohd300: "ENTER=Set MENU=Cancel"
- sansafuzeplus: "SELECT=Set BACK=Cancel"
- vibe500: "OK=Set C=Cancel"
+ *: ""
</source>
<dest>
- *: none
- alarm: "PLAY=Set OFF=Cancel"
- gigabeats: "SELECT=Set POWER=Cancel"
- ipod*: "SELECT=Set MENU=Cancel"
- iriverh10,iriverh10_5gb: "SELECT=Set PREV=Cancel"
- mpiohd300: "ENTER=Set MENU=Cancel"
- sansafuzeplus: "SELECT=Set BACK=Cancel"
- vibe500: "OK=Set C=Cancel"
+ *: ""
</dest>
<voice>
- *: none
- alarm,ipod*: ""
+ *: ""
</voice>
</phrase>
<phrase>
@@ -5022,20 +5019,17 @@
</voice>
</phrase>
<phrase>
- id: LANG_RECORDING_FORMAT
- desc: audio format item in recording menu
+ id: LANG_FORMAT
+ desc: audio format
user: core
<source>
- *: none
- recording: "Format"
+ *: "Format"
</source>
<dest>
- *: none
- recording: "Format"
+ *: "Format"
</dest>
<voice>
- *: none
- recording: "Format"
+ *: "Format"
</voice>
</phrase>
<phrase>
@@ -5417,19 +5411,19 @@
</phrase>
<phrase>
id: LANG_SET_AS_REC_DIR
- desc: used in the onplay menu to set a recording dir
+ desc: deprecated
user: core
<source>
*: none
- recording: "Set As Recording Directory"
+ recording: ""
</source>
<dest>
*: none
- recording: "Set As Recording Directory"
+ recording: ""
</dest>
<voice>
*: none
- recording: "Set As Recording Directory"
+ recording: ""
</voice>
</phrase>
<phrase>
@@ -6131,18 +6125,21 @@
user: core
<source>
*: none
- multivolume: "HD1"
+ hibylinux: "USB:"
+ multivolume: "HD1:"
sansac200*,sansaclipplus,sansae200*,sansafuze*: "mSD:"
xduoox3: "mSD2:"
</source>
<dest>
*: none
- multivolume: "HD1"
+ hibylinux: "USB:"
+ multivolume: "HD1:"
sansac200*,sansaclipplus,sansae200*,sansafuze*: "MSD:"
xduoox3: "mSD2:"
</dest>
<voice>
*: none
+ hibylinux: "U S B"
multivolume: "H D 1"
sansac200*,sansaclipplus,sansae200*,sansafuze*: "micro S D"
xduoox3: "micro S D 2"
@@ -6234,58 +6231,58 @@
</phrase>
<phrase>
id: LANG_INSERT
- desc: in onplay menu. insert a track/playlist into dynamic playlist.
+ desc: deprecated
user: core
<source>
- *: "Insert"
+ *: ""
</source>
<dest>
- *: "Insert"
+ *: ""
</dest>
<voice>
- *: "Insert"
+ *: ""
</voice>
</phrase>
<phrase>
id: LANG_INSERT_FIRST
- desc: in onplay menu. insert a track/playlist into dynamic playlist.
+ desc: deprecated
user: core
<source>
- *: "Insert Next"
+ *: ""
</source>
<dest>
- *: "Insert Next"
+ *: ""
</dest>
<voice>
- *: "Insert Next"
+ *: ""
</voice>
</phrase>
<phrase>
id: LANG_INSERT_LAST
- desc: in onplay menu. append a track/playlist into dynamic playlist.
+ desc: deprecated
user: core
<source>
- *: "Insert Last"
+ *: ""
</source>
<dest>
- *: "Insert Last"
+ *: ""
</dest>
<voice>
- *: "Insert Last"
+ *: ""
</voice>
</phrase>
<phrase>
id: LANG_INSERT_SHUFFLED
- desc: in onplay menu. insert a track/playlist randomly into dynamic playlist
+ desc: deprecated
user: core
<source>
- *: "Insert Shuffled"
+ *: ""
</source>
<dest>
- *: "Insert Shuffled"
+ *: ""
</dest>
<voice>
- *: "Insert Shuffled"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -6458,16 +6455,16 @@
</phrase>
<phrase>
id: LANG_CATALOG_ADD_TO
- desc: in onplay playlist catalogue submenu
+ desc: deprecated
user: core
<source>
- *: "Add to Playlist"
+ *: ""
</source>
<dest>
- *: "Add to Playlist"
+ *: ""
</dest>
<voice>
- *: "Add to Playlist"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -10986,16 +10983,16 @@
</phrase>
<phrase>
id: LANG_INSERT_LAST_SHUFFLED
- desc: in onplay menu. insert a playlist randomly at end of dynamic playlist
+ desc: deprecated
user: core
<source>
- *: "Insert Last Shuffled"
+ *: ""
</source>
<dest>
- *: "Insert Last Shuffled"
+ *: ""
</dest>
<voice>
- *: "Insert Last Shuffled"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -11593,16 +11590,16 @@
</phrase>
<phrase>
id: LANG_SET_AS_START_DIR
- desc: used in the onplay menu to set a starting browser dir
+ desc: deprecated
user: core
<source>
- *: "Start File Browser Here"
+ *: ""
</source>
<dest>
- *: "Start File Browser Here"
+ *: ""
</dest>
<voice>
- *: "Start File Browser Here"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -11708,16 +11705,16 @@
</phrase>
<phrase>
id: LANG_SET_AS_PLAYLISTCAT_DIR
- desc: used in the onplay menu to set a playlist catalogue dir
+ desc: deprecated
user: core
<source>
- *: "Set As Playlist Catalogue Directory"
+ *: ""
</source>
<dest>
- *: "Set As Playlist Catalogue Directory"
+ *: ""
</dest>
<voice>
- *: "Set As Playlist Catalogue Directory"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -11798,16 +11795,16 @@
</phrase>
<phrase>
id: LANG_AUTOMATIC
- desc: generic automatic
+ desc: deprecated
user: core
<source>
- *: "Automatic"
+ *: ""
</source>
<dest>
- *: "Automatic"
+ *: ""
</dest>
<voice>
- *: "Automatic"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -12171,6 +12168,23 @@
</voice>
</phrase>
<phrase>
+ id: LANG_FILTER_SUPER_SLOW
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ filter_roll_off: "Super Slow"
+ </source>
+ <dest>
+ *: none
+ filter_roll_off: "Super Slow"
+ </dest>
+ <voice>
+ *: none
+ filter_roll_off: "Super Slow"
+ </voice>
+</phrase>
+<phrase>
id: LANG_FILTER_SHORT
desc: in sound settings
user: core
@@ -12205,20 +12219,159 @@
</voice>
</phrase>
<phrase>
+ id: LANG_FILTER_LINEAR_FAST
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ es9218: "Linear Fast"
+ </source>
+ <dest>
+ *: none
+ es9218: "Linear Fast"
+ </dest>
+ <voice>
+ *: none
+ es9218: "Linear Fast"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FILTER_LINEAR_SLOW
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ es9218: "Linear Slow"
+ </source>
+ <dest>
+ *: none
+ es9218: "Linear Slow"
+ </dest>
+ <voice>
+ *: none
+ es9218: "Linear Slow"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FILTER_MINIMUM_FAST
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ es9218: "Minimum Fast"
+ </source>
+ <dest>
+ *: none
+ es9218: "Minimum Fast"
+ </dest>
+ <voice>
+ *: none
+ es9218: "Minimum Fast"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FILTER_MINIMUM_SLOW
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ es9218: "Minimum Slow"
+ </source>
+ <dest>
+ *: none
+ es9218: "Minimum Slow"
+ </dest>
+ <voice>
+ *: none
+ es9218: "Minimum Slow"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FILTER_APODIZING_1
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ es9218: "Apodizing type 1"
+ </source>
+ <dest>
+ *: none
+ es9218: "Apodizing type 1"
+ </dest>
+ <voice>
+ *: none
+ es9218: "Apodizing type 1"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FILTER_APODIZING_2
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ es9218: "Apodizing type 2"
+ </source>
+ <dest>
+ *: none
+ es9218: "Apodizing type 2"
+ </dest>
+ <voice>
+ *: none
+ es9218: "Apodizing type 2"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FILTER_HYBRID_FAST
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ es9218: "Hybrid Fast"
+ </source>
+ <dest>
+ *: none
+ es9218: "Hybrid Fast"
+ </dest>
+ <voice>
+ *: none
+ es9218: "Hybrid Fast"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FILTER_BRICK_WALL
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ es9218: "Brick Wall"
+ </source>
+ <dest>
+ *: none
+ es9218: "Brick Wall"
+ </dest>
+ <voice>
+ *: none
+ es9218: "Brick Wall"
+ </voice>
+</phrase>
+<phrase>
id: LANG_DAC_POWER_MODE
desc: in sound settings
user: core
<source>
*: none
dac_power_mode: "DAC's power mode"
+ es9218: "DAC's output level"
</source>
<dest>
*: none
dac_power_mode: "DAC's power mode"
+ es9218: "DAC's output level"
</dest>
<voice>
*: none
dac_power_mode: "DAC's power mode"
+ es9218: "DAC's output level"
</voice>
</phrase>
<phrase>
@@ -12228,14 +12381,17 @@
<source>
*: none
dac_power_mode: "High performance"
+ es9218: "High Gain (2 Vrms)"
</source>
<dest>
*: none
dac_power_mode: "High performance"
+ es9218: "High Gain (2 Vrms)"
</dest>
<voice>
*: none
dac_power_mode: "High performance"
+ es9218: "High Gain (2 Vrms)"
</voice>
</phrase>
<phrase>
@@ -12245,14 +12401,17 @@
<source>
*: none
dac_power_mode: "Save battery"
+ es9218: "Low Gain (1 Vrms)"
</source>
<dest>
*: none
dac_power_mode: "Save battery"
+ es9218: "Low Gain (1 Vrms)"
</dest>
<voice>
*: none
dac_power_mode: "Save battery"
+ es9218: "Low Gain (1 Vrms)"
</voice>
</phrase>
<phrase>
@@ -14566,58 +14725,58 @@
</phrase>
<phrase>
id: LANG_PROPERTIES_ARTIST
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Artist]"
+ *: ""
</source>
<dest>
- *: "[Artist]"
+ *: ""
</dest>
<voice>
- *: "Artist"
+ *: ""
</voice>
</phrase>
<phrase>
id: LANG_PROPERTIES_TITLE
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Title]"
+ *: ""
</source>
<dest>
- *: "[Title]"
+ *: ""
</dest>
<voice>
- *: "Title"
+ *: ""
</voice>
</phrase>
<phrase>
id: LANG_PROPERTIES_ALBUM
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Album]"
+ *: ""
</source>
<dest>
- *: "[Album]"
+ *: ""
</dest>
<voice>
- *: "Album"
+ *: ""
</voice>
</phrase>
<phrase>
id: LANG_PROPERTIES_DURATION
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Duration]"
+ *: ""
</source>
<dest>
- *: "[Duration]"
+ *: ""
</dest>
<voice>
- *: "Duration"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -14951,16 +15110,16 @@
</phrase>
<phrase>
id: LANG_CLEAR_PLAYLIST
- desc: in the pictureflow main menu
+ desc: deprecated
user: core
<source>
- *: "Clear playlist"
+ *: ""
</source>
<dest>
- *: "Clear playlist"
+ *: ""
</dest>
<voice>
- *: "Clear playlist"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -15007,7 +15166,7 @@
</phrase>
<phrase>
id: LANG_DIRECT
- desc: in the pictureflow settings
+ desc: in the pictureflow settings, also a volume adjustment mode
user: core
<source>
*: "Direct"
@@ -15353,16 +15512,13 @@
desc: used in track x of y constructs
user: core
<source>
- *: none
- hotkey: "Track"
+ *: "Track"
</source>
<dest>
- *: none
- hotkey: "Track"
+ *: "Track"
</dest>
<voice>
- *: none
- hotkey: "Track"
+ *: "Track"
</voice>
</phrase>
<phrase>
@@ -15654,16 +15810,16 @@
</phrase>
<phrase>
id: LANG_CLEAR_LIST_AND_PLAY_NEXT
- desc: in onplay menu. Replace current playlist with selected tracks
+ desc: deprecated
user: core
<source>
- *: "Clear List & Play Next"
+ *: ""
</source>
<dest>
- *: "Clear List & Play Next"
+ *: ""
</dest>
<voice>
- *: "Clear List & Play Next"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -15724,16 +15880,16 @@
</phrase>
<phrase>
id: LANG_CLEAR_LIST_AND_PLAY_SHUFFLED
- desc: in onplay menu. Replace current playlist with selected tracks in random order.
+ desc: deprecated
user: core
<source>
- *: "Clear List & Play Shuffled"
+ *: ""
</source>
<dest>
- *: "Clear List & Play Shuffled"
+ *: ""
</dest>
<voice>
- *: "Clear List & Play Shuffled"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -15792,3 +15948,710 @@
*: "Reload After Saving"
</voice>
</phrase>
+<phrase>
+ id: LANG_PROPERTIES_ALBUMARTIST
+ desc: deprecated
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_GENRE
+ desc: deprecated
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_COMMENT
+ desc: deprecated
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_COMPOSER
+ desc: deprecated
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_YEAR
+ desc: deprecated
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_TRACKNUM
+ desc: deprecated
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_DISCNUM
+ desc: deprecated
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_FREQUENCY
+ desc: deprecated
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_BITRATE
+ desc: deprecated
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: VOICE_NUMERIC_TENS_SWAP_SEPARATOR
+ desc: voice only, for speaking numbers in languages that swap the tens and ones fields. Leave blank for languages that do not need it, such as English ("231" => "two hundred thirty one") but other languages may speak it as "two hundred one [AND] thirty"
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_VOICED_DATE_FORMAT
+ desc: format string for how dates will be read back. Y == 4-digit year, A == month name, m == numeric month, d == numeric day. For example, "AdY" will read "January 21 2021"
+ user: core
+ <source>
+ *: "dAY"
+ </source>
+ <dest>
+ *: "dAY"
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_LIST_WRAPAROUND
+ desc: in Settings
+ user: core
+ <source>
+ *: "List Wraparound"
+ </source>
+ <dest>
+ *: "List Wraparound"
+ </dest>
+ <voice>
+ *: "List Wraparound"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_SHOW_SHUTDOWN_MESSAGE
+ desc: in Settings
+ user: core
+ <source>
+ *: "Show Shutdown Message"
+ </source>
+ <dest>
+ *: "Show Shutdown Message"
+ </dest>
+ <voice>
+ *: "Show Shutdown Message"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_LIST_ORDER
+ desc: in Settings
+ user: core
+ <source>
+ *: "List Order"
+ </source>
+ <dest>
+ *: "List Order"
+ </dest>
+ <voice>
+ *: "List Order"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_ASCENDING
+ desc: in Settings
+ user: core
+ <source>
+ *: "Ascending"
+ </source>
+ <dest>
+ *: "Ascending"
+ </dest>
+ <voice>
+ *: "Ascending"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_DESCENDING
+ desc: in Settings
+ user: core
+ <source>
+ *: "Descending"
+ </source>
+ <dest>
+ *: "Descending"
+ </dest>
+ <voice>
+ *: "Descending"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_ALBUM_ART
+ desc: in Settings
+ user: core
+ <source>
+ *: "Album Art"
+ </source>
+ <dest>
+ *: "Album Art"
+ </dest>
+ <voice>
+ *: "Album Art"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PREFER_EMBEDDED
+ desc: in Settings
+ user: core
+ <source>
+ *: "Prefer Embedded"
+ </source>
+ <dest>
+ *: "Prefer Embedded"
+ </dest>
+ <voice>
+ *: "Prefer Embedded"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PREFER_IMAGE_FILE
+ desc: in Settings
+ user: core
+ <source>
+ *: "Prefer Image File"
+ </source>
+ <dest>
+ *: "Prefer Image File"
+ </dest>
+ <voice>
+ *: "Prefer Image File"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FM_SYNC_RDS_TIME
+ desc: in radio screen and Settings
+ user: core
+ <source>
+ *: none
+ rds: "Sync RDS Time"
+ </source>
+ <dest>
+ *: none
+ rds: "Sync RDS Time"
+ </dest>
+ <voice>
+ *: none
+ rds: "Sync RDS Time"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_SORT_ALBUMS_BY
+ desc: in Settings
+ user: core
+ <source>
+ *: "Sort albums by"
+ </source>
+ <dest>
+ *: "Sort albums by"
+ </dest>
+ <voice>
+ *: "Sort albums by"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_ARTIST_PLUS_NAME
+ desc: in Settings
+ user: core
+ <source>
+ *: "Artist + Name"
+ </source>
+ <dest>
+ *: "Artist + Name"
+ </dest>
+ <voice>
+ *: "Artist And Name"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_ARTIST_PLUS_YEAR
+ desc: in Settings
+ user: core
+ <source>
+ *: "Artist + Year"
+ </source>
+ <dest>
+ *: "Artist + Year"
+ </dest>
+ <voice>
+ *: "Artist And Year"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_YEAR_SORT_ORDER
+ desc: in Settings
+ user: core
+ <source>
+ *: "Year sort order"
+ </source>
+ <dest>
+ *: "Year sort order"
+ </dest>
+ <voice>
+ *: "Year sort order"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_SHOW_YEAR_IN_ALBUM_TITLE
+ desc: in Settings
+ user: core
+ <source>
+ *: "Show year in album title"
+ </source>
+ <dest>
+ *: "Show year in album title"
+ </dest>
+ <voice>
+ *: "Show year in album title"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_WAIT_FOR_CACHE
+ desc: in Settings
+ user: core
+ <source>
+ *: "Cache needs to finish updating first!"
+ </source>
+ <dest>
+ *: "Cache needs to finish updating first!"
+ </dest>
+ <voice>
+ *: "Cache needs to finish updating first!"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_TRACK_INFO
+ desc: Track Info Title
+ user: core
+ <source>
+ *: "Track Info"
+ </source>
+ <dest>
+ *: "Track Info"
+ </dest>
+ <voice>
+ *: "Track Info"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PLAY
+ desc: play selected file/directory, in playlist context menu
+ user: core
+ <source>
+ *: "Play"
+ </source>
+ <dest>
+ *: "Play"
+ </dest>
+ <voice>
+ *: "Play"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PLAY_SHUFFLED
+ desc: play selected files in shuffled order, in playlist context menu
+ user: core
+ <source>
+ *: "Play Shuffled"
+ </source>
+ <dest>
+ *: "Play Shuffled"
+ </dest>
+ <voice>
+ *: "Play Shuffled"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_KEEP_CURRENT_TRACK_ON_REPLACE
+ desc: used in the playlist settings menu
+ user: core
+ <source>
+ *: "Keep Current Track When Replacing Playlist"
+ </source>
+ <dest>
+ *: "Keep Current Track When Replacing Playlist"
+ </dest>
+ <voice>
+ *: "Keep Current Track When Replacing Playlist"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_CLEAR_SETTINGS_ON_HOLD
+ desc: in the system sub menu
+ user: core
+ <source>
+ *: none
+ ipodcolor,ipodnano1g,ipodvideo,ipod4g,ipodmini1g,ipodmini2g: "Clear settings when hold switch is on during startup"
+ clear_settings_on_hold, iriverh10: "Clear settings when reset button is held during startup"
+ </source>
+ <dest>
+ *: none
+ ipodcolor,ipodnano1g,ipodvideo,ipod4g,ipodmini1g,ipodmini2g: "Clear settings when hold switch is on during startup"
+ clear_settings_on_hold, iriverh10: "Clear settings when reset button is held during startup"
+ </dest>
+ <voice>
+ *: none
+ ipodcolor,ipodnano1g,ipodvideo,ipod4g,ipodmini1g,ipodmini2g: "Clear settings when hold switch is on during startup"
+ clear_settings_on_hold, iriverh10: "Clear settings when reset button is held during startup"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_REWIND_ACROSS_TRACKS
+ desc: in playback settings menu
+ user: core
+ <source>
+ *: "Rewind Across Tracks"
+ </source>
+ <dest>
+ *: "Rewind Across Tracks"
+ </dest>
+ <voice>
+ *: "Rewind across tracks"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_SET_AS
+ desc: used in the onplay menu
+ user: core
+ <source>
+ *: "Set As..."
+ </source>
+ <dest>
+ *: "Set As..."
+ </dest>
+ <voice>
+ *: "Set As..."
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PLAYLIST_DIR
+ desc: used in the onplay menu
+ user: core
+ <source>
+ *: "Playlist Directory"
+ </source>
+ <dest>
+ *: "Playlist Directory"
+ </dest>
+ <voice>
+ *: "Playlist Directory"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_START_DIR
+ desc: used in the onplay menu
+ user: core
+ <source>
+ *: "Start Directory"
+ </source>
+ <dest>
+ *: "Start Directory"
+ </dest>
+ <voice>
+ *: "Start Directory"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_RECORDING_DIR
+ desc: used in the onplay menu
+ user: core
+ <source>
+ *: none
+ recording: "Recording Directory"
+ </source>
+ <dest>
+ *: none
+ recording: "Recording Directory"
+ </dest>
+ <voice>
+ *: none
+ recording: "Recording Directory"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_ADD_TO_PL
+ desc: used in the onplay menu
+ user: core
+ <source>
+ *: "Add to Playlist..."
+ </source>
+ <dest>
+ *: "Add to Playlist..."
+ </dest>
+ <voice>
+ *: "Add to Playlist..."
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_ADD_TO_EXISTING_PL
+ desc: used in the onplay menu
+ user: core
+ <source>
+ *: "Add to Existing Playlist"
+ </source>
+ <dest>
+ *: "Add to Existing Playlist"
+ </dest>
+ <voice>
+ *: "Add to Existing Playlist"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PLAYING_NEXT
+ desc: used in the onplay menu
+ user: core
+ <source>
+ *: "Playing Next..."
+ </source>
+ <dest>
+ *: "Playing Next..."
+ </dest>
+ <voice>
+ *: "Playing Next..."
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PLAY_NEXT
+ desc: used in the onplay menu
+ user: core
+ <source>
+ *: "Play Next"
+ </source>
+ <dest>
+ *: "Play Next"
+ </dest>
+ <voice>
+ *: "Play Next"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_ADD_SHUFFLED
+ desc: used in the onplay menu
+ user: core
+ <source>
+ *: "Add Shuffled"
+ </source>
+ <dest>
+ *: "Add Shuffled"
+ </dest>
+ <voice>
+ *: "Add Shuffled"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PLAY_LAST
+ desc: used in the onplay menu
+ user: core
+ <source>
+ *: "Play Last"
+ </source>
+ <dest>
+ *: "Play Last"
+ </dest>
+ <voice>
+ *: "Play Last"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PLAY_LAST_SHUFFLED
+ desc: used in the onplay menu
+ user: core
+ <source>
+ *: "Play Last Shuffled"
+ </source>
+ <dest>
+ *: "Play Last Shuffled"
+ </dest>
+ <voice>
+ *: "Play Last Shuffled"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_VOLUME_ADJUST_MODE
+ desc: in system settings
+ user: core
+ <source>
+ *: none
+ perceptual_volume: "Volume Adjustment Mode"
+ </source>
+ <dest>
+ *: none
+ perceptual_volume: "Volume Adjustment Mode"
+ </dest>
+ <voice>
+ *: none
+ perceptual_volume: "Volume Adjustment Mode"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_VOLUME_ADJUST_NORM_STEPS
+ desc: in system settings
+ user: core
+ <source>
+ *: none
+ perceptual_volume: "Number of Volume Steps"
+ </source>
+ <dest>
+ *: none
+ perceptual_volume: "Number of Volume Steps"
+ </dest>
+ <voice>
+ *: none
+ perceptual_volume: "Number of Volume Steps"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PERCEPTUAL
+ desc: in system settings -> volume adjustment mode
+ user: core
+ <source>
+ *: none
+ perceptual_volume: "Perceptual"
+ </source>
+ <dest>
+ *: none
+ perceptual_volume: "Perceptual"
+ </dest>
+ <voice>
+ *: none
+ perceptual_volume: "Perceptual"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_SHOW_TRACKS_WHILE_BROWSING
+ desc: in PictureFlow Main Menu
+ user: core
+ <source>
+ *: "Show Tracks While Browsing"
+ </source>
+ <dest>
+ *: "Show Tracks While Browsing"
+ </dest>
+ <voice>
+ *: "Show Tracks While Browsing"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_GOTO_LAST_ALBUM
+ desc: in PictureFlow Main Menu
+ user: core
+ <source>
+ *: "Go to Last Album"
+ </source>
+ <dest>
+ *: "Go to Last Album"
+ </dest>
+ <voice>
+ *: "Go to Last Album"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_DATABASE_DIR
+ desc: in database settings menu
+ user: core
+ <source>
+ *: "Database Directory"
+ </source>
+ <dest>
+ *: "Database Directory"
+ </dest>
+ <voice>
+ *: "Database Directory"
+ </voice>
+</phrase>
diff --git a/apps/lang/espanol.lang b/apps/lang/espanol.lang
index 0e1b120587..9863817a52 100644
--- a/apps/lang/espanol.lang
+++ b/apps/lang/espanol.lang
@@ -8699,20 +8699,17 @@
</voice>
</phrase>
<phrase>
- id: LANG_RECORDING_FORMAT
- desc: audio format item in recording menu
+ id: LANG_FORMAT
+ desc: audio format
user: core
<source>
- *: none
- recording: "Format"
+ *: "Format"
</source>
<dest>
- *: none
- recording: "Formato"
+ *: "Formato"
</dest>
<voice>
- *: none
- recording: "Formato"
+ *: "Formato"
</voice>
</phrase>
<phrase>
@@ -10219,15 +10216,15 @@
user: core
<source>
*: none
- gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
</source>
<dest>
*: none
- gigabeatfx,sansafuzeplus: "Sensibilidad de la superficie táctil"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Sensibilidad de la superficie táctil"
</dest>
<voice>
*: none
- gigabeatfx,sansafuzeplus: "Sensibilidad de la superficie táctil"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Sensibilidad de la superficie táctil"
</voice>
</phrase>
<phrase>
diff --git a/apps/lang/finnish.lang b/apps/lang/finnish.lang
index 845a5b4e0d..9ec74591f8 100644
--- a/apps/lang/finnish.lang
+++ b/apps/lang/finnish.lang
@@ -8258,20 +8258,17 @@
</voice>
</phrase>
<phrase>
- id: LANG_RECORDING_FORMAT
- desc: audio format item in recording menu
+ id: LANG_FORMAT
+ desc: audio format
user: core
<source>
- *: none
- recording: "Format"
+ *: "Format"
</source>
<dest>
- *: none
- recording: "Muoto"
+ *: "Muoto"
</dest>
<voice>
- *: none
- recording: "Muoto"
+ *: "Muoto"
</voice>
</phrase>
<phrase>
@@ -10296,15 +10293,15 @@
user: core
<source>
*: none
- gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
</source>
<dest>
*: none
- gigabeatfx,sansafuzeplus: "Kosketuslevyn herkkyys"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Kosketuslevyn herkkyys"
</dest>
<voice>
*: none
- gigabeatfx,sansafuzeplus: "Kosketuslevyn herkkyys"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Kosketuslevyn herkkyys"
</voice>
</phrase>
<phrase>
diff --git a/apps/lang/francais.lang b/apps/lang/francais.lang
index 49fa6f854b..ca61c27830 100644
--- a/apps/lang/francais.lang
+++ b/apps/lang/francais.lang
@@ -276,7 +276,7 @@
<source>
*: "PLAY = Yes"
cowond2*: "MENU, or top-right = Yes"
- creativezen*: "Select = Yes"
+ creativezen*: "SELECT = Yes"
gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT = Yes"
iriverh100,iriverh120,iriverh300: "NAVI = Yes"
mrobe500: "PLAY, POWER, or top-right = Yes"
@@ -286,7 +286,7 @@
*: "PLAY = Oui"
archosplayer: "(PLAY/STOP)"
cowond2*: "MENU ou en haut à gauche = Oui"
- creativezen*: "Select = Oui"
+ creativezen*: "SELECT = Oui"
gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT = Oui"
iriverh100,iriverh120,iriverh300: "NAVI = Oui"
mrobe500: "PLAY, POWER, ou en haut à gauche = Oui"
@@ -4994,20 +4994,17 @@
</voice>
</phrase>
<phrase>
- id: LANG_RECORDING_FORMAT
- desc: audio format item in recording menu
+ id: LANG_FORMAT
+ desc: audio format
user: core
<source>
- *: none
- recording: "Format"
+ *: "Format"
</source>
<dest>
- *: none
- recording: "Format"
+ *: "Format"
</dest>
<voice>
- *: none
- recording: "Format"
+ *: "Format"
</voice>
</phrase>
<phrase>
@@ -9658,15 +9655,15 @@
user: core
<source>
*: none
- gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
</source>
<dest>
*: none
- gigabeatfx,sansafuzeplus: "Sensibilité du pavé tactile"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Sensibilité du pavé tactile"
</dest>
<voice>
*: none
- gigabeatfx,sansafuzeplus: "Sensibilité du pavé tactile"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Sensibilité du pavé tactile"
</voice>
</phrase>
<phrase>
diff --git a/apps/lang/galego.lang b/apps/lang/galego.lang
index 70fe0297fa..958cfd2b2f 100644
--- a/apps/lang/galego.lang
+++ b/apps/lang/galego.lang
@@ -6601,15 +6601,15 @@
user: core
<source>
*: none
- gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
</source>
<dest>
*: none
- gigabeatfx,sansafuzeplus: "Sensibilidade do touchpad"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Sensibilidade do touchpad"
</dest>
<voice>
*: none
- gigabeatfx,sansafuzeplus: "Sensibilidade do touchpad"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Sensibilidade do touchpad"
</voice>
</phrase>
<phrase>
@@ -7221,20 +7221,17 @@
</voice>
</phrase>
<phrase>
- id: LANG_RECORDING_FORMAT
- desc: audio format item in recording menu
+ id: LANG_FORMAT
+ desc: audio format
user: core
<source>
- *: none
- recording: "Format"
+ *: "Format"
</source>
<dest>
- *: none
- recording: "Formato"
+ *: "Formato"
</dest>
<voice>
- *: none
- recording: "Formato"
+ *: "Formato"
</voice>
</phrase>
<phrase>
diff --git a/apps/lang/greek.lang b/apps/lang/greek.lang
index 44507e9eca..e094fd0479 100644
--- a/apps/lang/greek.lang
+++ b/apps/lang/greek.lang
@@ -252,7 +252,7 @@
*: "PLAY = Yes"
archosplayer: "(PLAY/STOP)"
cowond2*: "MENU, or top-right = Yes"
- creativezen*: "Select = Yes"
+ creativezen*: "SELECT = Yes"
gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT = Yes"
iriverh100,iriverh120,iriverh300: "NAVI = Yes"
mrobe500: "PLAY, POWER, or top-right = Yes"
@@ -262,7 +262,7 @@
*: "PLAY = ΕπικÏÏωση"
archosplayer: "(PLAY/STOP)"
cowond2*: "MENU = ΕπικÏÏωση"
- creativezen*: "Select = ΕπικÏÏωση"
+ creativezen*: "SELECT = ΕπικÏÏωση"
gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT = ΕπικÏÏωση"
iriverh100,iriverh120,iriverh300: "NAVI = ΕπικÏÏωση"
mrobe500: "PLAY, POWER, or top-right = ΕπικÏÏωση"
@@ -5217,20 +5217,17 @@
</voice>
</phrase>
<phrase>
- id: LANG_RECORDING_FORMAT
- desc: audio format item in recording menu
+ id: LANG_FORMAT
+ desc: audio format
user: core
<source>
- *: none
- recording: "Format"
+ *: "Format"
</source>
<dest>
- *: none
- recording: "ΤÏπος"
+ *: "ΤÏπος"
</dest>
<voice>
- *: none
- recording: "ΤÏπος"
+ *: "ΤÏπος"
</voice>
</phrase>
<phrase>
@@ -10125,15 +10122,15 @@
user: core
<source>
*: none
- gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
</source>
<dest>
*: none
- gigabeatfx,sansafuzeplus: "Ευαισθησία πινακίδας αφής"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Ευαισθησία πινακίδας αφής"
</dest>
<voice>
*: none
- gigabeatfx,sansafuzeplus: "Ευαισθησία πινακίδας αφής"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Ευαισθησία πινακίδας αφής"
</voice>
</phrase>
<phrase>
diff --git a/apps/lang/hebrew.lang b/apps/lang/hebrew.lang
index 2f732f0eef..272676a62e 100644
--- a/apps/lang/hebrew.lang
+++ b/apps/lang/hebrew.lang
@@ -14,6 +14,8 @@
# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
# KIND, either express or implied.
#
+# LANGUAGE_IS_RTL
+#
# Hebrew language file, translated by:
# - Ariel Saghiv
# - Liberman Shachar
@@ -1461,6 +1463,20 @@
</voice>
</phrase>
<phrase>
+ id: LANG_SINGLE_MODE
+ desc: single mode
+ user: core
+ <source>
+ *: "Single Mode"
+ </source>
+ <dest>
+ *: "מצב יחדי"
+ </dest>
+ <voice>
+ *: "מצב יחדי"
+ </voice>
+</phrase>
+<phrase>
id: LANG_PARTY_MODE
desc: party mode
user: core
@@ -5280,20 +5296,17 @@
</voice>
</phrase>
<phrase>
- id: LANG_RECORDING_FORMAT
- desc: audio format item in recording menu
+ id: LANG_FORMAT
+ desc: audio format
user: core
<source>
- *: none
- recording: "Format"
+ *: "Format"
</source>
<dest>
- *: none
- recording: "תבנית"
+ *: "תבנית"
</dest>
<voice>
- *: none
- recording: "תבנית"
+ *: "תבנית"
</voice>
</phrase>
<phrase>
@@ -10328,15 +10341,15 @@
user: core
<source>
*: none
- gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
</source>
<dest>
*: none
- gigabeatfx,sansafuzeplus: "רגישות משטח מגע"
+ fiiom3k,gigabeatfx,sansafuzeplus: "רגישות משטח מגע"
</dest>
<voice>
*: none
- gigabeatfx,sansafuzeplus: "רגישות משטח מגע"
+ fiiom3k,gigabeatfx,sansafuzeplus: "רגישות משטח מגע"
</voice>
</phrase>
<phrase>
diff --git a/apps/lang/hrvatski.lang b/apps/lang/hrvatski.lang
index dd9578dc8a..b4fd23699d 100644
--- a/apps/lang/hrvatski.lang
+++ b/apps/lang/hrvatski.lang
@@ -5260,20 +5260,17 @@
</voice>
</phrase>
<phrase>
- id: LANG_RECORDING_FORMAT
- desc: audio format item in recording menu
+ id: LANG_FORMAT
+ desc: audio format
user: core
<source>
- *: none
- recording: "Format"
+ *: "Format"
</source>
<dest>
- *: none
- recording: "Oblik"
+ *: "Oblik"
</dest>
<voice>
- *: none
- recording: "Oblik"
+ *: "Oblik"
</voice>
</phrase>
<phrase>
@@ -10308,15 +10305,15 @@
user: core
<source>
*: none
- gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
</source>
<dest>
*: none
- gigabeatfx,sansafuzeplus: "Osjetljivost touchpada"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Osjetljivost touchpada"
</dest>
<voice>
*: none
- gigabeatfx,sansafuzeplus: "Osjetljivost touchpada"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Osjetljivost touchpada"
</voice>
</phrase>
<phrase>
diff --git a/apps/lang/italiano.lang b/apps/lang/italiano.lang
index 559b53127d..b6b97df22f 100644
--- a/apps/lang/italiano.lang
+++ b/apps/lang/italiano.lang
@@ -249,8 +249,7 @@
<source>
*: "PLAY = Yes"
cowond2*: "MENU, or top-right = Yes"
- creativezen*: "Select = Yes"
- gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT = Yes"
+ creativezen*,gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT = Yes"
iriverh100,iriverh120,iriverh300: "NAVI = Yes"
mrobe500: "PLAY, POWER, or top-right = Yes"
vibe500: "OK = Yes"
@@ -1686,10 +1685,10 @@
</phrase>
<phrase>
id: LANG_AUDIOSCROBBLER
- desc: "Last.fm Log" in the playback menu
+ desc: "Last.fm Logger" in Plugin/apps/scrobbler
user: core
<source>
- *: "Last.fm Log"
+ *: "Last.fm Logger"
</source>
<dest>
*: "Log per Last.fm"
@@ -4097,31 +4096,16 @@
</phrase>
<phrase>
id: LANG_ALARM_MOD_KEYS
- desc: Shown key functions in alarm menu (for the RTC alarm mod).
+ desc: deprecated
user: core
<source>
- *: none
- alarm: "PLAY=Set OFF=Cancel"
- gigabeats: "SELECT=Set POWER=Cancel"
- ipod*: "SELECT=Set MENU=Cancel"
- iriverh10,iriverh10_5gb: "SELECT=Set PREV=Cancel"
- mpiohd300: "ENTER=Set MENU=Cancel"
- sansafuzeplus: "SELECT=Set BACK=Cancel"
- vibe500: "OK=Set C=Cancel"
+ *: ""
</source>
<dest>
- *: none
- alarm: "PLAY=Imposta OFF=Annulla"
- gigabeats: "SELECT=Imposta POWER=Annulla"
- ipod*: "SELECT=Imposta MENU=Annulla"
- iriverh10,iriverh10_5gb: "SELECT=Imposta PREV=Annulla"
- mpiohd300: "ENTER=Imposta MENU=Annulla"
- sansafuzeplus: "SELECT=Imposta BACK=Annulla"
- vibe500: "OK=Imposta C=Annulla"
+ *: ""
</dest>
<voice>
- *: none
- alarm,ipod*: ""
+ *: ""
</voice>
</phrase>
<phrase>
@@ -4948,20 +4932,17 @@
</voice>
</phrase>
<phrase>
- id: LANG_RECORDING_FORMAT
- desc: audio format item in recording menu
+ id: LANG_FORMAT
+ desc: audio format
user: core
<source>
- *: none
- recording: "Format"
+ *: "Format"
</source>
<dest>
- *: none
- recording: "Formato"
+ *: "Formato"
</dest>
<voice>
- *: none
- recording: "Formato"
+ *: "Formato"
</voice>
</phrase>
<phrase>
@@ -5343,19 +5324,17 @@
</phrase>
<phrase>
id: LANG_SET_AS_REC_DIR
- desc: used in the onplay menu to set a recording dir
+ desc: deprecated
user: core
<source>
*: none
- recording: "Set As Recording Directory"
+ recording: ""
</source>
<dest>
- *: none
- recording: "Imposta come cartella di registrazione"
+ *: ""
</dest>
<voice>
- *: none
- recording: "Imposta come cartella di registrazione"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -5994,12 +5973,10 @@
<source>
*: "Battery: %d%% %dh %dm"
ipodmini1g,ipodmini2g,iriverh10: "Batt: %d%% %dh %dm"
- iriverifp7xx: "%d%% %dh %dm"
</source>
<dest>
*: "Batteria: %d%% %dh %dm"
ipodmini1g,ipodmini2g,iriverh10: "Batt: %d%% %dh %dm"
- iriverifp7xx: "%d%% %dh"
</dest>
<voice>
*: "Livello Batteria"
@@ -6059,18 +6036,21 @@
user: core
<source>
*: none
- multivolume: "HD1"
+ hibylinux: "USB:"
+ multivolume: "HD1:"
sansac200*,sansaclipplus,sansae200*,sansafuze*: "mSD:"
xduoox3: "mSD2:"
</source>
<dest>
*: none
+ hibylinux: "USB:"
multivolume: "HD1"
sansac200*,sansaclipplus,sansae200*,sansafuze*: "MSD:"
xduoox3: "MSD2:"
</dest>
<voice>
*: none
+ hibylinux: "U S B"
multivolume: "HD 1"
sansac200*,sansaclipplus,sansae200*,sansafuze*: "Micro S D"
xduoox3: "Micro SD 2"
@@ -6162,58 +6142,58 @@
</phrase>
<phrase>
id: LANG_INSERT
- desc: in onplay menu. insert a track/playlist into dynamic playlist.
+ desc: deprecated
user: core
<source>
- *: "Insert"
+ *: ""
</source>
<dest>
- *: "Inserisci"
+ *: ""
</dest>
<voice>
- *: "Inserisci"
+ *: ""
</voice>
</phrase>
<phrase>
id: LANG_INSERT_FIRST
- desc: in onplay menu. insert a track/playlist into dynamic playlist.
+ desc: deprecated
user: core
<source>
- *: "Insert Next"
+ *: ""
</source>
<dest>
- *: "Inserisci come prossima"
+ *: ""
</dest>
<voice>
- *: "Inserisci come prossima"
+ *: ""
</voice>
</phrase>
<phrase>
id: LANG_INSERT_LAST
- desc: in onplay menu. append a track/playlist into dynamic playlist.
+ desc: deprecated
user: core
<source>
- *: "Insert Last"
+ *: ""
</source>
<dest>
- *: "Inserisci Come Ultima"
+ *: ""
</dest>
<voice>
- *: "Inserisci Come Ultima"
+ *: ""
</voice>
</phrase>
<phrase>
id: LANG_INSERT_SHUFFLED
- desc: in onplay menu. insert a track/playlist randomly into dynamic playlist
+ desc: deprecated
user: core
<source>
- *: "Insert Shuffled"
+ *: ""
</source>
<dest>
- *: "Mescola E Inserisci"
+ *: ""
</dest>
<voice>
- *: "Mescola E Inserisci"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -6386,16 +6366,16 @@
</phrase>
<phrase>
id: LANG_CATALOG_ADD_TO
- desc: in onplay playlist catalogue submenu
+ desc: deprecated
user: core
<source>
- *: "Add to Playlist"
+ *: ""
</source>
<dest>
- *: "Aggiungi alla playlist"
+ *: ""
</dest>
<voice>
- *: "Aggiungi alla playlist"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -6784,7 +6764,7 @@
*: "<No Info>"
</source>
<dest>
- *: "<No Info>"
+ *: "<Nessuna0 Info>"
</dest>
<voice>
*: "Nessuna Informazione"
@@ -10869,16 +10849,16 @@
</phrase>
<phrase>
id: LANG_INSERT_LAST_SHUFFLED
- desc: in onplay menu. insert a playlist randomly at end of dynamic playlist
+ desc: deprecated
user: core
<source>
- *: "Insert Last Shuffled"
+ *: ""
</source>
<dest>
- *: "Mescola E Inserisci Come Ultima"
+ *: ""
</dest>
<voice>
- *: "Mescola E Inserisci Come Ultima"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -11476,16 +11456,16 @@
</phrase>
<phrase>
id: LANG_SET_AS_START_DIR
- desc: used in the onplay menu to set a starting browser dir
+ desc: deprecated
user: core
<source>
- *: "Start File Browser Here"
+ *: ""
</source>
<dest>
- *: "Avvia Sfoglia File Da Qui"
+ *: ""
</dest>
<voice>
- *: "Avvia Sfoglia File Da Qui"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -11591,16 +11571,16 @@
</phrase>
<phrase>
id: LANG_SET_AS_PLAYLISTCAT_DIR
- desc: used in the onplay menu to set a playlist catalogue dir
+ desc: deprecated
user: core
<source>
- *: "Set As Playlist Catalogue Directory"
+ *: ""
</source>
<dest>
- *: "Imposta Come Cartella Catalogo Playlist"
+ *: ""
</dest>
<voice>
- *: "Imposta Come Cartella Catalogo Playlist"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -11681,16 +11661,16 @@
</phrase>
<phrase>
id: LANG_AUTOMATIC
- desc: generic automatic
+ desc: deprecated
user: core
<source>
- *: "Automatic"
+ *: ""
</source>
<dest>
- *: "Automatico"
+ *: ""
</dest>
<voice>
- *: "Automatico"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -12531,7 +12511,7 @@
</phrase>
<phrase>
id: LANG_DIRECT
- desc: in the pictureflow settings
+ desc: in the pictureflow settings, also a volume adjustment mode
user: core
<source>
*: "Direct"
@@ -13090,16 +13070,16 @@
</phrase>
<phrase>
id: LANG_PROPERTIES_ALBUM
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Album]"
+ *: ""
</source>
<dest>
- *: "[Album]"
+ *: ""
</dest>
<voice>
- *: "Album"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -13219,16 +13199,16 @@
</phrase>
<phrase>
id: LANG_PROPERTIES_ARTIST
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Artist]"
+ *: ""
</source>
<dest>
- *: "[Artista]"
+ *: ""
</dest>
<voice>
- *: "Artista"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -13278,16 +13258,16 @@
</phrase>
<phrase>
id: LANG_PROPERTIES_TITLE
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Title]"
+ *: ""
</source>
<dest>
- *: "[Titolo]"
+ *: ""
</dest>
<voice>
- *: "Titolo"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -13553,16 +13533,16 @@
</phrase>
<phrase>
id: LANG_PROPERTIES_DURATION
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Duration]"
+ *: ""
</source>
<dest>
- *: "[Durata]"
+ *: ""
</dest>
<voice>
- *: "Durata"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -14900,16 +14880,16 @@
</phrase>
<phrase>
id: LANG_CLEAR_PLAYLIST
- desc: in the pictureflow main menu
+ desc: deprecated
user: core
<source>
- *: "Clear playlist"
+ *: ""
</source>
<dest>
- *: "Svuota playlist"
+ *: ""
</dest>
<voice>
- *: "Svuota playlist"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -15148,16 +15128,13 @@
desc: used in track x of y constructs
user: core
<source>
- *: none
- hotkey: "Track"
+ *: "Track"
</source>
<dest>
- *: none
- hotkey: "Traccia"
+ *: "Traccia"
</dest>
<voice>
- *: none
- hotkey: "Traccia"
+ *: "Traccia"
</voice>
</phrase>
<phrase>
@@ -15531,16 +15508,16 @@
</phrase>
<phrase>
id: LANG_CLEAR_LIST_AND_PLAY_NEXT
- desc: in onplay menu. Replace current playlist with selected tracks
+ desc: deprecated
user: core
<source>
- *: "Clear List & Play Next"
+ *: ""
</source>
<dest>
- *: "Cancella Lista e Riproduci Successivo"
+ *: ""
</dest>
<voice>
- *: "Cancella Lista e Riproduci Successivo"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -15550,14 +15527,17 @@
<source>
*: none
dac_power_mode: "DAC's power mode"
+ es9218: "DAC's output level"
</source>
<dest>
*: none
dac_power_mode: "Modalità di Alimentazione del DAC"
+ es9218: "Livello Uscita Del DAC"
</dest>
<voice>
*: none
dac_power_mode: "Modalità di Alimentazione del DAC"
+ es9218: "Livello Uscita Del DAC"
</voice>
</phrase>
<phrase>
@@ -15567,14 +15547,17 @@
<source>
*: none
dac_power_mode: "High performance"
+ es9218: "High Gain (2 Vrms)"
</source>
<dest>
*: none
dac_power_mode: "Alte Prestazioni"
+ es9218: "Alto Guadagno (2 Vrms)"
</dest>
<voice>
*: none
dac_power_mode: "Alte prestazioni"
+ es9218: "Alto Guadagno (2 Vrms)"
</voice>
</phrase>
<phrase>
@@ -15584,14 +15567,17 @@
<source>
*: none
dac_power_mode: "Save battery"
+ es9218: "Low Gain (1 Vrms)"
</source>
<dest>
*: none
dac_power_mode: "Risparmio batteria"
+ es9218: "Basso Guadagno (1 Vrms)"
</dest>
<voice>
*: none
dac_power_mode: "Risparmio batteria"
+ es9218: "Basso Guadagno (1 Vrms)"
</voice>
</phrase>
<phrase>
@@ -15652,16 +15638,16 @@
</phrase>
<phrase>
id: LANG_CLEAR_LIST_AND_PLAY_SHUFFLED
- desc: in onplay menu. Replace current playlist with selected tracks in random order.
+ desc: deprecated
user: core
<source>
- *: "Clear List & Play Shuffled"
+ *: ""
</source>
<dest>
- *: "Cancella Lista e Riproduci in Ordine Casuale"
+ *: ""
</dest>
<voice>
- *: "Cancella Lista e Riproduci in Ordine Casuale"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -15706,3 +15692,877 @@
*: "Sempre Blocco Automatico"
</voice>
</phrase>
+<phrase>
+ id: LANG_SINGLE_MODE
+ desc: single mode
+ user: core
+ <source>
+ *: "Single Mode"
+ </source>
+ <dest>
+ *: "Modo Singolo"
+ </dest>
+ <voice>
+ *: "Modo Singolo"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FILTER_SUPER_SLOW
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ filter_roll_off: "Super Slow"
+ </source>
+ <dest>
+ *: none
+ filter_roll_off: "Super Slow"
+ </dest>
+ <voice>
+ *: none
+ filter_roll_off: "Super Slow"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FILTER_LINEAR_FAST
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ es9218: "Linear Fast"
+ </source>
+ <dest>
+ *: none
+ es9218: "Linear Fast"
+ </dest>
+ <voice>
+ *: none
+ es9218: "Linear Fast"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FILTER_LINEAR_SLOW
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ es9218: "Linear Slow"
+ </source>
+ <dest>
+ *: none
+ es9218: "Linear Slow"
+ </dest>
+ <voice>
+ *: none
+ es9218: "Linear Slow"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FILTER_MINIMUM_FAST
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ es9218: "Minimum Fast"
+ </source>
+ <dest>
+ *: none
+ es9218: "Minimum Fast"
+ </dest>
+ <voice>
+ *: none
+ es9218: "Minimum Fast"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FILTER_MINIMUM_SLOW
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ es9218: "Minimum Slow"
+ </source>
+ <dest>
+ *: none
+ es9218: "Minimum Slow"
+ </dest>
+ <voice>
+ *: none
+ es9218: "Minimum Slow"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FILTER_APODIZING_1
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ es9218: "Apodizing type 1"
+ </source>
+ <dest>
+ *: none
+ es9218: "Apodizing type 1"
+ </dest>
+ <voice>
+ *: none
+ es9218: "Apodizing type 1"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FILTER_APODIZING_2
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ es9218: "Apodizing type 2"
+ </source>
+ <dest>
+ *: none
+ es9218: "Apodizing type 2"
+ </dest>
+ <voice>
+ *: none
+ es9218: "Apodizing type 2"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FILTER_HYBRID_FAST
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ es9218: "Hybrid Fast"
+ </source>
+ <dest>
+ *: none
+ es9218: "Hybrid Fast"
+ </dest>
+ <voice>
+ *: none
+ es9218: "Hybrid Fast"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FILTER_BRICK_WALL
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ es9218: "Brick Wall"
+ </source>
+ <dest>
+ *: none
+ es9218: "Brick Wall"
+ </dest>
+ <voice>
+ *: none
+ es9218: "Brick Wall"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PLAYLIST_RELOAD_AFTER_SAVE
+ desc: reload playlist after saving
+ user: core
+ <source>
+ *: "Reload After Saving"
+ </source>
+ <dest>
+ *: "Ricarica Dopo Il Salvataggio"
+ </dest>
+ <voice>
+ *: "Ricarica Dopo Il Salvataggio"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_ALBUMARTIST
+ desc: deprecated
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_GENRE
+ desc: deprecated
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_COMMENT
+ desc: deprecated
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_COMPOSER
+ desc: deprecated
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_YEAR
+ desc: deprecated
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_TRACKNUM
+ desc: deprecated
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_DISCNUM
+ desc: deprecated
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_FREQUENCY
+ desc: deprecated
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_BITRATE
+ desc: deprecated
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: VOICE_NUMERIC_TENS_SWAP_SEPARATOR
+ desc: voice only, for speaking numbers in languages that swap the tens and ones fields. Leave blank for languages that do not need it, such as English ("231" => "two hundred thirty one") but other languages may speak it as "two hundred one [AND] thirty"
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_VOICED_DATE_FORMAT
+ desc: format string for how dates will be read back. Y == 4-digit year, A == month name, m == numeric month, d == numeric day. For example, "AdY" will read "January 21 2021"
+ user: core
+ <source>
+ *: "dAY"
+ </source>
+ <dest>
+ *: "dmY"
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_LIST_WRAPAROUND
+ desc: in Settings
+ user: core
+ <source>
+ *: "List Wraparound"
+ </source>
+ <dest>
+ *: "Riepilogo Lista"
+ </dest>
+ <voice>
+ *: "Riepilogo Lista"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_SHOW_SHUTDOWN_MESSAGE
+ desc: in Settings
+ user: core
+ <source>
+ *: "Show Shutdown Message"
+ </source>
+ <dest>
+ *: "Mostra Messaggio Di Spegnimento"
+ </dest>
+ <voice>
+ *: "Mostra Messaggio Di Spegnimento"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_LIST_ORDER
+ desc: in Settings
+ user: core
+ <source>
+ *: "List Order"
+ </source>
+ <dest>
+ *: "Ordinamento Lista"
+ </dest>
+ <voice>
+ *: "Ordinamento Lista"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_ASCENDING
+ desc: in Settings
+ user: core
+ <source>
+ *: "Ascending"
+ </source>
+ <dest>
+ *: "Ascendente"
+ </dest>
+ <voice>
+ *: "Ascendente"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_DESCENDING
+ desc: in Settings
+ user: core
+ <source>
+ *: "Descending"
+ </source>
+ <dest>
+ *: "Discendente"
+ </dest>
+ <voice>
+ *: "Discendente"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_ALBUM_ART
+ desc: in Settings
+ user: core
+ <source>
+ *: "Album Art"
+ </source>
+ <dest>
+ *: "Album Art"
+ </dest>
+ <voice>
+ *: "Album Art"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PREFER_EMBEDDED
+ desc: in Settings
+ user: core
+ <source>
+ *: "Prefer Embedded"
+ </source>
+ <dest>
+ *: "Preferire Incorporato"
+ </dest>
+ <voice>
+ *: "Preferire Incorporato"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PREFER_IMAGE_FILE
+ desc: in Settings
+ user: core
+ <source>
+ *: "Prefer Image File"
+ </source>
+ <dest>
+ *: "Preferire File Immagine"
+ </dest>
+ <voice>
+ *: "Preferire File Immagine"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FM_SYNC_RDS_TIME
+ desc: in radio screen and Settings
+ user: core
+ <source>
+ *: none
+ rds: "Sync RDS Time"
+ </source>
+ <dest>
+ *: none
+ rds: "Sync Orario con RDS"
+ </dest>
+ <voice>
+ *: none
+ rds: "Sync Orario con RDS"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_SORT_ALBUMS_BY
+ desc: in Settings
+ user: core
+ <source>
+ *: "Sort albums by"
+ </source>
+ <dest>
+ *: "Ordina album per"
+ </dest>
+ <voice>
+ *: "Ordina album per"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_ARTIST_PLUS_NAME
+ desc: in Settings
+ user: core
+ <source>
+ *: "Artist + Name"
+ </source>
+ <dest>
+ *: "Artista + Nome"
+ </dest>
+ <voice>
+ *: "Artista E Nome"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_ARTIST_PLUS_YEAR
+ desc: in Settings
+ user: core
+ <source>
+ *: "Artist + Year"
+ </source>
+ <dest>
+ *: "Artista + Anno"
+ </dest>
+ <voice>
+ *: "Artista E Anno"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_YEAR_SORT_ORDER
+ desc: in Settings
+ user: core
+ <source>
+ *: "Year sort order"
+ </source>
+ <dest>
+ *: "Ordina per anno"
+ </dest>
+ <voice>
+ *: "Ordina per anno"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_SHOW_YEAR_IN_ALBUM_TITLE
+ desc: in Settings
+ user: core
+ <source>
+ *: "Show year in album title"
+ </source>
+ <dest>
+ *: "Mostra l'anno nel titolo dell'album"
+ </dest>
+ <voice>
+ *: "Mostra l'anno nel titolo dell'album"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_WAIT_FOR_CACHE
+ desc: in Settings
+ user: core
+ <source>
+ *: "Cache needs to finish updating first!"
+ </source>
+ <dest>
+ *: "La cache deve prima completare l'aggiornamento!"
+ </dest>
+ <voice>
+ *: "La cache deve prima completare l'aggiornamento!"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_TRACK_INFO
+ desc: Track Info Title
+ user: core
+ <source>
+ *: "Track Info"
+ </source>
+ <dest>
+ *: "Info Traccia"
+ </dest>
+ <voice>
+ *: "Info Traccia"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PLAY
+ desc: play selected file/directory, in playlist context menu
+ user: core
+ <source>
+ *: "Play"
+ </source>
+ <dest>
+ *: "Riproduci"
+ </dest>
+ <voice>
+ *: "Riproduci"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PLAY_SHUFFLED
+ desc: play selected files in shuffled order, in playlist context menu
+ user: core
+ <source>
+ *: "Play Shuffled"
+ </source>
+ <dest>
+ *: "Riproduci Casualmente"
+ </dest>
+ <voice>
+ *: "Riproduci Casualmente"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_KEEP_CURRENT_TRACK_ON_REPLACE
+ desc: used in the playlist settings menu
+ user: core
+ <source>
+ *: "Keep Current Track When Replacing Playlist"
+ </source>
+ <dest>
+ *: "Mantieni La Traccia Corrente Quando Sostituisci La Playlist"
+ </dest>
+ <voice>
+ *: "Mantieni La Traccia Corrente Quando Sostituisci La Playlist"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_CLEAR_SETTINGS_ON_HOLD
+ desc: in the system sub menu
+ user: core
+ <source>
+ *: none
+ clear_settings_on_hold,iriverh10: "Clear settings when reset button is held during startup"
+ ipod4g,ipodcolor,ipodmini1g,ipodmini2g,ipodnano1g,ipodvideo: "Clear settings when hold switch is on during startup"
+ </source>
+ <dest>
+ *: none
+ clear_settings_on_hold,iriverh10: "Cancella le impostazioni quando si tiene premuto il pulsante reset durante l'avvio"
+ ipod4g,ipodcolor,ipodmini1g,ipodmini2g,ipodnano1g,ipodvideo: "Cancella le impostazioni quando l'interruttore di blocco è attivo durante l'avvio"
+ </dest>
+ <voice>
+ *: none
+ clear_settings_on_hold,iriverh10: "Cancella le impostazioni quando si tiene premuto il pulsante reset durante l'avvio"
+ ipod4g,ipodcolor,ipodmini1g,ipodmini2g,ipodnano1g,ipodvideo: "Cancella le impostazioni quando l'interruttore di blocco è attivo durante l'avvio"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_REWIND_ACROSS_TRACKS
+ desc: in playback settings menu
+ user: core
+ <source>
+ *: "Rewind Across Tracks"
+ </source>
+ <dest>
+ *: "Riavvolgi tra le tracce"
+ </dest>
+ <voice>
+ *: "Riavvolgi tra le tracce"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_SET_AS
+ desc: used in the onplay menu
+ user: core
+ <source>
+ *: "Set As..."
+ </source>
+ <dest>
+ *: "Imposta Come..."
+ </dest>
+ <voice>
+ *: "Imposta Come..."
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PLAYLIST_DIR
+ desc: used in the onplay menu
+ user: core
+ <source>
+ *: "Playlist Directory"
+ </source>
+ <dest>
+ *: "Cartella Playlist"
+ </dest>
+ <voice>
+ *: "Cartella Playlist"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_START_DIR
+ desc: used in the onplay menu
+ user: core
+ <source>
+ *: "Start Directory"
+ </source>
+ <dest>
+ *: "Cartella Iniziale"
+ </dest>
+ <voice>
+ *: "Cartella Iniziale"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_RECORDING_DIR
+ desc: used in the onplay menu
+ user: core
+ <source>
+ *: none
+ recording: "Recording Directory"
+ </source>
+ <dest>
+ *: none
+ recording: "Cartella Di Registrazione"
+ </dest>
+ <voice>
+ *: none
+ recording: "Cartella Di Registrazione"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_ADD_TO_PL
+ desc: used in the onplay menu
+ user: core
+ <source>
+ *: "Add to Playlist..."
+ </source>
+ <dest>
+ *: "Aggiungi alla Playlist..."
+ </dest>
+ <voice>
+ *: "Aggiungi alla Playlist..."
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_ADD_TO_EXISTING_PL
+ desc: used in the onplay menu
+ user: core
+ <source>
+ *: "Add to Existing Playlist"
+ </source>
+ <dest>
+ *: "Aggiungi alla Playlist Esistente"
+ </dest>
+ <voice>
+ *: "Aggiungi alla Playlist Esistente"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PLAYING_NEXT
+ desc: used in the onplay menu
+ user: core
+ <source>
+ *: "Playing Next..."
+ </source>
+ <dest>
+ *: "Riproduzione Brano Successivo..."
+ </dest>
+ <voice>
+ *: "Riproduzione Brano Successivo..."
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PLAY_NEXT
+ desc: used in the onplay menu
+ user: core
+ <source>
+ *: "Play Next"
+ </source>
+ <dest>
+ *: "Riproduci Successivo"
+ </dest>
+ <voice>
+ *: "Riproduci Successivo"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_ADD_SHUFFLED
+ desc: used in the onplay menu
+ user: core
+ <source>
+ *: "Add Shuffled"
+ </source>
+ <dest>
+ *: "Aggiungi Casualmente"
+ </dest>
+ <voice>
+ *: "Aggiungi Casualmente"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PLAY_LAST
+ desc: used in the onplay menu
+ user: core
+ <source>
+ *: "Play Last"
+ </source>
+ <dest>
+ *: "Riproduci Ultimo"
+ </dest>
+ <voice>
+ *: "Riproduci Ultimo"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PLAY_LAST_SHUFFLED
+ desc: used in the onplay menu
+ user: core
+ <source>
+ *: "Play Last Shuffled"
+ </source>
+ <dest>
+ *: "Riproduci Ultimo Casualmente"
+ </dest>
+ <voice>
+ *: "Riproduci Ultimo Casualmente"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_VOLUME_ADJUST_MODE
+ desc: in system settings
+ user: core
+ <source>
+ *: none
+ perceptual_volume: "Volume Adjustment Mode"
+ </source>
+ <dest>
+ *: none
+ perceptual_volume: "Modalità Aggiustamento Volume"
+ </dest>
+ <voice>
+ *: none
+ perceptual_volume: "Modalità Aggiustamento Volume"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_VOLUME_ADJUST_NORM_STEPS
+ desc: in system settings
+ user: core
+ <source>
+ *: none
+ perceptual_volume: "Number of Volume Steps"
+ </source>
+ <dest>
+ *: none
+ perceptual_volume: "Numero di Passi per il Volume"
+ </dest>
+ <voice>
+ *: none
+ perceptual_volume: "Numero di Passi per il Volume"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PERCEPTUAL
+ desc: in system settings -> volume adjustment mode
+ user: core
+ <source>
+ *: none
+ perceptual_volume: "Perceptual"
+ </source>
+ <dest>
+ *: none
+ perceptual_volume: "Percettivo"
+ </dest>
+ <voice>
+ *: none
+ perceptual_volume: "Percettivo"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_SHOW_TRACKS_WHILE_BROWSING
+ desc: in PictureFlow Main Menu
+ user: core
+ <source>
+ *: "Show Tracks While Browsing"
+ </source>
+ <dest>
+ *: "Mostra tracce durante la navigazione"
+ </dest>
+ <voice>
+ *: "Mostra tracce durante la navigazione"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_GOTO_LAST_ALBUM
+ desc: in PictureFlow Main Menu
+ user: core
+ <source>
+ *: "Go to Last Album"
+ </source>
+ <dest>
+ *: "Vai all'Ultimo Album"
+ </dest>
+ <voice>
+ *: "Vai all'Ultimo Album"
+ </voice>
+</phrase>
diff --git a/apps/lang/japanese.lang b/apps/lang/japanese.lang
index 4961753dc1..9b829ceb56 100644
--- a/apps/lang/japanese.lang
+++ b/apps/lang/japanese.lang
@@ -254,7 +254,7 @@
*: "PLAY = Yes"
archosplayer: "(PLAY/STOP)"
cowond2*: "MENU, or top-right = Yes"
- creativezen*: "Select = Yes"
+ creativezen*: "SELECT = Yes"
gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT = Yes"
iriverh100,iriverh120,iriverh300: "NAVI = Yes"
mrobe500: "PLAY, POWER, or top-right = Yes"
@@ -264,7 +264,7 @@
*: "PLAY = ã¯ã„"
archosplayer: "(PLAY/STOP)"
cowond2*: "MENU ã¾ãŸã¯ top-right = ã¯ã„"
- creativezen*: "Select = ã¯ã„"
+ creativezen*: "SELECT = ã¯ã„"
gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT = ã¯ã„"
iriverh100,iriverh120,iriverh300: "NAVI = ã¯ã„"
mrobe500: "PLAY ã¾ãŸã¯ POWER, top-right = ã¯ã„"
@@ -5271,20 +5271,17 @@
</voice>
</phrase>
<phrase>
- id: LANG_RECORDING_FORMAT
- desc: audio format item in recording menu
+ id: LANG_FORMAT
+ desc: audio format
user: core
<source>
- *: none
- recording: "Format"
+ *: "Format"
</source>
<dest>
- *: none
- recording: "フォーマット"
+ *: "フォーマット"
</dest>
<voice>
- *: none
- recording: "フォーマット"
+ *: "フォーマット"
</voice>
</phrase>
<phrase>
@@ -10319,15 +10316,15 @@
user: core
<source>
*: none
- gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
</source>
<dest>
*: none
- gigabeatfx,sansafuzeplus: "タッãƒãƒ‘ッド感度"
+ fiiom3k,gigabeatfx,sansafuzeplus: "タッãƒãƒ‘ッド感度"
</dest>
<voice>
*: none
- gigabeatfx,sansafuzeplus: "タッãƒãƒ‘ッド感度"
+ fiiom3k,gigabeatfx,sansafuzeplus: "タッãƒãƒ‘ッド感度"
</voice>
</phrase>
<phrase>
diff --git a/apps/lang/korean.lang b/apps/lang/korean.lang
index a02b97749f..3656675b19 100644
--- a/apps/lang/korean.lang
+++ b/apps/lang/korean.lang
@@ -8283,20 +8283,17 @@
</voice>
</phrase>
<phrase>
- id: LANG_RECORDING_FORMAT
- desc: audio format item in recording menu
+ id: LANG_FORMAT
+ desc: audio format
user: core
<source>
- *: none
- recording: "Format"
+ *: "Format"
</source>
<dest>
- *: none
- recording: "íŒŒì¼ í˜•ì‹"
+ *: "íŒŒì¼ í˜•ì‹"
</dest>
<voice>
- *: none
- recording: "íŒŒì¼ í˜•ì‹"
+ *: "íŒŒì¼ í˜•ì‹"
</voice>
</phrase>
<phrase>
@@ -9139,15 +9136,15 @@
user: core
<source>
*: none
- gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
</source>
<dest>
*: none
- gigabeatfx,sansafuzeplus: "터치패드 ê°ë„"
+ fiiom3k,gigabeatfx,sansafuzeplus: "터치패드 ê°ë„"
</dest>
<voice>
*: none
- gigabeatfx,sansafuzeplus: "터치패드 ê°ë„"
+ fiiom3k,gigabeatfx,sansafuzeplus: "터치패드 ê°ë„"
</voice>
</phrase>
<phrase>
diff --git a/apps/lang/latviesu.lang b/apps/lang/latviesu.lang
index c6ecd20cac..d43109eed2 100644
--- a/apps/lang/latviesu.lang
+++ b/apps/lang/latviesu.lang
@@ -5262,20 +5262,17 @@
</voice>
</phrase>
<phrase>
- id: LANG_RECORDING_FORMAT
- desc: audio format item in recording menu
+ id: LANG_FORMAT
+ desc: audio format
user: core
<source>
- *: none
- recording: "Format"
+ *: "Format"
</source>
<dest>
- *: none
- recording: "FormÄts"
+ *: "FormÄts"
</dest>
<voice>
- *: none
- recording: "formaats"
+ *: "formaats"
</voice>
</phrase>
<phrase>
@@ -10310,11 +10307,11 @@
user: core
<source>
*: none
- gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
</source>
<dest>
*: none
- gigabeatfx,sansafuzeplus: "SkÄrienvirsmas JÅ«tÄ«ba"
+ fiiom3k,gigabeatfx,sansafuzeplus: "SkÄrienvirsmas JÅ«tÄ«ba"
</dest>
<voice>
*: none
diff --git a/apps/lang/lietuviu.lang b/apps/lang/lietuviu.lang
index e2c661255e..51aece5107 100644
--- a/apps/lang/lietuviu.lang
+++ b/apps/lang/lietuviu.lang
@@ -5338,20 +5338,17 @@
</voice>
</phrase>
<phrase>
- id: LANG_RECORDING_FORMAT
- desc: audio format item in recording menu
+ id: LANG_FORMAT
+ desc: audio format
user: core
<source>
- *: none
- recording: "Format"
+ *: "Format"
</source>
<dest>
- *: none
- recording: "Formatas"
+ *: "Formatas"
</dest>
<voice>
- *: none
- recording: "Formatas"
+ *: "Formatas"
</voice>
</phrase>
<phrase>
@@ -10386,15 +10383,15 @@
user: core
<source>
*: none
- gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
</source>
<dest>
*: none
- gigabeatfx,sansafuzeplus: "Jautrumas lietimui"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Jautrumas lietimui"
</dest>
<voice>
*: none
- gigabeatfx,sansafuzeplus: "Jautrumas lietimui"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Jautrumas lietimui"
</voice>
</phrase>
<phrase>
diff --git a/apps/lang/magyar.lang b/apps/lang/magyar.lang
index 96e5833e8e..d6a8df5d62 100644
--- a/apps/lang/magyar.lang
+++ b/apps/lang/magyar.lang
@@ -140,7 +140,7 @@
*: "Betöltés ..."
</dest>
<voice>
- *: "Loading"
+ *: "Betöltés"
</voice>
</phrase>
<phrase>
@@ -154,7 +154,7 @@
*: "Betöltés... %d%% kész (%s)"
</dest>
<voice>
- *: ""
+ *: "Betöltés"
</voice>
</phrase>
<phrase>
@@ -210,7 +210,7 @@
*: "nem sikerült"
</dest>
<voice>
- *: ""
+ *: "nem sikerült"
</voice>
</phrase>
<phrase>
@@ -247,7 +247,6 @@
user: core
<source>
*: "PLAY = Yes"
- archosplayer: "(PLAY/STOP)"
cowond2*: "MENU = Yes"
gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT = Yes"
iriverh100,iriverh120,iriverh300: "NAVI = Yes"
@@ -255,7 +254,6 @@
</source>
<dest>
*: "PLAY = Igen"
- archosplayer: "(PLAY/STOP)"
cowond2*: "MENU = Igen"
gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT = Igen"
iriverh100,iriverh120,iriverh300: "NAVI = Igen"
@@ -271,15 +269,12 @@
user: core
<source>
*: "Any Other = No"
- archosplayer: none
</source>
<dest>
*: "Egyéb = Mégsem"
- archosplayer: none
</dest>
<voice>
*: ""
- archosplayer: none
</voice>
</phrase>
<phrase>
@@ -436,10 +431,10 @@
*: "Plugins"
</source>
<dest>
- *: "Pluginok"
+ *: "Kiegészítők"
</dest>
<voice>
- *: "Plugins"
+ *: "Kiegészítők"
</voice>
</phrase>
<phrase>
@@ -464,10 +459,10 @@
*: "Select Bookmark"
</source>
<dest>
- *: "Könyvjelző választása"
+ *: "Könyvjelző kiválasztása"
</dest>
<voice>
- *: "Select Bookmark"
+ *: "Könyvjelző kiválasztása"
</voice>
</phrase>
<phrase>
@@ -607,7 +602,7 @@
*: "Nincs könyvjelző"
</dest>
<voice>
- *: ""
+ *: "Nincs könyvjelző"
</voice>
</phrase>
<phrase>
@@ -646,10 +641,10 @@
*: "Bass"
</source>
<dest>
- *: "Mély"
+ *: "Mély kiemelés"
</dest>
<voice>
- *: "Bass"
+ *: "Mély kiemelés"
</voice>
</phrase>
<phrase>
@@ -660,10 +655,10 @@
*: "Treble"
</source>
<dest>
- *: "Magas"
+ *: "Magas kiemelés"
</dest>
<voice>
- *: "Treble"
+ *: "Magas kiemelés"
</voice>
</phrase>
<phrase>
@@ -674,10 +669,10 @@
*: "Balance"
</source>
<dest>
- *: "Balance"
+ *: "Balansz"
</dest>
<voice>
- *: "Balance"
+ *: "Balansz"
</voice>
</phrase>
<phrase>
@@ -800,10 +795,10 @@
*: "Crossfeed"
</source>
<dest>
- *: "Crossfeed"
+ *: "Térakusztika fejhallgatón (crossfeed)"
</dest>
<voice>
- *: "Crossfeed"
+ *: "Térakusztika fejhallgatón"
</voice>
</phrase>
<phrase>
@@ -996,10 +991,10 @@
*: "%d Hz Band Gain"
</source>
<dest>
- *: "%d Hz sáv módosítás"
+ *: "%d Hz sáv nyereség"
</dest>
<voice>
- *: ""
+ *: "Hz sáv nyereség"
</voice>
</phrase>
<phrase>
@@ -1010,10 +1005,10 @@
*: "Low Shelf Filter"
</source>
<dest>
- *: "Alsó perem szűrő"
+ *: "Alsó szűrő határa"
</dest>
<voice>
- *: ""
+ *: "Alsó szűrő határa"
</voice>
</phrase>
<phrase>
@@ -1027,7 +1022,7 @@
*: "Csúcsszűrő %d"
</dest>
<voice>
- *: ""
+ *: "Csúcsszűrő"
</voice>
</phrase>
<phrase>
@@ -1038,10 +1033,10 @@
*: "High Shelf Filter"
</source>
<dest>
- *: "Felső perem szűrő"
+ *: "Felső szűrő határa"
</dest>
<voice>
- *: ""
+ *: "Felső szűrő határa"
</voice>
</phrase>
<phrase>
@@ -1094,163 +1089,10 @@
*: "Dithering"
</source>
<dest>
- *: "Dithering"
- </dest>
- <voice>
- *: "Dithering"
- </voice>
-</phrase>
-<phrase>
- id: LANG_LOUDNESS
- desc: in sound_settings
- user: core
- <source>
- *: none
- masf: "Loudness"
- </source>
- <dest>
- *: none
- masf: "Loudness"
- </dest>
- <voice>
- *: none
- masf: "Loudness"
- </voice>
-</phrase>
-<phrase>
- id: LANG_AUTOVOL
- desc: in sound_settings
- user: core
- <source>
- *: none
- masf: "Auto Volume"
- </source>
- <dest>
- *: none
- masf: "Automatikus hangerő"
- </dest>
- <voice>
- *: none
- masf: "Auto Volume"
- </voice>
-</phrase>
-<phrase>
- id: LANG_DECAY
- desc: in sound_settings
- user: core
- <source>
- *: none
- masf: "AV Decay Time"
- </source>
- <dest>
- *: none
- masf: "Csökkentés átlagos ideje"
- </dest>
- <voice>
- *: none
- masf: ""
- </voice>
-</phrase>
-<phrase>
- id: LANG_SUPERBASS
- desc: in sound settings
- user: core
- <source>
- *: none
- masf: "Super Bass"
- </source>
- <dest>
- *: none
- masf: "Super Bass"
- </dest>
- <voice>
- *: none
- masf: "Super bass"
- </voice>
-</phrase>
-<phrase>
- id: LANG_MDB_ENABLE
- desc: in sound settings
- user: core
- <source>
- *: none
- masf: "MDB Enable"
- </source>
- <dest>
- *: none
- masf: "MDB bekapcsolása"
+ *: "Zajmoduláció (dithering)"
</dest>
<voice>
- *: none
- masf: "MDB Enable"
- </voice>
-</phrase>
-<phrase>
- id: LANG_MDB_STRENGTH
- desc: in sound settings
- user: core
- <source>
- *: none
- masf: "MDB Strength"
- </source>
- <dest>
- *: none
- masf: "MDB erőssége"
- </dest>
- <voice>
- *: none
- masf: "MDB Strength"
- </voice>
-</phrase>
-<phrase>
- id: LANG_MDB_HARMONICS
- desc: in sound settings
- user: core
- <source>
- *: none
- masf: "MDB Harmonics"
- </source>
- <dest>
- *: none
- masf: "MDB felhangok"
- </dest>
- <voice>
- *: none
- masf: "MDB Harmonics"
- </voice>
-</phrase>
-<phrase>
- id: LANG_MDB_CENTER
- desc: in sound settings
- user: core
- <source>
- *: none
- masf: "MDB Centre Frequency"
- </source>
- <dest>
- *: none
- masf: "MDB középfrekvencia"
- </dest>
- <voice>
- *: none
- masf: "MDB Center frequency"
- </voice>
-</phrase>
-<phrase>
- id: LANG_MDB_SHAPE
- desc: in sound settings
- user: core
- <source>
- *: none
- masf: "MDB Shape"
- </source>
- <dest>
- *: none
- masf: "MDB forma"
- </dest>
- <voice>
- *: none
- masf: "MDB Shape"
+ *: "Zajmoduláció"
</voice>
</phrase>
<phrase>
@@ -2405,7 +2247,8 @@
hold_button: "Világítás (Hold gomb lenyomásakor)"
</dest>
<voice>
- *: "Backlight on hold"
+ *: none
+ hold_button: "Világítás (Hold gomb lenyomásakor)"
</voice>
</phrase>
<phrase>
@@ -2488,23 +2331,6 @@
</voice>
</phrase>
<phrase>
- id: LANG_NEVER
- desc: in lcd settings
- user: core
- <source>
- *: none
- lcd_sleep: "Never"
- </source>
- <dest>
- *: none
- lcd_sleep: "Soha"
- </dest>
- <voice>
- *: none
- lcd_sleep: "Never"
- </voice>
-</phrase>
-<phrase>
id: LANG_BRIGHTNESS
desc: in settings_menu
user: core
@@ -2656,7 +2482,7 @@
</dest>
<voice>
*: none
- ldc_non-mono: "Background Colour"
+ lcd_color: "Background Colour"
</voice>
</phrase>
<phrase>
@@ -3026,23 +2852,6 @@
</voice>
</phrase>
<phrase>
- id: LANG_BUTTON_BAR
- desc: in settings menu
- user: core
- <source>
- *: none
- recorder_pad: "Button Bar"
- </source>
- <dest>
- *: none
- recorder_pad: "Kapcsoló sor"
- </dest>
- <voice>
- *: none
- recorder_pad: "Button bar"
- </voice>
-</phrase>
-<phrase>
id: LANG_VOLUME_DISPLAY
desc: Volume type title
user: core
@@ -3095,7 +2904,7 @@
*: "Számmal"
</dest>
<voice>
- *: "Numeric"
+ *: "Számmal"
</voice>
</phrase>
<phrase>
@@ -3104,15 +2913,12 @@
user: core
<source>
*: "Peak Meter"
- masd: none
</source>
<dest>
*: "Kivezérlés mutató"
- masd: none
</dest>
<voice>
- *: "Peak Meter"
- masd: none
+ *: "Kivezérlés mutató"
</voice>
</phrase>
<phrase>
@@ -3121,15 +2927,12 @@
user: core
<source>
*: "Clip Hold Time"
- masd: none
</source>
<dest>
*: "Clip Hold Time"
- masd: none
</dest>
<voice>
*: "Clip Hold Time"
- masd: none
</voice>
</phrase>
<phrase>
@@ -3138,15 +2941,12 @@
user: core
<source>
*: "Peak Hold Time"
- masd: none
</source>
<dest>
*: "Peak Hold Time"
- masd: none
</dest>
<voice>
*: "Peak Hold Time"
- masd: none
</voice>
</phrase>
<phrase>
@@ -3155,15 +2955,12 @@
user: core
<source>
*: "Eternal"
- masd: none
</source>
<dest>
*: "Végtelen"
- masd: none
</dest>
<voice>
- *: "Eternal"
- masd: none
+ *: "Végtelen"
</voice>
</phrase>
<phrase>
@@ -3172,15 +2969,12 @@
user: core
<source>
*: "Peak Release"
- masd: none
</source>
<dest>
*: "Peak Release"
- masd: none
</dest>
<voice>
*: "Peak Release"
- masd: none
</voice>
</phrase>
<phrase>
@@ -3189,15 +2983,12 @@
user: core
<source>
*: "Scale"
- masd: none
</source>
<dest>
*: "Skála"
- masd: none
</dest>
<voice>
- *: "Scale"
- masd: none
+ *: "Skála"
</voice>
</phrase>
<phrase>
@@ -3206,15 +2997,12 @@
user: core
<source>
*: "Logarithmic (dB)"
- masd: none
</source>
<dest>
*: "Logaritmikus (dB)"
- masd: none
</dest>
<voice>
- *: "Logarithmic decibel"
- masd: none
+ *: "Logaritmikus decibel"
</voice>
</phrase>
<phrase>
@@ -3223,15 +3011,12 @@
user: core
<source>
*: "Linear (%)"
- masd: none
</source>
<dest>
*: "Lineáris (%)"
- masd: none
</dest>
<voice>
*: "Linear percent"
- masd: none
</voice>
</phrase>
<phrase>
@@ -3240,15 +3025,12 @@
user: core
<source>
*: "Minimum Of Range"
- masd: none
</source>
<dest>
*: "Legkisebb érték"
- masd: none
</dest>
<voice>
- *: "Minimum Of Range"
- masd: none
+ *: "Legkisebb érték"
</voice>
</phrase>
<phrase>
@@ -3257,15 +3039,12 @@
user: core
<source>
*: "Maximum Of Range"
- masd: none
</source>
<dest>
*: "Legnagyobb érték"
- masd: none
</dest>
<voice>
- *: "Maximum Of Range"
- masd: none
+ *: "Legnagyobb érték"
</voice>
</phrase>
<phrase>
@@ -3480,8 +3259,8 @@
</dest>
<voice>
*: none
- button_light: "Button Light Timeout"
- sansae200*,sansafuze*: "Wheel Light Timeout"
+ button_light: "Gomb világításának hossza"
+ sansae200*,sansafuze*: "Kerék világításának hossza"
</voice>
</phrase>
<phrase>
@@ -3498,7 +3277,7 @@
</dest>
<voice>
*: none
- gigabeatfx: "Button Light Brightness"
+ buttonlight_brightness: "Gomb-világítás erőssége"
</voice>
</phrase>
<phrase>
@@ -3595,14 +3374,17 @@
<source>
*: none
battery_types: "Alkaline"
+ xduoox3: "Newer (2000 mAh)"
</source>
<dest>
*: none
battery_types: "Alkáli"
+ xduoox3: "Újabb (2000 mAh)"
</dest>
<voice>
*: none
battery_types: "Alkaline"
+ xduoox3: "Újabb 2000 m A h"
</voice>
</phrase>
<phrase>
@@ -3612,14 +3394,17 @@
<source>
*: none
battery_types: "NiMH"
+ xduoox3: "Older (1500 mAh)"
</source>
<dest>
*: none
battery_types: "NiMH"
+ xduoox3: "Régebbi (1500 mAh)"
</dest>
<voice>
*: none
battery_types: "Nickel metal hydride"
+ xduoox3: "Régebbi 1500 m A h"
</voice>
</phrase>
<phrase>
@@ -4418,20 +4203,6 @@
</voice>
</phrase>
<phrase>
- id: LANG_BOOKMARK_SETTINGS_UNIQUE_ONLY
- desc: Save only on bookmark for each playlist in recent bookmarks
- user: core
- <source>
- *: "Unique only"
- </source>
- <dest>
- *: "Unique only"
- </dest>
- <voice>
- *: "Unique only"
- </voice>
-</phrase>
-<phrase>
id: LANG_LANGUAGE
desc: in settings_menu
user: core
@@ -4453,10 +4224,10 @@
*: "New Language"
</source>
<dest>
- *: "Új nyelv"
+ *: "Nyelv beállítva"
</dest>
<voice>
- *: "New Language"
+ *: "Nyelv beállítva"
</voice>
</phrase>
<phrase>
@@ -4467,10 +4238,10 @@
*: "Voice"
</source>
<dest>
- *: "Hangok"
+ *: "Beszéd"
</dest>
<voice>
- *: "Voice"
+ *: "Beszéd"
</voice>
</phrase>
<phrase>
@@ -4551,10 +4322,10 @@
*: "Numbers"
</source>
<dest>
- *: "számok"
+ *: "Számok"
</dest>
<voice>
- *: "Numbers"
+ *: "Számok"
</voice>
</phrase>
<phrase>
@@ -4565,10 +4336,10 @@
*: "Spell"
</source>
<dest>
- *: "betűz"
+ *: "Betűz"
</dest>
<voice>
- *: "Spell"
+ *: "Betűz"
</voice>
</phrase>
<phrase>
@@ -4847,57 +4618,6 @@
</voice>
</phrase>
<phrase>
- id: LANG_BUTTONBAR_MENU
- desc: in button bar
- user: core
- <source>
- *: none
- radio_screen_button_bar: "Menu"
- </source>
- <dest>
- *: none
- radio_screen_button_bar: "Menü"
- </dest>
- <voice>
- *: none
- radio_screen_button_bar: ""
- </voice>
-</phrase>
-<phrase>
- id: LANG_FM_BUTTONBAR_EXIT
- desc: in radio screen
- user: core
- <source>
- *: none
- radio_screen_button_bar: "Exit"
- </source>
- <dest>
- *: none
- radio_screen_button_bar: "Kilépés"
- </dest>
- <voice>
- *: none
- radio_screen_button_bar: ""
- </voice>
-</phrase>
-<phrase>
- id: LANG_FM_BUTTONBAR_ACTION
- desc: in radio screen
- user: core
- <source>
- *: none
- radio_screen_button_bar: "Action"
- </source>
- <dest>
- *: none
- radio_screen_button_bar: "Művelet"
- </dest>
- <voice>
- *: none
- radio_screen_button_bar: ""
- </voice>
-</phrase>
-<phrase>
id: LANG_PRESET
desc: in button bar and radio screen / menu
user: core
@@ -4911,41 +4631,7 @@
</dest>
<voice>
*: none
- radio: ""
- </voice>
-</phrase>
-<phrase>
- id: LANG_FM_BUTTONBAR_ADD
- desc: in radio screen
- user: core
- <source>
- *: none
- radio_screen_button_bar: "Add"
- </source>
- <dest>
- *: none
- radio_screen_button_bar: "Hozzáad"
- </dest>
- <voice>
- *: none
- radio_screen_button_bar: ""
- </voice>
-</phrase>
-<phrase>
- id: LANG_FM_BUTTONBAR_RECORD
- desc: in radio screen
- user: core
- <source>
- *: none
- radio_screen_button_bar: "Record"
- </source>
- <dest>
- *: none
- radio_screen_button_bar: "Felvétel"
- </dest>
- <voice>
- *: none
- radio_screen_button_bar: ""
+ radio: "Beállított állomás"
</voice>
</phrase>
<phrase>
@@ -5060,11 +4746,11 @@
</source>
<dest>
*: none
- radio: "Keresés"
+ radio: "Hangolás"
</dest>
<voice>
*: none
- radio: ""
+ radio: "Hangolás"
</voice>
</phrase>
<phrase>
@@ -5195,11 +4881,12 @@
radio: "US / Canada"
</source>
<dest>
- *: "Észak-Amerika"
+ *: none
+ radio: "Észak-Amerika"
</dest>
<voice>
*: none
- radio: "US and Canada"
+ radio: "USA / Kama"
</voice>
</phrase>
<phrase>
@@ -5237,20 +4924,17 @@
</voice>
</phrase>
<phrase>
- id: LANG_RECORDING_FORMAT
- desc: audio format item in recording menu
+ id: LANG_FORMAT
+ desc: audio format
user: core
<source>
- *: none
- recording: "Format"
+ *: "Format"
</source>
<dest>
- *: none
- recording: "Formátum"
+ *: "Formátum"
</dest>
<voice>
- *: none
- recording: "Format"
+ *: "Format"
</voice>
</phrase>
<phrase>
@@ -5280,7 +4964,7 @@
</source>
<dest>
*: none
- recording: "wav"
+ recording: "WAV"
</dest>
<voice>
*: none
@@ -5373,23 +5057,6 @@
</voice>
</phrase>
<phrase>
- id: LANG_RECORDING_QUALITY
- desc: in the recording settings
- user: core
- <source>
- *: none
- recording_hwcodec: "Quality"
- </source>
- <dest>
- *: none
- recording_hwcodec: "Minőség"
- </dest>
- <voice>
- *: none
- recording_hwcodec: "Quality"
- </voice>
-</phrase>
-<phrase>
id: LANG_FREQUENCY
desc: in recording and playback settings
user: core
@@ -5403,7 +5070,7 @@
</dest>
<voice>
*: none
- play_frequency,recording: "Frequency"
+ play_frequency,recording: "Mintavétel"
</voice>
</phrase>
<phrase>
@@ -5456,8 +5123,8 @@
</dest>
<voice>
*: none
- iriverh100,iriverh120,iriverh300: "Internal Microphone"
- recording: "Microphone"
+ iriverh100,iriverh120,iriverh300: "Belső mikrofon"
+ recording: "Mikrofon"
</voice>
</phrase>
<phrase>
@@ -5487,28 +5154,11 @@
</source>
<dest>
*: none
- recording: "Line In"
- </dest>
- <voice>
- *: none
- recording: "Line In"
- </voice>
-</phrase>
-<phrase>
- id: LANG_RECORDING_EDITABLE
- desc: Editable recordings setting
- user: core
- <source>
- *: none
- recording_hwcodec: "Independent Frames"
- </source>
- <dest>
- *: none
- recording_hwcodec: "Független mp3-keretek"
+ recording: "Vonalbemenet"
</dest>
<voice>
*: none
- recording_hwcodec: "Independent frames"
+ recording: "Vonalbemenet"
</voice>
</phrase>
<phrase>
@@ -5848,7 +5498,7 @@
</dest>
<voice>
*: none
- recordng: "New file"
+ recording: "Új fájl"
</voice>
</phrase>
<phrase>
@@ -6283,23 +5933,6 @@
</voice>
</phrase>
<phrase>
- id: LANG_SHUTDOWN
- desc: in main menu
- user: core
- <source>
- *: none
- soft_shutdown: "Shut down"
- </source>
- <dest>
- *: none
- soft_shutdown: "Kikapcsol"
- </dest>
- <voice>
- *: none
- soft_shutdown: "Shut down"
- </voice>
-</phrase>
-<phrase>
id: LANG_ROCKBOX_INFO
desc: displayed topmost on the info screen and in the info menu
user: core
@@ -6310,7 +5943,7 @@
*: "Rockbox névjegy"
</dest>
<voice>
- *: "Rockbox Info"
+ *: "Rockbox névjegy"
</voice>
</phrase>
<phrase>
@@ -6319,14 +5952,12 @@
user: core
<source>
*: "Buffer:"
- archosplayer: "Buf:"
</source>
<dest>
*: "Puffer:"
- archosplayer: "Puf:"
</dest>
<voice>
- *: ""
+ *: "Puffer:"
</voice>
</phrase>
<phrase>
@@ -6398,21 +6029,19 @@
user: core
<source>
*: none
- archosondio*: "MMC:"
multivolume: "HD1"
sansac200*,sansaclipplus,sansae200*,sansafuze*: "mSD:"
xduoox3: "mSD2:"
</source>
<dest>
*: none
- archosondio*: "MMC:"
multivolume: "HD1"
sansac200*,sansaclipplus,sansae200*,sansafuze*: "mSD"
xduoox3: "mSD2:"
</dest>
<voice>
*: none
- archosondio*: "Multimedia card"
+ multivolume: "H D 1"
sansac200*,sansaclipplus,sansae200*,sansafuze*: "micro Secure Digital card:"
xduoox3: "micro Secure Digital card 2:"
</voice>
@@ -7477,15 +7106,12 @@
user: core
<source>
*: "End of Song List"
- archosplayer: "End of List"
</source>
<dest>
*: "Vége a számok listájának"
- archosplayer: "Lista vége"
</dest>
<voice>
*: "End of Song List"
- archosplayer: "End of List"
</voice>
</phrase>
<phrase>
@@ -7729,41 +7355,7 @@
</dest>
<voice>
*: none
- recording: ""
- </voice>
-</phrase>
-<phrase>
- id: LANG_CONFIRM_SHUTDOWN
- desc: in shutdown screen
- user: core
- <source>
- *: none
- soft_shutdown: "Press OFF to shut down"
- </source>
- <dest>
- *: none
- soft_shutdown: "OFF - Kikapcsolás"
- </dest>
- <voice>
- *: none
- soft_shutdown: ""
- </voice>
-</phrase>
-<phrase>
- id: LANG_REMOVE_MMC
- desc: before acknowledging usb in case an MMC is inserted (Ondio)
- user: core
- <source>
- *: none
- archosondio*: "Please remove inserted MMC"
- </source>
- <dest>
- *: none
- archosondio*: "Vedd ki a multimédia kártyát"
- </dest>
- <voice>
- *: none
- archosondio*: "Please remove multimedia card"
+ recording: "-végtelen"
</voice>
</phrase>
<phrase>
@@ -7796,7 +7388,7 @@
</phrase>
<phrase>
id: LANG_OFF_ABORT
- desc: Used on archosrecorder models
+ desc: Used on many models
user: core
<source>
*: "OFF to abort"
@@ -7888,7 +7480,7 @@
*: "Inkompatibilis modell"
</dest>
<voice>
- *: ""
+ *: "Inkompatibilis modell"
</voice>
</phrase>
<phrase>
@@ -7902,7 +7494,7 @@
*: "Inkompatibilis verzió"
</dest>
<voice>
- *: ""
+ *: "Inkompatibilis verzió"
</voice>
</phrase>
<phrase>
@@ -7916,7 +7508,7 @@
*: "Plugin hibát jelzett"
</dest>
<voice>
- *: ""
+ *: "Plugin hibát jelzett"
</voice>
</phrase>
<phrase>
@@ -7993,23 +7585,6 @@
</voice>
</phrase>
<phrase>
- id: LANG_BATTERY_TOPOFF_CHARGE
- desc: in info display, shows that top off charge is running Only for V1 archosrecorder
- user: core
- <source>
- *: none
- archosrecorder: "Battery: Top-Off Chg"
- </source>
- <dest>
- *: none
- archosrecorder: "Battery: Top-Off Chg"
- </dest>
- <voice>
- *: none
- archosrecorder: "Top off charge"
- </voice>
-</phrase>
-<phrase>
id: LANG_BATTERY_TRICKLE_CHARGE
desc: in info display, shows that trickle charge is running
user: core
@@ -8069,48 +7644,6 @@
</voice>
</phrase>
<phrase>
- id: LANG_KILOBYTE
- desc: a unit postfix, also voiced
- user: core
- <source>
- *: "KB"
- </source>
- <dest>
- *: "kB"
- </dest>
- <voice>
- *: "kilobyte"
- </voice>
-</phrase>
-<phrase>
- id: LANG_MEGABYTE
- desc: a unit postfix, also voiced
- user: core
- <source>
- *: "MB"
- </source>
- <dest>
- *: "MB"
- </dest>
- <voice>
- *: "megabyte"
- </voice>
-</phrase>
-<phrase>
- id: LANG_GIGABYTE
- desc: a unit postfix, also voiced
- user: core
- <source>
- *: "GB"
- </source>
- <dest>
- *: "GB"
- </dest>
- <voice>
- *: "gigabyte"
- </voice>
-</phrase>
-<phrase>
id: LANG_POINT
desc: decimal separator for composing numbers
user: core
@@ -9341,7 +8874,8 @@
remote: ""
</dest>
<voice>
- *: "remote while-playing-screen"
+ *: none
+ remote: "remote while-playing-screen"
</voice>
</phrase>
<phrase>
@@ -9446,241 +8980,6 @@
</voice>
</phrase>
<phrase>
- id: LANG_SYSFONT_MODE
- desc: in wps F2 pressed
- user: core
- <source>
- *: "Mode:"
- </source>
- <dest>
- *: "Mód:"
- </dest>
- <voice>
- *: ""
- </voice>
-</phrase>
-<phrase>
- id: LANG_SYSFONT_DIRBROWSE_F1
- desc: in dir browser, F1 button bar text
- user: core
- <source>
- *: none
- recorder_pad: "Menu"
- </source>
- <dest>
- *: none
- recorder_pad: "Menü"
- </dest>
- <voice>
- *: none
- recorder_pad: ""
- </voice>
-</phrase>
-<phrase>
- id: LANG_SYSFONT_DIRBROWSE_F2
- desc: in dir browser, F2 button bar text
- user: core
- <source>
- *: none
- recorder_pad: "Option"
- </source>
- <dest>
- *: none
- recorder_pad: "Beállítások"
- </dest>
- <voice>
- *: none
- recorder_pad: ""
- </voice>
-</phrase>
-<phrase>
- id: LANG_SYSFONT_DIRBROWSE_F3
- desc: in dir browser, F3 button bar text
- user: core
- <source>
- *: none
- recorder_pad: "LCD"
- </source>
- <dest>
- *: none
- recorder_pad: "LCD"
- </dest>
- <voice>
- *: none
- recorder_pad: ""
- </voice>
-</phrase>
-<phrase>
- id: LANG_SYSFONT_CHANNEL_STEREO
- desc: in sound_settings
- user: core
- <source>
- *: none
- recording: "Stereo"
- </source>
- <dest>
- *: none
- recording: "Sztereó"
- </dest>
- <voice>
- *: none
- recording: "Stereo"
- </voice>
-</phrase>
-<phrase>
- id: LANG_SYSFONT_CHANNEL_MONO
- desc: in sound_settings
- user: core
- <source>
- *: none
- recording: "Mono"
- </source>
- <dest>
- *: none
- recording: "Monó"
- </dest>
- <voice>
- *: none
- recording: "Mono"
- </voice>
-</phrase>
-<phrase>
- id: LANG_SYSFONT_RECORDING_QUALITY
- desc: in the recording settings
- user: core
- <source>
- *: none
- recording_hwcodec: "Quality"
- </source>
- <dest>
- *: none
- recording_hwcodec: "Minőség"
- </dest>
- <voice>
- *: none
- recording_hwcodec: "Quality"
- </voice>
-</phrase>
-<phrase>
- id: LANG_SYSFONT_RECORDING_FREQUENCY
- desc: in the recording settings
- user: core
- <source>
- *: none
- recording: "Frequency"
- </source>
- <dest>
- *: none
- recording: "Frekvencia"
- </dest>
- <voice>
- *: none
- recording: "Frequency"
- </voice>
-</phrase>
-<phrase>
- id: LANG_SYSFONT_RECORDING_SOURCE
- desc: in the recording settings
- user: core
- <source>
- *: none
- recording: "Source"
- </source>
- <dest>
- *: none
- recording: "Forrás"
- </dest>
- <voice>
- *: none
- recording: "Source"
- </voice>
-</phrase>
-<phrase>
- id: LANG_SYSFONT_RECORDING_SRC_MIC
- desc: in the recording settings
- user: core
- <source>
- *: none
- recording: "Int. Mic"
- </source>
- <dest>
- *: none
- recording: "Belső mikr."
- </dest>
- <voice>
- *: none
- recording: "Internal Microphone"
- </voice>
-</phrase>
-<phrase>
- id: LANG_SYSFONT_LINE_IN
- desc: in the recording settings
- user: core
- <source>
- *: none
- recording: "Line In"
- </source>
- <dest>
- *: none
- recording: "Line In"
- </dest>
- <voice>
- *: none
- recording: "Line In"
- </voice>
-</phrase>
-<phrase>
- id: LANG_SYSFONT_RECORDING_SRC_DIGITAL
- desc: in the recording settings
- user: core
- <source>
- *: none
- recording: "Digital"
- </source>
- <dest>
- *: none
- recording: "Digitális"
- </dest>
- <voice>
- *: none
- recording: "Digital"
- </voice>
-</phrase>
-<phrase>
- id: LANG_SYSFONT_CHANNELS
- desc: in the recording settings
- user: core
- <source>
- *: none
- recording: "Channels"
- </source>
- <dest>
- *: none
- recording: "Csatornák"
- </dest>
- <voice>
- *: none
- recording: "Channels"
- </voice>
-</phrase>
-<phrase>
- id: LANG_SYSFONT_RECORD_TRIGGER
- desc: in recording settings_menu
- user: core
- <source>
- *: none
- recording: "Trigger"
- </source>
- <dest>
- *: none
- recording: "Trigger"
- </dest>
- <voice>
- *: none
- recording: "Trigger"
- </voice>
-</phrase>
-<phrase>
id: VOICE_OF
desc: spoken only, as in 3/8 => 3 of 8
user: core
@@ -9975,7 +9274,7 @@
</dest>
<voice>
*: none
- gigabeatfx,ipod6g,ipodvideo,mpiohd200,mpiohd300,mrobe100: "Bass Cutoff"
+ gigabeatfx,ipod6g,ipodvideo,mpiohd200,mpiohd300,mrobe100: "Alsó határfrekvencia"
</voice>
</phrase>
<phrase>
@@ -10049,15 +9348,15 @@
user: core
<source>
*: none
- gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
+ fiiom3k,gigabeatfx,sansafuzeplus,fiiom3k: "Touchpad Sensitivity"
</source>
<dest>
*: none
- gigabeatfx: "Érintőpad érzékenysége"
+ fiiom3k,gigabeatfx,sansafuzeplus,fiiom3k: "Érintőpad érzékenysége"
</dest>
<voice>
*: none
- gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
+ fiiom3k,gigabeatfx,sansafuzeplus,fiiom3k: "Érintőpad érzékenysége"
</voice>
</phrase>
<phrase>
@@ -10652,7 +9951,7 @@
</dest>
<voice>
*: none
- gigabeatfx,ipod6g,ipodvideo,mpiohd200,mpiohd300,mrobe100: "Treble Cutoff"
+ gigabeatfx,ipod6g,ipodvideo,mpiohd200,mpiohd300,mrobe100: "Magas határfrekvencia"
</voice>
</phrase>
<phrase>
@@ -12806,7 +12105,7 @@
*: "Szerző"
</dest>
<voice>
- *: ""
+ *: "Szerző"
</voice>
</phrase>
<phrase>
diff --git a/apps/lang/nederlands.lang b/apps/lang/nederlands.lang
index 3e7abc613d..83139cc6de 100644
--- a/apps/lang/nederlands.lang
+++ b/apps/lang/nederlands.lang
@@ -3798,12 +3798,10 @@
<source>
*: "Battery: %d%% %dh %dm"
ipodmini1g,ipodmini2g,iriverh10: "Batt: %d%% %dh %dm"
- iriverifp7xx: "%d%% %dh %dm"
</source>
<dest>
*: "Batterij: %d%% %du %dm"
ipodmini1g,ipodmini2g,iriverh10: "Batt: %d%% %dh %dm"
- iriverifp7xx: "%d%% %dh %dm"
</dest>
<voice>
*: "Batterijniveau"
@@ -3843,14 +3841,17 @@
user: core
<source>
*: "Int:"
+ hibylinux: "mSD:"
xduoox3: "mSD1:"
</source>
<dest>
*: "Intern:"
+ hibylinux: "mSD:"
xduoox3: "mSD1:"
</dest>
<voice>
*: "Intern"
+ hibylinux: "micro S D"
xduoox3: "micro S D 1"
</voice>
</phrase>
@@ -3873,7 +3874,7 @@
<voice>
*: none
multivolume: "HD1"
- sansac200*,sansaclipplus,sansae200*,sansafuze*: "micro S D"
+ sansac200*,sansaclipplus,sansae200*,sansafuze*: "mSD:"
xduoox3: "micro S D 2"
</voice>
</phrase>
@@ -4032,7 +4033,7 @@
</dest>
<voice>
*: none
- gogearsa9200,iaudiom5,iaudiox5,ipod*,sansac200*,iriverh10,iriverh10_5gb,sansae200*,iriverh100,iriverh120,iriverh300,mrobe100,rtc,samsungyh*: ""
+ gigabeat*,gogearsa9200,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh100,iriverh10_5gb,iriverh120,iriverh300,mrobe100,rtc,samsungyh*,sansac200*,sansae200*: ""
</voice>
</phrase>
<phrase>
@@ -4249,31 +4250,16 @@
</phrase>
<phrase>
id: LANG_ALARM_MOD_KEYS
- desc: Shown key functions in alarm menu (for the RTC alarm mod).
+ desc: deprecated
user: core
<source>
- *: none
- alarm: "PLAY=Set OFF=Cancel"
- gigabeats: "SELECT=Set POWER=Cancel"
- ipod*: "SELECT=Set MENU=Cancel"
- iriverh10,iriverh10_5gb: "SELECT=Set PREV=Cancel"
- mpiohd300: "ENTER=Set MENU=Cancel"
- sansafuzeplus: "SELECT=Set BACK=Cancel"
- vibe500: "OK=Set C=Cancel"
+ *: ""
</source>
<dest>
- *: none
- alarm: "PLAY=Instellen, OFF=Annuleren"
- gigabeats: "SELECT=Instellen, POWER=Annuleren"
- ipod*: "SELECT=Instellen, MENU=Annuleren"
- iriverh10,iriverh10_5gb: "SELECT=Instellen, PREV=Annuleren"
- mpiohd300: "ENTER=Instellen, MENU=Annuleren"
- sansafuzeplus: "SELECT=Instellen, BACK=Annuleren"
- vibe500: "OK=Instellen, C=Annuleren"
+ *: ""
</dest>
<voice>
- *: none
- alarm,ipod*: ""
+ *: ""
</voice>
</phrase>
<phrase>
@@ -7579,10 +7565,10 @@
</phrase>
<phrase>
id: LANG_AUDIOSCROBBLER
- desc: "Last.fm Log" in the playback menu
+ desc: "Last.fm Logger" in Plugin/apps/scrobbler
user: core
<source>
- *: "Last.fm Log"
+ *: "Last.fm Logger"
</source>
<dest>
*: "Last.fm-logboek"
@@ -7620,20 +7606,17 @@
</voice>
</phrase>
<phrase>
- id: LANG_RECORDING_FORMAT
- desc: audio format item in recording menu
+ id: LANG_FORMAT
+ desc: audio format
user: core
<source>
- *: none
- recording: "Format"
+ *: "Format"
</source>
<dest>
- *: none
- recording: "Bestandsformaat"
+ *: "Bestandsformaat"
</dest>
<voice>
- *: none
- recording: "Bestandsformaat"
+ *: "Bestandsformaat"
</voice>
</phrase>
<phrase>
@@ -7926,16 +7909,16 @@
</phrase>
<phrase>
id: LANG_REPLACE
- desc: in onplay menu. Replace the current playlist with a new one.
+ desc: deprecated
user: core
<source>
- *: "Play Next"
+ *: ""
</source>
<dest>
- *: "Volgende afspelen"
+ *: ""
</dest>
<voice>
- *: "Volgende afspelen"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -8653,8 +8636,7 @@
<source>
*: "PLAY = Yes"
cowond2*: "MENU, or top-right = Yes"
- creativezen*: "Select = Yes"
- gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT = Yes"
+ creativezen*,gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT = Yes"
iriverh100,iriverh120,iriverh300: "NAVI = Yes"
mrobe500: "PLAY, POWER, or top-right = Yes"
vibe500: "OK = Yes"
@@ -8662,8 +8644,7 @@
<dest>
*: "PLAY = Ja"
cowond2*: "MENU of rechtsboven = Ja"
- creativezen*: "Select = Ja"
- gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT = Ja"
+ creativezen*,gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT = Ja"
iriverh100,iriverh120,iriverh300: "NAVI = Ja"
mrobe500: "PLAY, POWER of rechtsboven = Ja"
vibe500: "OK = Ja"
@@ -8698,7 +8679,7 @@
</dest>
<voice>
*: none
- gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansae200*,gogearsa9200,samsungyh*,iriverh100,iriverh120,iriverh300,rtc: ""
+ gigabeat*,gogearsa9200,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh100,iriverh10_5gb,iriverh120,iriverh300,mrobe100,rtc,samsungyh*,sansac200*,sansae200*: ""
</voice>
</phrase>
<phrase>
@@ -9601,15 +9582,15 @@
user: core
<source>
*: none
- gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
</source>
<dest>
*: none
- gigabeatfx,sansafuzeplus: "Touchpadgevoeligheid"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Touchpadgevoeligheid"
</dest>
<voice>
*: none
- gigabeatfx,sansafuzeplus: "Touchpadgevoeligheid"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Touchpadgevoeligheid"
</voice>
</phrase>
<phrase>
@@ -12130,7 +12111,7 @@
desc: Selective Actions
user: core
<source>
- *: "Play"
+ *: "Exempt Play"
</source>
<dest>
*: "Afspelen"
@@ -12158,7 +12139,7 @@
desc: Softlock behaviour setting
user: core
<source>
- *: "Disable Notify"
+ *: "Disable Locked Reminders"
</source>
<dest>
*: "Melden uitschakelen"
@@ -12284,7 +12265,7 @@
desc: Selective Actions
user: core
<source>
- *: "Skip"
+ *: "Exempt Skip"
</source>
<dest>
*: "Overslaan"
@@ -12315,10 +12296,10 @@
*: "Android Debug Bridge"
</source>
<dest>
- *: "Android Debug Bridge"
+ *: "Android-foutopsporingsbrug"
</dest>
<voice>
- *: "Android Debug Bridge"
+ *: "Android-foutopsporingsbrug"
</voice>
</phrase>
<phrase>
@@ -12413,7 +12394,7 @@
desc: Selective Actions
user: core
<source>
- *: "Seek"
+ *: "Exempt Seek"
</source>
<dest>
*: "Zoeken"
@@ -12561,11 +12542,11 @@
</source>
<dest>
*: none
- es9018: "Bypass"
+ es9018: "Omzeilen"
</dest>
<voice>
*: none
- es9018: "Bypass"
+ es9018: "Omzeilen"
</voice>
</phrase>
<phrase>
@@ -14434,58 +14415,58 @@
</phrase>
<phrase>
id: LANG_PROPERTIES_ARTIST
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Artist]"
+ *: ""
</source>
<dest>
- *: "[Artiest]"
+ *: ""
</dest>
<voice>
- *: "Artiest"
+ *: ""
</voice>
</phrase>
<phrase>
id: LANG_PROPERTIES_TITLE
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Title]"
+ *: ""
</source>
<dest>
- *: "[Titel]"
+ *: ""
</dest>
<voice>
- *: "Titel"
+ *: ""
</voice>
</phrase>
<phrase>
id: LANG_PROPERTIES_ALBUM
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Album]"
+ *: ""
</source>
<dest>
- *: "[Album]"
+ *: ""
</dest>
<voice>
- *: "Album"
+ *: ""
</voice>
</phrase>
<phrase>
id: LANG_PROPERTIES_DURATION
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Duration]"
+ *: ""
</source>
<dest>
- *: "[Looptijd]"
+ *: ""
</dest>
<voice>
- *: "Looptijd"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -14687,7 +14668,7 @@
lowmem: none
</dest>
<voice>
- *: "CPU Boost"
+ *: "CPU-boost"
lowmem: none
</voice>
</phrase>
@@ -15021,10 +15002,10 @@
*: "Update cache"
</source>
<dest>
- *: "Update cache"
+ *: "Cache bijwerken"
</dest>
<voice>
- *: "Update cache"
+ *: "Cache bijwerken"
</voice>
</phrase>
<phrase>
@@ -15119,8 +15100,8 @@
<dest>
*: "Druk op LINKS om te annuleren."
android: "Druk op RECHTS om te annuleren."
- hifietma*,zenvision: "Druk op BACK om te annuleren."
cowond2,creativezenxfi2,ibassodx50,ibassodx90,mrobe500,ondavx747: "Druk op POWER om te annuleren."
+ hifietma*,zenvision: "Druk op BACK om te annuleren."
ihifi760,ihifi960: "Dubbeltik op RETURN om te annuleren."
ihifi770,ihifi770c,ihifi800: "Druk op HOME om te annuleren."
iriverh10,samsungyh*: "Dubbeltik op LINKS om te annuleren."
@@ -15135,8 +15116,8 @@
<voice>
*: "Druk op LINKS om te annuleren."
android: "Druk op RECHTS om te annuleren."
- hifietma*,zenvision: "Druk op BACK om te annuleren."
cowond2,creativezenxfi2,ibassodx50,ibassodx90,mrobe500,ondavx747: "Druk op POWER om te annuleren."
+ hifietma*,zenvision: "Druk op BACK om te annuleren."
ihifi760,ihifi960: "Dubbeltik op RETURN om te annuleren."
ihifi770,ihifi770c,ihifi800: "Druk op HOME om te annuleren."
iriverh10,samsungyh*: "Dubbeltik op LINKS om te annuleren."
@@ -15223,16 +15204,13 @@
desc: used in track x of y constructs
user: core
<source>
- *: none
- hotkey: "Track"
+ *: "Track"
</source>
<dest>
- *: none
- hotkey: "Nummer"
+ *: "Nummer"
</dest>
<voice>
- *: none
- hotkey: "Nummer"
+ *: "Nummer"
</voice>
</phrase>
<phrase>
@@ -15522,3 +15500,827 @@
*: "USB massa opslag modus openen?"
</voice>
</phrase>
+<phrase>
+ id: LANG_TALK_MIXER_LEVEL
+ desc: Relative volume of voice prompts
+ user: core
+ <source>
+ *: "Voice prompt volume"
+ </source>
+ <dest>
+ *: "Volume van gesproken prompt"
+ </dest>
+ <voice>
+ *: "Volume van gesproken prompt"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FILTER_SUPER_SLOW
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ filter_roll_off: "Super Slow"
+ </source>
+ <dest>
+ *: none
+ filter_roll_off: "Super Traag"
+ </dest>
+ <voice>
+ *: none
+ filter_roll_off: "Super Traag"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FILTER_LINEAR_FAST
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ es9218: "Linear Fast"
+ </source>
+ <dest>
+ *: none
+ es9218: "Lineair Snel"
+ </dest>
+ <voice>
+ *: none
+ es9218: "Lineair Snel"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FILTER_LINEAR_SLOW
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ es9218: "Linear Slow"
+ </source>
+ <dest>
+ *: none
+ es9218: "Lineair Traag"
+ </dest>
+ <voice>
+ *: none
+ es9218: "Lineair Traag"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FILTER_MINIMUM_FAST
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ es9218: "Minimum Fast"
+ </source>
+ <dest>
+ *: none
+ es9218: "Minimaal Snel"
+ </dest>
+ <voice>
+ *: none
+ es9218: "Minimaal Snel"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FILTER_MINIMUM_SLOW
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ es9218: "Minimum Slow"
+ </source>
+ <dest>
+ *: none
+ es9218: "Minimaal Traag"
+ </dest>
+ <voice>
+ *: none
+ es9218: "Minimaal Traag"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FILTER_APODIZING_1
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ es9218: "Apodizing type 1"
+ </source>
+ <dest>
+ *: none
+ es9218: "Apodiserend type 1"
+ </dest>
+ <voice>
+ *: none
+ es9218: "Apodiserend type 1"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FILTER_APODIZING_2
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ es9218: "Apodizing type 2"
+ </source>
+ <dest>
+ *: none
+ es9218: "Apodiserend type 2"
+ </dest>
+ <voice>
+ *: none
+ es9218: "Apodiserend type 2"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FILTER_HYBRID_FAST
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ es9218: "Hybrid Fast"
+ </source>
+ <dest>
+ *: none
+ es9218: "Hybride Snel"
+ </dest>
+ <voice>
+ *: none
+ es9218: "Hybride Snel"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FILTER_BRICK_WALL
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ es9218: "Brick Wall"
+ </source>
+ <dest>
+ *: none
+ es9218: "Stenen Muur"
+ </dest>
+ <voice>
+ *: none
+ es9218: "Stenen Muur"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_DAC_POWER_MODE
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ dac_power_mode: "DAC's power mode"
+ es9218: "DAC's output level"
+ </source>
+ <dest>
+ *: none
+ dac_power_mode: "DAC energiemodus"
+ es9218: "DAC uitgangsniveau"
+ </dest>
+ <voice>
+ *: none
+ dac_power_mode: "DAC energiemodus"
+ es9218: "DAC uitgangsniveau"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_DAC_POWER_HIGH
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ dac_power_mode: "High performance"
+ es9218: "High Gain (2 Vrms)"
+ </source>
+ <dest>
+ *: none
+ dac_power_mode: "Hoge uitvoering"
+ es9218: "Hoge Versterking (2 Vrms)"
+ </dest>
+ <voice>
+ *: none
+ dac_power_mode: "Hoge uitvoering"
+ es9218: "Hoge Versterking (2 Vrms)"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_DAC_POWER_LOW
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ dac_power_mode: "Save battery"
+ es9218: "Low Gain (1 Vrms)"
+ </source>
+ <dest>
+ *: none
+ dac_power_mode: "Energie besparen"
+ es9218: "Lage versterking (1 Vrms)"
+ </dest>
+ <voice>
+ *: none
+ dac_power_mode: "Energie besparen"
+ es9218: "Lage versterking (1 Vrms)"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_CLEAR_LIST_AND_PLAY_NEXT
+ desc: deprecated
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_QUEUE_MENU
+ desc: in onplay menu
+ user: core
+ <source>
+ *: "Queue..."
+ </source>
+ <dest>
+ *: "Rij..."
+ </dest>
+ <voice>
+ *: "Rij..."
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_SHOW_QUEUE_OPTIONS
+ desc: in Current Playlist settings
+ user: core
+ <source>
+ *: "Show Queue Options"
+ </source>
+ <dest>
+ *: "Wachtrij-Opties Weergeven"
+ </dest>
+ <voice>
+ *: "Wachtrij Opties Weergeven"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_SHOW_SHUFFLED_ADDING_OPTIONS
+ desc: in Current Playlist settings
+ user: core
+ <source>
+ *: "Show Shuffled Adding Options"
+ </source>
+ <dest>
+ *: "Opties voor Geschud Toevoeging Weergeven"
+ </dest>
+ <voice>
+ *: "Opties voor Geschud Toevoeging Weergeven"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_IN_SUBMENU
+ desc: in Settings
+ user: core
+ <source>
+ *: "In Submenu"
+ </source>
+ <dest>
+ *: "In Submenu"
+ </dest>
+ <voice>
+ *: "In Submenu"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_CLEAR_LIST_AND_PLAY_SHUFFLED
+ desc: deprecated
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_SOFTLOCK_DISABLE_ALL_NOTIFY
+ desc: disable all softlock notifications
+ user: core
+ <source>
+ *: "Disable All Lock Notifications"
+ </source>
+ <dest>
+ *: "Alle Vergrendelingsmeldingen Uitschakelen"
+ </dest>
+ <voice>
+ *: "Alle Vergrendelingsmeldingen Uitschakelen"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_ACTION_VOLUME
+ desc: exempt volume from softlock
+ user: core
+ <source>
+ *: "Exempt Volume"
+ </source>
+ <dest>
+ *: "Volume Vrijstellen"
+ </dest>
+ <voice>
+ *: "Volume Vrijstellen"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_ACTION_ALWAYSAUTOLOCK
+ desc: always prime autolock
+ user: core
+ <source>
+ *: "Always Autolock"
+ </source>
+ <dest>
+ *: "Altijd Auto-vergrendelen"
+ </dest>
+ <voice>
+ *: "Altijd Auto-vergrendelen"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PLAYLIST_RELOAD_AFTER_SAVE
+ desc: reload playlist after saving
+ user: core
+ <source>
+ *: "Reload After Saving"
+ </source>
+ <dest>
+ *: "Herladen na Opslaan"
+ </dest>
+ <voice>
+ *: "Herladen na Opslaan"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_SINGLE_MODE
+ desc: single mode
+ user: core
+ <source>
+ *: "Single Mode"
+ </source>
+ <dest>
+ *: "Enkele Modus"
+ </dest>
+ <voice>
+ *: "Enkele Modus"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_ALBUMARTIST
+ desc: deprecated
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_GENRE
+ desc: deprecated
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_COMMENT
+ desc: deprecated
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_COMPOSER
+ desc: deprecated
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_YEAR
+ desc: deprecated
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_TRACKNUM
+ desc: deprecated
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_DISCNUM
+ desc: deprecated
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_FREQUENCY
+ desc: deprecated
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_BITRATE
+ desc: deprecated
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: VOICE_NUMERIC_TENS_SWAP_SEPARATOR
+ desc: voice only, for speaking numbers in languages that swap the tens and ones fields. Leave blank for languages that do not need it, such as English ("231" => "two hundred thirty one") but other languages may speak it as "two hundred one [AND] thirty"
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_VOICED_DATE_FORMAT
+ desc: format string for how dates will be read back. Y == 4-digit year, A == month name, m == numeric month, d == numeric day. For example, "AdY" will read "January 21 2021"
+ user: core
+ <source>
+ *: "dAY"
+ </source>
+ <dest>
+ *: "dAG"
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_LIST_WRAPAROUND
+ desc: in Settings
+ user: core
+ <source>
+ *: "List Wraparound"
+ </source>
+ <dest>
+ *: "Lijst Omhullend"
+ </dest>
+ <voice>
+ *: "Lijst Omhullend"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_SHOW_SHUTDOWN_MESSAGE
+ desc: in Settings
+ user: core
+ <source>
+ *: "Show Shutdown Message"
+ </source>
+ <dest>
+ *: "Afsluitbericht Weergeven"
+ </dest>
+ <voice>
+ *: "Afsluitbericht Weergeven"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_LIST_ORDER
+ desc: in Settings
+ user: core
+ <source>
+ *: "List Order"
+ </source>
+ <dest>
+ *: "Lijstvolgorde"
+ </dest>
+ <voice>
+ *: "Lijstvolgorde"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_ASCENDING
+ desc: in Settings
+ user: core
+ <source>
+ *: "Ascending"
+ </source>
+ <dest>
+ *: "Oplopend"
+ </dest>
+ <voice>
+ *: "Oplopend"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_DESCENDING
+ desc: in Settings
+ user: core
+ <source>
+ *: "Descending"
+ </source>
+ <dest>
+ *: "Aflopend"
+ </dest>
+ <voice>
+ *: "Aflopend"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_ALBUM_ART
+ desc: in Settings
+ user: core
+ <source>
+ *: "Album Art"
+ </source>
+ <dest>
+ *: "Albumhoezen"
+ </dest>
+ <voice>
+ *: "Albumhoezen"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PREFER_EMBEDDED
+ desc: in Settings
+ user: core
+ <source>
+ *: "Prefer Embedded"
+ </source>
+ <dest>
+ *: "Liever Ingesloten"
+ </dest>
+ <voice>
+ *: "Liever Ingesloten"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PREFER_IMAGE_FILE
+ desc: in Settings
+ user: core
+ <source>
+ *: "Prefer Image File"
+ </source>
+ <dest>
+ *: "Liever Afbeeldingsbestand"
+ </dest>
+ <voice>
+ *: "Liever Afbeeldingsbestand"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FM_SYNC_RDS_TIME
+ desc: in radio screen and Settings
+ user: core
+ <source>
+ *: none
+ rds: "Sync RDS Time"
+ </source>
+ <dest>
+ *: none
+ rds: "RDS-tijd Synchroniseren"
+ </dest>
+ <voice>
+ *: none
+ rds: "RDS Tijd Synchroniseren"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_SORT_ALBUMS_BY
+ desc: in Settings
+ user: core
+ <source>
+ *: "Sort albums by"
+ </source>
+ <dest>
+ *: "Sorteer albums op"
+ </dest>
+ <voice>
+ *: "Sorteer albums op"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_ARTIST_PLUS_NAME
+ desc: in Settings
+ user: core
+ <source>
+ *: "Artist + Name"
+ </source>
+ <dest>
+ *: "Artiest + Naam"
+ </dest>
+ <voice>
+ *: "Artiest En Naam"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_ARTIST_PLUS_YEAR
+ desc: in Settings
+ user: core
+ <source>
+ *: "Artist + Year"
+ </source>
+ <dest>
+ *: "Artiest + Jaar"
+ </dest>
+ <voice>
+ *: "Artiest En Jaar"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_YEAR_SORT_ORDER
+ desc: in Settings
+ user: core
+ <source>
+ *: "Year sort order"
+ </source>
+ <dest>
+ *: "Sorteervolgorde jaar"
+ </dest>
+ <voice>
+ *: "Sorteer volgorde jaar"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_SHOW_YEAR_IN_ALBUM_TITLE
+ desc: in Settings
+ user: core
+ <source>
+ *: "Show year in album title"
+ </source>
+ <dest>
+ *: "Jaar in albumtitel weergeven"
+ </dest>
+ <voice>
+ *: "Jaar in albumtitel weergeven"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_WAIT_FOR_CACHE
+ desc: in Settings
+ user: core
+ <source>
+ *: "Cache needs to finish updating first!"
+ </source>
+ <dest>
+ *: "Cache moet eerst de update voltooien!"
+ </dest>
+ <voice>
+ *: "Cache moet eerst de update voltooien!"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_TRACK_INFO
+ desc: Track Info Title
+ user: core
+ <source>
+ *: "Track Info"
+ </source>
+ <dest>
+ *: "Nummer Info"
+ </dest>
+ <voice>
+ *: "Nummer Info"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PLAY
+ desc: play selected file/directory, in playlist context menu
+ user: core
+ <source>
+ *: "Play"
+ </source>
+ <dest>
+ *: "Speel"
+ </dest>
+ <voice>
+ *: "Speel"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PLAY_SHUFFLED
+ desc: play selected files in shuffled order, in playlist context menu
+ user: core
+ <source>
+ *: "Play Shuffled"
+ </source>
+ <dest>
+ *: "Geschud Afspelen"
+ </dest>
+ <voice>
+ *: "Geschud Afspelen"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_KEEP_CURRENT_TRACK_ON_REPLACE
+ desc: used in the playlist settings menu
+ user: core
+ <source>
+ *: "Keep Current Track When Replacing Playlist"
+ </source>
+ <dest>
+ *: "Huidige Track Behouden bij het Vervangen van een Afspeellijst"
+ </dest>
+ <voice>
+ *: "Huidige Track Behouden bij het Vervangen van een Afspeellijst"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_CLEAR_SETTINGS_ON_HOLD
+ desc: in the system sub menu
+ user: core
+ <source>
+ *: none
+ clear_settings_on_hold,iriverh10: "Clear settings when reset button is held during startup"
+ ipod4g,ipodcolor,ipodmini1g,ipodmini2g,ipodnano1g,ipodvideo: "Clear settings when hold switch is on during startup"
+ </source>
+ <dest>
+ *: none
+ clear_settings_on_hold,iriverh10: "Instellingen wissen wanneer de resetknop wordt ingedrukt tijdens het opstarten"
+ ipod4g,ipodcolor,ipodmini1g,ipodmini2g,ipodnano1g,ipodvideo: "Instellingen wissen wanneer de houd-schakelaar aan staat tijdens het opstarten"
+ </dest>
+ <voice>
+ *: none
+ clear_settings_on_hold,iriverh10: "Instellingen wissen wanneer de resetknop wordt ingedrukt tijdens het opstarten"
+ ipod4g,ipodcolor,ipodmini1g,ipodmini2g,ipodnano1g,ipodvideo: "Instellingen wissen wanneer de houd schakelaar aan staat tijdens het opstarten"
+ </voice>
+</phrase>
diff --git a/apps/lang/norsk-nynorsk.lang b/apps/lang/norsk-nynorsk.lang
index 00606a1093..165dfeea14 100644
--- a/apps/lang/norsk-nynorsk.lang
+++ b/apps/lang/norsk-nynorsk.lang
@@ -8237,20 +8237,17 @@
</voice>
</phrase>
<phrase>
- id: LANG_RECORDING_FORMAT
- desc: audio format item in recording menu
+ id: LANG_FORMAT
+ desc: audio format
user: core
<source>
- *: none
- recording: "Format"
+ *: "Format"
</source>
<dest>
- *: none
- recording: "Format"
+ *: "Format"
</dest>
<voice>
- *: none
- recording: "Format"
+ *: "Format"
</voice>
</phrase>
<phrase>
@@ -10150,15 +10147,15 @@
user: core
<source>
*: none
- gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
</source>
<dest>
*: none
- gigabeatfx,sansafuzeplus: "Styreputefølsemd"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Styreputefølsemd"
</dest>
<voice>
*: none
- gigabeatfx,sansafuzeplus: "Styreputefølsemd"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Styreputefølsemd"
</voice>
</phrase>
<phrase>
diff --git a/apps/lang/norsk.lang b/apps/lang/norsk.lang
index 877cb1c269..b823d962b1 100644
--- a/apps/lang/norsk.lang
+++ b/apps/lang/norsk.lang
@@ -7772,20 +7772,17 @@
</voice>
</phrase>
<phrase>
- id: LANG_RECORDING_FORMAT
- desc: audio format item in recording menu
+ id: LANG_FORMAT
+ desc: audio format
user: core
<source>
- *: none
- recording: "Format"
+ *: "Format"
</source>
<dest>
- *: none
- recording: "Format"
+ *: "Format"
</dest>
<voice>
- *: none
- recording: "Format"
+ *: "Format"
</voice>
</phrase>
<phrase>
@@ -9192,7 +9189,7 @@
*: "PLAY = Yes"
archosplayer: "(PLAY/STOP)"
cowond2*: "MENU, or top-right = Yes"
- creativezen*: "Select = Yes"
+ creativezen*: "SELECT = Yes"
gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT = Yes"
iriverh100,iriverh120,iriverh300: "NAVI = Yes"
mrobe500: "PLAY, POWER, or top-right = Yes"
@@ -9202,7 +9199,7 @@
*: "PLAY = Ja"
archosplayer: "(PLAY/STOP)"
cowond2*: "MENU eller øvre høyre = Ja"
- creativezen*: "Select = Ja"
+ creativezen*: "SELECT = Ja"
gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT = Ja"
iriverh100,iriverh120,iriverh300: "NAVI = Ja"
mrobe500: "Play, POWER eller øvre høyre = Ja"
@@ -10095,15 +10092,15 @@
user: core
<source>
*: none
- gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
</source>
<dest>
*: none
- gigabeatfx,sansafuzeplus: "Styreflatefølsomhet"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Styreflatefølsomhet"
</dest>
<voice>
*: none
- gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
</voice>
</phrase>
<phrase>
diff --git a/apps/lang/polski.lang b/apps/lang/polski.lang
index ea426691a7..50487c1df2 100644
--- a/apps/lang/polski.lang
+++ b/apps/lang/polski.lang
@@ -255,8 +255,7 @@
<source>
*: "PLAY = Yes"
cowond2*: "MENU, or top-right = Yes"
- creativezen*: "Select = Yes"
- gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT = Yes"
+ creativezen*,gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT = Yes"
iriverh100,iriverh120,iriverh300: "NAVI = Yes"
mrobe500: "PLAY, POWER, or top-right = Yes"
vibe500: "OK = Yes"
@@ -264,8 +263,7 @@
<dest>
*: "PLAY = Tak"
cowond2*: "MENU lub góra-prawo = Tak"
- creativezen*: "Select = Tak"
- gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT = Tak"
+ creativezen*,gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT = Tak"
iriverh100,iriverh120,iriverh300: "NAVI = Tak"
mrobe500: "PLAY, POWER lub góra-prawo = Tak"
vibe500: "OK = Tak"
@@ -1693,16 +1691,16 @@
</phrase>
<phrase>
id: LANG_AUDIOSCROBBLER
- desc: "Last.fm Log" in the playback menu
+ desc: "Last.fm Logger" in Plugin/apps/scrobbler
user: core
<source>
- *: "Last.fm Log"
+ *: "Last.fm Logger"
</source>
<dest>
- *: "Zapisuj log Last.fm"
+ *: "Rejestrator Last.fm"
</dest>
<voice>
- *: "Zapisuj log Last ef em"
+ *: "Rejestrator Last ef em"
</voice>
</phrase>
<phrase>
@@ -2268,16 +2266,16 @@
desc: in lcd settings
user: core
<source>
- *: none
+ *: "Backlight on Lock"
hold_button: "Backlight on Hold"
</source>
<dest>
- *: none
- hold_button: "Podświetlenie (podczas blokady)"
+ *: "Podświetlenie (podczas blokady)"
+ hold_button: "Podświetlenie (podczas przytrzymania)"
</dest>
<voice>
- *: none
- hold_button: "Podświetlenie podczas blokady przycisków"
+ *: "Podświetlenie podczas blokady"
+ hold_button: "Podświetlenie podczas przytrzymania"
</voice>
</phrase>
<phrase>
@@ -4104,31 +4102,16 @@
</phrase>
<phrase>
id: LANG_ALARM_MOD_KEYS
- desc: Shown key functions in alarm menu (for the RTC alarm mod).
+ desc: deprecated
user: core
<source>
- *: none
- alarm: "PLAY=Set OFF=Cancel"
- gigabeats: "SELECT=Set POWER=Cancel"
- ipod*: "SELECT=Set MENU=Cancel"
- iriverh10,iriverh10_5gb: "SELECT=Set PREV=Cancel"
- mpiohd300: "ENTER=Set MENU=Cancel"
- sansafuzeplus: "SELECT=Set BACK=Cancel"
- vibe500: "OK=Set C=Cancel"
+ *: ""
</source>
<dest>
- *: none
- alarm: "PLAY=Ustaw OFF=Anuluj"
- gigabeats: "WYBIERZ=Ustaw POWER=Anuluj"
- ipod*: "WYBIERZ=Ustaw MENU=Anuluj"
- iriverh10,iriverh10_5gb: "WYBIERZ=Ustaw COFNIJ=Anuluj"
- mpiohd300: "ENTER=Ustaw MENU=Anuluj"
- sansafuzeplus: "SELECT=Ustaw BACK=Anuluj"
- vibe500: "OK=Ustaw C=Anuluj"
+ *: ""
</dest>
<voice>
- *: none
- alarm,ipod*: ""
+ *: ""
</voice>
</phrase>
<phrase>
@@ -4955,20 +4938,17 @@
</voice>
</phrase>
<phrase>
- id: LANG_RECORDING_FORMAT
- desc: audio format item in recording menu
+ id: LANG_FORMAT
+ desc: audio format
user: core
<source>
- *: none
- recording: "Format"
+ *: "Format"
</source>
<dest>
- *: none
- recording: "Format"
+ *: "Format"
</dest>
<voice>
- *: none
- recording: "Format"
+ *: "Format"
</voice>
</phrase>
<phrase>
@@ -5350,19 +5330,17 @@
</phrase>
<phrase>
id: LANG_SET_AS_REC_DIR
- desc: used in the onplay menu to set a recording dir
+ desc: deprecated
user: core
<source>
*: none
- recording: "Set As Recording Directory"
+ recording: ""
</source>
<dest>
- *: none
- recording: "Ustaw jako katalog nagrywania"
+ *: ""
</dest>
<voice>
- *: none
- recording: "Ustaw jako katalog nagrywania"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -6064,18 +6042,21 @@
user: core
<source>
*: none
- multivolume: "HD1"
+ hibylinux: "USB:"
+ multivolume: "HD1:"
sansac200*,sansaclipplus,sansae200*,sansafuze*: "mSD:"
xduoox3: "mSD2:"
</source>
<dest>
*: none
- multivolume: "HD1"
+ hibylinux: "USB:"
+ multivolume: "HD1:"
sansac200*,sansaclipplus,sansae200*,sansafuze*: "mSD:"
xduoox3: "mSD2:"
</dest>
<voice>
*: none
+ hibylinux: "U S B"
multivolume: "ha de 1"
sansac200*,sansaclipplus,sansae200*,sansafuze*: "mikro es de"
xduoox3: "mikro es de 2"
@@ -6167,58 +6148,58 @@
</phrase>
<phrase>
id: LANG_INSERT
- desc: in onplay menu. insert a track/playlist into dynamic playlist.
+ desc: deprecated
user: core
<source>
- *: "Insert"
+ *: ""
</source>
<dest>
- *: "Wstaw"
+ *: ""
</dest>
<voice>
- *: "Wstaw"
+ *: ""
</voice>
</phrase>
<phrase>
id: LANG_INSERT_FIRST
- desc: in onplay menu. insert a track/playlist into dynamic playlist.
+ desc: deprecated
user: core
<source>
- *: "Insert Next"
+ *: ""
</source>
<dest>
- *: "Wstaw następne"
+ *: ""
</dest>
<voice>
- *: "Wstaw następne"
+ *: ""
</voice>
</phrase>
<phrase>
id: LANG_INSERT_LAST
- desc: in onplay menu. append a track/playlist into dynamic playlist.
+ desc: deprecated
user: core
<source>
- *: "Insert Last"
+ *: ""
</source>
<dest>
- *: "Wstaw ostatnie"
+ *: ""
</dest>
<voice>
- *: "Wstaw ostatnie"
+ *: ""
</voice>
</phrase>
<phrase>
id: LANG_INSERT_SHUFFLED
- desc: in onplay menu. insert a track/playlist randomly into dynamic playlist
+ desc: deprecated
user: core
<source>
- *: "Insert Shuffled"
+ *: ""
</source>
<dest>
- *: "Wstaw losowo"
+ *: ""
</dest>
<voice>
- *: "Wstaw losowo"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -6391,16 +6372,16 @@
</phrase>
<phrase>
id: LANG_CATALOG_ADD_TO
- desc: in onplay playlist catalogue submenu
+ desc: deprecated
user: core
<source>
- *: "Add to Playlist"
+ *: ""
</source>
<dest>
- *: "Dołącz do listy"
+ *: ""
</dest>
<voice>
- *: "Dołącz do listy odtwarzania"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -10711,16 +10692,16 @@
</phrase>
<phrase>
id: LANG_INSERT_LAST_SHUFFLED
- desc: in onplay menu. insert a playlist randomly at end of dynamic playlist
+ desc: deprecated
user: core
<source>
- *: "Insert Last Shuffled"
+ *: ""
</source>
<dest>
- *: "Wstaw losowo na koniec"
+ *: ""
</dest>
<voice>
- *: "Wstaw losowo na koniec"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -11495,16 +11476,16 @@
</phrase>
<phrase>
id: LANG_SET_AS_START_DIR
- desc: used in the onplay menu to set a starting browser dir
+ desc: deprecated
user: core
<source>
- *: "Start File Browser Here"
+ *: ""
</source>
<dest>
- *: "Rozpocznij przeglądanie plików tutaj"
+ *: ""
</dest>
<voice>
- *: "Rozpocznij przeglądanie plików tutaj"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -11610,16 +11591,16 @@
</phrase>
<phrase>
id: LANG_SET_AS_PLAYLISTCAT_DIR
- desc: used in the onplay menu to set a playlist catalogue dir
+ desc: deprecated
user: core
<source>
- *: "Set As Playlist Catalogue Directory"
+ *: ""
</source>
<dest>
- *: "Ustaw jako katalog list odtwarzania"
+ *: ""
</dest>
<voice>
- *: "Ustaw jako katalog list odtwarzania"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -11734,16 +11715,16 @@
</phrase>
<phrase>
id: LANG_AUTOMATIC
- desc: generic automatic
+ desc: deprecated
user: core
<source>
- *: "Automatic"
+ *: ""
</source>
<dest>
- *: "Automatyczny"
+ *: ""
</dest>
<voice>
- *: "Automatyczny"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -13187,16 +13168,16 @@
</phrase>
<phrase>
id: LANG_PROPERTIES_ALBUM
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Album]"
+ *: ""
</source>
<dest>
- *: "[Album]"
+ *: ""
</dest>
<voice>
- *: "Album"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -13319,16 +13300,16 @@
</phrase>
<phrase>
id: LANG_CLEAR_PLAYLIST
- desc: in the pictureflow main menu
+ desc: deprecated
user: core
<source>
- *: "Clear playlist"
+ *: ""
</source>
<dest>
- *: "Wyczyść listę odtwarzania"
+ *: ""
</dest>
<voice>
- *: "Wyczyść listę odtwarzania"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -13659,16 +13640,16 @@
</phrase>
<phrase>
id: LANG_PROPERTIES_DURATION
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Duration]"
+ *: ""
</source>
<dest>
- *: "[Czas trwania]"
+ *: ""
</dest>
<voice>
- *: "Czas trwania"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -14229,16 +14210,16 @@
</phrase>
<phrase>
id: LANG_PROPERTIES_ARTIST
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Artist]"
+ *: ""
</source>
<dest>
- *: "[Artysta]"
+ *: ""
</dest>
<voice>
- *: "Artysta"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -14905,30 +14886,30 @@
</phrase>
<phrase>
id: LANG_PROPERTIES_TITLE
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Title]"
+ *: ""
</source>
<dest>
- *: "[Tytuł]"
+ *: ""
</dest>
<voice>
- *: "Tytuł"
+ *: ""
</voice>
</phrase>
<phrase>
id: LANG_DIRECT
- desc: in the pictureflow settings
+ desc: in the pictureflow settings, also a volume adjustment mode
user: core
<source>
*: "Direct"
</source>
<dest>
- *: "Bezpośrednie"
+ *: "Bezpośrednio"
</dest>
<voice>
- *: "Bezpośrednie"
+ *: "Bezpośrednio"
</voice>
</phrase>
<phrase>
@@ -15153,16 +15134,13 @@
desc: used in track x of y constructs
user: core
<source>
- *: none
- hotkey: "Track"
+ *: "Track"
</source>
<dest>
- *: none
- hotkey: "Utwór"
+ *: "Utwór"
</dest>
<voice>
- *: none
- hotkey: "Utwór"
+ *: "Utwór"
</voice>
</phrase>
<phrase>
@@ -15536,16 +15514,16 @@
</phrase>
<phrase>
id: LANG_CLEAR_LIST_AND_PLAY_NEXT
- desc: in onplay menu. Replace current playlist with selected tracks
+ desc: deprecated
user: core
<source>
- *: "Clear List & Play Next"
+ *: ""
</source>
<dest>
- *: "Wyczyść listę i odtwarzaj następne"
+ *: ""
</dest>
<voice>
- *: "Wyczyść listę i odtwarzaj następne"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -15606,16 +15584,16 @@
</phrase>
<phrase>
id: LANG_CLEAR_LIST_AND_PLAY_SHUFFLED
- desc: in onplay menu. Replace current playlist with selected tracks in random order.
+ desc: deprecated
user: core
<source>
- *: "Clear List & Play Shuffled"
+ *: ""
</source>
<dest>
- *: "Wyczyść listę i odtwarzaj losowe"
+ *: ""
</dest>
<voice>
- *: "Wyczyść listę i odtwarzaj losowe"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -15625,14 +15603,17 @@
<source>
*: none
dac_power_mode: "DAC's power mode"
+ es9218: "DAC's output level"
</source>
<dest>
*: none
dac_power_mode: "Tryb zasilania DAC-a"
+ es9218: "Poziom wyjściowy DAC-a"
</dest>
<voice>
*: none
dac_power_mode: "Tryb zasilania daka"
+ es9218: "Poziom wyjściowy daka"
</voice>
</phrase>
<phrase>
@@ -15642,14 +15623,17 @@
<source>
*: none
dac_power_mode: "High performance"
+ es9218: "High Gain (2 Vrms)"
</source>
<dest>
*: none
dac_power_mode: "Wysoka wydajność"
+ es9218: "Wysoki zysk (2 Vrms)"
</dest>
<voice>
*: none
dac_power_mode: "Wysoka wydajność"
+ es9218: "Wysoki zysk dwa fałeremes"
</voice>
</phrase>
<phrase>
@@ -15659,14 +15643,17 @@
<source>
*: none
dac_power_mode: "Save battery"
+ es9218: "Low Gain (1 Vrms)"
</source>
<dest>
*: none
dac_power_mode: "Oszczędzaj baterię"
+ es9218: "Niski zysk (1 Vrms)"
</dest>
<voice>
*: none
dac_power_mode: "Oszczędzaj baterię"
+ es9218: "Niski zysk jeden fałeremes"
</voice>
</phrase>
<phrase>
@@ -15725,3 +15712,863 @@
*: "Załaduj ponownie po zapisaniu"
</voice>
</phrase>
+<phrase>
+ id: LANG_FILTER_SUPER_SLOW
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ filter_roll_off: "Super Slow"
+ </source>
+ <dest>
+ *: none
+ filter_roll_off: "Super wolny"
+ </dest>
+ <voice>
+ *: none
+ filter_roll_off: "Super wolny"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FILTER_LINEAR_FAST
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ es9218: "Linear Fast"
+ </source>
+ <dest>
+ *: none
+ es9218: "Liniowy szybki"
+ </dest>
+ <voice>
+ *: none
+ es9218: "Liniowy szybki"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FILTER_LINEAR_SLOW
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ es9218: "Linear Slow"
+ </source>
+ <dest>
+ *: none
+ es9218: "Liniowy wolny"
+ </dest>
+ <voice>
+ *: none
+ es9218: "Liniowy wolny"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FILTER_MINIMUM_FAST
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ es9218: "Minimum Fast"
+ </source>
+ <dest>
+ *: none
+ es9218: "Minimalny szybki"
+ </dest>
+ <voice>
+ *: none
+ es9218: "Minimalny szybki"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FILTER_MINIMUM_SLOW
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ es9218: "Minimum Slow"
+ </source>
+ <dest>
+ *: none
+ es9218: "Minimalny wolny"
+ </dest>
+ <voice>
+ *: none
+ es9218: "Minimalny wolny"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FILTER_APODIZING_1
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ es9218: "Apodizing type 1"
+ </source>
+ <dest>
+ *: none
+ es9218: "Typ apodyzacji 1"
+ </dest>
+ <voice>
+ *: none
+ es9218: "Typ apodyzacji jeden"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FILTER_APODIZING_2
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ es9218: "Apodizing type 2"
+ </source>
+ <dest>
+ *: none
+ es9218: "Typ apodyzacji 2"
+ </dest>
+ <voice>
+ *: none
+ es9218: "Typ apodyzacji dwa"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FILTER_HYBRID_FAST
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ es9218: "Hybrid Fast"
+ </source>
+ <dest>
+ *: none
+ es9218: "Hybrydowy szybki"
+ </dest>
+ <voice>
+ *: none
+ es9218: "Hybrydowy szybki"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FILTER_BRICK_WALL
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ es9218: "Brick Wall"
+ </source>
+ <dest>
+ *: none
+ es9218: "Åšciana"
+ </dest>
+ <voice>
+ *: none
+ es9218: "Åšciana"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_ALBUMARTIST
+ desc: deprecated
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_GENRE
+ desc: deprecated
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_COMMENT
+ desc: deprecated
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_COMPOSER
+ desc: deprecated
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_YEAR
+ desc: deprecated
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_TRACKNUM
+ desc: deprecated
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_DISCNUM
+ desc: deprecated
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_FREQUENCY
+ desc: deprecated
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_BITRATE
+ desc: deprecated
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_SINGLE_MODE
+ desc: single mode
+ user: core
+ <source>
+ *: "Single Mode"
+ </source>
+ <dest>
+ *: "Tryb pojedynczy"
+ </dest>
+ <voice>
+ *: "Tryb pojedynczy"
+ </voice>
+</phrase>
+<phrase>
+ id: VOICE_NUMERIC_TENS_SWAP_SEPARATOR
+ desc: voice only, for speaking numbers in languages that swap the tens and ones fields. Leave blank for languages that do not need it, such as English ("231" => "two hundred thirty one") but other languages may speak it as "two hundred one [AND] thirty"
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_VOICED_DATE_FORMAT
+ desc: format string for how dates will be read back. Y == 4-digit year, A == month name, m == numeric month, d == numeric day. For example, "AdY" will read "January 21 2021"
+ user: core
+ <source>
+ *: "dAY"
+ </source>
+ <dest>
+ *: "dAY"
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_LIST_WRAPAROUND
+ desc: in Settings
+ user: core
+ <source>
+ *: "List Wraparound"
+ </source>
+ <dest>
+ *: "Zawijanie listy"
+ </dest>
+ <voice>
+ *: "Zawijanie listy"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_SHOW_SHUTDOWN_MESSAGE
+ desc: in Settings
+ user: core
+ <source>
+ *: "Show Shutdown Message"
+ </source>
+ <dest>
+ *: "Pokazuj komunikat wyłączenia"
+ </dest>
+ <voice>
+ *: "Pokazuj komunikat wyłączenia"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_LIST_ORDER
+ desc: in Settings
+ user: core
+ <source>
+ *: "List Order"
+ </source>
+ <dest>
+ *: "Kolejność na liście"
+ </dest>
+ <voice>
+ *: "Kolejność na liście"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_ASCENDING
+ desc: in Settings
+ user: core
+ <source>
+ *: "Ascending"
+ </source>
+ <dest>
+ *: "RosnÄ…co"
+ </dest>
+ <voice>
+ *: "RosnÄ…co"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_DESCENDING
+ desc: in Settings
+ user: core
+ <source>
+ *: "Descending"
+ </source>
+ <dest>
+ *: "MalejÄ…co"
+ </dest>
+ <voice>
+ *: "MalejÄ…co"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_ALBUM_ART
+ desc: in Settings
+ user: core
+ <source>
+ *: "Album Art"
+ </source>
+ <dest>
+ *: "Okładka albumu"
+ </dest>
+ <voice>
+ *: "Okładka albumu"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PREFER_EMBEDDED
+ desc: in Settings
+ user: core
+ <source>
+ *: "Prefer Embedded"
+ </source>
+ <dest>
+ *: "Preferuj wbudowanÄ…"
+ </dest>
+ <voice>
+ *: "Preferuj wbudowanÄ…"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PREFER_IMAGE_FILE
+ desc: in Settings
+ user: core
+ <source>
+ *: "Prefer Image File"
+ </source>
+ <dest>
+ *: "Preferuj plik obrazu"
+ </dest>
+ <voice>
+ *: "Preferuj plik obrazu"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FM_SYNC_RDS_TIME
+ desc: in radio screen and Settings
+ user: core
+ <source>
+ *: none
+ rds: "Sync RDS Time"
+ </source>
+ <dest>
+ *: none
+ rds: "Synchronizuj czas RDS"
+ </dest>
+ <voice>
+ *: none
+ rds: "Synchronizuj czas er de es"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_SORT_ALBUMS_BY
+ desc: in Settings
+ user: core
+ <source>
+ *: "Sort albums by"
+ </source>
+ <dest>
+ *: "Sortuj albumy według"
+ </dest>
+ <voice>
+ *: "Sortuj albumy według"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_ARTIST_PLUS_NAME
+ desc: in Settings
+ user: core
+ <source>
+ *: "Artist + Name"
+ </source>
+ <dest>
+ *: "Wykonawca + nazwa"
+ </dest>
+ <voice>
+ *: "Wykonawca plus nazwa"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_ARTIST_PLUS_YEAR
+ desc: in Settings
+ user: core
+ <source>
+ *: "Artist + Year"
+ </source>
+ <dest>
+ *: "Wykonawca + rok"
+ </dest>
+ <voice>
+ *: "Wykonawca plus rok"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_YEAR_SORT_ORDER
+ desc: in Settings
+ user: core
+ <source>
+ *: "Year sort order"
+ </source>
+ <dest>
+ *: "Kolejność sortowania według roku"
+ </dest>
+ <voice>
+ *: "Kolejność sortowania według roku"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_SHOW_YEAR_IN_ALBUM_TITLE
+ desc: in Settings
+ user: core
+ <source>
+ *: "Show year in album title"
+ </source>
+ <dest>
+ *: "Pokaż rok w tytule albumu"
+ </dest>
+ <voice>
+ *: "Pokaż rok w tytule albumu"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_WAIT_FOR_CACHE
+ desc: in Settings
+ user: core
+ <source>
+ *: "Cache needs to finish updating first!"
+ </source>
+ <dest>
+ *: "Pamięć podręczna musi najpierw zakończyć aktualizację!"
+ </dest>
+ <voice>
+ *: "Pamięć podręczna musi najpierw zakończyć aktualizację!"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_TRACK_INFO
+ desc: Track Info Title
+ user: core
+ <source>
+ *: "Track Info"
+ </source>
+ <dest>
+ *: "Informacje o utworze"
+ </dest>
+ <voice>
+ *: "Informacje o utworze"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PLAY
+ desc: play selected file/directory, in playlist context menu
+ user: core
+ <source>
+ *: "Play"
+ </source>
+ <dest>
+ *: "Odtwarzaj"
+ </dest>
+ <voice>
+ *: "Odtwarzaj"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PLAY_SHUFFLED
+ desc: play selected files in shuffled order, in playlist context menu
+ user: core
+ <source>
+ *: "Play Shuffled"
+ </source>
+ <dest>
+ *: "Odtwarzaj losowe"
+ </dest>
+ <voice>
+ *: "Odtwarzaj losowe"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_KEEP_CURRENT_TRACK_ON_REPLACE
+ desc: used in the playlist settings menu
+ user: core
+ <source>
+ *: "Keep Current Track When Replacing Playlist"
+ </source>
+ <dest>
+ *: "Zachowaj bieżący utwór podczas zastępowania listy odtwarzania"
+ </dest>
+ <voice>
+ *: "Zachowaj bieżący utwór podczas zastępowania listy odtwarzania"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_CLEAR_SETTINGS_ON_HOLD
+ desc: in the system sub menu
+ user: core
+ <source>
+ *: none
+ clear_settings_on_hold,iriverh10: "Clear settings when reset button is held during startup"
+ ipod4g,ipodcolor,ipodmini1g,ipodmini2g,ipodnano1g,ipodvideo: "Clear settings when hold switch is on during startup"
+ </source>
+ <dest>
+ *: none
+ clear_settings_on_hold,iriverh10: "Wyczyść ustawienia, gdy przycisk resetowania będzie przytrzymany podczas uruchamiania"
+ ipod4g,ipodcolor,ipodmini1g,ipodmini2g,ipodnano1g,ipodvideo: "Wyczyść ustawienia, gdy przełącznik blokady będzie włączony podczas uruchamiania"
+ </dest>
+ <voice>
+ *: none
+ clear_settings_on_hold,iriverh10: "Wyczyść ustawienia, gdy przycisk resetowania będzie przytrzymany podczas uruchamiania"
+ ipod4g,ipodcolor,ipodmini1g,ipodmini2g,ipodnano1g,ipodvideo: "Wyczyść ustawienia, gdy przełącznik blokady będzie włączony podczas uruchamiania"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_REWIND_ACROSS_TRACKS
+ desc: in playback settings menu
+ user: core
+ <source>
+ *: "Rewind Across Tracks"
+ </source>
+ <dest>
+ *: "Przewiń poprzez utwory"
+ </dest>
+ <voice>
+ *: "Przewiń poprzez utwory"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_SET_AS
+ desc: used in the onplay menu
+ user: core
+ <source>
+ *: "Set As..."
+ </source>
+ <dest>
+ *: "Ustaw jako..."
+ </dest>
+ <voice>
+ *: "Ustaw jako..."
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PLAYLIST_DIR
+ desc: used in the onplay menu
+ user: core
+ <source>
+ *: "Playlist Directory"
+ </source>
+ <dest>
+ *: "Katalog list odtwarzania"
+ </dest>
+ <voice>
+ *: "Katalog list odtwarzania"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_START_DIR
+ desc: used in the onplay menu
+ user: core
+ <source>
+ *: "Start Directory"
+ </source>
+ <dest>
+ *: "Katalog startowy"
+ </dest>
+ <voice>
+ *: "Katalog startowy"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_RECORDING_DIR
+ desc: used in the onplay menu
+ user: core
+ <source>
+ *: none
+ recording: "Recording Directory"
+ </source>
+ <dest>
+ *: none
+ recording: "Katalog nagrywania"
+ </dest>
+ <voice>
+ *: none
+ recording: "Katalog nagrywania"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_ADD_TO_PL
+ desc: used in the onplay menu
+ user: core
+ <source>
+ *: "Add to Playlist..."
+ </source>
+ <dest>
+ *: "Dodaj do listy odtwarzania..."
+ </dest>
+ <voice>
+ *: "Dodaj do listy odtwarzania..."
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_ADD_TO_EXISTING_PL
+ desc: used in the onplay menu
+ user: core
+ <source>
+ *: "Add to Existing Playlist"
+ </source>
+ <dest>
+ *: "Dodaj do istniejÄ…cej listy odtwarzania"
+ </dest>
+ <voice>
+ *: "Dodaj do istniejÄ…cej listy odtwarzania"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PLAYING_NEXT
+ desc: used in the onplay menu
+ user: core
+ <source>
+ *: "Playing Next..."
+ </source>
+ <dest>
+ *: "Odtwarzanie następnego..."
+ </dest>
+ <voice>
+ *: "Odtwarzanie następnego..."
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PLAY_NEXT
+ desc: used in the onplay menu
+ user: core
+ <source>
+ *: "Play Next"
+ </source>
+ <dest>
+ *: "Odtwórz następny"
+ </dest>
+ <voice>
+ *: "Odtwórz następny"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_ADD_SHUFFLED
+ desc: used in the onplay menu
+ user: core
+ <source>
+ *: "Add Shuffled"
+ </source>
+ <dest>
+ *: "Dodaj losowy"
+ </dest>
+ <voice>
+ *: "Dodaj losowy"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PLAY_LAST
+ desc: used in the onplay menu
+ user: core
+ <source>
+ *: "Play Last"
+ </source>
+ <dest>
+ *: "Odtwórz ostatni"
+ </dest>
+ <voice>
+ *: "Odtwórz ostatni"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PLAY_LAST_SHUFFLED
+ desc: used in the onplay menu
+ user: core
+ <source>
+ *: "Play Last Shuffled"
+ </source>
+ <dest>
+ *: "Odtwórz ostatni losowy"
+ </dest>
+ <voice>
+ *: "Odtwórz ostatni losowy"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_VOLUME_ADJUST_MODE
+ desc: in system settings
+ user: core
+ <source>
+ *: none
+ perceptual_volume: "Volume Adjustment Mode"
+ </source>
+ <dest>
+ *: none
+ perceptual_volume: "Tryb regulacji głośności"
+ </dest>
+ <voice>
+ *: none
+ perceptual_volume: "Tryb regulacji głośności"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_VOLUME_ADJUST_NORM_STEPS
+ desc: in system settings
+ user: core
+ <source>
+ *: none
+ perceptual_volume: "Number of Volume Steps"
+ </source>
+ <dest>
+ *: none
+ perceptual_volume: "Liczba kroków głośności"
+ </dest>
+ <voice>
+ *: none
+ perceptual_volume: "Liczba kroków głośności"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PERCEPTUAL
+ desc: in system settings -> volume adjustment mode
+ user: core
+ <source>
+ *: none
+ perceptual_volume: "Perceptual"
+ </source>
+ <dest>
+ *: none
+ perceptual_volume: "Percepcyjny"
+ </dest>
+ <voice>
+ *: none
+ perceptual_volume: "Percepcyjny"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_SHOW_TRACKS_WHILE_BROWSING
+ desc: in PictureFlow Main Menu
+ user: core
+ <source>
+ *: "Show Tracks While Browsing"
+ </source>
+ <dest>
+ *: "Pokazuj utwory podczas przeglÄ…dania"
+ </dest>
+ <voice>
+ *: "Pokazuj utwory podczas przeglÄ…dania"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_GOTO_LAST_ALBUM
+ desc: in PictureFlow Main Menu
+ user: core
+ <source>
+ *: "Go to Last Album"
+ </source>
+ <dest>
+ *: "Przejdź do ostatniego albumu"
+ </dest>
+ <voice>
+ *: "Przejdź do ostatniego albumu"
+ </voice>
+</phrase>
diff --git a/apps/lang/portugues-brasileiro.lang b/apps/lang/portugues-brasileiro.lang
index 2782d1d111..1100758535 100644
--- a/apps/lang/portugues-brasileiro.lang
+++ b/apps/lang/portugues-brasileiro.lang
@@ -5262,20 +5262,17 @@
</voice>
</phrase>
<phrase>
- id: LANG_RECORDING_FORMAT
- desc: audio format item in recording menu
+ id: LANG_FORMAT
+ desc: audio format
user: core
<source>
- *: none
- recording: "Format"
+ *: "Format"
</source>
<dest>
- *: none
- recording: "Formato"
+ *: "Formato"
</dest>
<voice>
- *: none
- recording: "Formato"
+ *: "Formato"
</voice>
</phrase>
<phrase>
@@ -10310,15 +10307,15 @@
user: core
<source>
*: none
- gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
</source>
<dest>
*: none
- gigabeatfx,sansafuzeplus: "Sensibilidade do Touchpad"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Sensibilidade do Touchpad"
</dest>
<voice>
*: none
- gigabeatfx,sansafuzeplus: "Sensibilidade do Touchpad"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Sensibilidade do Touchpad"
</voice>
</phrase>
<phrase>
diff --git a/apps/lang/portugues.lang b/apps/lang/portugues.lang
index 40035b1832..23629b592d 100644
--- a/apps/lang/portugues.lang
+++ b/apps/lang/portugues.lang
@@ -7216,20 +7216,17 @@
</voice>
</phrase>
<phrase>
- id: LANG_RECORDING_FORMAT
- desc: audio format item in recording menu
+ id: LANG_FORMAT
+ desc: audio format
user: core
<source>
- *: none
- recording: "Format"
+ *: "Format"
</source>
<dest>
- *: none
- recording: "Formato"
+ *: "Formato"
</dest>
<voice>
- *: none
- recording: "Formato"
+ *: "Formato"
</voice>
</phrase>
<phrase>
@@ -7377,7 +7374,7 @@
*: "PLAY = Yes"
archosplayer: "(PLAY/STOP)"
cowond2*: "MENU, or top-right = Yes"
- creativezen*: "Select = Yes"
+ creativezen*: "SELECT = Yes"
gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT = Yes"
iriverh100,iriverh120,iriverh300: "NAVI = Yes"
mrobe500: "PLAY, POWER, or top-right = Yes"
@@ -9605,15 +9602,15 @@
user: core
<source>
*: none
- gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
</source>
<dest>
*: none
- gigabeatfx,sansafuzeplus: "Sensitividade do Touchpad"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Sensitividade do Touchpad"
</dest>
<voice>
*: none
- gigabeatfx,sansafuzeplus: "Sensitividade do Touchpad"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Sensitividade do Touchpad"
</voice>
</phrase>
<phrase>
diff --git a/apps/lang/romaneste.lang b/apps/lang/romaneste.lang
index 9cbb897920..563ebb9e32 100644
--- a/apps/lang/romaneste.lang
+++ b/apps/lang/romaneste.lang
@@ -4180,20 +4180,17 @@
</voice>
</phrase>
<phrase>
- id: LANG_RECORDING_FORMAT
- desc: audio format item in recording menu
+ id: LANG_FORMAT
+ desc: audio format
user: core
<source>
- *: none
- recording: "Format"
+ *: "Format"
</source>
<dest>
- *: none
- recording: "Format"
+ *: "Format"
</dest>
<voice>
- *: none
- recording: "Format"
+ *: "Format"
</voice>
</phrase>
<phrase>
@@ -10229,15 +10226,15 @@
user: core
<source>
*: none
- gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
</source>
<dest>
*: none
- gigabeatfx,sansafuzeplus: "Sensibilitate Touchpad"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Sensibilitate Touchpad"
</dest>
<voice>
*: none
- gigabeatfx,sansafuzeplus: "Sensibilitate Touchpad"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Sensibilitate Touchpad"
</voice>
</phrase>
<phrase>
diff --git a/apps/lang/russian.lang b/apps/lang/russian.lang
index 9d71ad819c..547396cede 100644
--- a/apps/lang/russian.lang
+++ b/apps/lang/russian.lang
@@ -7220,20 +7220,17 @@
</voice>
</phrase>
<phrase>
- id: LANG_RECORDING_FORMAT
- desc: audio format item in recording menu
+ id: LANG_FORMAT
+ desc: audio format
user: core
<source>
- *: none
- recording: "Format"
+ *: "Format"
</source>
<dest>
- *: none
- recording: "Формат"
+ *: "Формат"
</dest>
<voice>
- *: none
- recording: "Формат"
+ *: "Формат"
</voice>
</phrase>
<phrase>
@@ -7398,7 +7395,7 @@
*: "PLAY = Yes"
archosplayer: "(PLAY/STOP)"
cowond2*: "MENU, or top-right = Yes"
- creativezen*: "Select = Yes"
+ creativezen*: "SELECT = Yes"
gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT = Yes"
iriverh100,iriverh120,iriverh300: "NAVI = Yes"
mrobe500: "PLAY, POWER, or top-right = Yes"
@@ -9667,15 +9664,15 @@
user: core
<source>
*: none
- gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
</source>
<dest>
*: none
- gigabeatfx,sansafuzeplus: "ЧувÑтвительноÑÑ‚ÑŒ тачпада"
+ fiiom3k,gigabeatfx,sansafuzeplus: "ЧувÑтвительноÑÑ‚ÑŒ тачпада"
</dest>
<voice>
*: none
- gigabeatfx,sansafuzeplus: "ЧувÑтвительноÑÑ‚ÑŒ тачпада"
+ fiiom3k,gigabeatfx,sansafuzeplus: "ЧувÑтвительноÑÑ‚ÑŒ тачпада"
</voice>
</phrase>
<phrase>
@@ -15278,16 +15275,13 @@
desc: used in track x of y constructs
user: core
<source>
- *: none
- hotkey: "Track"
+ *: "Track"
</source>
<dest>
- *: none
- hotkey: "Трек"
+ *: "Трек"
</dest>
<voice>
- *: none
- hotkey: "ТрÑк"
+ *: "ТрÑк"
</voice>
</phrase>
<phrase>
diff --git a/apps/lang/slovak.lang b/apps/lang/slovak.lang
index 00f98ecd35..2efe3b7b35 100644
--- a/apps/lang/slovak.lang
+++ b/apps/lang/slovak.lang
@@ -247,8 +247,7 @@
<source>
*: "PLAY = Yes"
cowond2*: "MENU, or top-right = Yes"
- creativezen*: "Select = Yes"
- gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT = Yes"
+ creativezen*,gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT = Yes"
iriverh100,iriverh120,iriverh300: "NAVI = Yes"
mrobe500: "PLAY, POWER, or top-right = Yes"
vibe500: "OK = Yes"
@@ -256,8 +255,7 @@
<dest>
*: "PLAY = Ãno"
cowond2*: "MENU, alebo vpravo-hore = Ãno"
- creativezen*: "Select = Ãno"
- gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT = Ãno"
+ creativezen*,gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT = Ãno"
iriverh100,iriverh120,iriverh300: "NAVI = Ãno"
mrobe500: "PLAY, POWER, alebo Vpravo-hore = Ãno"
vibe500: "OK = Ãno"
@@ -1685,10 +1683,10 @@
</phrase>
<phrase>
id: LANG_AUDIOSCROBBLER
- desc: "Last.fm Log" in the playback menu
+ desc: "Last.fm Logger" in Plugin/apps/scrobbler
user: core
<source>
- *: "Last.fm Log"
+ *: "Last.fm Logger"
</source>
<dest>
*: "Denník Last.fm"
@@ -3395,14 +3393,17 @@
<source>
*: none
battery_types: "Alkaline"
+ xduoox3: "Newer (2000 mAh)"
</source>
<dest>
*: none
battery_types: "Alkalická"
+ xduoox3: "Newer (2000 mAh)"
</dest>
<voice>
*: none
battery_types: "Alkalická"
+ xduoox3: "Newer 2000 milliamp hour"
</voice>
</phrase>
<phrase>
@@ -3412,14 +3413,17 @@
<source>
*: none
battery_types: "NiMH"
+ xduoox3: "Older (1500 mAh)"
</source>
<dest>
*: none
battery_types: "NiMH"
+ xduoox3: "Older (1500 mAh)"
</dest>
<voice>
*: none
battery_types: "Nikel metal hydridová"
+ xduoox3: "Older 1500 milliamp hour"
</voice>
</phrase>
<phrase>
@@ -4090,31 +4094,16 @@
</phrase>
<phrase>
id: LANG_ALARM_MOD_KEYS
- desc: Shown key functions in alarm menu (for the RTC alarm mod).
+ desc: deprecated
user: core
<source>
- *: none
- alarm: "PLAY=Set OFF=Cancel"
- gigabeats: "SELECT=Set POWER=Cancel"
- ipod*: "SELECT=Set MENU=Cancel"
- iriverh10,iriverh10_5gb: "SELECT=Set PREV=Cancel"
- mpiohd300: "ENTER=Set MENU=Cancel"
- sansafuzeplus: "SELECT=Set BACK=Cancel"
- vibe500: "OK=Set C=Cancel"
+ *: ""
</source>
<dest>
- *: none
- alarm: "PLAY=Nastaviť OFF=Zrušiť"
- gigabeats: "SELECT=Nastaviť POWER=Zrušiť"
- ipod*: "SELECT=Nastaviť MENU=Zrušiť"
- iriverh10,iriverh10_5gb: "SELECT=Nastaviť PREV=Zrušiť"
- mpiohd300: "ENTER=Nastaviť MENU=Zrušiť"
- sansafuzeplus: "SELECT=Nastaviť BACK=Zrušiť"
- vibe500: "OK=Nastaviť C=Zrušiť"
+ *: ""
</dest>
<voice>
- *: none
- alarm,ipod*: ""
+ *: ""
</voice>
</phrase>
<phrase>
@@ -4941,20 +4930,17 @@
</voice>
</phrase>
<phrase>
- id: LANG_RECORDING_FORMAT
- desc: audio format item in recording menu
+ id: LANG_FORMAT
+ desc: audio format
user: core
<source>
- *: none
- recording: "Format"
+ *: "Format"
</source>
<dest>
- *: none
- recording: "Formát"
+ *: "Formát"
</dest>
<voice>
- *: none
- recording: "Formát"
+ *: "Formát"
</voice>
</phrase>
<phrase>
@@ -5987,12 +5973,10 @@
<source>
*: "Battery: %d%% %dh %dm"
ipodmini1g,ipodmini2g,iriverh10: "Batt: %d%% %dh %dm"
- iriverifp7xx: "%d%% %dh %dm"
</source>
<dest>
*: "Batéria: %d%% %dh %dm"
ipodmini1g,ipodmini2g,iriverh10: "Bat: %d%% %dh %dm"
- iriverifp7xx: "%d%% %dh %dm"
</dest>
<voice>
*: "Stav batérie"
@@ -6032,14 +6016,17 @@
user: core
<source>
*: "Int:"
+ hibylinux: "mSD:"
xduoox3: "mSD1:"
</source>
<dest>
*: "Int:"
+ hibylinux: "mSD:"
xduoox3: "MSD1:"
</dest>
<voice>
*: "Interný"
+ hibylinux: "micro S D"
xduoox3: "mikro S D 1"
</voice>
</phrase>
@@ -6264,16 +6251,16 @@
</phrase>
<phrase>
id: LANG_REPLACE
- desc: in onplay menu. Replace the current playlist with a new one.
+ desc: deprecated
user: core
<source>
- *: "Play Next"
+ *: ""
</source>
<dest>
- *: "Prehrať Ďalšie"
+ *: ""
</dest>
<voice>
- *: "PrehraÅ¥ ÄalÅ¡ie"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -7670,10 +7657,10 @@
*: "."
</source>
<dest>
- *: "."
+ *: ","
</dest>
<voice>
- *: "bodka"
+ *: "Äiarka"
</voice>
</phrase>
<phrase>
@@ -9603,16 +9590,16 @@
user: core
<source>
*: none
- gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
</source>
<dest>
*: none
- gigabeatfx,sansafuzeplus: "Citlivosť Dotykovej Plochy"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Citlivosť Dotykovej Plochy"
</dest>
<voice>
*: none
+ fiiom3k,sansafuzeplus: "Citlivosť Dotykovej Plochy"
gigabeatfx: "Citlivosť dotykovej plochy"
- sansafuzeplus: "Citlivosť Dotykovej Plochy"
</voice>
</phrase>
<phrase>
@@ -12287,13 +12274,13 @@
desc: Selective Actions
user: core
<source>
- *: "Seek"
+ *: "Exempt Seek"
</source>
<dest>
- *: "SkoÄiÅ¥"
+ *: "VyňaÅ¥ skoÄenie"
</dest>
<voice>
- *: "SkoÄiÅ¥"
+ *: "VyňaÅ¥ skoÄenie"
</voice>
</phrase>
<phrase>
@@ -12301,7 +12288,7 @@
desc: Softlock behaviour setting
user: core
<source>
- *: "Disable Notify"
+ *: "Disable Locked Reminders"
</source>
<dest>
*: "Vypnúť upozornenie"
@@ -12469,13 +12456,13 @@
desc: Selective Actions
user: core
<source>
- *: "Play"
+ *: "Exempt Play"
</source>
<dest>
- *: "Prehrať"
+ *: "Vyňať prehratie"
</dest>
<voice>
- *: "Prehrať"
+ *: "Vyňať prehratie"
</voice>
</phrase>
<phrase>
@@ -12483,13 +12470,13 @@
desc: Selective Actions
user: core
<source>
- *: "Skip"
+ *: "Exempt Skip"
</source>
<dest>
- *: "preskoÄiÅ¥"
+ *: "VyňaÅ¥ preskoÄenie"
</dest>
<voice>
- *: "PreskoÄiÅ¥"
+ *: "VyňaÅ¥ preskoÄenie"
</voice>
</phrase>
<phrase>
@@ -12662,16 +12649,16 @@
</phrase>
<phrase>
id: LANG_PROPERTIES_TITLE
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Title]"
+ *: ""
</source>
<dest>
- *: "[Názov]"
+ *: ""
</dest>
<voice>
- *: "Názov"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -12885,11 +12872,11 @@
lowmem: none
</source>
<dest>
- *: "Reverberation"
+ *: "Ozvena"
lowmem: none
</dest>
<voice>
- *: "Reverberation"
+ *: "Ozvena"
lowmem: none
</voice>
</phrase>
@@ -13417,16 +13404,16 @@
</phrase>
<phrase>
id: LANG_PROPERTIES_DURATION
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Duration]"
+ *: ""
</source>
<dest>
- *: "[Trvanie]"
+ *: ""
</dest>
<voice>
- *: "Trvanie"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -14265,16 +14252,16 @@
</phrase>
<phrase>
id: LANG_PROPERTIES_ARTIST
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Artist]"
+ *: ""
</source>
<dest>
- *: "[Interpret]"
+ *: ""
</dest>
<voice>
- *: "Interpret"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -14425,16 +14412,16 @@
</phrase>
<phrase>
id: LANG_PROPERTIES_ALBUM
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Album]"
+ *: ""
</source>
<dest>
- *: "[Album]"
+ *: ""
</dest>
<voice>
- *: "Album"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -15175,16 +15162,13 @@
desc: used in track x of y constructs
user: core
<source>
- *: none
- hotkey: "Track"
+ *: "Track"
</source>
<dest>
- *: none
- hotkey: "Stopa"
+ *: "Stopa"
</dest>
<voice>
- *: none
- hotkey: "Stopa"
+ *: "Stopa"
</voice>
</phrase>
<phrase>
@@ -15306,3 +15290,1009 @@
hotkey: "Pridržte pre nastavenia"
</voice>
</phrase>
+<phrase>
+ id: LANG_SINGLE_MODE
+ desc: single mode
+ user: core
+ <source>
+ *: "Single Mode"
+ </source>
+ <dest>
+ *: "Single režim"
+ </dest>
+ <voice>
+ *: "Single režim"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_TALK_MIXER_LEVEL
+ desc: Relative volume of voice prompts
+ user: core
+ <source>
+ *: "Voice prompt volume"
+ </source>
+ <dest>
+ *: "HlasitosÅ¥ ozvuÄenia"
+ </dest>
+ <voice>
+ *: "HlasitosÅ¥ ozvuÄenia"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FILTER_SHORT_SHARP
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ filter_roll_off: "Short Sharp"
+ </source>
+ <dest>
+ *: none
+ filter_roll_off: "Short Sharp"
+ </dest>
+ <voice>
+ *: none
+ filter_roll_off: "Short Sharp"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FILTER_SHORT_SLOW
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ filter_roll_off: "Short Slow"
+ </source>
+ <dest>
+ *: none
+ filter_roll_off: "Short Slow"
+ </dest>
+ <voice>
+ *: none
+ filter_roll_off: "Short Slow"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FILTER_SUPER_SLOW
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ filter_roll_off: "Super Slow"
+ </source>
+ <dest>
+ *: none
+ filter_roll_off: "Super Slow"
+ </dest>
+ <voice>
+ *: none
+ filter_roll_off: "Super Slow"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FILTER_LINEAR_FAST
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ es9218: "Linear Fast"
+ </source>
+ <dest>
+ *: none
+ es9218: "Linear Fast"
+ </dest>
+ <voice>
+ *: none
+ es9218: "Linear Fast"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FILTER_LINEAR_SLOW
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ es9218: "Linear Slow"
+ </source>
+ <dest>
+ *: none
+ es9218: "Linear Slow"
+ </dest>
+ <voice>
+ *: none
+ es9218: "Linear Slow"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FILTER_MINIMUM_FAST
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ es9218: "Minimum Fast"
+ </source>
+ <dest>
+ *: none
+ es9218: "Minimum Fast"
+ </dest>
+ <voice>
+ *: none
+ es9218: "Minimum Fast"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FILTER_MINIMUM_SLOW
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ es9218: "Minimum Slow"
+ </source>
+ <dest>
+ *: none
+ es9218: "Minimum Slow"
+ </dest>
+ <voice>
+ *: none
+ es9218: "Minimum Slow"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FILTER_APODIZING_1
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ es9218: "Apodizing type 1"
+ </source>
+ <dest>
+ *: none
+ es9218: "Apodizing type 1"
+ </dest>
+ <voice>
+ *: none
+ es9218: "Apodizing type 1"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FILTER_APODIZING_2
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ es9218: "Apodizing type 2"
+ </source>
+ <dest>
+ *: none
+ es9218: "Apodizing type 2"
+ </dest>
+ <voice>
+ *: none
+ es9218: "Apodizing type 2"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FILTER_HYBRID_FAST
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ es9218: "Hybrid Fast"
+ </source>
+ <dest>
+ *: none
+ es9218: "Hybrid Fast"
+ </dest>
+ <voice>
+ *: none
+ es9218: "Hybrid Fast"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FILTER_BRICK_WALL
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ es9218: "Brick Wall"
+ </source>
+ <dest>
+ *: none
+ es9218: "Brick Wall"
+ </dest>
+ <voice>
+ *: none
+ es9218: "Brick Wall"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_DAC_POWER_MODE
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ dac_power_mode: "DAC's power mode"
+ es9218: "DAC's output level"
+ </source>
+ <dest>
+ *: none
+ dac_power_mode: "DAC's power mode"
+ es9218: "DAC's output level"
+ </dest>
+ <voice>
+ *: none
+ dac_power_mode: "DAC's power mode"
+ es9218: "DAC's output level"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_DAC_POWER_HIGH
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ dac_power_mode: "High performance"
+ es9218: "High Gain (2 Vrms)"
+ </source>
+ <dest>
+ *: none
+ dac_power_mode: "High performance"
+ es9218: "High Gain (2 Vrms)"
+ </dest>
+ <voice>
+ *: none
+ dac_power_mode: "High performance"
+ es9218: "High Gain (2 Vrms)"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_DAC_POWER_LOW
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ dac_power_mode: "Save battery"
+ es9218: "Low Gain (1 Vrms)"
+ </source>
+ <dest>
+ *: none
+ dac_power_mode: "Save battery"
+ es9218: "Low Gain (1 Vrms)"
+ </dest>
+ <voice>
+ *: none
+ dac_power_mode: "Save battery"
+ es9218: "Low Gain (1 Vrms)"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_OPEN_PLUGIN
+ desc: onplay open plugin
+ user: core
+ <source>
+ *: "Open Plugin"
+ </source>
+ <dest>
+ *: "Otvoriť plugin"
+ </dest>
+ <voice>
+ *: "Otvoriť plugin"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_OPEN_PLUGIN_NOT_A_PLUGIN
+ desc: open plugin module
+ user: core
+ <source>
+ *: "Not a plugin: %s"
+ </source>
+ <dest>
+ *: "Nie je plugin: %s"
+ </dest>
+ <voice>
+ *: "Nie je plugin"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_OPEN_PLUGIN_SET_WPS_CONTEXT_PLUGIN
+ desc: open plugin module
+ user: core
+ <source>
+ *: "Set Wps Context Plugin"
+ </source>
+ <dest>
+ *: "Set Wps Context Plugin"
+ </dest>
+ <voice>
+ *: "Set WPS Context Plugin"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PARAMETER
+ desc:
+ user: core
+ <source>
+ *: "Parameter"
+ </source>
+ <dest>
+ *: "Parameter"
+ </dest>
+ <voice>
+ *: "Parameter"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_NAME
+ desc:
+ user: core
+ <source>
+ *: "Name"
+ </source>
+ <dest>
+ *: "Názov"
+ </dest>
+ <voice>
+ *: "Názov"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_ADD
+ desc:
+ user: core
+ <source>
+ *: "Add"
+ </source>
+ <dest>
+ *: "Pridať"
+ </dest>
+ <voice>
+ *: "Pridať"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_BACK
+ desc:
+ user: core
+ <source>
+ *: "Back"
+ </source>
+ <dest>
+ *: "Späť"
+ </dest>
+ <voice>
+ *: "Späť"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_EDIT
+ desc:
+ user: core
+ <source>
+ *: "Edit"
+ </source>
+ <dest>
+ *: "Upraviť"
+ </dest>
+ <voice>
+ *: "Upraviť"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_RUN
+ desc:
+ user: core
+ <source>
+ *: "Run"
+ </source>
+ <dest>
+ *: "Spustiť"
+ </dest>
+ <voice>
+ *: "Spustiť"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_EXPORT
+ desc:
+ user: core
+ <source>
+ *: "Export"
+ </source>
+ <dest>
+ *: "Exportovať"
+ </dest>
+ <voice>
+ *: "Exportovať"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_BROWSE
+ desc:
+ user: core
+ <source>
+ *: "Browse"
+ </source>
+ <dest>
+ *: "Prehľadávať"
+ </dest>
+ <voice>
+ *: "Prehľadávať"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_ENTER_USB_STORAGE_MODE_QUERY
+ desc: upon plugging in USB
+ user: core
+ <source>
+ *: "Enter USB mass storage mode?"
+ </source>
+ <dest>
+ *: "Prejsť do režimu USB úložiska?"
+ </dest>
+ <voice>
+ *: "Prejsť do režimu USB úložiska?"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_CLEAR_LIST_AND_PLAY_NEXT
+ desc: deprecated
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_QUEUE_MENU
+ desc: in onplay menu
+ user: core
+ <source>
+ *: "Queue..."
+ </source>
+ <dest>
+ *: "Poradovník..."
+ </dest>
+ <voice>
+ *: "Poradovník..."
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_SHOW_QUEUE_OPTIONS
+ desc: in Current Playlist settings
+ user: core
+ <source>
+ *: "Show Queue Options"
+ </source>
+ <dest>
+ *: "Ukázať voľby poradovníka"
+ </dest>
+ <voice>
+ *: "Ukázať voľby poradovníka"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_SHOW_SHUFFLED_ADDING_OPTIONS
+ desc: in Current Playlist settings
+ user: core
+ <source>
+ *: "Show Shuffled Adding Options"
+ </source>
+ <dest>
+ *: "Ukázať voľby pridávania zamiešane"
+ </dest>
+ <voice>
+ *: "Ukázať voľby pridávania zamiešane"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_IN_SUBMENU
+ desc: in Settings
+ user: core
+ <source>
+ *: "In Submenu"
+ </source>
+ <dest>
+ *: "V podmenu"
+ </dest>
+ <voice>
+ *: "V podmenu"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_CLEAR_LIST_AND_PLAY_SHUFFLED
+ desc: deprecated
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_SOFTLOCK_DISABLE_ALL_NOTIFY
+ desc: disable all softlock notifications
+ user: core
+ <source>
+ *: "Disable All Lock Notifications"
+ </source>
+ <dest>
+ *: "Vypnúť všetky upozornenia o zamykaní"
+ </dest>
+ <voice>
+ *: "Vypnúť všetky upozornenia o zamykaní"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_ACTION_VOLUME
+ desc: exempt volume from softlock
+ user: core
+ <source>
+ *: "Exempt Volume"
+ </source>
+ <dest>
+ *: "Vyňať hlasitosť"
+ </dest>
+ <voice>
+ *: "Vyňať hlasitosť"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_ACTION_ALWAYSAUTOLOCK
+ desc: always prime autolock
+ user: core
+ <source>
+ *: "Always Autolock"
+ </source>
+ <dest>
+ *: "Vždy automaticky zamknúť"
+ </dest>
+ <voice>
+ *: "Vždy automaticky zamknúť"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PLAYLIST_RELOAD_AFTER_SAVE
+ desc: reload playlist after saving
+ user: core
+ <source>
+ *: "Reload After Saving"
+ </source>
+ <dest>
+ *: "NaÄítaÅ¥ znovu po uložení"
+ </dest>
+ <voice>
+ *: "NaÄítaÅ¥ znovu po uložení"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_ALBUMARTIST
+ desc: deprecated
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_GENRE
+ desc: deprecated
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_COMMENT
+ desc: deprecated
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_COMPOSER
+ desc: deprecated
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_YEAR
+ desc: deprecated
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_TRACKNUM
+ desc: deprecated
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_DISCNUM
+ desc: deprecated
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_FREQUENCY
+ desc: deprecated
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_BITRATE
+ desc: deprecated
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: VOICE_NUMERIC_TENS_SWAP_SEPARATOR
+ desc: voice only, for speaking numbers in languages that swap the tens and ones fields. Leave blank for languages that do not need it, such as English ("231" => "two hundred thirty one") but other languages may speak it as "two hundred one [AND] thirty"
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_VOICED_DATE_FORMAT
+ desc: format string for how dates will be read back. Y == 4-digit year, A == month name, m == numeric month, d == numeric day. For example, "AdY" will read "January 21 2021"
+ user: core
+ <source>
+ *: "dAY"
+ </source>
+ <dest>
+ *: "dAY"
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_LIST_WRAPAROUND
+ desc: in Settings
+ user: core
+ <source>
+ *: "List Wraparound"
+ </source>
+ <dest>
+ *: "OtáÄaÅ¥ zoznam"
+ </dest>
+ <voice>
+ *: "OtáÄaÅ¥ zoznam"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_SHOW_SHUTDOWN_MESSAGE
+ desc: in Settings
+ user: core
+ <source>
+ *: "Show Shutdown Message"
+ </source>
+ <dest>
+ *: "Zobraziť správu pri vypínaní"
+ </dest>
+ <voice>
+ *: "Zobraziť správu pri vypínaní"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_LIST_ORDER
+ desc: in Settings
+ user: core
+ <source>
+ *: "List Order"
+ </source>
+ <dest>
+ *: "Poradie zoznamu"
+ </dest>
+ <voice>
+ *: "Poradie zoznamu"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_ASCENDING
+ desc: in Settings
+ user: core
+ <source>
+ *: "Ascending"
+ </source>
+ <dest>
+ *: "Vzostupne"
+ </dest>
+ <voice>
+ *: "Vzostupne"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_DESCENDING
+ desc: in Settings
+ user: core
+ <source>
+ *: "Descending"
+ </source>
+ <dest>
+ *: "Zostupne"
+ </dest>
+ <voice>
+ *: "Zostupne"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_ALBUM_ART
+ desc: in Settings
+ user: core
+ <source>
+ *: "Album Art"
+ </source>
+ <dest>
+ *: "Obal albumu"
+ </dest>
+ <voice>
+ *: "Obal albumu"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PREFER_EMBEDDED
+ desc: in Settings
+ user: core
+ <source>
+ *: "Prefer Embedded"
+ </source>
+ <dest>
+ *: "Uprednostniť vložený"
+ </dest>
+ <voice>
+ *: "Uprednostniť vložený"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PREFER_IMAGE_FILE
+ desc: in Settings
+ user: core
+ <source>
+ *: "Prefer Image File"
+ </source>
+ <dest>
+ *: "Uprednostniť súbor s obrázkom"
+ </dest>
+ <voice>
+ *: "Uprednostniť súbor s obrázkom"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FM_SYNC_RDS_TIME
+ desc: in radio screen and Settings
+ user: core
+ <source>
+ *: none
+ rds: "Sync RDS Time"
+ </source>
+ <dest>
+ *: none
+ rds: "SynchronizovaÅ¥ s Äasom RDS"
+ </dest>
+ <voice>
+ *: none
+ rds: "SynchronizovaÅ¥ s Äasom RDS"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_SORT_ALBUMS_BY
+ desc: in Settings
+ user: core
+ <source>
+ *: "Sort albums by"
+ </source>
+ <dest>
+ *: "Triediť albumy podľa"
+ </dest>
+ <voice>
+ *: "Triediť albumy podľa"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_ARTIST_PLUS_NAME
+ desc: in Settings
+ user: core
+ <source>
+ *: "Artist + Name"
+ </source>
+ <dest>
+ *: "Interpret + názov"
+ </dest>
+ <voice>
+ *: "Interpret a názov"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_ARTIST_PLUS_YEAR
+ desc: in Settings
+ user: core
+ <source>
+ *: "Artist + Year"
+ </source>
+ <dest>
+ *: "Interpret + rok"
+ </dest>
+ <voice>
+ *: "Interpret a rok"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_YEAR_SORT_ORDER
+ desc: in Settings
+ user: core
+ <source>
+ *: "Year sort order"
+ </source>
+ <dest>
+ *: "Triediť podľa roku"
+ </dest>
+ <voice>
+ *: "Triediť podľa roku"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_SHOW_YEAR_IN_ALBUM_TITLE
+ desc: in Settings
+ user: core
+ <source>
+ *: "Show year in album title"
+ </source>
+ <dest>
+ *: "Ukázať rok v názve albumu"
+ </dest>
+ <voice>
+ *: "Ukázať rok v názve albumu"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_WAIT_FOR_CACHE
+ desc: in Settings
+ user: core
+ <source>
+ *: "Cache needs to finish updating first!"
+ </source>
+ <dest>
+ *: "Najprv sa musí dokonÄiÅ¥ aktualizácia cache!"
+ </dest>
+ <voice>
+ *: "Najprv sa musí dokonÄiÅ¥ aktualizácia cache!"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_TRACK_INFO
+ desc: Track Info Title
+ user: core
+ <source>
+ *: "Track Info"
+ </source>
+ <dest>
+ *: "Info o stope"
+ </dest>
+ <voice>
+ *: "Info o stope"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PLAY
+ desc: play selected file/directory, in playlist context menu
+ user: core
+ <source>
+ *: "Play"
+ </source>
+ <dest>
+ *: "Prehrať"
+ </dest>
+ <voice>
+ *: "Prehrať"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PLAY_SHUFFLED
+ desc: play selected files in shuffled order, in playlist context menu
+ user: core
+ <source>
+ *: "Play Shuffled"
+ </source>
+ <dest>
+ *: "Prehrať zamiešané"
+ </dest>
+ <voice>
+ *: "Prehrať zamiešané"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_KEEP_CURRENT_TRACK_ON_REPLACE
+ desc: used in the playlist settings menu
+ user: core
+ <source>
+ *: "Keep Current Track When Replacing Playlist"
+ </source>
+ <dest>
+ *: "Ponechať aktuálnu stopu pri nahradení playlistu"
+ </dest>
+ <voice>
+ *: "Ponechať aktuálnu stopu pri nahradení playlistu"
+ </voice>
+</phrase>
diff --git a/apps/lang/slovenscina.lang b/apps/lang/slovenscina.lang
index 5cc5fbd944..bda482762d 100644
--- a/apps/lang/slovenscina.lang
+++ b/apps/lang/slovenscina.lang
@@ -3896,15 +3896,15 @@
user: core
<source>
*: none
- gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
</source>
<dest>
*: none
- gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
</dest>
<voice>
*: none
- gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
</voice>
</phrase>
<phrase>
@@ -4741,20 +4741,17 @@
</voice>
</phrase>
<phrase>
- id: LANG_RECORDING_FORMAT
- desc: audio format item in recording menu
+ id: LANG_FORMAT
+ desc: audio format
user: core
<source>
- *: none
- recording: "Format"
+ *: "Format"
</source>
<dest>
- *: none
- recording: "Format"
+ *: "Format"
</dest>
<voice>
- *: none
- recording: "Format"
+ *: "Format"
</voice>
</phrase>
<phrase>
diff --git a/apps/lang/srpski.lang b/apps/lang/srpski.lang
index 95063ca0c0..e9b97e9777 100644
--- a/apps/lang/srpski.lang
+++ b/apps/lang/srpski.lang
@@ -247,8 +247,7 @@
<source>
*: "PLAY = Yes"
cowond2*: "MENU, or top-right = Yes"
- creativezen*: "Select = Yes"
- gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT = Yes"
+ creativezen*,gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT = Yes"
iriverh100,iriverh120,iriverh300: "NAVI = Yes"
mrobe500: "PLAY, POWER, or top-right = Yes"
vibe500: "OK = Yes"
@@ -256,8 +255,7 @@
<dest>
*: "PLAY = Да"
cowond2*: "MENU, или горе-деÑно = Да"
- creativezen*: "Select = Да"
- gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT = Да"
+ creativezen*,gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT = Да"
iriverh100,iriverh120,iriverh300: "NAVI = Да"
mrobe500: "PLAY, POWER, или горе-деÑно = Да"
vibe500: "OK = Да"
@@ -278,7 +276,6 @@
</dest>
<voice>
*: ""
- archosplayer: none
</voice>
</phrase>
<phrase>
@@ -799,10 +796,10 @@
*: "Crossfeed"
</source>
<dest>
- *: "Crossfeed"
+ *: "КроÑфид"
</dest>
<voice>
- *: "Crossfeed"
+ *: "КроÑфид"
</voice>
</phrase>
<phrase>
@@ -981,7 +978,7 @@
*: "Edit mode: %s"
</source>
<dest>
- *: "Мод подешавања: %s"
+ *: "Режим уређивања: %s"
</dest>
<voice>
*: ""
@@ -1292,10 +1289,10 @@
*: "Party Mode"
</source>
<dest>
- *: "Мод за журке"
+ *: "Режим за журке"
</dest>
<voice>
- *: "Мод за журке"
+ *: "Режим за журке"
</voice>
</phrase>
<phrase>
@@ -1308,11 +1305,11 @@
</source>
<dest>
*: none
- crossfade: "Crossfade"
+ crossfade: "Претапање"
</dest>
<voice>
*: none
- crossfade: "Crossfade"
+ crossfade: "Претапање"
</voice>
</phrase>
<phrase>
@@ -1325,11 +1322,11 @@
</source>
<dest>
*: none
- crossfade: "Укључи Crossfade"
+ crossfade: "Укључи претапање"
</dest>
<voice>
*: none
- crossfade: "Укључи Crossfade"
+ crossfade: "Укључи претапање"
</voice>
</phrase>
<phrase>
@@ -1444,11 +1441,11 @@
</source>
<dest>
*: none
- crossfade: "Fade-Out мод"
+ crossfade: "Fade-Out режим"
</dest>
<voice>
*: none
- crossfade: "Fade-Out мод"
+ crossfade: "Fade-Out режим"
</voice>
</phrase>
<phrase>
@@ -1461,11 +1458,11 @@
</source>
<dest>
*: none
- crossfade: "Mix"
+ crossfade: "МикÑ"
</dest>
<voice>
*: none
- crossfade: "Mix"
+ crossfade: "МикÑ"
</voice>
</phrase>
<phrase>
@@ -1479,7 +1476,7 @@
*: "Replaygain"
</dest>
<voice>
- *: "Replaygain"
+ *: "Риплејгејн"
</voice>
</phrase>
<phrase>
@@ -1669,16 +1666,16 @@
</phrase>
<phrase>
id: LANG_AUDIOSCROBBLER
- desc: "Last.fm Log" in the playback menu
+ desc: "Last.fm Logger" in Plugin/apps/scrobbler
user: core
<source>
- *: "Last.fm Log"
+ *: "Last.fm Logger"
</source>
<dest>
- *: "Last.fm дневник"
+ *: "Креатор Last.fm дневника"
</dest>
<voice>
- *: "Last.fm дневник"
+ *: "Креатор Last.fm дневника"
</voice>
</phrase>
<phrase>
@@ -2376,11 +2373,11 @@
</source>
<dest>
*: none
- lcd_invert,remote_lcd_invert: "LCD мод"
+ lcd_invert,remote_lcd_invert: "LCD режим"
</dest>
<voice>
*: none
- lcd_invert,remote_lcd_invert: "LCD мод"
+ lcd_invert,remote_lcd_invert: "LCD режим"
</voice>
</phrase>
<phrase>
@@ -3389,7 +3386,7 @@
<voice>
*: none
battery_types: "Ðлкална"
- xduoox3: "Ðовија (2000 милиампер чаÑова)"
+ xduoox3: "Ðовија двехиљаде милиампер чаÑова"
</voice>
</phrase>
<phrase>
@@ -3409,7 +3406,7 @@
<voice>
*: none
battery_types: "Ðикл метал хидридна"
- xduoox3: "Старија (1500 милиампер чаÑова)"
+ xduoox3: "Старија хиљадупетÑто милиампер чаÑова"
</voice>
</phrase>
<phrase>
@@ -3599,7 +3596,7 @@
</dest>
<voice>
*: none
- gigabeat*,gogearsa9200,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh100,iriverh10_5gb,iriverh120,iriverh300,mrobe100,rtc,sansac200*,sansae200*: ""
+ gigabeat*,gogearsa9200,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh100,iriverh10_5gb,iriverh120,iriverh300,mrobe100,rtc,samsungyh*,sansac200*,sansae200*: ""
</voice>
</phrase>
<phrase>
@@ -3969,11 +3966,11 @@
</source>
<dest>
*: none
- charging: "Мод за адаптер у аутомобилу"
+ charging: "Режим за адаптер у аутомобилу"
</dest>
<voice>
*: none
- charging: "Мод за адаптер у аутомобилу"
+ charging: "Режим за адаптер у аутомобилу"
</voice>
</phrase>
<phrase>
@@ -4080,31 +4077,16 @@
</phrase>
<phrase>
id: LANG_ALARM_MOD_KEYS
- desc: Shown key functions in alarm menu (for the RTC alarm mod).
+ desc: deprecated
user: core
<source>
- *: none
- alarm: "PLAY=Set OFF=Cancel"
- gigabeats: "SELECT=Set POWER=Cancel"
- ipod*: "SELECT=Set MENU=Cancel"
- iriverh10,iriverh10_5gb: "SELECT=Set PREV=Cancel"
- mpiohd300: "ENTER=Set MENU=Cancel"
- sansafuzeplus: "SELECT=Set BACK=Cancel"
- vibe500: "OK=Set C=Cancel"
+ *: ""
</source>
<dest>
- *: none
- alarm: "PLAY=ПоÑтави OFF=ОдуÑтани"
- gigabeats: "SELECT=ПоÑтави POWER=ОдуÑтани"
- ipod*: "SELECT=ПоÑтави MENU=ОдуÑтани"
- iriverh10,iriverh10_5gb: "SELECT=ПоÑтави PREV=ОдуÑтани"
- mpiohd300: "ENTER=ПоÑтави MENU=ОдуÑтани"
- sansafuzeplus: "SELECT=ПоÑтави BACK=ОдуÑтани"
- vibe500: "OK=ПоÑтави C=ОдуÑтани"
+ *: ""
</dest>
<voice>
- *: none
- alarm,ipod*: ""
+ *: ""
</voice>
</phrase>
<phrase>
@@ -4931,20 +4913,17 @@
</voice>
</phrase>
<phrase>
- id: LANG_RECORDING_FORMAT
- desc: audio format item in recording menu
+ id: LANG_FORMAT
+ desc: audio format
user: core
<source>
- *: none
- recording: "Format"
+ *: "Format"
</source>
<dest>
- *: none
- recording: "Формат"
+ *: "Формат"
</dest>
<voice>
- *: none
- recording: "Формат"
+ *: "Формат"
</voice>
</phrase>
<phrase>
@@ -4961,7 +4940,7 @@
</dest>
<voice>
*: none
- recording: "MPEG Layer 3"
+ recording: "Епмег лејер три"
</voice>
</phrase>
<phrase>
@@ -4978,7 +4957,7 @@
</dest>
<voice>
*: none
- recording: "PCM Wave"
+ recording: "пи Ñи ем вејв"
</voice>
</phrase>
<phrase>
@@ -4995,7 +4974,7 @@
</dest>
<voice>
*: none
- recording: "WavPack"
+ recording: "вавпак"
</voice>
</phrase>
<phrase>
@@ -5012,7 +4991,7 @@
</dest>
<voice>
*: none
- recording: "AIFF"
+ recording: "еј ај еф еф"
</voice>
</phrase>
<phrase>
@@ -5326,19 +5305,19 @@
</phrase>
<phrase>
id: LANG_SET_AS_REC_DIR
- desc: used in the onplay menu to set a recording dir
+ desc: deprecated
user: core
<source>
*: none
- recording: "Set As Recording Directory"
+ recording: ""
</source>
<dest>
*: none
- recording: "ПоÑтави као директоријум за Ñнимање"
+ recording: ""
</dest>
<voice>
*: none
- recording: "ПоÑтави као директоријум за Ñнимање"
+ recording: ""
</voice>
</phrase>
<phrase>
@@ -5977,12 +5956,10 @@
<source>
*: "Battery: %d%% %dh %dm"
ipodmini1g,ipodmini2g,iriverh10: "Batt: %d%% %dh %dm"
- iriverifp7xx: "%d%% %dh %dm"
</source>
<dest>
*: "Батерија: %d%% %dч %dм"
ipodmini1g,ipodmini2g,iriverh10: "Бат: %d%% %dч %dм"
- iriverifp7xx: "%d%% %dч %dм"
</dest>
<voice>
*: "Ðиво батерије"
@@ -6022,15 +5999,18 @@
user: core
<source>
*: "Int:"
+ hibylinux: "mSD:"
xduoox3: "mSD1:"
</source>
<dest>
*: "Инт:"
+ hibylinux: "mSD:"
xduoox3: "mSD1:"
</dest>
<voice>
*: "Интерни"
- xduoox3: "мајкро Ð•Ñ Ð”Ðµ 1"
+ hibylinux: "мајкро Ð•Ñ Ð”Ðµ"
+ xduoox3: "мајкро Ð•Ñ Ð”Ðµ један"
</voice>
</phrase>
<phrase>
@@ -6051,9 +6031,9 @@
</dest>
<voice>
*: none
- multivolume: "Ха Де 1"
+ multivolume: "Ха Де један"
sansac200*,sansaclipplus,sansae200*,sansafuze*: "мајкро Ð•Ñ Ð”Ðµ"
- xduoox3: "мајкро Ð•Ñ Ð”Ðµ 2"
+ xduoox3: "мајкро Ð•Ñ Ð”Ðµ два"
</voice>
</phrase>
<phrase>
@@ -6142,58 +6122,58 @@
</phrase>
<phrase>
id: LANG_INSERT
- desc: in onplay menu. insert a track/playlist into dynamic playlist.
+ desc: deprecated
user: core
<source>
- *: "Insert"
+ *: ""
</source>
<dest>
- *: "Уметни"
+ *: ""
</dest>
<voice>
- *: "Уметни"
+ *: ""
</voice>
</phrase>
<phrase>
id: LANG_INSERT_FIRST
- desc: in onplay menu. insert a track/playlist into dynamic playlist.
+ desc: deprecated
user: core
<source>
- *: "Insert Next"
+ *: ""
</source>
<dest>
- *: "Уметни наредну"
+ *: ""
</dest>
<voice>
- *: "Уметни наредну"
+ *: ""
</voice>
</phrase>
<phrase>
id: LANG_INSERT_LAST
- desc: in onplay menu. append a track/playlist into dynamic playlist.
+ desc: deprecated
user: core
<source>
- *: "Insert Last"
+ *: ""
</source>
<dest>
- *: "Уметни поÑледњу"
+ *: ""
</dest>
<voice>
- *: "Уметни поÑледњу"
+ *: ""
</voice>
</phrase>
<phrase>
id: LANG_INSERT_SHUFFLED
- desc: in onplay menu. insert a track/playlist randomly into dynamic playlist
+ desc: deprecated
user: core
<source>
- *: "Insert Shuffled"
+ *: ""
</source>
<dest>
- *: "Уметни промешано"
+ *: ""
</dest>
<voice>
- *: "Уметни промешано"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -6254,16 +6234,16 @@
</phrase>
<phrase>
id: LANG_REPLACE
- desc: in onplay menu. Replace the current playlist with a new one.
+ desc: deprecated
user: core
<source>
- *: "Play Next"
+ *: ""
</source>
<dest>
- *: "Репродукуј Ñледећу"
+ *: ""
</dest>
<voice>
- *: "Репродукуј Ñледећу"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -6366,16 +6346,16 @@
</phrase>
<phrase>
id: LANG_CATALOG_ADD_TO
- desc: in onplay playlist catalogue submenu
+ desc: deprecated
user: core
<source>
- *: "Add to Playlist"
+ *: ""
</source>
<dest>
- *: "Додај у плејлиÑту"
+ *: ""
</dest>
<voice>
- *: "Додај у плејлиÑту"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -7244,10 +7224,10 @@
*: "Mode:"
</source>
<dest>
- *: "Мод:"
+ *: "Режим:"
</dest>
<voice>
- *: "Мод:"
+ *: "Режим:"
</voice>
</phrase>
<phrase>
@@ -7348,7 +7328,7 @@
</dest>
<voice>
*: none
- iaudiom5,iaudiox5,iriverh100,iriverh120,iriverh300,recording,sansac200*,sansae200*: ""
+ iaudiom5,iaudiox5,iriverh100,iriverh120,iriverh300,recording,samsungyh*,sansac200*,sansae200*,vibe500: ""
</voice>
</phrase>
<phrase>
@@ -7486,7 +7466,7 @@
*: "Incompatible model"
</source>
<dest>
- *: "Модел није одговарајући"
+ *: "Овај модел није подржан"
</dest>
<voice>
*: "Ðеодговарајући модел"
@@ -8307,7 +8287,7 @@
*: ""
</dest>
<voice>
- *: "pixel"
+ *: "пикÑела"
</voice>
</phrase>
<phrase>
@@ -8335,7 +8315,7 @@
*: ""
</dest>
<voice>
- *: "херц"
+ *: "херца"
</voice>
</phrase>
<phrase>
@@ -8811,7 +8791,7 @@
*: ""
</dest>
<voice>
- *: "while-playing-screen"
+ *: "екран-током-репродукције"
</voice>
</phrase>
<phrase>
@@ -8867,7 +8847,7 @@
*: ""
</dest>
<voice>
- *: "firmware"
+ *: "фирмвер"
</voice>
</phrase>
<phrase>
@@ -8912,7 +8892,7 @@
*: ""
</dest>
<voice>
- *: "cuesheet"
+ *: "кјулиÑта"
</voice>
</phrase>
<phrase>
@@ -9111,7 +9091,7 @@
*: ""
</dest>
<voice>
- *: "units per tick"
+ *: "јединица по подеоку"
</voice>
</phrase>
<phrase>
@@ -9125,7 +9105,7 @@
*: ""
</dest>
<voice>
- *: "o'clock"
+ *: "Ñати"
</voice>
</phrase>
<phrase>
@@ -9139,7 +9119,7 @@
*: ""
</dest>
<voice>
- *: "P M"
+ *: "поподне"
</voice>
</phrase>
<phrase>
@@ -9153,7 +9133,7 @@
*: ""
</dest>
<voice>
- *: "A M"
+ *: "преподне"
</voice>
</phrase>
<phrase>
@@ -9167,7 +9147,7 @@
*: ""
</dest>
<voice>
- *: "oh"
+ *: "о"
</voice>
</phrase>
<phrase>
@@ -9345,7 +9325,7 @@
*: ""
</dest>
<voice>
- *: "Edit"
+ *: "Уреди"
</voice>
</phrase>
<phrase>
@@ -9359,7 +9339,7 @@
*: ""
</dest>
<voice>
- *: "Blank"
+ *: "Празно"
</voice>
</phrase>
<phrase>
@@ -9373,7 +9353,7 @@
*: ""
</dest>
<voice>
- *: "Empty list"
+ *: "Празна лиÑта"
</voice>
</phrase>
<phrase>
@@ -9539,7 +9519,7 @@
*: ""
</dest>
<voice>
- *: "Quick screen"
+ *: "Брзи екран"
</voice>
</phrase>
<phrase>
@@ -9567,7 +9547,7 @@
*: ""
</dest>
<voice>
- *: "OK"
+ *: "О Кеј"
</voice>
</phrase>
<phrase>
@@ -9601,7 +9581,7 @@
</dest>
<voice>
*: none
- recording: ""
+ recording: "Величина"
</voice>
</phrase>
<phrase>
@@ -9627,15 +9607,15 @@
user: core
<source>
*: none
- gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
</source>
<dest>
*: none
- gigabeatfx,sansafuzeplus: "ОÑетљивоÑÑ‚ Touchpad-а"
+ fiiom3k,gigabeatfx,sansafuzeplus: "ОÑетљивоÑÑ‚ Touchpad-а"
</dest>
<voice>
*: none
- gigabeatfx,sansafuzeplus: "ОÑетљивоÑÑ‚ Touchpad-а"
+ fiiom3k,gigabeatfx,sansafuzeplus: "ОÑетљивоÑÑ‚ Touchpad-а"
</voice>
</phrase>
<phrase>
@@ -9694,7 +9674,7 @@
</dest>
<voice>
*: none
- recording: ""
+ recording: "Време дељења"
</voice>
</phrase>
<phrase>
@@ -9725,7 +9705,7 @@
</dest>
<voice>
*: none
- recording: ""
+ recording: "КЛИП"
</voice>
</phrase>
<phrase>
@@ -9755,11 +9735,11 @@
</source>
<dest>
*: none
- recording: "Фајл:"
+ recording: "Име фајла:"
</dest>
<voice>
*: none
- recording: ""
+ recording: "Име фајла"
</voice>
</phrase>
<phrase>
@@ -9817,7 +9797,7 @@
</source>
<dest>
*: none
- agc: "AGC"
+ agc: "ÐРП"
</dest>
<voice>
*: none
@@ -9834,11 +9814,11 @@
</source>
<dest>
*: none
- agc: "AGC време клиповања"
+ agc: "ÐРП време клиповања"
</dest>
<voice>
*: none
- agc: "AGC време клиповања"
+ agc: "AРП време клиповања"
</voice>
</phrase>
<phrase>
@@ -9978,11 +9958,11 @@
</source>
<dest>
*: none
- recording: "Моно мод"
+ recording: "Моно режим"
</dest>
<voice>
*: none
- recording: "Моно мод"
+ recording: "Моно режим"
</voice>
</phrase>
<phrase>
@@ -10204,11 +10184,11 @@
</source>
<dest>
*: none
- touchscreen: "ТачÑкрин мод"
+ touchscreen: "ТачÑкрин режим"
</dest>
<voice>
*: none
- touchscreen: "ТачÑкрин мод"
+ touchscreen: "ТачÑкрин режим"
</voice>
</phrase>
<phrase>
@@ -10250,7 +10230,7 @@
*: ""
</dest>
<voice>
- *: "statusbar skin"
+ *: "изглед ÑтатуÑне линије"
</voice>
</phrase>
<phrase>
@@ -10340,7 +10320,7 @@
</dest>
<voice>
*: none
- remote: "remote statusbar skin"
+ remote: "изглед удаљене ÑтатуÑне линије"
</voice>
</phrase>
<phrase>
@@ -10351,10 +10331,10 @@
*: "Knee"
</source>
<dest>
- *: "Knee"
+ *: "Колено"
</dest>
<voice>
- *: "Ðи"
+ *: "колено"
</voice>
</phrase>
<phrase>
@@ -10373,16 +10353,16 @@
</phrase>
<phrase>
id: LANG_INSERT_LAST_SHUFFLED
- desc: in onplay menu. insert a playlist randomly at end of dynamic playlist
+ desc: deprecated
user: core
<source>
- *: "Insert Last Shuffled"
+ *: ""
</source>
<dest>
- *: "Уметни на крај промешану"
+ *: ""
</dest>
<voice>
- *: "Уметни на крај промешану"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -10393,10 +10373,10 @@
*: "Soft Knee"
</source>
<dest>
- *: "Soft Knee"
+ *: "Меко колено"
</dest>
<voice>
- *: "Софт ни"
+ *: "меко колено"
</voice>
</phrase>
<phrase>
@@ -10437,11 +10417,11 @@
</source>
<dest>
*: none
- usb_hid: "Мод USB таÑтатуре"
+ usb_hid: "Режим USB таÑтатуре"
</dest>
<voice>
*: none
- usb_hid: "Мод USB таÑтатуре"
+ usb_hid: "режим у ÐµÑ Ð±Ðµ таÑтатуре"
</voice>
</phrase>
<phrase>
@@ -10452,10 +10432,10 @@
*: "Hard Knee"
</source>
<dest>
- *: "Hard Knee"
+ *: "Тврдо колено"
</dest>
<voice>
- *: "Хард ни"
+ *: "тврдо колено"
</voice>
</phrase>
<phrase>
@@ -10485,11 +10465,11 @@
</source>
<dest>
*: none
- pitchscreen: "Rate"
+ pitchscreen: "Стопа"
</dest>
<voice>
*: none
- pitchscreen: "Рејт"
+ pitchscreen: "Стопа"
</voice>
</phrase>
<phrase>
@@ -10523,7 +10503,7 @@
</dest>
<voice>
*: none
- usb_hid: "USB Human Interface Device"
+ usb_hid: "У ÐµÑ Ð±Ðµ хјуман Ð¸Ð½Ñ‚ÐµÑ€Ñ„ÐµÑ˜Ñ Ð´Ð¸Ð²Ð°Ñ˜Ñ"
</voice>
</phrase>
<phrase>
@@ -10816,10 +10796,10 @@
*: "Timestretch"
</source>
<dest>
- *: "Timestretch"
+ *: "ВременÑко раÑтезање"
</dest>
<voice>
- *: "Timestretch"
+ *: "ВременÑко раÑтезање"
</voice>
</phrase>
<phrase>
@@ -10978,11 +10958,11 @@
</source>
<dest>
*: none
- touchscreen: "OK"
+ touchscreen: "ОК"
</dest>
<voice>
*: none
- touchscreen: "OK"
+ touchscreen: "О Кеј"
</voice>
</phrase>
<phrase>
@@ -11115,7 +11095,7 @@
</dest>
<voice>
*: none
- touchscreen: "Del"
+ touchscreen: "Дел"
</voice>
</phrase>
<phrase>
@@ -11487,16 +11467,16 @@
</phrase>
<phrase>
id: LANG_SET_AS_START_DIR
- desc: used in the onplay menu to set a starting browser dir
+ desc: deprecated
user: core
<source>
- *: "Start File Browser Here"
+ *: ""
</source>
<dest>
- *: "Покрени прегледач фајлова у овом фолдеру"
+ *: ""
</dest>
<voice>
- *: "Покрени прегледач фајлова у овом фолдеру"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -11667,16 +11647,16 @@
</phrase>
<phrase>
id: LANG_SET_AS_PLAYLISTCAT_DIR
- desc: used in the onplay menu to set a playlist catalogue dir
+ desc: deprecated
user: core
<source>
- *: "Set As Playlist Catalogue Directory"
+ *: ""
</source>
<dest>
- *: "ПоÑтави као директоријум каталога плејлиÑти"
+ *: ""
</dest>
<voice>
- *: "ПоÑтави као директоријум каталога плејлиÑти"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -11802,10 +11782,10 @@
*: "Simple (Meier)"
</source>
<dest>
- *: "ЈедноÑтаван (Meier)"
+ *: "ЈедноÑтаван (Мајер)"
</dest>
<voice>
- *: "ЈедноÑтаван Мејеров"
+ *: "ЈедноÑтаван Мајеров"
</voice>
</phrase>
<phrase>
@@ -11982,7 +11962,7 @@
*: "Q"
</dest>
<voice>
- *: "Q"
+ *: "Ку фактор"
</voice>
</phrase>
<phrase>
@@ -12026,11 +12006,11 @@
</source>
<dest>
*: none
- filter_roll_off: "Стрмина DAC филтра"
+ filter_roll_off: "Стрмина ДÐК филтра"
</dest>
<voice>
*: none
- filter_roll_off: "Стрмина DAC филтра"
+ filter_roll_off: "Стрмина ДÐК филтра"
</voice>
</phrase>
<phrase>
@@ -12041,10 +12021,10 @@
*: "Line Separator"
</source>
<dest>
- *: "Сепаратор линија"
+ *: "Граничник линија"
</dest>
<voice>
- *: "Сепаратор линија"
+ *: "Граничник линија"
</voice>
</phrase>
<phrase>
@@ -12069,10 +12049,10 @@
*: "Line Separator Colour"
</source>
<dest>
- *: "Боја Ñепаратора линија"
+ *: "Боја граничника линија"
</dest>
<voice>
- *: "Боја Ñепаратора линија"
+ *: "Боја граничника линија"
</voice>
</phrase>
<phrase>
@@ -12125,13 +12105,13 @@
desc: Selective Actions
user: core
<source>
- *: "Play"
+ *: "Exempt Play"
</source>
<dest>
- *: "Репродукуј"
+ *: "Изузми репродукцију"
</dest>
<voice>
- *: "Репродукуј"
+ *: "Изузми репродукцију"
</voice>
</phrase>
<phrase>
@@ -12139,13 +12119,13 @@
desc: Softlock behaviour setting
user: core
<source>
- *: "Disable Notify"
+ *: "Disable Locked Reminders"
</source>
<dest>
- *: "Онемогући обавештење"
+ *: "Онемогући закључана обавештења"
</dest>
<voice>
- *: "Онемогући обавештење"
+ *: "Онемогући закључана обавештења"
</voice>
</phrase>
<phrase>
@@ -12184,10 +12164,10 @@
*: "USB Mode"
</source>
<dest>
- *: "USB Мод"
+ *: "USB режим"
</dest>
<voice>
- *: "USB Мод"
+ *: "USB режим"
</voice>
</phrase>
<phrase>
@@ -12212,10 +12192,10 @@
*: "Android Debug Bridge"
</source>
<dest>
- *: "Android Debug Bridge"
+ *: "Ðндроид дибаг моÑÑ‚"
</dest>
<voice>
- *: "Android Debug Bridge"
+ *: "Ðндроид дибаг моÑÑ‚"
</voice>
</phrase>
<phrase>
@@ -12251,13 +12231,13 @@
desc: Selective Actions
user: core
<source>
- *: "Skip"
+ *: "Exempt Skip"
</source>
<dest>
- *: "ПреÑкочи"
+ *: "Изузми преÑкакање"
</dest>
<voice>
- *: "ПреÑкочи"
+ *: "Изузми преÑкакање"
</voice>
</phrase>
<phrase>
@@ -12268,10 +12248,10 @@
*: "Haas Surround"
</source>
<dest>
- *: "Haas Surround"
+ *: "Ð¥Ð°Ñ Ð¾ÐºÑ€ÑƒÐ¶ÑƒÑ˜ÑƒÑ›Ð¸ звук"
</dest>
<voice>
- *: "Haas Surround"
+ *: "Ð¥Ð°Ñ Ð¾ÐºÑ€ÑƒÐ¶ÑƒÑ˜ÑƒÑ›Ð¸ звук"
</voice>
</phrase>
<phrase>
@@ -12279,13 +12259,13 @@
desc: Selective Actions
user: core
<source>
- *: "Seek"
+ *: "Exempt Seek"
</source>
<dest>
- *: "Премотај"
+ *: "Изузми премотавање"
</dest>
<voice>
- *: "Премотај"
+ *: "Изузми премотавање"
</voice>
</phrase>
<phrase>
@@ -12919,7 +12899,7 @@
*: "ОÑобине директоријума"
</dest>
<voice>
- *: ""
+ *: "ОÑобине директоријума"
</voice>
</phrase>
<phrase>
@@ -12949,7 +12929,7 @@
lowmem: none
</dest>
<voice>
- *: "ПоÑтави време наÑтављања"
+ *: "ПоÑтави време наÑтављања у минутама"
lowmem: none
</voice>
</phrase>
@@ -13028,16 +13008,16 @@
</phrase>
<phrase>
id: LANG_PROPERTIES_ALBUM
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Album]"
+ *: ""
</source>
<dest>
- *: "[Ðлбум]"
+ *: ""
</dest>
<voice>
- *: "Ðлбум"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -13138,7 +13118,7 @@
*: "РедоÑлед Rockbox главног менија"
</dest>
<voice>
- *: ""
+ *: "РедоÑлед Ð Ð¾ÐºÐ±Ð¾ÐºÑ Ð³Ð»Ð°Ð²Ð½Ð¾Ð³ менија"
</voice>
</phrase>
<phrase>
@@ -13152,7 +13132,7 @@
*: "ОÑобине фајла"
</dest>
<voice>
- *: ""
+ *: "ОÑобине фајла"
</voice>
</phrase>
<phrase>
@@ -13205,10 +13185,10 @@
*: "Cache will be rebuilt on next restart"
</source>
<dest>
- *: "Кеш ће да Ñе изради при наредном поновном покретању"
+ *: "Кеш ће да Ñе изгради при наредном поновном покретању"
</dest>
<voice>
- *: "Кеш ће да Ñе изради при наредном поновном покретању"
+ *: "Кеш ће да Ñе изгради при наредном поновном покретању"
</voice>
</phrase>
<phrase>
@@ -13370,16 +13350,16 @@
</phrase>
<phrase>
id: LANG_PROPERTIES_TITLE
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Title]"
+ *: ""
</source>
<dest>
- *: "[ÐаÑлов]"
+ *: ""
</dest>
<voice>
- *: "ÐаÑлов"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -13785,16 +13765,16 @@
</phrase>
<phrase>
id: LANG_PROPERTIES_ARTIST
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Artist]"
+ *: ""
</source>
<dest>
- *: "[Уметник]"
+ *: ""
</dest>
<voice>
- *: "Уметник"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -13889,16 +13869,16 @@
</phrase>
<phrase>
id: LANG_PROPERTIES_DURATION
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Duration]"
+ *: ""
</source>
<dest>
- *: "[Трајање]"
+ *: ""
</dest>
<voice>
- *: "Трајање"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -14262,7 +14242,7 @@
</phrase>
<phrase>
id: LANG_DIRECT
- desc: in the pictureflow settings
+ desc: in the pictureflow settings, also a volume adjustment mode
user: core
<source>
*: "Direct"
@@ -14302,7 +14282,7 @@
*: ""
</dest>
<voice>
- *: ""
+ *: "ПритиÑните репродукцију за покретање теÑта батерије или Ñтоп за отказивање"
</voice>
</phrase>
<phrase>
@@ -14586,7 +14566,7 @@
*: ""
</dest>
<voice>
- *: ""
+ *: "Обележено"
</voice>
</phrase>
<phrase>
@@ -14875,7 +14855,7 @@
*: "Играј Вормлет!"
</dest>
<voice>
- *: "Играј Вормлет!!"
+ *: "Играј Вормлет!"
</voice>
</phrase>
<phrase>
@@ -15102,16 +15082,13 @@
desc: used in track x of y constructs
user: core
<source>
- *: none
- hotkey: "Track"
+ *: "Track"
</source>
<dest>
- *: none
- hotkey: "Ðумера"
+ *: "Ðумера"
</dest>
<voice>
- *: none
- hotkey: "Ðумера"
+ *: "Ðумера"
</voice>
</phrase>
<phrase>
@@ -15216,11 +15193,11 @@
lowmem: none
</source>
<dest>
- *: "УчеÑтаноÑÑ‚ узорковања"
+ *: "УчеÑталоÑÑ‚ узорковања"
lowmem: none
</dest>
<voice>
- *: "УчеÑтаноÑÑ‚ узорковања"
+ *: "УчеÑталоÑÑ‚ узорковања"
lowmem: none
</voice>
</phrase>
@@ -15355,7 +15332,6 @@
mpiohd200: "Дупли тап REC за прекид."
mpiohd300: "Дупли тап MENU за прекид."
rx27generic: "ПритиÑните VOLUME за прекид."
- sonynwza860: "Мапе таÑтера ниÑу комплетне."
touchscreen: "ПритиÑните Middle Left за прекид."
vibe500: "ПритиÑните PREV за прекид."
xduoox20,xduoox3,xduoox3ii: "Дупли тап HOME за прекид."
@@ -15465,3 +15441,1097 @@
hotkey: "Објављивање укључено"
</voice>
</phrase>
+<phrase>
+ id: LANG_SINGLE_MODE
+ desc: single mode
+ user: core
+ <source>
+ *: "Single Mode"
+ </source>
+ <dest>
+ *: "Режим Ñамо једне"
+ </dest>
+ <voice>
+ *: "режим Ñамо једне"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_TALK_MIXER_LEVEL
+ desc: Relative volume of voice prompts
+ user: core
+ <source>
+ *: "Voice prompt volume"
+ </source>
+ <dest>
+ *: "Јачина глаÑа"
+ </dest>
+ <voice>
+ *: "јачина глаÑа"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FILTER_SHORT_SHARP
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ filter_roll_off: "Short Sharp"
+ </source>
+ <dest>
+ *: none
+ filter_roll_off: "Кратки оштар"
+ </dest>
+ <voice>
+ *: none
+ filter_roll_off: "кратки оштар"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FILTER_SHORT_SLOW
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ filter_roll_off: "Short Slow"
+ </source>
+ <dest>
+ *: none
+ filter_roll_off: "Кратки Ñпори"
+ </dest>
+ <voice>
+ *: none
+ filter_roll_off: "Кратки Ñпори"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FILTER_SUPER_SLOW
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ filter_roll_off: "Super Slow"
+ </source>
+ <dest>
+ *: none
+ filter_roll_off: "Супер Ñпори"
+ </dest>
+ <voice>
+ *: none
+ filter_roll_off: "Супер Ñпори"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FILTER_LINEAR_FAST
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ es9218: "Linear Fast"
+ </source>
+ <dest>
+ *: none
+ es9218: "Линеарни брзи"
+ </dest>
+ <voice>
+ *: none
+ es9218: "линеарни брзи"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FILTER_LINEAR_SLOW
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ es9218: "Linear Slow"
+ </source>
+ <dest>
+ *: none
+ es9218: "Линеарни Ñпори"
+ </dest>
+ <voice>
+ *: none
+ es9218: "линеарни Ñпори"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FILTER_MINIMUM_FAST
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ es9218: "Minimum Fast"
+ </source>
+ <dest>
+ *: none
+ es9218: "Минимални брзи"
+ </dest>
+ <voice>
+ *: none
+ es9218: "минимални брзи"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FILTER_MINIMUM_SLOW
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ es9218: "Minimum Slow"
+ </source>
+ <dest>
+ *: none
+ es9218: "Минимални Ñпори"
+ </dest>
+ <voice>
+ *: none
+ es9218: "минимални Ñпори"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FILTER_APODIZING_1
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ es9218: "Apodizing type 1"
+ </source>
+ <dest>
+ *: none
+ es9218: "Ðподизациони тип 1"
+ </dest>
+ <voice>
+ *: none
+ es9218: "Ðподизациони тип 1"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FILTER_APODIZING_2
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ es9218: "Apodizing type 2"
+ </source>
+ <dest>
+ *: none
+ es9218: "Ðподизациони тип 2"
+ </dest>
+ <voice>
+ *: none
+ es9218: "Ðподизациони тип 2"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FILTER_HYBRID_FAST
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ es9218: "Hybrid Fast"
+ </source>
+ <dest>
+ *: none
+ es9218: "Хибридни брзи"
+ </dest>
+ <voice>
+ *: none
+ es9218: "Хибридни брзи"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FILTER_BRICK_WALL
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ es9218: "Brick Wall"
+ </source>
+ <dest>
+ *: none
+ es9218: "Зид"
+ </dest>
+ <voice>
+ *: none
+ es9218: "Зид"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_DAC_POWER_MODE
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ dac_power_mode: "DAC's power mode"
+ es9218: "DAC's output level"
+ </source>
+ <dest>
+ *: none
+ dac_power_mode: "Режим напајања ДÐК"
+ es9218: "Излазни ниво ДÐК"
+ </dest>
+ <voice>
+ *: none
+ dac_power_mode: "Режим напајања ДÐК"
+ es9218: "Излазни ниво ДÐК"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_DAC_POWER_HIGH
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ dac_power_mode: "High performance"
+ es9218: "High Gain (2 Vrms)"
+ </source>
+ <dest>
+ *: none
+ dac_power_mode: "ВиÑоке перформанÑе"
+ es9218: "ВиÑоко појачање (2 Vrms)"
+ </dest>
+ <voice>
+ *: none
+ dac_power_mode: "ВиÑоке перформанÑе"
+ es9218: "ВиÑоко појачање два волта ефективно"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_DAC_POWER_LOW
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ dac_power_mode: "Save battery"
+ es9218: "Low Gain (1 Vrms)"
+ </source>
+ <dest>
+ *: none
+ dac_power_mode: "Штедња батерије"
+ es9218: "ÐиÑко појачање (1 Vrms)"
+ </dest>
+ <voice>
+ *: none
+ dac_power_mode: "Штедња батерије"
+ es9218: "ÐиÑко појачање један волт ефективно"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_ENTER_USB_STORAGE_MODE_QUERY
+ desc: upon plugging in USB
+ user: core
+ <source>
+ *: "Enter USB mass storage mode?"
+ </source>
+ <dest>
+ *: "Да пређем у режим USB маÑовне меморије?"
+ </dest>
+ <voice>
+ *: "Да пређем у режим У Ð•Ñ Ð‘Ðµ маÑовне меморије?"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_CLEAR_LIST_AND_PLAY_NEXT
+ desc: deprecated
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_QUEUE_MENU
+ desc: in onplay menu
+ user: core
+ <source>
+ *: "Queue..."
+ </source>
+ <dest>
+ *: "Ред..."
+ </dest>
+ <voice>
+ *: "ред..."
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_SHOW_QUEUE_OPTIONS
+ desc: in Current Playlist settings
+ user: core
+ <source>
+ *: "Show Queue Options"
+ </source>
+ <dest>
+ *: "Прикажи опције реда"
+ </dest>
+ <voice>
+ *: "Прикажи опције реда"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_SHOW_SHUFFLED_ADDING_OPTIONS
+ desc: in Current Playlist settings
+ user: core
+ <source>
+ *: "Show Shuffled Adding Options"
+ </source>
+ <dest>
+ *: "Прикажи опције измешаног додавања"
+ </dest>
+ <voice>
+ *: "Прикажи опције измешаног додавања"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_IN_SUBMENU
+ desc: in Settings
+ user: core
+ <source>
+ *: "In Submenu"
+ </source>
+ <dest>
+ *: "У подменију"
+ </dest>
+ <voice>
+ *: "У подменију"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_CLEAR_LIST_AND_PLAY_SHUFFLED
+ desc: deprecated
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_SOFTLOCK_DISABLE_ALL_NOTIFY
+ desc: disable all softlock notifications
+ user: core
+ <source>
+ *: "Disable All Lock Notifications"
+ </source>
+ <dest>
+ *: "ИÑкључи Ñва закључана обавештења"
+ </dest>
+ <voice>
+ *: "ИÑкључи Ñва закључана обавештења"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_ACTION_VOLUME
+ desc: exempt volume from softlock
+ user: core
+ <source>
+ *: "Exempt Volume"
+ </source>
+ <dest>
+ *: "Изузми јачину"
+ </dest>
+ <voice>
+ *: "Изузми јачину"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_ACTION_ALWAYSAUTOLOCK
+ desc: always prime autolock
+ user: core
+ <source>
+ *: "Always Autolock"
+ </source>
+ <dest>
+ *: "Увек аутоматÑки закључај"
+ </dest>
+ <voice>
+ *: "Увек аутоматÑки закључај"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PLAYLIST_RELOAD_AFTER_SAVE
+ desc: reload playlist after saving
+ user: core
+ <source>
+ *: "Reload After Saving"
+ </source>
+ <dest>
+ *: "Поново учитај након чувања"
+ </dest>
+ <voice>
+ *: "Поново учитај након чувања"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_ALBUMARTIST
+ desc: deprecated
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_GENRE
+ desc: deprecated
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_COMMENT
+ desc: deprecated
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_COMPOSER
+ desc: deprecated
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_YEAR
+ desc: deprecated
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_TRACKNUM
+ desc: deprecated
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_DISCNUM
+ desc: deprecated
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_FREQUENCY
+ desc: deprecated
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_BITRATE
+ desc: deprecated
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: VOICE_NUMERIC_TENS_SWAP_SEPARATOR
+ desc: voice only, for speaking numbers in languages that swap the tens and ones fields. Leave blank for languages that do not need it, such as English ("231" => "two hundred thirty one") but other languages may speak it as "two hundred one [AND] thirty"
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_VOICED_DATE_FORMAT
+ desc: format string for how dates will be read back. Y == 4-digit year, A == month name, m == numeric month, d == numeric day. For example, "AdY" will read "January 21 2021"
+ user: core
+ <source>
+ *: "dAY"
+ </source>
+ <dest>
+ *: "dAY"
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_LIST_WRAPAROUND
+ desc: in Settings
+ user: core
+ <source>
+ *: "List Wraparound"
+ </source>
+ <dest>
+ *: "Обмотавање лиÑте"
+ </dest>
+ <voice>
+ *: "Обмотавање лиÑте"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_SHOW_SHUTDOWN_MESSAGE
+ desc: in Settings
+ user: core
+ <source>
+ *: "Show Shutdown Message"
+ </source>
+ <dest>
+ *: "Прикажи поруку иÑкључивања"
+ </dest>
+ <voice>
+ *: "Прикажи поруку иÑкључивања"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_LIST_ORDER
+ desc: in Settings
+ user: core
+ <source>
+ *: "List Order"
+ </source>
+ <dest>
+ *: "РедоÑлед лиÑте"
+ </dest>
+ <voice>
+ *: "РедоÑлед лиÑте"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_ASCENDING
+ desc: in Settings
+ user: core
+ <source>
+ *: "Ascending"
+ </source>
+ <dest>
+ *: "РаÑтући"
+ </dest>
+ <voice>
+ *: "раÑтући"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_DESCENDING
+ desc: in Settings
+ user: core
+ <source>
+ *: "Descending"
+ </source>
+ <dest>
+ *: "Опадајући"
+ </dest>
+ <voice>
+ *: "опадајући"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_ALBUM_ART
+ desc: in Settings
+ user: core
+ <source>
+ *: "Album Art"
+ </source>
+ <dest>
+ *: "Слика омота"
+ </dest>
+ <voice>
+ *: "Ñлика омота"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PREFER_EMBEDDED
+ desc: in Settings
+ user: core
+ <source>
+ *: "Prefer Embedded"
+ </source>
+ <dest>
+ *: "ПредноÑÑ‚ има уграђена"
+ </dest>
+ <voice>
+ *: "ПредноÑÑ‚ има уграђена"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PREFER_IMAGE_FILE
+ desc: in Settings
+ user: core
+ <source>
+ *: "Prefer Image File"
+ </source>
+ <dest>
+ *: "ПредноÑÑ‚ има фајл Ñлике"
+ </dest>
+ <voice>
+ *: "ПредноÑÑ‚ има фајл Ñлике"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FM_SYNC_RDS_TIME
+ desc: in radio screen and Settings
+ user: core
+ <source>
+ *: none
+ rds: "Sync RDS Time"
+ </source>
+ <dest>
+ *: none
+ rds: "Синхронизуј Ñа RDS временом"
+ </dest>
+ <voice>
+ *: none
+ rds: "Синхронизуј Ñа Ер де ÐµÑ Ð²Ñ€ÐµÐ¼ÐµÐ½Ð¾Ð¼"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_SORT_ALBUMS_BY
+ desc: in Settings
+ user: core
+ <source>
+ *: "Sort albums by"
+ </source>
+ <dest>
+ *: "Сортирај албуме по"
+ </dest>
+ <voice>
+ *: "Сортирај албуме по"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_ARTIST_PLUS_NAME
+ desc: in Settings
+ user: core
+ <source>
+ *: "Artist + Name"
+ </source>
+ <dest>
+ *: "Уметник + Име"
+ </dest>
+ <voice>
+ *: "Уметник и име"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_ARTIST_PLUS_YEAR
+ desc: in Settings
+ user: core
+ <source>
+ *: "Artist + Year"
+ </source>
+ <dest>
+ *: "Уметник + Година"
+ </dest>
+ <voice>
+ *: "Ументик и година"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_YEAR_SORT_ORDER
+ desc: in Settings
+ user: core
+ <source>
+ *: "Year sort order"
+ </source>
+ <dest>
+ *: "РедоÑлед Ñортирања године"
+ </dest>
+ <voice>
+ *: "РедоÑлед Ñортирања године"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_SHOW_YEAR_IN_ALBUM_TITLE
+ desc: in Settings
+ user: core
+ <source>
+ *: "Show year in album title"
+ </source>
+ <dest>
+ *: "Приказуј годину у наÑлову албума"
+ </dest>
+ <voice>
+ *: "Приказуј годину у наÑлову албума"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_WAIT_FOR_CACHE
+ desc: in Settings
+ user: core
+ <source>
+ *: "Cache needs to finish updating first!"
+ </source>
+ <dest>
+ *: "Ðајпре мора да Ñе оÑвежи кеш!"
+ </dest>
+ <voice>
+ *: "Ðајпре мора да Ñе оÑвежи кеш!"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_TRACK_INFO
+ desc: Track Info Title
+ user: core
+ <source>
+ *: "Track Info"
+ </source>
+ <dest>
+ *: "Инфо о нумери"
+ </dest>
+ <voice>
+ *: "Инфо о нумери"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PLAY
+ desc: play selected file/directory, in playlist context menu
+ user: core
+ <source>
+ *: "Play"
+ </source>
+ <dest>
+ *: "ПуÑти"
+ </dest>
+ <voice>
+ *: "ПуÑти"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PLAY_SHUFFLED
+ desc: play selected files in shuffled order, in playlist context menu
+ user: core
+ <source>
+ *: "Play Shuffled"
+ </source>
+ <dest>
+ *: "ПуÑти измешано"
+ </dest>
+ <voice>
+ *: "ПуÑти измешано"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_KEEP_CURRENT_TRACK_ON_REPLACE
+ desc: used in the playlist settings menu
+ user: core
+ <source>
+ *: "Keep Current Track When Replacing Playlist"
+ </source>
+ <dest>
+ *: "Задржи текућу нумеру када Ñе замењује плејлиÑта"
+ </dest>
+ <voice>
+ *: "Задржи текућу нумеру када Ñе замењује плејлиÑта"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_CLEAR_SETTINGS_ON_HOLD
+ desc: in the system sub menu
+ user: core
+ <source>
+ *: none
+ clear_settings_on_hold,iriverh10: "Clear settings when reset button is held during startup"
+ ipod4g,ipodcolor,ipodmini1g,ipodmini2g,ipodnano1g,ipodvideo: "Clear settings when hold switch is on during startup"
+ </source>
+ <dest>
+ *: none
+ clear_settings_on_hold,iriverh10: "Обриши подешавања када Ñе током покретања држи реÑет таÑтер"
+ ipod4g,ipodcolor,ipodmini1g,ipodmini2g,ipodnano1g,ipodvideo: "Обриши подешавања када је током покретања укључен таÑтер hold"
+ </dest>
+ <voice>
+ *: none
+ clear_settings_on_hold,iriverh10: "Обриши подешавања када Ñе током покретања држи реÑет таÑтер"
+ ipod4g,ipodcolor,ipodmini1g,ipodmini2g,ipodnano1g,ipodvideo: "Обриши подешавања када је током покретања укључен таÑтер hold"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_REWIND_ACROSS_TRACKS
+ desc: in playback settings menu
+ user: core
+ <source>
+ *: "Rewind Across Tracks"
+ </source>
+ <dest>
+ *: "Премотавај преко нумера"
+ </dest>
+ <voice>
+ *: "Премотавај преко нумера"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_SET_AS
+ desc: used in the onplay menu
+ user: core
+ <source>
+ *: "Set As..."
+ </source>
+ <dest>
+ *: "ПоÑтави као..."
+ </dest>
+ <voice>
+ *: "ПоÑтави као..."
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PLAYLIST_DIR
+ desc: used in the onplay menu
+ user: core
+ <source>
+ *: "Playlist Directory"
+ </source>
+ <dest>
+ *: "Директоријум Ñа плејлиÑтама"
+ </dest>
+ <voice>
+ *: "Директоријум Ñа плејлиÑтама"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_START_DIR
+ desc: used in the onplay menu
+ user: core
+ <source>
+ *: "Start Directory"
+ </source>
+ <dest>
+ *: "Почетни директоријум"
+ </dest>
+ <voice>
+ *: "Почетни директоријум"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_RECORDING_DIR
+ desc: used in the onplay menu
+ user: core
+ <source>
+ *: none
+ recording: "Recording Directory"
+ </source>
+ <dest>
+ *: none
+ recording: "Директоријум за Ñнимање"
+ </dest>
+ <voice>
+ *: none
+ recording: "Директоријум за Ñнимање"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_ADD_TO_PL
+ desc: used in the onplay menu
+ user: core
+ <source>
+ *: "Add to Playlist..."
+ </source>
+ <dest>
+ *: "Додај у плејлиÑту..."
+ </dest>
+ <voice>
+ *: "Додај у плејлиÑту..."
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_ADD_TO_EXISTING_PL
+ desc: used in the onplay menu
+ user: core
+ <source>
+ *: "Add to Existing Playlist"
+ </source>
+ <dest>
+ *: "Додај у поÑтојећу плејлиÑту"
+ </dest>
+ <voice>
+ *: "Додај у поÑтојећу плејлиÑту"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PLAYING_NEXT
+ desc: used in the onplay menu
+ user: core
+ <source>
+ *: "Playing Next..."
+ </source>
+ <dest>
+ *: "Пушта Ñе Ñледеће..."
+ </dest>
+ <voice>
+ *: "Пушта Ñе Ñледеће..."
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PLAY_NEXT
+ desc: used in the onplay menu
+ user: core
+ <source>
+ *: "Play Next"
+ </source>
+ <dest>
+ *: "ПуÑти Ñледећу"
+ </dest>
+ <voice>
+ *: "ПуÑти Ñледећу"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_ADD_SHUFFLED
+ desc: used in the onplay menu
+ user: core
+ <source>
+ *: "Add Shuffled"
+ </source>
+ <dest>
+ *: "Додај промешано"
+ </dest>
+ <voice>
+ *: "Додај промешано"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PLAY_LAST
+ desc: used in the onplay menu
+ user: core
+ <source>
+ *: "Play Last"
+ </source>
+ <dest>
+ *: "ПуÑти поÑледње"
+ </dest>
+ <voice>
+ *: "ПуÑти поÑледње"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PLAY_LAST_SHUFFLED
+ desc: used in the onplay menu
+ user: core
+ <source>
+ *: "Play Last Shuffled"
+ </source>
+ <dest>
+ *: "ПуÑти поÑледње промешано"
+ </dest>
+ <voice>
+ *: "ПуÑти поÑледње промешано"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_VOLUME_ADJUST_MODE
+ desc: in system settings
+ user: core
+ <source>
+ *: none
+ perceptual_volume: "Volume Adjustment Mode"
+ </source>
+ <dest>
+ *: none
+ perceptual_volume: "Режим подешавања јачине"
+ </dest>
+ <voice>
+ *: none
+ perceptual_volume: "Режим подешавања јачине"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_VOLUME_ADJUST_NORM_STEPS
+ desc: in system settings
+ user: core
+ <source>
+ *: none
+ perceptual_volume: "Number of Volume Steps"
+ </source>
+ <dest>
+ *: none
+ perceptual_volume: "Број корака јачине"
+ </dest>
+ <voice>
+ *: none
+ perceptual_volume: "Број корака јачине"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PERCEPTUAL
+ desc: in system settings -> volume adjustment mode
+ user: core
+ <source>
+ *: none
+ perceptual_volume: "Perceptual"
+ </source>
+ <dest>
+ *: none
+ perceptual_volume: "Перцептуално"
+ </dest>
+ <voice>
+ *: none
+ perceptual_volume: "Перцептуално"
+ </voice>
+</phrase>
diff --git a/apps/lang/svenska.lang b/apps/lang/svenska.lang
index 758ad373ef..4c4b7b21cc 100644
--- a/apps/lang/svenska.lang
+++ b/apps/lang/svenska.lang
@@ -254,7 +254,7 @@
*: "PLAY = Yes"
archosplayer: "(PLAY/STOP)"
cowond2*: "MENU, or top-right = Yes"
- creativezen*: "Select = Yes"
+ creativezen*: "SELECT = Yes"
gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT = Yes"
iriverh100,iriverh120,iriverh300: "NAVI = Yes"
mrobe500: "PLAY, POWER, or top-right = Yes"
@@ -5270,20 +5270,17 @@
</voice>
</phrase>
<phrase>
- id: LANG_RECORDING_FORMAT
- desc: audio format item in recording menu
+ id: LANG_FORMAT
+ desc: audio format
user: core
<source>
- *: none
- recording: "Format"
+ *: "Format"
</source>
<dest>
- *: none
- recording: "Format"
+ *: "Format"
</dest>
<voice>
- *: none
- recording: "Format"
+ *: "Format"
</voice>
</phrase>
<phrase>
@@ -10318,15 +10315,15 @@
user: core
<source>
*: none
- gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
</source>
<dest>
*: none
- gigabeatfx,sansafuzeplus: "Pekskärmskänslighet"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Pekskärmskänslighet"
</dest>
<voice>
*: none
- gigabeatfx,sansafuzeplus: "Pekskärmskänslighet"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Pekskärmskänslighet"
</voice>
</phrase>
<phrase>
diff --git a/apps/lang/tagalog.lang b/apps/lang/tagalog.lang
index 6877569031..b5ccde1ced 100644
--- a/apps/lang/tagalog.lang
+++ b/apps/lang/tagalog.lang
@@ -5259,20 +5259,17 @@
</voice>
</phrase>
<phrase>
- id: LANG_RECORDING_FORMAT
- desc: audio format item in recording menu
+ id: LANG_FORMAT
+ desc: audio format
user: core
<source>
- *: none
- recording: "Format"
+ *: "Format"
</source>
<dest>
- *: none
- recording: "Format"
+ *: "Format"
</dest>
<voice>
- *: none
- recording: "Format"
+ *: "Format"
</voice>
</phrase>
<phrase>
@@ -10191,15 +10188,15 @@
user: core
<source>
*: none
- gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
</source>
<dest>
*: none
- gigabeatfx,sansafuzeplus: "Sensitibo Touchpad"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Sensitibo Touchpad"
</dest>
<voice>
*: none
- gigabeatfx,sansafuzeplus: "Sensitibo Touchpad"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Sensitibo Touchpad"
</voice>
</phrase>
<phrase>
diff --git a/apps/lang/thai.lang b/apps/lang/thai.lang
index d5fb5ec7f5..36aa488496 100644
--- a/apps/lang/thai.lang
+++ b/apps/lang/thai.lang
@@ -5241,20 +5241,17 @@
</voice>
</phrase>
<phrase>
- id: LANG_RECORDING_FORMAT
- desc: audio format item in recording menu
+ id: LANG_FORMAT
+ desc: audio format
user: core
<source>
- *: none
- recording: "Format"
+ *: "Format"
</source>
<dest>
- *: none
- recording: "รูปà¹à¸šà¸š"
+ *: "รูปà¹à¸šà¸š"
</dest>
<voice>
- *: none
- recording: "Format"
+ *: "Format"
</voice>
</phrase>
<phrase>
@@ -10237,15 +10234,15 @@
user: core
<source>
*: none
- gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
</source>
<dest>
*: none
- gigabeatfx,sansafuzeplus: "ความไวà¹à¸œà¹ˆà¸™à¸ªà¸±à¸¡à¸œà¸±à¸ª"
+ fiiom3k,gigabeatfx,sansafuzeplus: "ความไวà¹à¸œà¹ˆà¸™à¸ªà¸±à¸¡à¸œà¸±à¸ª"
</dest>
<voice>
*: none
- gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
</voice>
</phrase>
<phrase>
diff --git a/apps/lang/turkce.lang b/apps/lang/turkce.lang
index 4fc073bebc..a5596afaf1 100644
--- a/apps/lang/turkce.lang
+++ b/apps/lang/turkce.lang
@@ -18,6 +18,7 @@
# - Melih Özbek
# - Brian Cloutier
# - Altay Oz
+# - Selami Dinçer
<phrase>
id: LANG_SET_BOOL_YES
desc: bool true representation
@@ -124,7 +125,7 @@
*: "Loading..."
</source>
<dest>
- *: "Yükleniyor"
+ *: "Yükleniyor..."
</dest>
<voice>
*: "Yükleniyor"
@@ -141,7 +142,7 @@
*: "Yükleniyor... %%%d bitti (%s)"
</dest>
<voice>
- *: ""
+ *: "Yükleniyor... %%%d tamamlandı (%s)"
</voice>
</phrase>
<phrase>
@@ -152,10 +153,10 @@
*: "Scanning disk..."
</source>
<dest>
- *: "Disk Kontrol yapıyor..."
+ *: "Disk Kontrolü yapıyor..."
</dest>
<voice>
- *: "Disk Kontrol yapıyor..."
+ *: "Disk Kontrolü yapıyor..."
</voice>
</phrase>
<phrase>
@@ -180,7 +181,7 @@
*: "Cancelled"
</source>
<dest>
- *: "Ä°ptal etildi"
+ *: "Ä°ptal etildi!"
</dest>
<voice>
*: "Ä°ptal etildi"
@@ -194,10 +195,10 @@
*: "Failed"
</source>
<dest>
- *: "Başaramadı"
+ *: "Başarısız oldu"
</dest>
<voice>
- *: "Başarısız kaldı"
+ *: "Başarısız oldu"
</voice>
</phrase>
<phrase>
@@ -234,17 +235,14 @@
user: core
<source>
*: "PLAY = Yes"
- archosplayer: "(PLAY/STOP)"
cowond2*: "MENU, or top-right = Yes"
- creativezen*: "Select = Yes"
- gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT = Yes"
+ creativezen*,gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT = Yes"
iriverh100,iriverh120,iriverh300: "NAVI = Yes"
mrobe500: "PLAY, POWER, or top-right = Yes"
vibe500: "OK = Yes"
</source>
<dest>
*: "PLAY = Devam et"
- archosplayer: "(PLAY/STOP)"
cowond2*: "MENU, veya üst-sağ = Evet"
creativezen*,gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT = Evet"
iriverh100,iriverh120,iriverh300: "NAVI = Devam et"
@@ -261,17 +259,12 @@
user: core
<source>
*: "Any Other = No"
- archosplayer: none
</source>
<dest>
- *: "Herhangi PLAY'dan baÅŸka = Ä°ptal et"
- archosplayer: none
- gigabeat*,gogearsa9200,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansae200: "Herhangi SELECT'ten baÅŸka = Ä°ptal et"
- iriverh100,iriverh120,iriverh300: "Herhangi NAVI'den baÅŸka = Ä°ptal et"
+ *: "İptal etmek içim Play harici bir tuşa bas."
</dest>
<voice>
*: ""
- archosplayer: none
</voice>
</phrase>
<phrase>
@@ -282,10 +275,10 @@
*: "Recent Bookmarks"
</source>
<dest>
- *: "En Son Kullanıldı Yer Imiler"
+ *: "Son Yer imleri"
</dest>
<voice>
- *: "En son kullanıldı yer imiler"
+ *: "Son Yer imleri"
</voice>
</phrase>
<phrase>
@@ -324,10 +317,10 @@
*: "Now Playing"
</source>
<dest>
- *: "Çalan Şarkı"
+ *: "Şimdi Çalınan..."
</dest>
<voice>
- *: "Çalan Şarkı"
+ *: "Şimdi Çalınan"
</voice>
</phrase>
<phrase>
@@ -338,10 +331,10 @@
*: "Resume Playback"
</source>
<dest>
- *: "Çalmayı Devam Et"
+ *: "Oynatmaya Devam Et"
</dest>
<voice>
- *: "Çalmayı Devam Et"
+ *: "Oynatmaya Devam Et"
</voice>
</phrase>
<phrase>
@@ -442,10 +435,10 @@
*: "Select Bookmark"
</source>
<dest>
- *: "Yer İmiyi Seç"
+ *: "Yer İmi Seç"
</dest>
<voice>
- *: "Yer İmiyi Seç"
+ *: "Yer İmi Seç"
</voice>
</phrase>
<phrase>
@@ -484,10 +477,10 @@
*: "<Invalid Bookmark>"
</source>
<dest>
- *: "<Yer İmiyi Geçersiztir>"
+ *: "<Geçersiz Yer imi>"
</dest>
<voice>
- *: "Yer İmiyi Geçersiztir"
+ *: "Geçersiz Yer imi"
</voice>
</phrase>
<phrase>
@@ -498,10 +491,10 @@
*: "Bookmark Actions"
</source>
<dest>
- *: "Yer Ä°mi Eylemler"
+ *: "Yer Ä°mi Eylemleri"
</dest>
<voice>
- *: "Yer Ä°mi Eylemler"
+ *: "Yer Ä°mi Eylemleri"
</voice>
</phrase>
<phrase>
@@ -540,10 +533,10 @@
*: "Create a Bookmark?"
</source>
<dest>
- *: "Yeni Bir Yer İmiyi Yarat Mı?"
+ *: "Yeni bir Yer imi Ãœretilsin mi?"
</dest>
<voice>
- *: "Yeni Bir Yer İmiyi Yarat Mı?"
+ *: "Yeni bir Yer imi Ãœretilsin mi?"
</voice>
</phrase>
<phrase>
@@ -554,10 +547,10 @@
*: "Bookmark Created"
</source>
<dest>
- *: "Yeni Yer İmi Yaratıldı"
+ *: "Yeni Yer Ä°mi oluÅŸturuldu!"
</dest>
<voice>
- *: "Yeni Yer İmi Yaratıldı"
+ *: "Yeni Yer Ä°mi oluÅŸturuldu"
</voice>
</phrase>
<phrase>
@@ -568,10 +561,10 @@
*: "Bookmark Failed!"
</source>
<dest>
- *: "Yer İmiyi Yaratmayı Başaramadı."
+ *: "Yeni Yer İmi oluşturma Başarısız!"
</dest>
<voice>
- *: "Yer İmiyi Yaratmayı Başaramadı."
+ *: "Yeni Yer İmi oluşturma Başarısız!"
</voice>
</phrase>
<phrase>
@@ -582,10 +575,10 @@
*: "Bookmark Empty"
</source>
<dest>
- *: "Yer Ä°mi BoÅŸtur."
+ *: "Yer Ä°mi BoÅŸ!"
</dest>
<voice>
- *: "Yer Ä°mi BoÅŸtur."
+ *: "Yer Ä°mi BoÅŸ"
</voice>
</phrase>
<phrase>
@@ -624,10 +617,10 @@
*: "Bass"
</source>
<dest>
- *: "Bas Sesi"
+ *: "Bas Ayarı"
</dest>
<voice>
- *: "Bas Sesi"
+ *: "Bas Ayarı"
</voice>
</phrase>
<phrase>
@@ -638,10 +631,10 @@
*: "Treble"
</source>
<dest>
- *: "Soprano Sesi"
+ *: "Tiz Ayarı"
</dest>
<voice>
- *: "Soprano Sesi"
+ *: "Tiz Ayarı"
</voice>
</phrase>
<phrase>
@@ -666,10 +659,10 @@
*: "Channel Configuration"
</source>
<dest>
- *: "Kanal Düzeneği"
+ *: "Kanal Yapılandırması"
</dest>
<voice>
- *: "Kanal Düzeneği"
+ *: "Kanal Yapılandırması"
</voice>
</phrase>
<phrase>
@@ -680,10 +673,10 @@
*: "Stereo"
</source>
<dest>
- *: "Stereo"
+ *: "Stereo Çift Kanallı"
</dest>
<voice>
- *: ""
+ *: "Stereo Çift Kanallı"
</voice>
</phrase>
<phrase>
@@ -694,10 +687,10 @@
*: "Mono"
</source>
<dest>
- *: "Mono"
+ *: "Mono Tek Kanallı"
</dest>
<voice>
- *: ""
+ *: "Mono Tek Kanallı"
</voice>
</phrase>
<phrase>
@@ -708,10 +701,10 @@
*: "Custom"
</source>
<dest>
- *: "Ismarlama"
+ *: "Özel"
</dest>
<voice>
- *: "Ismarlama"
+ *: "Özel"
</voice>
</phrase>
<phrase>
@@ -750,10 +743,10 @@
*: "Stereo Width"
</source>
<dest>
- *: "Stereo GeniÅŸliÄŸi"
+ *: "Stereo GeniÅŸletme"
</dest>
<voice>
- *: "Stereo GeniÅŸliÄŸi"
+ *: "Stereo GeniÅŸletme"
</voice>
</phrase>
<phrase>
@@ -764,10 +757,10 @@
*: "Direct Gain"
</source>
<dest>
- *: "Direct Gain"
+ *: "Doğrudan kazanç"
</dest>
<voice>
- *: "Direct Gain"
+ *: "Doğrudan kazanç"
</voice>
</phrase>
<phrase>
@@ -778,10 +771,10 @@
*: "Cross Gain"
</source>
<dest>
- *: "Cross Gain"
+ *: "Çapraz Kazanç"
</dest>
<voice>
- *: "Cross Gain"
+ *: "Çapraz Kazanç"
</voice>
</phrase>
<phrase>
@@ -792,10 +785,10 @@
*: "High-Frequency Attenuation"
</source>
<dest>
- *: "High-Frequency Attenuation"
+ *: "Yüksek Frekanslı Zayıflama"
</dest>
<voice>
- *: "High-Frequency Attenuation"
+ *: "Yüksek Frekanslı Zayıflama"
</voice>
</phrase>
<phrase>
@@ -806,10 +799,10 @@
*: "High-Frequency Cutoff"
</source>
<dest>
- *: "High-Frequency Cutoff"
+ *: "Yüksek Frekans Kesimi "
</dest>
<voice>
- *: "High-Frequency Cutoff"
+ *: "Yüksek Frekans Kesimi"
</voice>
</phrase>
<phrase>
@@ -820,10 +813,10 @@
*: "Equalizer"
</source>
<dest>
- *: "Ses Dengeleyici"
+ *: "Ekolayzer"
</dest>
<voice>
- *: "Ses Dengeleyici"
+ *: "Ekolayzer"
</voice>
</phrase>
<phrase>
@@ -834,10 +827,10 @@
*: "Enable EQ"
</source>
<dest>
- *: "EQ'yı başlat"
+ *: "Ekolayzer'ı Etkinleştir"
</dest>
<voice>
- *: "Ses Dengeleyiciyi baÅŸlat"
+ *: "Ekolayzer'ı Etkinleştir"
</voice>
</phrase>
<phrase>
@@ -848,10 +841,10 @@
*: "Graphical EQ"
</source>
<dest>
- *: "Çizgesel EQ"
+ *: "Grafik Ekolayzer"
</dest>
<voice>
- *: "Çizgesel Ses Dengeleyici"
+ *: "Grafik Ekolayzer"
</voice>
</phrase>
<phrase>
@@ -862,10 +855,10 @@
*: "Precut"
</source>
<dest>
- *: "Precut"
+ *: "önceden kesilmiş"
</dest>
<voice>
- *: "Precut"
+ *: "önceden kesilmiş "
</voice>
</phrase>
<phrase>
@@ -876,10 +869,10 @@
*: "Simple EQ Settings"
</source>
<dest>
- *: "EQ Ayarları"
+ *: "Basit Ekolayzer Ayarları"
</dest>
<voice>
- *: "Ses Dengeleyici Ayarları"
+ *: "Basit Ekolayzer Ayarları"
</voice>
</phrase>
<phrase>
@@ -890,10 +883,10 @@
*: "Advanced EQ Settings"
</source>
<dest>
- *: "Karmaşık EQ Ayarları"
+ *: "Gelişmiş Ekolayzer Ayarları"
</dest>
<voice>
- *: "Karmaşık Ses Dengeleyici Ayarları"
+ *: "Gelişmiş Ekolayzer Ayarları"
</voice>
</phrase>
<phrase>
@@ -904,10 +897,10 @@
*: "Save EQ Preset"
</source>
<dest>
- *: "EQ Ayarları Kaydet"
+ *: "Ekolayzer Ayarlarını Kaydet"
</dest>
<voice>
- *: "Ses Dengeleyici Ayarları Kaydet"
+ *: "Ekolayzer Ayarlarını Kaydet"
</voice>
</phrase>
<phrase>
@@ -918,10 +911,10 @@
*: "Browse EQ Presets"
</source>
<dest>
- *: "EQ Ayarlara göz at"
+ *: "EkolayzerAyarlarına göz at"
</dest>
<voice>
- *: "Ses Dengeleyici Ayarlara göz at"
+ *: "EkolayzerAyarlarına göz at"
</voice>
</phrase>
<phrase>
@@ -953,125 +946,6 @@
</voice>
</phrase>
<phrase>
- id: LANG_LOUDNESS
- desc: in sound_settings
- user: core
- <source>
- *: none
- masf: "Loudness"
- </source>
- <dest>
- *: none
- masf: "Ses yüksekliği"
- </dest>
- <voice>
- *: none
- masf: "Ses yüksekliği"
- </voice>
-</phrase>
-<phrase>
- id: LANG_AUTOVOL
- desc: in sound_settings
- user: core
- <source>
- *: none
- masf: "Auto Volume"
- </source>
- <dest>
- *: none
- masf: "Özdevimli Ses Yüksekliği"
- </dest>
- <voice>
- *: none
- masf: "Özdevimli Ses Yüksekliği"
- </voice>
-</phrase>
-<phrase>
- id: LANG_SUPERBASS
- desc: in sound settings
- user: core
- <source>
- *: none
- masf: "Super Bass"
- </source>
- <dest>
- *: none
- masf: "Süper Bass"
- </dest>
- <voice>
- *: none
- masf: "Süper Bass"
- </voice>
-</phrase>
-<phrase>
- id: LANG_MDB_ENABLE
- desc: in sound settings
- user: core
- <source>
- *: none
- masf: "MDB Enable"
- </source>
- <dest>
- *: none
- masf: "MDB BaÅŸla"
- </dest>
- <voice>
- *: none
- masf: "MDB BaÅŸla"
- </voice>
-</phrase>
-<phrase>
- id: LANG_MDB_STRENGTH
- desc: in sound settings
- user: core
- <source>
- *: none
- masf: "MDB Strength"
- </source>
- <dest>
- *: none
- masf: "MDB Güçü"
- </dest>
- <voice>
- *: none
- masf: "MDB Güçü"
- </voice>
-</phrase>
-<phrase>
- id: LANG_MDB_HARMONICS
- desc: in sound settings
- user: core
- <source>
- *: none
- masf: "MDB Harmonics"
- </source>
- <dest>
- *: none
- masf: "MDB HarmoniÄŸi"
- </dest>
- <voice>
- *: none
- masf: "MDB HarmoniÄŸi"
- </voice>
-</phrase>
-<phrase>
- id: LANG_MDB_SHAPE
- desc: in sound settings
- user: core
- <source>
- *: none
- masf: "MDB Shape"
- </source>
- <dest>
- *: none
- masf: "MDB Åžekil"
- </dest>
- <voice>
- *: none
- masf: "MDB Åžekil"
- </voice>
-</phrase>
-<phrase>
id: LANG_GENERAL_SETTINGS
desc: in the main menu
user: core
@@ -1079,10 +953,10 @@
*: "General Settings"
</source>
<dest>
- *: "Genel Ayarları"
+ *: "Genel Ayarlar"
</dest>
<voice>
- *: "Genel Ayarları"
+ *: "Genel Ayarlar"
</voice>
</phrase>
<phrase>
@@ -1107,10 +981,10 @@
*: "Shuffle"
</source>
<dest>
- *: "Karıştırmayı"
+ *: "Karışık Çal"
</dest>
<voice>
- *: "Karıştırmayı"
+ *: "Karışık Çal"
</voice>
</phrase>
<phrase>
@@ -1121,10 +995,10 @@
*: "Repeat"
</source>
<dest>
- *: "Tekrar Etmeyi"
+ *: "Tekrarla"
</dest>
<voice>
- *: "Tekrar Etmeyi"
+ *: "Tekrarla"
</voice>
</phrase>
<phrase>
@@ -1135,10 +1009,10 @@
*: "All"
</source>
<dest>
- *: "Hep"
+ *: "Hepsini Tekrarla"
</dest>
<voice>
- *: "Hep"
+ *: "Hepsini Tekrarla"
</voice>
</phrase>
<phrase>
@@ -1149,10 +1023,10 @@
*: "One"
</source>
<dest>
- *: "Bir"
+ *: "Seçilen Parçayı Tekrarla"
</dest>
<voice>
- *: "Bir"
+ *: "Seçilen Parçayı Tekrarla"
</voice>
</phrase>
<phrase>
@@ -1163,10 +1037,10 @@
*: "Play Selected First"
</source>
<dest>
- *: "Çalmayı başla şeçilmiş şarkıyla"
+ *: "Önce Seçileni Oynat"
</dest>
<voice>
- *: "Çalmayı başla şeçilmiş şarkıyı ile"
+ *: "Önce Seçileni Oynat"
</voice>
</phrase>
<phrase>
@@ -1236,10 +1110,10 @@
*: "Fade on Stop/Pause"
</source>
<dest>
- *: "Durarken Sol"
+ *: "Dururken Sesi Azalt"
</dest>
<voice>
- *: "Durarken Sol"
+ *: "Dururken Sesi Azalt"
</voice>
</phrase>
<phrase>
@@ -1250,10 +1124,10 @@
*: "Party Mode"
</source>
<dest>
- *: "Parti Hali"
+ *: "Parti Modu"
</dest>
<voice>
- *: "Parti Hali"
+ *: "Parti Modu"
</voice>
</phrase>
<phrase>
@@ -1266,11 +1140,11 @@
</source>
<dest>
*: none
- crossfade: "Crossfade BaÅŸla"
+ crossfade: "Çapraz Geçiş'i Etkinleştir"
</dest>
<voice>
*: none
- crossfade: "Crossfade baÅŸla"
+ crossfade: "Çapraz Geçiş'i Etkinleştir"
</voice>
</phrase>
<phrase>
@@ -1281,10 +1155,10 @@
*: "File View"
</source>
<dest>
- *: "Dosya Gorunumu"
+ *: "Dosya Görünümü"
</dest>
<voice>
- *: ""
+ *: "Dosya Görünümü"
</voice>
</phrase>
<phrase>
@@ -1295,10 +1169,10 @@
*: "Sort Case Sensitive"
</source>
<dest>
- *: "Buyuk Kucuk Harf Sirasi"
+ *: "Büyük/Küçük Harfe Duyarlı Sırala"
</dest>
<voice>
- *: ""
+ *: "Büyük/Küçük Harfe Duyarlı Sırala"
</voice>
</phrase>
<phrase>
@@ -1312,7 +1186,7 @@
*: "Gösterilecek Dosyalar"
</dest>
<voice>
- *: ""
+ *: "Gösterilecek Dosyalar"
</voice>
</phrase>
<phrase>
@@ -1326,7 +1200,7 @@
*: "Desteklenen"
</dest>
<voice>
- *: ""
+ *: "Desteklenen"
</voice>
</phrase>
<phrase>
@@ -1337,10 +1211,10 @@
*: "Music"
</source>
<dest>
- *: "Muzik"
+ *: "Müzik"
</dest>
<voice>
- *: ""
+ *: "Müzik"
</voice>
</phrase>
<phrase>
@@ -1351,10 +1225,10 @@
*: "Follow Playlist"
</source>
<dest>
- *: "Listeyi Takip Et"
+ *: "Oynatma Listesini Takip Et"
</dest>
<voice>
- *: ""
+ *: "Oynatma Listesini Takip Et"
</voice>
</phrase>
<phrase>
@@ -1363,7 +1237,6 @@
user: core
<source>
*: "Building database... %d found (OFF to return)"
- archosplayer: "Building DB %d found"
gigabeat*,iaudiom5,iaudiox5,mrobe100,samsungyh*: "Building database... %d found (LEFT to return)"
gogearsa9200: "Building database... %d found (REW to return)"
ipod*,iriverh10,iriverh10_5gb,sansac200*,sansae200*,sansafuze*,vibe500: "Building database... %d found (PREV to return)"
@@ -1371,7 +1244,6 @@
</source>
<dest>
*: "Veritabanı oluşturuluyor... %d bulundu (dönmek için OFF)"
- archosplayer: "DB oluÅŸturuluyor... %d bulundu"
gigabeat*,iaudiom5,iaudiox5,mrobe100,samsungyh*: "Veritabanı oluşturuluyor... %d bulundu (dönmek için LEFT)"
gogearsa9200: "Veritabanı oluşturuluyor... %d bulundu (dönmek için REW)"
ipod*,iriverh10,iriverh10_5gb,sansac200*,sansae200*,sansafuze*,vibe500: "Veritabanı oluşturuluyor... %d bulundu (dönmek için PREV)"
@@ -1389,10 +1261,10 @@
*: "Display"
</source>
<dest>
- *: "Ekran"
+ *: "Ekran Ayarları"
</dest>
<voice>
- *: ""
+ *: "Ekran Ayarları"
</voice>
</phrase>
<phrase>
@@ -1403,10 +1275,10 @@
*: "Backlight"
</source>
<dest>
- *: "LCD Isigi"
+ *: "Ekran Işığı Zaman aşımı"
</dest>
<voice>
- *: ""
+ *: "Ekran Işığı Zaman aşımı"
</voice>
</phrase>
<phrase>
@@ -1423,7 +1295,7 @@
</dest>
<voice>
*: none
- charging: ""
+ charging: "Aydınlatma (Bağlı iken)"
</voice>
</phrase>
<phrase>
@@ -1436,11 +1308,11 @@
</source>
<dest>
*: none
- backlight_fade*: "Aydınlatma Açılıyor"
+ backlight_fade*: "Aydınlatma Açılma süresi"
</dest>
<voice>
*: none
- backlight_fade*: ""
+ backlight_fade*: "Aydınlatma Açılma süresi"
</voice>
</phrase>
<phrase>
@@ -1453,11 +1325,11 @@
</source>
<dest>
*: none
- backlight_fade*: "Aydınlatma Kapanıyor"
+ backlight_fade*: "Aydınlatma Kapatma süresi"
</dest>
<voice>
*: none
- backlight_fade*: ""
+ backlight_fade*: "Aydınlatma Kapatma süresi"
</voice>
</phrase>
<phrase>
@@ -1471,7 +1343,7 @@
*: "Kontrast"
</dest>
<voice>
- *: ""
+ *: "Kontrast"
</voice>
</phrase>
<phrase>
@@ -1488,7 +1360,7 @@
</dest>
<voice>
*: none
- lcd_invert,remote_lcd_invert: ""
+ lcd_invert,remote_lcd_invert: "LCD Modu"
</voice>
</phrase>
<phrase>
@@ -1505,7 +1377,7 @@
</dest>
<voice>
*: none
- lcd_invert,remote_lcd_invert: ""
+ lcd_invert,remote_lcd_invert: "Ters"
</voice>
</phrase>
<phrase>
@@ -1516,10 +1388,10 @@
*: "Scrolling"
</source>
<dest>
- *: "Kayma Hizi"
+ *: "Kayma Hızı"
</dest>
<voice>
- *: ""
+ *: "Kayma Hızı"
</voice>
</phrase>
<phrase>
@@ -1530,7 +1402,7 @@
*: "Scroll Speed Setting Example"
</source>
<dest>
- *: "Kayma Hizi Ayari Ornegi"
+ *: "Kayma Hızı Ayarı Örneği"
</dest>
<voice>
*: ""
@@ -1547,7 +1419,7 @@
*: "Kaydırma Çubuğu"
</dest>
<voice>
- *: ""
+ *: "Kaydırma Çubuğu"
</voice>
</phrase>
<phrase>
@@ -1561,7 +1433,7 @@
*: "Durum Çubuğu"
</dest>
<voice>
- *: ""
+ *: "Durum Çubuğu"
</voice>
</phrase>
<phrase>
@@ -1575,7 +1447,7 @@
*: "Ses Düzeyi"
</dest>
<voice>
- *: ""
+ *: "Ses Düzeyi"
</voice>
</phrase>
<phrase>
@@ -1589,7 +1461,7 @@
*: "Pil Düzeyi"
</dest>
<voice>
- *: ""
+ *: "Pil Düzeyi"
</voice>
</phrase>
<phrase>
@@ -1603,7 +1475,7 @@
*: "Grafik"
</dest>
<voice>
- *: ""
+ *: "Grafik"
</voice>
</phrase>
<phrase>
@@ -1617,7 +1489,7 @@
*: "Sayısal"
</dest>
<voice>
- *: ""
+ *: "Sayısal"
</voice>
</phrase>
<phrase>
@@ -1626,15 +1498,12 @@
user: core
<source>
*: "Peak Meter"
- masd: none
</source>
<dest>
*: "Ses Düzeyi Gösterimi"
- masd: none
</dest>
<voice>
- *: ""
- masd: none
+ *: "Ses Düzeyi Gösterimi"
</voice>
</phrase>
<phrase>
@@ -1643,15 +1512,12 @@
user: core
<source>
*: "Clip Hold Time"
- masd: none
</source>
<dest>
- *: "Parça Kalış Süresi"
- masd: none
+ *: "Parçanın Kalan Süresi"
</dest>
<voice>
- *: ""
- masd: none
+ *: "Parçanın Kalan Süresi"
</voice>
</phrase>
<phrase>
@@ -1660,15 +1526,12 @@
user: core
<source>
*: "Peak Hold Time"
- masd: none
</source>
<dest>
*: "Tepe noktası kalış süresi"
- masd: none
</dest>
<voice>
- *: ""
- masd: none
+ *: "Tepe noktası kalış süresi"
</voice>
</phrase>
<phrase>
@@ -1677,15 +1540,12 @@
user: core
<source>
*: "Eternal"
- masd: none
</source>
<dest>
*: "Sonsuz"
- masd: none
</dest>
<voice>
- *: ""
- masd: none
+ *: "Sonsuz"
</voice>
</phrase>
<phrase>
@@ -1694,15 +1554,12 @@
user: core
<source>
*: "Peak Release"
- masd: none
</source>
<dest>
*: "Tepe Noktası Bırakma"
- masd: none
</dest>
<voice>
- *: ""
- masd: none
+ *: "Tepe Noktası Bırakma"
</voice>
</phrase>
<phrase>
@@ -1711,15 +1568,12 @@
user: core
<source>
*: "Scale"
- masd: none
</source>
<dest>
*: "Ölçek"
- masd: none
</dest>
<voice>
- *: ""
- masd: none
+ *: "Ölçek"
</voice>
</phrase>
<phrase>
@@ -1728,15 +1582,12 @@
user: core
<source>
*: "Logarithmic (dB)"
- masd: none
</source>
<dest>
*: "Logaritmik (dB)"
- masd: none
</dest>
<voice>
- *: ""
- masd: none
+ *: "Logaritmik (dB)"
</voice>
</phrase>
<phrase>
@@ -1745,15 +1596,12 @@
user: core
<source>
*: "Linear (%)"
- masd: none
</source>
<dest>
*: "DoÄŸrusal (%)"
- masd: none
</dest>
<voice>
- *: ""
- masd: none
+ *: "DoÄŸrusal (%)"
</voice>
</phrase>
<phrase>
@@ -1762,15 +1610,12 @@
user: core
<source>
*: "Minimum Of Range"
- masd: none
</source>
<dest>
*: "En Düşük Aralık"
- masd: none
</dest>
<voice>
- *: ""
- masd: none
+ *: "En Düşük Aralık"
</voice>
</phrase>
<phrase>
@@ -1779,15 +1624,12 @@
user: core
<source>
*: "Maximum Of Range"
- masd: none
</source>
<dest>
*: "En Yüksek Aralık"
- masd: none
</dest>
<voice>
- *: ""
- masd: none
+ *: "En Yüksek Aralık"
</voice>
</phrase>
<phrase>
@@ -1806,7 +1648,8 @@
</dest>
<voice>
*: none
- button_light,sansae200*,sansafuze*: ""
+ button_light: "Düğme Işığı Zaman Aşımı"
+ sansae200*,sansafuze*: "Teker Işığı Zaman Aşımı"
</voice>
</phrase>
<phrase>
@@ -1823,7 +1666,7 @@
</dest>
<voice>
*: none
- buttonlight_brightness: ""
+ buttonlight_brightness: "Düğme Işığı Parlaklığı"
</voice>
</phrase>
<phrase>
@@ -1835,11 +1678,11 @@
flash_storage: none
</source>
<dest>
- *: "Disk Donusu"
+ *: "Disk Dönüşü"
flash_storage: none
</dest>
<voice>
- *: ""
+ *: "Disk Dönüşü"
flash_storage: none
</voice>
</phrase>
@@ -1851,10 +1694,10 @@
*: "Time Format"
</source>
<dest>
- *: "Zaman Biçimi"
+ *: "Saat Formatı"
</dest>
<voice>
- *: ""
+ *: "Saat Formatı"
</voice>
</phrase>
<phrase>
@@ -1865,10 +1708,10 @@
*: "12 Hour Clock"
</source>
<dest>
- *: "12 Saatlık Zaman"
+ *: "12 Saatlık Dilim"
</dest>
<voice>
- *: ""
+ *: "12 Saatlık Dilim"
</voice>
</phrase>
<phrase>
@@ -1879,10 +1722,10 @@
*: "24 Hour Clock"
</source>
<dest>
- *: "24 Saatlık Zaman"
+ *: "24 Saatlık Dilim"
</dest>
<voice>
- *: ""
+ *: "24 Saatlık Dilim"
</voice>
</phrase>
<phrase>
@@ -1901,16 +1744,17 @@
</source>
<dest>
*: none
- gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT = Ayarlandı"
- gogearsa9200,samsungyh*: "PLAY = Ayarlandı"
- iriverh100,iriverh120,iriverh300: "NAVI = Ayarlandı"
- mpiohd300: "ENTER = Ayarlandı"
- mrobe500: "HEART = Ayarlandı"
- rtc: "ON = Set"
- vibe500: "OK = Ayarlandı"
+ gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SEÇ Tuşu = Ayarla"
+ gogearsa9200,samsungyh*: "PLAY TuÅŸu = Ayarla"
+ iriverh100,iriverh120,iriverh300: "NAVI TuÅŸu = Ayarla"
+ mpiohd300: "Tamam TuÅŸu = Ayarla"
+ mrobe500: "Kalp TuÅŸu = Ayarla"
+ rtc: "Açma Tuşu = Ayarla"
+ vibe500: "Tamam TuÅŸu = Ayarla"
</dest>
<voice>
*: none
+ gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansae200*,gogearsa9200,samsungyh*,iriverh100,iriverh120,iriverh300,rtc: ""
</voice>
</phrase>
<phrase>
@@ -1935,6 +1779,7 @@
*: none
gigabeatfx,mrobe500: "POWER = Geri Döndür"
gigabeats: "LEFT = Revert"
+ sansafuzeplus: "BACK = Geri Döndür"
gogearsa9200: "LEFT = Geri Döndür"
iaudiom5,iaudiox5: "RECORD = Geri Döndür"
ipod*,mpiohd300,sansac200*: "MENU = Geri Döndür"
@@ -1943,11 +1788,11 @@
mrobe100: "DISPLAY = Geri Döndür"
rtc: "OFF = Geri Döndür"
samsungyh*: "REW = Geri Döndür"
- sansafuzeplus: "BACK = Geri Döndür"
vibe500: "C = Geri Döndür"
</dest>
<voice>
*: none
+ gogearsa9200,iaudiom5,iaudiox5,ipod*,sansac200*,iriverh10,iriverh10_5gb,sansae200*,iriverh100,iriverh120,iriverh300,mrobe100,rtc,samsungyh*: ""
</voice>
</phrase>
<phrase>
@@ -2105,7 +1950,7 @@
*: "Mar"
</source>
<dest>
- *: "Mar"
+ *: "Mart"
</dest>
<voice>
*: "Mart"
@@ -2248,7 +2093,7 @@
*: "Otomatik Kapanma"
</dest>
<voice>
- *: ""
+ *: "Otomatik Kapanma"
</voice>
</phrase>
<phrase>
@@ -2262,7 +2107,7 @@
*: "Yeni Dil"
</dest>
<voice>
- *: ""
+ *: "Yeni Dil"
</voice>
</phrase>
<phrase>
@@ -2273,10 +2118,10 @@
*: "Reset Settings"
</source>
<dest>
- *: "Ayarları Başa Döndür"
+ *: "Ayarları Sıfırla"
</dest>
<voice>
- *: ""
+ *: "Ayarları Sıfırla"
</voice>
</phrase>
<phrase>
@@ -2287,10 +2132,10 @@
*: "Cleared"
</source>
<dest>
- *: "Temizlendi"
+ *: "Temizlendi!"
</dest>
<voice>
- *: ""
+ *: "Temizlendi"
</voice>
</phrase>
<phrase>
@@ -2304,7 +2149,7 @@
*: "Rockbox Bilgi:"
</dest>
<voice>
- *: ""
+ *: "Rockbox Bilgi:"
</voice>
</phrase>
<phrase>
@@ -2313,40 +2158,35 @@
user: core
<source>
*: "Buffer:"
- archosplayer: "Buf:"
</source>
<dest>
*: "Tampon: "
- archosplayer: "Tmpn:"
</dest>
<voice>
- *: "Buffer size"
+ *: "Tampon: "
</voice>
</phrase>
<phrase>
id: LANG_DISK_NAME_MMC
- desc: in info menu; name for external disk with multivolume (Ondio; keep short!)
+ desc: in info menu; name for external disk with multivolume (keep short!)
user: core
<source>
*: none
- archosondio*: "MMC:"
multivolume: "HD1"
sansac200*,sansaclipplus,sansae200*,sansafuze*: "mSD:"
xduoox3: "mSD2:"
</source>
<dest>
*: none
- archosondio*: "MMC:"
multivolume: "HD1"
- sansac200*,sansaclipplus,sansae200*,sansafuze*: "mSD:"
- xduoox3: "mSD2:"
+ sansac200*,sansaclipplus,sansae200*,sansafuze*: "mikro-SD:"
+ xduoox3: "mikro-SD2:"
</dest>
<voice>
*: none
- archosondio*: "M M C"
multivolume: "H D 1"
- sansac200*,sansaclipplus,sansae200*,sansafuze*: "micro S D"
- xduoox3: "micro S D 2"
+ sansac200*,sansaclipplus,sansae200*,sansafuze*: "mikroSD"
+ xduoox3: "mikroSD2"
</voice>
</phrase>
<phrase>
@@ -2360,7 +2200,7 @@
*: "Sürüm"
</dest>
<voice>
- *: ""
+ *: "Sürüm"
</voice>
</phrase>
<phrase>
@@ -2374,7 +2214,7 @@
*: "Hata Arındırma (Uzak durun!)"
</dest>
<voice>
- *: ""
+ *: "Hata Arındırma (Uzak durun!)"
</voice>
</phrase>
<phrase>
@@ -2385,10 +2225,10 @@
*: "Title"
</source>
<dest>
- *: "Adi"
+ *: "Başlık"
</dest>
<voice>
- *: "Adi"
+ *: "Başlık"
</voice>
</phrase>
<phrase>
@@ -2399,10 +2239,10 @@
*: "Artist"
</source>
<dest>
- *: "Sanatci"
+ *: "Sanatçı"
</dest>
<voice>
- *: "Sanatci"
+ *: "Sanatçı"
</voice>
</phrase>
<phrase>
@@ -2413,10 +2253,10 @@
*: "Tracknum"
</source>
<dest>
- *: "Sarki no"
+ *: "Parça Numarası"
</dest>
<voice>
- *: "Sarki no"
+ *: "Parça Numarası"
</voice>
</phrase>
<phrase>
@@ -2427,10 +2267,10 @@
*: "Playlist"
</source>
<dest>
- *: "Sarki Listesi"
+ *: "Şarkı Listesi"
</dest>
<voice>
- *: "Sarki Listesi"
+ *: "Şarkı Listesi"
</voice>
</phrase>
<phrase>
@@ -2471,6 +2311,7 @@
</source>
<dest>
*: none
+ pitchscreen: "Pitch Yukarı"
</dest>
<voice>
*: none
@@ -2487,6 +2328,7 @@
</source>
<dest>
*: none
+ pitchscreen: "Pitch Aşağı"
</dest>
<voice>
*: none
@@ -2501,10 +2343,10 @@
*: "Playlist Buffer Full"
</source>
<dest>
- *: "SarkiListesi tampon dolu"
+ *: "Oynatma Listesi Tamponu Dolu"
</dest>
<voice>
- *: ""
+ *: "Oynatma Listesi Tamponu Dolu"
</voice>
</phrase>
<phrase>
@@ -2515,10 +2357,10 @@
*: "Pause"
</source>
<dest>
- *: "Durdur"
+ *: "Duraklat"
</dest>
<voice>
- *: ""
+ *: "Duraklat"
</voice>
</phrase>
<phrase>
@@ -2532,7 +2374,7 @@
*: "Zaman/Tarih ayarla"
</dest>
<voice>
- *: ""
+ *: "Zaman/Tarih ayarla"
</voice>
</phrase>
<phrase>
@@ -2549,20 +2391,20 @@
</source>
<dest>
*: none
- iaudiom5,iaudiox5: "The disk is full. Press POWER to continue."
- iriverh100,iriverh120,iriverh300: "The disk is full. Press STOP to continue."
- recording: "The disk is full. Press OFF to continue."
- samsungyh*: "Disk full. Press LEFT to continue."
- sansac200*,sansae200,vibe500: "The disk is full. Press UP to continue."
+ iaudiom5,iaudiox5: "Disk dolu. Devam etmek için GÜÇ'e basın."
+ iriverh100,iriverh120,iriverh300: "Disk dolu. Devam etmek için STOP'a basın."
+ recording: "Disk dolu. Devam etmek için KAPAT'a basın."
+ samsungyh*: "Disk dolu. Devam etmek için SOL tuşuna basın"
+ sansac200*,sansae200*,vibe500: "Disk dolu. Devam etmek için PREV'e basın."
</dest>
<voice>
*: none
- iaudiom5,iaudiox5,iriverh100,iriverh120,iriverh300,recording,sansac200*,sansae200*: ""
+ iaudiom5,iaudiox5,iriverh100,iriverh120,iriverh300,recording,samsungyh*,sansac200*,sansae200*,vibe500: ""
</voice>
</phrase>
<phrase>
id: LANG_OFF_ABORT
- desc: Used on archosrecorder models
+ desc: Used on many models
user: core
<source>
*: "OFF to abort"
@@ -2574,11 +2416,13 @@
iriverh100,iriverh120,iriverh300: "STOP to abort"
</source>
<dest>
- *: "OFF to abort"
- iaudiom5,iaudiox5: "Long PLAY to abort"
- ipod*: "PLAY/PAUSE to abort"
- iriverh10,iriverh10_5gb,sansac200*,sansae200,vibe500: "PREV to abort"
- iriverh100,iriverh120,iriverh300: "STOP to abort"
+ *: "İptal etmek için KAPALI"
+ gigabeatfx: "iptal etmek için GÜÇ tu;una basin"
+ gigabeats,sansafuzeplus: "iptal etmek için GERİ DÖN"
+ iaudiom5,iaudiox5: "İptal etmek için uzun OYNAT"
+ ipod*: "iptal etmek için OYNAT/DURAKLAT"
+ iriverh10,iriverh10_5gb,sansac200*,sansae200*,vibe500: "iptal etmek için PREV"
+ iriverh100,iriverh120,iriverh300: "iptal etmek için DUR"
</dest>
<voice>
*: ""
@@ -2592,10 +2436,10 @@
*: "Plugin returned error"
</source>
<dest>
- *: "Eklenti hata olması bildirdi"
+ *: "Eklenti hatası bildirdi"
</dest>
<voice>
- *: ""
+ *: "Eklenti hatası bildirdi"
</voice>
</phrase>
<phrase>
@@ -2606,10 +2450,10 @@
*: "Dir Buffer is Full!"
</source>
<dest>
- *: "Dir Tamponu dolu!"
+ *: "Dizin Tamponu dolu!"
</dest>
<voice>
- *: ""
+ *: "Dizin Tamponu dolu!"
</voice>
</phrase>
<phrase>
@@ -2620,10 +2464,10 @@
*: "Invalid Filename!"
</source>
<dest>
- *: "Dosyanın adısı geçersiz!"
+ *: "Geçersiz Dosya Adı!"
</dest>
<voice>
- *: "Dosyanın adısı geçersiz"
+ *: "Geçersiz Dosya Adı"
</voice>
</phrase>
<phrase>
@@ -2634,10 +2478,10 @@
*: "Please reboot to enable"
</source>
<dest>
- *: "Lütfen sistemi yeniden aç"
+ *: "Etkinleştirmek için lütfen yeniden başlatın"
</dest>
<voice>
- *: "Lütfen sistemi yeniden aç"
+ *: "Etkinleştirmek için lütfen yeniden başlatın"
</voice>
</phrase>
<phrase>
@@ -2650,28 +2494,11 @@
</source>
<dest>
*: none
- charging: "Pil yükliyor"
+ charging: "Pil Åžarj Oluyor"
</dest>
<voice>
*: none
- charging: "Pil yükliyor"
- </voice>
-</phrase>
-<phrase>
- id: LANG_BATTERY_TOPOFF_CHARGE
- desc: in info display, shows that top off charge is running Only for V1 archosrecorder
- user: core
- <source>
- *: none
- archosrecorder: "Battery: Top-Off Chg"
- </source>
- <dest>
- *: none
- archosrecorder: "Pil en son yüklemeler yapıyor"
- </dest>
- <voice>
- *: none
- archosrecorder: "Pil en son yüklemeler yapıyor"
+ charging: "Pil Åžarj Oluyor"
</voice>
</phrase>
<phrase>
@@ -2684,11 +2511,11 @@
</source>
<dest>
*: none
- charging: "Pil yavaşça yükliyor"
+ charging: "Pil yavaşça doluyor"
</dest>
<voice>
*: none
- charging: "Pil yavaşça yükliyor"
+ charging: "Pil yavaşça doluyor"
</voice>
</phrase>
<phrase>
@@ -2713,10 +2540,10 @@
*: "Battery empty! RECHARGE!"
</source>
<dest>
- *: "Pil boÅŸ! Doldur!"
+ *: "Pil boÅŸ! Åžarj Et "
</dest>
<voice>
- *: "Pil boÅŸ! Doldur!"
+ *: "Pil boÅŸ! Åžarj Et "
</voice>
</phrase>
<phrase>
@@ -2825,7 +2652,7 @@
*: "Edit mode: %s %s"
</source>
<dest>
- *: "Edit mode: %s"
+ *: "Düzenleme Modu: %s"
</dest>
<voice>
*: ""
@@ -2839,57 +2666,10 @@
*: "Cutoff"
</source>
<dest>
- *: "Cutoff Frequency"
- </dest>
- <voice>
- *: "Cutoff Frequency"
- </voice>
-</phrase>
-<phrase>
- id: LANG_SYSFONT_CHANNEL_STEREO
- desc: in sound_settings
- user: core
- <source>
- *: none
- recording: "Stereo"
- </source>
- <dest>
- *: none
- </dest>
- <voice>
- *: none
- </voice>
-</phrase>
-<phrase>
- id: LANG_SYSFONT_CHANNEL_MONO
- desc: in sound_settings
- user: core
- <source>
- *: none
- recording: "Mono"
- </source>
- <dest>
- *: none
- </dest>
- <voice>
- *: none
- </voice>
-</phrase>
-<phrase>
- id: LANG_SYSFONT_CHANNELS
- desc: in the recording settings
- user: core
- <source>
- *: none
- recording: "Channels"
- </source>
- <dest>
- *: none
- recording: "Kanallar"
+ *: "Kesme Frekansı"
</dest>
<voice>
- *: none
- recording: "Kanallar"
+ *: "Kesme Frekansı"
</voice>
</phrase>
<phrase>
@@ -2956,10 +2736,10 @@
*: "Show Filename Extensions"
</source>
<dest>
- *: "Dosya Çeşitleri Göster"
+ *: "Dosya Adlarını Göster"
</dest>
<voice>
- *: "dosya çeşitleri göster"
+ *: "Dosya Adlarını Göster"
</voice>
</phrase>
<phrase>
@@ -2970,10 +2750,10 @@
*: "Only Unknown Types"
</source>
<dest>
- *: "Sadece bilinmez çeşitler"
+ *: "Yalnızca Bilinmeyen Türler"
</dest>
<voice>
- *: "Sadece bilinmez çeşitler"
+ *: "Yalnızca Bilinmeyen Türler"
</voice>
</phrase>
<phrase>
@@ -2984,10 +2764,10 @@
*: "Only When Viewing All Types"
</source>
<dest>
- *: "Sadece hepsi çeşiti görürken"
+ *: "Yalnızca Tüm Türleri Görüntülerken"
</dest>
<voice>
- *: "Sadece hepsi çeşiti görürken"
+ *: "Yalnızca Tüm Türleri Görüntülerken"
</voice>
</phrase>
<phrase>
@@ -3001,7 +2781,7 @@
*: ""
</dest>
<voice>
- *: "bir tikta birim"
+ *: "Tık başına birim"
</voice>
</phrase>
<phrase>
@@ -3015,7 +2795,7 @@
*: ""
</dest>
<voice>
- *: ""
+ *: "Saat"
</voice>
</phrase>
<phrase>
@@ -3029,7 +2809,7 @@
*: ""
</dest>
<voice>
- *: "akÅŸam"
+ *: "ÖĞLEDEN SONRA"
</voice>
</phrase>
<phrase>
@@ -3043,7 +2823,7 @@
*: ""
</dest>
<voice>
- *: "sabah"
+ *: "ÖĞLEDEN ÖNCE"
</voice>
</phrase>
<phrase>
@@ -3101,11 +2881,11 @@
</source>
<dest>
*: none
- lcd_color: "Çizgi Seçen Renkler"
+ lcd_color: "Menü Gezinme Renkleri"
</dest>
<voice>
*: none
- lcd_color: "Çizgi seçen renkler"
+ lcd_color: "Menü Gezinme Renkleri"
</voice>
</phrase>
<phrase>
@@ -3119,7 +2899,7 @@
*: ""
</dest>
<voice>
- *: "Edit et"
+ *: "Düzenle"
</voice>
</phrase>
<phrase>
@@ -3160,11 +2940,11 @@
</source>
<dest>
*: none
- multivolume: "Önceden ayarlamadı"
+ multivolume: "Mevcut DeÄŸil"
</dest>
<voice>
*: none
- multivolume: "Önceden ayarlamadı"
+ multivolume: "Mevcut DeÄŸil"
</voice>
</phrase>
<phrase>
@@ -3175,10 +2955,10 @@
*: "Announce Battery Level"
</source>
<dest>
- *: "Pil güçü söyle"
+ *: "Pil Seviyesini Bildir"
</dest>
<voice>
- *: "Pil güçü söyle"
+ *: "Pil Seviyesini Bildir"
</voice>
</phrase>
<phrase>
@@ -3189,10 +2969,10 @@
*: "Say File Type"
</source>
<dest>
- *: "Dosya Çeşiti Söyle"
+ *: "Dosya Türünü Söyle"
</dest>
<voice>
- *: "Dosya Çeşiti Söyle"
+ *: "Dosya Türünü Söyle"
</voice>
</phrase>
<phrase>
@@ -3203,10 +2983,10 @@
*: "<Random>"
</source>
<dest>
- *: "<Rasgele>"
+ *: "<Rastgele>"
</dest>
<voice>
- *: "Rasgele"
+ *: "Rastgele"
</voice>
</phrase>
<phrase>
@@ -3261,11 +3041,11 @@
</source>
<dest>
*: none
- accessory_supply: "Yardımcı Elektrik Sağlayan"
+ accessory_supply: "Güç Kaynağı Aksesuarı"
</dest>
<voice>
*: none
- accessory_supply: "Yardımcı Elektrik Sağlayan"
+ accessory_supply: "Güç Kaynağı Aksesuarı"
</voice>
</phrase>
<phrase>
@@ -3276,10 +3056,10 @@
*: "Unknown"
</source>
<dest>
- *: "Bilinmez"
+ *: "Bilinmiyor"
</dest>
<voice>
- *: "Bilinmez"
+ *: "Bilinmiyor"
</voice>
</phrase>
<phrase>
@@ -3293,7 +3073,7 @@
*: ""
</dest>
<voice>
- *: "Hızlı ekranı"
+ *: "Hızlı ekran"
</voice>
</phrase>
<phrase>
@@ -3320,11 +3100,11 @@
</source>
<dest>
*: none
- recording: "Ses kaydeti durup kapat"
+ recording: "Kaydı Durdur ve Kapat"
</dest>
<voice>
*: none
- recording: "Ses kaydeti durup kapat"
+ recording: "Kaydı Durdur ve Kapat"
</voice>
</phrase>
<phrase>
@@ -3333,15 +3113,15 @@
user: core
<source>
*: none
- gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
</source>
<dest>
*: none
- gigabeatfx,sansafuzeplus: "Touchpad Duyarlık"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Touchpad Duyarlık"
</dest>
<voice>
*: none
- gigabeatfx,sansafuzeplus: "Touchpad Duyarlık"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Touchpad Duyarlık"
</voice>
</phrase>
<phrase>
@@ -3408,10 +3188,10 @@
*: "Skip Length"
</source>
<dest>
- *: "Şarkıyı Atlamasının Adımı"
+ *: "Şarkı Atlama Adımı"
</dest>
<voice>
- *: "Şarkıyı Atlamasının Adımı"
+ *: "Şarkı Atlama Adımı"
</voice>
</phrase>
<phrase>
@@ -3422,10 +3202,10 @@
*: "Skip Track"
</source>
<dest>
- *: "Bütün Şarkı"
+ *: "Tüm Şarkı"
</dest>
<voice>
- *: "Bütün Şarkı"
+ *: "Tüm Şarkı"
</voice>
</phrase>
<phrase>
@@ -3450,10 +3230,10 @@
*: "Base Skin"
</source>
<dest>
- *: "Temel Kabuk"
+ *: "Ana Tema"
</dest>
<voice>
- *: "Base Skin"
+ *: "Ana Tema"
</voice>
</phrase>
<phrase>
@@ -3483,11 +3263,11 @@
</source>
<dest>
*: none
- usb_hid: "Sunum Kipi"
+ usb_hid: "Sunum"
</dest>
<voice>
*: none
- usb_hid: "Presentation Mode"
+ usb_hid: "Sunum modu"
</voice>
</phrase>
<phrase>
@@ -3500,11 +3280,11 @@
</source>
<dest>
*: none
- usb_hid: "Çokluortam Kipi"
+ usb_hid: "Çoklu Ortam(Multimedya)"
</dest>
<voice>
*: none
- usb_hid: "Multimedia Mode"
+ usb_hid: "Multimedia Modu"
</voice>
</phrase>
<phrase>
@@ -3517,11 +3297,11 @@
</source>
<dest>
*: none
- usb_hid: "Fare Kipi"
+ usb_hid: "Fare Kullanımı"
</dest>
<voice>
*: none
- usb_hid: "Mouse Mode"
+ usb_hid: "Fare Kullanımı"
</voice>
</phrase>
<phrase>
@@ -3534,11 +3314,11 @@
</source>
<dest>
*: none
- usb_hid: "Tarayıcı Kipi"
+ usb_hid: "Tarayıcı Modu"
</dest>
<voice>
*: none
- usb_hid: "Browser Mode"
+ usb_hid: "Tarayıcı Modu"
</voice>
</phrase>
<phrase>
@@ -3552,24 +3332,7 @@
*: "Uzak Temel Kabuk"
</dest>
<voice>
- *: "Remote Base Skin"
- </voice>
-</phrase>
-<phrase>
- id: LANG_DECAY
- desc: in sound_settings
- user: core
- <source>
- *: none
- masf: "AV Decay Time"
- </source>
- <dest>
- *: none
- masf: "AV Bozulum Zamanı"
- </dest>
- <voice>
- *: none
- masf: ""
+ *: "Uzak Temel Kabuk"
</voice>
</phrase>
<phrase>
@@ -3586,7 +3349,7 @@
</dest>
<voice>
*: none
- lineout_poweroff: "Line Out"
+ lineout_poweroff: "Çıkış"
</voice>
</phrase>
<phrase>
@@ -3603,7 +3366,7 @@
</dest>
<voice>
*: none
- radio: "radio screen skin"
+ radio: "Radio Ekranı"
</voice>
</phrase>
<phrase>
@@ -3637,7 +3400,7 @@
</dest>
<voice>
*: none
- radio_remote: "remote radio screen skin"
+ radio_remote: "Radio Ekranı Teması"
</voice>
</phrase>
<phrase>
@@ -3654,7 +3417,7 @@
</dest>
<voice>
*: none
- remote: "Remote Screen"
+ remote: "Uzak Ekran"
</voice>
</phrase>
<phrase>
@@ -3684,11 +3447,11 @@
</source>
<dest>
*: none
- hotkey: "Dosya Tarama Geçiş Tuşu"
+ hotkey: "Dosya Tarayıcı Kısayol Tuşu"
</dest>
<voice>
*: none
- hotkey: "File Browser Hotkey"
+ hotkey: "Dosya Tarayıcı Kısayol Tuşu"
</voice>
</phrase>
<phrase>
@@ -3701,11 +3464,11 @@
</source>
<dest>
*: none
- hardware_click: "Headphone Keyclick"
+ hardware_click: "Tuş Sesi-Kulaklık"
</dest>
<voice>
*: none
- hardware_click: "Headphone Keyclick"
+ hardware_click: "Kulaklık Tuş sesi"
</voice>
</phrase>
<phrase>
@@ -3718,11 +3481,11 @@
</source>
<dest>
*: none
- recording: "Start new file"
+ recording: "Yeni dosya baÅŸlat"
</dest>
<voice>
*: none
- recording: "Start new file"
+ recording: "Yeni dosya baÅŸlat"
</voice>
</phrase>
<phrase>
@@ -3747,10 +3510,10 @@
*: "Strong"
</source>
<dest>
- *: "Strong"
+ *: "Güçlü"
</dest>
<voice>
- *: "Strong"
+ *: "Güçlü"
</voice>
</phrase>
<phrase>
@@ -3763,11 +3526,11 @@
</source>
<dest>
*: none
- battery_types: "Battery Type"
+ battery_types: "Pil Türü"
</dest>
<voice>
*: none
- battery_types: "Battery type"
+ battery_types: "Pil Türü"
</voice>
</phrase>
<phrase>
@@ -3780,11 +3543,11 @@
</source>
<dest>
*: none
- play_frequency,recording: "Frequency"
+ play_frequency,recording: "Frekans"
</dest>
<voice>
*: none
- play_frequency,recording: "Frequency"
+ play_frequency,recording: "Frekans"
</voice>
</phrase>
<phrase>
@@ -3793,15 +3556,18 @@
user: core
<source>
*: "Int:"
+ hibylinux: "mSD:"
xduoox3: "mSD1:"
</source>
<dest>
- *: "Int:"
- xduoox3: "mSD1:"
+ *: "Dahili"
+ hibylinux: "MicroSD"
+ xduoox3: "MicroSD1"
</dest>
<voice>
- *: "Internal"
- xduoox3: "micro S D 1"
+ *: "Dahili"
+ hibylinux: "MicroSD"
+ xduoox3: "MicroSD1"
</voice>
</phrase>
<phrase>
@@ -3812,27 +3578,10 @@
*: "While Playing Screen"
</source>
<dest>
- *: "While Playing Screen"
+ *: "Oynatma Ekranı"
</dest>
<voice>
- *: "While playing screen"
- </voice>
-</phrase>
-<phrase>
- id: LANG_FM_BUTTONBAR_EXIT
- desc: in radio screen
- user: core
- <source>
- *: none
- radio_screen_button_bar: "Exit"
- </source>
- <dest>
- *: none
- radio_screen_button_bar: "Çıkış"
- </dest>
- <voice>
- *: none
- radio_screen_button_bar: ""
+ *: "Oynatma Ekranı"
</voice>
</phrase>
<phrase>
@@ -3857,10 +3606,10 @@
*: "Start Screen"
</source>
<dest>
- *: "Start Screen"
+ *: "Başlama Ekranı"
</dest>
<voice>
- *: "Start Screen"
+ *: "Başlama Ekranı"
</voice>
</phrase>
<phrase>
@@ -3873,11 +3622,11 @@
</source>
<dest>
*: none
- remote: "Remote Scrolling Options"
+ remote: "Uzaktan Kaydırma Seçenekleri"
</dest>
<voice>
*: none
- remote: "Remote Scrolling Options"
+ remote: "Uzaktan Kaydırma Seçenekleri"
</voice>
</phrase>
<phrase>
@@ -3902,10 +3651,10 @@
*: "Prevent Clipping"
</source>
<dest>
- *: "Prevent Clipping"
+ *: "Kırpmayı Önle"
</dest>
<voice>
- *: "Prevent Clipping"
+ *: "Kırpmayı Önle"
</voice>
</phrase>
<phrase>
@@ -3918,11 +3667,11 @@
</source>
<dest>
*: none
- radio: "Preset List"
+ radio: "Ön Ayar Listesi"
</dest>
<voice>
*: none
- radio: "Preset List"
+ radio: "Ön Ayar Listesi"
</voice>
</phrase>
<phrase>
@@ -3935,11 +3684,11 @@
</source>
<dest>
*: none
- headphone_detection: "Pause and Resume"
+ headphone_detection: "Duraklat ve Devam Ettir"
</dest>
<voice>
*: none
- headphone_detection: "Pause and Resume"
+ headphone_detection: "Duraklat ve Devam Ettir"
</voice>
</phrase>
<phrase>
@@ -3966,11 +3715,11 @@
</source>
<dest>
*: none
- recording: "Clip Counter"
+ recording: "Klip Sayacı"
</dest>
<voice>
*: none
- recording: "Clip Counter"
+ recording: "Klip Sayacı"
</voice>
</phrase>
<phrase>
@@ -3981,10 +3730,10 @@
*: "Next:"
</source>
<dest>
- *: "Next:"
+ *: "Sonraki:"
</dest>
<voice>
- *: "Next:"
+ *: "Sonraki:"
</voice>
</phrase>
<phrase>
@@ -3995,10 +3744,10 @@
*: "Soft Knee"
</source>
<dest>
- *: "Soft Knee"
+ *: "YumuÅŸak Knee"
</dest>
<voice>
- *: "Soft Knee"
+ *: "YumuÅŸak Diz"
</voice>
</phrase>
<phrase>
@@ -4011,11 +3760,11 @@
</source>
<dest>
*: none
- morse_input: "Use Morse Code Input"
+ morse_input: "Mors Kodu GiriÅŸini Kullan"
</dest>
<voice>
*: none
- morse_input: "Use Morse Code Input"
+ morse_input: "Mors Kodu GiriÅŸini Kullan"
</voice>
</phrase>
<phrase>
@@ -4026,10 +3775,10 @@
*: "Queue"
</source>
<dest>
- *: "Queue"
+ *: "Sıra"
</dest>
<voice>
- *: "Queue"
+ *: "Sıra"
</voice>
</phrase>
<phrase>
@@ -4040,10 +3789,10 @@
*: "Recursively?"
</source>
<dest>
- *: "Recursively?"
+ *: "Tekrarlı?"
</dest>
<voice>
- *: "Recursively?"
+ *: "Tekrarlı?"
</voice>
</phrase>
<phrase>
@@ -4056,11 +3805,11 @@
</source>
<dest>
*: none
- touchscreen: "OK"
+ touchscreen: "Tamam"
</dest>
<voice>
*: none
- touchscreen: "OK"
+ touchscreen: "Tamam"
</voice>
</phrase>
<phrase>
@@ -4071,10 +3820,10 @@
*: "Left"
</source>
<dest>
- *: "Left"
+ *: "Sol"
</dest>
<voice>
- *: "Left"
+ *: "Sol"
</voice>
</phrase>
<phrase>
@@ -4085,10 +3834,10 @@
*: "Autolock Off"
</source>
<dest>
- *: "Autolock Off"
+ *: "Otomatik Kilitleme Kapalı"
</dest>
<voice>
- *: "Autolock Off"
+ *: "Otomatik Kilitleme Kapalı"
</voice>
</phrase>
<phrase>
@@ -4115,11 +3864,11 @@
</source>
<dest>
*: none
- radio: "Preset Save Failed"
+ radio: "Ön Ayar Kaydetme Başarısız!"
</dest>
<voice>
*: none
- radio: "Preset Save Failed"
+ radio: "Ön Ayar Kaydetme Başarısız!"
</voice>
</phrase>
<phrase>
@@ -4144,10 +3893,10 @@
*: "Automatic"
</source>
<dest>
- *: "Automatic"
+ *: "Otomatik"
</dest>
<voice>
- *: "Automatic"
+ *: "Otomatik"
</voice>
</phrase>
<phrase>
@@ -4160,11 +3909,11 @@
</source>
<dest>
*: none
- radio: "Remove Preset"
+ radio: "Ön Ayarı Sil"
</dest>
<voice>
*: none
- radio: "Remove Preset"
+ radio: "Ön Ayarı Sil"
</voice>
</phrase>
<phrase>
@@ -4175,10 +3924,10 @@
*: "Screen Scroll Step Size"
</source>
<dest>
- *: "Screen Scroll Step Size"
+ *: "Ekran Kaydırma Adım Boyutu"
</dest>
<voice>
- *: "Screen Scroll Step Size"
+ *: "Ekran Kaydırma Adım Boyutu"
</voice>
</phrase>
<phrase>
@@ -4189,27 +3938,10 @@
*: "Select directories to scan"
</source>
<dest>
- *: "Select directories to scan"
- </dest>
- <voice>
- *: "Select directories to scan"
- </voice>
-</phrase>
-<phrase>
- id: LANG_SYSFONT_RECORDING_SOURCE
- desc: in the recording settings
- user: core
- <source>
- *: none
- recording: "Source"
- </source>
- <dest>
- *: none
- recording: "Kaynak"
+ *: "Taranacak dizinleri seçin"
</dest>
<voice>
- *: none
- recording: "Kaynak"
+ *: "Taranacak dizinleri seçin"
</voice>
</phrase>
<phrase>
@@ -4222,11 +3954,11 @@
</source>
<dest>
*: none
- recording: "(No Settings)"
+ recording: "(Ayarlanmadı)"
</dest>
<voice>
*: none
- recording: "No settings available"
+ recording: "(Ayarlanmadı)"
</voice>
</phrase>
<phrase>
@@ -4237,10 +3969,10 @@
*: "Settings Loaded"
</source>
<dest>
- *: "Settings Loaded"
+ *: "Ayarlar Yüklendi"
</dest>
<voice>
- *: "Settings Loaded"
+ *: "Ayarlar Yüklendi"
</voice>
</phrase>
<phrase>
@@ -4265,10 +3997,10 @@
*: "Search In Playlist"
</source>
<dest>
- *: "Search In Playlist"
+ *: "Oynatma Listesinde Ara"
</dest>
<voice>
- *: "Search In Playlist"
+ *: "Oynatma Listesinde Ara"
</voice>
</phrase>
<phrase>
@@ -4279,10 +4011,10 @@
*: "View Current Playlist"
</source>
<dest>
- *: "View Current Playlist"
+ *: "Mevcut Oynatma Listesini Görüntüle"
</dest>
<voice>
- *: "View Current Playlist"
+ *: "Mevcut Oynatma Listesini Görüntüle"
</voice>
</phrase>
<phrase>
@@ -4293,7 +4025,7 @@
*: "Scroll Step Size Setting Example Text"
</source>
<dest>
- *: "Scroll Step Size Setting Example Text"
+ *: "Kaydırma Adım Boyutu Ayarı Örnek Metin"
</dest>
<voice>
*: ""
@@ -4314,20 +4046,6 @@
</voice>
</phrase>
<phrase>
- id: LANG_BOOKMARK_SETTINGS_UNIQUE_ONLY
- desc: Save only on bookmark for each playlist in recent bookmarks
- user: core
- <source>
- *: "Unique only"
- </source>
- <dest>
- *: "Unique only"
- </dest>
- <voice>
- *: "Unique only"
- </voice>
-</phrase>
-<phrase>
id: LANG_FM_STATION_HEADER
desc: in radio screen
user: core
@@ -4337,7 +4055,7 @@
</source>
<dest>
*: none
- radio: "Station:"
+ radio: "Ä°stasyon:"
</dest>
<voice>
*: none
@@ -4352,10 +4070,10 @@
*: "Reset Playlist Catalogue Directory"
</source>
<dest>
- *: "Reset Playlist Catalogue Directory"
+ *: "Oynatma Listesi Katalog Dizinini Sıfırla"
</dest>
<voice>
- *: "Reset Playlist Catalogue Directory"
+ *: "Oynatma Listesi Katalog Dizinini Sıfırla"
</voice>
</phrase>
<phrase>
@@ -4366,10 +4084,10 @@
*: "Haas Surround"
</source>
<dest>
- *: "Haas Surround"
+ *: "Surround Haas "
</dest>
<voice>
- *: "Haas Surround"
+ *: "Surround Haas "
</voice>
</phrase>
<phrase>
@@ -4394,10 +4112,10 @@
*: "Queued %d tracks (%s)"
</source>
<dest>
- *: "Queued %d tracks (%s)"
+ *: "Kuyruğa Alınan %d Parça (%s)"
</dest>
<voice>
- *: "tracks queued"
+ *: "Kuyruğa Alınan %d Parça (%s)"
</voice>
</phrase>
<phrase>
@@ -4408,10 +4126,10 @@
*: "Rename"
</source>
<dest>
- *: "Rename"
+ *: "Yeniden Adlandır"
</dest>
<voice>
- *: "Rename"
+ *: "Yeniden Adlandır"
</voice>
</phrase>
<phrase>
@@ -4424,7 +4142,7 @@
</source>
<dest>
*: none
- recording: "Filename:"
+ recording: "Dosya adı:"
</dest>
<voice>
*: none
@@ -4441,11 +4159,11 @@
</source>
<dest>
*: none
- recording: "Trigger"
+ recording: "Tetikle"
</dest>
<voice>
*: none
- recording: "Trigger"
+ recording: "Tetikle"
</voice>
</phrase>
<phrase>
@@ -4459,7 +4177,7 @@
*: ""
</dest>
<voice>
- *: ""
+ *: "1"
</voice>
</phrase>
<phrase>
@@ -4472,11 +4190,11 @@
</source>
<dest>
*: none
- recording: "Split Measure"
+ recording: "Bölünmüş Ölçü"
</dest>
<voice>
*: none
- recording: "Split Measure"
+ recording: "Bölünmüş Ölçü"
</voice>
</phrase>
<phrase>
@@ -4506,7 +4224,7 @@
</source>
<dest>
*: none
- recording: "Time:"
+ recording: "Zaman:"
</dest>
<voice>
*: none
@@ -4523,11 +4241,11 @@
</source>
<dest>
*: none
- recording: "Stop Below"
+ recording: "Aşağıda Dur"
</dest>
<voice>
*: none
- recording: "Stop Below"
+ recording: "Aşağıda Dur"
</voice>
</phrase>
<phrase>
@@ -4538,10 +4256,10 @@
*: "Sort Directories"
</source>
<dest>
- *: "Sort Directories"
+ *: "Dizinleri Sırala"
</dest>
<voice>
- *: "sort directories"
+ *: "Dizinleri Sırala"
</voice>
</phrase>
<phrase>
@@ -4566,10 +4284,10 @@
*: "Android Debug Bridge"
</source>
<dest>
- *: "Android Debug Bridge"
+ *: "Android Hata Ayıklama Köprüsü"
</dest>
<voice>
- *: "Android Debug Bridge"
+ *: "Android Hata Ayıklama Köprüsü"
</voice>
</phrase>
<phrase>
@@ -4582,11 +4300,11 @@
</source>
<dest>
*: none
- gigabeats,samsungypr1: "Advanced Tone Control Settings"
+ gigabeats,samsungypr1: "Gelişmiş Ton Kontrol Ayarları"
</dest>
<voice>
*: none
- gigabeats,samsungypr1: "Advanced Tone Control Settings"
+ gigabeats,samsungypr1: "Gelişmiş Ton Kontrol Ayarları"
</voice>
</phrase>
<phrase>
@@ -4625,10 +4343,10 @@
*: "Save Changes?"
</source>
<dest>
- *: "Save Changes?"
+ *: "DeÄŸiÅŸiklikleri Kaydet?"
</dest>
<voice>
- *: "Save Changes?"
+ *: "DeÄŸiÅŸiklikleri Kaydet?"
</voice>
</phrase>
<phrase>
@@ -4641,11 +4359,11 @@
</source>
<dest>
*: none
- recording: "Gain R"
+ recording: "Kazanç R Sağ"
</dest>
<voice>
*: none
- recording: "Gain Right"
+ recording: "Kazanç R Sağ"
</voice>
</phrase>
<phrase>
@@ -4659,7 +4377,7 @@
*: ""
</dest>
<voice>
- *: "milliseconds"
+ *: "Milisaniye"
</voice>
</phrase>
<phrase>
@@ -4670,10 +4388,10 @@
*: "Full Path"
</source>
<dest>
- *: "Full Path"
+ *: "Tam yol"
</dest>
<voice>
- *: "Full Path"
+ *: "Tam Yol"
</voice>
</phrase>
<phrase>
@@ -4698,10 +4416,10 @@
*: "Disk"
</source>
<dest>
- *: "Disk"
+ *: "Disk Ayarları"
</dest>
<voice>
- *: "Disk"
+ *: "Disk Ayarları"
</voice>
</phrase>
<phrase>
@@ -4714,11 +4432,11 @@
</source>
<dest>
*: none
- filter_roll_off: "Slow"
+ filter_roll_off: "YavaÅŸ"
</dest>
<voice>
*: none
- filter_roll_off: "Slow"
+ filter_roll_off: "YavaÅŸ"
</voice>
</phrase>
<phrase>
@@ -4729,10 +4447,10 @@
*: "High Shelf Filter"
</source>
<dest>
- *: "High Shelf Filter"
+ *: "Yüksek Band Filtresi "
</dest>
<voice>
- *: "High shelf filter"
+ *: "Yüksek Band Filtresi "
</voice>
</phrase>
<phrase>
@@ -4743,10 +4461,10 @@
*: "Remove"
</source>
<dest>
- *: "Remove"
+ *: "Sil"
</dest>
<voice>
- *: "Remove"
+ *: "Sil"
</voice>
</phrase>
<phrase>
@@ -4759,11 +4477,11 @@
</source>
<dest>
*: none
- radio: "Add Preset"
+ radio: "Ön Ayar Ekle"
</dest>
<voice>
*: none
- radio: "Add Preset"
+ radio: "Ön Ayar Ekle"
</voice>
</phrase>
<phrase>
@@ -4809,23 +4527,6 @@
</voice>
</phrase>
<phrase>
- id: LANG_BUTTON_BAR
- desc: in settings menu
- user: core
- <source>
- *: none
- recorder_pad: "Button Bar"
- </source>
- <dest>
- *: none
- recorder_pad: "Button Bar"
- </dest>
- <voice>
- *: none
- recorder_pad: "Button Bar"
- </voice>
-</phrase>
-<phrase>
id: LANG_CODEPAGE_TURKISH
desc: in codepage setting menu
user: core
@@ -4833,10 +4534,10 @@
*: "Turkish (ISO-8859-9)"
</source>
<dest>
- *: "Turkish (ISO-8859-9)"
+ *: "Turkçe (ISO-8859-9)"
</dest>
<voice>
- *: "Turkish"
+ *: "Türkçe"
</voice>
</phrase>
<phrase>
@@ -4847,10 +4548,10 @@
*: "Previous Screen"
</source>
<dest>
- *: "Previous Screen"
+ *: "Önceki Ekran"
</dest>
<voice>
- *: "Previous Screen"
+ *: "Önceki Ekran"
</voice>
</phrase>
<phrase>
@@ -4861,10 +4562,10 @@
*: "Yes - Recent only"
</source>
<dest>
- *: "Yes - Recent only"
+ *: "Evet - Yalnızca son zamanlarda"
</dest>
<voice>
- *: "Yes - Recent only"
+ *: "Evet - Yalnızca son zamanlarda"
</voice>
</phrase>
<phrase>
@@ -4875,10 +4576,10 @@
*: "Queue Shuffled"
</source>
<dest>
- *: "Queue Shuffled"
+ *: "Karışık Sıralama"
</dest>
<voice>
- *: "Queue Shuffled"
+ *: "Karışık Sıralama"
</voice>
</phrase>
<phrase>
@@ -4889,10 +4590,10 @@
*: "Filetype array full"
</source>
<dest>
- *: "Filetype array full"
+ *: "Dosya türü dizisi dolu"
</dest>
<voice>
- *: "Filetype array full"
+ *: "Dosya türü dizisi dolu"
</voice>
</phrase>
<phrase>
@@ -4903,10 +4604,10 @@
*: "Speed"
</source>
<dest>
- *: "Speed"
+ *: "Hız"
</dest>
<voice>
- *: "Speed"
+ *: "Hız"
</voice>
</phrase>
<phrase>
@@ -4919,11 +4620,11 @@
</source>
<dest>
*: none
- radio: "Save Preset List"
+ radio: "Ön Ayar Listesini Kaydet"
</dest>
<voice>
*: none
- radio: "Save Preset List"
+ radio: "Ön Ayar Listesini Kaydet"
</voice>
</phrase>
<phrase>
@@ -4951,10 +4652,10 @@
*: "Track Name Only"
</source>
<dest>
- *: "Track Name Only"
+ *: "Yalnızca Parça Adı"
</dest>
<voice>
- *: "Track Name Only"
+ *: "Yalnızca Parça Adı"
</voice>
</phrase>
<phrase>
@@ -4965,10 +4666,10 @@
*: "Limits"
</source>
<dest>
- *: "Limits"
+ *: "Sınırlar"
</dest>
<voice>
- *: "Limits"
+ *: "Sınırlar"
</voice>
</phrase>
<phrase>
@@ -4981,11 +4682,11 @@
</source>
<dest>
*: none
- agc: "Safety (clip)"
+ agc: "Güvenlik (clip)"
</dest>
<voice>
*: none
- agc: "Safety (clip)"
+ agc: "Güvenlik (clip)"
</voice>
</phrase>
<phrase>
@@ -4996,10 +4697,10 @@
*: "Set As Playlist Catalogue Directory"
</source>
<dest>
- *: "Set As Playlist Catalogue Directory"
+ *: "Oynatma Listesi Katalog Dizini Olarak Ayarla"
</dest>
<voice>
- *: "Set As Playlist Catalogue Directory"
+ *: "Oynatma Listesi Katalog Dizini Olarak Ayarla"
</voice>
</phrase>
<phrase>
@@ -5030,7 +4731,7 @@
</dest>
<voice>
*: none
- rtc: "Current time:"
+ rtc: "Geçerli Zaman:"
</voice>
</phrase>
<phrase>
@@ -5055,10 +4756,10 @@
*: "Browse Cuesheet"
</source>
<dest>
- *: "Browse Cuesheet"
+ *: "Cuesheet'e göz atın"
</dest>
<voice>
- *: "Browse Cuesheet"
+ *: "Cuesheet'e göz atın"
</voice>
</phrase>
<phrase>
@@ -5102,11 +4803,11 @@
</source>
<dest>
*: none
- recording: "Stop"
+ recording: "Dur"
</dest>
<voice>
*: none
- recording: "Stop"
+ recording: "Dur"
</voice>
</phrase>
<phrase>
@@ -5117,10 +4818,10 @@
*: "Insert"
</source>
<dest>
- *: "Insert"
+ *: "Tak"
</dest>
<voice>
- *: "Insert"
+ *: "Tak"
</voice>
</phrase>
<phrase>
@@ -5131,10 +4832,10 @@
*: "LCD Settings"
</source>
<dest>
- *: "LCD Settings"
+ *: "Ekran Ayarları"
</dest>
<voice>
- *: "LCD Settings"
+ *: "Ekran Ayarları"
</voice>
</phrase>
<phrase>
@@ -5147,11 +4848,11 @@
</source>
<dest>
*: none
- hardware_click: "Speaker Keyclick"
+ hardware_click: "Tuş Sesi-Hoparlör "
</dest>
<voice>
*: none
- hardware_click: "Speaker Keyclick"
+ hardware_click: "Hoparlör Tuş Sesi"
</voice>
</phrase>
<phrase>
@@ -5162,10 +4863,10 @@
*: "Voice Directories"
</source>
<dest>
- *: "Voice Directories"
+ *: "Ses Dizinleri"
</dest>
<voice>
- *: "Voice Directories"
+ *: "Ses Dizinleri"
</voice>
</phrase>
<phrase>
@@ -5176,10 +4877,10 @@
*: "Start File Browser Here"
</source>
<dest>
- *: "Start File Browser Here"
+ *: "Dosya Tarayıcısını Buradan Başlat "
</dest>
<voice>
- *: "Start File Browser Here"
+ *: "Dosya Tarayıcısını Buradan Başlat"
</voice>
</phrase>
<phrase>
@@ -5192,11 +4893,11 @@
</source>
<dest>
*: none
- touchscreen: "Absolute Point"
+ touchscreen: "Mutlak Nokta"
</dest>
<voice>
*: none
- touchscreen: "Absolute Point"
+ touchscreen: "Mutlak Nokta"
</voice>
</phrase>
<phrase>
@@ -5207,10 +4908,10 @@
*: "Line Separator"
</source>
<dest>
- *: "Line Separator"
+ *: "Hat Ayırıcı"
</dest>
<voice>
- *: "Line Separator"
+ *: "Hat Ayırıcı"
</voice>
</phrase>
<phrase>
@@ -5221,10 +4922,10 @@
*: "Caption Backlight"
</source>
<dest>
- *: "Caption Backlight"
+ *: "Altyazı Arka Işık"
</dest>
<voice>
- *: "Caption Backlight"
+ *: "Altyazı Arka Işık"
</voice>
</phrase>
<phrase>
@@ -5235,10 +4936,10 @@
*: "Disable Touch"
</source>
<dest>
- *: "Disable Touch"
+ *: "Dokunmayı Devre Dışı Bırak"
</dest>
<voice>
- *: "Disable Touch"
+ *: "Dokunmayı Devre Dışı Bırak"
</voice>
</phrase>
<phrase>
@@ -5249,10 +4950,10 @@
*: "Latin Extended (ISO-8859-2)"
</source>
<dest>
- *: "Latin Extended (ISO-8859-2)"
+ *: "GeniÅŸletilmiÅŸ Latince (ISO-8859-2)"
</dest>
<voice>
- *: "Latin extended"
+ *: "GeniÅŸletilmiÅŸ Latince"
</voice>
</phrase>
<phrase>
@@ -5263,10 +4964,10 @@
*: "Automatic resume"
</source>
<dest>
- *: "Automatic resume"
+ *: "Otomatik Devam"
</dest>
<voice>
- *: "Automatic resume"
+ *: "Otomatik Devam"
</voice>
</phrase>
<phrase>
@@ -5321,11 +5022,11 @@
</source>
<dest>
*: none
- recording: "Line In"
+ recording: "Line In GiriÅŸi"
</dest>
<voice>
*: none
- recording: "Line In"
+ recording: "Line In GiriÅŸi"
</voice>
</phrase>
<phrase>
@@ -5336,10 +5037,10 @@
*: "Advanced Key Lock"
</source>
<dest>
- *: "Advanced Key Lock"
+ *: "GeliÅŸmiÅŸ TuÅŸ Kilidi"
</dest>
<voice>
- *: "Advanced Key Lock"
+ *: "GeliÅŸmiÅŸ TuÅŸ Kilidi"
</voice>
</phrase>
<phrase>
@@ -5352,11 +5053,11 @@
</source>
<dest>
*: none
- lcd_non-mono: "Set As Backdrop"
+ lcd_non-mono: "Arka Plan Olarak Ayarla"
</dest>
<voice>
*: none
- lcd_non-mono: "Set As Backdrop"
+ lcd_non-mono: "Arka Plan Olarak Ayarla"
</voice>
</phrase>
<phrase>
@@ -5386,11 +5087,11 @@
</source>
<dest>
*: none
- quickscreen: "Set as Right Quickscreen Item"
+ quickscreen: "Sağ Hızlı Ekran Öğesi Olarak Ayarla"
</dest>
<voice>
*: none
- quickscreen: "Set as Right Quickscreen Item"
+ quickscreen: "Sağ Hızlı Ekran Öğesi Olarak Ayarla"
</voice>
</phrase>
<phrase>
@@ -5403,11 +5104,11 @@
</source>
<dest>
*: none
- usb_charging_enable: "Charge During USB Connection"
+ usb_charging_enable: "USB Bağlantısı Sırasında Şarj Et"
</dest>
<voice>
*: none
- usb_charging_enable: "Charge During U S B Connection"
+ usb_charging_enable: "USB Bağlantısı Sırasında Şarj Et"
</voice>
</phrase>
<phrase>
@@ -5418,10 +5119,10 @@
*: "View Catalogue"
</source>
<dest>
- *: "View Catalogue"
+ *: "Kataloğu Görüntüle "
</dest>
<voice>
- *: "View Catalogue"
+ *: "Kataloğu Görüntüle "
</voice>
</phrase>
<phrase>
@@ -5446,10 +5147,10 @@
*: "Update on Stop"
</source>
<dest>
- *: "Update on Stop"
+ *: "Durunca Güncelle"
</dest>
<voice>
- *: "Update on Stop"
+ *: "Durunca Güncelle"
</voice>
</phrase>
<phrase>
@@ -5460,10 +5161,10 @@
*: "Manage Settings"
</source>
<dest>
- *: "Manage Settings"
+ *: "Ayarları Yönet"
</dest>
<voice>
- *: "Manage Settings"
+ *: "Ayarları Yönet"
</voice>
</phrase>
<phrase>
@@ -5474,10 +5175,10 @@
*: "Cyrillic (CP1251)"
</source>
<dest>
- *: "Cyrillic (CP1251)"
+ *: "Kiril (CP1251)"
</dest>
<voice>
- *: "Cyrillic"
+ *: "Kiril"
</voice>
</phrase>
<phrase>
@@ -5504,11 +5205,11 @@
</source>
<dest>
*: none
- crossfade: "Shuffle or Manual Track Skip"
+ crossfade: "Karışık veya Manuel Parça Atlama"
</dest>
<voice>
*: none
- crossfade: "Shuffle or Manual Track Skip"
+ crossfade: "Karışık veya Manuel Parça Atlama"
</voice>
</phrase>
<phrase>
@@ -5536,10 +5237,10 @@
*: "Max Playlist Size"
</source>
<dest>
- *: "Max Playlist Size"
+ *: "Maksimum Oynatma Listesi Boyutu"
</dest>
<voice>
- *: "Maximum playlist size"
+ *: "Maksimum Oynatma Listesi Boyutu"
</voice>
</phrase>
<phrase>
@@ -5552,11 +5253,11 @@
</source>
<dest>
*: none
- recording: "(Same As Source)"
+ recording: "(Kaynakla aynı)"
</dest>
<voice>
*: none
- recording: "Same As Source"
+ recording: "(Kaynakla aynı)"
</voice>
</phrase>
<phrase>
@@ -5567,10 +5268,10 @@
*: "Incompatible version"
</source>
<dest>
- *: "Incompatible version"
+ *: "Uyumsuz sürüm"
</dest>
<voice>
- *: ""
+ *: "Uyumsuz sürüm"
</voice>
</phrase>
<phrase>
@@ -5581,10 +5282,10 @@
*: "Discnum"
</source>
<dest>
- *: "Discnum"
+ *: "Disk numarası"
</dest>
<voice>
- *: ""
+ *: "Disk numarası"
</voice>
</phrase>
<phrase>
@@ -5595,10 +5296,10 @@
*: "Simp. Chinese (GB2312)"
</source>
<dest>
- *: "Simp. Chinese (GB2312)"
+ *: "Basitleştirilmiş Çince (GB2312)"
</dest>
<voice>
- *: "Simplified Chinese"
+ *: "Basitleştirilmiş Çince"
</voice>
</phrase>
<phrase>
@@ -5611,7 +5312,7 @@
</source>
<dest>
*: none
- lcd_color: "Invalid colour"
+ lcd_color: "Geçersiz renk"
</dest>
<voice>
*: none
@@ -5626,10 +5327,10 @@
*: "Bitrate"
</source>
<dest>
- *: "Bitrate"
+ *: "Bit hızı"
</dest>
<voice>
- *: ""
+ *: "Bit hızı"
</voice>
</phrase>
<phrase>
@@ -5642,11 +5343,11 @@
</source>
<dest>
*: none
- recording: "Once"
+ recording: "Bir Kere"
</dest>
<voice>
*: none
- recording: "Once"
+ recording: "Bir Kere"
</voice>
</phrase>
<phrase>
@@ -5657,10 +5358,10 @@
*: "Context Menu"
</source>
<dest>
- *: "Context Menu"
+ *: "Bağlam Menüsü"
</dest>
<voice>
- *: "Context Menu"
+ *: "Bağlam Menüsü"
</voice>
</phrase>
<phrase>
@@ -5671,10 +5372,10 @@
*: "Properties"
</source>
<dest>
- *: "Properties"
+ *: "Özellikler"
</dest>
<voice>
- *: "Properties"
+ *: "Özellikler"
</voice>
</phrase>
<phrase>
@@ -5685,10 +5386,10 @@
*: "Open With..."
</source>
<dest>
- *: "Open With..."
+ *: "Bununla aç..."
</dest>
<voice>
- *: "open with"
+ *: "Bununla aç..."
</voice>
</phrase>
<phrase>
@@ -5701,11 +5402,11 @@
</source>
<dest>
*: none
- touchscreen: "Touchscreen Mode"
+ touchscreen: "Dokunmatik Ekran Modu"
</dest>
<voice>
*: none
- touchscreen: "Touchscreen Mode"
+ touchscreen: "Dokunmatik Ekran Modu"
</voice>
</phrase>
<phrase>
@@ -5716,10 +5417,10 @@
*: "Ratio"
</source>
<dest>
- *: "Ratio"
+ *: "Oran"
</dest>
<voice>
- *: "Ratio"
+ *: "Oran"
</voice>
</phrase>
<phrase>
@@ -5744,10 +5445,10 @@
*: "Backlight Exemptions"
</source>
<dest>
- *: "Backlight Exemptions"
+ *: "Arka Işık Muafiyetleri"
</dest>
<voice>
- *: "Backlight Exemptions"
+ *: "Arka Işık Muafiyetleri"
</voice>
</phrase>
<phrase>
@@ -5772,10 +5473,10 @@
*: "Composer"
</source>
<dest>
- *: "Composer"
+ *: "Besteci"
</dest>
<voice>
- *: ""
+ *: "Besteci"
</voice>
</phrase>
<phrase>
@@ -5788,11 +5489,11 @@
</source>
<dest>
*: none
- remote_ticking: "Reduce Ticking"
+ remote_ticking: "Ä°ÅŸaretlemeyi Azalt"
</dest>
<voice>
*: none
- remote_ticking: "Reduce Ticking"
+ remote_ticking: "Ä°ÅŸaretlemeyi Azalt"
</voice>
</phrase>
<phrase>
@@ -5803,10 +5504,10 @@
*: "Top Time"
</source>
<dest>
- *: "Top Time"
+ *: "En Ä°yi Zaman"
</dest>
<voice>
- *: "Top Time"
+ *: "En Ä°yi Zaman"
</voice>
</phrase>
<phrase>
@@ -5819,11 +5520,11 @@
</source>
<dest>
*: none
- filter_roll_off: "DAC's filter roll-off"
+ filter_roll_off: "DAC'nin filtre roll-off'u"
</dest>
<voice>
*: none
- filter_roll_off: "DAC's filter roll-off"
+ filter_roll_off: "DAC'nin filtre roll-off'u"
</voice>
</phrase>
<phrase>
@@ -5848,7 +5549,7 @@
*: "Buttons Unlocked"
</source>
<dest>
- *: "Buttons Unlocked"
+ *: "Tuş Kilidi Açıldı"
</dest>
<voice>
*: ""
@@ -5862,10 +5563,10 @@
*: "Select one or more directories"
</source>
<dest>
- *: "Select one or more directories"
+ *: "Bir veya daha fazla dizin seçin"
</dest>
<voice>
- *: "Select one or more directories"
+ *: "Bir veya daha fazla dizin seçin"
</voice>
</phrase>
<phrase>
@@ -5878,11 +5579,11 @@
</source>
<dest>
*: none
- gigabeatfx,ipod6g,ipodvideo,mpiohd200,mpiohd300,mrobe100: "Treble Cutoff"
+ gigabeatfx,ipod6g,ipodvideo,mpiohd200,mpiohd300,mrobe100: "Tiz Kesme"
</dest>
<voice>
*: none
- gigabeatfx,ipod6g,ipodvideo,mpiohd200,mpiohd300,mrobe100: "Treble Cutoff"
+ gigabeatfx,ipod6g,ipodvideo,mpiohd200,mpiohd300,mrobe100: "Tiz Kesme"
</voice>
</phrase>
<phrase>
@@ -5893,10 +5594,10 @@
*: "Scroll Step Size"
</source>
<dest>
- *: "Scroll Step Size"
+ *: "Adım Boyutunu Kaydır"
</dest>
<voice>
- *: "Scroll Step Size"
+ *: "Adım Boyutunu Kaydır"
</voice>
</phrase>
<phrase>
@@ -5909,11 +5610,11 @@
</source>
<dest>
*: none
- recording: "Split Time"
+ recording: "Ayrık Zaman"
</dest>
<voice>
*: none
- recording: "Split Time"
+ recording: "Ayrık Zaman"
</voice>
</phrase>
<phrase>
@@ -5926,11 +5627,11 @@
</source>
<dest>
*: none
- quickscreen: "Set as Left Quickscreen Item"
+ quickscreen: "Sol Hızlı Ekran Öğesi Olarak Ayarla"
</dest>
<voice>
*: none
- quickscreen: "Set as Left Quickscreen Item"
+ quickscreen: "Sol Hızlı Ekran Öğesi Olarak Ayarla"
</voice>
</phrase>
<phrase>
@@ -5955,10 +5656,10 @@
*: "%d Hz Band Gain"
</source>
<dest>
- *: "%d Hz Band Gain"
+ *: "%d Hz Band Kazancı"
</dest>
<voice>
- *: "hertz band gain"
+ *: "%d Hz Band Kazancı"
</voice>
</phrase>
<phrase>
@@ -5977,16 +5678,16 @@
</phrase>
<phrase>
id: LANG_PLUGIN_WRONG_MODEL
- desc: The plugin is not compatible with the archos model trying to run it
+ desc: The plugin is not compatible with the player model trying to run it
user: core
<source>
*: "Incompatible model"
</source>
<dest>
- *: "Incompatible model"
+ *: "Uyumsuz model"
</dest>
<voice>
- *: ""
+ *: "Uyumsuz model"
</voice>
</phrase>
<phrase>
@@ -6014,10 +5715,10 @@
*: "No files"
</source>
<dest>
- *: "No files"
+ *: "Dosya yok"
</dest>
<voice>
- *: "No files"
+ *: "Dosya yok"
</voice>
</phrase>
<phrase>
@@ -6044,11 +5745,11 @@
</source>
<dest>
*: none
- radio: "Signal strength:"
+ radio: "Sinyal gücü:"
</dest>
<voice>
*: none
- radio: "Signal strength:"
+ radio: "Sinyal gücü:"
</voice>
</phrase>
<phrase>
@@ -6061,11 +5762,11 @@
</source>
<dest>
*: none
- radio: "Japan"
+ radio: "Japonya"
</dest>
<voice>
*: none
- radio: "Japan"
+ radio: "Japonya"
</voice>
</phrase>
<phrase>
@@ -6076,10 +5777,10 @@
*: "Moderate"
</source>
<dest>
- *: "Moderate"
+ *: "Makul"
</dest>
<voice>
- *: "Moderate"
+ *: "Makul"
</voice>
</phrase>
<phrase>
@@ -6107,7 +5808,7 @@
*: ""
</dest>
<voice>
- *: "kilohertz"
+ *: "kilo-hertz"
</voice>
</phrase>
<phrase>
@@ -6118,10 +5819,10 @@
*: "Save Theme Settings"
</source>
<dest>
- *: "Save Theme Settings"
+ *: "Tema Ayarlarını Kaydet"
</dest>
<voice>
- *: "Save Theme Settings"
+ *: "Tema Ayarlarını Kaydet"
</voice>
</phrase>
<phrase>
@@ -6134,11 +5835,11 @@
</source>
<dest>
*: none
- quickscreen: "Set as Bottom Quickscreen Item"
+ quickscreen: "Alt Hızlı Ekran Öğesi Olarak Ayarla"
</dest>
<voice>
*: none
- quickscreen: "Set as Bottom Quickscreen Item"
+ quickscreen: "Alt Hızlı Ekran Öğesi Olarak Ayarla"
</voice>
</phrase>
<phrase>
@@ -6163,10 +5864,10 @@
*: "Credits"
</source>
<dest>
- *: "Credits"
+ *: "Kredi"
</dest>
<voice>
- *: "Credits"
+ *: "Kredi"
</voice>
</phrase>
<phrase>
@@ -6179,11 +5880,11 @@
</source>
<dest>
*: none
- alarm: "Alarm Set"
+ alarm: "Alarm kuruldu"
</dest>
<voice>
*: none
- alarm: "Alarm set"
+ alarm: "Alarm kuruldu"
</voice>
</phrase>
<phrase>
@@ -6196,11 +5897,11 @@
</source>
<dest>
*: none
- pitchscreen: "Semitone"
+ pitchscreen: "Yarım ton"
</dest>
<voice>
*: none
- pitchscreen: "Semitone"
+ pitchscreen: "Yarım ton"
</voice>
</phrase>
<phrase>
@@ -6211,10 +5912,10 @@
*: "Database is not ready"
</source>
<dest>
- *: "Database is not ready"
+ *: "Veritabanı hazır değil"
</dest>
<voice>
- *: "Database is not ready"
+ *: "Veritabanı hazır değil"
</voice>
</phrase>
<phrase>
@@ -6242,10 +5943,10 @@
*: "Clear Time?"
</source>
<dest>
- *: "Clear Time?"
+ *: "Süreyi Sıfırla?"
</dest>
<voice>
- *: "Clear Time?"
+ *: "Süreyi Sıfırla?"
</voice>
</phrase>
<phrase>
@@ -6284,10 +5985,10 @@
*: "Show Path"
</source>
<dest>
- *: "Show Path"
+ *: "Yolu Göster"
</dest>
<voice>
- *: "Show Path"
+ *: "Yolu Göster"
</voice>
</phrase>
<phrase>
@@ -6301,7 +6002,7 @@
*: ""
</dest>
<voice>
- *: "billion"
+ *: "milyar"
</voice>
</phrase>
<phrase>
@@ -6314,11 +6015,11 @@
</source>
<dest>
*: none
- lcd_color: "Secondary Colour"
+ lcd_color: "Ä°kincil Renk"
</dest>
<voice>
*: none
- lcd_color: "Secondary Colour"
+ lcd_color: "Ä°kincil Renk"
</voice>
</phrase>
<phrase>
@@ -6329,10 +6030,10 @@
*: "Current Playlist"
</source>
<dest>
- *: "Current Playlist"
+ *: "Geçerli Oynatma Listesi"
</dest>
<voice>
- *: "Current Playlist"
+ *: "Geçerli Oynatma Listesi"
</voice>
</phrase>
<phrase>
@@ -6346,7 +6047,7 @@
*: ""
</dest>
<voice>
- *: "minus"
+ *: "Eksi"
</voice>
</phrase>
<phrase>
@@ -6357,10 +6058,10 @@
*: "Maximum Volume Limit"
</source>
<dest>
- *: "Maximum Volume Limit"
+ *: "Maksimum Ses Sınırı"
</dest>
<voice>
- *: "Maximum Volume Limit"
+ *: "Maksimum Ses Sınırı"
</voice>
</phrase>
<phrase>
@@ -6374,7 +6075,7 @@
*: ""
</dest>
<voice>
- *: "cuesheet"
+ *: "işaret levhası"
</voice>
</phrase>
<phrase>
@@ -6385,10 +6086,10 @@
*: "Central European (CP1250)"
</source>
<dest>
- *: "Central European (CP1250)"
+ *: "Orta Avrupa (CP1250)"
</dest>
<voice>
- *: "Central European"
+ *: "Orta Avrupa"
</voice>
</phrase>
<phrase>
@@ -6399,27 +6100,10 @@
*: "Copying..."
</source>
<dest>
- *: "Copying..."
- </dest>
- <voice>
- *: "Copying"
- </voice>
-</phrase>
-<phrase>
- id: LANG_FM_BUTTONBAR_ADD
- desc: in radio screen
- user: core
- <source>
- *: none
- radio_screen_button_bar: "Add"
- </source>
- <dest>
- *: none
- radio_screen_button_bar: "Add"
+ *: "Kopyalanıyor..."
</dest>
<voice>
- *: none
- radio_screen_button_bar: ""
+ *: "Kopyalanıyor..."
</voice>
</phrase>
<phrase>
@@ -6432,11 +6116,11 @@
</source>
<dest>
*: none
- recording: "File Split Options"
+ recording: "Dosya Bölme Seçenekleri"
</dest>
<voice>
*: none
- recording: "File Split Options"
+ recording: "Dosya Bölme Seçenekleri"
</voice>
</phrase>
<phrase>
@@ -6461,27 +6145,10 @@
*: "Max Entries in File Browser"
</source>
<dest>
- *: "Max Entries in File Browser"
- </dest>
- <voice>
- *: "Maximum entries in file browser"
- </voice>
-</phrase>
-<phrase>
- id: LANG_FM_BUTTONBAR_RECORD
- desc: in radio screen
- user: core
- <source>
- *: none
- radio_screen_button_bar: "Record"
- </source>
- <dest>
- *: none
- radio_screen_button_bar: "Record"
+ *: "Dosya Tarayıcıda Maksimum Giriş"
</dest>
<voice>
- *: none
- radio_screen_button_bar: ""
+ *: "Dosya Tarayıcıda Maksimum Giriş"
</voice>
</phrase>
<phrase>
@@ -6494,11 +6161,11 @@
</source>
<dest>
*: none
- lcd_color: "Reset Colours"
+ lcd_color: "Renkleri Sıfırla"
</dest>
<voice>
*: none
- lcd_color: "Reset Colours"
+ lcd_color: "Renkleri Sıfırla"
</voice>
</phrase>
<phrase>
@@ -6509,10 +6176,10 @@
*: "Hard Knee"
</source>
<dest>
- *: "Hard Knee"
+ *: "Sert Knee"
</dest>
<voice>
- *: "Hard Knee"
+ *: "Sert Knee"
</voice>
</phrase>
<phrase>
@@ -6525,11 +6192,11 @@
</source>
<dest>
*: none
- recording: "Trigtype"
+ recording: "tetik tipi"
</dest>
<voice>
*: none
- recording: "Trigger type"
+ recording: "tetik tipi"
</voice>
</phrase>
<phrase>
@@ -6556,11 +6223,11 @@
</source>
<dest>
*: none
- lcd_color: "Bar (Solid Colour)"
+ lcd_color: "Bar (Katı Renk)"
</dest>
<voice>
*: none
- lcd_color: "Bar (Solid Colour)"
+ lcd_color: "Bar (Katı Renk)"
</voice>
</phrase>
<phrase>
@@ -6574,7 +6241,7 @@
*: ""
</dest>
<voice>
- *: "keyboard"
+ *: "Tuş Takımı"
</voice>
</phrase>
<phrase>
@@ -6587,11 +6254,11 @@
</source>
<dest>
*: none
- touchscreen: "3x3 Grid"
+ touchscreen: "3x3 Izgara"
</dest>
<voice>
*: none
- touchscreen: "3 by 3 grid"
+ touchscreen: "3x3 Izgara"
</voice>
</phrase>
<phrase>
@@ -6616,27 +6283,10 @@
*: "First Buttonpress Enables Backlight Only"
</source>
<dest>
- *: "First Buttonpress Enables Backlight Only"
- </dest>
- <voice>
- *: "First Buttonpress Enables Backlight Only"
- </voice>
-</phrase>
-<phrase>
- id: LANG_CONFIRM_SHUTDOWN
- desc: in shutdown screen
- user: core
- <source>
- *: none
- soft_shutdown: "Press OFF to shut down"
- </source>
- <dest>
- *: none
- soft_shutdown: "Press OFF to shut down"
+ *: "Tuşa İlk Basımada Yalnızca Arka Işığı Etkinleştirir"
</dest>
<voice>
- *: none
- soft_shutdown: ""
+ *: "Tuşa İlk Basımada Yalnızca Arka Işığı Etkinleştirir"
</voice>
</phrase>
<phrase>
@@ -6649,11 +6299,11 @@
</source>
<dest>
*: none
- alarm: "Waking Up In %d:%02d"
+ alarm: "süre içinde uyanma %d:%02d"
</dest>
<voice>
*: none
- alarm: "Waking up in"
+ alarm: "içinde uyanmak"
</voice>
</phrase>
<phrase>
@@ -6680,11 +6330,11 @@
</source>
<dest>
*: none
- headphone_detection: "Pause on Headphone Unplug"
+ headphone_detection: "Kulaklık Çıkarıldığında Duraklat"
</dest>
<voice>
*: none
- headphone_detection: "Pause on Headphone Unplug"
+ headphone_detection: "Kulaklık Çıkarıldığında Duraklat"
</voice>
</phrase>
<phrase>
@@ -6695,10 +6345,10 @@
*: "Paged Scrolling"
</source>
<dest>
- *: "Paged Scrolling"
+ *: "Sayfalı Kaydırma"
</dest>
<voice>
- *: "Paged scrolling"
+ *: "Sayfalı Kaydırma"
</voice>
</phrase>
<phrase>
@@ -6726,7 +6376,7 @@
*: "Uzunluk"
</dest>
<voice>
- *: ""
+ *: "Uzunluk"
</voice>
</phrase>
<phrase>
@@ -6739,11 +6389,11 @@
</source>
<dest>
*: none
- recording: "Can't write to recording directory"
+ recording: "Kayıt dizinine yazılamıyor"
</dest>
<voice>
*: none
- recording: "Can't write to recording directory"
+ recording: "Kayıt dizinine yazılamıyor"
</voice>
</phrase>
<phrase>
@@ -6782,10 +6432,10 @@
*: "Knee"
</source>
<dest>
- *: "Knee"
+ *: "Knee (Diz?)"
</dest>
<voice>
- *: "Knee"
+ *: "Knee (Diz?)"
</voice>
</phrase>
<phrase>
@@ -6799,7 +6449,7 @@
*: ""
</dest>
<voice>
- *: "hundred"
+ *: "Yüz"
</voice>
</phrase>
<phrase>
@@ -6810,10 +6460,10 @@
*: "Queue Last"
</source>
<dest>
- *: "Queue Last"
+ *: "Kuyruk Sonu"
</dest>
<voice>
- *: "Queue Last"
+ *: "Kuyruk Sonu"
</voice>
</phrase>
<phrase>
@@ -6824,7 +6474,7 @@
*: "Timestretch"
</source>
<dest>
- *: "Timestretch"
+ *: "zaman aralığı"
</dest>
<voice>
*: "Timestretch"
@@ -6838,10 +6488,10 @@
*: "Track Skip Beep"
</source>
<dest>
- *: "Track Skip Beep"
+ *: "Parça Atlamada Bip Sesi"
</dest>
<voice>
- *: "Track Skip Beep"
+ *: "Parça Atlamada Bip Sesi"
</voice>
</phrase>
<phrase>
@@ -6854,7 +6504,7 @@
</source>
<dest>
*: none
- recording: "Pre-Recording"
+ recording: "Ön Kayıt"
</dest>
<voice>
*: none
@@ -6869,10 +6519,10 @@
*: "Perceptual Bass Enhancement"
</source>
<dest>
- *: "Perceptual Bass Enhancement"
+ *: "Algısal Bas Geliştirme"
</dest>
<voice>
- *: "Perceptual Bass Enhancement"
+ *: "Algısal Bas Geliştirme"
</voice>
</phrase>
<phrase>
@@ -6897,10 +6547,10 @@
*: "Latin1 (ISO-8859-1)"
</source>
<dest>
- *: "Latin1 (ISO-8859-1)"
+ *: "Latince1 (ISO-8859-1)"
</dest>
<voice>
- *: "Latin 1"
+ *: "Latince 1"
</voice>
</phrase>
<phrase>
@@ -6913,11 +6563,11 @@
</source>
<dest>
*: none
- touchscreen: "Reset Calibration"
+ touchscreen: "Kalibrasyonu Sıfırla"
</dest>
<voice>
*: none
- touchscreen: "Reset Calibration"
+ touchscreen: "Kalibrasyonu Sıfırla"
</voice>
</phrase>
<phrase>
@@ -6928,10 +6578,10 @@
*: "f(x1)"
</source>
<dest>
- *: "f(x1)"
+ *: "Surround f(x1)"
</dest>
<voice>
- *: "f(x1)"
+ *: "Surround f(x1)"
</voice>
</phrase>
<phrase>
@@ -6945,7 +6595,7 @@
*: ""
</dest>
<voice>
- *: "font"
+ *: "Yazı Tipi"
</voice>
</phrase>
<phrase>
@@ -6958,11 +6608,11 @@
</source>
<dest>
*: none
- crossfade: "Fade-Out Delay"
+ crossfade: "Kararma Gecikmesi"
</dest>
<voice>
*: none
- crossfade: "Fade-Out Delay"
+ crossfade: "Kararma Gecikmesi"
</voice>
</phrase>
<phrase>
@@ -6976,7 +6626,7 @@
*: ""
</dest>
<voice>
- *: "seconds"
+ *: "saniye"
</voice>
</phrase>
<phrase>
@@ -6989,11 +6639,11 @@
</source>
<dest>
*: none
- speaker: "Enable Speaker"
+ speaker: "Hoparlörü Etkinleştir"
</dest>
<voice>
*: none
- speaker: "Enable Speaker"
+ speaker: "Hoparlörü Etkinleştir"
</voice>
</phrase>
<phrase>
@@ -7007,7 +6657,7 @@
*: ""
</dest>
<voice>
- *: "minute"
+ *: "Dakika"
</voice>
</phrase>
<phrase>
@@ -7018,7 +6668,7 @@
*: "Disk:"
</source>
<dest>
- *: "Disk:"
+ *: "Disk Boyutu:"
</dest>
<voice>
*: "Disk boyutu"
@@ -7034,7 +6684,7 @@
</source>
<dest>
*: none
- recording: "Size:"
+ recording: "Boyut:"
</dest>
<voice>
*: none
@@ -7052,7 +6702,7 @@
*: " (VBR)"
</dest>
<voice>
- *: ""
+ *: "VBR"
</voice>
</phrase>
<phrase>
@@ -7063,10 +6713,10 @@
*: "Limit"
</source>
<dest>
- *: "Limit"
+ *: "Sınır"
</dest>
<voice>
- *: "Limit"
+ *: "Sınır"
</voice>
</phrase>
<phrase>
@@ -7077,10 +6727,10 @@
*: "Start File Browser at /"
</source>
<dest>
- *: "Start File Browser at /"
+ *: "Dosya Tarayıcı konumunda başlatın "
</dest>
<voice>
- *: "Start File Browser at root"
+ *: "Dosya Tarayıcı konumunda başlatın "
</voice>
</phrase>
<phrase>
@@ -7093,11 +6743,11 @@
</source>
<dest>
*: none
- dircache: "Directory Cache"
+ dircache: "Dizin Önbelleği"
</dest>
<voice>
*: none
- dircache: "Directory Cache"
+ dircache: "Dizin Önbelleği"
</voice>
</phrase>
<phrase>
@@ -7110,28 +6760,11 @@
</source>
<dest>
*: none
- alarm: "Alarm Time Is Too Soon!"
- </dest>
- <voice>
- *: none
- alarm: "Alarm Time Is Too Soon!"
- </voice>
-</phrase>
-<phrase>
- id: LANG_SYSFONT_RECORDING_SRC_MIC
- desc: in the recording settings
- user: core
- <source>
- *: none
- recording: "Int. Mic"
- </source>
- <dest>
- *: none
- recording: "Dhl. Mik"
+ alarm: "Alarm Zamanı Çok Yakında!"
</dest>
<voice>
*: none
- recording: "Dahili Mikrofon"
+ alarm: "Alarm Zamanı Çok Yakında!"
</voice>
</phrase>
<phrase>
@@ -7142,10 +6775,10 @@
*: "Track Gain"
</source>
<dest>
- *: "Track Gain"
+ *: "Kazanç Takibi"
</dest>
<voice>
- *: "Track Gain"
+ *: "Kazanç Takibi"
</voice>
</phrase>
<phrase>
@@ -7172,11 +6805,11 @@
</source>
<dest>
*: none
- recording: "Prerecord Time"
+ recording: "Ön Kayıt Süresi"
</dest>
<voice>
*: none
- recording: "Pre-Record time"
+ recording: "Ön Kayıt Süresi"
</voice>
</phrase>
<phrase>
@@ -7187,10 +6820,10 @@
*: "Ask - Recent only"
</source>
<dest>
- *: "Ask - Recent only"
+ *: "Sor - Yalnızca son zamanlarda"
</dest>
<voice>
- *: "Ask - Recent only"
+ *: "Sor - Yalnızca son zamanlarda"
</voice>
</phrase>
<phrase>
@@ -7203,11 +6836,11 @@
</source>
<dest>
*: none
- gigabeatfx: "High"
+ gigabeatfx: "Yüksek"
</dest>
<voice>
*: none
- gigabeatfx: "High"
+ gigabeatfx: "Yüksek"
</voice>
</phrase>
<phrase>
@@ -7232,10 +6865,10 @@
*: "Battery"
</source>
<dest>
- *: "Battery"
+ *: "Pil"
</dest>
<voice>
- *: "Battery"
+ *: "Pil"
</voice>
</phrase>
<phrase>
@@ -7246,10 +6879,10 @@
*: "Enabled"
</source>
<dest>
- *: "Enabled"
+ *: "Etkin"
</dest>
<voice>
- *: "Enabled"
+ *: "Etkin"
</voice>
</phrase>
<phrase>
@@ -7263,7 +6896,7 @@
*: ""
</dest>
<voice>
- *: "pixel"
+ *: "piksel"
</voice>
</phrase>
<phrase>
@@ -7276,11 +6909,11 @@
</source>
<dest>
*: none
- radio: "FM Radio Menu"
+ radio: "FM Radio Menüsü"
</dest>
<voice>
*: none
- radio: "FM Radio Menu"
+ radio: "FM Radio Menüsü"
</voice>
</phrase>
<phrase>
@@ -7307,11 +6940,11 @@
</source>
<dest>
*: none
- multidrive_usb: "USB Hide Internal Drive"
+ multidrive_usb: "USB Dahili Sürücüyü Gizle"
</dest>
<voice>
*: none
- multidrive_usb: "USB Hide Internal Drive"
+ multidrive_usb: "USB Dahili Sürücüyü Gizle"
</voice>
</phrase>
<phrase>
@@ -7324,11 +6957,11 @@
</source>
<dest>
*: none
- pitchscreen: "Pitch"
+ pitchscreen: "Pitch (Aralık)"
</dest>
<voice>
*: none
- pitchscreen: "Pitch"
+ pitchscreen: "Pitch (Aralık)"
</voice>
</phrase>
<phrase>
@@ -7339,10 +6972,10 @@
*: "Show Track Info"
</source>
<dest>
- *: "Show Track Info"
+ *: "Parça Bilgisini Göster"
</dest>
<voice>
- *: "Show Track Info"
+ *: "Parça Bilgisini Göster"
</voice>
</phrase>
<phrase>
@@ -7355,11 +6988,11 @@
</source>
<dest>
*: none
- recording: "Source"
+ recording: "Kaynak"
</dest>
<voice>
*: none
- recording: "Source"
+ recording: "Kaynak"
</voice>
</phrase>
<phrase>
@@ -7370,10 +7003,10 @@
*: "Updating in background"
</source>
<dest>
- *: "Updating in background"
+ *: "Arka planda güncelle"
</dest>
<voice>
- *: "Updating in background"
+ *: "Arka planda güncelle"
</voice>
</phrase>
<phrase>
@@ -7384,10 +7017,10 @@
*: "Playlist control file is invalid"
</source>
<dest>
- *: "Playlist control file is invalid"
+ *: "Oynatma listesi kontrol dosyası geçersiz"
</dest>
<voice>
- *: "Playlist control file is invalid"
+ *: "Oynatma listesi kontrol dosyası geçersiz"
</voice>
</phrase>
<phrase>
@@ -7398,10 +7031,10 @@
*: "Rewind on Pause"
</source>
<dest>
- *: "Rewind on Pause"
+ *: "Duraklatıldığında Geri Sar"
</dest>
<voice>
- *: "Rewind on Pause"
+ *: "Duraklatıldığında Geri Sar"
</voice>
</phrase>
<phrase>
@@ -7412,10 +7045,10 @@
*: "Right"
</source>
<dest>
- *: "Right"
+ *: "SaÄŸ"
</dest>
<voice>
- *: "Right"
+ *: "SaÄŸ"
</voice>
</phrase>
<phrase>
@@ -7429,24 +7062,7 @@
*: ""
</dest>
<voice>
- *: "firmware"
- </voice>
-</phrase>
-<phrase>
- id: LANG_SYSFONT_RECORDING_SRC_DIGITAL
- desc: in the recording settings
- user: core
- <source>
- *: none
- recording: "Digital"
- </source>
- <dest>
- *: none
- recording: "Digital"
- </dest>
- <voice>
- *: none
- recording: "Digital"
+ *: "Bllenim-firmware"
</voice>
</phrase>
<phrase>
@@ -7457,10 +7073,10 @@
*: "Start Sleep Timer"
</source>
<dest>
- *: "Start Sleep Timer"
+ *: "Uyku Zamanlayıcısını Başlat"
</dest>
<voice>
- *: "Start Sleep Timer"
+ *: "Uyku Zamanlayıcısını Başlat"
</voice>
</phrase>
<phrase>
@@ -7483,14 +7099,12 @@
user: core
<source>
*: "End of Song List"
- archosplayer: "End of List"
</source>
<dest>
- *: "End of Song List"
- archosplayer: "End of List"
+ *: "Şarkı Listesinin Sonu"
</dest>
<voice>
- *: "End of Song List"
+ *: "Şarkı Listesinin Sonu"
</voice>
</phrase>
<phrase>
@@ -7501,10 +7115,10 @@
*: "Save Current Playlist"
</source>
<dest>
- *: "Save Current Playlist"
+ *: "Geçerli Oynatma Listesini Kaydet"
</dest>
<voice>
- *: "Save Current Playlist"
+ *: "Geçerli Oynatma Listesini Kaydet"
</voice>
</phrase>
<phrase>
@@ -7517,11 +7131,11 @@
</source>
<dest>
*: none
- sansafuzeplus: "Touchpad Dead Zone"
+ sansafuzeplus: "Dokunmatik Yüzey Ölü Bölge"
</dest>
<voice>
*: none
- sansafuzeplus: "Touchpad Dead Zone"
+ sansafuzeplus: "Dokunmatik Yüzey Ölü Bölge"
</voice>
</phrase>
<phrase>
@@ -7532,10 +7146,10 @@
*: "By Newest Date"
</source>
<dest>
- *: "By Newest Date"
+ *: "Tarihe Göre-En Yeni"
</dest>
<voice>
- *: "By Newest Date"
+ *: "Tarihe Göre-En Yeni"
</voice>
</phrase>
<phrase>
@@ -7546,10 +7160,10 @@
*: "Disable Unmapped Keys"
</source>
<dest>
- *: "Disable Unmapped Keys"
+ *: "Eşlenmemiş Anahtarları Devre Dışı Bırak"
</dest>
<voice>
- *: "Disable Unmapped Keys"
+ *: "Eşlenmemiş Anahtarları Devre Dışı Bırak"
</voice>
</phrase>
<phrase>
@@ -7562,11 +7176,11 @@
</source>
<dest>
*: none
- quickscreen: "Use Shortcuts Menu Instead of Quick Screen"
+ quickscreen: "Hızlı Ekran Yerine Kısayollar Menüsünü Kullanın"
</dest>
<voice>
*: none
- quickscreen: "Use Shortcuts Menu Instead of Quick Screen"
+ quickscreen: "Hızlı Ekran Yerine Kısayollar Menüsünü Kullanın"
</voice>
</phrase>
<phrase>
@@ -7579,11 +7193,11 @@
</source>
<dest>
*: none
- remote: "Remote-LCD Settings"
+ remote: "Ekran Ayarlarını Kontrol Et"
</dest>
<voice>
*: none
- remote: "Remote LCD settings"
+ remote: "Ekran Ayarlarını Kontrol Et"
</voice>
</phrase>
<phrase>
@@ -7596,11 +7210,11 @@
</source>
<dest>
*: none
- radio: "Preset"
+ radio: "Ön Ayar"
</dest>
<voice>
*: none
- radio: "Preset"
+ radio: "Ön Ayar"
</voice>
</phrase>
<phrase>
@@ -7613,11 +7227,11 @@
</source>
<dest>
*: none
- recording: "Recording Settings"
+ recording: "Kayıt Ayarları"
</dest>
<voice>
*: none
- recording: "Recording Settings"
+ recording: "Kayıt Ayarları"
</voice>
</phrase>
<phrase>
@@ -7628,10 +7242,10 @@
*: "Maintain a List of Recent Bookmarks?"
</source>
<dest>
- *: "Maintain a List of Recent Bookmarks?"
+ *: "Son Yer Ä°mlerinin Bir Listesi Korunsun mu?"
</dest>
<voice>
- *: "Maintain a List of Recent Bookmarks?"
+ *: "Son Yer Ä°mlerinin Bir Listesi Korunsun mu?"
</voice>
</phrase>
<phrase>
@@ -7644,11 +7258,11 @@
</source>
<dest>
*: none
- headphone_detection: "Disable resume on startup if phones unplugged"
+ headphone_detection: "Kulaklık takılı değilse başlangıçta devam etmeyi devre dışı bırakın"
</dest>
<voice>
*: none
- headphone_detection: "Disable resume on startup if phones unplugged"
+ headphone_detection: "Kulaklık takılı değilse başlangıçta devam etmeyi devre dışı bırakın"
</voice>
</phrase>
<phrase>
@@ -7678,11 +7292,11 @@
</source>
<dest>
*: none
- recording: "Set As Recording Directory"
+ recording: "Kayıt Dizini Olarak Ayarla"
</dest>
<voice>
*: none
- recording: "Set As Recording Directory"
+ recording: "Kayıt Dizini Olarak Ayarla"
</voice>
</phrase>
<phrase>
@@ -7693,10 +7307,10 @@
*: "Moving..."
</source>
<dest>
- *: "Moving..."
+ *: "Hareketli..."
</dest>
<voice>
- *: "Moving"
+ *: "Hareketli..."
</voice>
</phrase>
<phrase>
@@ -7709,11 +7323,11 @@
</source>
<dest>
*: none
- radio: "Load Preset List"
+ radio: "Ön Ayar Listesini Yükle"
</dest>
<voice>
*: none
- radio: "Load Preset List"
+ radio: "Ön Ayar Listesini Yükle"
</voice>
</phrase>
<phrase>
@@ -7726,28 +7340,11 @@
</source>
<dest>
*: none
- hold_button: "Backlight on Hold"
+ hold_button: "Beklemede Arka Işık"
</dest>
<voice>
*: none
- hold_button: "Backlight on hold"
- </voice>
-</phrase>
-<phrase>
- id: LANG_RECORDING_EDITABLE
- desc: Editable recordings setting
- user: core
- <source>
- *: none
- recording_hwcodec: "Independent Frames"
- </source>
- <dest>
- *: none
- recording_hwcodec: "Independent Frames"
- </dest>
- <voice>
- *: none
- recording_hwcodec: "Independent Frames"
+ hold_button: "Beklemede Arka Işık"
</voice>
</phrase>
<phrase>
@@ -7760,11 +7357,11 @@
</source>
<dest>
*: none
- filter_roll_off: "Sharp"
+ filter_roll_off: "Keskin"
</dest>
<voice>
*: none
- filter_roll_off: "Sharp"
+ filter_roll_off: "Keskin"
</voice>
</phrase>
<phrase>
@@ -7775,10 +7372,10 @@
*: "Auto Update"
</source>
<dest>
- *: "Auto Update"
+ *: "Otomatik güncelleme"
</dest>
<voice>
- *: "Auto Update"
+ *: "Otomatik güncelleme"
</voice>
</phrase>
<phrase>
@@ -7789,10 +7386,10 @@
*: "In custom directories only"
</source>
<dest>
- *: "In custom directories only"
+ *: "Yalnızca özel dizinlerde"
</dest>
<voice>
- *: "In custom directories only"
+ *: "Yalnızca özel dizinlerde"
</voice>
</phrase>
<phrase>
@@ -7803,10 +7400,10 @@
*: "View"
</source>
<dest>
- *: "View"
+ *: "Görüntüle"
</dest>
<voice>
- *: "View"
+ *: "Görüntüle"
</voice>
</phrase>
<phrase>
@@ -7817,10 +7414,10 @@
*: "Copy"
</source>
<dest>
- *: "Copy"
+ *: "Kopyala"
</dest>
<voice>
- *: "Copy"
+ *: "Kopyala"
</voice>
</phrase>
<phrase>
@@ -7833,11 +7430,11 @@
</source>
<dest>
*: none
- touchscreen: "Touchscreen Settings"
+ touchscreen: "Dokunmatik Ekran Ayarları"
</dest>
<voice>
*: none
- touchscreen: "Touchscreen Settings"
+ touchscreen: "Dokunmatik Ekran Ayarları"
</voice>
</phrase>
<phrase>
@@ -7848,10 +7445,10 @@
*: "Create Directory"
</source>
<dest>
- *: "Create Directory"
+ *: "Dizin OluÅŸtur"
</dest>
<voice>
- *: "Create Directory"
+ *: "Dizin OluÅŸtur"
</voice>
</phrase>
<phrase>
@@ -7865,24 +7462,7 @@
*: ""
</dest>
<voice>
- *: "percent"
- </voice>
-</phrase>
-<phrase>
- id: LANG_SYSFONT_RECORDING_QUALITY
- desc: in the recording settings
- user: core
- <source>
- *: none
- recording_hwcodec: "Quality"
- </source>
- <dest>
- *: none
- recording_hwcodec: "Quality"
- </dest>
- <voice>
- *: none
- recording_hwcodec: "Quality"
+ *: "yüzde"
</voice>
</phrase>
<phrase>
@@ -7895,11 +7475,11 @@
</source>
<dest>
*: none
- radio: "Korea"
+ radio: "Kore"
</dest>
<voice>
*: none
- radio: "Korea"
+ radio: "Kore"
</voice>
</phrase>
<phrase>
@@ -7910,27 +7490,10 @@
*: "Western European (CP1252)"
</source>
<dest>
- *: "Western European (CP1252)"
+ *: "Batı Avrupa (CP1252)"
</dest>
<voice>
- *: "Western European"
- </voice>
-</phrase>
-<phrase>
- id: LANG_SYSFONT_DIRBROWSE_F2
- desc: in dir browser, F2 button bar text
- user: core
- <source>
- *: none
- recorder_pad: "Option"
- </source>
- <dest>
- *: none
- recorder_pad: "Option"
- </dest>
- <voice>
- *: none
- recorder_pad: ""
+ *: "Batı Avrupa"
</voice>
</phrase>
<phrase>
@@ -7955,10 +7518,10 @@
*: "Queue Next"
</source>
<dest>
- *: "Queue Next"
+ *: "Sonraki"
</dest>
<voice>
- *: "Queue Next"
+ *: "Sonraki"
</voice>
</phrase>
<phrase>
@@ -7971,11 +7534,11 @@
</source>
<dest>
*: none
- remote: "(Vol- : Re-enable)"
+ remote: "(Vol- : Yeniden etkinleÅŸtir)"
</dest>
<voice>
*: none
- remote: "(Vol- : Re-enable)"
+ remote: "(Vol- : Yeniden etkinleÅŸtir)"
</voice>
</phrase>
<phrase>
@@ -8002,11 +7565,11 @@
</source>
<dest>
*: none
- spdif_power: "Optical Output"
+ spdif_power: "Optik Çıkış"
</dest>
<voice>
*: none
- spdif_power: "Optical Output"
+ spdif_power: "Optik Çıkış"
</voice>
</phrase>
<phrase>
@@ -8031,10 +7594,10 @@
*: "Crossfeed"
</source>
<dest>
- *: "Crossfeed"
+ *: "Çapraz besleme"
</dest>
<voice>
- *: "Crossfeed"
+ *: "Çapraz besleme"
</voice>
</phrase>
<phrase>
@@ -8045,10 +7608,10 @@
*: "Nothing to resume"
</source>
<dest>
- *: "Nothing to resume"
+ *: "Devam edecek bir ÅŸey yok"
</dest>
<voice>
- *: "Nothing to resume"
+ *: "Devam edecek bir ÅŸey yok"
</voice>
</phrase>
<phrase>
@@ -8059,10 +7622,10 @@
*: "Insert Next"
</source>
<dest>
- *: "Insert Next"
+ *: "Sonrakini Ekle"
</dest>
<voice>
- *: "Insert Next"
+ *: "Sonrakini Ekle"
</voice>
</phrase>
<phrase>
@@ -8073,10 +7636,10 @@
*: "Numbers"
</source>
<dest>
- *: "Numbers"
+ *: "Sayılar"
</dest>
<voice>
- *: "Numbers"
+ *: "Sayılar"
</voice>
</phrase>
<phrase>
@@ -8089,11 +7652,11 @@
</source>
<dest>
*: none
- agc: "Voice (fast)"
+ agc: "Ses (hızlı)"
</dest>
<voice>
*: none
- agc: "Voice (fast)"
+ agc: "Ses (hızlı)"
</voice>
</phrase>
<phrase>
@@ -8105,11 +7668,11 @@
wheel_acceleration: none
</source>
<dest>
- *: "List Acceleration Start Delay"
+ *: "Liste Hızlandırma Başlatma Gecikmesi"
wheel_acceleration: none
</dest>
<voice>
- *: "List Acceleration Start Delay"
+ *: "Liste Hızlandırma Başlatma Gecikmesi"
wheel_acceleration: none
</voice>
</phrase>
@@ -8123,11 +7686,11 @@
</source>
<dest>
*: none
- radio: "Italy"
+ radio: "Italya"
</dest>
<voice>
*: none
- radio: "Italy"
+ radio: "Italya"
</voice>
</phrase>
<phrase>
@@ -8149,16 +7712,16 @@
</phrase>
<phrase>
id: LANG_REPLACE
- desc: in onplay menu. Replace the current playlist with a new one.
+ desc: deprecated
user: core
<source>
- *: "Play Next"
+ *: ""
</source>
<dest>
- *: "Play Next"
+ *: ""
</dest>
<voice>
- *: "Play Next"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -8169,10 +7732,10 @@
*: "Makeup Gain"
</source>
<dest>
- *: "Makeup Gain"
+ *: "Makeup Kazanç"
</dest>
<voice>
- *: "Makeup Gain"
+ *: "Makeup Kazanç"
</voice>
</phrase>
<phrase>
@@ -8183,10 +7746,10 @@
*: "Playlist Catalogue"
</source>
<dest>
- *: "Playlist Catalogue"
+ *: "Oynatma Listesi KataloÄŸu"
</dest>
<voice>
- *: "Playlist Catalogue"
+ *: "Oynatma Listesi KataloÄŸu"
</voice>
</phrase>
<phrase>
@@ -8199,11 +7762,11 @@
</source>
<dest>
*: none
- recording: "What to do when Splitting"
+ recording: "Bölme yaparken ne yapmalı"
</dest>
<voice>
*: none
- recording: "What to do when Splitting"
+ recording: "Bölme yaparken ne yapmalı"
</voice>
</phrase>
<phrase>
@@ -8216,25 +7779,25 @@
</source>
<dest>
*: none
- pitchscreen: "Limit"
+ pitchscreen: "sınır"
</dest>
<voice>
*: none
- pitchscreen: "Limit"
+ pitchscreen: "sınır"
</voice>
</phrase>
<phrase>
id: LANG_DITHERING
- desc: in the sound settings menu
+ desc: in the sound settings and some other menus
user: core
<source>
*: "Dithering"
</source>
<dest>
- *: "Dithering"
+ *: "Duraksama"
</dest>
<voice>
- *: "Dithering"
+ *: "Duraksama"
</voice>
</phrase>
<phrase>
@@ -8247,11 +7810,11 @@
</source>
<dest>
*: none
- radio: "Scan"
+ radio: "Tara"
</dest>
<voice>
*: none
- radio: "Scan"
+ radio: "Tara"
</voice>
</phrase>
<phrase>
@@ -8259,7 +7822,7 @@
desc: Selective Actions
user: core
<source>
- *: "Seek"
+ *: "Exempt Seek"
</source>
<dest>
*: "Ara"
@@ -8278,11 +7841,11 @@
</source>
<dest>
*: none
- recording: "Digital"
+ recording: "Dijital"
</dest>
<voice>
*: none
- recording: "Digital"
+ recording: "Dijital"
</voice>
</phrase>
<phrase>
@@ -8307,10 +7870,10 @@
*: "Import Modifications"
</source>
<dest>
- *: "Import Modifications"
+ *: "Değişiklikleri İçe Aktar"
</dest>
<voice>
- *: "Import Modifications"
+ *: "Değişiklikleri İçe Aktar"
</voice>
</phrase>
<phrase>
@@ -8323,11 +7886,11 @@
</source>
<dest>
*: none
- lcd_non-mono: "Clear Backdrop"
+ lcd_non-mono: "Backdrop Zemini Temizle"
</dest>
<voice>
*: none
- lcd_non-mono: "Clear Backdrop"
+ lcd_non-mono: "Backdrop Zemini Temizle"
</voice>
</phrase>
<phrase>
@@ -8340,11 +7903,11 @@
</source>
<dest>
*: none
- touchscreen: "Cancel"
+ touchscreen: "Ä°ptal"
</dest>
<voice>
*: none
- touchscreen: "Cancel"
+ touchscreen: "Ä°ptal"
</voice>
</phrase>
<phrase>
@@ -8355,10 +7918,10 @@
*: "Replaygain Type"
</source>
<dest>
- *: "Replaygain Type"
+ *: "Yeniden Oynatma Türü"
</dest>
<voice>
- *: "Replaygain Type"
+ *: "Yeniden Oynatma Türü"
</voice>
</phrase>
<phrase>
@@ -8369,10 +7932,10 @@
*: "Export Modifications"
</source>
<dest>
- *: "Export Modifications"
+ *: "Değişiklikleri Dışa Aktar"
</dest>
<voice>
- *: "Export Modifications"
+ *: "Değişiklikleri Dışa Aktar"
</voice>
</phrase>
<phrase>
@@ -8383,10 +7946,10 @@
*: "Reset Setting"
</source>
<dest>
- *: "Reset Setting"
+ *: "Ayarları Sıfırla"
</dest>
<voice>
- *: "Reset Setting"
+ *: "Ayarları Sıfırla"
</voice>
</phrase>
<phrase>
@@ -8399,28 +7962,11 @@
</source>
<dest>
*: none
- touchscreen: "Del"
- </dest>
- <voice>
- *: none
- touchscreen: "Del"
- </voice>
-</phrase>
-<phrase>
- id: LANG_MDB_CENTER
- desc: in sound settings
- user: core
- <source>
- *: none
- masf: "MDB Centre Frequency"
- </source>
- <dest>
- *: none
- masf: "MDB Centre Frequency"
+ touchscreen: "Sil"
</dest>
<voice>
*: none
- masf: "MDB Centre Frequency"
+ touchscreen: "Sil"
</voice>
</phrase>
<phrase>
@@ -8433,11 +7979,11 @@
</source>
<dest>
*: none
- lcd_color: "Text Colour"
+ lcd_color: "Metin Rengi"
</dest>
<voice>
*: none
- lcd_color: "Text Colour"
+ lcd_color: "Metin Rengi"
</voice>
</phrase>
<phrase>
@@ -8448,10 +7994,10 @@
*: "Add to Playlist"
</source>
<dest>
- *: "Add to Playlist"
+ *: "Oynatma listesine ekle"
</dest>
<voice>
- *: "Add to Playlist"
+ *: "Oynatma listesine ekle"
</voice>
</phrase>
<phrase>
@@ -8462,27 +8008,10 @@
*: "Reshuffle"
</source>
<dest>
- *: "Reshuffle"
+ *: "yeniden karıştır"
</dest>
<voice>
- *: "Reshuffle"
- </voice>
-</phrase>
-<phrase>
- id: LANG_SYSFONT_RECORDING_FREQUENCY
- desc: in the recording settings
- user: core
- <source>
- *: none
- recording: "Frequency"
- </source>
- <dest>
- *: none
- recording: "Frequency"
- </dest>
- <voice>
- *: none
- recording: "Frequency"
+ *: "yeniden karıştır"
</voice>
</phrase>
<phrase>
@@ -8493,10 +8022,10 @@
*: "<Untagged>"
</source>
<dest>
- *: "<Untagged>"
+ *: "<EtiketlenmemiÅŸ>"
</dest>
<voice>
- *: "Untagged"
+ *: "<EtiketlenmemiÅŸ>"
</voice>
</phrase>
<phrase>
@@ -8507,27 +8036,10 @@
*: "Constrain Auto-Change"
</source>
<dest>
- *: "Constrain Auto-Change"
- </dest>
- <voice>
- *: "Constrain Auto-Change"
- </voice>
-</phrase>
-<phrase>
- id: LANG_FM_BUTTONBAR_ACTION
- desc: in radio screen
- user: core
- <source>
- *: none
- radio_screen_button_bar: "Action"
- </source>
- <dest>
- *: none
- radio_screen_button_bar: "Action"
+ *: "Otomatik Değişimi Kısıtla"
</dest>
<voice>
- *: none
- radio_screen_button_bar: ""
+ *: "Otomatik Değişimi Kısıtla"
</voice>
</phrase>
<phrase>
@@ -8538,27 +8050,10 @@
*: "Comment"
</source>
<dest>
- *: "Comment"
+ *: "Yorumcu"
</dest>
<voice>
- *: ""
- </voice>
-</phrase>
-<phrase>
- id: LANG_BUTTONBAR_MENU
- desc: in button bar
- user: core
- <source>
- *: none
- radio_screen_button_bar: "Menu"
- </source>
- <dest>
- *: none
- radio_screen_button_bar: "Menu"
- </dest>
- <voice>
- *: none
- radio_screen_button_bar: ""
+ *: "Yorumcu"
</voice>
</phrase>
<phrase>
@@ -8571,11 +8066,11 @@
</source>
<dest>
*: none
- lcd_color: "Foreground Colour"
+ lcd_color: "Ön Plan Rengi"
</dest>
<voice>
*: none
- lcd_color: "Foreground Colour"
+ lcd_color: "Ön Plan Rengi"
</voice>
</phrase>
<phrase>
@@ -8588,11 +8083,11 @@
</source>
<dest>
*: none
- remote: "Remote While Playing Screen"
+ remote: "Oynatma Ekranı Kontrolü"
</dest>
<voice>
*: none
- remote: "Remote While Playing Screen"
+ remote: "Oynatma Ekranı Kontrolü"
</voice>
</phrase>
<phrase>
@@ -8603,10 +8098,10 @@
*: "Karaoke"
</source>
<dest>
- *: "Karaoke"
+ *: "Karaoke vokal"
</dest>
<voice>
- *: "Karaoke"
+ *: "Karaoke vokal"
</voice>
</phrase>
<phrase>
@@ -8619,11 +8114,11 @@
</source>
<dest>
*: none
- remote: "Remote Statusbar"
+ remote: "Durum Çubuğu Kontrolü"
</dest>
<voice>
*: none
- remote: "Remote Statusbar"
+ remote: "Durum Çubuğu Kontrolü"
</voice>
</phrase>
<phrase>
@@ -8664,7 +8159,7 @@
</source>
<dest>
*: none
- alarm: "Alarm Time:"
+ alarm: "Alarm Zamanı:"
</dest>
<voice>
*: none
@@ -8726,11 +8221,11 @@
</source>
<dest>
*: none
- radio: "Europe"
+ radio: "Avrupa"
</dest>
<voice>
*: none
- radio: "Europe"
+ radio: "Avrupa"
</voice>
</phrase>
<phrase>
@@ -8741,10 +8236,10 @@
*: "Recursively Insert Directories"
</source>
<dest>
- *: "Recursively Insert Directories"
+ *: "Dizinleri Özyinelemeli Ekle"
</dest>
<voice>
- *: "Recursively Insert Directories"
+ *: "Dizinleri Özyinelemeli Ekle"
</voice>
</phrase>
<phrase>
@@ -8755,10 +8250,10 @@
*: "Weak"
</source>
<dest>
- *: "Weak"
+ *: "Zayıf"
</dest>
<voice>
- *: "Weak"
+ *: "Zayıf"
</voice>
</phrase>
<phrase>
@@ -8769,10 +8264,10 @@
*: "Glyphs To Cache"
</source>
<dest>
- *: "Glyphs To Cache"
+ *: "Önbelleğe Alınacak Glifler"
</dest>
<voice>
- *: "Glyphs To Cache"
+ *: "Önbelleğe Alınacak Glifler"
</voice>
</phrase>
<phrase>
@@ -8783,10 +8278,10 @@
*: "Sort Files"
</source>
<dest>
- *: "Sort Files"
+ *: "Dosyaları Sırala"
</dest>
<voice>
- *: "sort files"
+ *: "Dosyaları Sırala"
</voice>
</phrase>
<phrase>
@@ -8797,10 +8292,10 @@
*: "Free:"
</source>
<dest>
- *: "Free:"
+ *: "BoÅŸ Alan:"
</dest>
<voice>
- *: "Free diskspace:"
+ *: "BoÅŸ Alan:"
</voice>
</phrase>
<phrase>
@@ -8811,10 +8306,10 @@
*: "<No Info>"
</source>
<dest>
- *: "<No Info>"
+ *: "<Bilgi Yok>"
</dest>
<voice>
- *: ""
+ *: "Bilgi yok"
</voice>
</phrase>
<phrase>
@@ -8831,7 +8326,7 @@
</dest>
<voice>
*: none
- recording: "minus infinity"
+ recording: "eksi sonsuzluk"
</voice>
</phrase>
<phrase>
@@ -8859,10 +8354,10 @@
*: "Paste"
</source>
<dest>
- *: "Paste"
+ *: "Yapıştır"
</dest>
<voice>
- *: "Paste"
+ *: "Yapıştır"
</voice>
</phrase>
<phrase>
@@ -8873,10 +8368,10 @@
*: "Prevent Track Skipping"
</source>
<dest>
- *: "Prevent Track Skipping"
+ *: "Atlamayı Önle"
</dest>
<voice>
- *: "Prevent Track Skipping"
+ *: "Atlamayı Önle"
</voice>
</phrase>
<phrase>
@@ -8887,10 +8382,10 @@
*: "Track Display"
</source>
<dest>
- *: "Track Display"
+ *: "Parça Ekranı"
</dest>
<voice>
- *: "Track Display"
+ *: "Parça Ekranı"
</voice>
</phrase>
<phrase>
@@ -8918,7 +8413,7 @@
*: ""
</dest>
<voice>
- *: "while-playing-screen"
+ *: "Oynatma Ekranı"
</voice>
</phrase>
<phrase>
@@ -8932,7 +8427,7 @@
*: "Tarz"
</dest>
<voice>
- *: ""
+ *: "Tarz"
</voice>
</phrase>
<phrase>
@@ -8943,10 +8438,10 @@
*: "Browse Theme Files"
</source>
<dest>
- *: "Browse Theme Files"
+ *: "Tema Dosyalarına Göz Atın"
</dest>
<voice>
- *: "Browse Theme Files"
+ *: "Tema Dosyalarına Göz Atın"
</voice>
</phrase>
<phrase>
@@ -8957,27 +8452,10 @@
*: "Inserted %d tracks (%s)"
</source>
<dest>
- *: "Inserted %d tracks (%s)"
+ *: "Eklendi %d Parça (%s)"
</dest>
<voice>
- *: "tracks inserted"
- </voice>
-</phrase>
-<phrase>
- id: LANG_RECORDING_QUALITY
- desc: in the recording settings
- user: core
- <source>
- *: none
- recording_hwcodec: "Quality"
- </source>
- <dest>
- *: none
- recording_hwcodec: "Quality"
- </dest>
- <voice>
- *: none
- recording_hwcodec: "Quality"
+ *: "eklenen parçalar"
</voice>
</phrase>
<phrase>
@@ -9018,11 +8496,11 @@
</source>
<dest>
*: none
- radio: "Region"
+ radio: "Bölge"
</dest>
<voice>
*: none
- radio: "Region"
+ radio: "Bölge"
</voice>
</phrase>
<phrase>
@@ -9033,10 +8511,10 @@
*: "Track Gain if Shuffling"
</source>
<dest>
- *: "Track Gain if Shuffling"
+ *: "Karıştırılıyorsa Kazancı Takip Edin"
</dest>
<voice>
- *: "Track Gain if Shuffling"
+ *: "Karıştırılıyorsa Kazancı Takip Edin"
</voice>
</phrase>
<phrase>
@@ -9061,10 +8539,10 @@
*: "Use Directory .talk Clips"
</source>
<dest>
- *: "Use Directory .talk Clips"
+ *: "Directory .talk Kliplerini Kullanın"
</dest>
<voice>
- *: "Use Directory .talk Clips"
+ *: "Directory .talk Kliplerini Kullanın"
</voice>
</phrase>
<phrase>
@@ -9075,10 +8553,10 @@
*: "Thai (ISO-8859-11)"
</source>
<dest>
- *: "Thai (ISO-8859-11)"
+ *: "Tayca (ISO-8859-11)"
</dest>
<voice>
- *: "Thai"
+ *: "Tayca"
</voice>
</phrase>
<phrase>
@@ -9089,10 +8567,10 @@
*: "Searching... %d found (%s)"
</source>
<dest>
- *: "Searching... %d found (%s)"
+ *: "Aranıyor... %d Bulundu (%s)"
</dest>
<voice>
- *: ""
+ *: "Bulundu"
</voice>
</phrase>
<phrase>
@@ -9103,10 +8581,10 @@
*: "Insert Shuffled"
</source>
<dest>
- *: "Insert Shuffled"
+ *: "Karışık Ekle"
</dest>
<voice>
- *: "Insert Shuffled"
+ *: "Karışık Ekle"
</voice>
</phrase>
<phrase>
@@ -9117,10 +8595,10 @@
*: "Upside Down"
</source>
<dest>
- *: "Upside Down"
+ *: "Başaşağı"
</dest>
<voice>
- *: "Upside Down"
+ *: "Başaşağı"
</voice>
</phrase>
<phrase>
@@ -9133,11 +8611,11 @@
</source>
<dest>
*: none
- agc: "DJ-Set (slow)"
+ agc: "DJ Seti (yavaÅŸ)"
</dest>
<voice>
*: none
- agc: "DJ set (slow)"
+ agc: "DJ Seti (yavaÅŸ)"
</voice>
</phrase>
<phrase>
@@ -9148,7 +8626,7 @@
*: "Buttons Locked"
</source>
<dest>
- *: "Buttons Locked"
+ *: "TuÅŸlar Kilitli"
</dest>
<voice>
*: ""
@@ -9165,13 +8643,13 @@
</source>
<dest>
*: none
- iriverh100,iriverh120,iriverh300: "Internal Microphone"
- recording: "Microphone"
+ iriverh100,iriverh120,iriverh300: "Dahili Mikrofon"
+ recording: "Mikrofon"
</dest>
<voice>
*: none
- iriverh100,iriverh120,iriverh300: "Internal Microphone"
- recording: "Microphone"
+ iriverh100,iriverh120,iriverh300: "Dahili Mikrofon"
+ recording: "Mikrofon"
</voice>
</phrase>
<phrase>
@@ -9182,10 +8660,10 @@
*: "Spell"
</source>
<dest>
- *: "Spell"
+ *: "Harf harf kodlamak"
</dest>
<voice>
- *: "Spell"
+ *: "Harf harf kodlamak"
</voice>
</phrase>
<phrase>
@@ -9196,10 +8674,10 @@
*: "Add to Shortcuts"
</source>
<dest>
- *: "Add to Shortcuts"
+ *: "Kısayollara Ekle"
</dest>
<voice>
- *: "Add to Shortcuts"
+ *: "Kısayollara Ekle"
</voice>
</phrase>
<phrase>
@@ -9210,10 +8688,10 @@
*: "File/directory exists. Overwrite?"
</source>
<dest>
- *: "File/directory exists. Overwrite?"
+ *: "Dosya/dizin var. Üzerine yazılsın mı?"
</dest>
<voice>
- *: "File or directory exists. Overwrite?"
+ *: "Dosya/dizin var. Üzerine yazılsın mı?"
</voice>
</phrase>
<phrase>
@@ -9224,10 +8702,10 @@
*: "Skip to Outro"
</source>
<dest>
- *: "Skip to Outro"
+ *: "Bitimden 5sn. atla"
</dest>
<voice>
- *: "Skip to Outro"
+ *: "Bitimden 5sn. atla"
</voice>
</phrase>
<phrase>
@@ -9241,7 +8719,7 @@
*: ""
</dest>
<voice>
- *: "audio"
+ *: "Ses"
</voice>
</phrase>
<phrase>
@@ -9254,11 +8732,11 @@
</source>
<dest>
*: none
- radio: "Auto-Scan Presets"
+ radio: "Otomatik Tarama Ön Ayarları"
</dest>
<voice>
*: none
- radio: "Auto scan presets"
+ radio: "Otomatik Tarama Ön Ayarları"
</voice>
</phrase>
<phrase>
@@ -9285,11 +8763,11 @@
</source>
<dest>
*: none
- recording: "New file"
+ recording: "Yeni dosya"
</dest>
<voice>
*: none
- recording: "New file"
+ recording: "Yeni dosya"
</voice>
</phrase>
<phrase>
@@ -9331,10 +8809,10 @@
*: "Insert Last Shuffled"
</source>
<dest>
- *: "Insert Last Shuffled"
+ *: "Son Karıştırılanı Ekle"
</dest>
<voice>
- *: "Insert Last Shuffled"
+ *: "Son Karıştırılanı Ekle"
</voice>
</phrase>
<phrase>
@@ -9347,11 +8825,11 @@
</source>
<dest>
*: none
- remote: "Main and Remote Unit"
+ remote: "Ana ve Uzak Ãœnite"
</dest>
<voice>
*: none
- remote: "Main and remote unit"
+ remote: "Ana ve Uzak Ãœnite"
</voice>
</phrase>
<phrase>
@@ -9362,10 +8840,10 @@
*: "Scroll Start Delay"
</source>
<dest>
- *: "Scroll Start Delay"
+ *: "Kaydırma Başlatma Gecikmesi"
</dest>
<voice>
- *: "Scroll Start Delay"
+ *: "Kaydırma Başlatma Gecikmesi"
</voice>
</phrase>
<phrase>
@@ -9376,10 +8854,10 @@
*: "Error accessing playlist file"
</source>
<dest>
- *: "Error accessing playlist file"
+ *: "Oynatma listesi dosyasına erişilirken hata oluştu"
</dest>
<voice>
- *: "Error accessing playlist file"
+ *: "Oynatma listesi dosyasına erişilirken hata oluştu"
</voice>
</phrase>
<phrase>
@@ -9390,10 +8868,10 @@
*: "Year"
</source>
<dest>
- *: "Year"
+ *: "Yıl"
</dest>
<voice>
- *: ""
+ *: "Yıl"
</voice>
</phrase>
<phrase>
@@ -9406,11 +8884,11 @@
</source>
<dest>
*: none
- recording: "Presplit Gap"
+ recording: "Önceden Bölünmüş Boşluk"
</dest>
<voice>
*: none
- recording: "Presplit Gap"
+ recording: "Önceden Bölünmüş Boşluk"
</voice>
</phrase>
<phrase>
@@ -9423,25 +8901,11 @@
</source>
<dest>
*: none
- touchscreen: "Calibrate"
+ touchscreen: "kalibre et"
</dest>
<voice>
*: none
- touchscreen: "Calibrate"
- </voice>
-</phrase>
-<phrase>
- id: LANG_SYSFONT_MODE
- desc: in wps F2 pressed
- user: core
- <source>
- *: "Mode:"
- </source>
- <dest>
- *: "Mode:"
- </dest>
- <voice>
- *: ""
+ touchscreen: "kalibre et"
</voice>
</phrase>
<phrase>
@@ -9460,16 +8924,17 @@
</source>
<dest>
*: none
- alarm: "PLAY=Set OFF=Cancel"
- gigabeats: "SELECT=Set POWER=Cancel"
- ipod*: "SELECT=Set MENU=Cancel"
- iriverh10,iriverh10_5gb: "SELECT=Set PREV=Cancel"
- mpiohd300: "ENTER=Set MENU=Cancel"
- sansafuzeplus: "SELECT=Set BACK=Cancel"
- vibe500: "OK=Set C=Cancel"
+ alarm: "OYNAT=KAPALI olarak ayarla=Ä°ptal"
+ gigabeats: "SELECT=GÜÇ Ayarla=İptal"
+ ipod*: "SELECT=Ayarla,MENÜ=İptal"
+ iriverh10,iriverh10_5gb: "SELECT=Ayarla PREV=Ä°ptal"
+ mpiohd300: "ENTER=Ayarla MENÜ=İptal"
+ sansafuzeplus: "SELECT=GERÄ° Ayarla=Ä°ptal"
+ vibe500: "Tamam=Ayarla, C=Ä°ptal"
</dest>
<voice>
*: none
+ alarm,ipod*: ""
</voice>
</phrase>
<phrase>
@@ -9480,10 +8945,10 @@
*: "Peak Filter %d"
</source>
<dest>
- *: "Peak Filter %d"
+ *: "Tepe Filtresi %d"
</dest>
<voice>
- *: "Peak filter"
+ *: "Tepe Filtresi"
</voice>
</phrase>
<phrase>
@@ -9496,11 +8961,11 @@
</source>
<dest>
*: none
- recording: "Encoder Settings"
+ recording: "Kodlayıcı Ayarları"
</dest>
<voice>
*: none
- recording: "Encoder Settings"
+ recording: "Kodlayıcı Ayarları"
</voice>
</phrase>
<phrase>
@@ -9513,11 +8978,11 @@
</source>
<dest>
*: none
- radio: "The Preset List is Full"
+ radio: "Ön Ayar Listesi Dolu"
</dest>
<voice>
*: none
- radio: "The Preset List is Full"
+ radio: "Ön Ayar Listesi Dolu"
</voice>
</phrase>
<phrase>
@@ -9531,7 +8996,7 @@
*: ""
</dest>
<voice>
- *: "of"
+ *: "den"
</voice>
</phrase>
<phrase>
@@ -9544,11 +9009,11 @@
</source>
<dest>
*: none
- agc: "Live (slow)"
+ agc: "Canlı (yavaş)"
</dest>
<voice>
*: none
- agc: "Live (slow)"
+ agc: "Canlı (yavaş)"
</voice>
</phrase>
<phrase>
@@ -9559,10 +9024,10 @@
*: "Create Playlist"
</source>
<dest>
- *: "Create Playlist"
+ *: "Oynatma listesi oluÅŸtur"
</dest>
<voice>
- *: "Create Playlist"
+ *: "Oynatma listesi oluÅŸtur"
</voice>
</phrase>
<phrase>
@@ -9575,7 +9040,7 @@
</source>
<dest>
*: none
- pitchscreen: "Semitone Up"
+ pitchscreen: "Yarım Ton Yukarı"
</dest>
<voice>
*: none
@@ -9606,11 +9071,11 @@
</source>
<dest>
*: none
- rtc: "Time & Date"
+ rtc: "Saat ve Tarih"
</dest>
<voice>
*: none
- rtc: "Time and Date"
+ rtc: "Saat ve Tarih"
</voice>
</phrase>
<phrase>
@@ -9623,11 +9088,11 @@
</source>
<dest>
*: none
- recording: "Start Above"
+ recording: "Yukarıdan Başlayın"
</dest>
<voice>
*: none
- recording: "Start Above"
+ recording: "Yukarıdan Başlayın"
</voice>
</phrase>
<phrase>
@@ -9638,10 +9103,10 @@
*: "of"
</source>
<dest>
- *: "of"
+ *: "den"
</dest>
<voice>
- *: "of"
+ *: "den"
</voice>
</phrase>
<phrase>
@@ -9654,11 +9119,11 @@
</source>
<dest>
*: none
- remote: "Main Screen"
+ remote: "Ana ekran"
</dest>
<voice>
*: none
- remote: "Main Screen"
+ remote: "Ana ekran"
</voice>
</phrase>
<phrase>
@@ -9671,11 +9136,11 @@
</source>
<dest>
*: none
- serial_port: "Serial Bitrate"
+ serial_port: "Seri Bit Hızı"
</dest>
<voice>
*: none
- serial_port: "Serial Bitrate"
+ serial_port: "Seri Bit Hızı"
</voice>
</phrase>
<phrase>
@@ -9700,10 +9165,10 @@
*: "Playlist Viewer Settings"
</source>
<dest>
- *: "Playlist Viewer Settings"
+ *: "Oynatma Listesi Görüntüleyici Ayarları"
</dest>
<voice>
- *: "Playlist Viewer Settings"
+ *: "Oynatma Listesi Görüntüleyici Ayarları"
</voice>
</phrase>
<phrase>
@@ -9716,11 +9181,11 @@
</source>
<dest>
*: none
- ibassodx50,ibassodx90: "Freq Scaling Governor"
+ ibassodx50,ibassodx90: "Frekans Ölçekleme Valisi"
</dest>
<voice>
*: none
- ibassodx50,ibassodx90: "Freq Scaling Governor"
+ ibassodx50,ibassodx90: "Frekans Ölçekleme Valisi"
</voice>
</phrase>
<phrase>
@@ -9733,11 +9198,11 @@
</source>
<dest>
*: none
- crossfade: "Manual Track Skip Only"
+ crossfade: "Yalnızca Manuel Atlama"
</dest>
<voice>
*: none
- crossfade: "Manual Track Skip Only"
+ crossfade: "Yalnızca Manuel Atlama"
</voice>
</phrase>
<phrase>
@@ -9750,11 +9215,11 @@
</source>
<dest>
*: none
- hotkey: "WPS Hotkey"
+ hotkey: "WPS Kısayol Tuşu"
</dest>
<voice>
*: none
- hotkey: "WPS Hotkey"
+ hotkey: "WPS Kısayol Tuşu"
</voice>
</phrase>
<phrase>
@@ -9767,7 +9232,7 @@
</source>
<dest>
*: none
- recording: "Split Time:"
+ recording: "Bölme Süresi:"
</dest>
<voice>
*: none
@@ -9796,10 +9261,10 @@
*: "Threshold"
</source>
<dest>
- *: "Threshold"
+ *: "EÅŸik"
</dest>
<voice>
- *: "Threshold"
+ *: "EÅŸik"
</voice>
</phrase>
<phrase>
@@ -9807,16 +9272,13 @@
desc: in lcd settings
user: core
<source>
- *: none
- lcd_sleep: "Never"
+ *: "Never"
</source>
<dest>
- *: none
- lcd_sleep: "Never"
+ *: "Asla"
</dest>
<voice>
- *: none
- lcd_sleep: "Never"
+ *: "Asla"
</voice>
</phrase>
<phrase>
@@ -9827,10 +9289,10 @@
*: "Bookmark on Stop"
</source>
<dest>
- *: "Bookmark on Stop"
+ *: "Durunca Yer imi oluÅŸtur"
</dest>
<voice>
- *: "Bookmark on Stop"
+ *: "Durunca Yer imi oluÅŸtur"
</voice>
</phrase>
<phrase>
@@ -9844,7 +9306,7 @@
*: ""
</dest>
<voice>
- *: "minutes"
+ *: "Dakika"
</voice>
</phrase>
<phrase>
@@ -9869,10 +9331,10 @@
*: "Restart Sleep Timer On Keypress"
</source>
<dest>
- *: "Restart Sleep Timer On Keypress"
+ *: "Tuşa Basıldığında Uyku Zamanlayıcısını Yeniden Başlatın"
</dest>
<voice>
- *: "Restart Sleep Timer On Keypress"
+ *: "Tuşa Basıldığında Uyku Zamanlayıcısını Yeniden Başlatın"
</voice>
</phrase>
<phrase>
@@ -9885,11 +9347,11 @@
</source>
<dest>
*: none
- hotkey: "Hotkey"
+ hotkey: "kısayol tuşu"
</dest>
<voice>
*: none
- hotkey: "Hotkey"
+ hotkey: "kısayol tuşu"
</voice>
</phrase>
<phrase>
@@ -9902,11 +9364,11 @@
</source>
<dest>
*: none
- crossfade: "Crossfade"
+ crossfade: "Çapraz geçiş"
</dest>
<voice>
*: none
- crossfade: "Crossfade"
+ crossfade: "Çapraz geçiş"
</voice>
</phrase>
<phrase>
@@ -9958,14 +9420,17 @@
<source>
*: none
battery_types: "NiMH"
+ xduoox3: "Older (1500 mAh)"
</source>
<dest>
*: none
- battery_types: "NiMH"
+ battery_types: "NiMH Nikel metal Hidrit"
+ xduoox3: "Daha eski (1500 mAh)"
</dest>
<voice>
*: none
- battery_types: "Nickel metal hydride"
+ battery_types: "Nikel metal Hidrit"
+ xduoox3: "Daha eski 1500 miliamper saat"
</voice>
</phrase>
<phrase>
@@ -9976,10 +9441,10 @@
*: "Auto"
</source>
<dest>
- *: "Auto"
+ *: "Oto"
</dest>
<voice>
- *: "Auto"
+ *: "Oto"
</voice>
</phrase>
<phrase>
@@ -9990,10 +9455,10 @@
*: "Arabic (CP1256)"
</source>
<dest>
- *: "Arabic (CP1256)"
+ *: "Arapça (CP1256)"
</dest>
<voice>
- *: "Arabic"
+ *: "Arapça"
</voice>
</phrase>
<phrase>
@@ -10018,10 +9483,10 @@
*: "Committing database"
</source>
<dest>
- *: "Committing database"
+ *: "Veritabanı işleme"
</dest>
<voice>
- *: "Committing database"
+ *: "Veritabanı işleme"
</voice>
</phrase>
<phrase>
@@ -10035,7 +9500,7 @@
*: ""
</dest>
<voice>
- *: "hour"
+ *: "Saat"
</voice>
</phrase>
<phrase>
@@ -10046,10 +9511,10 @@
*: "Greek (ISO-8859-7)"
</source>
<dest>
- *: "Greek (ISO-8859-7)"
+ *: "Yunanca (ISO-8859-7)"
</dest>
<voice>
- *: "Greek"
+ *: "Yunanca"
</voice>
</phrase>
<phrase>
@@ -10062,11 +9527,11 @@
</source>
<dest>
*: none
- crossfade: "Fade-In Duration"
+ crossfade: "Yavaşlatma Süresi"
</dest>
<voice>
*: none
- crossfade: "Fade-In Duration"
+ crossfade: "Yavaşlatma Süresi"
</voice>
</phrase>
<phrase>
@@ -10077,10 +9542,10 @@
*: "By Date"
</source>
<dest>
- *: "By Date"
+ *: "Tarihe göre"
</dest>
<voice>
- *: "By Date"
+ *: "Tarihe göre"
</voice>
</phrase>
<phrase>
@@ -10093,11 +9558,11 @@
</source>
<dest>
*: none
- remote: "Main Unit Only"
+ remote: "Sadece Ana Ãœnite"
</dest>
<voice>
*: none
- remote: "Main unit only"
+ remote: "Sadece Ana Ãœnite"
</voice>
</phrase>
<phrase>
@@ -10110,11 +9575,11 @@
</source>
<dest>
*: none
- touchscreen: "Line Padding in Lists"
+ touchscreen: "Listelerde Satır Doldurma"
</dest>
<voice>
*: none
- touchscreen: "Line Padding in Lists"
+ touchscreen: "Listelerde Satır Doldurma"
</voice>
</phrase>
<phrase>
@@ -10127,11 +9592,11 @@
</source>
<dest>
*: none
- agc: "AGC clip time"
+ agc: "AGC klip zamanı"
</dest>
<voice>
*: none
- agc: "AGC clip time"
+ agc: "AGC klip zamanı"
</voice>
</phrase>
<phrase>
@@ -10142,10 +9607,10 @@
*: "Playlist"
</source>
<dest>
- *: "Playlist"
+ *: "Çalma listesi"
</dest>
<voice>
- *: "Playlist"
+ *: "Çalma listesi"
</voice>
</phrase>
<phrase>
@@ -10172,11 +9637,11 @@
</source>
<dest>
*: none
- radio: "Clear Preset List"
+ radio: "Ön Ayar Listesini Temizle"
</dest>
<voice>
*: none
- radio: "Clear Preset List"
+ radio: "Ön Ayar Listesini Temizle"
</voice>
</phrase>
<phrase>
@@ -10187,10 +9652,10 @@
*: "Boot changed"
</source>
<dest>
- *: "Boot changed"
+ *: "Önyükleme değişti"
</dest>
<voice>
- *: "Boot changed"
+ *: "Önyükleme değişti"
</voice>
</phrase>
<phrase>
@@ -10201,10 +9666,10 @@
*: "Alphabetical"
</source>
<dest>
- *: "Alphabetical"
+ *: "Alfabetik"
</dest>
<voice>
- *: "Alphabetical"
+ *: "Alfabetik"
</voice>
</phrase>
<phrase>
@@ -10231,11 +9696,11 @@
</source>
<dest>
*: none
- crossfade: "Fade-In Delay"
+ crossfade: "Azalma Gecikmesi"
</dest>
<voice>
*: none
- crossfade: "Fade-In Delay"
+ crossfade: "Azalma Gecikmesi"
</voice>
</phrase>
<phrase>
@@ -10243,7 +9708,7 @@
desc: Selective Actions
user: core
<source>
- *: "Play"
+ *: "Exempt Play"
</source>
<dest>
*: "Play"
@@ -10260,10 +9725,10 @@
*: "Move"
</source>
<dest>
- *: "Move"
+ *: "Taşı"
</dest>
<voice>
- *: "Move"
+ *: "Taşı"
</voice>
</phrase>
<phrase>
@@ -10274,10 +9739,10 @@
*: "Settings Saved"
</source>
<dest>
- *: "Settings Saved"
+ *: "Ayarlar kaydedildi"
</dest>
<voice>
- *: "Settings Saved"
+ *: "Ayarlar kaydedildi"
</voice>
</phrase>
<phrase>
@@ -10288,10 +9753,10 @@
*: "Use File .talk Clips"
</source>
<dest>
- *: "Use File .talk Clips"
+ *: "Dosya .talk Kliplerini Kullan"
</dest>
<voice>
- *: "Use File .talk Clips"
+ *: "Dosya .talk Kliplerini Kullan"
</voice>
</phrase>
<phrase>
@@ -10316,10 +9781,10 @@
*: "Disable on External Power"
</source>
<dest>
- *: "Disable on External Power"
+ *: "Harici Güçte Devre Dışı Bırak"
</dest>
<voice>
- *: "Disable on External Power"
+ *: "Harici Güçte Devre Dışı Bırak"
</voice>
</phrase>
<phrase>
@@ -10330,7 +9795,7 @@
*: "Creating"
</source>
<dest>
- *: "Creating"
+ *: "OluÅŸturuluyor..."
</dest>
<voice>
*: ""
@@ -10347,7 +9812,7 @@
*: ""
</dest>
<voice>
- *: "statusbar skin"
+ *: "durum çubuğu görünümü"
</voice>
</phrase>
<phrase>
@@ -10374,11 +9839,11 @@
</source>
<dest>
*: none
- gigabeatfx,ipod6g,ipodvideo,mpiohd200,mpiohd300,mrobe100: "Bass Cutoff"
+ gigabeatfx,ipod6g,ipodvideo,mpiohd200,mpiohd300,mrobe100: "Bas Kesme"
</dest>
<voice>
*: none
- gigabeatfx,ipod6g,ipodvideo,mpiohd200,mpiohd300,mrobe100: "Bass Cutoff"
+ gigabeatfx,ipod6g,ipodvideo,mpiohd200,mpiohd300,mrobe100: "Bas Kesme"
</voice>
</phrase>
<phrase>
@@ -10391,11 +9856,11 @@
</source>
<dest>
*: none
- recording: "MPEG Layer 3"
+ recording: "MP3"
</dest>
<voice>
*: none
- recording: "MPEG Layer 3"
+ recording: "MP3"
</voice>
</phrase>
<phrase>
@@ -10406,10 +9871,10 @@
*: "Next Track:"
</source>
<dest>
- *: "Next Track:"
+ *: "Sonraki parça:"
</dest>
<voice>
- *: "Next Track:"
+ *: "Sonraki parça:"
</voice>
</phrase>
<phrase>
@@ -10423,7 +9888,7 @@
*: ""
</dest>
<voice>
- *: "Index"
+ *: "Dizin"
</voice>
</phrase>
<phrase>
@@ -10434,10 +9899,10 @@
*: "Insert Last"
</source>
<dest>
- *: "Insert Last"
+ *: "Sona Ekle"
</dest>
<voice>
- *: "Insert Last"
+ *: "Sona Ekle"
</voice>
</phrase>
<phrase>
@@ -10462,10 +9927,10 @@
*: "Low Shelf Filter"
</source>
<dest>
- *: "Low Shelf Filter"
+ *: "Düşük Raf Filtresi"
</dest>
<voice>
- *: "Low shelf filter"
+ *: "Düşük Raf Filtresi"
</voice>
</phrase>
<phrase>
@@ -10478,11 +9943,11 @@
</source>
<dest>
*: none
- radio: "Force Mono"
+ radio: "Mono'yu Zorla"
</dest>
<voice>
*: none
- radio: "Force Mono"
+ radio: "Mono'yu Zorla"
</voice>
</phrase>
<phrase>
@@ -10495,11 +9960,11 @@
</source>
<dest>
*: none
- radio: "Clear Current Presets?"
+ radio: "Mevcut Ön Ayarlar Temizlensin mi?"
</dest>
<voice>
*: none
- radio: "Clear Current Presets?"
+ radio: "Mevcut Ön Ayarlar Temizlensin mi?"
</voice>
</phrase>
<phrase>
@@ -10510,10 +9975,10 @@
*: "Erase dynamic playlist?"
</source>
<dest>
- *: "Erase dynamic playlist?"
+ *: "Dinamik oynatma listesi silinsin mi?"
</dest>
<voice>
- *: "Erase dynamic playlist?"
+ *: "Dinamik oynatma listesi silinsin mi?"
</voice>
</phrase>
<phrase>
@@ -10524,10 +9989,10 @@
*: "By Type"
</source>
<dest>
- *: "By Type"
+ *: "Türe Göre"
</dest>
<voice>
- *: "By Type"
+ *: "Türe Göre"
</voice>
</phrase>
<phrase>
@@ -10541,7 +10006,7 @@
*: ""
</dest>
<voice>
- *: "decibel"
+ *: "desibel"
</voice>
</phrase>
<phrase>
@@ -10569,10 +10034,10 @@
*: "USB Mode"
</source>
<dest>
- *: "USB Mode"
+ *: "USB Modu"
</dest>
<voice>
- *: "USB Mode"
+ *: "USB Modu"
</voice>
</phrase>
<phrase>
@@ -10583,10 +10048,10 @@
*: "Update Now"
</source>
<dest>
- *: "Update Now"
+ *: "Şimdi güncelle"
</dest>
<voice>
- *: "Update Now"
+ *: "Şimdi güncelle"
</voice>
</phrase>
<phrase>
@@ -10597,10 +10062,10 @@
*: "Simple (Meier)"
</source>
<dest>
- *: "Simple (Meier)"
+ *: "Basit (Meier)"
</dest>
<voice>
- *: "Simple"
+ *: "Basit"
</voice>
</phrase>
<phrase>
@@ -10628,10 +10093,10 @@
*: "f(x2)"
</source>
<dest>
- *: "f(x2)"
+ *: "Surround f(x2)"
</dest>
<voice>
- *: "f(x2)"
+ *: "Surround f(x2)"
</voice>
</phrase>
<phrase>
@@ -10648,7 +10113,7 @@
</dest>
<voice>
*: none
- remote: "remote while-playing-screen"
+ remote: "Oyun oynarken uzaktan kumanda"
</voice>
</phrase>
<phrase>
@@ -10659,10 +10124,10 @@
*: "Gather Runtime Data"
</source>
<dest>
- *: "Gather Runtime Data"
+ *: "Çalışma Zamanı Verilerini Toplayın"
</dest>
<voice>
- *: "Gather Runtime Data"
+ *: "Çalışma Zamanı Verilerini Toplayın"
</voice>
</phrase>
<phrase>
@@ -10675,11 +10140,11 @@
</source>
<dest>
*: none
- radio: "Radio Screen"
+ radio: "Radyo ekranı"
</dest>
<voice>
*: none
- radio: "Radio Screen"
+ radio: "Radyo ekranı"
</voice>
</phrase>
<phrase>
@@ -10692,11 +10157,11 @@
</source>
<dest>
*: none
- radio: "Other"
+ radio: "DiÄŸer"
</dest>
<voice>
*: none
- radio: "Other"
+ radio: "DiÄŸer"
</voice>
</phrase>
<phrase>
@@ -10709,11 +10174,11 @@
</source>
<dest>
*: none
- lcd_color: "Bar (Gradient Colour)"
+ lcd_color: "Bar (Degrade Renk)"
</dest>
<voice>
*: none
- lcd_color: "Bar (Gradient Colour)"
+ lcd_color: "Bar Degrade Renk"
</voice>
</phrase>
<phrase>
@@ -10724,10 +10189,10 @@
*: "Default Codepage"
</source>
<dest>
- *: "Default Codepage"
+ *: "Varsayılan Kod Sayfası"
</dest>
<voice>
- *: "Default codepage"
+ *: "Varsayılan Kod Sayfası"
</voice>
</phrase>
<phrase>
@@ -10738,10 +10203,10 @@
*: "Warn When Erasing Dynamic Playlist"
</source>
<dest>
- *: "Warn When Erasing Dynamic Playlist"
+ *: "Dinamik Oynatma Listesini Silerken Uyar"
</dest>
<voice>
- *: "Warn When Erasing Dynamic Playlist"
+ *: "Dinamik Oynatma Listesini Silerken Uyar"
</voice>
</phrase>
<phrase>
@@ -10752,10 +10217,10 @@
*: "Bookmarking"
</source>
<dest>
- *: "Bookmarking"
+ *: "Yer imi"
</dest>
<voice>
- *: "Bookmarking"
+ *: "Yer imi"
</voice>
</phrase>
<phrase>
@@ -10781,11 +10246,11 @@
wheel_acceleration: none
</source>
<dest>
- *: "List Acceleration Speed"
+ *: "Liste Hızlandırma Hızı"
wheel_acceleration: none
</dest>
<voice>
- *: "List Acceleration Speed"
+ *: "Liste Hızlandırma Hızı"
wheel_acceleration: none
</voice>
</phrase>
@@ -10799,11 +10264,11 @@
</source>
<dest>
*: none
- recording: "Split Filesize"
+ recording: "Dosya Boyutunu Böl"
</dest>
<voice>
*: none
- recording: "Split Filesize"
+ recording: "Dosya Boyutunu Böl"
</voice>
</phrase>
<phrase>
@@ -10816,25 +10281,11 @@
</source>
<dest>
*: none
- gigabeats,samsungypr1: "Band %d Frequency"
+ gigabeats,samsungypr1: "Bant %d Frekans"
</dest>
<voice>
*: none
- gigabeats,samsungypr1: "Band Frequency"
- </voice>
-</phrase>
-<phrase>
- id: LANG_GIGABYTE
- desc: a unit postfix, also voiced
- user: core
- <source>
- *: "GB"
- </source>
- <dest>
- *: "GB"
- </dest>
- <voice>
- *: "gigabyte"
+ gigabeats,samsungypr1: "Bant Frekansı"
</voice>
</phrase>
<phrase>
@@ -10845,10 +10296,10 @@
*: "Last.fm Log"
</source>
<dest>
- *: "Last.fm Log"
+ *: "Last.fm Günlüğü"
</dest>
<voice>
- *: "Last.fm Log"
+ *: "Last.fm Günlüğü"
</voice>
</phrase>
<phrase>
@@ -10859,10 +10310,10 @@
*: "Cancel Sleep Timer"
</source>
<dest>
- *: "Cancel Sleep Timer"
+ *: "Uyku Zamanlayıcısını İptal Et"
</dest>
<voice>
- *: "Cancel Sleep Timer"
+ *: "Uyku Zamanlayıcısını İptal Et"
</voice>
</phrase>
<phrase>
@@ -10875,11 +10326,11 @@
</source>
<dest>
*: none
- remote: "Remote Unit Only"
+ remote: "Yalnızca Uzak Ünite"
</dest>
<voice>
*: none
- remote: "Remote unit only"
+ remote: "Yalnızca Uzak Ünite"
</voice>
</phrase>
<phrase>
@@ -10890,10 +10341,10 @@
*: "Bar (Inverse)"
</source>
<dest>
- *: "Bar (Inverse)"
+ *: "Bar (Ters)"
</dest>
<voice>
- *: "Inverse Bar"
+ *: "Ters Bar"
</voice>
</phrase>
<phrase>
@@ -10904,10 +10355,10 @@
*: "<All tracks>"
</source>
<dest>
- *: "<All tracks>"
+ *: "<Tüm parçalar>"
</dest>
<voice>
- *: "All tracks"
+ *: "Tüm parçalar"
</voice>
</phrase>
<phrase>
@@ -10918,10 +10369,10 @@
*: "Set Song Rating"
</source>
<dest>
- *: "Set Song Rating"
+ *: "Şarkı Derecelendirmesini Ayarla"
</dest>
<voice>
- *: "Set Song Rating"
+ *: "Şarkı Derecelendirmesini Ayarla"
</voice>
</phrase>
<phrase>
@@ -10932,10 +10383,10 @@
*: "Mass Storage"
</source>
<dest>
- *: "Mass Storage"
+ *: "Yığın Bellek"
</dest>
<voice>
- *: "Mass Storage"
+ *: "Yığın Bellek"
</voice>
</phrase>
<phrase>
@@ -10946,10 +10397,10 @@
*: "Track Gain"
</source>
<dest>
- *: "Track Gain"
+ *: "Kazanç Takibi"
</dest>
<voice>
- *: ""
+ *: "Kazanç Takibi"
</voice>
</phrase>
<phrase>
@@ -10962,11 +10413,11 @@
</source>
<dest>
*: none
- agc: "AGC max. gain"
+ agc: "AGC maks. kazanç"
</dest>
<voice>
*: none
- agc: "AGC maximum gain"
+ agc: "AGC maksimum kazanç"
</voice>
</phrase>
<phrase>
@@ -10977,10 +10428,10 @@
*: "Gain"
</source>
<dest>
- *: "Gain"
+ *: "Kazanç"
</dest>
<voice>
- *: "Gain"
+ *: "Kazanç"
</voice>
</phrase>
<phrase>
@@ -10991,24 +10442,10 @@
*: "Cuesheet Support"
</source>
<dest>
- *: "Cuesheet Support"
- </dest>
- <voice>
- *: "Cuesheet Support"
- </voice>
-</phrase>
-<phrase>
- id: LANG_KILOBYTE
- desc: a unit postfix, also voiced
- user: core
- <source>
- *: "KB"
- </source>
- <dest>
- *: "KB"
+ *: "Cuesheet DesteÄŸi"
</dest>
<voice>
- *: "kilobyte"
+ *: "Cuesheet DesteÄŸi"
</voice>
</phrase>
<phrase>
@@ -11019,10 +10456,10 @@
*: "Force"
</source>
<dest>
- *: "Force"
+ *: "Kuvvet"
</dest>
<voice>
- *: "Force"
+ *: "Kuvvet"
</voice>
</phrase>
<phrase>
@@ -11033,10 +10470,10 @@
*: "Initialize Now"
</source>
<dest>
- *: "Initialize Now"
+ *: "Åžimdi BaÅŸlat"
</dest>
<voice>
- *: "Initialize Now"
+ *: "Åžimdi BaÅŸlat"
</voice>
</phrase>
<phrase>
@@ -11049,28 +10486,11 @@
</source>
<dest>
*: none
- radio: "No settings found. Autoscan?"
- </dest>
- <voice>
- *: none
- radio: "No settings found. Autoscan?"
- </voice>
-</phrase>
-<phrase>
- id: LANG_SYSFONT_LINE_IN
- desc: in the recording settings
- user: core
- <source>
- *: none
- recording: "Line In"
- </source>
- <dest>
- *: none
- recording: "Line In"
+ radio: "Ayar bulunamadı. Otomatik taransın mı?"
</dest>
<voice>
*: none
- recording: "Line In"
+ radio: "Ayar bulunamadı. Otomatik taransın mı?"
</voice>
</phrase>
<phrase>
@@ -11111,11 +10531,11 @@
</source>
<dest>
*: none
- crossfade: "Mix"
+ crossfade: "Karışık"
</dest>
<voice>
*: none
- crossfade: "Mix"
+ crossfade: "Karışık"
</voice>
</phrase>
<phrase>
@@ -11126,10 +10546,10 @@
*: "Album Gain"
</source>
<dest>
- *: "Album Gain"
+ *: "Albüm Kazancı"
</dest>
<voice>
- *: ""
+ *: "Albüm Kazancı"
</voice>
</phrase>
<phrase>
@@ -11140,10 +10560,10 @@
*: "Show Icons"
</source>
<dest>
- *: "Show Icons"
+ *: "Simgeleri Göste"
</dest>
<voice>
- *: "Show Icons"
+ *: "Simgeleri Göste"
</voice>
</phrase>
<phrase>
@@ -11154,10 +10574,10 @@
*: "Reboot now?"
</source>
<dest>
- *: "Reboot now?"
+ *: "Åžimdi yeniden baÅŸlat?"
</dest>
<voice>
- *: "Reboot now?"
+ *: "Åžimdi yeniden baÅŸlat?"
</voice>
</phrase>
<phrase>
@@ -11170,11 +10590,11 @@
</source>
<dest>
*: none
- depth_3d: "3-D Enhancement"
+ depth_3d: "3-D GeliÅŸtirme"
</dest>
<voice>
*: none
- depth_3d: "3-D Enhancement"
+ depth_3d: "3-D GeliÅŸtirme"
</voice>
</phrase>
<phrase>
@@ -11216,10 +10636,10 @@
*: "Shortcuts"
</source>
<dest>
- *: "Shortcuts"
+ *: "Kısayollar"
</dest>
<voice>
- *: "Shortcuts"
+ *: "Kısayollar"
</voice>
</phrase>
<phrase>
@@ -11230,10 +10650,10 @@
*: "Unicode (UTF-8)"
</source>
<dest>
- *: "Unicode (UTF-8)"
+ *: "Tek kodlu Unicode (UTF-8)"
</dest>
<voice>
- *: "Unicode"
+ *: "Tek kodlu"
</voice>
</phrase>
<phrase>
@@ -11246,11 +10666,11 @@
</source>
<dest>
*: none
- rtc: "Set Time/Date"
+ rtc: "Saati/Tarihi Ayarla"
</dest>
<voice>
*: none
- rtc: "Set Time and Date"
+ rtc: "Saati/Tarihi Ayarla"
</voice>
</phrase>
<phrase>
@@ -11263,11 +10683,11 @@
</source>
<dest>
*: none
- agc: "AGC"
+ agc: "AGC (O.K.K)"
</dest>
<voice>
*: none
- agc: "Automatic gain control"
+ agc: "Otomatik kazanç kontrolü"
</voice>
</phrase>
<phrase>
@@ -11292,10 +10712,10 @@
*: "Korean (KSX1001)"
</source>
<dest>
- *: "Korean (KSX1001)"
+ *: "Korece (KSX1001)"
</dest>
<voice>
- *: "Korean"
+ *: "Korece"
</voice>
</phrase>
<phrase>
@@ -11306,10 +10726,10 @@
*: "Bidirectional Scroll Limit"
</source>
<dest>
- *: "Bidirectional Scroll Limit"
+ *: "BÇift Yönlü Kaydırma Sınırı"
</dest>
<voice>
- *: "Bidirectional Scroll Limit"
+ *: "Çift Yönlü Kaydırma Sınırı"
</voice>
</phrase>
<phrase>
@@ -11322,11 +10742,11 @@
</source>
<dest>
*: none
- quickscreen: "Set as Top Quickscreen Item"
+ quickscreen: "En İyi Hızlı Ekran Öğesi Olarak Ayarla"
</dest>
<voice>
*: none
- quickscreen: "Set as Top Quickscreen Item"
+ quickscreen: "En İyi Hızlı Ekran Öğesi Olarak Ayarla"
</voice>
</phrase>
<phrase>
@@ -11339,11 +10759,11 @@
</source>
<dest>
*: none
- alarm: "Wake-Up Alarm"
+ alarm: "Uyandırma Alarmı"
</dest>
<voice>
*: none
- alarm: "Wake-Up Alarm"
+ alarm: "Uyandırma Alarmı"
</voice>
</phrase>
<phrase>
@@ -11354,10 +10774,10 @@
*: "Sleep Timer"
</source>
<dest>
- *: "Sleep Timer"
+ *: "Uyku Zamanlayıcısı"
</dest>
<voice>
- *: "Sleep Timer"
+ *: "Uyku Zamanlayıcısı"
</voice>
</phrase>
<phrase>
@@ -11368,10 +10788,10 @@
*: "Hebrew (ISO-8859-8)"
</source>
<dest>
- *: "Hebrew (ISO-8859-8)"
+ *: "Ä°branice (ISO-8859-8)"
</dest>
<voice>
- *: "Hebrew"
+ *: "Ä°branice"
</voice>
</phrase>
<phrase>
@@ -11385,7 +10805,7 @@
*: ""
</dest>
<voice>
- *: "second"
+ *: "ikinci"
</voice>
</phrase>
<phrase>
@@ -11396,10 +10816,10 @@
*: "Album"
</source>
<dest>
- *: "Album"
+ *: "Albüm"
</dest>
<voice>
- *: ""
+ *: "Albüm"
</voice>
</phrase>
<phrase>
@@ -11409,15 +10829,13 @@
<source>
*: "Battery: %d%% %dh %dm"
ipodmini1g,ipodmini2g,iriverh10: "Batt: %d%% %dh %dm"
- iriverifp7xx: "%d%% %dh %dm"
</source>
<dest>
- *: "Battery: %d%% %dh %dm"
- ipodmini1g,ipodmini2g,iriverh10: "Batt: %d%% %dh %dm"
- iriverifp7xx: "%d%% %dh %dm"
+ *: "Pil: %d%% %dh %dm"
+ ipodmini1g,ipodmini2g,iriverh10: "Pil: %d%% %dh %dm"
</dest>
<voice>
- *: "Battery level"
+ *: "Pil seviyesi"
</voice>
</phrase>
<phrase>
@@ -11430,7 +10848,7 @@
</source>
<dest>
*: none
- radio: "Scanning %d.%02d MHz"
+ radio: "Taranıyor %d.%02d MHz"
</dest>
<voice>
*: none
@@ -11462,27 +10880,10 @@
*: "Line Separator Colour"
</source>
<dest>
- *: "Line Separator Colour"
+ *: "Satır Ayırıcı Rengi"
</dest>
<voice>
- *: "Line Separator Colour"
- </voice>
-</phrase>
-<phrase>
- id: LANG_SYSFONT_DIRBROWSE_F1
- desc: in dir browser, F1 button bar text
- user: core
- <source>
- *: none
- recorder_pad: "Menu"
- </source>
- <dest>
- *: none
- recorder_pad: "Menu"
- </dest>
- <voice>
- *: none
- recorder_pad: ""
+ *: "Satır Ayırıcı Rengi"
</voice>
</phrase>
<phrase>
@@ -11493,10 +10894,10 @@
*: "Auditory Fatigue Reduction"
</source>
<dest>
- *: "Auditory Fatigue Reduction"
+ *: "Ä°ÅŸitsel Yorgunluk Azaltma"
</dest>
<voice>
- *: "Auditory Fatigue Reduction"
+ *: "Ä°ÅŸitsel Yorgunluk Azaltma"
</voice>
</phrase>
<phrase>
@@ -11507,10 +10908,10 @@
*: "Deleting..."
</source>
<dest>
- *: "Deleting..."
+ *: "Siliniyor..."
</dest>
<voice>
- *: "Deleting"
+ *: "Siliniyor..."
</voice>
</phrase>
<phrase>
@@ -11523,11 +10924,11 @@
</source>
<dest>
*: none
- recording: "Bitrate"
+ recording: "bit hızı"
</dest>
<voice>
*: none
- recording: "Bitrate"
+ recording: "bit hızı"
</voice>
</phrase>
<phrase>
@@ -11541,7 +10942,7 @@
*: ""
</dest>
<voice>
- *: "kilobits per second"
+ *: "saniyede kilobit"
</voice>
</phrase>
<phrase>
@@ -11568,11 +10969,11 @@
</source>
<dest>
*: none
- crossfade: "Automatic Track Change Only"
+ crossfade: "Yalnızca Otomatik Parça Değişimi"
</dest>
<voice>
*: none
- crossfade: "Automatic Track Change Only"
+ crossfade: "Yalnızca Otomatik Parça Değişimi"
</voice>
</phrase>
<phrase>
@@ -11583,10 +10984,10 @@
*: "SIDE ONLY"
</source>
<dest>
- *: "SIDE ONLY"
+ *: "SADECE YAN"
</dest>
<voice>
- *: "SIDE ONLY"
+ *: "SADECE YAN"
</voice>
</phrase>
<phrase>
@@ -11599,11 +11000,11 @@
</source>
<dest>
*: none
- recording: "Mono Left + Right"
+ recording: "Mono Sol + SaÄŸ"
</dest>
<voice>
*: none
- recording: "Mono Left plus Right"
+ recording: "Mono Sol artı Sağ"
</voice>
</phrase>
<phrase>
@@ -11617,24 +11018,7 @@
*: ""
</dest>
<voice>
- *: "plus"
- </voice>
-</phrase>
-<phrase>
- id: LANG_SYSFONT_DIRBROWSE_F3
- desc: in dir browser, F3 button bar text
- user: core
- <source>
- *: none
- recorder_pad: "LCD"
- </source>
- <dest>
- *: none
- recorder_pad: "LCD"
- </dest>
- <voice>
- *: none
- recorder_pad: ""
+ *: "artı"
</voice>
</phrase>
<phrase>
@@ -11648,7 +11032,7 @@
*: ""
</dest>
<voice>
- *: "milli-amp hours"
+ *: "mili amper saat"
</voice>
</phrase>
<phrase>
@@ -11656,7 +11040,7 @@
desc: Softlock behaviour setting
user: core
<source>
- *: "Disable Notify"
+ *: "Disable Locked Reminders"
</source>
<dest>
*: "Disable Notify"
@@ -11675,7 +11059,7 @@
</source>
<dest>
*: none
- pitchscreen: "Semitone Down"
+ pitchscreen: "Yarım Ton Aşağı"
</dest>
<voice>
*: none
@@ -11690,10 +11074,10 @@
*: "Interpret numbers when sorting"
</source>
<dest>
- *: "Interpret numbers when sorting"
+ *: "Sıralama sırasında sayıları yorumlayın"
</dest>
<voice>
- *: "Interpret numbers when sorting"
+ *: "Sıralama sırasında sayıları yorumlayın"
</voice>
</phrase>
<phrase>
@@ -11706,11 +11090,11 @@
</source>
<dest>
*: none
- alarm: "Alarm Wake up Screen"
+ alarm: "Alarm Uyandırma Ekranı"
</dest>
<voice>
*: none
- alarm: "Alarm Wake up Screen"
+ alarm: "Alarm Uyandırma Ekranı"
</voice>
</phrase>
<phrase>
@@ -11721,10 +11105,10 @@
*: "Show Indices"
</source>
<dest>
- *: "Show Indices"
+ *: "Endeksleri Göster"
</dest>
<voice>
- *: "Show Indices"
+ *: "Endeksleri Göster"
</voice>
</phrase>
<phrase>
@@ -11735,10 +11119,10 @@
*: "Auto-Change Directory"
</source>
<dest>
- *: "Auto-Change Directory"
+ *: "Dizini Otomatik DeÄŸiÅŸtir"
</dest>
<voice>
- *: "Auto-Change Directory"
+ *: "Dizini Otomatik DeÄŸiÅŸtir"
</voice>
</phrase>
<phrase>
@@ -11751,11 +11135,11 @@
</source>
<dest>
*: none
- recording: "Gain L"
+ recording: "Kazanç L"
</dest>
<voice>
*: none
- recording: "Gain Left"
+ recording: "Sol Kazanç"
</voice>
</phrase>
<phrase>
@@ -11768,11 +11152,11 @@
</source>
<dest>
*: none
- lcd_color: "Background Colour"
+ lcd_color: "Arkaplan rengi"
</dest>
<voice>
*: none
- lcd_color: "Background Colour"
+ lcd_color: "Arkaplan rengi"
</voice>
</phrase>
<phrase>
@@ -11797,10 +11181,10 @@
*: "Browse .cfg Files"
</source>
<dest>
- *: "Browse .cfg Files"
+ *: ".cfg Dosyalarına Gözatın"
</dest>
<voice>
- *: "Browse configuration files"
+ *: ".cfg Dosyalarına Gözatın"
</voice>
</phrase>
<phrase>
@@ -11813,11 +11197,11 @@
</source>
<dest>
*: none
- lcd_color: "Primary Colour"
+ lcd_color: "Ana Renk"
</dest>
<voice>
*: none
- lcd_color: "Primary Colour"
+ lcd_color: "Ana Renk"
</voice>
</phrase>
<phrase>
@@ -11828,10 +11212,10 @@
*: "Create Bookmark"
</source>
<dest>
- *: "Create Bookmark"
+ *: "Yer imi OluÅŸtur"
</dest>
<voice>
- *: "Create Bookmark"
+ *: "Yer imi OluÅŸtur"
</voice>
</phrase>
<phrase>
@@ -11844,11 +11228,11 @@
</source>
<dest>
*: none
- remote: "Remote Display OFF"
+ remote: "Uzak Ekran KAPALI"
</dest>
<voice>
*: none
- remote: "Remote Display OFF"
+ remote: "Uzak Ekran KAPALI"
</voice>
</phrase>
<phrase>
@@ -11861,11 +11245,11 @@
</source>
<dest>
*: none
- alarm: "Alarm Disabled"
+ alarm: "Alarm Devre Dışı"
</dest>
<voice>
*: none
- alarm: "Alarm Disabled"
+ alarm: "Alarm Devre Dışı"
</voice>
</phrase>
<phrase>
@@ -11879,7 +11263,7 @@
*: ""
</dest>
<voice>
- *: "per second"
+ *: "her saniye"
</voice>
</phrase>
<phrase>
@@ -11892,7 +11276,7 @@
</source>
<dest>
*: none
- radio: "Screen frozen!"
+ radio: "Ekran dondu!"
</dest>
<voice>
*: none
@@ -11910,7 +11294,7 @@
*: ""
</dest>
<voice>
- *: "hertz"
+ *: "-hertz"
</voice>
</phrase>
<phrase>
@@ -11921,10 +11305,10 @@
*: "Error updating playlist control file"
</source>
<dest>
- *: "Error updating playlist control file"
+ *: "Oynatma listesi kontrol dosyası güncellenirken hata oluştu"
</dest>
<voice>
- *: "Error updating playlist control file"
+ *: "Oynatma listesi kontrol dosyası güncellenirken hata oluştu"
</voice>
</phrase>
<phrase>
@@ -11935,10 +11319,10 @@
*: "Replaygain"
</source>
<dest>
- *: "Replaygain"
+ *: "Yeniden Oynatma kazancı"
</dest>
<voice>
- *: "Replaygain"
+ *: "Yeniden Oynatma kazancı"
</voice>
</phrase>
<phrase>
@@ -11949,10 +11333,10 @@
*: "Font"
</source>
<dest>
- *: "Font"
+ *: "Yazı tipi (Font)"
</dest>
<voice>
- *: "Font"
+ *: "Yazı tipi (Font)"
</voice>
</phrase>
<phrase>
@@ -11965,11 +11349,11 @@
</source>
<dest>
*: none
- radio_remote: "Remote Radio Screen"
+ radio_remote: "Radyo Ekranı"
</dest>
<voice>
*: none
- radio_remote: "Remote Radio Screen"
+ radio_remote: "Radyo Ekranı"
</voice>
</phrase>
<phrase>
@@ -11980,10 +11364,10 @@
*: "Screen Scrolls Out Of View"
</source>
<dest>
- *: "Screen Scrolls Out Of View"
+ *: "Satırlar Ekrandan Taşsın mı?"
</dest>
<voice>
- *: "Screen Scrolls Out Of View"
+ *: "Satırlar Ekrandan Taşsın mı?"
</voice>
</phrase>
<phrase>
@@ -11994,30 +11378,13 @@
*: "Failed reading %s"
</source>
<dest>
- *: "Failed reading %s"
+ *: "Okunamadı %s"
</dest>
<voice>
*: ""
</voice>
</phrase>
<phrase>
- id: LANG_SYSFONT_RECORD_TRIGGER
- desc: in recording settings_menu
- user: core
- <source>
- *: none
- recording: "Trigger"
- </source>
- <dest>
- *: none
- recording: "Trigger"
- </dest>
- <voice>
- *: none
- recording: "Trigger"
- </voice>
-</phrase>
-<phrase>
id: LANG_SYSFONT_EQUALIZER_BAND_Q
desc: in the equalizer settings menu
user: core
@@ -12053,10 +11420,10 @@
*: "Default Sleep Timer Duration"
</source>
<dest>
- *: "Default Sleep Timer Duration"
+ *: "Varsayılan Uyku Zamanlayıcı Süresi"
</dest>
<voice>
- *: "Default Sleep Timer Duration"
+ *: "Varsayılan Uyku Zamanlayıcı Süresi"
</voice>
</phrase>
<phrase>
@@ -12067,10 +11434,10 @@
*: "Bottom"
</source>
<dest>
- *: "Bottom"
+ *: "Alt"
</dest>
<voice>
- *: "Bottom"
+ *: "Alt"
</voice>
</phrase>
<phrase>
@@ -12083,11 +11450,11 @@
</source>
<dest>
*: none
- crossfade: "Fade-Out Duration"
+ crossfade: "Kararma Süresi"
</dest>
<voice>
*: none
- crossfade: "Fade-Out Duration"
+ crossfade: "Kararma Süresi"
</voice>
</phrase>
<phrase>
@@ -12098,10 +11465,10 @@
*: "As whole numbers"
</source>
<dest>
- *: "As whole numbers"
+ *: "Tam Sayılar Olarak"
</dest>
<voice>
- *: "As whole numbers"
+ *: "Tam Sayılar Olarak"
</voice>
</phrase>
<phrase>
@@ -12114,11 +11481,11 @@
</source>
<dest>
*: none
- recording: "for at least"
+ recording: "En azından"
</dest>
<voice>
*: none
- recording: "for at least"
+ recording: "En azından"
</voice>
</phrase>
<phrase>
@@ -12129,10 +11496,10 @@
*: ".talk Clip"
</source>
<dest>
- *: ".talk Clip"
+ *: ".talk Klip"
</dest>
<voice>
- *: "talk Clip"
+ *: "KonuÅŸma Klipleri"
</voice>
</phrase>
<phrase>
@@ -12143,10 +11510,10 @@
*: "Scroll Bar Position"
</source>
<dest>
- *: "Scroll Bar Position"
+ *: "Kaydırma Çubuğu Konumu"
</dest>
<voice>
- *: "Scroll bar position"
+ *: "Kaydırma Çubuğu Konumu"
</voice>
</phrase>
<phrase>
@@ -12159,11 +11526,11 @@
</source>
<dest>
*: none
- recording: "Directory"
+ recording: "Dizin"
</dest>
<voice>
*: none
- recording: "Directory"
+ recording: "Dizin"
</voice>
</phrase>
<phrase>
@@ -12174,10 +11541,10 @@
*: "Current Directory Only"
</source>
<dest>
- *: "Current Directory Only"
+ *: "Yalnızca Geçerli Dizin"
</dest>
<voice>
- *: "Current Directory Only"
+ *: "Yalnızca Geçerli Dizin"
</voice>
</phrase>
<phrase>
@@ -12202,10 +11569,10 @@
*: "Status-/Scrollbar"
</source>
<dest>
- *: "Status-/Scrollbar"
+ *: "Durum-/Kaydırma çubuğu"
</dest>
<voice>
- *: "Status- and Scrollbar"
+ *: "Durum Kaydırma çubuğu"
</voice>
</phrase>
<phrase>
@@ -12216,10 +11583,10 @@
*: "Resume on automatic track change"
</source>
<dest>
- *: "Resume on automatic track change"
+ *: "Otomatik şarkı değiştirmeye devam et"
</dest>
<voice>
- *: "Resume on automatic track change"
+ *: "Otomatik şarkı değiştirmeye devam et"
</voice>
</phrase>
<phrase>
@@ -12244,10 +11611,10 @@
*: "Bookmarks"
</source>
<dest>
- *: "Bookmarks"
+ *: "Yer imleri"
</dest>
<voice>
- *: "Bookmarks"
+ *: "Yer imleri"
</voice>
</phrase>
<phrase>
@@ -12272,10 +11639,10 @@
*: "Line Selector Type"
</source>
<dest>
- *: "Line Selector Type"
+ *: "Hat Seçici Tipi"
</dest>
<voice>
- *: "Line Selector Type"
+ *: "Hat Seçici Tipi"
</voice>
</phrase>
<phrase>
@@ -12286,10 +11653,10 @@
*: "New Keyboard"
</source>
<dest>
- *: "New Keyboard"
+ *: "Yeni Klavye"
</dest>
<voice>
- *: "New Keyboard"
+ *: "Yeni Klavye"
</voice>
</phrase>
<phrase>
@@ -12300,10 +11667,10 @@
*: "Saved %d tracks (%s)"
</source>
<dest>
- *: "Saved %d tracks (%s)"
+ *: "Kaydedildi %d parça (%s)"
</dest>
<voice>
- *: "tracks saved"
+ *: "parçalar kaydedildi"
</voice>
</phrase>
<phrase>
@@ -12316,11 +11683,11 @@
</source>
<dest>
*: none
- tc_ramcache: "Load to RAM"
+ tc_ramcache: "RAM yükle"
</dest>
<voice>
*: none
- tc_ramcache: "Load to RAM"
+ tc_ramcache: "RAM'e yükle"
</voice>
</phrase>
<phrase>
@@ -12333,7 +11700,7 @@
</source>
<dest>
*: none
- recording: "CLIP:"
+ recording: "KLÄ°P:"
</dest>
<voice>
*: none
@@ -12362,10 +11729,10 @@
*: "Release Time"
</source>
<dest>
- *: "Release Time"
+ *: "Yayın Süresi"
</dest>
<voice>
- *: "Release Time"
+ *: "Yayın Süresi"
</voice>
</phrase>
<phrase>
@@ -12407,10 +11774,10 @@
*: "Deleted"
</source>
<dest>
- *: "Deleted"
+ *: "Silindi!"
</dest>
<voice>
- *: "Deleted"
+ *: "Silindi!"
</voice>
</phrase>
<phrase>
@@ -12427,7 +11794,7 @@
</dest>
<voice>
*: none
- remote: "remote statusbar skin"
+ remote: "Durum Çubuğu Görünümü"
</voice>
</phrase>
<phrase>
@@ -12438,10 +11805,10 @@
*: "Voice Filenames"
</source>
<dest>
- *: "Voice Filenames"
+ *: "Ses Dosya Adları"
</dest>
<voice>
- *: "Voice Filenames"
+ *: "Ses Dosya Adları"
</voice>
</phrase>
<phrase>
@@ -12452,10 +11819,10 @@
*: "Error accessing directory"
</source>
<dest>
- *: "Error accessing directory"
+ *: "Dizine erişim hatası"
</dest>
<voice>
- *: "Error accessing directory"
+ *: "Dizine erişim hatası"
</voice>
</phrase>
<phrase>
@@ -12466,10 +11833,10 @@
*: "Pointer"
</source>
<dest>
- *: "Pointer"
+ *: "Işaretçi"
</dest>
<voice>
- *: "Pointer"
+ *: "Işaretçi"
</voice>
</phrase>
<phrase>
@@ -12480,27 +11847,10 @@
*: "Delete?"
</source>
<dest>
- *: "Delete?"
+ *: "Silinsin mi?"
</dest>
<voice>
- *: "Really delete?"
- </voice>
-</phrase>
-<phrase>
- id: LANG_SHUTDOWN
- desc: in main menu
- user: core
- <source>
- *: none
- soft_shutdown: "Shut down"
- </source>
- <dest>
- *: none
- soft_shutdown: "Shut down"
- </dest>
- <voice>
- *: none
- soft_shutdown: "Shut down"
+ *: "Silinsin mi?"
</voice>
</phrase>
<phrase>
@@ -12525,10 +11875,10 @@
*: "Custom"
</source>
<dest>
- *: "Custom"
+ *: "Özel"
</dest>
<voice>
- *: "Custom"
+ *: "Özel"
</voice>
</phrase>
<phrase>
@@ -12539,10 +11889,10 @@
*: "Delete"
</source>
<dest>
- *: "Delete"
+ *: "Sil"
</dest>
<voice>
- *: "Delete"
+ *: "Sil"
</voice>
</phrase>
<phrase>
@@ -12555,11 +11905,11 @@
</source>
<dest>
*: none
- recording: "Clipping Light"
+ recording: "Kırpma Işığı"
</dest>
<voice>
*: none
- recording: "Clipping Light"
+ recording: "Kırpma Işığı"
</voice>
</phrase>
<phrase>
@@ -12570,10 +11920,10 @@
*: "List Bookmarks"
</source>
<dest>
- *: "List Bookmarks"
+ *: "Yer Ä°mlerini Listeleme"
</dest>
<voice>
- *: "List Bookmarks"
+ *: "Yer Ä°mlerini Listeleme"
</voice>
</phrase>
<phrase>
@@ -12584,27 +11934,10 @@
*: "Battery Capacity"
</source>
<dest>
- *: "Battery Capacity"
- </dest>
- <voice>
- *: "Battery Capacity"
- </voice>
-</phrase>
-<phrase>
- id: LANG_REMOVE_MMC
- desc: before acknowledging usb in case an MMC is inserted (Ondio)
- user: core
- <source>
- *: none
- archosondio*: "Please remove inserted MMC"
- </source>
- <dest>
- *: none
- archosondio*: "Please remove inserted MMC"
+ *: "Pil Kapasitesi"
</dest>
<voice>
- *: none
- archosondio*: "Please remove multimedia card"
+ *: "Pil Kapasitesi"
</voice>
</phrase>
<phrase>
@@ -12632,10 +11965,10 @@
*: "Queue Last Shuffled"
</source>
<dest>
- *: "Queue Last Shuffled"
+ *: "Kuyruk Sonunda Karıştırıldı"
</dest>
<voice>
- *: "Queue Last Shuffled"
+ *: "Kuyruk Sonunda Karıştırıldı"
</voice>
</phrase>
<phrase>
@@ -12653,20 +11986,6 @@
</voice>
</phrase>
<phrase>
- id: LANG_MEGABYTE
- desc: a unit postfix, also voiced
- user: core
- <source>
- *: "MB"
- </source>
- <dest>
- *: "MB"
- </dest>
- <voice>
- *: "megabayt"
- </voice>
-</phrase>
-<phrase>
id: LANG_SERIAL_BITRATE_57600
desc: in system settings menu
user: core
@@ -12693,11 +12012,11 @@
</source>
<dest>
*: none
- recording: "Stop recording"
+ recording: "Kaydı Durdur"
</dest>
<voice>
*: none
- recording: "Stop recording"
+ recording: "Kaydı Durdur"
</voice>
</phrase>
<phrase>
@@ -12722,10 +12041,10 @@
*: "Cut"
</source>
<dest>
- *: "Cut"
+ *: "Kes"
</dest>
<voice>
- *: "Cut"
+ *: "Kes"
</voice>
</phrase>
<phrase>
@@ -12738,11 +12057,11 @@
</source>
<dest>
*: none
- lcd_sleep: "Sleep (After Backlight Off)"
+ lcd_sleep: "Uyku (Arka Işık Kapatıldıktan Sonra)"
</dest>
<voice>
*: none
- lcd_sleep: "Sleep after backlight off"
+ lcd_sleep: "Arka ışık kapatıldıktan sonra uykuya geç"
</voice>
</phrase>
<phrase>
@@ -12755,11 +12074,11 @@
</source>
<dest>
*: none
- radio: "US / Canada"
+ radio: "ABD / Kanada"
</dest>
<voice>
*: none
- radio: "US and Canada"
+ radio: "Amerika Kanada"
</voice>
</phrase>
<phrase>
@@ -12770,10 +12089,10 @@
*: "Custom"
</source>
<dest>
- *: "Custom"
+ *: "Özel"
</dest>
<voice>
- *: "Custom"
+ *: "Özel"
</voice>
</phrase>
<phrase>
@@ -12786,11 +12105,11 @@
</source>
<dest>
*: none
- pitchscreen: "Rate"
+ pitchscreen: "Oran"
</dest>
<voice>
*: none
- pitchscreen: "Rate"
+ pitchscreen: "Oran"
</voice>
</phrase>
<phrase>
@@ -12801,10 +12120,10 @@
*: "PictureFlow"
</source>
<dest>
- *: "PictureFlow"
+ *: "Resim Akışı"
</dest>
<voice>
- *: "open picture flow"
+ *: "Resim Akışı"
</voice>
</phrase>
<phrase>
@@ -12817,11 +12136,11 @@
</source>
<dest>
*: none
- crossfade: "Fade-Out Mode"
+ crossfade: "Ses Azaltma Modu"
</dest>
<voice>
*: none
- crossfade: "Fade-Out Mode"
+ crossfade: "Ses Azaltma Modu"
</voice>
</phrase>
<phrase>
@@ -12860,27 +12179,24 @@
*: "Add to New Playlist"
</source>
<dest>
- *: "Add to New Playlist"
+ *: "Yeni Oynatma Listesine Ekle"
</dest>
<voice>
- *: "Add to New Playlist"
+ *: "Yeni Oynatma Listesine Ekle"
</voice>
</phrase>
<phrase>
- id: LANG_RECORDING_FORMAT
- desc: audio format item in recording menu
+ id: LANG_FORMAT
+ desc: audio format
user: core
<source>
- *: none
- recording: "Format"
+ *: "Format"
</source>
<dest>
- *: none
- recording: "Format"
+ *: "Ses Formatı"
</dest>
<voice>
- *: none
- recording: "Format"
+ *: "Ses Formatı"
</voice>
</phrase>
<phrase>
@@ -12893,11 +12209,11 @@
</source>
<dest>
*: none
- recording: "Mono mode"
+ recording: "Mono modu"
</dest>
<voice>
*: none
- recording: "Mono mode"
+ recording: "Mono modu"
</voice>
</phrase>
<phrase>
@@ -12908,10 +12224,10 @@
*: "Centre Frequency"
</source>
<dest>
- *: "Centre Frequency"
+ *: "Merkez Frekansı"
</dest>
<voice>
- *: "Centre frequency"
+ *: "Merkez Frekansı"
</voice>
</phrase>
<phrase>
@@ -12941,11 +12257,11 @@
</source>
<dest>
*: none
- gigabeats,samsungypr1: "Tone Controls"
+ gigabeats,samsungypr1: "Ton Kontrolleri"
</dest>
<voice>
*: none
- gigabeats,samsungypr1: "Tone Controls"
+ gigabeats,samsungypr1: "Ton Kontrolleri"
</voice>
</phrase>
<phrase>
@@ -12956,10 +12272,10 @@
*: "Top"
</source>
<dest>
- *: "Top"
+ *: "Yukarı"
</dest>
<voice>
- *: "Top"
+ *: "Yukarı"
</voice>
</phrase>
<phrase>
@@ -12967,7 +12283,7 @@
desc: Selective Actions
user: core
<source>
- *: "Skip"
+ *: "Exempt Skip"
</source>
<dest>
*: "Skip"
@@ -12983,14 +12299,17 @@
<source>
*: none
battery_types: "Alkaline"
+ xduoox3: "Newer (2000 mAh)"
</source>
<dest>
*: none
- battery_types: "Alkaline"
+ battery_types: "Alkalin"
+ xduoox3: "Daha yeni (2000 mAh)"
</dest>
<voice>
*: none
- battery_types: "Alkaline"
+ battery_types: "Alkalin"
+ xduoox3: "Daha yeni 2000 mAh den"
</voice>
</phrase>
<phrase>
@@ -13001,10 +12320,10 @@
*: "Album Artist"
</source>
<dest>
- *: "Album Artist"
+ *: "Albüm sanatçısı"
</dest>
<voice>
- *: ""
+ *: "Albüm sanatçısı"
</voice>
</phrase>
<phrase>
@@ -13015,10 +12334,10 @@
*: "Voice"
</source>
<dest>
- *: "Voice"
+ *: "Ses"
</dest>
<voice>
- *: "Voice"
+ *: "Ses"
</voice>
</phrase>
<phrase>
@@ -13029,10 +12348,10 @@
*: "Charge Only"
</source>
<dest>
- *: "Charge Only"
+ *: "Sadece Åžarj"
</dest>
<voice>
- *: "Charge Only"
+ *: "Sadece Åžarj"
</voice>
</phrase>
<phrase>
@@ -13059,11 +12378,11 @@
</source>
<dest>
*: none
- histogram: "Histogram interval"
+ histogram: "histogram aralığı"
</dest>
<voice>
*: none
- histogram: "Histogram interval"
+ histogram: "histogram aralığı"
</voice>
</phrase>
<phrase>
@@ -13074,10 +12393,10 @@
*: "No Playlists"
</source>
<dest>
- *: "No Playlists"
+ *: "Oynatma Listesi Yok"
</dest>
<voice>
- *: "No Playlists"
+ *: "Oynatma Listesi Yok"
</voice>
</phrase>
<phrase>
@@ -13088,10 +12407,10 @@
*: "Dry / Wet Mix"
</source>
<dest>
- *: "Dry / Wet Mix"
+ *: "Suuround Karışım"
</dest>
<voice>
- *: "Dry / Wet Mix"
+ *: "Suuround Karışım"
</voice>
</phrase>
<phrase>
@@ -13102,9 +12421,3694 @@
*: "Language"
</source>
<dest>
- *: "Language"
+ *: "Dil"
</dest>
<voice>
- *: "Language"
+ *: "Dil"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_SINGLE_MODE
+ desc: single mode
+ user: core
+ <source>
+ *: "Single Mode"
+ </source>
+ <dest>
+ *: "Tek mod"
+ </dest>
+ <voice>
+ *: "Tek mod"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_TALK_MIXER_LEVEL
+ desc: Relative volume of voice prompts
+ user: core
+ <source>
+ *: "Voice prompt volume"
+ </source>
+ <dest>
+ *: "Ses istemi ses seviyesi"
+ </dest>
+ <voice>
+ *: "Ses istemi ses seviyesi"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FILTER_SHORT_SHARP
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ filter_roll_off: "Short Sharp"
+ </source>
+ <dest>
+ *: none
+ filter_roll_off: "Kısa Keskin"
+ </dest>
+ <voice>
+ *: none
+ filter_roll_off: "Kısa Keskin"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FILTER_SHORT_SLOW
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ filter_roll_off: "Short Slow"
+ </source>
+ <dest>
+ *: none
+ filter_roll_off: "Kısa Yavaş"
+ </dest>
+ <voice>
+ *: none
+ filter_roll_off: "Kısa Yavaş"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FILTER_SUPER_SLOW
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ filter_roll_off: "Super Slow"
+ </source>
+ <dest>
+ *: none
+ filter_roll_off: "Süper Yavaş"
+ </dest>
+ <voice>
+ *: none
+ filter_roll_off: "Süper Yavaş"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FILTER_SHORT
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ es9018: "Short"
+ </source>
+ <dest>
+ *: none
+ es9018: "Kısa"
+ </dest>
+ <voice>
+ *: none
+ es9018: "Kısa"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FILTER_BYPASS
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ es9018: "Bypass"
+ </source>
+ <dest>
+ *: none
+ es9018: "Bypass"
+ </dest>
+ <voice>
+ *: none
+ es9018: "Bypass"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FILTER_LINEAR_FAST
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ es9218: "Linear Fast"
+ </source>
+ <dest>
+ *: none
+ es9218: "Doğrusal Hızlı"
+ </dest>
+ <voice>
+ *: none
+ es9218: "Doğrusal Hızlı"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FILTER_LINEAR_SLOW
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ es9218: "Linear Slow"
+ </source>
+ <dest>
+ *: none
+ es9218: "DoÄŸrusal YavaÅŸ"
+ </dest>
+ <voice>
+ *: none
+ es9218: "DoÄŸrusal YavaÅŸ"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FILTER_MINIMUM_FAST
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ es9218: "Minimum Fast"
+ </source>
+ <dest>
+ *: none
+ es9218: "Asgari Hızlı"
+ </dest>
+ <voice>
+ *: none
+ es9218: "Asgari Hızlı"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FILTER_MINIMUM_SLOW
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ es9218: "Minimum Slow"
+ </source>
+ <dest>
+ *: none
+ es9218: "Minimum YavaÅŸ"
+ </dest>
+ <voice>
+ *: none
+ es9218: "Minimum YavaÅŸ"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FILTER_APODIZING_1
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ es9218: "Apodizing type 1"
+ </source>
+ <dest>
+ *: none
+ es9218: "Apodize 1"
+ </dest>
+ <voice>
+ *: none
+ es9218: "Apodize 1"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FILTER_APODIZING_2
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ es9218: "Apodizing type 2"
+ </source>
+ <dest>
+ *: none
+ es9218: "Apodize 2"
+ </dest>
+ <voice>
+ *: none
+ es9218: "Apodize 2"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FILTER_HYBRID_FAST
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ es9218: "Hybrid Fast"
+ </source>
+ <dest>
+ *: none
+ es9218: "Hibrit Hızlı"
+ </dest>
+ <voice>
+ *: none
+ es9218: "Hibrit Hızlı"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FILTER_BRICK_WALL
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ es9218: "Brick Wall"
+ </source>
+ <dest>
+ *: none
+ es9218: "Brick Wall"
+ </dest>
+ <voice>
+ *: none
+ es9218: "Brick Wall"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_DAC_POWER_MODE
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ dac_power_mode: "DAC's power mode"
+ es9218: "DAC's output level"
+ </source>
+ <dest>
+ *: none
+ dac_power_mode: "DAC güç modu"
+ es9218: "DAC çıkış seviyesi"
+ </dest>
+ <voice>
+ *: none
+ dac_power_mode: "DAC güç modu"
+ es9218: "DAC çıkış seviyesi"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_DAC_POWER_HIGH
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ dac_power_mode: "High performance"
+ es9218: "High Gain (2 Vrms)"
+ </source>
+ <dest>
+ *: none
+ dac_power_mode: "Yüksek performans"
+ es9218: "Yüksek Kazanç (2 Vrms)"
+ </dest>
+ <voice>
+ *: none
+ dac_power_mode: "Yüksek performans"
+ es9218: "Yüksek Kazanç 2 Vrms"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_DAC_POWER_LOW
+ desc: in sound settings
+ user: core
+ <source>
+ *: none
+ dac_power_mode: "Save battery"
+ es9218: "Low Gain (1 Vrms)"
+ </source>
+ <dest>
+ *: none
+ dac_power_mode: "Pilden tasarruf edin"
+ es9218: "Düşük Kazanç (1 Vrms)"
+ </dest>
+ <voice>
+ *: none
+ dac_power_mode: "Pilden tasarruf edin"
+ es9218: "Düşük Kazanç 1 Vrms"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_KIBIBYTE
+ desc: a unit postfix, also voiced
+ user: core
+ <source>
+ *: "KiB"
+ </source>
+ <dest>
+ *: "KiB"
+ </dest>
+ <voice>
+ *: "kibibyte"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_MEBIBYTE
+ desc: a unit postfix, also voiced
+ user: core
+ <source>
+ *: "MiB"
+ </source>
+ <dest>
+ *: "MiB"
+ </dest>
+ <voice>
+ *: "mebibyte"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_GIBIBYTE
+ desc: a unit postfix, also voiced
+ user: core
+ <source>
+ *: "GiB"
+ </source>
+ <dest>
+ *: "GiB"
+ </dest>
+ <voice>
+ *: "gibibyte"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_BOOKMARK_SETTINGS_ONE_PER_PLAYLIST
+ desc: Save only one bookmark for a playlist in recent bookmarks
+ user: core
+ <source>
+ *: "One per playlist"
+ </source>
+ <dest>
+ *: "Oynatma listesi başına bir tane"
+ </dest>
+ <voice>
+ *: "Oynatma listesi başına bir tane"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_BOOKMARK_SETTINGS_ONE_PER_TRACK
+ desc: Save only one bookmark for a combination (playlist,track) in recent bookmarks
+ user: core
+ <source>
+ *: "One per track"
+ </source>
+ <dest>
+ *: "Parça başına bir tane"
+ </dest>
+ <voice>
+ *: "Parça başına bir tane"
+ </voice>
+</phrase>
+<phrase>
+ id: VOICE_TRACK_TO_MOVE
+ desc: playlist viewer
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: "Hareket etmek için takip edin"
+ </voice>
+</phrase>
+<phrase>
+ id: VOICE_QUEUED
+ desc: playlist viewer
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: "sıraya alındı"
+ </voice>
+</phrase>
+<phrase>
+ id: VOICE_BAD_TRACK
+ desc: playlist viewer
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: "Bozuk Parça"
+ </voice>
+</phrase>
+<phrase>
+ id: VOICE_MOVING_TRACK
+ desc: playlist viewer
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: "Hareketli parça"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_NO_VIEWERS
+ desc: text for splash to indicate that no viewers are available
+ user: core
+ <source>
+ *: "No viewers found"
+ </source>
+ <dest>
+ *: "izleyici bulunamadı"
+ </dest>
+ <voice>
+ *: "izleyici bulunamadı"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PLAYTIME_ELAPSED
+ desc: playing time screen
+ user: core
+ <source>
+ *: "Playlist elapsed: %s / %s %ld%%"
+ </source>
+ <dest>
+ *: "Geçen oynatma listesi: %s / %s %ld%%"
+ </dest>
+ <voice>
+ *: "Geçen oynatma listesi"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PLAYTIME_TRK_ELAPSED
+ desc: playing time screen
+ user: core
+ <source>
+ *: "Track elapsed: %s / %s %ld%%"
+ </source>
+ <dest>
+ *: "Geçen parça: %s / %s %ld%%"
+ </dest>
+ <voice>
+ *: "Geçen parça"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PLAYTIME_REMAINING
+ desc: playing time screen
+ user: core
+ <source>
+ *: "Playlist remaining: %s"
+ </source>
+ <dest>
+ *: "Kalan oynatma listesi: %s"
+ </dest>
+ <voice>
+ *: "Kalan oynatma listesi"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PLAYTIME_TRK_REMAINING
+ desc: playing time screen
+ user: core
+ <source>
+ *: "Track remaining: %s"
+ </source>
+ <dest>
+ *: "Kalan parça: %s"
+ </dest>
+ <voice>
+ *: "Kalan parça"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PLAYTIME_TRACK
+ desc: playing time screen
+ user: core
+ <source>
+ *: "Track %d / %d %d%%"
+ </source>
+ <dest>
+ *: "Parça %d / %d %d%%"
+ </dest>
+ <voice>
+ *: "Parça"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PLAYTIME_STORAGE
+ desc: playing time screen
+ user: core
+ <source>
+ *: "Storage: %s (done %s, remaining %s)"
+ </source>
+ <dest>
+ *: "Depolama: %s (done %s, remaining %s)"
+ </dest>
+ <voice>
+ *: "Depolama"
+ </voice>
+</phrase>
+<phrase>
+ id: VOICE_PLAYTIME_DONE
+ desc: playing time screen
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: "Tamamlandı"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PLAYTIME_AVG_TRACK_SIZE
+ desc: playing time screen
+ user: core
+ <source>
+ *: "Average track size: %s"
+ </source>
+ <dest>
+ *: "Ortalama parça boyutu: %s"
+ </dest>
+ <voice>
+ *: "Ortalama parça boyutu"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PLAYTIME_AVG_BITRATE
+ desc: playing time screen
+ user: core
+ <source>
+ *: "Average bitrate: %ld kbps"
+ </source>
+ <dest>
+ *: "Ortalama bit hızı: %ld kbps"
+ </dest>
+ <voice>
+ *: "Ortalama bit hızı"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PLAYTIME_ERROR
+ desc: playing time screen
+ user: core
+ <source>
+ *: "Error while gathering info"
+ </source>
+ <dest>
+ *: "Bilgi toplanırken hata oluştu"
+ </dest>
+ <voice>
+ *: "Bilgi toplanırken hata oluştu"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PLAYING_TIME
+ desc: onplay menu
+ user: core
+ <source>
+ *: "Playing time"
+ </source>
+ <dest>
+ *: "Çalma Süresi"
+ </dest>
+ <voice>
+ *: "Çalma Süresi"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_CAR_ADAPTER_MODE_DELAY
+ desc: Displayed for setting car adapter mode delay
+ user: core
+ <source>
+ *: none
+ charging: "Delay Before Resume"
+ </source>
+ <dest>
+ *: none
+ charging: "Devam Etmeden Önce Gecikme"
+ </dest>
+ <voice>
+ *: none
+ charging: "Devam Etmeden Önce Gecikme"
+ </voice>
+</phrase>
+<phrase>
+ id: VOICE_PITCH_ABSOLUTE_MODE
+ desc: spoken only
+ user: core
+ <source>
+ *: none
+ pitchscreen: ""
+ </source>
+ <dest>
+ *: none
+ pitchscreen: ""
+ </dest>
+ <voice>
+ *: none
+ pitchscreen: "mutlak mod"
+ </voice>
+</phrase>
+<phrase>
+ id: VOICE_PITCH_SEMITONE_MODE
+ desc: spoken only
+ user: core
+ <source>
+ *: none
+ pitchscreen: ""
+ </source>
+ <dest>
+ *: none
+ pitchscreen: ""
+ </dest>
+ <voice>
+ *: none
+ pitchscreen: "yarım ton modu"
+ </voice>
+</phrase>
+<phrase>
+ id: VOICE_PITCH_TIMESTRETCH_MODE
+ desc: spoken only
+ user: core
+ <source>
+ *: none
+ pitchscreen: ""
+ </source>
+ <dest>
+ *: none
+ pitchscreen: ""
+ </dest>
+ <voice>
+ *: none
+ pitchscreen: "zaman uzatma"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_REMOTE_CONTROL
+ desc: Item for menus
+ user: core
+ <source>
+ *: "Remote Control"
+ </source>
+ <dest>
+ *: "Uzaktan kumanda"
+ </dest>
+ <voice>
+ *: "Uzaktan kumanda"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_NO_REM_CONTROL
+ desc: Item for menus
+ user: core
+ <source>
+ *: "No Rem. Control"
+ </source>
+ <dest>
+ *: "Uz.Kumanda Yok"
+ </dest>
+ <voice>
+ *: "Uzaktan Kumanda Yok"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_OUT_OF_CONTROL
+ desc: Item for menus
+ user: core
+ <source>
+ *: "Out of Control"
+ </source>
+ <dest>
+ *: "Kontrol dışı"
+ </dest>
+ <voice>
+ *: "Kontrol dışı"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_2_KEY_CONTROL
+ desc: Item for menus
+ user: core
+ <source>
+ *: "2 Key Control"
+ </source>
+ <dest>
+ *: "2 Tuş Kontrolü"
+ </dest>
+ <voice>
+ *: "2 Tuş Kontrolü"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_4_KEY_CONTROL
+ desc: Item for menus
+ user: core
+ <source>
+ *: "4 Key Control"
+ </source>
+ <dest>
+ *: "4 Tuş Kontrolü"
+ </dest>
+ <voice>
+ *: "4 Tuş Kontrolü"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PLAY_WORMLET
+ desc: For wormlet menu
+ user: core
+ <source>
+ *: "Play Wormlet!"
+ </source>
+ <dest>
+ *: "Wormlet'i oynayın!"
+ </dest>
+ <voice>
+ *: "Wormlet'i oynayın!"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_NUMBER_OF_WORMS
+ desc: For wormlet menu
+ user: core
+ <source>
+ *: "Number of Worms"
+ </source>
+ <dest>
+ *: "Solucan Sayısı"
+ </dest>
+ <voice>
+ *: "Solucan Sayısı"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_WORM_GROWTH_PER_FOOD
+ desc: For wormlet menu
+ user: core
+ <source>
+ *: "Worm Growth Per Food"
+ </source>
+ <dest>
+ *: "Gıda Başına Solucan Büyümesi"
+ </dest>
+ <voice>
+ *: "Gıda Başına Solucan Büyümesi"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_WORM_SPEED
+ desc: For wormlet menu
+ user: core
+ <source>
+ *: "Worm Speed"
+ </source>
+ <dest>
+ *: "solucan hızı"
+ </dest>
+ <voice>
+ *: "solucan hızı"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_ARGHS_PER_FOOD
+ desc: For wormlet menu
+ user: core
+ <source>
+ *: "Arghs Per Food"
+ </source>
+ <dest>
+ *: "AGıda Başına Arghs"
+ </dest>
+ <voice>
+ *: "Gıda Başına Arghs"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_ARGH_SIZE
+ desc: For wormlet menu
+ user: core
+ <source>
+ *: "Argh Size"
+ </source>
+ <dest>
+ *: "Argh Boyutu"
+ </dest>
+ <voice>
+ *: "Argh Boyutu"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FOOD_SIZE
+ desc: For wormlet menu
+ user: core
+ <source>
+ *: "Food Size"
+ </source>
+ <dest>
+ *: "Gıda Boyutu"
+ </dest>
+ <voice>
+ *: "Gıda Boyutu"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_NUMBER_OF_PLAYERS
+ desc: For game menus
+ user: core
+ <source>
+ *: "Number of Players"
+ </source>
+ <dest>
+ *: "Oyuncu sayısı"
+ </dest>
+ <voice>
+ *: "Oyuncu sayısı"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_CONTROL_STYLE
+ desc: In various menus
+ user: core
+ <source>
+ *: "Control Style"
+ </source>
+ <dest>
+ *: "Kontrol Stili"
+ </dest>
+ <voice>
+ *: "Kontrol Stili"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_REVERT_TO_DEFAULT_SETTINGS
+ desc: In various menus
+ user: core
+ <source>
+ *: "Revert to Default Settings"
+ </source>
+ <dest>
+ *: "Varsayılan Ayarlara Dön"
+ </dest>
+ <voice>
+ *: "Varsayılan Ayarlara Dön"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_MENU_QUIT
+ desc: in various menus
+ user: core
+ <source>
+ *: "Quit"
+ </source>
+ <dest>
+ *: "Çık"
+ </dest>
+ <voice>
+ *: "Çık"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_MENU_DISPLAY_OPTIONS
+ desc: in various menus
+ user: core
+ <source>
+ *: "Display Options"
+ </source>
+ <dest>
+ *: "Görüntüleme seçenekleri"
+ </dest>
+ <voice>
+ *: "Görüntüleme seçenekleri"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PREVTRACK
+ desc: in playback control menu
+ user: core
+ <source>
+ *: "Previous Track"
+ </source>
+ <dest>
+ *: "Önceki parça"
+ </dest>
+ <voice>
+ *: "Önceki parça"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PLAYPAUSE
+ desc: in playback control menu
+ user: core
+ <source>
+ *: "Pause / Play"
+ </source>
+ <dest>
+ *: "Duraklat / Oynat"
+ </dest>
+ <voice>
+ *: "Duraklat Oynat"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_STOP_PLAYBACK
+ desc: in playback control menu
+ user: core
+ <source>
+ *: "Stop Playback"
+ </source>
+ <dest>
+ *: "Oynatmayı Durdur"
+ </dest>
+ <voice>
+ *: "Oynatmayı Durdur"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_NEXTTRACK
+ desc: in playback control menu
+ user: core
+ <source>
+ *: "Next Track"
+ </source>
+ <dest>
+ *: "Sonraki parça"
+ </dest>
+ <voice>
+ *: "Sonraki parça"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_CHANGE_VOLUME
+ desc: in playback control menu
+ user: core
+ <source>
+ *: "Change Volume"
+ </source>
+ <dest>
+ *: "Sesi DeÄŸiÅŸtir"
+ </dest>
+ <voice>
+ *: "Sesi DeÄŸiÅŸtir"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_CHANGE_SHUFFLE_MODE
+ desc: in playback control menu
+ user: core
+ <source>
+ *: "Shuffle Mode"
+ </source>
+ <dest>
+ *: "Karıştırma modu"
+ </dest>
+ <voice>
+ *: "Karıştırma modu"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_CHANGE_REPEAT_MODE
+ desc: in playback control menu
+ user: core
+ <source>
+ *: "Change Repeat Mode"
+ </source>
+ <dest>
+ *: "Tekrar Modunu DeÄŸiÅŸtir"
+ </dest>
+ <voice>
+ *: "Tekrar Modunu DeÄŸiÅŸtir"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PLAYBACK_CONTROL
+ desc: in playback control menu
+ user: core
+ <source>
+ *: "Playback Control"
+ </source>
+ <dest>
+ *: "Oynatma Kontrolü"
+ </dest>
+ <voice>
+ *: "Oynatma Kontrolü"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_CHESSBOX_CHECKMATE
+ desc: in chessbox
+ user: core
+ <source>
+ *: "Checkmate!"
+ </source>
+ <dest>
+ *: "Åžah Mat!"
+ </dest>
+ <voice>
+ *: "Åžah Mat"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_CHESSBOX_ILLEGAL_MOVE
+ desc: in chessbox
+ user: core
+ <source>
+ *: "Illegal move!"
+ </source>
+ <dest>
+ *: "Geçersiz hareket!"
+ </dest>
+ <voice>
+ *: "Geçersiz hareket"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_CHESSBOX_MENU_NEW_GAME
+ desc: in the chessbox menu
+ user: core
+ <source>
+ *: "New Game"
+ </source>
+ <dest>
+ *: "Yeni oyun"
+ </dest>
+ <voice>
+ *: "Yeni oyun"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_CHESSBOX_MENU_RESUME_GAME
+ desc: in the chessbox menu
+ user: core
+ <source>
+ *: "Resume Game"
+ </source>
+ <dest>
+ *: "Oyuna devam et"
+ </dest>
+ <voice>
+ *: "Oyuna devam et"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_CHESSBOX_MENU_SAVE_GAME
+ desc: in the chessbox menu
+ user: core
+ <source>
+ *: "Save Game"
+ </source>
+ <dest>
+ *: "Oyunu kaydet"
+ </dest>
+ <voice>
+ *: "Oyunu kaydet"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_CHESSBOX_MENU_RESTORE_GAME
+ desc: in the chessbox menu
+ user: core
+ <source>
+ *: "Restore Game"
+ </source>
+ <dest>
+ *: "Oyunu yenile"
+ </dest>
+ <voice>
+ *: "Oyunu yenile"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_CHESSBOX_MENU_RESTART_GAME
+ desc: in the chessbox menu
+ user: core
+ <source>
+ *: "Restart Game"
+ </source>
+ <dest>
+ *: "Oyunu yeniden baÅŸlat"
+ </dest>
+ <voice>
+ *: "Oyunu yeniden baÅŸlat"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_CHESSBOX_MENU_SELECT_OTHER_GAME
+ desc: in the chessbox menu
+ user: core
+ <source>
+ *: "Select Other Game"
+ </source>
+ <dest>
+ *: "Diğer Oyunu Seç"
+ </dest>
+ <voice>
+ *: "Diğer Oyunu Seç"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_CHESSBOX_LEVEL_1
+ desc: in the chessbox game level selection
+ user: core
+ <source>
+ *: "Level 1: 60 moves / 5 min"
+ </source>
+ <dest>
+ *: "Seviye 1: 60 hamle / 5 dk."
+ </dest>
+ <voice>
+ *: "Seviye 1 5 dakikada 60 hamle"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_CHESSBOX_LEVEL_2
+ desc: in the chessbox game level selection
+ user: core
+ <source>
+ *: "Level 2: 60 moves / 15 min"
+ </source>
+ <dest>
+ *: "Seviye 2: 60 hamle / 15 dk."
+ </dest>
+ <voice>
+ *: "Seviye 2 15 dakikada 60 hamle"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_CHESSBOX_LEVEL_3
+ desc: in the chessbox game level selection
+ user: core
+ <source>
+ *: "Level 3: 60 moves / 30 min"
+ </source>
+ <dest>
+ *: "Seviye 3: 60 hamle / 30 dk."
+ </dest>
+ <voice>
+ *: "Seviye 3 30 dakikada 60 hamle"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_CHESSBOX_LEVEL_4
+ desc: in the chessbox game level selection
+ user: core
+ <source>
+ *: "Level 4: 40 moves / 30 min"
+ </source>
+ <dest>
+ *: "Seviye 4: 40 hamle / 30 dk."
+ </dest>
+ <voice>
+ *: "4. Seviye 30 dakikada 40 hamle"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_CHESSBOX_LEVEL_5
+ desc: in the chessbox game level selection
+ user: core
+ <source>
+ *: "Level 5: 40 moves / 60 min"
+ </source>
+ <dest>
+ *: "Seviye 5: 40 hamle / 60 dk."
+ </dest>
+ <voice>
+ *: "Seviye 5 60 dakikada 40 hamle"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_CHESSBOX_LEVEL_6
+ desc: in the chessbox game level selection
+ user: core
+ <source>
+ *: "Level 6: 40 moves / 120 min"
+ </source>
+ <dest>
+ *: "Seviye 6: 40 hamle / 120 dk."
+ </dest>
+ <voice>
+ *: "Seviye 6 120 dakikada 40 hamle"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_CHESSBOX_LEVEL_7
+ desc: in the chessbox game level selection
+ user: core
+ <source>
+ *: "Level 7: 40 moves / 240 min"
+ </source>
+ <dest>
+ *: "Seviye 7: 40 hamle / 240 dk."
+ </dest>
+ <voice>
+ *: "7. Seviye 240 dakikada 40 hamle"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_CHESSBOX_LEVEL_8
+ desc: in the chessbox game level selection
+ user: core
+ <source>
+ *: "Level 8: 1 move / 15 min"
+ </source>
+ <dest>
+ *: "Seviye 8: 1 hamle / 15 dk."
+ </dest>
+ <voice>
+ *: "Seviye 8 15 dakikada 1 hamle"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_CHESSBOX_LEVEL_9
+ desc: in the chessbox game level selection
+ user: core
+ <source>
+ *: "Level 9: 1 move / 60 min"
+ </source>
+ <dest>
+ *: "Seviye 9: 1 hamle / 60 dk."
+ </dest>
+ <voice>
+ *: "Seviye 9 60 dakikada 1 hamle"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_CHESSBOX_LEVEL_10
+ desc: in the chessbox game level selection
+ user: core
+ <source>
+ *: "Level 10: 1 move / 600 min"
+ </source>
+ <dest>
+ *: "Seviye 10: 1 hamle / 600 dk."
+ </dest>
+ <voice>
+ *: "Seviye 10: 600 dakikada 1 hamle"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_CHESSBOX_PGN_PARSE_ERROR
+ desc: in the chessbox game viewer
+ user: core
+ <source>
+ *: "Error parsing game !"
+ </source>
+ <dest>
+ *: "Oyun ayrıştırılırken hata oluştu!"
+ </dest>
+ <voice>
+ *: "Oyun ayrıştırılırken hata oluştu"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_CHESSBOX_NO_GAMES
+ desc: in the chessbox game viewer
+ user: core
+ <source>
+ *: "No games found !"
+ </source>
+ <dest>
+ *: "Oyun bulunamadı!"
+ </dest>
+ <voice>
+ *: "Oyun bulunamadı!"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_CHESSBOX_GAME_BEGINNING
+ desc: in the chessbox game viewer
+ user: core
+ <source>
+ *: "At the beginning of the game"
+ </source>
+ <dest>
+ *: "Oyunun başında"
+ </dest>
+ <voice>
+ *: "Oyunun başında"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_CHESSBOX_GAME_END
+ desc: in the chessbox game viewer
+ user: core
+ <source>
+ *: "At the end of the game"
+ </source>
+ <dest>
+ *: "oyunun sonunda"
+ </dest>
+ <voice>
+ *: "oyunun sonunda"
+ </voice>
+</phrase>
+<phrase>
+ id: VOICE_PLAYER
+ desc: spoken only, for announcing player's id
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: "Oyuncu"
+ </voice>
+</phrase>
+<phrase>
+ id: VOICE_GNUCHESS
+ desc: spoken only, for announcing player's id
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: "GNU Satranç"
+ </voice>
+</phrase>
+<phrase>
+ id: VOICE_MARKED
+ desc: spoken only, for announcing chess piece marking
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: "Ä°ÅŸaretlenmiÅŸ"
+ </voice>
+</phrase>
+<phrase>
+ id: VOICE_UNMARKED
+ desc: spoken only, for announcing chess piece unmarking
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: "iÅŸaretlenmemiÅŸ"
+ </voice>
+</phrase>
+<phrase>
+ id: VOICE_WHITE
+ desc: spoken only, for announcing chess piece color
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: "Beyaz"
+ </voice>
+</phrase>
+<phrase>
+ id: VOICE_BLACK
+ desc: spoken only, for announcing chess piece color
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: "Siyah"
+ </voice>
+</phrase>
+<phrase>
+ id: VOICE_CHESSBOX_CHECK
+ desc: spoken only, for announcing chess moves
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: "Kontrol"
+ </voice>
+</phrase>
+<phrase>
+ id: VOICE_CHESSBOX_CAPTURES
+ desc: spoken only, for announcing chess moves
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: "yakalar"
+ </voice>
+</phrase>
+<phrase>
+ id: VOICE_CHESSBOX_CASTLE
+ desc: spoken only, for announcing chess moves
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: "kale"
+ </voice>
+</phrase>
+<phrase>
+ id: VOICE_CHESSBOX_KINGSIDE
+ desc: spoken only, for announcing chess moves
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: "ÅžAH TARAFI"
+ </voice>
+</phrase>
+<phrase>
+ id: VOICE_CHESSBOX_QUEENSIDE
+ desc: spoken only, for announcing chess moves
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: "Vezir tarafı"
+ </voice>
+</phrase>
+<phrase>
+ id: VOICE_PAWN
+ desc: spoken only, for announcing chess piece names
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: "Piyon"
+ </voice>
+</phrase>
+<phrase>
+ id: VOICE_KNIGHT
+ desc: spoken only, for announcing chess piece names
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: "AT"
+ </voice>
+</phrase>
+<phrase>
+ id: VOICE_BISHOP
+ desc: spoken only, for announcing chess piece names
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: "Bishop"
+ </voice>
+</phrase>
+<phrase>
+ id: VOICE_ROOK
+ desc: spoken only, for announcing chess piece names
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: "Kale"
+ </voice>
+</phrase>
+<phrase>
+ id: VOICE_QUEEN
+ desc: spoken only, for announcing chess piece names
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: "VEZÄ°R"
+ </voice>
+</phrase>
+<phrase>
+ id: VOICE_KING
+ desc: spoken only, for announcing chess piece names
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: "ÅžAH"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_CHESSBOX_GAMES
+ desc: in chessbox
+ user: core
+ <source>
+ *: "Games"
+ </source>
+ <dest>
+ *: "Oyunlar"
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_CHESSBOX_SAVING_POSITION
+ desc: in chessbox
+ user: core
+ <source>
+ *: "Saving position"
+ </source>
+ <dest>
+ *: "Konum kaydediliyor"
+ </dest>
+ <voice>
+ *: "Konum kaydediliyor"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_CHESSBOX_LOADING_POSITION
+ desc: in chessbox
+ user: core
+ <source>
+ *: "Loading position"
+ </source>
+ <dest>
+ *: "Yükleme konumu"
+ </dest>
+ <voice>
+ *: "Yükleme konumu"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_CHESSBOX_THINKING
+ desc: in chessbox
+ user: core
+ <source>
+ *: "Thinking..."
+ </source>
+ <dest>
+ *: "düşün.."
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: VOICE_BATTERY_BENCH_IS_ALREADY_RUNNING
+ desc: Spoken if battery bench is already running
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: "Pil tezgahı zaten çalışıyor"
+ </voice>
+</phrase>
+<phrase>
+ id: VOICE_BAT_BENCH_KEYS
+ desc: Battery bench start up message
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: "Pil sehpasını çalıştırmak için oynat'a basın veya iptal etmek için durdurun"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_CANNOT_RESTART_PLAYBACK
+ desc: cannot restart playback splash in imageviewer
+ user: core
+ <source>
+ *: "Cannot restart playback"
+ </source>
+ <dest>
+ *: "Oynatma yeniden başlatılamıyor"
+ </dest>
+ <voice>
+ *: "Oynatma yeniden başlatılamıyor"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_ORDERED
+ desc: in the imageviewer settings menu
+ user: core
+ <source>
+ *: "Ordered"
+ </source>
+ <dest>
+ *: "sipariÅŸ edildi"
+ </dest>
+ <voice>
+ *: "sipariÅŸ edildi"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_DIFFUSION
+ desc: in the imageviewer settings menu
+ user: core
+ <source>
+ *: "Diffusion"
+ </source>
+ <dest>
+ *: "difüzyon"
+ </dest>
+ <voice>
+ *: "difüzyon"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_GRAYSCALE
+ desc: in the imageviewer settings menu
+ user: core
+ <source>
+ *: "Grayscale"
+ </source>
+ <dest>
+ *: "Gri tonlamalı"
+ </dest>
+ <voice>
+ *: "Gri tonlamalı"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_SLIDESHOW_MODE
+ desc: in the imageviewer settings menu
+ user: core
+ <source>
+ *: "Toggle Slideshow Mode"
+ </source>
+ <dest>
+ *: "Slayt Gösterisi Modunu Değiştir"
+ </dest>
+ <voice>
+ *: "Slayt Gösterisi Modunu Değiştir"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_SLIDESHOW_TIME
+ desc: in the imageviewer settings menu
+ user: core
+ <source>
+ *: "Slideshow Time"
+ </source>
+ <dest>
+ *: "Slayt Gösterisi Zamanı"
+ </dest>
+ <voice>
+ *: "Slayt Gösterisi Zamanı"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_RETURN
+ desc: in various plugin menus
+ user: core
+ <source>
+ *: "Return"
+ </source>
+ <dest>
+ *: "Dönüş"
+ </dest>
+ <voice>
+ *: "Dönüş"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_REC_DIR
+ desc: used in the info screen to show a recording dir
+ user: core
+ <source>
+ *: none
+ recording: "Recording Directory"
+ </source>
+ <dest>
+ *: none
+ recording: "Kayıt Dizini"
+ </dest>
+ <voice>
+ *: none
+ recording: "Kayıt Dizini"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_CHESSBOX_MENU_VIEW_GAMES
+ desc: in the chessbox menu
+ user: core
+ <source>
+ *: "View Played Games"
+ </source>
+ <dest>
+ *: "Oynanan Oyunları Görüntüle"
+ </dest>
+ <voice>
+ *: "Oynanan Oyunları Görüntüle"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_MENU_AUDIO_OPTIONS
+ desc: in mpegplayer menus
+ user: core
+ <source>
+ *: "Audio Options"
+ lowmem: none
+ </source>
+ <dest>
+ *: "Ses Seçenekleri"
+ lowmem: none
+ </dest>
+ <voice>
+ *: "Ses Seçenekleri"
+ lowmem: none
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_MENU_RESUME_OPTIONS
+ desc: in mpegplayer menus
+ user: core
+ <source>
+ *: "Resume Options"
+ lowmem: none
+ </source>
+ <dest>
+ *: "Devam Seçenekleri"
+ lowmem: none
+ </dest>
+ <voice>
+ *: "Devam Seçenekleri"
+ lowmem: none
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_MENU_PLAY_MODE
+ desc: in mpegplayer menus
+ user: core
+ <source>
+ *: "Play Mode"
+ lowmem: none
+ </source>
+ <dest>
+ *: "Oynatma modu"
+ lowmem: none
+ </dest>
+ <voice>
+ *: "Oynatma modu"
+ lowmem: none
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_SINGLE
+ desc: in mpegplayer menus
+ user: core
+ <source>
+ *: "Single"
+ lowmem: none
+ </source>
+ <dest>
+ *: "Tek"
+ lowmem: none
+ </dest>
+ <voice>
+ *: "Tek"
+ lowmem: none
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_USE_SOUND_SETTING
+ desc: in mpegplayer menus
+ user: core
+ <source>
+ *: "Use sound setting"
+ lowmem: none
+ </source>
+ <dest>
+ *: "Ses ayarını kullan"
+ lowmem: none
+ </dest>
+ <voice>
+ *: "Ses ayarını kullan"
+ lowmem: none
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_RESTART_PLAYBACK
+ desc: in the mpegplayer settings menu
+ user: core
+ <source>
+ *: "Play from beginning"
+ lowmem: none
+ </source>
+ <dest>
+ *: "BaÅŸtan oynat"
+ lowmem: none
+ </dest>
+ <voice>
+ *: "BaÅŸtan oynat"
+ lowmem: none
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_SET_RESUME_TIME
+ desc: in the mpegplayer settings menu
+ user: core
+ <source>
+ *: "Set resume time (min)"
+ lowmem: none
+ </source>
+ <dest>
+ *: "Devam süresini ayarla (dk)"
+ lowmem: none
+ </dest>
+ <voice>
+ *: "Devam süresini ayarla dakika olarak"
+ lowmem: none
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_DISPLAY_FPS
+ desc: in the mpegplayer and pictureflow settings menus
+ user: core
+ <source>
+ *: "Display FPS"
+ </source>
+ <dest>
+ *: "Ekran FPS'si"
+ </dest>
+ <voice>
+ *: "Ekran FPSsi"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_LIMIT_FPS
+ desc: in the mpegplayer settings menu
+ user: core
+ <source>
+ *: "Limit FPS"
+ lowmem: none
+ </source>
+ <dest>
+ *: "FPS'yi sınırla"
+ lowmem: none
+ </dest>
+ <voice>
+ *: "FPS'yi sınırla"
+ lowmem: none
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_SKIP_FRAMES
+ desc: in the mpegplayer settings menu
+ user: core
+ <source>
+ *: "Skip frames"
+ lowmem: none
+ </source>
+ <dest>
+ *: "Çerçeveleri atla"
+ lowmem: none
+ </dest>
+ <voice>
+ *: "Çerçeveleri atla"
+ lowmem: none
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_BACKLIGHT_BRIGHTNESS
+ desc: in the mpegplayer settings menu
+ user: core
+ <source>
+ *: "Backlight brightness"
+ lowmem: none
+ </source>
+ <dest>
+ *: "Arka ışık parlaklığı"
+ lowmem: none
+ </dest>
+ <voice>
+ *: "Arka ışık parlaklığı"
+ lowmem: none
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_USE_COMMON_SETTING
+ desc: in the mpegplayer settings menu
+ user: core
+ <source>
+ *: "Use common setting"
+ lowmem: none
+ </source>
+ <dest>
+ *: "Ortak ayarı kullan"
+ lowmem: none
+ </dest>
+ <voice>
+ *: "Ortak ayarı kullan"
+ lowmem: none
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_TONE_CONTROLS
+ desc: in the mpegplayer settings menu
+ user: core
+ <source>
+ *: "Tone controls"
+ lowmem: none
+ </source>
+ <dest>
+ *: "Ton kontrolleri"
+ lowmem: none
+ </dest>
+ <voice>
+ *: "Ton kontrolleri"
+ lowmem: none
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FORCE_START_MENU
+ desc: in mpegplayer menus
+ user: core
+ <source>
+ *: "Start menu"
+ lowmem: none
+ </source>
+ <dest>
+ *: "Başlangıç ​​menüsü"
+ lowmem: none
+ </dest>
+ <voice>
+ *: "Başlangıç ​​menüsü"
+ lowmem: none
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_CONDITIONAL_START_MENU
+ desc: in mpegplayer menus
+ user: core
+ <source>
+ *: "Start menu if not completed"
+ lowmem: none
+ </source>
+ <dest>
+ *: "Tamamlanmadıysa başlat menüsü"
+ lowmem: none
+ </dest>
+ <voice>
+ *: "Tamamlanmadıysa başlat menüsü"
+ lowmem: none
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_AUTO_RESUME
+ desc: in mpegplayer menus
+ user: core
+ <source>
+ *: "Resume automatically"
+ lowmem: none
+ </source>
+ <dest>
+ *: "Otomatik olarak devam ettir"
+ lowmem: none
+ </dest>
+ <voice>
+ *: "Otomatik olarak devam ettir"
+ lowmem: none
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_CLEAR_ALL_RESUMES
+ desc: in the mpegplayer settings menu
+ user: core
+ <source>
+ *: "Clear all resumes"
+ lowmem: none
+ </source>
+ <dest>
+ *: "Tüm özgeçmişleri temizle"
+ lowmem: none
+ </dest>
+ <voice>
+ *: "Tüm özgeçmişleri temizle"
+ lowmem: none
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_UNAVAILABLE
+ desc: in mpegplayer settings
+ user: core
+ <source>
+ *: "Unavailable"
+ lowmem: none
+ </source>
+ <dest>
+ *: "Kullanım dışı"
+ lowmem: none
+ </dest>
+ <voice>
+ *: "Kullanım dışı"
+ lowmem: none
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_TOGGLE_ITEM
+ desc: in main_menu_config
+ user: core
+ <source>
+ *: "Toggle Item"
+ </source>
+ <dest>
+ *: "Öğeyi Aç/Kapat"
+ </dest>
+ <voice>
+ *: "Öğeyi Aç Kapat"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_MOVE_ITEM_UP
+ desc: in main_menu_config
+ user: core
+ <source>
+ *: "Move Item Up"
+ </source>
+ <dest>
+ *: "Öğeyi Yukarı Taşı"
+ </dest>
+ <voice>
+ *: "Öğeyi Yukarı Taşı"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_MOVE_ITEM_DOWN
+ desc: in main_menu_config
+ user: core
+ <source>
+ *: "Move Item Down"
+ </source>
+ <dest>
+ *: "Öğeyi Aşağı Taşı"
+ </dest>
+ <voice>
+ *: "Öğeyi Aşağı Taşı"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_LOAD_DEFAULT_CONFIGURATION
+ desc: in main_menu_config
+ user: core
+ <source>
+ *: "Load Default Configuration"
+ </source>
+ <dest>
+ *: "Varsayılan Yapılandırmayı Yükle"
+ </dest>
+ <voice>
+ *: "Varsayılan Yapılandırmayı Yükle"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_SAVE_EXIT
+ desc: in main_menu_config
+ user: core
+ <source>
+ *: "Save and Exit"
+ </source>
+ <dest>
+ *: "Kaydet ve çık"
+ </dest>
+ <voice>
+ *: "Kaydet ve çık"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_MAIN_MENU_ORDER
+ desc: main_menu_config plugin title
+ user: core
+ <source>
+ *: "Rockbox Main Menu Order"
+ </source>
+ <dest>
+ *: "Rockbox Ana Menü Sıralaması"
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_PATH
+ desc: in properties plugin
+ user: core
+ <source>
+ *: "[Path]"
+ </source>
+ <dest>
+ *: "[YOL]"
+ </dest>
+ <voice>
+ *: "Yol"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_FILENAME
+ desc: in properties plugin
+ user: core
+ <source>
+ *: "[Filename]"
+ </source>
+ <dest>
+ *: "[Dosya adı]"
+ </dest>
+ <voice>
+ *: "Dosya adı"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_SIZE
+ desc: in properties plugin
+ user: core
+ <source>
+ *: "[Size]"
+ </source>
+ <dest>
+ *: "[BOYUT]"
+ </dest>
+ <voice>
+ *: "BOYUT"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_DATE
+ desc: in properties plugin
+ user: core
+ <source>
+ *: "[Date]"
+ </source>
+ <dest>
+ *: "[Tarih]"
+ </dest>
+ <voice>
+ *: "Tarih"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_TIME
+ desc: in properties plugin
+ user: core
+ <source>
+ *: "[Time]"
+ </source>
+ <dest>
+ *: "[Zaman]"
+ </dest>
+ <voice>
+ *: "Zaman"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_ARTIST
+ desc: in properties plugin
+ user: core
+ <source>
+ *: "[Artist]"
+ </source>
+ <dest>
+ *: "[Sanatçı]"
+ </dest>
+ <voice>
+ *: "Sanatçı"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_TITLE
+ desc: in properties plugin
+ user: core
+ <source>
+ *: "[Title]"
+ </source>
+ <dest>
+ *: "[Başlık]"
+ </dest>
+ <voice>
+ *: "Başlık"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_ALBUM
+ desc: in properties plugin
+ user: core
+ <source>
+ *: "[Album]"
+ </source>
+ <dest>
+ *: "[Albüm]"
+ </dest>
+ <voice>
+ *: "Albüm"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_DURATION
+ desc: in properties plugin
+ user: core
+ <source>
+ *: "[Duration]"
+ </source>
+ <dest>
+ *: "[Süre]"
+ </dest>
+ <voice>
+ *: "Süre"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_SUBDIRS
+ desc: in properties plugin
+ user: core
+ <source>
+ *: "[Subdirs]"
+ </source>
+ <dest>
+ *: "[Alt dizinler]"
+ </dest>
+ <voice>
+ *: "alt dizinler"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_FILES
+ desc: in properties plugin
+ user: core
+ <source>
+ *: "[Files]"
+ </source>
+ <dest>
+ *: "[Dosyalar]"
+ </dest>
+ <voice>
+ *: "Dosyalar"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_DIRECTORY_PROPERTIES
+ desc: in properties plugin
+ user: core
+ <source>
+ *: "Directory properties"
+ </source>
+ <dest>
+ *: "Dizin özellikleri"
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_FILE_PROPERTIES
+ desc: in properties plugin
+ user: core
+ <source>
+ *: "File properties"
+ </source>
+ <dest>
+ *: "Dosya özellikleri"
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_FAIL
+ desc: in properties plugin
+ user: core
+ <source>
+ *: "Failed to gather information"
+ </source>
+ <dest>
+ *: "Bilgi toplanamadı"
+ </dest>
+ <voice>
+ *: "Bilgi toplanamadı"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_SWAP_CHANNELS
+ desc: in sound_settings
+ user: core
+ <source>
+ *: "Swap Channels"
+ </source>
+ <dest>
+ *: "Kanalları Değiştir"
+ </dest>
+ <voice>
+ *: "Kanalları Değiştir"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PANNING_SEPARATION
+ desc: in mikmod settings menu
+ user: core
+ <source>
+ *: "Panning Separation"
+ lowmem: none
+ </source>
+ <dest>
+ *: "Kaydırma Ayrımı"
+ lowmem: none
+ </dest>
+ <voice>
+ *: "Kaydırma Ayrımı"
+ lowmem: none
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_REVERBERATION
+ desc: in mikmod settings menu
+ user: core
+ <source>
+ *: "Reverberation"
+ lowmem: none
+ </source>
+ <dest>
+ *: "Yankılanma ReverB"
+ lowmem: none
+ </dest>
+ <voice>
+ *: "yankılanma"
+ lowmem: none
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_INTERPOLATION
+ desc: in mikmod settings menu
+ user: core
+ <source>
+ *: "Interpolation"
+ lowmem: none
+ </source>
+ <dest>
+ *: "Ä°nterpolasyon"
+ lowmem: none
+ </dest>
+ <voice>
+ *: "Ä°nterpolasyon"
+ lowmem: none
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_MIKMOD_SURROUND
+ desc: in mikmod settings menu
+ user: core
+ <source>
+ *: "Surround"
+ lowmem: none
+ </source>
+ <dest>
+ *: "Surround (Çevresel)"
+ lowmem: none
+ </dest>
+ <voice>
+ *: "Surround Çevresel"
+ lowmem: none
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_MIKMOD_HQMIXER
+ desc: in mikmod settings menu
+ user: core
+ <source>
+ *: "HQ Mixer"
+ lowmem: none
+ </source>
+ <dest>
+ *: "HQ Mikser"
+ lowmem: none
+ </dest>
+ <voice>
+ *: "Yüksek Kaliteli Mikser"
+ lowmem: none
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_MIKMOD_SAMPLERATE
+ desc: in mikmod settings menu
+ user: core
+ <source>
+ *: "Sample Rate"
+ lowmem: none
+ </source>
+ <dest>
+ *: "Aynı oran"
+ lowmem: none
+ </dest>
+ <voice>
+ *: "Aynı oran"
+ lowmem: none
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_CPU_BOOST
+ desc: in mikmod settings menu
+ user: core
+ <source>
+ *: "CPU Boost"
+ lowmem: none
+ </source>
+ <dest>
+ *: "CPU Artışı"
+ lowmem: none
+ </dest>
+ <voice>
+ *: "CPU Artışı"
+ lowmem: none
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_SPACING
+ desc: in the pictureflow settings menu
+ user: core
+ <source>
+ *: "Spacing"
+ </source>
+ <dest>
+ *: "boÅŸluk"
+ </dest>
+ <voice>
+ *: "boÅŸluk"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_CENTRE_MARGIN
+ desc: in the pictureflow settings menu
+ user: core
+ <source>
+ *: "Centre margin"
+ </source>
+ <dest>
+ *: "Merkez kenar boÅŸluÄŸu"
+ </dest>
+ <voice>
+ *: "Merkez kenar boÅŸluÄŸu"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_NUMBER_OF_SLIDES
+ desc: in the pictureflow settings menu
+ user: core
+ <source>
+ *: "Number of slides"
+ </source>
+ <dest>
+ *: "Slayt sayısı"
+ </dest>
+ <voice>
+ *: "Slayt sayısı"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_ZOOM
+ desc: in the pictureflow settings menu
+ user: core
+ <source>
+ *: "Zoom"
+ </source>
+ <dest>
+ *: "Yakınlaştırma"
+ </dest>
+ <voice>
+ *: "Yakınlaştırma"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_SHOW_ALBUM_TITLE
+ desc: in the pictureflow settings menu
+ user: core
+ <source>
+ *: "Show album title"
+ </source>
+ <dest>
+ *: "Albüm başlığını göster"
+ </dest>
+ <voice>
+ *: "Albüm başlığını göster"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_RESIZE_COVERS
+ desc: in the pictureflow settings menu
+ user: core
+ <source>
+ *: "Resize Covers"
+ </source>
+ <dest>
+ *: "Kapakları Yeniden Boyutlandır"
+ </dest>
+ <voice>
+ *: "Kapakları Yeniden Boyutlandır"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_REBUILD_CACHE
+ desc: in the pictureflow settings menu
+ user: core
+ <source>
+ *: "Rebuild cache"
+ </source>
+ <dest>
+ *: "Önbelleği yeniden oluştu"
+ </dest>
+ <voice>
+ *: "Önbelleği yeniden oluştu"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_WPS_INTEGRATION
+ desc: in the pictureflow settings menu
+ user: core
+ <source>
+ *: "WPS Integration"
+ </source>
+ <dest>
+ *: "WPS Entegrasyonu"
+ </dest>
+ <voice>
+ *: "WPS Entegrasyonu"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_GOTO_WPS
+ desc: in the pictureflow main menu
+ user: core
+ <source>
+ *: "Go to WPS"
+ </source>
+ <dest>
+ *: "WPS'ye git"
+ </dest>
+ <voice>
+ *: "WPS'ye git"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_CLEAR_PLAYLIST
+ desc: in the pictureflow main menu
+ user: core
+ <source>
+ *: "Clear playlist"
+ </source>
+ <dest>
+ *: "Oynatma listesini temizle"
+ </dest>
+ <voice>
+ *: "Oynatma listesini temizle"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_HIDE_ALBUM_TITLE
+ desc: in the pictureflow settings
+ user: core
+ <source>
+ *: "Hide album title"
+ </source>
+ <dest>
+ *: "Albüm başlığını gizle"
+ </dest>
+ <voice>
+ *: "Albüm başlığını gizle"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_SHOW_AT_THE_BOTTOM
+ desc: in the pictureflow settings
+ user: core
+ <source>
+ *: "Show at the bottom"
+ </source>
+ <dest>
+ *: "Altta göster"
+ </dest>
+ <voice>
+ *: "Altta göster"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_SHOW_AT_THE_TOP
+ desc: in the pictureflow settings
+ user: core
+ <source>
+ *: "Show at the top"
+ </source>
+ <dest>
+ *: "En üstte göster"
+ </dest>
+ <voice>
+ *: "En üstte göster"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_DIRECT
+ desc: in the pictureflow settings
+ user: core
+ <source>
+ *: "Direct"
+ </source>
+ <dest>
+ *: "DoÄŸrudan"
+ </dest>
+ <voice>
+ *: "DoÄŸrudan"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_VIA_TRACK_LIST
+ desc: in the pictureflow settings
+ user: core
+ <source>
+ *: "Via Track list"
+ </source>
+ <dest>
+ *: "Parça listesi aracılığıyla"
+ </dest>
+ <voice>
+ *: "Parça listesi aracılığıyla"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_ALWAYS_ON
+ desc: in the pictureflow settings menu
+ user: core
+ <source>
+ *: "Always On"
+ </source>
+ <dest>
+ *: "Her zaman"
+ </dest>
+ <voice>
+ *: "Her zaman"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_NO_ALBUMART_FOUND
+ desc: in the pictureflow splash messages
+ user: core
+ <source>
+ *: "No album art found"
+ </source>
+ <dest>
+ *: "Albüm resmi bulunamadı"
+ </dest>
+ <voice>
+ *: "Albüm resmi bulunamadı"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_CACHE_REBUILT_NEXT_RESTART
+ desc: in the pictureflow splash messages
+ user: core
+ <source>
+ *: "Cache will be rebuilt on next restart"
+ </source>
+ <dest>
+ *: "Önbellek bir sonraki yeniden başlatmada yeniden oluşturulacak"
+ </dest>
+ <voice>
+ *: "Önbellek bir sonraki yeniden başlatmada yeniden oluşturulacak"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PLAYLIST_CLEARED
+ desc: in the pictureflow splash messages
+ user: core
+ <source>
+ *: "Playlist Cleared"
+ </source>
+ <dest>
+ *: "Oynatma Listesi Temizlendi"
+ </dest>
+ <voice>
+ *: "Oynatma Listesi Temizlendi"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_ADDED_TO_PLAYLIST
+ desc: in the pictureflow splash messages
+ user: core
+ <source>
+ *: "Added to playlist"
+ </source>
+ <dest>
+ *: "Oynatma listesine eklendi"
+ </dest>
+ <voice>
+ *: "Oynatma listesine eklendi"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_ERROR_WRITING_CONFIG
+ desc: in the pictureflow splash messages
+ user: core
+ <source>
+ *: "Error writing config"
+ </source>
+ <dest>
+ *: "Yapılandırma yazılırken hata oluştu"
+ </dest>
+ <voice>
+ *: "Yapılandırma yazılırken hata oluştu"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_NOT_A_VBR_FILE
+ desc: in vbrfix plugin
+ user: core
+ <source>
+ *: "Not a VBR file"
+ </source>
+ <dest>
+ *: "VBR dosyası değil"
+ </dest>
+ <voice>
+ *: "VBR dosyası değil"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FILE_ERROR
+ desc: in vbrfix plugin
+ user: core
+ <source>
+ *: "File error: %d"
+ </source>
+ <dest>
+ *: "Dosya hatası: %d"
+ </dest>
+ <voice>
+ *: "Dosya hatası"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_UPDATE_CACHE
+ desc: in pictureflow
+ user: core
+ <source>
+ *: "Update cache"
+ </source>
+ <dest>
+ *: "Önbelleği güncelle"
+ </dest>
+ <voice>
+ *: "Önbelleği güncelle"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_HIDE_ALBUM_TITLE_NEW
+ desc: in the pictureflow settings
+ user: core
+ <source>
+ *: "Hide information"
+ </source>
+ <dest>
+ *: "Bilgileri gizle"
+ </dest>
+ <voice>
+ *: "Bilgileri gizle"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_SHOW_AT_THE_BOTTOM_NEW
+ desc: in the pictureflow settings
+ user: core
+ <source>
+ *: "Show album at the bottom"
+ </source>
+ <dest>
+ *: "Albümü altta göster"
+ </dest>
+ <voice>
+ *: "Albümü altta göster"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_SHOW_AT_THE_TOP_NEW
+ desc: in the pictureflow settings
+ user: core
+ <source>
+ *: "Show album at the top"
+ </source>
+ <dest>
+ *: "Albümü en üstte göster"
+ </dest>
+ <voice>
+ *: "Albümü en üstte göster"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_SHOW_ALL_AT_THE_TOP
+ desc: in the pictureflow settings
+ user: core
+ <source>
+ *: "Show album and artist at the top"
+ </source>
+ <dest>
+ *: "Albümü ve sanatçıyı en üstte göster"
+ </dest>
+ <voice>
+ *: "Albümü ve sanatçıyı en üstte göster"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_SHOW_ALL_AT_THE_BOTTOM
+ desc: in the pictureflow settings
+ user: core
+ <source>
+ *: "Show album and artist at the bottom"
+ </source>
+ <dest>
+ *: "Albümü ve sanatçıyı altta göster"
+ </dest>
+ <voice>
+ *: "Albümü ve sanatçıyı altta göster"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_ACTION_STD_CANCEL
+ desc: standard press x to cancel string
+ user: core
+ <source>
+ *: "Press LEFT to cancel."
+ android,hifietma*,zenvision: "Press BACK to cancel."
+ cowond2,creativezenxfi2,ibassodx50,ibassodx90,mrobe500,ondavx747: "Press POWER to cancel."
+ ihifi760,ihifi960: "Double tap RETURN to cancel."
+ ihifi770,ihifi770c,ihifi800: "Press HOME to cancel."
+ iriverh10,samsungyh*: "Double tap LEFT to cancel."
+ mpiohd200: "Double tap REC to cancel."
+ mpiohd300: "Double tap MENU to cancel."
+ rx27generic: "Press VOLUME to cancel."
+ sonynwza860: "Keymaps incomplete."
+ touchscreen: "Press Middle Left to cancel."
+ vibe500: "Press PREV to cancel."
+ xduoox20,xduoox3,xduoox3ii: "Double tap HOME to cancel."
+ </source>
+ <dest>
+ *: "İptal etmek için SOL tuşuna basın."
+ android,hifietma*,zenvision: "İptal etmek için GERİ tuşuna basın."
+ cowond2,creativezenxfi2,ibassodx50,ibassodx90,mrobe500,ondavx747: "İptal etmek için GÜÇ'e basın."
+ ihifi760,ihifi960: "İptal etmek için GERİ DÖN'e iki kez dokunun."
+ ihifi770,ihifi770c,ihifi800: "İptal etmek için EV TUŞUNA basın."
+ iriverh10: "Dİptal etmek için SOL'a iki kez dokunun."
+ mpiohd200,mpiohd300: "İptal etmek için KAYIT'a iki kez dokunun."
+ rx27generic: "İptal etmek için VOLUME'a basın."
+ samsungyh*: "İptal etmek için SOL'a iki kez dokunun."
+ sonynwza860: "Tuş haritaları eksik."
+ touchscreen: "İptal etmek için Orta Sol'a basın."
+ vibe500: "İptal etmek için PREV'e basın."
+ xduoox20,xduoox3,xduoox3ii: "İptal etmek için EV tuşuna iki kez dokunun."
+ </dest>
+ <voice>
+ *: "İptal etmek için SOL tuşuna basın."
+ android,hifietma*,zenvision: "İptal etmek için GERİ tuşuna basın."
+ cowond2,creativezenxfi2,ibassodx50,ibassodx90,mrobe500,ondavx747: "İptal etmek için GÜÇ'e basın."
+ ihifi760,ihifi960: "İptal etmek için GERİ DÖN'e iki kez dokunun."
+ ihifi770,ihifi770c,ihifi800: "İptal etmek için EV TUŞUNA basın."
+ iriverh10,samsungyh*: "İptal etmek için SOL'a iki kez dokunun."
+ mpiohd200,mpiohd300: "İptal etmek için KAYIT'a iki kez dokunun."
+ rx27generic: "İptal etmek için SES TUŞUNA basın."
+ touchscreen: "İptal etmek için Orta Sol'a basın."
+ vibe500: "İptal etmek için PREV'e basın."
+ xduoox20,xduoox3,xduoox3ii: "İptal etmek için EV tuşuna iki kez dokunun."
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_DATE
+ desc: for constructing time and date announcements
+ user: core
+ <source>
+ *: "Date"
+ </source>
+ <dest>
+ *: "Tarih"
+ </dest>
+ <voice>
+ *: "Tarih"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_CLEAR_ALL
+ desc:
+ user: core
+ <source>
+ *: "Clear all"
+ </source>
+ <dest>
+ *: "Hepsini temizle"
+ </dest>
+ <voice>
+ *: "Hepsini temizle"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_CANCEL_0
+ desc: CANCEL.
+ user: core
+ <source>
+ *: "Cancel"
+ </source>
+ <dest>
+ *: "Ä°ptal et"
+ </dest>
+ <voice>
+ *: "Ä°ptal et"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_SAVE
+ desc:
+ user: core
+ <source>
+ *: "Save"
+ </source>
+ <dest>
+ *: "Kaydet"
+ </dest>
+ <voice>
+ *: "Kaydet"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_TIMEOUT
+ desc:
+ user: core
+ <source>
+ *: "Timeout"
+ </source>
+ <dest>
+ *: "Zaman aşımı"
+ </dest>
+ <voice>
+ *: "Zaman aşımı"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_TRACK
+ desc: used in track x of y constructs
+ user: core
+ <source>
+ *: "Track"
+ </source>
+ <dest>
+ *: "Track"
+ </dest>
+ <voice>
+ *: "Track"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_ELAPSED
+ desc: prefix for elapsed playtime announcement
+ user: core
+ <source>
+ *: none
+ hotkey: "Elapsed"
+ </source>
+ <dest>
+ *: none
+ hotkey: "geçen"
+ </dest>
+ <voice>
+ *: none
+ hotkey: "geçen"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_ANNOUNCEMENT_FMT
+ desc: format for wps hotkey announcement
+ user: core
+ <source>
+ *: none
+ hotkey: "Announcement format"
+ </source>
+ <dest>
+ *: none
+ hotkey: "Duyuru formatı"
+ </dest>
+ <voice>
+ *: none
+ hotkey: "Duyuru formatı"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_REMAIN
+ desc: for constructs such as number of tracks remaining etc
+ user: core
+ <source>
+ *: none
+ hotkey: "Remain"
+ </source>
+ <dest>
+ *: none
+ hotkey: "Kalan"
+ </dest>
+ <voice>
+ *: none
+ hotkey: "Kalan"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_GROUPING
+ desc:
+ user: core
+ <source>
+ *: none
+ hotkey: "Grouping"
+ </source>
+ <dest>
+ *: none
+ hotkey: "gruplama"
+ </dest>
+ <voice>
+ *: none
+ hotkey: "gruplama"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_ANNOUNCE_ON
+ desc:
+ user: core
+ <source>
+ *: none
+ hotkey: "Announce on"
+ </source>
+ <dest>
+ *: none
+ hotkey: "Duyuru"
+ </dest>
+ <voice>
+ *: none
+ hotkey: "Duyuru"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_TRACK_CHANGE
+ desc:
+ user: core
+ <source>
+ *: none
+ hotkey: "Track change"
+ </source>
+ <dest>
+ *: none
+ hotkey: "Parça Değiştirme"
+ </dest>
+ <voice>
+ *: none
+ hotkey: "Parça Değiştirme"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_HOLD_FOR_SETTINGS
+ desc:
+ user: core
+ <source>
+ *: none
+ hotkey: "Hold for settings"
+ </source>
+ <dest>
+ *: none
+ hotkey: "Ayarlar için basılı tutun"
+ </dest>
+ <voice>
+ *: none
+ hotkey: "Ayarlar için basılı tutun"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_OPEN_PLUGIN
+ desc: onplay open plugin
+ user: core
+ <source>
+ *: "Open Plugin"
+ </source>
+ <dest>
+ *: "Eklentiyi Aç"
+ </dest>
+ <voice>
+ *: "Eklentiyi Aç"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_OPEN_PLUGIN_NOT_A_PLUGIN
+ desc: open plugin module
+ user: core
+ <source>
+ *: "Not a plugin: %s"
+ </source>
+ <dest>
+ *: "Eklenti deÄŸil: %s"
+ </dest>
+ <voice>
+ *: "Eklenti deÄŸil"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_OPEN_PLUGIN_SET_WPS_CONTEXT_PLUGIN
+ desc: open plugin module
+ user: core
+ <source>
+ *: "Set Wps Context Plugin"
+ </source>
+ <dest>
+ *: "Wps BaÄŸlam Eklentisini Ayarla"
+ </dest>
+ <voice>
+ *: "Wps BaÄŸlam Eklentisini Ayarla"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PARAMETER
+ desc:
+ user: core
+ <source>
+ *: "Parameter"
+ </source>
+ <dest>
+ *: "Parametre"
+ </dest>
+ <voice>
+ *: "Parametre"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_NAME
+ desc:
+ user: core
+ <source>
+ *: "Name"
+ </source>
+ <dest>
+ *: "Ä°sim"
+ </dest>
+ <voice>
+ *: "Ä°sim"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_ADD
+ desc:
+ user: core
+ <source>
+ *: "Add"
+ </source>
+ <dest>
+ *: "Ekle"
+ </dest>
+ <voice>
+ *: "Ekle"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_BACK
+ desc:
+ user: core
+ <source>
+ *: "Back"
+ </source>
+ <dest>
+ *: "Geri"
+ </dest>
+ <voice>
+ *: "Geri"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_EDIT
+ desc:
+ user: core
+ <source>
+ *: "Edit"
+ </source>
+ <dest>
+ *: "Düzenle"
+ </dest>
+ <voice>
+ *: "Düzenle"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_RUN
+ desc:
+ user: core
+ <source>
+ *: "Run"
+ </source>
+ <dest>
+ *: "Çalıştır"
+ </dest>
+ <voice>
+ *: "Çalıştır"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_EXPORT
+ desc:
+ user: core
+ <source>
+ *: "Export"
+ </source>
+ <dest>
+ *: "Dışa Aktar"
+ </dest>
+ <voice>
+ *: "Dışa Aktar"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_BROWSE
+ desc:
+ user: core
+ <source>
+ *: "Browse"
+ </source>
+ <dest>
+ *: "Göz at"
+ </dest>
+ <voice>
+ *: "Göz at"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_ENTER_USB_STORAGE_MODE_QUERY
+ desc: upon plugging in USB
+ user: core
+ <source>
+ *: "Enter USB mass storage mode?"
+ </source>
+ <dest>
+ *: "USB yığın depolama moduna girilsin mi?"
+ </dest>
+ <voice>
+ *: "USB yığın depolama moduna girilsin mi?"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_CLEAR_LIST_AND_PLAY_NEXT
+ desc: in onplay menu. Replace current playlist with selected tracks
+ user: core
+ <source>
+ *: "Clear List & Play Next"
+ </source>
+ <dest>
+ *: "Listeyi Temizle ve Sonrakini Oynat"
+ </dest>
+ <voice>
+ *: "Listeyi Temizle ve Sonrakini Oynat"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_QUEUE_MENU
+ desc: in onplay menu
+ user: core
+ <source>
+ *: "Queue..."
+ </source>
+ <dest>
+ *: "Sıra..."
+ </dest>
+ <voice>
+ *: "Sıra"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_SHOW_QUEUE_OPTIONS
+ desc: in Current Playlist settings
+ user: core
+ <source>
+ *: "Show Queue Options"
+ </source>
+ <dest>
+ *: "Sıra Seçeneklerini Göster"
+ </dest>
+ <voice>
+ *: "Sıra Seçeneklerini Göster"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_SHOW_SHUFFLED_ADDING_OPTIONS
+ desc: in Current Playlist settings
+ user: core
+ <source>
+ *: "Show Shuffled Adding Options"
+ </source>
+ <dest>
+ *: "Karışık Ekleme Seçeneklerini Göster"
+ </dest>
+ <voice>
+ *: "Karışık Ekleme Seçeneklerini Göster"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_IN_SUBMENU
+ desc: in Settings
+ user: core
+ <source>
+ *: "In Submenu"
+ </source>
+ <dest>
+ *: "Alt menüde"
+ </dest>
+ <voice>
+ *: "Alt menüde"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_CLEAR_LIST_AND_PLAY_SHUFFLED
+ desc: in onplay menu. Replace current playlist with selected tracks in random order.
+ user: core
+ <source>
+ *: "Clear List & Play Shuffled"
+ </source>
+ <dest>
+ *: "Listeyi Temizle ve Karışık Oynat"
+ </dest>
+ <voice>
+ *: "Listeyi Temizle ve Karışık Oynat"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_SOFTLOCK_DISABLE_ALL_NOTIFY
+ desc: disable all softlock notifications
+ user: core
+ <source>
+ *: "Disable All Lock Notifications"
+ </source>
+ <dest>
+ *: "Tüm Kilit Bildirimlerini Devre Dışı Bırak"
+ </dest>
+ <voice>
+ *: "Tüm Kilit Bildirimlerini Devre Dışı Bırak"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_ACTION_VOLUME
+ desc: exempt volume from softlock
+ user: core
+ <source>
+ *: "Exempt Volume"
+ </source>
+ <dest>
+ *: "Muaf Hacim"
+ </dest>
+ <voice>
+ *: "Muaf Hacim"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_ACTION_ALWAYSAUTOLOCK
+ desc: always prime autolock
+ user: core
+ <source>
+ *: "Always Autolock"
+ </source>
+ <dest>
+ *: "Daima Otomatik Kilitleme"
+ </dest>
+ <voice>
+ *: "Daima Otomatik Kilitleme"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PLAYLIST_RELOAD_AFTER_SAVE
+ desc: reload playlist after saving
+ user: core
+ <source>
+ *: "Reload After Saving"
+ </source>
+ <dest>
+ *: "Kaydettikten Sonra Yeniden Yükle"
+ </dest>
+ <voice>
+ *: "Kaydettikten Sonra Yeniden Yükle"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_ALBUMARTIST
+ desc: in properties plugin
+ user: core
+ <source>
+ *: "[Album Artist]"
+ </source>
+ <dest>
+ *: "[Albüm sanatçısı]"
+ </dest>
+ <voice>
+ *: "Albüm sanatçısı"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_GENRE
+ desc: in properties plugin
+ user: core
+ <source>
+ *: "[Genre]"
+ </source>
+ <dest>
+ *: "[Tür]"
+ </dest>
+ <voice>
+ *: "Tür"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_COMMENT
+ desc: in properties plugin
+ user: core
+ <source>
+ *: "[Comment]"
+ </source>
+ <dest>
+ *: "[Yorum]"
+ </dest>
+ <voice>
+ *: "Yorum Yap"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_COMPOSER
+ desc: in properties plugin
+ user: core
+ <source>
+ *: "[Composer]"
+ </source>
+ <dest>
+ *: "[Besteci]"
+ </dest>
+ <voice>
+ *: "Besteci"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_YEAR
+ desc: in properties plugin
+ user: core
+ <source>
+ *: "[Year]"
+ </source>
+ <dest>
+ *: "[Yıl]"
+ </dest>
+ <voice>
+ *: "Yıl"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_TRACKNUM
+ desc: in properties plugin
+ user: core
+ <source>
+ *: "[Tracknum]"
+ </source>
+ <dest>
+ *: "[Parça numarası]"
+ </dest>
+ <voice>
+ *: "Parça numarası"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_DISCNUM
+ desc: in properties plugin
+ user: core
+ <source>
+ *: "[Discnum]"
+ </source>
+ <dest>
+ *: "[Disknumarası]"
+ </dest>
+ <voice>
+ *: "Disk numarası"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_FREQUENCY
+ desc: in properties plugin
+ user: core
+ <source>
+ *: "[Frequency]"
+ </source>
+ <dest>
+ *: "[Frekans]"
+ </dest>
+ <voice>
+ *: "Frekans"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_BITRATE
+ desc: in properties plugin
+ user: core
+ <source>
+ *: "[Bitrate]"
+ </source>
+ <dest>
+ *: "[Bithızı]"
+ </dest>
+ <voice>
+ *: "bit hızı"
+ </voice>
+</phrase>
+<phrase>
+ id: VOICE_NUMERIC_TENS_SWAP_SEPARATOR
+ desc: voice only, for speaking numbers in languages that swap the tens and ones fields. Leave blank for languages that do not need it, such as English ("231" => "two hundred thirty one") but other languages may speak it as "two hundred one [AND] thirty"
+ user: core
+ <source>
+ *: ""
+ </source>
+ <dest>
+ *: ""
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_VOICED_DATE_FORMAT
+ desc: format string for how dates will be read back. Y == 4-digit year, A == month name, m == numeric month, d == numeric day. For example, "AdY" will read "January 21 2021"
+ user: core
+ <source>
+ *: "dAY"
+ </source>
+ <dest>
+ *: "Gün"
+ </dest>
+ <voice>
+ *: ""
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_LIST_WRAPAROUND
+ desc: in Settings
+ user: core
+ <source>
+ *: "List Wraparound"
+ </source>
+ <dest>
+ *: "Liste Sarma"
+ </dest>
+ <voice>
+ *: "Liste Sarma"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_SHOW_SHUTDOWN_MESSAGE
+ desc: in Settings
+ user: core
+ <source>
+ *: "Show Shutdown Message"
+ </source>
+ <dest>
+ *: "Kapatma Mesajını Göster"
+ </dest>
+ <voice>
+ *: "Kapatma Mesajını Göster"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_LIST_ORDER
+ desc: in Settings
+ user: core
+ <source>
+ *: "List Order"
+ </source>
+ <dest>
+ *: "Liste Sırası"
+ </dest>
+ <voice>
+ *: "Liste Sırası"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_ASCENDING
+ desc: in Settings
+ user: core
+ <source>
+ *: "Ascending"
+ </source>
+ <dest>
+ *: "artan"
+ </dest>
+ <voice>
+ *: "artan"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_DESCENDING
+ desc: in Settings
+ user: core
+ <source>
+ *: "Descending"
+ </source>
+ <dest>
+ *: "Azalan"
+ </dest>
+ <voice>
+ *: "Azalan"
</voice>
</phrase>
diff --git a/apps/lang/ukrainian.lang b/apps/lang/ukrainian.lang
index 8f6b5ba4e4..91603a03c0 100644
--- a/apps/lang/ukrainian.lang
+++ b/apps/lang/ukrainian.lang
@@ -5260,20 +5260,17 @@
</voice>
</phrase>
<phrase>
- id: LANG_RECORDING_FORMAT
- desc: audio format item in recording menu
+ id: LANG_FORMAT
+ desc: audio format
user: core
<source>
- *: none
- recording: "Format"
+ *: "Format"
</source>
<dest>
- *: none
- recording: "Формат"
+ *: "Формат"
</dest>
<voice>
- *: none
- recording: "Формат"
+ *: "Формат"
</voice>
</phrase>
<phrase>
@@ -10308,15 +10305,15 @@
user: core
<source>
*: none
- gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
</source>
<dest>
*: none
- gigabeatfx,sansafuzeplus: "ЧутливiÑÑ‚ÑŒ Тачпаду"
+ fiiom3k,gigabeatfx,sansafuzeplus: "ЧутливiÑÑ‚ÑŒ Тачпаду"
</dest>
<voice>
*: none
- gigabeatfx,sansafuzeplus: "ЧутливiÑÑ‚ÑŒ Тачпаду"
+ fiiom3k,gigabeatfx,sansafuzeplus: "ЧутливiÑÑ‚ÑŒ Тачпаду"
</voice>
</phrase>
<phrase>
diff --git a/apps/lang/vlaams.lang b/apps/lang/vlaams.lang
index b77da656a4..dbe9804d3a 100644
--- a/apps/lang/vlaams.lang
+++ b/apps/lang/vlaams.lang
@@ -8219,20 +8219,17 @@
</voice>
</phrase>
<phrase>
- id: LANG_RECORDING_FORMAT
- desc: audio format item in recording menu
+ id: LANG_FORMAT
+ desc: audio format
user: core
<source>
- *: none
- recording: "Format"
+ *: "Format"
</source>
<dest>
- *: none
- recording: "Bestandsformaat"
+ *: "Bestandsformaat"
</dest>
<voice>
- *: none
- recording: "Bestandsformaat"
+ *: "Bestandsformaat"
</voice>
</phrase>
<phrase>
@@ -9274,7 +9271,7 @@
*: "PLAY = Yes"
archosplayer: "(PLAY/STOP)"
cowond2*: "MENU, or top-right = Yes"
- creativezen*: "Select = Yes"
+ creativezen*: "SELECT = Yes"
gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT = Yes"
iriverh100,iriverh120,iriverh300: "NAVI = Yes"
mrobe500: "PLAY, POWER, or top-right = Yes"
@@ -9284,7 +9281,7 @@
*: "PLAY = ja"
archosplayer: "(PLAY/STOP)"
cowond2*: "MENU of rechtsboven = ja"
- creativezen*: "Select = ja"
+ creativezen*: "SELECT = ja"
gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT = ja"
iriverh100,iriverh120,iriverh300: "NAVI = ja"
mrobe500: "PLAY, POWER of rechtsboven = ja"
@@ -9294,7 +9291,7 @@
*: "PLAY = ja"
archosplayer: "(PLAY/STOP)"
cowond2*: "MENU of rechtsboven = ja"
- creativezen*: "Select = ja"
+ creativezen*: "SELECT = ja"
gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT = ja"
iriverh100,iriverh120,iriverh300: "NAVI = ja"
mrobe500: "PLAY, POWER of rechtsboven = ja"
@@ -10272,15 +10269,15 @@
user: core
<source>
*: none
- gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
</source>
<dest>
*: none
- gigabeatfx,sansafuzeplus: "Touchpadgevoeligheid"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Touchpadgevoeligheid"
</dest>
<voice>
*: none
- gigabeatfx,sansafuzeplus: "Touchpadgevoeligheid"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Touchpadgevoeligheid"
</voice>
</phrase>
<phrase>
diff --git a/apps/lang/walon.lang b/apps/lang/walon.lang
index 9fa5ed9346..82ed676828 100644
--- a/apps/lang/walon.lang
+++ b/apps/lang/walon.lang
@@ -5281,20 +5281,17 @@
</voice>
</phrase>
<phrase>
- id: LANG_RECORDING_FORMAT
- desc: audio format item in recording menu
+ id: LANG_FORMAT
+ desc: audio format
user: core
<source>
- *: none
- recording: "Format"
+ *: "Format"
</source>
<dest>
- *: none
- recording: "Cogne"
+ *: "Cogne"
</dest>
<voice>
- *: none
- recording: "Cogne"
+ *: "Cogne"
</voice>
</phrase>
<phrase>
@@ -10327,15 +10324,15 @@
user: core
<source>
*: none
- gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
</source>
<dest>
*: none
- gigabeatfx,sansafuzeplus: "Tinrûlisté do pavé-taprece"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Tinrûlisté do pavé-taprece"
</dest>
<voice>
*: none
- gigabeatfx,sansafuzeplus: "Tinrûlisté do pavé-taprece"
+ fiiom3k,gigabeatfx,sansafuzeplus: "Tinrûlisté do pavé-taprece"
</voice>
</phrase>
<phrase>
diff --git a/apps/language.c b/apps/language.c
index 7fe1258dea..d177303d89 100644
--- a/apps/language.c
+++ b/apps/language.c
@@ -37,7 +37,9 @@
/* See tools/genlang (TODO: Use common include for both) */
#define LANGUAGE_COOKIE 0x1a
#define LANGUAGE_VERSION 0x06
-#define LANGUAGE_FLAG_RTL 0x01
+
+#define LANGUAGE_FLAG_RTL 0x01
+#define LANGUAGE_FLAG_UNITS_FIRST 0x02
#define HEADER_SIZE 4
#define SUBHEADER_SIZE 6
@@ -54,8 +56,8 @@ void lang_init(const unsigned char *builtin, unsigned char **dest, int count)
}
}
-int lang_load(const char *filename, const unsigned char *builtin,
- unsigned char **dest, unsigned char *buffer,
+int lang_load(const char *filename, const unsigned char *builtin,
+ unsigned char **dest, unsigned char *buffer,
unsigned int user_num, int max_lang_size,
unsigned int max_id)
{
@@ -86,7 +88,7 @@ int lang_load(const char *filename, const unsigned char *builtin,
lang_init(builtin, dest, max_id);
lseek(fd, foffset, SEEK_SET);
read(fd, buffer, lang_size);
-
+ buffer[max_lang_size - 1] = '\0'; /* ensure buffer is null terminated */
while(lang_size>3) {
id = ((buffer[0]<<8) | buffer[1]); /* get two-byte id */
buffer += 2; /* pass the id */
@@ -100,7 +102,7 @@ int lang_load(const char *filename, const unsigned char *builtin,
while(*buffer) { /* pass the string */
lang_size--;
buffer++;
- }
+ }
lang_size-=3; /* the id and the terminating zero */
buffer++; /* pass the terminating zero-byte */
}
@@ -143,3 +145,8 @@ int lang_is_rtl(void)
{
return (lang_options & LANGUAGE_FLAG_RTL) != 0;
}
+
+int lang_units_first(void)
+{
+ return (lang_options & LANGUAGE_FLAG_UNITS_FIRST) != 0;
+}
diff --git a/apps/language.h b/apps/language.h
index cbfa7e2c1d..85fa7f6efa 100644
--- a/apps/language.h
+++ b/apps/language.h
@@ -37,4 +37,6 @@ int lang_english_to_id(const char *english);
/* returns whether the loaded language is a right-to-left language */
int lang_is_rtl(void);
+/* returns whether the loaded language needs units spoken before the value */
+int lang_units_first(void);
#endif
diff --git a/apps/logfdisp.c b/apps/logfdisp.c
index b139f30ac7..efbfa192f5 100644
--- a/apps/logfdisp.c
+++ b/apps/logfdisp.c
@@ -35,7 +35,9 @@
#include "logfdisp.h"
#include "action.h"
#include "splash.h"
-
+#if CONFIG_RTC
+#include "misc.h"
+#endif /*CONFIG_RTC*/
int compute_nb_lines(int w, struct font* font)
{
int i, nb_lines;
@@ -212,10 +214,6 @@ bool logfdisplay(void)
bool logfdump(void)
{
int fd;
-#if CONFIG_RTC
- struct tm *nowtm;
- char fname[MAX_PATH];
-#endif
splashf(HZ, "Log File Dumped");
@@ -227,11 +225,12 @@ bool logfdump(void)
logfenabled = false;
#if CONFIG_RTC
- nowtm = get_time();
- snprintf(fname, MAX_PATH, "%s/logf_%04d%02d%02d%02d%02d%02d.txt", ROCKBOX_DIR,
+ char fname[MAX_PATH];
+ struct tm *nowtm = get_time();
+ fd = open_pathfmt(fname, sizeof(fname), O_CREAT|O_WRONLY|O_TRUNC,
+ "%s/logf_%04d%02d%02d%02d%02d%02d.txt", ROCKBOX_DIR,
nowtm->tm_year + 1900, nowtm->tm_mon + 1, nowtm->tm_mday,
nowtm->tm_hour, nowtm->tm_min, nowtm->tm_sec);
- fd = open(fname, O_CREAT|O_WRONLY|O_TRUNC);
#else
fd = open(ROCKBOX_DIR "/logf.txt", O_CREAT|O_WRONLY|O_TRUNC, 0666);
#endif
diff --git a/apps/main.c b/apps/main.c
index c7b223faea..cf7d302fc9 100644
--- a/apps/main.c
+++ b/apps/main.c
@@ -21,6 +21,7 @@
#include "config.h"
#include "system.h"
+#include "version.h"
#include "gcc_extensions.h"
#include "storage.h"
#include "disk.h"
@@ -31,15 +32,15 @@
#include "led.h"
#include "../kernel-internal.h"
#include "button.h"
+#include "core_keymap.h"
#include "tree.h"
#include "filetypes.h"
#include "panic.h"
#include "menu.h"
#include "usb.h"
+#include "wifi.h"
#include "powermgmt.h"
-#if !defined(DX50) && !defined(DX90)
#include "adc.h"
-#endif
#include "i2c.h"
#ifndef DEBUG
#include "serial.h"
@@ -70,13 +71,13 @@
#include "string.h"
#include "splash.h"
#include "eeprom_settings.h"
-#include "scrobbler.h"
#include "icon.h"
#include "viewport.h"
#include "skin_engine/skin_engine.h"
#include "statusbar-skinned.h"
#include "bootchart.h"
#include "logdiskf.h"
+#include "bootdata.h"
#if (CONFIG_PLATFORM & PLATFORM_ANDROID)
#include "notification.h"
#endif
@@ -168,7 +169,7 @@ int main(void)
screens[i].update();
}
list_init();
- tree_gui_init();
+ tree_init();
/* Keep the order of this 3
* Must be done before any code uses the multi-screen API */
#ifdef HAVE_USBSTACK
@@ -176,6 +177,15 @@ int main(void)
usb_start_monitoring();
#endif
+#if !defined(DISABLE_ACTION_REMAP) && defined(CORE_KEYREMAP_FILE)
+ if (file_exists(CORE_KEYREMAP_FILE))
+ {
+ int mapct = core_load_key_remap(CORE_KEYREMAP_FILE);
+ if (mapct <= 0)
+ splashf(HZ, "key remap failed: %d, %s", mapct, CORE_KEYREMAP_FILE);
+ }
+#endif
+
#ifdef AUTOROCK
{
char filename[MAX_PATH];
@@ -200,6 +210,50 @@ int main(void)
root_menu();
}
+/* The disk isn't ready at boot, rblogo is stored in bin and erased after boot */
+int show_logo_boot( void ) INIT_ATTR;
+int show_logo_boot( void )
+{
+ unsigned char version[32];
+ int font_h, ver_w;
+ snprintf(version, sizeof(version), "Ver. %s", rbversion);
+ ver_w = font_getstringsize(version, NULL, &font_h, FONT_SYSFIXED);
+ lcd_clear_display();
+ lcd_setfont(FONT_SYSFIXED);
+#if defined(SANSA_CLIP) || defined(SANSA_CLIPV2) || defined(SANSA_CLIPPLUS)
+ /* display the logo in the blue area of the screen (bottom 48 pixels) */
+ if (ver_w > LCD_WIDTH)
+ lcd_putsxy(0, 0, rbversion);
+ else
+ lcd_putsxy((LCD_WIDTH/2) - (ver_w/2), 0, version);
+ lcd_bmp(&bm_rockboxlogo, (LCD_WIDTH - BMPWIDTH_rockboxlogo) / 2, 16);
+#else
+ lcd_bmp(&bm_rockboxlogo, (LCD_WIDTH - BMPWIDTH_rockboxlogo) / 2, 10);
+ if (ver_w > LCD_WIDTH)
+ lcd_putsxy(0, LCD_HEIGHT-font_h, rbversion);
+ else
+ lcd_putsxy((LCD_WIDTH/2) - (ver_w/2), LCD_HEIGHT-font_h, version);
+#endif
+ lcd_setfont(FONT_UI);
+ lcd_update();
+#ifdef HAVE_REMOTE_LCD
+ lcd_remote_clear_display();
+ lcd_remote_bmp(&bm_remote_rockboxlogo, 0, 10);
+ lcd_remote_setfont(FONT_SYSFIXED);
+ if (ver_w > LCD_REMOTE_WIDTH)
+ lcd_remote_putsxy(0, LCD_REMOTE_HEIGHT-font_h, rbversion);
+ else
+ lcd_remote_putsxy((LCD_REMOTE_WIDTH/2) - (ver_w/2),
+ LCD_REMOTE_HEIGHT-font_h, version);
+ lcd_remote_setfont(FONT_UI);
+ lcd_remote_update();
+#endif
+#ifdef SIMULATOR
+ sleep(HZ); /* sim is too fast to see logo */
+#endif
+ return 0;
+}
+
#ifdef HAVE_DIRCACHE
static int INIT_ATTR init_dircache(bool preinit)
{
@@ -233,7 +287,7 @@ static int INIT_ATTR init_dircache(bool preinit)
splash(0, str(LANG_SCANNING_DISK));
dircache_wait();
backlight_on();
- show_logo();
+ show_logo_boot();
}
struct dircache_info info;
@@ -280,13 +334,15 @@ static void init_tagcache(void)
#endif
if (lang_is_rtl())
{
- splashf(0, "[%d/%d] %s", ret, tagcache_get_max_commit_step(),
- str(LANG_TAGCACHE_INIT));
+ splash_progress(ret, tagcache_get_max_commit_step(),
+ "[%d/%d] %s", ret, tagcache_get_max_commit_step(),
+ str(LANG_TAGCACHE_INIT));
}
else
{
- splashf(0, "%s [%d/%d]", str(LANG_TAGCACHE_INIT), ret,
- tagcache_get_max_commit_step());
+ splash_progress(ret, tagcache_get_max_commit_step(),
+ "%s [%d/%d]", str(LANG_TAGCACHE_INIT), ret,
+ tagcache_get_max_commit_step());
}
clear = true;
}
@@ -297,7 +353,7 @@ static void init_tagcache(void)
if (clear)
{
backlight_on();
- show_logo();
+ show_logo_boot();
}
}
#endif /* HAVE_TAGCACHE */
@@ -320,7 +376,7 @@ static void init(void)
FOR_NB_SCREENS(i)
global_status.font_id[i] = FONT_SYSFIXED;
font_init();
- show_logo();
+ show_logo_boot();
button_init();
powermgmt_init();
backlight_init();
@@ -364,9 +420,6 @@ static void init(void)
playlist_init();
shortcuts_init();
- if (global_settings.audioscrobbler)
- scrobbler_init();
-
audio_init();
talk_announce_voice_invalid(); /* notify user w/ voice prompt if voice file invalid */
settings_apply_skins();
@@ -393,6 +446,10 @@ static void init(void)
core_allocator_init();
kernel_init();
+#if defined(HAVE_BOOTDATA) && !defined(BOOTLOADER)
+ verify_boot_data();
+#endif
+
/* early early early! */
filesystem_init();
@@ -427,7 +484,7 @@ static void init(void)
settings_reset();
CHART(">show_logo");
- show_logo();
+ show_logo_boot();
CHART("<show_logo");
lang_init(core_language_builtin, language_strings,
LANG_LAST_INDEX_IN_ARRAY);
@@ -443,11 +500,6 @@ static void init(void)
#if CONFIG_RTC
rtc_init();
#endif
-#ifdef HAVE_RTC_RAM
- CHART(">settings_load(RTC)");
- settings_load(SETTINGS_RTC); /* early load parts of global_settings */
- CHART("<settings_load(RTC)");
-#endif
adc_init();
@@ -566,27 +618,28 @@ static void init(void)
pcm_init();
dsp_init();
-#if defined(SETTINGS_RESET) || (CONFIG_KEYPAD == IPOD_4G_PAD) || \
+ CHART(">settings_load(ALL)");
+ settings_load(SETTINGS_ALL);
+ CHART("<settings_load(ALL)");
+
+#if defined(BUTTON_REC) || \
+ (CONFIG_KEYPAD == GIGABEAT_PAD) || \
+ (CONFIG_KEYPAD == IPOD_4G_PAD) || \
(CONFIG_KEYPAD == IRIVER_H10_PAD)
+ if (global_settings.clear_settings_on_hold &&
#ifdef SETTINGS_RESET
/* Reset settings if holding the reset button. (Rec on Archos,
A on Gigabeat) */
- if ((button_status() & SETTINGS_RESET) == SETTINGS_RESET)
+ ((button_status() & SETTINGS_RESET) == SETTINGS_RESET))
#else
/* Reset settings if the hold button is turned on */
- if (button_hold())
+ (button_hold()))
#endif
{
splash(HZ*2, str(LANG_RESET_DONE_CLEAR));
settings_reset();
}
- else
#endif
- {
- CHART(">settings_load(ALL)");
- settings_load(SETTINGS_ALL);
- CHART("<settings_load(ALL)");
- }
#ifdef HAVE_DIRCACHE
CHART(">init_dircache(true)");
@@ -594,7 +647,7 @@ static void init(void)
CHART("<init_dircache(true)");
#ifdef HAVE_TAGCACHE
if (rc < 0)
- remove(TAGCACHE_STATEFILE);
+ tagcache_remove_statefile();
#endif /* HAVE_TAGCACHE */
#endif /* HAVE_DIRCACHE */
@@ -626,9 +679,6 @@ static void init(void)
tree_mem_init();
filetype_init();
- if (global_settings.audioscrobbler)
- scrobbler_init();
-
shortcuts_init();
CHART(">audio_init");
@@ -636,6 +686,10 @@ static void init(void)
CHART("<audio_init");
talk_announce_voice_invalid(); /* notify user w/ voice prompt if voice file invalid */
+#ifdef HAVE_WIFI
+ wifi_init();
+#endif
+
/* runtime database has to be initialized after audio_init() */
cpu_boost(false);
diff --git a/apps/menu.c b/apps/menu.c
index 6279ec10a6..df2284be12 100644
--- a/apps/menu.c
+++ b/apps/menu.c
@@ -67,8 +67,14 @@ static int current_subitems[MAX_MENU_SUBITEMS];
static int current_subitems_count = 0;
static int talk_menu_item(int selected_item, void *data);
+struct menu_data_t
+{
+ const struct menu_item_ex *menu;
+ int selected;
+};
+
static void get_menu_callback(const struct menu_item_ex *m,
- menu_callback_type *menu_callback)
+ menu_callback_type *menu_callback)
{
if (m->flags&(MENU_HAS_DESC|MENU_DYNAMIC_DESC))
*menu_callback= m->callback_and_desc->menu_callback;
@@ -76,6 +82,19 @@ static void get_menu_callback(const struct menu_item_ex *m,
*menu_callback = m->menu_callback;
}
+static bool query_audio_status(int *old_audio_status)
+{
+ bool redraw_list = false;
+ /* query audio status to see if it changed */
+ int new_audio_status = audio_status();
+ if (*old_audio_status != new_audio_status)
+ { /* force a redraw if anything changed the audio status from outside */
+ *old_audio_status = new_audio_status;
+ redraw_list = true;
+ }
+ return redraw_list;
+}
+
static int get_menu_selection(int selected_item, const struct menu_item_ex *menu)
{
int type = (menu->flags&MENU_TYPE_MASK);
@@ -128,8 +147,7 @@ static const char* get_menu_item_name(int selected_item,
type = (menu->flags&MENU_TYPE_MASK);
if ((type == MT_SETTING) || (type == MT_SETTING_W_TEXT))
{
- const struct settings_list *v
- = find_setting(menu->variable, NULL);
+ const struct settings_list *v = find_setting(menu->variable);
if (v)
return str(v->lang_id);
else return "Not Done yet!";
@@ -158,29 +176,29 @@ static enum themable_icons menu_get_icon(int selected_item, void * data)
if (menu_icon == Icon_NOICON)
{
- switch (menu->flags&MENU_TYPE_MASK)
- {
- case MT_SETTING:
- case MT_SETTING_W_TEXT:
- menu_icon = Icon_Menu_setting;
- break;
- case MT_MENU:
- menu_icon = Icon_Submenu;
- break;
- case MT_FUNCTION_CALL:
- case MT_RETURN_VALUE:
- menu_icon = Icon_Menu_functioncall;
- break;
- }
+ unsigned int flags = (menu->flags&MENU_TYPE_MASK);
+ if(flags == MT_MENU)
+ menu_icon = Icon_Submenu;
+ else if (flags == MT_SETTING || flags == MT_SETTING_W_TEXT)
+ menu_icon = Icon_Menu_setting;
+ else if (flags == MT_FUNCTION_CALL || flags == MT_RETURN_VALUE)
+ menu_icon = Icon_Menu_functioncall;
}
return menu_icon;
}
-static void init_menu_lists(const struct menu_item_ex *menu,
+static int init_menu_lists(const struct menu_item_ex *menu,
struct gui_synclist *lists, int selected, bool callback,
struct viewport parent[NB_SCREENS])
{
+ if (!menu || !lists)
+ {
+ panicf("init_menu_lists, NULL pointer");
+ return 0;
+ }
+
int i;
+ int start_action = ACTION_ENTER_MENUITEM;
int count = MIN(MENU_GET_COUNT(menu->flags), MAX_MENU_SUBITEMS);
int type = (menu->flags&MENU_TYPE_MASK);
menu_callback_type menu_callback = NULL;
@@ -242,12 +260,13 @@ static void init_menu_lists(const struct menu_item_ex *menu,
if(global_settings.talk_menu)
gui_synclist_set_voice_callback(lists, talk_menu_item);
gui_synclist_set_nb_items(lists,current_subitems_count);
- gui_synclist_limit_scroll(lists,true);
gui_synclist_select_item(lists, find_menu_selection(selected));
get_menu_callback(menu,&menu_callback);
if (callback && menu_callback)
- menu_callback(ACTION_ENTER_MENUITEM, menu, lists);
+ start_action = menu_callback(start_action, menu, lists);
+
+ return start_action;
}
static int talk_menu_item(int selected_item, void *data)
@@ -314,10 +333,7 @@ void do_setting_screen(const struct settings_list *setting, const char * title,
if (setting->flags&F_PADTITLE)
{
int i = 0, len;
- if (setting->lang_id == -1)
- title = (char*)setting->cfg_vals;
- else
- title = P2STR((unsigned char*)title);
+ title = P2STR((unsigned char*)title);
len = strlen(title);
while (i < MAX_PATH-1)
{
@@ -334,19 +350,24 @@ void do_setting_screen(const struct settings_list *setting, const char * title,
option_screen((struct settings_list *)setting, parent,
setting->flags&F_TEMPVAR, (char*)title);
}
-
+
void do_setting_from_menu(const struct menu_item_ex *temp,
struct viewport parent[NB_SCREENS])
{
char *title;
- int setting_id;
- const struct settings_list *setting =
- find_setting(temp->variable, &setting_id);
- if (temp && ((temp->flags&MENU_TYPE_MASK) == MT_SETTING_W_TEXT))
+ if (!temp)
+ {
+ panicf("do_setting_from_menu, NULL pointer");
+ return;
+ }
+ const struct settings_list *setting = find_setting(temp->variable);
+
+ if ((temp->flags&MENU_TYPE_MASK) == MT_SETTING_W_TEXT)
title = temp->callback_and_desc->desc;
else
title = ID2P(setting->lang_id);
+
do_setting_screen(setting, title, parent);
}
@@ -355,33 +376,39 @@ int do_menu(const struct menu_item_ex *start_menu, int *start_selected,
struct viewport parent[NB_SCREENS], bool hide_theme)
{
int selected = start_selected? *start_selected : 0;
+ int ret = 0;
int action;
+ int start_action;
struct gui_synclist lists;
const struct menu_item_ex *temp = NULL;
const struct menu_item_ex *menu = start_menu;
- int ret = 0;
+
+ bool in_stringlist, done = false;
bool redraw_lists;
+
int old_audio_status = audio_status();
#ifdef HAVE_TOUCHSCREEN
/* plugins possibly have grid mode active. force global settings in lists */
enum touchscreen_mode tsm = touchscreen_get_mode();
+ enum touchscreen_mode old_global_mode = global_settings.touch_mode;
touchscreen_set_mode(global_settings.touch_mode);
#endif
FOR_NB_SCREENS(i)
viewportmanager_theme_enable(i, !hide_theme, NULL);
- const struct menu_item_ex *menu_stack[MAX_MENUS];
- int menu_stack_selected_item[MAX_MENUS];
+ struct menu_data_t mstack[MAX_MENUS]; /* menu, selected */
int stack_top = 0;
- bool in_stringlist, done = false;
+
struct viewport *vps = NULL;
menu_callback_type menu_callback = NULL;
/* if hide_theme is true, assume parent has been fixed before passed into
- * this function, e.g. with viewport_set_defaults(parent, screen) */
- init_menu_lists(menu, &lists, selected, true, parent);
+ * this function, e.g. with viewport_set_defaults(parent, screen)
+ * start_action allows an action to be processed
+ * by menu logic by bypassing get_action on the initial run */
+ start_action = init_menu_lists(menu, &lists, selected, true, parent);
vps = *(lists.parent);
in_stringlist = ((menu->flags&MENU_TYPE_MASK) == MT_RETURN_ID);
/* load the callback, and only reload it if menu changes */
@@ -389,54 +416,56 @@ int do_menu(const struct menu_item_ex *start_menu, int *start_selected,
gui_synclist_draw(&lists);
gui_synclist_speak_item(&lists);
+
while (!done)
{
- int new_audio_status;
- redraw_lists = false;
keyclick_set_callback(gui_synclist_keyclick_callback, &lists);
- action = get_action(CONTEXT_MAINMENU|ALLOW_SOFTLOCK,
- list_do_action_timeout(&lists, HZ));
- /* query audio status to see if it changed */
- new_audio_status = audio_status();
- if (old_audio_status != new_audio_status)
- { /* force a redraw if anything changed the audio status
- * from outside */
- redraw_lists = true;
- old_audio_status = new_audio_status;
+ if (UNLIKELY(start_action != ACTION_ENTER_MENUITEM))
+ {
+ action = start_action;
+ start_action = ACTION_ENTER_MENUITEM;
}
- /* HZ so the status bar redraws corectly */
+ else
+ action = get_action(CONTEXT_MAINMENU|ALLOW_SOFTLOCK,
+ list_do_action_timeout(&lists, HZ));
+ /* HZ so the status bar redraws corectly */
+
+ /* query audio status to see if it changed */
+ redraw_lists = query_audio_status(&old_audio_status);
if (menu_callback)
{
- int old_action = action;
- action = menu_callback(action, menu, &lists);
- if (action == ACTION_EXIT_AFTER_THIS_MENUITEM)
- {
- action = old_action;
- ret = MENU_SELECTED_EXIT; /* will exit after returning
- from selection */
- }
- else if (action == ACTION_REDRAW)
- {
- action = old_action;
+ int new_action = menu_callback(action, menu, &lists);
+ if (new_action == ACTION_EXIT_AFTER_THIS_MENUITEM)
+ ret = MENU_SELECTED_EXIT; /* exit after return from selection */
+ else if (new_action == ACTION_REDRAW)
redraw_lists = true;
- }
+ else
+ action = new_action;
}
- if (gui_synclist_do_button(&lists, &action, LIST_WRAP_UNLESS_HELD))
+ if (LIKELY(gui_synclist_do_button(&lists, &action)))
continue;
#ifdef HAVE_QUICKSCREEN
else if (action == ACTION_STD_QUICKSCREEN)
{
- if (global_settings.shortcuts_replaces_qs)
+ if (global_settings.shortcuts_replaces_qs ||
+ quick_screen_quick(action) == QUICKSCREEN_GOTO_SHORTCUTS_MENU)
{
+ int last_screen = global_status.last_screen;
global_status.last_screen = GO_TO_SHORTCUTMENU;
- ret = quick_screen_quick(action);
- done = true;
+ int shortcut_ret = do_shortcut_menu(NULL);
+ if (shortcut_ret == GO_TO_PREVIOUS)
+ global_status.last_screen = last_screen;
+ else
+ {
+ ret = shortcut_ret;
+ done = true;
+ }
}
- else
- quick_screen_quick(action);
+ if (!done)
+ init_menu_lists(menu, &lists, lists.selected_item, false, vps);
redraw_lists = true;
}
#endif
@@ -487,20 +516,22 @@ int do_menu(const struct menu_item_ex *start_menu, int *start_selected,
ID2P(LANG_RIGHT_QS_ITEM),
ID2P(LANG_ADD_TO_FAVES));
#endif
- MENUITEM_STRINGLIST(notquickscreen_able_option,
+ MENUITEM_STRINGLIST(notquickscreen_able_option,
ID2P(LANG_ONPLAY_MENU_TITLE), NULL,
ID2P(LANG_RESET_SETTING));
- const struct menu_item_ex *menu;
- int menu_selection = 0;
- const struct settings_list *setting =
- find_setting(temp->variable, NULL);
+ const struct menu_item_ex *context_menu;
+ const struct settings_list *setting =
+ find_setting(temp->variable);
#ifdef HAVE_QUICKSCREEN
if (is_setting_quickscreenable(setting))
- menu = &quickscreen_able_option;
+ context_menu = &quickscreen_able_option;
else
#endif
- menu = &notquickscreen_able_option;
- switch (do_menu(menu, &menu_selection, NULL, false))
+ context_menu = &notquickscreen_able_option;
+
+ int msel = do_menu(context_menu, NULL, NULL, false);
+
+ switch (msel)
{
case GO_TO_PREVIOUS:
break;
@@ -511,16 +542,16 @@ int do_menu(const struct menu_item_ex *start_menu, int *start_selected,
break;
#ifdef HAVE_QUICKSCREEN
case 1: /* set as top QS item */
- set_as_qs_item(setting, QUICKSCREEN_TOP);
+ global_settings.qs_items[QUICKSCREEN_TOP] = setting;
break;
case 2: /* set as left QS item */
- set_as_qs_item(setting, QUICKSCREEN_LEFT);
+ global_settings.qs_items[QUICKSCREEN_LEFT] = setting;
break;
case 3: /* set as bottom QS item */
- set_as_qs_item(setting, QUICKSCREEN_BOTTOM);
+ global_settings.qs_items[QUICKSCREEN_BOTTOM] = setting;
break;
case 4: /* set as right QS item */
- set_as_qs_item(setting, QUICKSCREEN_RIGHT);
+ global_settings.qs_items[QUICKSCREEN_RIGHT] = setting;
break;
case 5: /* Add to faves. Same limitation on which can be
added to the shortcuts menu as the quickscreen */
@@ -542,10 +573,12 @@ int do_menu(const struct menu_item_ex *start_menu, int *start_selected,
}
else if (action == ACTION_STD_CANCEL)
{
- bool exiting_menu = false;
- in_stringlist = false;
/* might be leaving list, so stop scrolling */
gui_synclist_scroll_stop(&lists);
+
+ bool exiting_menu = false;
+ in_stringlist = false;
+
if (menu_callback)
menu_callback(ACTION_EXIT_MENUITEM, menu, &lists);
@@ -553,15 +586,16 @@ int do_menu(const struct menu_item_ex *start_menu, int *start_selected,
done = true;
else if ((menu->flags&MENU_TYPE_MASK) == MT_MENU)
exiting_menu = true;
+
if (stack_top > 0)
{
stack_top--;
- menu = menu_stack[stack_top];
+ menu = mstack[stack_top].menu;
+ int msel = mstack[stack_top].selected;
if (!exiting_menu && (menu->flags&MENU_EXITAFTERTHISMENU))
done = true;
else
- init_menu_lists(menu, &lists,
- menu_stack_selected_item[stack_top], false, vps);
+ init_menu_lists(menu, &lists, msel, false, vps);
redraw_lists = true;
/* new menu, so reload the callback */
get_menu_callback(menu, &menu_callback);
@@ -574,19 +608,18 @@ int do_menu(const struct menu_item_ex *start_menu, int *start_selected,
}
else if (action == ACTION_STD_OK)
{
- int type = (menu->flags&MENU_TYPE_MASK);
/* entering an item that may not be a list, so stop scrolling */
gui_synclist_scroll_stop(&lists);
+ redraw_lists = true;
+
+ int type = (menu->flags&MENU_TYPE_MASK);
selected = get_menu_selection(gui_synclist_get_sel_pos(&lists), menu);
if (type == MT_MENU)
temp = menu->submenus[selected];
- else
+ else if (!in_stringlist)
type = -1;
- redraw_lists = true;
- if (in_stringlist)
- type = (menu->flags&MENU_TYPE_MASK);
- else if (temp)
+ if (!in_stringlist && temp)
{
type = (temp->flags&MENU_TYPE_MASK);
get_menu_callback(temp, &menu_callback);
@@ -602,25 +635,31 @@ int do_menu(const struct menu_item_ex *start_menu, int *start_selected,
case MT_MENU:
if (stack_top < MAX_MENUS)
{
- menu_stack[stack_top] = menu;
- menu_stack_selected_item[stack_top] = selected;
+ mstack[stack_top].menu = menu;
+ mstack[stack_top].selected = selected;
stack_top++;
menu = temp;
init_menu_lists(menu, &lists, 0, true, vps);
}
break;
+ case MT_FUNCTION_CALL_W_PARAM:
case MT_FUNCTION_CALL:
{
int return_value;
- if (temp->flags&MENU_FUNC_USEPARAM)
- return_value = temp->function->function_w_param(
- temp->function->param);
+ if (type == MT_FUNCTION_CALL_W_PARAM)
+ {
+ return_value = temp->function_param->function_w_param(
+ temp->function_param->param);
+ }
else
+ {
return_value = temp->function->function();
+ }
if (!(menu->flags&MENU_EXITAFTERTHISMENU) ||
(temp->flags&MENU_EXITAFTERTHISMENU))
{
- init_menu_lists(menu, &lists, selected, true, vps);
+ /* Reload menu but don't run the calback again FS#8117 */
+ init_menu_lists(menu, &lists, selected, false, vps);
}
if (temp->flags&MENU_FUNC_CHECK_RETVAL)
{
@@ -636,7 +675,9 @@ int do_menu(const struct menu_item_ex *start_menu, int *start_selected,
case MT_SETTING_W_TEXT:
{
do_setting_from_menu(temp, vps);
- send_event(GUI_EVENT_ACTIONUPDATE, (void*)1); /* force a redraw */
+ init_menu_lists(menu, &lists, selected, false, vps);
+ redraw_lists = true;
+
break;
}
case MT_RETURN_ID:
@@ -647,8 +688,8 @@ int do_menu(const struct menu_item_ex *start_menu, int *start_selected,
}
else if (stack_top < MAX_MENUS)
{
- menu_stack[stack_top] = menu;
- menu_stack_selected_item[stack_top] = selected;
+ mstack[stack_top].menu = menu;
+ mstack[stack_top].selected = selected;
stack_top++;
menu = temp;
init_menu_lists(menu,&lists,0,false, vps);
@@ -674,7 +715,7 @@ int do_menu(const struct menu_item_ex *start_menu, int *start_selected,
init_menu_lists(menu,&lists,selected,true,vps);
/* callback was changed, so reload the menu's callback */
get_menu_callback(menu, &menu_callback);
- if ((menu->flags&MENU_EXITAFTERTHISMENU) &&
+ if ((menu->flags&MENU_EXITAFTERTHISMENU) &&
!(temp->flags&MENU_EXITAFTERTHISMENU))
{
done = true;
@@ -706,6 +747,8 @@ int do_menu(const struct menu_item_ex *start_menu, int *start_selected,
if (menu_callback(ACTION_REDRAW, menu, &lists) != ACTION_REDRAW)
continue;
+
+ gui_synclist_set_title(&lists, lists.title, lists.title_icon);
gui_synclist_draw(&lists);
gui_synclist_speak_item(&lists);
}
@@ -717,8 +760,8 @@ int do_menu(const struct menu_item_ex *start_menu, int *start_selected,
the selected item from the menu do_menu() was called from */
if (stack_top > 0)
{
- menu = menu_stack[0];
- init_menu_lists(menu,&lists,menu_stack_selected_item[0],true, vps);
+ menu = mstack[0].menu;
+ init_menu_lists(menu,&lists,mstack[0].selected,true, vps);
}
*start_selected = get_menu_selection(
gui_synclist_get_sel_pos(&lists), menu);
@@ -727,8 +770,12 @@ int do_menu(const struct menu_item_ex *start_menu, int *start_selected,
FOR_NB_SCREENS(i)
viewportmanager_theme_undo(i, false);
#ifdef HAVE_TOUCHSCREEN
- touchscreen_set_mode(tsm);
+ /* This is needed because this function runs the settings menu and we do
+ * not want to switch back to the old mode if the user intentionally went
+ * to a different one. This is a very hacky way to do this... */
+ if(!(global_settings.touch_mode != (int)old_global_mode &&
+ tsm == old_global_mode))
+ touchscreen_set_mode(tsm);
#endif
-
return ret;
}
diff --git a/apps/menu.h b/apps/menu.h
index e56a4a149b..1f017b0c49 100644
--- a/apps/menu.h
+++ b/apps/menu.h
@@ -37,12 +37,13 @@ enum menu_item_type {
text for the setting title,
ID2P() or "literal" for the str param */
MT_FUNCTION_CALL, /* call a function from the menus */
+ MT_FUNCTION_CALL_W_PARAM, /* call a function from the menus */
MT_RETURN_ID, /* returns the position of the selected item (starting at 0)*/
MT_RETURN_VALUE, /* returns a value associated with an item */
};
#define MENU_TYPE_MASK 0xF /* MT_* type */
-struct menu_func {
+struct menu_func_param {
union {
int (*function_w_param)(void* param); /* intptr_t instead of void*
for 64bit systems */
@@ -51,6 +52,10 @@ struct menu_func {
void *param; /* passed to function_w_param */
};
+struct menu_func {
+ int (*function)(void);
+};
+
/* these next two are mutually exclusive */
#define MENU_HAS_DESC 0x10
#define MENU_DYNAMIC_DESC 0x20 /* the name of this menu item is set by the \
@@ -75,6 +80,7 @@ struct menu_item_ex {
void *variable; /* used with MT_SETTING,
must be in the settings_list.c list */
const struct menu_func *function; /* MT_FUNCTION_* */
+ const struct menu_func_param *function_param; /* MT_FUNCTION_*_W_PARAM */
const char **strings; /* used with MT_RETURN_ID */
int value; /* MT_RETURN_VALUE */
};
@@ -185,31 +191,54 @@ int do_menu(const struct menu_item_ex *menu, int *start_selected,
{ MT_RETURN_VALUE|MENU_DYNAMIC_DESC, { .value = val}, \
{.menu_get_name_and_icon = & name##_}};
-/* Use this to put a function call into the menu.
+/* Use this to put a function call expecting no arguments into the menu.
When the user selects this item the function will be run,
if MENU_FUNC_CHECK_RETVAL is set, the return value
- will be checked, returning 1 will exit do_menu();
- if MENU_FUNC_USEPARAM is set, param will be passed to the function */
-#define MENUITEM_FUNCTION(name, flags, str, func, param, \
- callback, icon) \
+ will be checked, returning 1 will exit do_menu(); */
+#define MENUITEM_FUNCTION(name, flags, str, func, callback, icon) \
static const struct menu_callback_with_desc name##_ = {callback,str,icon}; \
- static const struct menu_func name##__ = {{(void*)func}, param}; \
+ static const struct menu_func name##__ = {(void*)func}; \
/* should be const, but recording_settings wont let us do that */ \
const struct menu_item_ex name = \
{ MT_FUNCTION_CALL|MENU_HAS_DESC|flags, \
{ .function = & name##__}, {.callback_and_desc = & name##_}};
-
+
/* As above, except the text is dynamic */
-#define MENUITEM_FUNCTION_DYNTEXT(name, flags, func, param, \
+#define MENUITEM_FUNCTION_DYNTEXT(name, flags, func, \
text_callback, voice_callback, \
text_cb_data, callback, icon) \
static const struct menu_get_name_and_icon name##_ \
= {callback,text_callback,voice_callback,text_cb_data,icon}; \
- static const struct menu_func name##__ = {{(void*)func}, param}; \
- const struct menu_item_ex name = \
- { MT_FUNCTION_CALL|MENU_DYNAMIC_DESC|flags, \
+ static const struct menu_func name##__ = {(void*)func}; \
+ const struct menu_item_ex name = \
+ { MT_FUNCTION_CALL|MENU_DYNAMIC_DESC|flags, \
{ .function = & name##__}, {.menu_get_name_and_icon = & name##_}};
+/* Use this to put a function call into the menu.
+ When the user selects this item the function will be run,
+ if MENU_FUNC_CHECK_RETVAL is set, the return value
+ will be checked, returning 1 will exit do_menu();
+ param will be passed to the function */
+#define MENUITEM_FUNCTION_W_PARAM(name, flags, str, func, param, \
+ callback, icon) \
+ static const struct menu_callback_with_desc name##_ = {callback,str,icon}; \
+ static const struct menu_func_param name##__ = {{(void*)func}, param}; \
+ /* should be const, but recording_settings wont let us do that */ \
+ const struct menu_item_ex name = \
+ { MT_FUNCTION_CALL_W_PARAM|MENU_HAS_DESC|MENU_FUNC_USEPARAM|flags, \
+ { .function_param = & name##__}, {.callback_and_desc = & name##_}};
+
+/* As above, except the text is dynamic */
+#define MENUITEM_FUNCTION_DYNTEXT_W_PARAM(name, flags, func, param, \
+ text_callback, voice_callback, \
+ text_cb_data, callback, icon) \
+ static const struct menu_get_name_and_icon name##_ \
+ = {callback,text_callback,voice_callback,text_cb_data,icon}; \
+ static const struct menu_func_param name##__ = {{(void*)func}, param}; \
+ const struct menu_item_ex name = \
+ { MT_FUNCTION_CALL_W_PARAM|MENU_DYNAMIC_DESC|flags, \
+ { .function_param = & name##__}, {.menu_get_name_and_icon = & name##_}};
+
/* Use this to actually create a menu. the ... argument is a list of pointers
to any of the above macro'd variables.
(It can also have other menus in the list.) */
diff --git a/apps/menus/audiohw_eq_menu.c b/apps/menus/audiohw_eq_menu.c
index 06ab32c151..8bfd2260b0 100644
--- a/apps/menus/audiohw_eq_menu.c
+++ b/apps/menus/audiohw_eq_menu.c
@@ -80,106 +80,106 @@ static int hw_eq_do_band_setting(void *param)
return 0;
}
-MENUITEM_FUNCTION_DYNTEXT(hw_eq_band1_gain, MENU_FUNC_USEPARAM,
- hw_eq_do_band_setting,
- HW_EQ_IDX(AUDIOHW_EQ_BAND1, AUDIOHW_EQ_GAIN),
- hw_eq_get_name, hw_eq_speak_item,
- HW_EQ_IDX(AUDIOHW_EQ_BAND1, AUDIOHW_EQ_GAIN),
- NULL, Icon_Menu_setting);
+MENUITEM_FUNCTION_DYNTEXT_W_PARAM(hw_eq_band1_gain, 0,
+ hw_eq_do_band_setting,
+ HW_EQ_IDX(AUDIOHW_EQ_BAND1, AUDIOHW_EQ_GAIN),
+ hw_eq_get_name, hw_eq_speak_item,
+ HW_EQ_IDX(AUDIOHW_EQ_BAND1, AUDIOHW_EQ_GAIN),
+ NULL, Icon_Menu_setting);
#ifdef AUDIOHW_HAVE_EQ_BAND1_FREQUENCY
-MENUITEM_FUNCTION_DYNTEXT(hw_eq_band1_frequency, MENU_FUNC_USEPARAM,
- hw_eq_do_band_setting,
- HW_EQ_IDX(AUDIOHW_EQ_BAND1, AUDIOHW_EQ_FREQUENCY),
- hw_eq_get_name, hw_eq_speak_item,
- HW_EQ_IDX(AUDIOHW_EQ_BAND1, AUDIOHW_EQ_FREQUENCY),
- NULL, Icon_NOICON);
+MENUITEM_FUNCTION_DYNTEXT_W_PARAM(hw_eq_band1_frequency, 0,
+ hw_eq_do_band_setting,
+ HW_EQ_IDX(AUDIOHW_EQ_BAND1, AUDIOHW_EQ_FREQUENCY),
+ hw_eq_get_name, hw_eq_speak_item,
+ HW_EQ_IDX(AUDIOHW_EQ_BAND1, AUDIOHW_EQ_FREQUENCY),
+ NULL, Icon_NOICON);
#endif
#ifdef AUDIOHW_HAVE_EQ_BAND2
-MENUITEM_FUNCTION_DYNTEXT(hw_eq_band2_gain, MENU_FUNC_USEPARAM,
- hw_eq_do_band_setting,
- HW_EQ_IDX(AUDIOHW_EQ_BAND2, AUDIOHW_EQ_GAIN),
- hw_eq_get_name, hw_eq_speak_item,
- HW_EQ_IDX(AUDIOHW_EQ_BAND2, AUDIOHW_EQ_GAIN),
- NULL, Icon_Menu_setting);
+MENUITEM_FUNCTION_DYNTEXT_W_PARAM(hw_eq_band2_gain, 0,
+ hw_eq_do_band_setting,
+ HW_EQ_IDX(AUDIOHW_EQ_BAND2, AUDIOHW_EQ_GAIN),
+ hw_eq_get_name, hw_eq_speak_item,
+ HW_EQ_IDX(AUDIOHW_EQ_BAND2, AUDIOHW_EQ_GAIN),
+ NULL, Icon_Menu_setting);
#ifdef AUDIOHW_HAVE_EQ_BAND2_FREQUENCY
-MENUITEM_FUNCTION_DYNTEXT(hw_eq_band2_frequency, MENU_FUNC_USEPARAM,
- hw_eq_do_band_setting,
- HW_EQ_IDX(AUDIOHW_EQ_BAND2, AUDIOHW_EQ_FREQUENCY),
- hw_eq_get_name, hw_eq_speak_item,
- HW_EQ_IDX(AUDIOHW_EQ_BAND2, AUDIOHW_EQ_FREQUENCY),
- NULL, Icon_NOICON);
+MENUITEM_FUNCTION_DYNTEXT_W_PARAM(hw_eq_band2_frequency, 0,
+ hw_eq_do_band_setting,
+ HW_EQ_IDX(AUDIOHW_EQ_BAND2, AUDIOHW_EQ_FREQUENCY),
+ hw_eq_get_name, hw_eq_speak_item,
+ HW_EQ_IDX(AUDIOHW_EQ_BAND2, AUDIOHW_EQ_FREQUENCY),
+ NULL, Icon_NOICON);
#endif
#ifdef AUDIOHW_HAVE_EQ_BAND2_WIDTH
-MENUITEM_FUNCTION_DYNTEXT(hw_eq_band2_width, MENU_FUNC_USEPARAM,
- hw_eq_do_band_setting,
- HW_EQ_IDX(AUDIOHW_EQ_BAND2, AUDIOHW_EQ_WIDTH),
- hw_eq_get_name, hw_eq_speak_item,
- HW_EQ_IDX(AUDIOHW_EQ_BAND2, AUDIOHW_EQ_WIDTH),
- NULL, Icon_NOICON);
+MENUITEM_FUNCTION_DYNTEXT_W_PARAM(hw_eq_band2_width, 0,
+ hw_eq_do_band_setting,
+ HW_EQ_IDX(AUDIOHW_EQ_BAND2, AUDIOHW_EQ_WIDTH),
+ hw_eq_get_name, hw_eq_speak_item,
+ HW_EQ_IDX(AUDIOHW_EQ_BAND2, AUDIOHW_EQ_WIDTH),
+ NULL, Icon_NOICON);
#endif
#endif /* AUDIOHW_HAVE_EQ_BAND2 */
#ifdef AUDIOHW_HAVE_EQ_BAND3
-MENUITEM_FUNCTION_DYNTEXT(hw_eq_band3_gain, MENU_FUNC_USEPARAM,
- hw_eq_do_band_setting,
- HW_EQ_IDX(AUDIOHW_EQ_BAND3, AUDIOHW_EQ_GAIN),
- hw_eq_get_name, hw_eq_speak_item,
- HW_EQ_IDX(AUDIOHW_EQ_BAND3, AUDIOHW_EQ_GAIN),
- NULL, Icon_Menu_setting);
+MENUITEM_FUNCTION_DYNTEXT_W_PARAM(hw_eq_band3_gain, 0,
+ hw_eq_do_band_setting,
+ HW_EQ_IDX(AUDIOHW_EQ_BAND3, AUDIOHW_EQ_GAIN),
+ hw_eq_get_name, hw_eq_speak_item,
+ HW_EQ_IDX(AUDIOHW_EQ_BAND3, AUDIOHW_EQ_GAIN),
+ NULL, Icon_Menu_setting);
#ifdef AUDIOHW_HAVE_EQ_BAND3_FREQUENCY
-MENUITEM_FUNCTION_DYNTEXT(hw_eq_band3_frequency, MENU_FUNC_USEPARAM,
- hw_eq_do_band_setting,
- HW_EQ_IDX(AUDIOHW_EQ_BAND3, AUDIOHW_EQ_FREQUENCY),
- hw_eq_get_name, hw_eq_speak_item,
- HW_EQ_IDX(AUDIOHW_EQ_BAND3, AUDIOHW_EQ_FREQUENCY),
- NULL, Icon_NOICON);
+MENUITEM_FUNCTION_DYNTEXT_W_PARAM(hw_eq_band3_frequency, 0,
+ hw_eq_do_band_setting,
+ HW_EQ_IDX(AUDIOHW_EQ_BAND3, AUDIOHW_EQ_FREQUENCY),
+ hw_eq_get_name, hw_eq_speak_item,
+ HW_EQ_IDX(AUDIOHW_EQ_BAND3, AUDIOHW_EQ_FREQUENCY),
+ NULL, Icon_NOICON);
#endif
#ifdef AUDIOHW_HAVE_EQ_BAND3_WIDTH
-MENUITEM_FUNCTION_DYNTEXT(hw_eq_band3_width, MENU_FUNC_USEPARAM,
- hw_eq_do_band_setting,
- HW_EQ_IDX(AUDIOHW_EQ_BAND3, AUDIOHW_EQ_WIDTH),
- hw_eq_get_name, hw_eq_speak_item,
- HW_EQ_IDX(AUDIOHW_EQ_BAND3, AUDIOHW_EQ_WIDTH),
- NULL, Icon_NOICON);
+MENUITEM_FUNCTION_DYNTEXT_W_PARAM(hw_eq_band3_width, 0,
+ hw_eq_do_band_setting,
+ HW_EQ_IDX(AUDIOHW_EQ_BAND3, AUDIOHW_EQ_WIDTH),
+ hw_eq_get_name, hw_eq_speak_item,
+ HW_EQ_IDX(AUDIOHW_EQ_BAND3, AUDIOHW_EQ_WIDTH),
+ NULL, Icon_NOICON);
#endif
#endif /* AUDIOHW_HAVE_EQ_BAND3 */
#ifdef AUDIOHW_HAVE_EQ_BAND4
-MENUITEM_FUNCTION_DYNTEXT(hw_eq_band4_gain, MENU_FUNC_USEPARAM,
- hw_eq_do_band_setting,
- HW_EQ_IDX(AUDIOHW_EQ_BAND4, AUDIOHW_EQ_GAIN),
- hw_eq_get_name, hw_eq_speak_item,
- HW_EQ_IDX(AUDIOHW_EQ_BAND4, AUDIOHW_EQ_GAIN),
- NULL, Icon_Menu_setting);
+MENUITEM_FUNCTION_DYNTEXT_W_PARAM(hw_eq_band4_gain, 0,
+ hw_eq_do_band_setting,
+ HW_EQ_IDX(AUDIOHW_EQ_BAND4, AUDIOHW_EQ_GAIN),
+ hw_eq_get_name, hw_eq_speak_item,
+ HW_EQ_IDX(AUDIOHW_EQ_BAND4, AUDIOHW_EQ_GAIN),
+ NULL, Icon_Menu_setting);
#ifdef AUDIOHW_HAVE_EQ_BAND4_FREQUENCY
-MENUITEM_FUNCTION_DYNTEXT(hw_eq_band4_frequency, MENU_FUNC_USEPARAM,
- hw_eq_do_band_setting,
- HW_EQ_IDX(AUDIOHW_EQ_BAND4, AUDIOHW_EQ_FREQUENCY),
- hw_eq_get_name, hw_eq_speak_item,
- HW_EQ_IDX(AUDIOHW_EQ_BAND4, AUDIOHW_EQ_FREQUENCY),
- NULL, Icon_NOICON);
+MENUITEM_FUNCTION_DYNTEXT_W_PARAM(hw_eq_band4_frequency, 0,
+ hw_eq_do_band_setting,
+ HW_EQ_IDX(AUDIOHW_EQ_BAND4, AUDIOHW_EQ_FREQUENCY),
+ hw_eq_get_name, hw_eq_speak_item,
+ HW_EQ_IDX(AUDIOHW_EQ_BAND4, AUDIOHW_EQ_FREQUENCY),
+ NULL, Icon_NOICON);
#endif
#ifdef AUDIOHW_HAVE_EQ_BAND4_WIDTH
-MENUITEM_FUNCTION_DYNTEXT(hw_eq_band4_width, MENU_FUNC_USEPARAM,
- hw_eq_do_band_setting,
- HW_EQ_IDX(AUDIOHW_EQ_BAND4, AUDIOHW_EQ_WIDTH),
- hw_eq_get_name, hw_eq_speak_item,
- HW_EQ_IDX(AUDIOHW_EQ_BAND4, AUDIOHW_EQ_WIDTH),
- NULL, Icon_NOICON);
+MENUITEM_FUNCTION_DYNTEXT_W_PARAM(hw_eq_band4_width, 0,
+ hw_eq_do_band_setting,
+ HW_EQ_IDX(AUDIOHW_EQ_BAND4, AUDIOHW_EQ_WIDTH),
+ hw_eq_get_name, hw_eq_speak_item,
+ HW_EQ_IDX(AUDIOHW_EQ_BAND4, AUDIOHW_EQ_WIDTH),
+ NULL, Icon_NOICON);
#endif
#endif /* AUDIOHW_HAVE_EQ_BAND4 */
#ifdef AUDIOHW_HAVE_EQ_BAND5
-MENUITEM_FUNCTION_DYNTEXT(hw_eq_band5_gain, MENU_FUNC_USEPARAM,
- hw_eq_do_band_setting,
- HW_EQ_IDX(AUDIOHW_EQ_BAND5, AUDIOHW_EQ_GAIN),
- hw_eq_get_name, hw_eq_speak_item,
- HW_EQ_IDX(AUDIOHW_EQ_BAND5, AUDIOHW_EQ_GAIN),
- NULL, Icon_Menu_setting);
+MENUITEM_FUNCTION_DYNTEXT_W_PARAM(hw_eq_band5_gain, 0,
+ hw_eq_do_band_setting,
+ HW_EQ_IDX(AUDIOHW_EQ_BAND5, AUDIOHW_EQ_GAIN),
+ hw_eq_get_name, hw_eq_speak_item,
+ HW_EQ_IDX(AUDIOHW_EQ_BAND5, AUDIOHW_EQ_GAIN),
+ NULL, Icon_Menu_setting);
#ifdef AUDIOHW_HAVE_EQ_BAND5_FREQUENCY
-MENUITEM_FUNCTION_DYNTEXT(hw_eq_band5_frequency, MENU_FUNC_USEPARAM,
- hw_eq_do_band_setting,
- HW_EQ_IDX(AUDIOHW_EQ_BAND5, AUDIOHW_EQ_FREQUENCY),
- hw_eq_get_name, hw_eq_speak_item,
- HW_EQ_IDX(AUDIOHW_EQ_BAND5, AUDIOHW_EQ_FREQUENCY),
- NULL, Icon_NOICON);
+MENUITEM_FUNCTION_DYNTEXT_W_PARAM(hw_eq_band5_frequency, 0,
+ hw_eq_do_band_setting,
+ HW_EQ_IDX(AUDIOHW_EQ_BAND5, AUDIOHW_EQ_FREQUENCY),
+ hw_eq_get_name, hw_eq_speak_item,
+ HW_EQ_IDX(AUDIOHW_EQ_BAND5, AUDIOHW_EQ_FREQUENCY),
+ NULL, Icon_NOICON);
#endif
#endif /* AUDIOHW_HAVE_EQ_BAND5 */
diff --git a/apps/menus/display_menu.c b/apps/menus/display_menu.c
index c72fb08fae..c5c5e6d908 100644
--- a/apps/menus/display_menu.c
+++ b/apps/menus/display_menu.c
@@ -149,10 +149,10 @@ MENUITEM_SETTING(backlight_timeout, &global_settings.backlight_timeout, NULL);
MENUITEM_SETTING(backlight_timeout_plugged,
&global_settings.backlight_timeout_plugged, NULL);
#endif
-#ifdef HAS_BUTTON_HOLD
+
MENUITEM_SETTING(backlight_on_button_hold,
&global_settings.backlight_on_button_hold, NULL);
-#endif
+
MENUITEM_SETTING(caption_backlight, &global_settings.caption_backlight, NULL);
#if defined(HAVE_BACKLIGHT_FADING_INT_SETTING) \
|| defined(HAVE_BACKLIGHT_FADING_BOOL_SETTING)
@@ -168,8 +168,8 @@ MENUITEM_SETTING(bl_selective_actions,
selectivebacklight_callback);
MENUITEM_FUNCTION(sel_backlight_mask, 0, ID2P(LANG_SETTINGS),
- selectivebacklight_set_mask, NULL,
- selectivebacklight_callback, Icon_Menu_setting);
+ selectivebacklight_set_mask, selectivebacklight_callback,
+ Icon_Menu_setting);
MAKE_MENU(sel_backlight, ID2P(LANG_BACKLIGHT_SELECTIVE),
NULL, Icon_Menu_setting, &bl_selective_actions, &sel_backlight_mask);
@@ -200,9 +200,7 @@ MAKE_MENU(lcd_settings,ID2P(LANG_LCD_MENU),
# if CONFIG_CHARGING
,&backlight_timeout_plugged
# endif
-# ifdef HAS_BUTTON_HOLD
,&backlight_on_button_hold
-# endif
,&caption_backlight
#if defined(HAVE_BACKLIGHT_FADING_INT_SETTING) \
|| defined(HAVE_BACKLIGHT_FADING_BOOL_SETTING)
@@ -332,24 +330,11 @@ MENUITEM_SETTING(list_accel_start_delay,
&global_settings.list_accel_start_delay, NULL);
MENUITEM_SETTING(list_accel_wait, &global_settings.list_accel_wait, NULL);
#endif /* HAVE_WHEEL_ACCELERATION */
-static int screenscroll_callback(int action,
- const struct menu_item_ex *this_item,
- struct gui_synclist *this_list)
-{
- (void)this_item;
- (void)this_list;
- switch (action)
- {
- case ACTION_EXIT_MENUITEM:
- gui_list_screen_scroll_out_of_view(global_settings.offset_out_of_view);
- break;
- }
- return action;
-}
-MENUITEM_SETTING(offset_out_of_view, &global_settings.offset_out_of_view,
- screenscroll_callback);
+MENUITEM_SETTING(offset_out_of_view, &global_settings.offset_out_of_view, NULL);
MENUITEM_SETTING(screen_scroll_step, &global_settings.screen_scroll_step, NULL);
MENUITEM_SETTING(scroll_paginated, &global_settings.scroll_paginated, NULL);
+MENUITEM_SETTING(list_wraparound, &global_settings.list_wraparound, NULL);
+MENUITEM_SETTING(list_order, &global_settings.list_order, NULL);
MAKE_MENU(scroll_settings_menu, ID2P(LANG_SCROLL_MENU), 0, Icon_NOICON,
&scroll_speed, &scroll_delay,
@@ -360,6 +345,8 @@ MAKE_MENU(scroll_settings_menu, ID2P(LANG_SCROLL_MENU), 0, Icon_NOICON,
#endif
&offset_out_of_view, &screen_scroll_step,
&scroll_paginated,
+ &list_wraparound,
+ &list_order,
#ifndef HAVE_WHEEL_ACCELERATION
&list_accel_start_delay, &list_accel_wait
#endif
@@ -528,21 +515,20 @@ static bool history_interval(void)
return set_option(str(LANG_HISTOGRAM_INTERVAL),
&global_settings.histogram_interval,
- INT, names, 4, NULL );
+ RB_INT, names, 4, NULL );
}
-MENUITEM_FUNCTION(histogram, 0,
- ID2P(LANG_HISTOGRAM_INTERVAL),
- history_interval, NULL, NULL, Icon_Menu_setting);
+MENUITEM_FUNCTION(histogram, 0, ID2P(LANG_HISTOGRAM_INTERVAL),
+ history_interval, NULL, Icon_Menu_setting);
#endif
MENUITEM_FUNCTION(peak_meter_scale_item, 0, ID2P(LANG_PM_SCALE),
- peak_meter_scale, NULL, NULL, Icon_NOICON);
+ peak_meter_scale, NULL, Icon_NOICON);
MENUITEM_FUNCTION(peak_meter_min_item, 0, ID2P(LANG_PM_MIN),
- peak_meter_min, NULL, NULL, Icon_NOICON);
+ peak_meter_min, NULL, Icon_NOICON);
MENUITEM_FUNCTION(peak_meter_max_item, 0, ID2P(LANG_PM_MAX),
- peak_meter_max, NULL, NULL, Icon_NOICON);
+ peak_meter_max, NULL, Icon_NOICON);
MAKE_MENU(peak_meter_menu, ID2P(LANG_PM_MENU), NULL, Icon_NOICON,
&peak_meter_release, &peak_meter_hold,
&peak_meter_clip_hold,
@@ -586,10 +572,11 @@ static int line_padding_callback(int action,
MENUITEM_SETTING(touch_mode, &global_settings.touch_mode, touch_mode_callback);
-MENUITEM_FUNCTION(touchscreen_menu_calibrate, 0, ID2P(LANG_TOUCHSCREEN_CALIBRATE), calibrate,
- NULL, NULL, Icon_NOICON);
-MENUITEM_FUNCTION(touchscreen_menu_reset_calibration, 0, ID2P(LANG_TOUCHSCREEN_RESET_CALIBRATION), reset_mapping,
- NULL, NULL, Icon_NOICON);
+MENUITEM_FUNCTION(touchscreen_menu_calibrate, 0,
+ ID2P(LANG_TOUCHSCREEN_CALIBRATE), calibrate, NULL, Icon_NOICON);
+MENUITEM_FUNCTION(touchscreen_menu_reset_calibration, 0,
+ ID2P(LANG_TOUCHSCREEN_RESET_CALIBRATION),
+ reset_mapping, NULL, Icon_NOICON);
MENUITEM_SETTING(list_line_padding, &global_settings.list_line_padding, line_padding_callback);
MAKE_MENU(touchscreen_menu, ID2P(LANG_TOUCHSCREEN_SETTINGS), NULL, Icon_NOICON, &list_line_padding, &touch_mode,
diff --git a/apps/menus/eq_menu.c b/apps/menus/eq_menu.c
index 49d35c133b..59dcc59ff0 100644
--- a/apps/menus/eq_menu.c
+++ b/apps/menus/eq_menu.c
@@ -49,6 +49,9 @@
#include "exported_menus.h"
#include "pcmbuf.h"
#include "option_select.h"
+#include "string-extra.h"
+
+static void eq_apply(void);
/*
* Utility functions
@@ -68,6 +71,12 @@ const char* eq_precut_format(char* buffer, size_t buffer_size, int value, const
return buffer;
}
+void eq_enabled_option_callback(bool enabled)
+{
+ (void)enabled;
+ eq_apply();
+}
+
/*
* Settings functions
*/
@@ -145,9 +154,9 @@ static int32_t get_dec_talkid(int value, int unit)
static const struct int_setting gain_int_setting = {
.option_callback = NULL,
.unit = UNIT_DB,
+ .step = EQ_GAIN_STEP,
.min = EQ_GAIN_MIN,
.max = EQ_GAIN_MAX,
- .step = EQ_GAIN_STEP,
.formatter = db_format,
.get_talk_id = get_dec_talkid,
};
@@ -155,9 +164,9 @@ static const struct int_setting gain_int_setting = {
static const struct int_setting q_int_setting = {
.option_callback = NULL,
.unit = UNIT_INT,
+ .step = EQ_Q_STEP,
.min = EQ_Q_MIN,
.max = EQ_Q_MAX,
- .step = EQ_Q_STEP,
.formatter = eq_q_format,
.get_talk_id = get_dec_talkid,
};
@@ -165,9 +174,9 @@ static const struct int_setting q_int_setting = {
static const struct int_setting cutoff_int_setting = {
.option_callback = NULL,
.unit = UNIT_HERTZ,
+ .step = EQ_CUTOFF_STEP,
.min = EQ_CUTOFF_MIN,
.max = EQ_CUTOFF_MAX,
- .step = EQ_CUTOFF_STEP,
.formatter = NULL,
.get_talk_id = NULL,
};
@@ -213,8 +222,8 @@ static int eq_do_simple_menu(void * param)
}
return 0;
}
-MENUITEM_FUNCTION(gain_menu, 0, ID2P(LANG_EQUALIZER_GAIN), eq_do_simple_menu,
- NULL, NULL, Icon_Submenu);
+MENUITEM_FUNCTION(gain_menu, 0, ID2P(LANG_EQUALIZER_GAIN),
+ eq_do_simple_menu, NULL, Icon_Submenu);
static void selection_to_banditem(int selection, int expanded_band, int *band, int *item)
{
@@ -239,14 +248,12 @@ static void selection_to_banditem(int selection, int expanded_band, int *band, i
static char *advancedmenu_item_get_name(int selected_item, void *data, char *buffer, size_t len)
{
- (void)len;
int band;
int item;
- char *lang = NULL;
+ int lang = -1;
selection_to_banditem(selected_item, *(intptr_t*)data, &band, &item);
- strcpy(buffer, "\t");
switch (item)
{
case 0: /* Band title */
@@ -256,27 +263,34 @@ static char *advancedmenu_item_get_name(int selected_item, void *data, char *buf
return str(LANG_EQUALIZER_BAND_HIGH_SHELF);
else
{
- snprintf(buffer, MAX_PATH, str(LANG_EQUALIZER_BAND_PEAK), band);
+ snprintf(buffer, len, str(LANG_EQUALIZER_BAND_PEAK), band);
return buffer;
}
break;
case 1: /* cutoff */
if (band == 0)
- lang = str(LANG_EQUALIZER_BAND_CUTOFF);
+ lang = LANG_EQUALIZER_BAND_CUTOFF;
else if (band == EQ_NUM_BANDS - 1)
- lang = str(LANG_EQUALIZER_BAND_CUTOFF);
+ lang = LANG_EQUALIZER_BAND_CUTOFF;
else
- lang = str(LANG_EQUALIZER_BAND_CENTER);
+ lang = LANG_EQUALIZER_BAND_CENTER;
break;
case 2: /* Q */
- lang = str(LANG_EQUALIZER_BAND_Q);
+ lang = LANG_EQUALIZER_BAND_Q;
break;
case 3: /* Gain */
- lang = str(LANG_GAIN);
+ lang = LANG_GAIN;
break;
}
- return strcat(buffer, lang);;
+ if(lang < 0)
+ buffer[0] = 0;
+ else {
+ buffer[0] = '\t';
+ strmemccpy(&buffer[1], str(lang), len - 1);
+ }
+
+ return buffer;
}
static int advancedmenu_speak_item(int selected_item, void *data)
@@ -412,7 +426,7 @@ static int eq_do_advanced_menu(void * param)
return 0;
}
MENUITEM_FUNCTION(advanced_menu, 0, ID2P(LANG_EQUALIZER_ADVANCED),
- eq_do_advanced_menu, NULL, NULL, Icon_EQ);
+ eq_do_advanced_menu, NULL, Icon_EQ);
enum eq_slider_mode {
GAIN,
@@ -466,17 +480,18 @@ static int draw_eq_slider(struct screen * screen, int x, int y,
/* Print out the band label */
if (band == 0) {
screen->putsxy(x1, y1, "LS: ");
- screen->getstringsize("LS:", &w, &h);
+ /*screen->getstringsize("LS:", &w, &h); UNUSED*/
} else if (band == EQ_NUM_BANDS - 1) {
screen->putsxy(x1, y1, "HS: ");
- screen->getstringsize("HS:", &w, &h);
+ /*screen->getstringsize("HS:", &w, &h); UNUSED*/
} else {
snprintf(buf, sizeof(buf), "PK%d:", band);
screen->putsxy(x1, y1, buf);
- screen->getstringsize(buf, &w, &h);
+ /*screen->getstringsize(buf, &w, &h); UNUSED*/
}
- screen->getstringsize("A", &w, &h);
+ w = screen->getstringsize("A", NULL, &h);
+
x1 += 5*w; /* 4 chars for label + 1 space = 5 */
/* Print out gain part of status line (left justify after label) */
@@ -489,7 +504,7 @@ static int draw_eq_slider(struct screen * screen, int x, int y,
abs_gain / EQ_USER_DIVISOR, abs_gain % EQ_USER_DIVISOR,
screen->lcdwidth >= 160 ? "dB" : "");
screen->putsxy(x1, y1, buf);
- screen->getstringsize(buf, &w, &h);
+ w = screen->getstringsize(buf, NULL, NULL);
x1 += w;
/* Print out Q part of status line (right justify) */
@@ -500,7 +515,7 @@ static int draw_eq_slider(struct screen * screen, int x, int y,
snprintf(buf, sizeof(buf), "%d.%d%s", q / EQ_USER_DIVISOR,
q % EQ_USER_DIVISOR, screen->lcdwidth >= 160 ? " Q" : "");
- screen->getstringsize(buf, &w, &h);
+ w = screen->getstringsize(buf, NULL, NULL);
x2 = x + width - w - 2;
screen->putsxy(x2, y1, buf);
@@ -512,7 +527,7 @@ static int draw_eq_slider(struct screen * screen, int x, int y,
snprintf(buf, sizeof(buf), "%5d%s", cutoff,
screen->lcdwidth >= 160 ? "Hz" : "");
- screen->getstringsize(buf, &w, &h);
+ w = screen->getstringsize(buf, NULL, NULL);
x1 = x1 + (x2 - x1 - w)/2;
screen->putsxy(x1, y1, buf);
@@ -575,8 +590,7 @@ int eq_menu_graphical(void)
int *setting;
int current_band, x, y, step, fast_step, min, max;
enum eq_slider_mode mode;
- char buf[24];
- int w, h, height, start_item, nb_eq_sliders[NB_SCREENS];
+ int h, height, start_item, nb_eq_sliders[NB_SCREENS];
FOR_NB_SCREENS(i)
viewportmanager_theme_enable(i, false, NULL);
@@ -587,7 +601,7 @@ int eq_menu_graphical(void)
screens[i].clear_display();
/* Figure out how many sliders can be drawn on the screen */
- screens[i].getstringsize("A", &w, &h);
+ h = screens[i].getcharheight();
/* Total height includes margins (1), text, slider, and line selector (1) */
height = 3 + h + 1 + SCROLLBAR_SIZE + 3;
@@ -623,10 +637,8 @@ int eq_menu_graphical(void)
min = EQ_GAIN_MIN;
max = EQ_GAIN_MAX;
- snprintf(buf, sizeof(buf), str(LANG_SYSFONT_EQUALIZER_EDIT_MODE),
+ screens[i].putsxyf(0, 0, str(LANG_SYSFONT_EQUALIZER_EDIT_MODE),
str(LANG_SYSFONT_GAIN), "(dB)");
-
- screens[i].putsxy(0, 0, buf);
} else if (mode == CUTOFF) {
/* cutoff */
setting = &global_settings.eq_band_settings[current_band].cutoff;
@@ -636,10 +648,8 @@ int eq_menu_graphical(void)
min = EQ_CUTOFF_MIN;
max = EQ_CUTOFF_MAX;
- snprintf(buf, sizeof(buf), str(LANG_SYSFONT_EQUALIZER_EDIT_MODE),
+ screens[i].putsxyf(0, 0, str(LANG_SYSFONT_EQUALIZER_EDIT_MODE),
str(LANG_SYSFONT_EQUALIZER_BAND_CUTOFF), "(Hz)");
-
- screens[i].putsxy(0, 0, buf);
} else {
/* Q */
setting = &global_settings.eq_band_settings[current_band].q;
@@ -649,10 +659,8 @@ int eq_menu_graphical(void)
min = EQ_Q_MIN;
max = EQ_Q_MAX;
- snprintf(buf, sizeof(buf), str(LANG_SYSFONT_EQUALIZER_EDIT_MODE),
+ screens[i].putsxyf(0, 0, str(LANG_SYSFONT_EQUALIZER_EDIT_MODE),
str(LANG_SYSFONT_EQUALIZER_BAND_Q), "");
-
- screens[i].putsxy(0, 0, buf);
}
/* Draw scrollbar if needed */
@@ -660,7 +668,7 @@ int eq_menu_graphical(void)
{
if (current_band == 0) {
start_item = 0;
- } else if (current_band == 9) {
+ } else if (current_band == EQ_NUM_BANDS - 1) {
start_item = EQ_NUM_BANDS - nb_eq_sliders[i];
} else {
start_item = current_band - 1;
@@ -775,13 +783,12 @@ static int eq_save_preset(void)
static struct browse_folder_info eqs = { EQS_DIR, SHOW_CFG };
MENUITEM_FUNCTION(eq_graphical, 0, ID2P(LANG_EQUALIZER_GRAPHICAL),
- eq_menu_graphical, NULL, lowlatency_callback,
- Icon_EQ);
+ eq_menu_graphical, lowlatency_callback, Icon_EQ);
MENUITEM_FUNCTION(eq_save, 0, ID2P(LANG_EQUALIZER_SAVE),
- eq_save_preset, NULL, NULL, Icon_NOICON);
-MENUITEM_FUNCTION(eq_browse, MENU_FUNC_USEPARAM, ID2P(LANG_EQUALIZER_BROWSE),
- browse_folder, (void*)&eqs, lowlatency_callback,
- Icon_NOICON);
+ eq_save_preset, NULL, Icon_NOICON);
+MENUITEM_FUNCTION_W_PARAM(eq_browse, 0, ID2P(LANG_EQUALIZER_BROWSE),
+ browse_folder, (void*)&eqs,
+ lowlatency_callback, Icon_NOICON);
MAKE_MENU(equalizer_menu, ID2P(LANG_EQUALIZER), NULL, Icon_EQ,
&eq_enable, &eq_graphical, &eq_precut, &gain_menu,
diff --git a/apps/menus/eq_menu.h b/apps/menus/eq_menu.h
index 04e8be2ead..f99f83d94f 100644
--- a/apps/menus/eq_menu.h
+++ b/apps/menus/eq_menu.h
@@ -48,4 +48,7 @@ const char* eq_q_format(char* buffer, size_t buffer_size, int value,
const char* eq_precut_format(char* buffer, size_t buffer_size, int value,
const char* unit);
+/* callbacks for settings_list.c */
+void eq_enabled_option_callback(bool enabled);
+
#endif
diff --git a/apps/menus/main_menu.c b/apps/menus/main_menu.c
index 5e9b935937..73c9f7be33 100644
--- a/apps/menus/main_menu.c
+++ b/apps/menus/main_menu.c
@@ -51,7 +51,7 @@
#include "disk.h"
static const struct browse_folder_info config = {ROCKBOX_DIR, SHOW_CFG};
-
+static int show_info(void);
/***********************************/
/* MANAGE SETTINGS MENU */
@@ -78,6 +78,7 @@ static int reset_settings(void)
settings_apply(true);
settings_apply_skins();
break;
+ case YESNO_TMO:
case YESNO_NO:
break;
case YESNO_USB:
@@ -90,16 +91,19 @@ static int write_settings_file(void* param)
return settings_save_config((intptr_t)param);
}
-MENUITEM_FUNCTION(browse_configs, MENU_FUNC_USEPARAM, ID2P(LANG_CUSTOM_CFG),
- browse_folder, (void*)&config, NULL, Icon_NOICON);
-MENUITEM_FUNCTION(save_settings_item, MENU_FUNC_USEPARAM, ID2P(LANG_SAVE_SETTINGS),
- write_settings_file, (void*)SETTINGS_SAVE_ALL, NULL, Icon_NOICON);
-MENUITEM_FUNCTION(save_theme_item, MENU_FUNC_USEPARAM, ID2P(LANG_SAVE_THEME),
- write_settings_file, (void*)SETTINGS_SAVE_THEME, NULL, Icon_NOICON);
-MENUITEM_FUNCTION(save_sound_item, MENU_FUNC_USEPARAM, ID2P(LANG_SAVE_SOUND),
- write_settings_file, (void*)SETTINGS_SAVE_SOUND, NULL, Icon_NOICON);
+MENUITEM_FUNCTION_W_PARAM(browse_configs, 0, ID2P(LANG_CUSTOM_CFG),
+ browse_folder, (void*)&config, NULL, Icon_NOICON);
+MENUITEM_FUNCTION_W_PARAM(save_settings_item, 0, ID2P(LANG_SAVE_SETTINGS),
+ write_settings_file, (void*)SETTINGS_SAVE_ALL,
+ NULL, Icon_NOICON);
+MENUITEM_FUNCTION_W_PARAM(save_theme_item, 0, ID2P(LANG_SAVE_THEME),
+ write_settings_file, (void*)SETTINGS_SAVE_THEME,
+ NULL, Icon_NOICON);
+MENUITEM_FUNCTION_W_PARAM(save_sound_item, 0, ID2P(LANG_SAVE_SOUND),
+ write_settings_file, (void*)SETTINGS_SAVE_SOUND,
+ NULL, Icon_NOICON);
MENUITEM_FUNCTION(reset_settings_item, 0, ID2P(LANG_RESET),
- reset_settings, NULL, NULL, Icon_NOICON);
+ reset_settings, NULL, Icon_NOICON);
MAKE_MENU(manage_settings, ID2P(LANG_MANAGE_MENU), NULL, Icon_Config,
&browse_configs, &reset_settings_item,
@@ -110,18 +114,10 @@ MAKE_MENU(manage_settings, ID2P(LANG_MANAGE_MENU), NULL, Icon_Config,
/***********************************/
/* INFO MENU */
-
static int show_credits(void)
{
- char credits[MAX_PATH] = { '\0' };
- snprintf(credits, MAX_PATH, "%s/credits.rock", VIEWERS_DIR);
- if (plugin_load(credits, NULL) != PLUGIN_OK)
- {
- /* show the rockbox logo and version untill a button is pressed */
- show_logo();
- while (IS_SYSEVENT(get_action(CONTEXT_STD, TIMEOUT_BLOCK)))
- ;
- }
+ if (plugin_load(VIEWERS_DIR "/credits.rock", NULL) != PLUGIN_OK)
+ show_info();
return 0;
}
@@ -141,6 +137,7 @@ enum infoscreenorder
#ifdef HAVE_RECORDING
INFO_REC_DIR,
#endif
+ INFO_ROOT_DIR,
INFO_VERSION,
#if CONFIG_RTC
INFO_DATE,
@@ -245,7 +242,9 @@ static const char* info_getname(int selected_item, void *data,
snprintf(buffer, buffer_len, "%s %s", str(LANG_REC_DIR), global_settings.rec_directory);
break;
#endif
-
+ case INFO_ROOT_DIR:
+ snprintf(buffer, buffer_len, "%s %s", str(LANG_DISPLAY_FULL_PATH), root_realpath());
+ break;
case INFO_BUFFER: /* buffer */
{
long kib = audio_buffer_size() >> 10; /* to KiB */
@@ -338,29 +337,16 @@ static int info_speak_item(int selected_item, void * data)
#ifdef HAVE_RECORDING
case INFO_REC_DIR:
talk_id(LANG_REC_DIR, false);
- if (global_settings.rec_directory && global_settings.rec_directory[0])
+ if (global_settings.rec_directory[0])
{
- long *pathsep = NULL;
- char rec_directory[MAX_PATHNAME+1];
- char *s;
- strcpy(rec_directory, global_settings.rec_directory);
- s = rec_directory;
- if ((strlen(s) > 1) && (s[strlen(s) - 1] == '/'))
- s[strlen(s) - 1] = 0;
- while (s)
- {
- s = strchr(s + 1, '/');
- if (s)
- s[0] = 0;
- talk_dir_or_spell(rec_directory, pathsep, true);
- if (s)
- s[0] = '/';
- pathsep = TALK_IDARRAY(VOICE_CHAR_SLASH);
- }
+ talk_fullpath(global_settings.rec_directory, true);
}
break;
#endif
-
+ case INFO_ROOT_DIR:
+ talk_id(LANG_DISPLAY_FULL_PATH, false);
+ talk_fullpath(root_realpath(), true);
+ break;
case INFO_BUFFER: /* buffer */
{
talk_id(LANG_BUFFER_STAT, false);
@@ -441,8 +427,7 @@ static int info_action_callback(int action, struct gui_synclist *lists)
else if (action == ACTION_NONE)
{
static int last_redraw = 0;
- if (gui_synclist_item_is_onscreen(lists, 0, INFO_TIME)
- && TIME_AFTER(current_tick, last_redraw + HZ*5))
+ if (TIME_AFTER(current_tick, last_redraw + HZ*5))
{
last_redraw = current_tick;
return ACTION_REDRAW;
@@ -458,9 +443,6 @@ static int show_info(void)
struct simplelist_info info;
int count = INFO_COUNT + refresh_data(&data) - 1;
simplelist_info_init(&info, str(LANG_ROCKBOX_INFO), count, (void*)&data);
- info.hide_selection = !global_settings.talk_menu;
- if (info.hide_selection)
- info.scroll_all = true;
info.get_name = info_getname;
if(global_settings.talk_menu)
info.get_talk = info_speak_item;
@@ -470,22 +452,22 @@ static int show_info(void)
MENUITEM_FUNCTION(show_info_item, 0, ID2P(LANG_ROCKBOX_INFO),
- show_info, NULL, NULL, Icon_NOICON);
+ show_info, NULL, Icon_NOICON);
#if CONFIG_RTC
int time_screen(void* ignored);
MENUITEM_FUNCTION(timedate_item, MENU_FUNC_CHECK_RETVAL, ID2P(LANG_TIME_MENU),
- time_screen, NULL, NULL, Icon_Menu_setting );
+ time_screen, NULL, Icon_Menu_setting );
#endif
MENUITEM_FUNCTION(show_credits_item, 0, ID2P(LANG_CREDITS),
- show_credits, NULL, NULL, Icon_NOICON);
+ show_credits, NULL, Icon_NOICON);
MENUITEM_FUNCTION(show_runtime_item, 0, ID2P(LANG_RUNNING_TIME),
- view_runtime, NULL, NULL, Icon_NOICON);
+ view_runtime, NULL, Icon_NOICON);
MENUITEM_FUNCTION(debug_menu_item, 0, ID2P(LANG_DEBUG),
- debug_menu, NULL, NULL, Icon_NOICON);
+ debug_menu, NULL, Icon_NOICON);
MAKE_MENU(info_menu, ID2P(LANG_SYSTEM), 0, Icon_System_menu,
&show_info_item, &show_credits_item,
diff --git a/apps/menus/playback_menu.c b/apps/menus/playback_menu.c
index 41c738725c..f28fffa8f6 100644
--- a/apps/menus/playback_menu.c
+++ b/apps/menus/playback_menu.c
@@ -31,7 +31,6 @@
#include "sound_menu.h"
#include "kernel.h"
#include "playlist.h"
-#include "scrobbler.h"
#include "audio.h"
#include "cuesheet.h"
#include "misc.h"
@@ -61,12 +60,8 @@ static int setcrossfadeonexit_callback(int action,
/***********************************/
/* PLAYBACK MENU */
-static int playback_callback(int action,
- const struct menu_item_ex *this_item,
- struct gui_synclist *this_list);
-
-MENUITEM_SETTING(shuffle_item, &global_settings.playlist_shuffle, playback_callback);
-MENUITEM_SETTING(repeat_mode, &global_settings.repeat_mode, playback_callback);
+MENUITEM_SETTING(shuffle_item, &global_settings.playlist_shuffle, NULL);
+MENUITEM_SETTING(repeat_mode, &global_settings.repeat_mode, NULL);
MENUITEM_SETTING(play_selected, &global_settings.play_selected, NULL);
MENUITEM_SETTING(ff_rewind_accel, &global_settings.ff_rewind_accel, NULL);
@@ -92,6 +87,7 @@ MENUITEM_SETTING(buffer_margin, &global_settings.buffer_margin,
buffermargin_callback);
#endif /*HAVE_DISK_STORAGE */
MENUITEM_SETTING(fade_on_stop, &global_settings.fade_on_stop, NULL);
+MENUITEM_SETTING(single_mode, &global_settings.single_mode, NULL);
MENUITEM_SETTING(party_mode, &global_settings.party_mode, NULL);
#ifdef HAVE_CROSSFADE
@@ -149,26 +145,6 @@ MENUITEM_SETTING(spdif_enable, &global_settings.spdif_enable, NULL);
MENUITEM_SETTING(next_folder, &global_settings.next_folder, NULL);
MENUITEM_SETTING(constrain_next_folder,
&global_settings.constrain_next_folder, NULL);
-static int audioscrobbler_callback(int action,
- const struct menu_item_ex *this_item,
- struct gui_synclist *this_list)
-{
- (void)this_item;
- (void)this_list;
- switch (action)
- {
- case ACTION_EXIT_MENUITEM: /* on exit */
- if (!scrobbler_is_enabled() && global_settings.audioscrobbler)
- scrobbler_init();
-
- if(scrobbler_is_enabled() && !global_settings.audioscrobbler)
- scrobbler_shutdown(false);
- break;
- }
- return action;
-}
-MENUITEM_SETTING(audioscrobbler, &global_settings.audioscrobbler, audioscrobbler_callback);
-
static int cuesheet_callback(int action,
const struct menu_item_ex *this_item,
@@ -194,11 +170,33 @@ MAKE_MENU(unplug_menu, ID2P(LANG_HEADPHONE_UNPLUG), 0, Icon_NOICON,
MENUITEM_SETTING(skip_length, &global_settings.skip_length, NULL);
MENUITEM_SETTING(prevent_skip, &global_settings.prevent_skip, NULL);
+MENUITEM_SETTING(rewind_across_tracks, &global_settings.rewind_across_tracks, NULL);
MENUITEM_SETTING(resume_rewind, &global_settings.resume_rewind, NULL);
MENUITEM_SETTING(pause_rewind, &global_settings.pause_rewind, NULL);
#ifdef HAVE_PLAY_FREQ
-MENUITEM_SETTING(play_frequency, &global_settings.play_frequency,
- playback_callback);
+MENUITEM_SETTING(play_frequency, &global_settings.play_frequency, NULL);
+#endif
+#ifdef HAVE_ALBUMART
+static int albumart_callback(int action,
+ const struct menu_item_ex *this_item,
+ struct gui_synclist *this_list)
+{
+ (void)this_item;
+ (void)this_list;
+ static int initial_aa_setting;
+ switch (action)
+ {
+ case ACTION_ENTER_MENUITEM:
+ initial_aa_setting = global_settings.album_art;
+ break;
+ case ACTION_EXIT_MENUITEM: /* on exit */
+ if (initial_aa_setting != global_settings.album_art)
+ set_albumart_mode(global_settings.album_art);
+ }
+ return action;
+}
+MENUITEM_SETTING(album_art, &global_settings.album_art,
+ albumart_callback);
#endif
MAKE_MENU(playback_settings,ID2P(LANG_PLAYBACK),0,
@@ -208,7 +206,7 @@ MAKE_MENU(playback_settings,ID2P(LANG_PLAYBACK),0,
#ifdef HAVE_DISK_STORAGE
&buffer_margin,
#endif
- &fade_on_stop, &party_mode,
+ &fade_on_stop, &single_mode, &party_mode,
#if defined(HAVE_CROSSFADE)
&crossfade_settings_menu,
@@ -219,80 +217,22 @@ MAKE_MENU(playback_settings,ID2P(LANG_PLAYBACK),0,
#ifdef HAVE_SPDIF_POWER
&spdif_enable,
#endif
- &next_folder, &constrain_next_folder, &audioscrobbler, &cuesheet
+ &next_folder, &constrain_next_folder, &cuesheet
#ifdef HAVE_HEADPHONE_DETECTION
,&unplug_menu
#endif
,&skip_length, &prevent_skip
+ ,&rewind_across_tracks
,&resume_rewind
,&pause_rewind
#ifdef HAVE_PLAY_FREQ
,&play_frequency
#endif
+#ifdef HAVE_ALBUMART
+ ,&album_art
+#endif
);
-static int playback_callback(int action,
- const struct menu_item_ex *this_item,
- struct gui_synclist *this_list)
-{
- (void)this_list;
- static bool old_shuffle = false;
- static int old_repeat = 0;
- switch (action)
- {
- case ACTION_ENTER_MENUITEM:
- if (this_item == &shuffle_item)
- {
- old_shuffle = global_settings.playlist_shuffle;
- }
- else if (this_item == &repeat_mode)
- {
- old_repeat = global_settings.repeat_mode;
- }
- break;
-
- case ACTION_EXIT_MENUITEM: /* on exit */
- /* Playing or not */
-#ifdef HAVE_PLAY_FREQ
- if (this_item == &play_frequency)
- {
- audio_set_playback_frequency(global_settings.play_frequency);
- break;
- }
-#endif /* HAVE_PLAY_FREQ */
-
- if (!(audio_status() & AUDIO_STATUS_PLAY))
- break;
-
- /* Playing only */
- if (this_item == &shuffle_item)
- {
- if (old_shuffle == global_settings.playlist_shuffle)
- break;
-
- replaygain_update();
-
- if (global_settings.playlist_shuffle)
- {
- playlist_randomise(NULL, current_tick, true);
- }
- else
- {
- playlist_sort(NULL, true);
- }
- }
- else if (this_item == &repeat_mode)
- {
- if (old_repeat == global_settings.repeat_mode)
- break;
-
- audio_flush_and_reload_tracks();
- }
-
- break;
- }
- return action;
-}
/* PLAYBACK MENU */
/***********************************/
diff --git a/apps/menus/playlist_menu.c b/apps/menus/playlist_menu.c
index b84abe0b37..affe20418d 100644
--- a/apps/menus/playlist_menu.c
+++ b/apps/menus/playlist_menu.c
@@ -39,6 +39,7 @@
#include "playlist_catalog.h"
#include "splash.h"
#include "filetree.h"
+#include "general.h"
/* load a screen to save the playlist passed in (or current playlist if NULL is passed) */
int save_playlist_screen(struct playlist_info* playlist)
@@ -51,33 +52,37 @@ int save_playlist_screen(struct playlist_info* playlist)
uint32_t resume_elapsed;
uint32_t resume_offset;
- char temp[MAX_PATH+1], *dot;
+ char temp[MAX_PATH+1], *p;
int len;
+ catalog_get_directory(directoryonly, sizeof(directoryonly));
playlist_get_name(playlist, temp, sizeof(temp)-1);
len = strlen(temp);
- dot = strrchr(temp, '.');
- if (!dot && len <= 1)
+ if (len <= 1) /* root or dynamic playlist */
+ create_numbered_filename(temp, directoryonly, PLAYLIST_UNTITLED_PREFIX, ".m3u8",
+ 1 IF_CNFN_NUM_(, NULL));
+ else if (!strcmp((temp + len - 1), "/")) /* dir playlists other than root */
{
- snprintf(temp, sizeof(temp), "%s%s",
- catalog_get_directory(), DEFAULT_DYNAMIC_PLAYLIST_NAME);
- }
+ temp[len - 1] = '\0';
- dot = strrchr(temp, '.');
- if (dot) /* remove extension */
- *dot = '\0';
+ if ((p = strrchr(temp, '/'))) /* use last path component as playlist name */
+ {
+ strlcat(directoryonly, p, sizeof(directoryonly));
+ strlcat(directoryonly, ".m3u8", sizeof(directoryonly));
+ strmemccpy(temp, directoryonly, sizeof(temp));
+ }
+ else
+ create_numbered_filename(temp, directoryonly, PLAYLIST_UNTITLED_PREFIX, ".m3u8",
+ 1 IF_CNFN_NUM_(, NULL));
+ }
- if (!kbd_input(temp, sizeof(temp), NULL))
+ if (catalog_pick_new_playlist_name(temp, sizeof(temp),
+ playlist ? playlist->filename :
+ playlist_get_current()->filename))
{
- len = strlen(temp);
- if(len > 4 && !strcasecmp(&temp[len-4], ".m3u"))
- strlcat(temp, "8", sizeof(temp));
- else if(len <= 5 || strcasecmp(&temp[len-5], ".m3u8"))
- strlcat(temp, ".m3u8", sizeof(temp));
-
- playlist_save(playlist, temp, NULL, 0);
+ playlist_save(playlist, temp);
/* reload in case playlist was saved to cwd */
reload_directory();
@@ -116,7 +121,7 @@ int save_playlist_screen(struct playlist_info* playlist)
}
/* can't trust index from id3 (don't know why), get it from playlist */
- resume_index = playlist_get_current()->index;
+ resume_index = playlist_get_display_index() - 1;
struct mp3entry* id3 = audio_current_track();
@@ -137,16 +142,18 @@ int save_playlist_screen(struct playlist_info* playlist)
static int playlist_view_(void)
{
- playlist_viewer_ex(NULL);
+ playlist_viewer_ex(NULL, NULL);
+ FOR_NB_SCREENS(i) /* Playlist Viewer defers skin updates when popping its activity */
+ skin_update(CUSTOM_STATUSBAR, i, SKIN_REFRESH_ALL);
return 0;
}
MENUITEM_FUNCTION(create_playlist_item, 0, ID2P(LANG_CREATE_PLAYLIST),
- create_playlist, NULL, NULL, Icon_NOICON);
+ create_playlist, NULL, Icon_NOICON);
MENUITEM_FUNCTION(view_cur_playlist, 0,
ID2P(LANG_VIEW_DYNAMIC_PLAYLIST),
- playlist_view_, NULL, NULL, Icon_NOICON);
-MENUITEM_FUNCTION(save_playlist, MENU_FUNC_USEPARAM, ID2P(LANG_SAVE_DYNAMIC_PLAYLIST),
- save_playlist_screen, NULL, NULL, Icon_NOICON);
+ playlist_view_, NULL, Icon_NOICON);
+MENUITEM_FUNCTION_W_PARAM(save_playlist, 0, ID2P(LANG_SAVE_DYNAMIC_PLAYLIST),
+ save_playlist_screen, NULL, NULL, Icon_NOICON);
MENUITEM_SETTING(recursive_dir_insert, &global_settings.recursive_dir_insert, NULL);
static int clear_catalog_directory(void)
{
@@ -156,7 +163,7 @@ static int clear_catalog_directory(void)
return false;
}
MENUITEM_FUNCTION(clear_catalog_directory_item, 0, ID2P(LANG_RESET_PLAYLISTCAT_DIR),
- clear_catalog_directory, NULL, NULL, Icon_file_view_menu);
+ clear_catalog_directory, NULL, Icon_file_view_menu);
/* Playlist viewer settings submenu */
MENUITEM_SETTING(show_icons, &global_settings.playlist_viewer_icons, NULL);
@@ -169,12 +176,17 @@ MAKE_MENU(viewer_settings_menu, ID2P(LANG_PLAYLISTVIEWER_SETTINGS),
/* Current Playlist submenu */
MENUITEM_SETTING(warn_on_erase, &global_settings.warnon_erase_dynplaylist, NULL);
+MENUITEM_SETTING(keep_current_track_on_replace, &global_settings.keep_current_track_on_replace_playlist, NULL);
MENUITEM_SETTING(show_shuffled_adding_options, &global_settings.show_shuffled_adding_options, NULL);
MENUITEM_SETTING(show_queue_options, &global_settings.show_queue_options, NULL);
MENUITEM_SETTING(playlist_reload_after_save, &global_settings.playlist_reload_after_save, NULL);
MAKE_MENU(currentplaylist_settings_menu, ID2P(LANG_CURRENT_PLAYLIST),
NULL, Icon_Playlist,
- &warn_on_erase, &show_shuffled_adding_options, &show_queue_options, &playlist_reload_after_save);
+ &warn_on_erase,
+ &keep_current_track_on_replace,
+ &show_shuffled_adding_options,
+ &show_queue_options,
+ &playlist_reload_after_save);
MAKE_MENU(playlist_settings, ID2P(LANG_PLAYLISTS), NULL,
Icon_Playlist,
diff --git a/apps/menus/plugin_menu.c b/apps/menus/plugin_menu.c
index 055cfce140..fa0d721c05 100644
--- a/apps/menus/plugin_menu.c
+++ b/apps/menus/plugin_menu.c
@@ -24,12 +24,12 @@
#include "config.h"
#include "lang.h"
#include "menu.h"
+#include "action.h"
#include "settings.h"
#include "rbpaths.h"
#include "root_menu.h"
#include "tree.h"
-
enum {
GAMES,
APPS,
@@ -45,30 +45,67 @@ static const struct {
{ PLUGIN_DEMOS_DIR, LANG_PLUGIN_DEMOS },
};
+/* if handler is active we are waiting to reenter menu */
+static void pm_handler(unsigned short id, void *data)
+{
+ remove_event(id, data);
+}
+
static int plugins_menu(void* param)
{
intptr_t item = (intptr_t)param;
- struct browse_context browse;
int ret;
- browse_context_init(&browse, SHOW_PLUGINS, 0, str(items[item].id),
- Icon_Plugin, items[item].path, NULL);
-
+ struct browse_context browse = {
+ .dirfilter = SHOW_PLUGINS,
+ .title = str(items[item].id),
+ .icon = Icon_Plugin,
+ .root = items[item].path,
+ };
+
ret = rockbox_browse(&browse);
+
if (ret == GO_TO_PREVIOUS)
return 0;
+ if (ret == GO_TO_PLUGIN)
+ add_event(SYS_EVENT_USB_INSERTED, pm_handler);
+
return ret;
}
-#define ITEM_FLAG (MENU_FUNC_USEPARAM|MENU_FUNC_CHECK_RETVAL)
+static int menu_callback(int action,
+ const struct menu_item_ex *this_item,
+ struct gui_synclist *this_list)
+{
+ (void)this_item;
+ static int selected = 0;
+
+ if (action == ACTION_ENTER_MENUITEM)
+ {
+ this_list->selected_item = selected;
+ if (!add_event(SYS_EVENT_USB_INSERTED, pm_handler))
+ {
+ action = ACTION_STD_OK; /* event exists -- reenter menu */
+ }
+ remove_event(SYS_EVENT_USB_INSERTED, pm_handler);
+ }
+ else if (action == ACTION_STD_OK)
+ {
+ selected = gui_synclist_get_sel_pos(this_list);
+ }
+ return action;
+}
+
+#define ITEM_FLAG (MENU_FUNC_CHECK_RETVAL)
+
+MENUITEM_FUNCTION_W_PARAM(games_item, ITEM_FLAG, ID2P(LANG_PLUGIN_GAMES),
+ plugins_menu, (void*)GAMES, NULL, Icon_Folder);
+MENUITEM_FUNCTION_W_PARAM(apps_item, ITEM_FLAG, ID2P(LANG_PLUGIN_APPS),
+ plugins_menu, (void*)APPS, NULL, Icon_Folder);
+MENUITEM_FUNCTION_W_PARAM(demos_item, ITEM_FLAG, ID2P(LANG_PLUGIN_DEMOS),
+ plugins_menu, (void*)DEMOS, NULL, Icon_Folder);
-MENUITEM_FUNCTION(games_item, ITEM_FLAG, ID2P(LANG_PLUGIN_GAMES),
- plugins_menu, (void*)GAMES, NULL, Icon_Folder);
-MENUITEM_FUNCTION(apps_item, ITEM_FLAG, ID2P(LANG_PLUGIN_APPS),
- plugins_menu, (void*)APPS, NULL, Icon_Folder);
-MENUITEM_FUNCTION(demos_item, ITEM_FLAG, ID2P(LANG_PLUGIN_DEMOS),
- plugins_menu, (void*)DEMOS, NULL, Icon_Folder);
-MAKE_MENU(plugin_menu, ID2P(LANG_PLUGINS), NULL,
+MAKE_MENU(plugin_menu, ID2P(LANG_PLUGINS), &menu_callback,
Icon_Plugin,
&games_item, &apps_item, &demos_item);
diff --git a/apps/menus/radio_menu.c b/apps/menus/radio_menu.c
index a6d259a21d..9fd25aa503 100644
--- a/apps/menus/radio_menu.c
+++ b/apps/menus/radio_menu.c
@@ -52,7 +52,7 @@ static int fm_recording_screen(void)
}
MENUITEM_FUNCTION(recscreen_item, 0, ID2P(LANG_RECORDING),
- fm_recording_screen, NULL, NULL, Icon_Recording);
+ fm_recording_screen, NULL, Icon_Recording);
#endif /* defined(HAVE_FMRADIO_REC) */
#if defined(HAVE_FMRADIO_REC)
@@ -65,28 +65,31 @@ static int fm_recording_settings(void)
}
MENUITEM_FUNCTION(recsettings_item, 0, ID2P(LANG_RECORDING_SETTINGS),
- fm_recording_settings, NULL, NULL, Icon_Recording);
+ fm_recording_settings, NULL, Icon_Recording);
#endif /* defined(HAVE_FMRADIO_REC) */
#endif /* HAVE_RECORDING */
#ifndef FM_PRESET
MENUITEM_FUNCTION(radio_presets_item, 0, ID2P(LANG_PRESET),
- handle_radio_presets, NULL, NULL, Icon_NOICON);
+ handle_radio_presets, NULL, Icon_NOICON);
#endif
#ifndef FM_PRESET_ADD
MENUITEM_FUNCTION(radio_addpreset_item, 0, ID2P(LANG_FM_ADD_PRESET),
- handle_radio_add_preset, NULL, NULL, Icon_NOICON);
+ handle_radio_add_preset, NULL, Icon_NOICON);
#endif
MENUITEM_FUNCTION(presetload_item, 0, ID2P(LANG_FM_PRESET_LOAD),
- preset_list_load, NULL, NULL, Icon_NOICON);
+ preset_list_load, NULL, Icon_NOICON);
MENUITEM_FUNCTION(presetsave_item, 0, ID2P(LANG_FM_PRESET_SAVE),
- preset_list_save, NULL, NULL, Icon_NOICON);
+ preset_list_save, NULL, Icon_NOICON);
MENUITEM_FUNCTION(presetclear_item, 0, ID2P(LANG_FM_PRESET_CLEAR),
- preset_list_clear, NULL, NULL, Icon_NOICON);
+ preset_list_clear, NULL, Icon_NOICON);
MENUITEM_SETTING(set_region, &global_settings.fm_region, NULL);
MENUITEM_SETTING(force_mono, &global_settings.fm_force_mono, NULL);
+#if defined(HAVE_RDS_CAP) && defined(CONFIG_RTC)
+MENUITEM_SETTING(sync_rds_time, &global_settings.sync_rds_time, NULL);
+#endif
#ifndef FM_MODE
extern int radio_mode;
@@ -117,15 +120,14 @@ static int toggle_radio_mode(void)
RADIO_PRESET_MODE : RADIO_SCAN_MODE;
return 0;
}
-MENUITEM_FUNCTION_DYNTEXT(radio_mode_item, 0,
- toggle_radio_mode, NULL,
- get_mode_text, mode_speak_item,
- NULL, NULL, Icon_NOICON);
+MENUITEM_FUNCTION_DYNTEXT(radio_mode_item, 0, toggle_radio_mode,
+ get_mode_text, mode_speak_item,
+ NULL, NULL, Icon_NOICON);
#endif
-MENUITEM_FUNCTION(scan_presets_item, MENU_FUNC_USEPARAM,
- ID2P(LANG_FM_SCAN_PRESETS),
- presets_scan, NULL, NULL, Icon_NOICON);
+MENUITEM_FUNCTION_W_PARAM(scan_presets_item, 0,
+ ID2P(LANG_FM_SCAN_PRESETS),
+ presets_scan, NULL, NULL, Icon_NOICON);
MAKE_MENU(radio_settings_menu, ID2P(LANG_FM_MENU), NULL,
Icon_Radio_screen,
@@ -147,5 +149,8 @@ MAKE_MENU(radio_settings_menu, ID2P(LANG_FM_MENU), NULL,
#ifdef FM_RECORDING_SETTINGS
&recsettings_item,
#endif
+#if defined(HAVE_RDS_CAP) && defined(CONFIG_RTC)
+ &sync_rds_time,
+#endif
&scan_presets_item);
diff --git a/apps/menus/recording_menu.c b/apps/menus/recording_menu.c
index a870a5ee9b..9ef0eb6bac 100644
--- a/apps/menus/recording_menu.c
+++ b/apps/menus/recording_menu.c
@@ -63,6 +63,7 @@
#include "list.h"
#include "viewport.h"
#include "exported_menus.h"
+#include "pathfuncs.h"
static bool no_source_in_menu = false;
static int recmenu_callback(int action,
@@ -91,11 +92,11 @@ static int recsource_func(void)
#endif
return set_option(str(LANG_RECORDING_SOURCE),
- &global_settings.rec_source, INT, names,
+ &global_settings.rec_source, RB_INT, names,
n_opts, NULL );
}
-MENUITEM_FUNCTION(recsource, 0, ID2P(LANG_RECORDING_SOURCE),
- recsource_func, NULL, recmenu_callback, Icon_Menu_setting);
+MENUITEM_FUNCTION(recsource, 0, ID2P(LANG_RECORDING_SOURCE),
+ recsource_func, recmenu_callback, Icon_Menu_setting);
/* Makes an options list from a source list of options and indexes */
static void make_options_from_indexes(const struct opt_items *src_names,
@@ -171,7 +172,7 @@ static int recfrequency_func(void)
}
ret = set_option(str(LANG_FREQUENCY),
- &rec_frequency, INT, opts, n_opts, NULL );
+ &rec_frequency, RB_INT, opts, n_opts, NULL );
if (!ret
HAVE_SPDIF_REC_( && global_settings.rec_source != REC_SRC_SPDIF)
@@ -188,7 +189,7 @@ static int recfrequency_func(void)
return ret;
} /* recfrequency */
MENUITEM_FUNCTION(recfrequency, 0, ID2P(LANG_FREQUENCY),
- recfrequency_func, NULL, NULL, Icon_Menu_setting);
+ recfrequency_func, NULL, Icon_Menu_setting);
static int recchannels_func(void)
@@ -221,7 +222,7 @@ static int recchannels_func(void)
make_options_from_indexes(names, table, n_opts, opts);
ret = set_option(str(LANG_CHANNELS), &rec_channels,
- INT, opts, n_opts, NULL );
+ RB_INT, opts, n_opts, NULL );
if (!ret)
global_settings.rec_channels = table[rec_channels];
@@ -229,7 +230,7 @@ static int recchannels_func(void)
return ret;
}
MENUITEM_FUNCTION(recchannels, 0, ID2P(LANG_CHANNELS),
- recchannels_func, NULL, NULL, Icon_Menu_setting);
+ recchannels_func, NULL, Icon_Menu_setting);
static int recmonomode_func(void)
{
@@ -241,7 +242,7 @@ static int recmonomode_func(void)
int rec_mono_mode = global_settings.rec_mono_mode;
bool ret = set_option(str(LANG_RECORDING_MONO_MODE), &rec_mono_mode,
- INT, names, 3, NULL );
+ RB_INT, names, 3, NULL );
if (rec_mono_mode != global_settings.rec_mono_mode)
global_settings.rec_mono_mode = rec_mono_mode;
@@ -249,7 +250,7 @@ static int recmonomode_func(void)
return ret;
}
MENUITEM_FUNCTION(recmonomode, 0, ID2P(LANG_RECORDING_MONO_MODE),
- recmonomode_func, NULL, NULL, Icon_Menu_setting);
+ recmonomode_func, NULL, Icon_Menu_setting);
static int recformat_func(void)
{
@@ -261,7 +262,7 @@ static int recformat_func(void)
};
int rec_format = global_settings.rec_format;
- bool res = set_option(str(LANG_RECORDING_FORMAT), &rec_format, INT,
+ bool res = set_option(str(LANG_FORMAT), &rec_format, RB_INT,
names, REC_NUM_FORMATS, NULL );
if (rec_format != global_settings.rec_format)
@@ -272,12 +273,11 @@ static int recformat_func(void)
return res;
} /* recformat */
-MENUITEM_FUNCTION(recformat, 0, ID2P(LANG_RECORDING_FORMAT),
- recformat_func, NULL, NULL, Icon_Menu_setting);
+MENUITEM_FUNCTION(recformat, 0, ID2P(LANG_FORMAT),
+ recformat_func, NULL, Icon_Menu_setting);
MENUITEM_FUNCTION(enc_global_config_menu_item, 0, ID2P(LANG_ENCODER_SETTINGS),
- enc_global_config_menu,
- NULL, NULL, Icon_Submenu);
+ enc_global_config_menu, NULL, Icon_Submenu);
static int recmenu_callback(int action,
const struct menu_item_ex *this_item,
@@ -306,13 +306,14 @@ MENUITEM_SETTING(rec_prerecord_time, &global_settings.rec_prerecord_time, NULL);
static int clear_rec_directory(void)
{
- strcpy(global_settings.rec_directory, REC_BASE_DIR);
+ path_append(global_settings.rec_directory, REC_BASE_DIR,
+ PA_SEP_HARD, sizeof(global_settings.rec_directory));
settings_save();
splash(HZ, ID2P(LANG_RESET_DONE_CLEAR));
return false;
}
-MENUITEM_FUNCTION(clear_rec_directory_item, 0, ID2P(LANG_CLEAR_REC_DIR),
- clear_rec_directory, NULL, NULL, Icon_Folder);
+MENUITEM_FUNCTION(clear_rec_directory_item, 0, ID2P(LANG_CLEAR_REC_DIR),
+ clear_rec_directory, NULL, Icon_Folder);
MENUITEM_SETTING(cliplight, &global_settings.cliplight, NULL);
@@ -330,11 +331,11 @@ static int agc_preset_func(void)
if (global_settings.rec_source)
return set_option(str(LANG_RECORDING_AGC_PRESET),
&global_settings.rec_agc_preset_line,
- INT, names, 6, NULL );
+ RB_INT, names, 6, NULL );
else
return set_option(str(LANG_RECORDING_AGC_PRESET),
&global_settings.rec_agc_preset_mic,
- INT, names, 6, NULL );
+ RB_INT, names, 6, NULL );
}
static int agc_cliptime_func(void)
@@ -348,12 +349,12 @@ static int agc_cliptime_func(void)
};
return set_option(str(LANG_RECORDING_AGC_CLIPTIME),
&global_settings.rec_agc_cliptime,
- INT, names, 5, NULL );
+ RB_INT, names, 5, NULL );
}
-MENUITEM_FUNCTION(agc_preset, 0, ID2P(LANG_RECORDING_AGC_PRESET),
- agc_preset_func, NULL, NULL, Icon_Menu_setting);
-MENUITEM_FUNCTION(agc_cliptime, 0, ID2P(LANG_RECORDING_AGC_CLIPTIME),
- agc_cliptime_func, NULL, NULL, Icon_Menu_setting);
+MENUITEM_FUNCTION(agc_preset, 0, ID2P(LANG_RECORDING_AGC_PRESET),
+ agc_preset_func, NULL, Icon_Menu_setting);
+MENUITEM_FUNCTION(agc_cliptime, 0, ID2P(LANG_RECORDING_AGC_CLIPTIME),
+ agc_cliptime_func, NULL, Icon_Menu_setting);
#endif /* HAVE_AGC */
/** Rec trigger **/
@@ -381,7 +382,7 @@ static enum themable_icons trigger_get_icon(int selected_item, void * data)
static const char * trigger_get_name(int selected_item, void * data,
char * buffer, size_t buffer_len)
{
- const struct settings_list **settings =
+ const struct settings_list **settings =
(const struct settings_list **)data;
const struct settings_list *s = settings[selected_item / 2];
if ((selected_item % 2) == 0) /* header */
@@ -463,30 +464,30 @@ int rectrigger(void)
/* TODO: what to do if there is < 4 lines on the screen? */
settings[TRIGGER_MODE] =
- find_setting(&global_settings.rec_trigger_mode, NULL);
+ find_setting(&global_settings.rec_trigger_mode);
settings[TRIGGER_TYPE] =
- find_setting(&global_settings.rec_trigger_type, NULL);
+ find_setting(&global_settings.rec_trigger_type);
settings[PRERECORD_TIME] =
- find_setting(&global_settings.rec_prerecord_time, NULL);
+ find_setting(&global_settings.rec_prerecord_time);
settings[START_DURATION] =
- find_setting(&global_settings.rec_start_duration, NULL);
+ find_setting(&global_settings.rec_start_duration);
settings[STOP_POSTREC] =
- find_setting(&global_settings.rec_stop_postrec, NULL);
+ find_setting(&global_settings.rec_stop_postrec);
settings[STOP_GAP] =
- find_setting(&global_settings.rec_stop_gap, NULL);
+ find_setting(&global_settings.rec_stop_gap);
if (global_settings.peak_meter_dbfs) /* show the dB settings */
{
settings[START_THRESHOLD] =
- find_setting(&global_settings.rec_start_thres_db, NULL);
+ find_setting(&global_settings.rec_start_thres_db);
settings[STOP_THRESHOLD] =
- find_setting(&global_settings.rec_stop_thres_db, NULL);
+ find_setting(&global_settings.rec_stop_thres_db);
}
else
{
settings[START_THRESHOLD] =
- find_setting(&global_settings.rec_start_thres_linear, NULL);
+ find_setting(&global_settings.rec_start_thres_linear);
settings[STOP_THRESHOLD] =
- find_setting(&global_settings.rec_stop_thres_linear, NULL);
+ find_setting(&global_settings.rec_stop_thres_linear);
}
gui_synclist_init(&lists, trigger_get_name, settings, false, 2, vp);
gui_synclist_set_nb_items(&lists, TRIG_OPTION_COUNT*2);
@@ -494,7 +495,7 @@ int rectrigger(void)
/* restart trigger with new values */
settings_apply_trigger();
peak_meter_trigger (global_settings.rec_trigger_mode != TRIG_MODE_OFF);
-
+
trigger_speak_item(settings, 0, true);
while (!done)
@@ -506,7 +507,7 @@ int rectrigger(void)
settings_apply_trigger();
changed = false;
}
-
+
FOR_NB_SCREENS(i)
screens[i].set_viewport(&triggervp[i]);
peak_meter_draw_trig(trig_xpos, trig_ypos, trig_width, NB_SCREENS);
@@ -571,18 +572,19 @@ int rectrigger(void)
settings_save();
return 0;
}
-MENUITEM_FUNCTION(rectrigger_item, 0, ID2P(LANG_RECORD_TRIGGER),
- rectrigger, NULL, NULL, Icon_Menu_setting);
+MENUITEM_FUNCTION(rectrigger_item, 0, ID2P(LANG_RECORD_TRIGGER),
+ rectrigger, NULL, Icon_Menu_setting);
static struct browse_folder_info rec_config_browse = {RECPRESETS_DIR, SHOW_CFG};
-MENUITEM_FUNCTION(browse_recconfigs, MENU_FUNC_USEPARAM, ID2P(LANG_CUSTOM_CFG),
- browse_folder, (void*)&rec_config_browse, NULL, Icon_Config);
+MENUITEM_FUNCTION_W_PARAM(browse_recconfigs, 0, ID2P(LANG_CUSTOM_CFG),
+ browse_folder, (void*)&rec_config_browse,
+ NULL, Icon_Config);
static int write_settings_file(void)
{
return settings_save_config(SETTINGS_SAVE_RECPRESETS);
}
-MENUITEM_FUNCTION(save_recpresets_item, 0, ID2P(LANG_SAVE_SETTINGS),
- write_settings_file, NULL, NULL, Icon_Config);
+MENUITEM_FUNCTION(save_recpresets_item, 0, ID2P(LANG_SAVE_SETTINGS),
+ write_settings_file, NULL, Icon_Config);
MAKE_MENU(recording_settings_menu, ID2P(LANG_RECORDING_SETTINGS),
NULL, Icon_Recording,
@@ -614,5 +616,5 @@ int recording_menu(bool no_source)
return retval;
};
-MENUITEM_FUNCTION(recording_settings, MENU_FUNC_USEPARAM, ID2P(LANG_RECORDING_SETTINGS),
+MENUITEM_FUNCTION_W_PARAM(recording_settings, 0, ID2P(LANG_RECORDING_SETTINGS),
recording_menu, 0, NULL, Icon_Recording);
diff --git a/apps/menus/settings_menu.c b/apps/menus/settings_menu.c
index a5e747651a..a71245cf80 100644
--- a/apps/menus/settings_menu.c
+++ b/apps/menus/settings_menu.c
@@ -48,7 +48,6 @@
#ifdef HAVE_DIRCACHE
#include "dircache.h"
#endif
-#include "folder_select.h"
#ifndef HAS_BUTTON_HOLD
#include "mask_select.h"
#endif
@@ -56,6 +55,8 @@
#include "governor-ibasso.h"
#include "usb-ibasso.h"
#endif
+#include "plugin.h"
+#include "onplay.h"
#ifndef HAS_BUTTON_HOLD
static int selectivesoftlock_callback(int action,
@@ -133,8 +134,7 @@ static void tagcache_update_with_splash(void)
static int dirs_to_scan(void)
{
- if (folder_select(global_settings.tagcache_scan_paths,
- sizeof(global_settings.tagcache_scan_paths)))
+ if(plugin_load(VIEWERS_DIR"/db_folder_select.rock", NULL) > PLUGIN_OK)
{
static const char *lines[] = {ID2P(LANG_TAGCACHE_BUSY),
ID2P(LANG_TAGCACHE_FORCE_UPDATE)};
@@ -151,22 +151,20 @@ MENUITEM_SETTING(tagcache_ram, &global_settings.tagcache_ram, NULL);
#endif
MENUITEM_SETTING(tagcache_autoupdate, &global_settings.tagcache_autoupdate, NULL);
MENUITEM_FUNCTION(tc_init, 0, ID2P(LANG_TAGCACHE_FORCE_UPDATE),
- (int(*)(void))tagcache_rebuild_with_splash,
- NULL, NULL, Icon_NOICON);
+ (int(*)(void))tagcache_rebuild_with_splash, NULL, Icon_NOICON);
MENUITEM_FUNCTION(tc_update, 0, ID2P(LANG_TAGCACHE_UPDATE),
- (int(*)(void))tagcache_update_with_splash,
- NULL, NULL, Icon_NOICON);
+ (int(*)(void))tagcache_update_with_splash, NULL, Icon_NOICON);
MENUITEM_SETTING(runtimedb, &global_settings.runtimedb, NULL);
MENUITEM_FUNCTION(tc_export, 0, ID2P(LANG_TAGCACHE_EXPORT),
- tagtree_export, NULL,
- NULL, Icon_NOICON);
+ tagtree_export,
+ NULL, Icon_NOICON);
MENUITEM_FUNCTION(tc_import, 0, ID2P(LANG_TAGCACHE_IMPORT),
- tagtree_import, NULL,
- NULL, Icon_NOICON);
+ tagtree_import,
+ NULL, Icon_NOICON);
MENUITEM_FUNCTION(tc_paths, 0, ID2P(LANG_SELECT_DATABASE_DIRS),
- dirs_to_scan, NULL, NULL, Icon_NOICON);
+ dirs_to_scan, NULL, Icon_NOICON);
MAKE_MENU(tagcache_menu, ID2P(LANG_TAGCACHE), 0, Icon_NOICON,
#ifdef HAVE_TC_RAMCACHE
@@ -195,13 +193,14 @@ MENUITEM_SETTING(browse_current, &global_settings.browse_current, NULL);
MENUITEM_SETTING(show_path_in_browser, &global_settings.show_path_in_browser, NULL);
static int clear_start_directory(void)
{
- strcpy(global_settings.start_directory, "/");
+ path_append(global_settings.start_directory, PATH_ROOTSTR,
+ PA_SEP_HARD, sizeof(global_settings.start_directory));
settings_save();
splash(HZ, ID2P(LANG_RESET_DONE_CLEAR));
return false;
}
MENUITEM_FUNCTION(clear_start_directory_item, 0, ID2P(LANG_RESET_START_DIR),
- clear_start_directory, NULL, NULL, Icon_file_view_menu);
+ clear_start_directory, NULL, Icon_file_view_menu);
static int fileview_callback(int action,
const struct menu_item_ex *this_item,
struct gui_synclist *this_list)
@@ -222,12 +221,30 @@ static int fileview_callback(int action,
return action;
}
-MAKE_MENU(file_menu, ID2P(LANG_FILE), 0, Icon_file_view_menu,
+static int filemenu_callback(int action,
+ const struct menu_item_ex *this_item,
+ struct gui_synclist *this_list);
+MAKE_MENU(file_menu, ID2P(LANG_FILE), filemenu_callback, Icon_file_view_menu,
&sort_case, &sort_dir, &sort_file, &interpret_numbers,
&dirfilter, &show_filename_ext, &browse_current,
&show_path_in_browser,
&clear_start_directory_item
);
+static int filemenu_callback(int action,
+ const struct menu_item_ex *this_item,
+ struct gui_synclist *this_list)
+{
+ (void)this_list;
+
+ if (action == ACTION_REQUEST_MENUITEM &&
+ this_item == &file_menu &&
+ get_onplay_context() == CONTEXT_ID3DB &&
+ get_current_activity() != ACTIVITY_SETTINGS)
+ return ACTION_EXIT_MENUITEM;
+
+ return action;
+}
+
/* FILE VIEW MENU */
/***********************************/
@@ -260,7 +277,7 @@ static int usbcharging_callback(int action,
MENUITEM_SETTING(usb_charging, &global_settings.usb_charging, usbcharging_callback);
#endif /* HAVE_USB_CHARGING_ENABLE */
MAKE_MENU(battery_menu, ID2P(LANG_BATTERY_MENU), 0, Icon_NOICON,
-#if defined(BATTERY_CAPACITY_INC) && BATTERY_CAPACITY_INC > 0
+#if BATTERY_CAPACITY_INC > 0
&battery_capacity,
#endif
#if BATTERY_TYPES_COUNT > 1
@@ -322,6 +339,11 @@ MAKE_MENU(limits_menu, ID2P(LANG_LIMITS_MENU), 0, Icon_NOICON,
,&default_glyphs
);
+#ifdef HAVE_PERCEPTUAL_VOLUME
+/* Volume adjustment */
+MENUITEM_SETTING(volume_adjust_mode, &global_settings.volume_adjust_mode, NULL);
+MENUITEM_SETTING(volume_adjust_norm_steps, &global_settings.volume_adjust_norm_steps, NULL);
+#endif
/* Keyclick menu */
MENUITEM_SETTING(keyclick, &global_settings.keyclick, NULL);
@@ -388,8 +410,8 @@ MENUITEM_SETTING(bt_selective_actions,
&global_settings.bt_selective_softlock_actions,
selectivesoftlock_callback);
MENUITEM_FUNCTION(sel_softlock_mask, 0, ID2P(LANG_SETTINGS),
- selectivesoftlock_set_mask, NULL,
- selectivesoftlock_callback, Icon_Menu_setting);
+ selectivesoftlock_set_mask, selectivesoftlock_callback,
+ Icon_Menu_setting);
MAKE_MENU(sel_softlock, ID2P(LANG_SOFTLOCK_SELECTIVE),
NULL, Icon_Menu_setting, &bt_selective_actions, &sel_softlock_mask);
@@ -408,6 +430,10 @@ MAKE_MENU(system_menu, ID2P(LANG_SYSTEM),
&disk_menu,
#endif
&limits_menu,
+#ifdef HAVE_PERCEPTUAL_VOLUME
+ &volume_adjust_mode,
+ &volume_adjust_norm_steps,
+#endif
#ifdef HAVE_QUICKSCREEN
&shortcuts_replaces_quickscreen,
#endif
@@ -554,7 +580,7 @@ static int sleeptimer_duration_cb(int action,
MENUITEM_SETTING(start_screen, &global_settings.start_in_screen, NULL);
MENUITEM_SETTING(poweroff, &global_settings.poweroff, NULL);
-MENUITEM_FUNCTION_DYNTEXT(sleeptimer_toggle, 0, toggle_sleeptimer, NULL,
+MENUITEM_FUNCTION_DYNTEXT(sleeptimer_toggle, 0, toggle_sleeptimer,
sleep_timer_getname, sleep_timer_voice, NULL,
NULL, Icon_NOICON);
MENUITEM_SETTING(sleeptimer_duration,
@@ -564,15 +590,30 @@ MENUITEM_SETTING(sleeptimer_on_startup,
&global_settings.sleeptimer_on_startup, NULL);
MENUITEM_SETTING(keypress_restarts_sleeptimer,
&global_settings.keypress_restarts_sleeptimer, NULL);
+MENUITEM_SETTING(show_shutdown_message, &global_settings.show_shutdown_message, NULL);
+
+#if defined(BUTTON_REC) || \
+ (CONFIG_KEYPAD == GIGABEAT_PAD) || \
+ (CONFIG_KEYPAD == IPOD_4G_PAD) || \
+ (CONFIG_KEYPAD == IRIVER_H10_PAD)
+#define SETTINGS_CLEAR_ON_HOLD
+MENUITEM_SETTING(clear_settings_on_hold,
+ &global_settings.clear_settings_on_hold, NULL);
+#endif
MAKE_MENU(startup_shutdown_menu, ID2P(LANG_STARTUP_SHUTDOWN),
0, Icon_System_menu,
+ &show_shutdown_message,
&start_screen,
&poweroff,
&sleeptimer_toggle,
&sleeptimer_duration,
&sleeptimer_on_startup,
- &keypress_restarts_sleeptimer
+ &keypress_restarts_sleeptimer,
+#if defined(SETTINGS_CLEAR_ON_HOLD)
+ &clear_settings_on_hold,
+#undef SETTINGS_CLEAR_ON_HOLD
+#endif
);
/* STARTUP/SHUTDOWN MENU */
@@ -650,8 +691,8 @@ static int autoresume_nexttrack_callback(int action,
break;
case ACTION_EXIT_MENUITEM:
if (global_settings.autoresume_automatic == AUTORESUME_NEXTTRACK_CUSTOM
- && !folder_select(global_settings.autoresume_paths,
- MAX_PATHNAME+1))
+ && plugin_load(VIEWERS_DIR"/db_folder_select.rock",
+ str(LANG_AUTORESUME)) == PLUGIN_OK)
{
global_settings.autoresume_automatic = oldval;
}
@@ -729,7 +770,7 @@ static void wps_plugin_cb(void)
}
MENUITEM_FUNCTION(wps_set_context_plugin, 0,
ID2P(LANG_OPEN_PLUGIN_SET_WPS_CONTEXT_PLUGIN),
- wps_plugin_cb, NULL, NULL, Icon_Plugin);
+ wps_plugin_cb, NULL, Icon_Plugin);
/* WPS_CONTEXT_PLUGIN */
/***********************************/
@@ -751,8 +792,8 @@ MAKE_MENU(hotkey_menu, ID2P(LANG_HOTKEY), 0, Icon_NOICON,
static struct browse_folder_info langs = { LANG_DIR, SHOW_LNG };
-MENUITEM_FUNCTION(browse_langs, MENU_FUNC_USEPARAM, ID2P(LANG_LANGUAGE),
- browse_folder, (void*)&langs, NULL, Icon_Language);
+MENUITEM_FUNCTION_W_PARAM(browse_langs, 0, ID2P(LANG_LANGUAGE),
+ browse_folder, (void*)&langs, NULL, Icon_Language);
MAKE_MENU(settings_menu_item, ID2P(LANG_GENERAL_SETTINGS), 0,
Icon_General_settings_menu,
diff --git a/apps/menus/sound_menu.c b/apps/menus/sound_menu.c
index 00fe006399..d72e3c7fa7 100644
--- a/apps/menus/sound_menu.c
+++ b/apps/menus/sound_menu.c
@@ -54,13 +54,17 @@ static int volume_limit_callback(int action,
(void)this_list;
static struct int_setting volume_limit_int_setting;
- volume_limit_int_setting.option_callback = NULL;
- volume_limit_int_setting.unit = UNIT_DB;
- volume_limit_int_setting.min = sound_min(SOUND_VOLUME);
- volume_limit_int_setting.max = sound_max(SOUND_VOLUME);
- volume_limit_int_setting.step = sound_steps(SOUND_VOLUME);
- volume_limit_int_setting.formatter = vol_limit_format;
- volume_limit_int_setting.get_talk_id = NULL;
+
+ volume_limit_int_setting = (struct int_setting)
+ {
+ .option_callback = NULL,
+ .unit = UNIT_DB,
+ .step = sound_steps(SOUND_VOLUME),
+ .min = sound_min(SOUND_VOLUME),
+ .max = sound_max(SOUND_VOLUME),
+ .formatter = vol_limit_format,
+ .get_talk_id = NULL
+ };
struct settings_list setting;
setting.flags = F_BANFROMQS|F_INT_SETTING|F_T_INT|F_NO_WRAP;
@@ -73,6 +77,7 @@ static int volume_limit_callback(int action,
case ACTION_ENTER_MENUITEM:
setting.setting = &global_settings.volume_limit;
option_screen(&setting, NULL, false, ID2P(LANG_VOLUME_LIMIT));
+ /* Fallthrough */
case ACTION_EXIT_MENUITEM: /* on exit */
setvol();
break;
diff --git a/apps/menus/theme_menu.c b/apps/menus/theme_menu.c
index 2a50ed44b5..1b501452d0 100644
--- a/apps/menus/theme_menu.c
+++ b/apps/menus/theme_menu.c
@@ -62,7 +62,7 @@ static int clear_main_backdrop(void)
return 0;
}
MENUITEM_FUNCTION(clear_main_bd, 0, ID2P(LANG_CLEAR_BACKDROP),
- clear_main_backdrop, NULL, NULL, Icon_NOICON);
+ clear_main_backdrop, NULL, Icon_NOICON);
#endif
#ifdef HAVE_LCD_COLOR
@@ -129,20 +129,20 @@ static int reset_color(void)
settings_apply_skins();
return 0;
}
-MENUITEM_FUNCTION(set_bg_col, MENU_FUNC_USEPARAM, ID2P(LANG_BACKGROUND_COLOR),
- set_color_func, (void*)COLOR_BG, NULL, Icon_NOICON);
-MENUITEM_FUNCTION(set_fg_col, MENU_FUNC_USEPARAM, ID2P(LANG_FOREGROUND_COLOR),
- set_color_func, (void*)COLOR_FG, NULL, Icon_NOICON);
-MENUITEM_FUNCTION(set_lss_col, MENU_FUNC_USEPARAM, ID2P(LANG_SELECTOR_START_COLOR),
- set_color_func, (void*)COLOR_LSS, NULL, Icon_NOICON);
-MENUITEM_FUNCTION(set_lse_col, MENU_FUNC_USEPARAM, ID2P(LANG_SELECTOR_END_COLOR),
- set_color_func, (void*)COLOR_LSE, NULL, Icon_NOICON);
-MENUITEM_FUNCTION(set_lst_col, MENU_FUNC_USEPARAM, ID2P(LANG_SELECTOR_TEXT_COLOR),
- set_color_func, (void*)COLOR_LST, NULL, Icon_NOICON);
-MENUITEM_FUNCTION(set_sep_col, MENU_FUNC_USEPARAM, ID2P(LANG_LIST_SEPARATOR_COLOR),
- set_color_func, (void*)COLOR_SEP, NULL, Icon_NOICON);
+MENUITEM_FUNCTION_W_PARAM(set_bg_col, 0, ID2P(LANG_BACKGROUND_COLOR),
+ set_color_func, (void*)COLOR_BG, NULL, Icon_NOICON);
+MENUITEM_FUNCTION_W_PARAM(set_fg_col, 0, ID2P(LANG_FOREGROUND_COLOR),
+ set_color_func, (void*)COLOR_FG, NULL, Icon_NOICON);
+MENUITEM_FUNCTION_W_PARAM(set_lss_col, 0, ID2P(LANG_SELECTOR_START_COLOR),
+ set_color_func, (void*)COLOR_LSS, NULL, Icon_NOICON);
+MENUITEM_FUNCTION_W_PARAM(set_lse_col, 0, ID2P(LANG_SELECTOR_END_COLOR),
+ set_color_func, (void*)COLOR_LSE, NULL, Icon_NOICON);
+MENUITEM_FUNCTION_W_PARAM(set_lst_col, 0, ID2P(LANG_SELECTOR_TEXT_COLOR),
+ set_color_func, (void*)COLOR_LST, NULL, Icon_NOICON);
+MENUITEM_FUNCTION_W_PARAM(set_sep_col, 0, ID2P(LANG_LIST_SEPARATOR_COLOR),
+ set_color_func, (void*)COLOR_SEP, NULL, Icon_NOICON);
MENUITEM_FUNCTION(reset_colors, 0, ID2P(LANG_RESET_COLORS),
- reset_color, NULL, NULL, Icon_NOICON);
+ reset_color, NULL, Icon_NOICON);
MAKE_MENU(lss_settings, ID2P(LANG_SELECTOR_COLOR_MENU),
NULL, Icon_NOICON,
@@ -165,7 +165,6 @@ MAKE_MENU(colors_settings, ID2P(LANG_COLORS_MENU),
/* BARS MENU */
/* */
-
static int statusbar_callback_ex(int action,const struct menu_item_ex *this_item,
enum screen_type screen)
{
@@ -206,8 +205,7 @@ static int statusbar_callback(int action,
MENUITEM_SETTING(scrollbar_item, &global_settings.scrollbar, NULL);
MENUITEM_SETTING(scrollbar_width, &global_settings.scrollbar_width, NULL);
-MENUITEM_SETTING(statusbar, &global_settings.statusbar,
- statusbar_callback);
+MENUITEM_SETTING(statusbar, &global_settings.statusbar, statusbar_callback);
#ifdef HAVE_REMOTE_LCD
MENUITEM_SETTING(remote_statusbar, &global_settings.remote_statusbar,
statusbar_callback_remote);
@@ -251,9 +249,12 @@ int browse_folder(void *param)
char selected[MAX_FILENAME+10];
const struct browse_folder_info *info =
(const struct browse_folder_info*)param;
- struct browse_context browse;
- browse_context_init(&browse, info->show_options, 0,
- NULL, NOICON, info->dir, NULL);
+
+ struct browse_context browse = {
+ .dirfilter = info->show_options,
+ .icon = Icon_NOICON,
+ .root = info->dir,
+ };
/* if we are in a special settings folder, center the current setting */
switch(info->show_options)
@@ -325,36 +326,28 @@ int browse_folder(void *param)
return rockbox_browse(&browse);
}
-MENUITEM_FUNCTION(browse_fonts, MENU_FUNC_USEPARAM,
- ID2P(LANG_CUSTOM_FONT),
- browse_folder, (void*)&fonts, NULL, Icon_Font);
+MENUITEM_FUNCTION_W_PARAM(browse_fonts, 0, ID2P(LANG_CUSTOM_FONT),
+ browse_folder, (void*)&fonts, NULL, Icon_Font);
-MENUITEM_FUNCTION(browse_sbs, MENU_FUNC_USEPARAM,
- ID2P(LANG_BASE_SKIN),
- browse_folder, (void*)&sbs, NULL, Icon_Wps);
+MENUITEM_FUNCTION_W_PARAM(browse_sbs, 0, ID2P(LANG_BASE_SKIN),
+ browse_folder, (void*)&sbs, NULL, Icon_Wps);
#if CONFIG_TUNER
-MENUITEM_FUNCTION(browse_fms, MENU_FUNC_USEPARAM,
- ID2P(LANG_RADIOSCREEN),
- browse_folder, (void*)&fms, NULL, Icon_Wps);
+MENUITEM_FUNCTION_W_PARAM(browse_fms, 0, ID2P(LANG_RADIOSCREEN),
+ browse_folder, (void*)&fms, NULL, Icon_Wps);
#endif
-MENUITEM_FUNCTION(browse_wps, MENU_FUNC_USEPARAM,
- ID2P(LANG_WHILE_PLAYING),
- browse_folder, (void*)&wps, NULL, Icon_Wps);
+MENUITEM_FUNCTION_W_PARAM(browse_wps, 0, ID2P(LANG_WHILE_PLAYING),
+ browse_folder, (void*)&wps, NULL, Icon_Wps);
#ifdef HAVE_REMOTE_LCD
-MENUITEM_FUNCTION(browse_rwps, MENU_FUNC_USEPARAM,
- ID2P(LANG_REMOTE_WHILE_PLAYING),
- browse_folder, (void*)&rwps, NULL, Icon_Wps);
-MENUITEM_FUNCTION(browse_rsbs, MENU_FUNC_USEPARAM,
- ID2P(LANG_REMOTE_BASE_SKIN),
- browse_folder, (void*)&rsbs, NULL, Icon_Wps);
+MENUITEM_FUNCTION_W_PARAM(browse_rwps, 0, ID2P(LANG_REMOTE_WHILE_PLAYING),
+ browse_folder, (void*)&rwps, NULL, Icon_Wps);
+MENUITEM_FUNCTION_W_PARAM(browse_rsbs, 0, ID2P(LANG_REMOTE_BASE_SKIN),
+ browse_folder, (void*)&rsbs, NULL, Icon_Wps);
#if CONFIG_TUNER
-MENUITEM_FUNCTION(browse_rfms, MENU_FUNC_USEPARAM,
- ID2P(LANG_REMOTE_RADIOSCREEN),
- browse_folder, (void*)&rfms, NULL, Icon_Wps);
+MENUITEM_FUNCTION_W_PARAM(browse_rfms, 0, ID2P(LANG_REMOTE_RADIOSCREEN),
+ browse_folder, (void*)&rfms, NULL, Icon_Wps);
#endif
#endif
-
static int showicons_callback(int action,
const struct menu_item_ex *this_item,
struct gui_synclist *this_list)
@@ -376,9 +369,8 @@ static int showicons_callback(int action,
}
MENUITEM_SETTING(show_icons, &global_settings.show_icons, showicons_callback);
-MENUITEM_FUNCTION(browse_themes, MENU_FUNC_USEPARAM,
- ID2P(LANG_CUSTOM_THEME),
- browse_folder, (void*)&themes, NULL, Icon_Config);
+MENUITEM_FUNCTION_W_PARAM(browse_themes, 0, ID2P(LANG_CUSTOM_THEME),
+ browse_folder, (void*)&themes, NULL, Icon_Config);
MENUITEM_SETTING(cursor_style, &global_settings.cursor_style, NULL);
#if LCD_DEPTH > 1
MENUITEM_SETTING(sep_menu, &global_settings.list_separator_height, NULL);
diff --git a/apps/menus/time_menu.c b/apps/menus/time_menu.c
index edd2e19a2b..e6b5637047 100644
--- a/apps/menus/time_menu.c
+++ b/apps/menus/time_menu.c
@@ -73,7 +73,7 @@ static int timedate_set(void)
tm.tm_year = YEAR-1900;
}
- result = (int)set_time_screen(str(LANG_SET_TIME), &tm);
+ result = (int)set_time_screen(str(LANG_SET_TIME), &tm, true);
if(tm.tm_year != -1) {
set_time(&tm);
@@ -81,12 +81,12 @@ static int timedate_set(void)
return result;
}
-MENUITEM_FUNCTION(time_set, 0, ID2P(LANG_SET_TIME),
- timedate_set, NULL, NULL, Icon_NOICON);
+MENUITEM_FUNCTION(time_set, 0, ID2P(LANG_SET_TIME),
+ timedate_set, NULL, Icon_NOICON);
MENUITEM_SETTING(timeformat, &global_settings.timeformat, NULL);
#ifdef HAVE_RTC_ALARM
MENUITEM_FUNCTION(alarm_screen_call, 0, ID2P(LANG_ALARM_MOD_ALARM_MENU),
- alarm_screen, NULL, NULL, Icon_NOICON);
+ alarm_screen, NULL, Icon_NOICON);
#if CONFIG_TUNER || defined(HAVE_RECORDING)
#if CONFIG_TUNER && !defined(HAVE_RECORDING)
@@ -135,11 +135,11 @@ static int alarm_setting(void)
#endif
return set_option(str(LANG_ALARM_WAKEUP_SCREEN),
&global_settings.alarm_wake_up_screen,
- INT, items, i, NULL);
+ RB_INT, items, i, NULL);
}
MENUITEM_FUNCTION(alarm_wake_up_screen, 0, ID2P(LANG_ALARM_WAKEUP_SCREEN),
- alarm_setting, NULL, alarm_callback, Icon_Menu_setting);
+ alarm_setting, alarm_callback, Icon_Menu_setting);
#endif /* CONFIG_TUNER || defined(HAVE_RECORDING) */
#endif /* HAVE_RTC_ALARM */
@@ -242,6 +242,9 @@ static int time_menu_callback(int action,
return action;
}
+#if defined(HAVE_RDS_CAP) && defined(CONFIG_RTC)
+MENUITEM_SETTING(sync_rds_time, &global_settings.sync_rds_time, NULL);
+#endif
MAKE_MENU(time_menu, ID2P(LANG_TIME_MENU), time_menu_callback, Icon_NOICON,
&time_set,
@@ -251,6 +254,9 @@ MAKE_MENU(time_menu, ID2P(LANG_TIME_MENU), time_menu_callback, Icon_NOICON,
&alarm_wake_up_screen,
#endif
#endif
+#if defined(HAVE_RDS_CAP) && defined(CONFIG_RTC)
+ &sync_rds_time,
+#endif
&timeformat);
int time_screen(void* ignored)
@@ -291,8 +297,10 @@ int time_screen(void* ignored)
because they always report "02:02:02" as time.
*/
struct tm *tm = get_time();
- if (tm->tm_year==102 && tm->tm_hour==2 && tm->tm_min==2 && tm->tm_sec==2) {
+ if (tm->tm_year==102 && tm->tm_hour==2 && tm->tm_min==2 && tm->tm_sec==2)
+ {
splash(4*HZ, "Can't set time/date due to hardware issues!");
+ pop_current_activity();
return 0;
}
#endif
diff --git a/apps/misc.c b/apps/misc.c
index 96ad534c68..cb5f1cea9c 100644
--- a/apps/misc.c
+++ b/apps/misc.c
@@ -54,11 +54,9 @@
#include "power.h"
#include "powermgmt.h"
#include "backlight.h"
-#include "version.h"
#include "font.h"
#include "splash.h"
#include "tagcache.h"
-#include "scrobbler.h"
#include "sound.h"
#include "playlist.h"
#include "yesno.h"
@@ -101,6 +99,10 @@
#endif
#endif
+#ifndef PLUGIN
+#include "core_alloc.h" /*core_load_bmp()*/
+#endif
+
#ifdef HAVE_HARDWARE_CLICK
#include "piezo.h"
#endif
@@ -269,10 +271,20 @@ bool settings_parseline(char* line, char** name, char** value)
return false;
*name = line;
- *ptr = 0;
+ *ptr = '\0'; /* terminate previous */
ptr++;
ptr = skip_whitespace(ptr);
*value = ptr;
+
+ /* strip whitespace from the right side of value */
+ ptr += strlen(ptr);
+ ptr--;
+ while ((ptr > (*value) - 1) && isspace(*ptr))
+ {
+ *ptr = '\0';
+ ptr--;
+ }
+
return true;
}
@@ -289,10 +301,17 @@ static void system_restore(void)
tree_restore();
}
-static bool clean_shutdown(void (*callback)(void *), void *parameter)
+static bool clean_shutdown(enum shutdown_type sd_type,
+ void (*callback)(void *), void *parameter)
{
long msg_id = -1;
+ if (!global_settings.show_shutdown_message && get_sleep_timer_active())
+ {
+ talk_force_shutup();
+ talk_disable(true);
+ }
+
status_save();
#if CONFIG_CHARGING && !defined(HAVE_POWEROFF_WHILE_CHARGING)
@@ -323,7 +342,10 @@ static bool clean_shutdown(void (*callback)(void *), void *parameter)
#endif
level = battery_level();
if (level > 10 || level < 0)
- splash(0, str(LANG_SHUTTINGDOWN));
+ {
+ if (global_settings.show_shutdown_message)
+ splash(0, str(LANG_SHUTTINGDOWN));
+ }
else
{
msg_id = LANG_WARNING_BATTERY_LOW;
@@ -362,7 +384,6 @@ static bool clean_shutdown(void (*callback)(void *), void *parameter)
#if defined(HAVE_RECORDING)
audio_close_recording();
#endif
- scrobbler_shutdown(true);
system_flush();
#ifdef HAVE_EEPROM_SETTINGS
@@ -391,7 +412,7 @@ static bool clean_shutdown(void (*callback)(void *), void *parameter)
voice_wait();
}
- shutdown_hw();
+ shutdown_hw(sd_type);
}
return false;
}
@@ -463,7 +484,7 @@ static void car_adapter_mode_processing(bool inserted)
if ((audio_status() & AUDIO_STATUS_PLAY) &&
!(audio_status() & AUDIO_STATUS_PAUSE))
{
- pause_action(true, true);
+ pause_action(true);
paused_on_unplugged = true;
}
else if (!waiting_to_resume_play)
@@ -511,14 +532,14 @@ static void hp_unplug_change(bool inserted)
if ((audio_stat & AUDIO_STATUS_PLAY) &&
headphone_caused_pause &&
global_settings.unplug_mode > 1 )
- unpause_action(true, true);
+ unpause_action(true);
headphone_caused_pause = false;
} else {
if ((audio_stat & AUDIO_STATUS_PLAY) &&
!(audio_stat & AUDIO_STATUS_PAUSE))
{
headphone_caused_pause = true;
- pause_action(false, false);
+ pause_action(false);
}
}
}
@@ -550,14 +571,14 @@ static void lo_unplug_change(bool inserted)
if ((audio_stat & AUDIO_STATUS_PLAY) &&
lineout_caused_pause &&
global_settings.unplug_mode > 1 )
- unpause_action(true, true);
+ unpause_action(true);
lineout_caused_pause = false;
} else {
if ((audio_stat & AUDIO_STATUS_PLAY) &&
!(audio_stat & AUDIO_STATUS_PAUSE))
{
lineout_caused_pause = true;
- pause_action(false, false);
+ pause_action(false);
}
}
}
@@ -604,8 +625,17 @@ long default_event_handler_ex(long event, void (*callback)(void *), void *parame
return SYS_USB_CONNECTED;
case SYS_POWEROFF:
- if (!clean_shutdown(callback, parameter))
- return SYS_POWEROFF;
+ case SYS_REBOOT:
+ {
+ enum shutdown_type sd_type;
+ if (event == SYS_POWEROFF)
+ sd_type = SHUTDOWN_POWER_OFF;
+ else
+ sd_type = SHUTDOWN_REBOOT;
+
+ if (!clean_shutdown(sd_type, callback, parameter))
+ return event;
+ }
break;
#if CONFIG_CHARGING
case SYS_CHARGER_CONNECTED:
@@ -618,7 +648,7 @@ long default_event_handler_ex(long event, void (*callback)(void *), void *parame
return SYS_CHARGER_DISCONNECTED;
case SYS_CAR_ADAPTER_RESUME:
- unpause_action(true, true);
+ unpause_action(true);
return SYS_CAR_ADAPTER_RESUME;
#endif
#ifdef HAVE_HOTSWAP_STORAGE_AS_MAIN
@@ -699,9 +729,9 @@ long default_event_handler_ex(long event, void (*callback)(void *), void *parame
if (status & AUDIO_STATUS_PLAY)
{
if (status & AUDIO_STATUS_PAUSE)
- unpause_action(true, true);
+ unpause_action(true);
else
- pause_action(true, true);
+ pause_action(true);
}
else
if (playlist_resume() != -1)
@@ -735,48 +765,6 @@ long default_event_handler(long event)
return default_event_handler_ex(event, NULL, NULL);
}
-int show_logo( void )
-{
- char version[32];
- int font_h, font_w;
-
- snprintf(version, sizeof(version), "Ver. %s", rbversion);
-
- lcd_clear_display();
-#if defined(SANSA_CLIP) || defined(SANSA_CLIPV2) || defined(SANSA_CLIPPLUS)
- /* display the logo in the blue area of the screen (bottom 48 pixels) */
- lcd_setfont(FONT_SYSFIXED);
- lcd_getstringsize((unsigned char *)"A", &font_w, &font_h);
- lcd_putsxy((LCD_WIDTH/2) - ((strlen(version)*font_w)/2),
- 0, (unsigned char *)version);
- lcd_bmp(&bm_rockboxlogo, (LCD_WIDTH - BMPWIDTH_rockboxlogo) / 2, 16);
-#else
- lcd_bmp(&bm_rockboxlogo, (LCD_WIDTH - BMPWIDTH_rockboxlogo) / 2, 10);
- lcd_setfont(FONT_SYSFIXED);
- lcd_getstringsize((unsigned char *)"A", &font_w, &font_h);
- lcd_putsxy((LCD_WIDTH/2) - ((strlen(version)*font_w)/2),
- LCD_HEIGHT-font_h, (unsigned char *)version);
-#endif
- lcd_setfont(FONT_UI);
-
- lcd_update();
-
-#ifdef HAVE_REMOTE_LCD
- lcd_remote_clear_display();
- lcd_remote_bmp(&bm_remote_rockboxlogo, 0, 10);
- lcd_remote_setfont(FONT_SYSFIXED);
- lcd_remote_getstringsize((unsigned char *)"A", &font_w, &font_h);
- lcd_remote_putsxy((LCD_REMOTE_WIDTH/2) - ((strlen(version)*font_w)/2),
- LCD_REMOTE_HEIGHT-font_h, (unsigned char *)version);
- lcd_remote_setfont(FONT_UI);
- lcd_remote_update();
-#endif
-#ifdef SIMULATOR
- sleep(HZ); /* sim is too fast to see logo */
-#endif
- return 0;
-}
-
#ifdef BOOTFILE
#if !defined(USB_NONE) && !defined(USB_HANDLED_BY_OF) || defined(HAVE_HOTSWAP_STORAGE_AS_MAIN)
/*
@@ -818,6 +806,7 @@ void check_bootfile(bool do_rolo)
}
}
mtime = info.mtime;
+ break;
}
}
closedir(dir);
@@ -842,6 +831,113 @@ void setvol(void)
settings_save();
}
+#ifdef HAVE_PERCEPTUAL_VOLUME
+static short norm_tab[MAX_NORM_VOLUME_STEPS+2];
+static int norm_tab_num_steps;
+static int norm_tab_size;
+
+static void update_norm_tab(void)
+{
+ const int lim = global_settings.volume_adjust_norm_steps;
+ if (lim == norm_tab_num_steps)
+ return;
+ norm_tab_num_steps = lim;
+
+ const int min = sound_min(SOUND_VOLUME);
+ const int max = sound_max(SOUND_VOLUME);
+ const int step = sound_steps(SOUND_VOLUME);
+
+ /* Ensure the table contains the minimum volume */
+ norm_tab[0] = min;
+ norm_tab_size = 1;
+
+ for (int i = 0; i < lim; ++i)
+ {
+ int vol = from_normalized_volume(i, min, max, lim);
+ int rem = vol % step;
+
+ vol -= rem;
+ if (abs(rem) > step/2)
+ vol += rem < 0 ? -step : step;
+
+ /* Add volume step, ignoring any duplicate entries that may
+ * occur due to rounding */
+ if (vol != norm_tab[norm_tab_size-1])
+ norm_tab[norm_tab_size++] = vol;
+ }
+
+ /* Ensure the table contains the maximum volume */
+ if (norm_tab[norm_tab_size-1] != max)
+ norm_tab[norm_tab_size++] = max;
+}
+
+void set_normalized_volume(int vol)
+{
+ update_norm_tab();
+
+ if (vol < 0)
+ vol = 0;
+ if (vol >= norm_tab_size)
+ vol = norm_tab_size - 1;
+
+ global_settings.volume = norm_tab[vol];
+}
+
+int get_normalized_volume(void)
+{
+ update_norm_tab();
+
+ int a = 0, b = norm_tab_size - 1;
+ while (a != b)
+ {
+ int i = (a + b + 1) / 2;
+ if (global_settings.volume < norm_tab[i])
+ b = i - 1;
+ else
+ a = i;
+ }
+
+ return a;
+}
+#else
+void set_normalized_volume(int vol)
+{
+ global_settings.volume = vol * sound_steps(SOUND_VOLUME);
+}
+
+int get_normalized_volume(void)
+{
+ return global_settings.volume / sound_steps(SOUND_VOLUME);
+}
+#endif
+
+void adjust_volume(int steps)
+{
+#ifdef HAVE_PERCEPTUAL_VOLUME
+ adjust_volume_ex(steps, global_settings.volume_adjust_mode);
+#else
+ adjust_volume_ex(steps, VOLUME_ADJUST_DIRECT);
+#endif
+}
+
+void adjust_volume_ex(int steps, enum volume_adjust_mode mode)
+{
+ switch (mode)
+ {
+ case VOLUME_ADJUST_PERCEPTUAL:
+#ifdef HAVE_PERCEPTUAL_VOLUME
+ set_normalized_volume(get_normalized_volume() + steps);
+ break;
+#endif
+ case VOLUME_ADJUST_DIRECT:
+ default:
+ global_settings.volume += steps * sound_steps(SOUND_VOLUME);
+ break;
+ }
+
+ setvol();
+}
+
char* strrsplt(char* str, int c)
{
char* s = strrchr(str, c);
@@ -864,27 +960,17 @@ char* strrsplt(char* str, int c)
*/
char *strip_extension(char* buffer, int buffer_size, const char *filename)
{
- char *dot = strrchr(filename, '.');
- int len;
-
- if (buffer_size <= 0)
+ if (!buffer || !filename || buffer_size <= 0)
{
return NULL;
}
- buffer_size--; /* Make room for end nil */
+ off_t dotpos = (strrchr(filename, '.') - filename) + 1;
- if (dot != 0 && filename[0] != '.')
- {
- len = dot - filename;
- len = MIN(len, buffer_size);
- }
- else
- {
- len = buffer_size;
- }
-
- strlcpy(buffer, filename, len + 1);
+ /* no match on filename beginning with '.' or beyond buffer_size */
+ if(dotpos > 1 && dotpos < buffer_size)
+ buffer_size = dotpos;
+ strmemccpy(buffer, filename, buffer_size);
return buffer;
}
@@ -1049,6 +1135,11 @@ int format_sound_value(char *buf, size_t size, int snd, int val)
int physval = sound_val2phys(snd, val);
unsigned int factor = ipow(10, numdec);
+ if (factor == 0)
+ {
+ DEBUGF("DIVISION BY ZERO: format_sound_value s:%d v:%d", snd, val);
+ factor = 1;
+ }
unsigned int av = abs(physval);
unsigned int i = av / factor;
unsigned int d = av - i*factor;
@@ -1116,7 +1207,6 @@ int read_line(int fd, char* buffer, int buffer_size)
return rdbufend >= 0 ? num_read : -1;
}
-
char* skip_whitespace(char* const str)
{
char *s = str;
@@ -1128,6 +1218,23 @@ char* skip_whitespace(char* const str)
}
#if !defined(CHECKWPS) && !defined(DBTOOL)
+
+int confirm_delete_yesno(const char *name)
+{
+ const char *lines[] = { ID2P(LANG_REALLY_DELETE), name };
+ const char *yes_lines[] = { ID2P(LANG_DELETING), name };
+ const struct text_message message = { lines, 2 };
+ const struct text_message yes_message = { yes_lines, 2 };
+ return gui_syncyesno_run(&message, &yes_message, NULL);
+}
+
+int confirm_overwrite_yesno(void)
+{
+ static const char *lines[] = { ID2P(LANG_REALLY_OVERWRITE) };
+ static const struct text_message message = { lines, 1 };
+ return gui_syncyesno_run(&message, NULL, NULL);
+}
+
/* time_split_units()
split time values depending on base unit
unit_idx: UNIT_HOUR, UNIT_MIN, UNIT_SEC, UNIT_MS
@@ -1220,14 +1327,14 @@ const char *format_time_auto(char *buffer, int buf_len, long value,
[UNIT_IDX_SEC] = 4,/* 999.59:59:0 RTL offsets */
[UNIT_IDX_MS] = 0,/* 0 .4 :7 :10 won't change */
}; /* {10,7,4,0}; Offsets */
- const uint16_t unitlock[UNIT_IDX_TIME_COUNT] =
+ static const uint16_t unitlock[UNIT_IDX_TIME_COUNT] =
{
[UNIT_IDX_HR] = UNIT_LOCK_HR,
[UNIT_IDX_MIN] = UNIT_LOCK_MIN,
[UNIT_IDX_SEC] = UNIT_LOCK_SEC,
[UNIT_IDX_MS] = 0,
}; /* unitlock */
- const uint16_t units[UNIT_IDX_TIME_COUNT] =
+ static const uint16_t units[UNIT_IDX_TIME_COUNT] =
{
[UNIT_IDX_HR] = UNIT_HOUR,
[UNIT_IDX_MIN] = UNIT_MIN,
@@ -1318,12 +1425,12 @@ const char *format_time_auto(char *buffer, int buf_len, long value,
if (!supress_unit)
{
- strlcpy(buffer, unit_strings_core[units[max_idx]], buf_len);
+ strmemccpy(buffer, unit_strings_core[units[max_idx]], buf_len);
left_offset += strlcat(buffer, " ", buf_len);
strlcat(buffer, &timebuf[offsets[base_idx]], buf_len);
}
else
- strlcpy(buffer, &timebuf[offsets[base_idx]], buf_len);
+ strmemccpy(buffer, &timebuf[offsets[base_idx]], buf_len);
strlcat(buffer, sign, buf_len);
}
@@ -1367,25 +1474,77 @@ void format_time(char* buf, int buf_size, long t)
int split_string(char *str, const char split_char, char *vector[], const int vector_length)
{
int i;
- char *p = str;
-
- /* skip leading splitters */
- while(*p == split_char) p++;
+ char sep[2] = {split_char, '\0'};
+ char *e, *p = strtok_r(str, sep, &e);
- /* *p in the condition takes care of trailing splitters */
- for(i = 0; p && *p && i < vector_length; i++)
+ /* strtok takes care of leading & trailing splitters */
+ for(i = 0; i < vector_length; i++)
{
vector[i] = p;
- if ((p = strchr(p, split_char)))
- {
- *p++ = '\0';
- while(*p == split_char) p++; /* skip successive splitters */
- }
+ if (!p)
+ break;
+ p = strtok_r(NULL, sep, &e);
}
return i;
}
+/* returns match index from option list
+ * returns -1 if option was not found
+ * option list is array of char pointers with the final item set to null
+ * ex - const char * const option[] = { "op_a", "op_b", "op_c", NULL}
+ */
+int string_option(const char *option, const char *const oplist[], bool ignore_case)
+{
+ const char *op;
+ int (*cmp_fn)(const char*, const char*) = &strcasecmp;
+ if (!ignore_case)
+ cmp_fn = strcmp;
+ for (int i=0; (op=oplist[i]) != NULL; i++)
+ {
+ if (cmp_fn(op, option) == 0)
+ return i;
+ }
+ return -1;
+}
+
+/* Make sure part of path only contain chars valid for a FAT32 long name.
+ * Double quotes are replaced with single quotes, other unsupported chars
+ * are replaced with an underscore.
+ *
+ * path - path to modify.
+ * offset - where in path to start checking.
+ * count - number of chars to check.
+ */
+void fix_path_part(char* path, int offset, int count)
+{
+ static const char invalid_chars[] = "*/:<>?\\|";
+ int i;
+
+ path += offset;
+
+ for (i = 0; i <= count; i++, path++)
+ {
+ if (*path == 0)
+ return;
+ if (*path == '"')
+ *path = '\'';
+ else if (strchr(invalid_chars, *path))
+ *path = '_';
+ }
+}
+
+/* open but with a builtin printf for assembling the path */
+int open_pathfmt(char *buf, size_t size, int oflag, const char *pathfmt, ...)
+{
+ va_list ap;
+ va_start(ap, pathfmt);
+ vsnprintf(buf, size, pathfmt, ap);
+ va_end(ap);
+ if ((oflag & O_PATH) == O_PATH)
+ return -1;
+ return open(buf, oflag, 0666);
+}
/** Open a UTF-8 file and set file descriptor to first byte after BOM.
* If no BOM is present this behaves like open().
@@ -1507,28 +1666,224 @@ int clamp_value_wrap(int value, int max, int min)
static enum current_activity
current_activity[MAX_ACTIVITY_DEPTH] = {ACTIVITY_UNKNOWN};
static int current_activity_top = 0;
-void push_current_activity(enum current_activity screen)
+
+static void push_current_activity_refresh(enum current_activity screen, bool refresh)
{
current_activity[current_activity_top++] = screen;
FOR_NB_SCREENS(i)
{
skinlist_set_cfg(i, NULL);
- skin_update(CUSTOM_STATUSBAR, i, SKIN_REFRESH_ALL);
+ if (refresh)
+ skin_update(CUSTOM_STATUSBAR, i, SKIN_REFRESH_ALL);
+ }
+}
+
+static void pop_current_activity_refresh(bool refresh)
+{
+ current_activity_top--;
+ FOR_NB_SCREENS(i)
+ {
+ skinlist_set_cfg(i, NULL);
+ if (refresh)
+ skin_update(CUSTOM_STATUSBAR, i, SKIN_REFRESH_ALL);
}
}
+void push_current_activity(enum current_activity screen)
+{
+ push_current_activity_refresh(screen, true);
+}
+
+void push_activity_without_refresh(enum current_activity screen)
+{
+ push_current_activity_refresh(screen, false);
+}
+
void pop_current_activity(void)
{
+ pop_current_activity_refresh(true);
+#if 0
current_activity_top--;
FOR_NB_SCREENS(i)
{
skinlist_set_cfg(i, NULL);
- skin_update(CUSTOM_STATUSBAR, i, SKIN_REFRESH_ALL);
+ if (ACTIVITY_REFRESH_NOW == refresh)
+ skin_update(CUSTOM_STATUSBAR, i, SKIN_REFRESH_ALL);
}
+#endif
+}
+
+void pop_current_activity_without_refresh(void)
+{
+ pop_current_activity_refresh(false);
}
+
enum current_activity get_current_activity(void)
{
return current_activity[current_activity_top?current_activity_top-1:0];
}
-#endif
+/* core_load_bmp opens bitmp filename and allocates space for it
+* you must set bm->data with the result from core_get_data(handle)
+* you must also call core_free(handle) when finished with the bitmap
+* returns handle, ALOC_ERR(0) on failure
+* ** Extended error info truth table **
+* [ Handle ][buf_reqd]
+* [ > 0 ][ > 0 ] buf_reqd indicates how many bytes were used
+* [ALOC_ERR][ > 0 ] buf_reqd indicates how many bytes are needed
+* [ALOC_ERR][READ_ERR] there was an error reading the file or it is empty
+*/
+int core_load_bmp(const char * filename, struct bitmap *bm, const int bmformat,
+ ssize_t *buf_reqd, struct buflib_callbacks *ops)
+{
+ ssize_t buf_size;
+ ssize_t size_read = 0;
+ int handle = CLB_ALOC_ERR;
+
+ int fd = open(filename, O_RDONLY);
+ *buf_reqd = CLB_READ_ERR;
+
+ if (fd < 0) /* Exit if file opening failed */
+ {
+ DEBUGF("read_bmp_file: can't open '%s', rc: %d\n", filename, fd);
+ return CLB_ALOC_ERR;
+ }
+
+ buf_size = read_bmp_fd(fd, bm, 0, bmformat|FORMAT_RETURN_SIZE, NULL);
+
+ if (buf_size > 0)
+ {
+ handle = core_alloc_ex(buf_size, ops);
+ if (handle > 0)
+ {
+ bm->data = core_get_data_pinned(handle);
+ lseek(fd, 0, SEEK_SET); /* reset to beginning of file */
+ size_read = read_bmp_fd(fd, bm, buf_size, bmformat, NULL);
+
+ if (size_read > 0) /* free unused alpha channel, if any */
+ {
+ core_shrink(handle, bm->data, size_read);
+ *buf_reqd = size_read;
+ }
+
+ core_put_data_pinned(bm->data);
+ bm->data = NULL; /* do this to force a crash later if the
+ caller doesnt call core_get_data() */
+ }
+ else
+ *buf_reqd = buf_size; /* couldn't allocate pass bytes needed */
+
+ if (size_read <= 0)
+ {
+ /* error reading file */
+ core_free(handle); /* core_free() ignores free handles (<= 0) */
+ handle = CLB_ALOC_ERR;
+ }
+ }
+
+ close(fd);
+ return handle;
+}
+
+/*
+ * Normalized volume routines adapted from alsamixer volume_mapping.c
+ */
+/*
+ * Copyright (c) 2010 Clemens Ladisch <clemens@ladisch.de>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+/*
+ * "The mapping is designed so that the position in the interval is proportional
+ * to the volume as a human ear would perceive it (i.e., the position is the
+ * cubic root of the linear sample multiplication factor). For controls with
+ * a small range (24 dB or less), the mapping is linear in the dB values so
+ * that each step has the same size visually. Only for controls without dB
+ * information, a linear mapping of the hardware volume register values is used
+ * (this is the same algorithm as used in the old alsamixer)."
+ */
+
+#define NVOL_FRACBITS 16
+#define NVOL_UNITY (1L << NVOL_FRACBITS)
+#define NVOL_FACTOR (600L << NVOL_FRACBITS)
+
+#define NVOL_MAX_LINEAR_DB_SCALE (240L << NVOL_FRACBITS)
+
+#define nvol_div(x,y) fp_div((x), (y), NVOL_FRACBITS)
+#define nvol_mul(x,y) fp_mul((x), (y), NVOL_FRACBITS)
+#define nvol_exp10(x) fp_exp10((x), NVOL_FRACBITS)
+#define nvol_log10(x) fp_log10((x), NVOL_FRACBITS)
+
+static bool use_linear_dB_scale(long min_vol, long max_vol)
+{
+ /*
+ * Alsamixer uses a linear scale for small ranges.
+ * Commented out so perceptual volume works as advertised on all targets.
+ */
+ /*
+ return max_vol - min_vol <= NVOL_MAX_LINEAR_DB_SCALE;
+ */
+
+ (void)min_vol;
+ (void)max_vol;
+ return false;
+}
+
+long to_normalized_volume(long vol, long min_vol, long max_vol, long max_norm)
+{
+ long norm, min_norm;
+
+ vol <<= NVOL_FRACBITS;
+ min_vol <<= NVOL_FRACBITS;
+ max_vol <<= NVOL_FRACBITS;
+ max_norm <<= NVOL_FRACBITS;
+
+ if (use_linear_dB_scale(min_vol, max_vol))
+ {
+ norm = nvol_div(vol - min_vol, max_vol - min_vol);
+ }
+ else
+ {
+ min_norm = nvol_exp10(nvol_div(min_vol - max_vol, NVOL_FACTOR));
+ norm = nvol_exp10(nvol_div(vol - max_vol, NVOL_FACTOR));
+ norm = nvol_div(norm - min_norm, NVOL_UNITY - min_norm);
+ }
+
+ return nvol_mul(norm, max_norm) >> NVOL_FRACBITS;
+}
+
+long from_normalized_volume(long norm, long min_vol, long max_vol, long max_norm)
+{
+ long vol, min_norm;
+
+ norm <<= NVOL_FRACBITS;
+ min_vol <<= NVOL_FRACBITS;
+ max_vol <<= NVOL_FRACBITS;
+ max_norm <<= NVOL_FRACBITS;
+
+ vol = nvol_div(norm, max_norm);
+
+ if (use_linear_dB_scale(min_vol, max_vol))
+ {
+ vol = nvol_mul(vol, max_vol - min_vol) + min_vol;
+ }
+ else
+ {
+ min_norm = nvol_exp10(nvol_div(min_vol - max_vol, NVOL_FACTOR));
+ vol = nvol_mul(vol, NVOL_UNITY - min_norm) + min_norm;
+ vol = nvol_mul(nvol_log10(vol), NVOL_FACTOR) + max_vol;
+ }
+
+ return vol >> NVOL_FRACBITS;
+}
+#endif /* ndef __PCTOOL__ */
diff --git a/apps/misc.h b/apps/misc.h
index c4057f4ebc..497aa04c78 100644
--- a/apps/misc.h
+++ b/apps/misc.h
@@ -112,7 +112,6 @@ long default_event_handler_ex(long event, void (*callback)(void *), void *parame
long default_event_handler(long event);
bool list_stop_handler(void);
void car_adapter_mode_init(void) INIT_ATTR;
-extern int show_logo(void);
/* Unicode byte order mark sequences and lengths */
#define BOM_UTF_8 "\xef\xbb\xbf"
@@ -122,7 +121,14 @@ extern int show_logo(void);
#define BOM_UTF_16_SIZE 2
int split_string(char *str, const char needle, char *vector[], int vector_length);
+#ifndef O_PATH
+#define O_PATH 0x2000
+#endif
+
+void fix_path_part(char* path, int offset, int count);
+int open_pathfmt(char *buf, size_t size, int oflag, const char *pathfmt, ...);
int open_utf8(const char* pathname, int flags);
+int string_option(const char *option, const char *const oplist[], bool ignore_case);
#ifdef BOOTFILE
#if !defined(USB_NONE) && !defined(USB_HANDLED_BY_OF) \
@@ -131,13 +137,30 @@ void check_bootfile(bool do_rolo);
#endif
#endif
+enum volume_adjust_mode
+{
+ VOLUME_ADJUST_DIRECT, /* adjust in units of the volume step size */
+ VOLUME_ADJUST_PERCEPTUAL, /* adjust using perceptual steps */
+};
+
+/* min/max values for global_settings.volume_adjust_norm_steps */
+#define MIN_NORM_VOLUME_STEPS 10
+#define MAX_NORM_VOLUME_STEPS 100
+
/* check range, set volume and save settings */
void setvol(void);
+void set_normalized_volume(int vol);
+int get_normalized_volume(void);
+void adjust_volume(int steps);
+void adjust_volume_ex(int steps, enum volume_adjust_mode mode);
#ifdef HAVE_LCD_COLOR
int hex_to_rgb(const char* hex, int* color);
#endif
+int confirm_delete_yesno(const char *name);
+int confirm_overwrite_yesno(void);
+
char* strrsplt(char* str, int c);
char* skip_whitespace(char* const str);
@@ -179,6 +202,9 @@ enum current_activity {
ACTIVITY_USBSCREEN
};
+/* custom string representation of activity */
+#define MAKE_ACT_STR(act) ((char[3]){'>', 'A'+ (act), 0x0})
+
void beep_play(unsigned int frequency, unsigned int duration,
unsigned int amplitude);
@@ -208,10 +234,31 @@ int id3_get_replaygain_mode(const struct mp3entry *id3);
void replaygain_update(void);
void push_current_activity(enum current_activity screen);
+void push_activity_without_refresh(enum current_activity screen);
void pop_current_activity(void);
+void pop_current_activity_without_refresh(void);
enum current_activity get_current_activity(void);
/* format a sound value like: -1.05 dB */
int format_sound_value(char *buf, size_t len, int snd, int val);
+#ifndef PLUGIN
+enum core_load_bmp_error
+{
+ CLB_ALOC_ERR = 0,
+ CLB_READ_ERR = -1,
+};
+struct buflib_callbacks;
+int core_load_bmp(const char *filename, struct bitmap *bm, const int bmformat,
+ ssize_t *buf_reqd, struct buflib_callbacks *ops);
+#endif
+
+/* Convert a volume (in tenth dB) in the range [min_vol, max_vol]
+ * to a normalized linear value in the range [0, max_norm]. */
+long to_normalized_volume(long vol, long min_vol, long max_vol, long max_norm);
+
+/* Inverse of to_normalized_volume(), returns the volume in tenth dB
+ * for the given normalized volume. */
+long from_normalized_volume(long norm, long min_vol, long max_vol, long max_norm);
+
#endif /* MISC_H */
diff --git a/apps/onplay.c b/apps/onplay.c
index 0942d69d3f..0b5dc2fe27 100644
--- a/apps/onplay.c
+++ b/apps/onplay.c
@@ -63,12 +63,16 @@
#include "viewport.h"
#include "pathfuncs.h"
#include "shortcuts.h"
+#include "misc.h"
static int context;
static const char *selected_file = NULL;
static char selected_file_path[MAX_PATH];
static int selected_file_attr = 0;
static int onplay_result = ONPLAY_OK;
+static bool in_queue_submenu = false;
+static bool (*ctx_current_playlist_insert)(int position, bool queue, bool create_new);
+static int (*ctx_add_to_playlist)(const char* playlist, bool new_playlist);
extern struct menu_item_ex file_menu; /* settings_menu.c */
/* redefine MAKE_MENU so the MENU_EXITAFTERTHISMENU flag can be added easily */
@@ -125,8 +129,8 @@ static bool clipboard_clip(struct clipboard *clip, const char *path,
unsigned int attr, unsigned int flags)
{
/* if it fits it clips */
- if (strlcpy(clip->path, path, sizeof (clip->path))
- < sizeof (clip->path)) {
+ if (strmemccpy(clip->path, path, sizeof (clip->path)) != NULL)
+ {
clip->attr = attr;
clip->flags = flags;
return true;
@@ -142,16 +146,25 @@ static bool clipboard_clip(struct clipboard *clip, const char *path,
/* interface function. */
/* ----------------------------------------------------------------------- */
+
+static int bookmark_load_menu_wrapper(void)
+{
+ if (get_current_activity() == ACTIVITY_CONTEXTMENU) /* get rid of parent activity */
+ pop_current_activity_without_refresh(); /* when called from ctxt menu */
+
+ return bookmark_load_menu();
+}
+
static int bookmark_menu_callback(int action,
const struct menu_item_ex *this_item,
struct gui_synclist *this_list);
MENUITEM_FUNCTION(bookmark_create_menu_item, 0,
ID2P(LANG_BOOKMARK_MENU_CREATE),
- bookmark_create_menu, NULL,
+ bookmark_create_menu,
bookmark_menu_callback, Icon_Bookmark);
MENUITEM_FUNCTION(bookmark_load_menu_item, 0,
ID2P(LANG_BOOKMARK_MENU_LIST),
- bookmark_load_menu, NULL,
+ bookmark_load_menu_wrapper,
bookmark_menu_callback, Icon_Bookmark);
MAKE_ONPLAYMENU(bookmark_menu, ID2P(LANG_BOOKMARK_MENU),
bookmark_menu_callback, Icon_Bookmark,
@@ -187,261 +200,6 @@ static int bookmark_menu_callback(int action,
return action;
}
-/* playing_time screen context */
-struct playing_time_info {
- int curr_playing; /* index of currently playing track in playlist */
- int nb_tracks; /* how many tracks in playlist */
- /* seconds before and after current position, and total. Datatype
- allows for values up to 68years. If I had kept it in ms
- though, it would have overflowed at 24days, which takes
- something like 8.5GB at 32kbps, and so we could conceivably
- have playlists lasting longer than that. */
- long secs_bef, secs_aft, secs_ttl;
- long trk_secs_bef, trk_secs_aft, trk_secs_ttl;
- /* kilobytes played before and after current pos, and total.
- Kilobytes because bytes would overflow. Data type range is up
- to 2TB. */
- long kbs_bef, kbs_aft, kbs_ttl;
-};
-
-/* list callback for playing_time screen */
-static const char * playing_time_get_or_speak_info(int selected_item, void * data,
- char *buf, size_t buffer_len,
- bool say_it)
-{
- struct playing_time_info *pti = (struct playing_time_info *)data;
- switch(selected_item) {
- case 0: { /* elapsed and total time */
- char timestr1[25], timestr2[25];
- format_time_auto(timestr1, sizeof(timestr1), pti->secs_bef,
- UNIT_SEC, false);
- format_time_auto(timestr2, sizeof(timestr2), pti->secs_ttl,
- UNIT_SEC, false);
- long elapsed_perc; /* percentage of duration elapsed */
- if (pti->secs_ttl == 0)
- elapsed_perc = 0;
- else if (pti->secs_ttl <= 0xFFFFFF)
- elapsed_perc = pti->secs_bef *100 / pti->secs_ttl;
- else /* sacrifice some precision to avoid overflow */
- elapsed_perc = (pti->secs_bef>>7) *100 /(pti->secs_ttl>>7);
- snprintf(buf, buffer_len, str(LANG_PLAYTIME_ELAPSED),
- timestr1, timestr2, elapsed_perc);
- if (say_it)
- talk_ids(false, LANG_PLAYTIME_ELAPSED,
- TALK_ID(pti->secs_bef, UNIT_TIME),
- VOICE_OF,
- TALK_ID(pti->secs_ttl, UNIT_TIME),
- VOICE_PAUSE,
- TALK_ID(elapsed_perc, UNIT_PERCENT));
- break;
- }
- case 1: { /* playlist remaining time */
- char timestr[25];
- format_time_auto(timestr, sizeof(timestr), pti->secs_aft,
- UNIT_SEC, false);
- snprintf(buf, buffer_len, str(LANG_PLAYTIME_REMAINING),
- timestr);
- if (say_it)
- talk_ids(false, LANG_PLAYTIME_REMAINING,
- TALK_ID(pti->secs_aft, UNIT_TIME));
- break;
- }
- case 2: { /* track elapsed and duration */
- char timestr1[25], timestr2[25];
- format_time_auto(timestr1, sizeof(timestr1), pti->trk_secs_bef,
- UNIT_SEC, false);
- format_time_auto(timestr2, sizeof(timestr2), pti->trk_secs_ttl,
- UNIT_SEC, false);
- long elapsed_perc; /* percentage of duration elapsed */
- if (pti->trk_secs_ttl == 0)
- elapsed_perc = 0;
- else if (pti->trk_secs_ttl <= 0xFFFFFF)
- elapsed_perc = pti->trk_secs_bef *100 / pti->trk_secs_ttl;
- else /* sacrifice some precision to avoid overflow */
- elapsed_perc = (pti->trk_secs_bef>>7) *100 /(pti->trk_secs_ttl>>7);
- snprintf(buf, buffer_len, str(LANG_PLAYTIME_TRK_ELAPSED),
- timestr1, timestr2, elapsed_perc);
- if (say_it)
- talk_ids(false, LANG_PLAYTIME_TRK_ELAPSED,
- TALK_ID(pti->trk_secs_bef, UNIT_TIME),
- VOICE_OF,
- TALK_ID(pti->trk_secs_ttl, UNIT_TIME),
- VOICE_PAUSE,
- TALK_ID(elapsed_perc, UNIT_PERCENT));
- break;
- }
- case 3: { /* track remaining time */
- char timestr[25];
- format_time_auto(timestr, sizeof(timestr), pti->trk_secs_aft,
- UNIT_SEC, false);
- snprintf(buf, buffer_len, str(LANG_PLAYTIME_TRK_REMAINING),
- timestr);
- if (say_it)
- talk_ids(false, LANG_PLAYTIME_TRK_REMAINING,
- TALK_ID(pti->trk_secs_aft, UNIT_TIME));
- break;
- }
- case 4: { /* track index */
- int track_perc = (pti->curr_playing+1) *100 / pti->nb_tracks;
- snprintf(buf, buffer_len, str(LANG_PLAYTIME_TRACK),
- pti->curr_playing + 1, pti->nb_tracks, track_perc);
- if (say_it)
- talk_ids(false, LANG_PLAYTIME_TRACK,
- TALK_ID(pti->curr_playing+1, UNIT_INT),
- VOICE_OF,
- TALK_ID(pti->nb_tracks, UNIT_INT),
- VOICE_PAUSE,
- TALK_ID(track_perc, UNIT_PERCENT));
- break;
- }
- case 5: { /* storage size */
- char str1[10], str2[10], str3[10];
- output_dyn_value(str1, sizeof(str1), pti->kbs_ttl, kibyte_units, 3, true);
- output_dyn_value(str2, sizeof(str2), pti->kbs_bef, kibyte_units, 3, true);
- output_dyn_value(str3, sizeof(str3), pti->kbs_aft, kibyte_units, 3, true);
- snprintf(buf, buffer_len, str(LANG_PLAYTIME_STORAGE),
- str1,str2,str3);
- if (say_it) {
- talk_id(LANG_PLAYTIME_STORAGE, false);
- output_dyn_value(NULL, 0, pti->kbs_ttl, kibyte_units, 3, true);
- talk_ids(true, VOICE_PAUSE, VOICE_PLAYTIME_DONE);
- output_dyn_value(NULL, 0, pti->kbs_bef, kibyte_units, 3, true);
- talk_id(LANG_PLAYTIME_REMAINING, true);
- output_dyn_value(NULL, 0, pti->kbs_aft, kibyte_units, 3, true);
- }
- break;
- }
- case 6: { /* Average track file size */
- char str[10];
- long avg_track_size = pti->kbs_ttl /pti->nb_tracks;
- output_dyn_value(str, sizeof(str), avg_track_size, kibyte_units, 3, true);
- snprintf(buf, buffer_len, str(LANG_PLAYTIME_AVG_TRACK_SIZE),
- str);
- if (say_it) {
- talk_id(LANG_PLAYTIME_AVG_TRACK_SIZE, false);
- output_dyn_value(NULL, 0, avg_track_size, kibyte_units, 3, true);
- }
- break;
- }
- case 7: { /* Average bitrate */
- /* Convert power of 2 kilobytes to power of 10 kilobits */
- long avg_bitrate = pti->kbs_ttl / pti->secs_ttl *1024 *8 /1000;
- snprintf(buf, buffer_len, str(LANG_PLAYTIME_AVG_BITRATE),
- avg_bitrate);
- if (say_it)
- talk_ids(false, LANG_PLAYTIME_AVG_BITRATE,
- TALK_ID(avg_bitrate, UNIT_KBIT));
- break;
- }
- }
- return buf;
-}
-
-static const char * playing_time_get_info(int selected_item, void * data,
- char *buffer, size_t buffer_len)
-{
- return playing_time_get_or_speak_info(selected_item, data,
- buffer, buffer_len, false);
-}
-
-static int playing_time_speak_info(int selected_item, void * data)
-{
- static char buffer[MAX_PATH];
- playing_time_get_or_speak_info(selected_item, data,
- buffer, MAX_PATH, true);
- return 0;
-}
-
-/* playing time screen: shows total and elapsed playlist duration and
- other stats */
-static bool playing_time(void)
-{
- unsigned long talked_tick = current_tick;
- struct playing_time_info pti;
- struct playlist_track_info pltrack;
- struct mp3entry id3;
- int i, fd, ret;
-
- pti.nb_tracks = playlist_amount();
- playlist_get_resume_info(&pti.curr_playing);
- struct mp3entry *curr_id3 = audio_current_track();
- if (pti.curr_playing == -1 || !curr_id3)
- return false;
- pti.secs_bef = pti.trk_secs_bef = curr_id3->elapsed/1000;
- pti.secs_aft = pti.trk_secs_aft
- = (curr_id3->length -curr_id3->elapsed)/1000;
- pti.kbs_bef = curr_id3->offset/1024;
- pti.kbs_aft = (curr_id3->filesize -curr_id3->offset)/1024;
-
- splash(0, ID2P(LANG_WAIT));
-
- /* Go through each file in the playlist and get its stats. For
- huge playlists this can take a while... The reference position
- is the position at the moment this function was invoked,
- although playback continues forward. */
- for (i = 0; i < pti.nb_tracks; i++) {
- /* Show a splash while we are loading. */
- splashf(0, str(LANG_LOADING_PERCENT),
- i*100/pti.nb_tracks, str(LANG_OFF_ABORT));
- /* Voice equivalent */
- if (TIME_AFTER(current_tick, talked_tick+5*HZ)) {
- talked_tick = current_tick;
- talk_ids(false, LANG_LOADING_PERCENT,
- TALK_ID(i*100/pti.nb_tracks, UNIT_PERCENT));
- }
- if (action_userabort(TIMEOUT_NOBLOCK))
- goto exit;
-
- if (i == pti.curr_playing)
- continue;
-
- if (playlist_get_track_info(NULL, i, &pltrack) < 0)
- goto error;
- if ((fd = open(pltrack.filename, O_RDONLY)) < 0)
- goto error;
- ret = get_metadata(&id3, fd, pltrack.filename);
- close(fd);
- if (!ret)
- goto error;
-
- if (i < pti.curr_playing) {
- pti.secs_bef += id3.length/1000;
- pti.kbs_bef += id3.filesize/1024;
- } else {
- pti.secs_aft += id3.length/1000;
- pti.kbs_aft += id3.filesize/1024;
- }
- }
-
- pti.secs_ttl = pti.secs_bef +pti.secs_aft;
- pti.trk_secs_ttl = pti.trk_secs_bef +pti.trk_secs_aft;
- pti.kbs_ttl = pti.kbs_bef +pti.kbs_aft;
-
- struct gui_synclist pt_lists;
- int key;
-
- gui_synclist_init(&pt_lists, &playing_time_get_info, &pti, true, 1, NULL);
- if (global_settings.talk_menu)
- gui_synclist_set_voice_callback(&pt_lists, playing_time_speak_info);
- gui_synclist_set_nb_items(&pt_lists, 8);
- gui_synclist_draw(&pt_lists);
-/* gui_syncstatusbar_draw(&statusbars, true); */
- gui_synclist_speak_item(&pt_lists);
- while (true) {
-/* gui_syncstatusbar_draw(&statusbars, false); */
- if (list_do_action(CONTEXT_LIST, HZ/2,
- &pt_lists, &key, LIST_WRAP_UNLESS_HELD) == 0
- && key!=ACTION_NONE && key!=ACTION_UNKNOWN)
- {
- talk_force_shutup();
- return(default_event_handler(key) == SYS_USB_CONNECTED);
- }
- }
- error:
- splash(HZ, ID2P(LANG_PLAYTIME_ERROR));
- exit:
- return false;
-}
/* CONTEXT_WPS playlist options */
@@ -451,9 +209,11 @@ static bool shuffle_playlist(void)
return false;
playlist_sort(NULL, true);
playlist_randomise(NULL, current_tick, true);
+ playlist_set_modified(NULL, true);
return false;
}
+
static bool save_playlist(void)
{
/* save_playlist_screen should load the newly saved playlist and resume */
@@ -461,31 +221,71 @@ static bool save_playlist(void)
return false;
}
-extern struct menu_item_ex view_cur_playlist; /* from playlist_menu.c */
+static int wps_view_cur_playlist(void)
+{
+ if (get_current_activity() == ACTIVITY_CONTEXTMENU) /* get rid of parent activity */
+ pop_current_activity_without_refresh(); /* when called from ctxt menu */
+
+ playlist_viewer_ex(NULL, NULL);
+
+ return 0;
+}
+
+static void playing_time(void)
+{
+ plugin_load(PLUGIN_APPS_DIR"/playing_time.rock", NULL);
+}
+
+MENUITEM_FUNCTION(wps_view_cur_playlist_item, 0, ID2P(LANG_VIEW_DYNAMIC_PLAYLIST),
+ wps_view_cur_playlist, NULL, Icon_NOICON);
MENUITEM_FUNCTION(search_playlist_item, 0, ID2P(LANG_SEARCH_IN_PLAYLIST),
- search_playlist, NULL, NULL, Icon_Playlist);
+ search_playlist, NULL, Icon_Playlist);
MENUITEM_FUNCTION(playlist_save_item, 0, ID2P(LANG_SAVE_DYNAMIC_PLAYLIST),
- save_playlist, NULL, NULL, Icon_Playlist);
+ save_playlist, NULL, Icon_Playlist);
MENUITEM_FUNCTION(reshuffle_item, 0, ID2P(LANG_SHUFFLE_PLAYLIST),
- shuffle_playlist, NULL, NULL, Icon_Playlist);
+ shuffle_playlist, NULL, Icon_Playlist);
MENUITEM_FUNCTION(playing_time_item, 0, ID2P(LANG_PLAYING_TIME),
- playing_time, NULL, NULL, Icon_Playlist);
-MAKE_ONPLAYMENU( wps_playlist_menu, ID2P(LANG_PLAYLIST),
+ playing_time, NULL, Icon_Playlist);
+MAKE_ONPLAYMENU( wps_playlist_menu, ID2P(LANG_CURRENT_PLAYLIST),
NULL, Icon_Playlist,
- &view_cur_playlist, &search_playlist_item,
+ &wps_view_cur_playlist_item, &search_playlist_item,
&playlist_save_item, &reshuffle_item, &playing_time_item
);
-/* CONTEXT_[TREE|ID3DB] playlist options */
-static bool add_to_playlist(int position, bool queue)
+/* argument for add_to_playlist (for use by menu callbacks) */
+struct add_to_pl_param
{
- bool new_playlist = false;
- if (!(audio_status() & AUDIO_STATUS_PLAY))
- {
- new_playlist = true;
- if (position == PLAYLIST_REPLACE)
- position = PLAYLIST_INSERT;
- }
+ int8_t position;
+ unsigned int queue: 1;
+ unsigned int replace: 1;
+};
+
+static struct add_to_pl_param addtopl_insert = {PLAYLIST_INSERT, 0, 0};
+static struct add_to_pl_param addtopl_insert_first = {PLAYLIST_INSERT_FIRST, 0, 0};
+static struct add_to_pl_param addtopl_insert_last = {PLAYLIST_INSERT_LAST, 0, 0};
+static struct add_to_pl_param addtopl_insert_shuf = {PLAYLIST_INSERT_SHUFFLED, 0, 0};
+static struct add_to_pl_param addtopl_insert_last_shuf = {PLAYLIST_INSERT_LAST_SHUFFLED, 0, 0};
+
+static struct add_to_pl_param addtopl_queue = {PLAYLIST_INSERT, 1, 0};
+static struct add_to_pl_param addtopl_queue_first = {PLAYLIST_INSERT_FIRST, 1, 0};
+static struct add_to_pl_param addtopl_queue_last = {PLAYLIST_INSERT_LAST, 1, 0};
+static struct add_to_pl_param addtopl_queue_shuf = {PLAYLIST_INSERT_SHUFFLED, 1, 0};
+static struct add_to_pl_param addtopl_queue_last_shuf = {PLAYLIST_INSERT_LAST_SHUFFLED, 1, 0};
+
+static struct add_to_pl_param addtopl_replace = {PLAYLIST_INSERT, 0, 1};
+static struct add_to_pl_param addtopl_replace_shuffled = {PLAYLIST_INSERT_LAST_SHUFFLED, 0, 1};
+
+/* CONTEXT_[TREE|ID3DB|STD] playlist options */
+static int add_to_playlist(void* arg)
+{
+ struct add_to_pl_param* param = arg;
+ int position = param->position;
+ bool new_playlist = !!param->replace;
+ bool queue = !!param->queue;
+
+ /* warn if replacing the playlist */
+ if (new_playlist && !warn_on_pl_erase())
+ return 0;
const char *lines[] = {
ID2P(LANG_RECURSE_DIRECTORY_QUESTION),
@@ -495,6 +295,15 @@ static bool add_to_playlist(int position, bool queue)
splash(0, ID2P(LANG_WAIT));
+ if (new_playlist && global_settings.keep_current_track_on_replace_playlist)
+ {
+ if (audio_status() & AUDIO_STATUS_PLAY)
+ {
+ playlist_remove_all_tracks(NULL);
+ new_playlist = false;
+ }
+ }
+
if (new_playlist)
playlist_create(NULL, NULL);
@@ -508,9 +317,13 @@ static bool add_to_playlist(int position, bool queue)
}
#ifdef HAVE_TAGCACHE
- if (context == CONTEXT_ID3DB)
+ if ((context == CONTEXT_ID3DB) && (selected_file_attr & ATTR_DIRECTORY))
+ {
+ tagtree_current_playlist_insert(position, queue);
+ }
+ else if (context == CONTEXT_STD && ctx_current_playlist_insert != NULL)
{
- tagtree_insert_selection_playlist(position, queue);
+ ctx_current_playlist_insert(position, queue, false);
}
else
#endif
@@ -546,17 +359,17 @@ static bool add_to_playlist(int position, bool queue)
onplay_result = ONPLAY_START_PLAY;
}
+ playlist_set_modified(NULL, true);
return false;
}
static bool view_playlist(void)
{
- bool was_playing = audio_status() & AUDIO_STATUS_PLAY;
bool result;
- result = playlist_viewer_ex(selected_file);
+ result = playlist_viewer_ex(selected_file, NULL);
- if (!was_playing && (audio_status() & AUDIO_STATUS_PLAY) &&
+ if (result == PLAYLIST_VIEWER_OK &&
onplay_result == ONPLAY_OK)
/* playlist was started from viewer */
onplay_result = ONPLAY_START_PLAY;
@@ -564,136 +377,74 @@ static bool view_playlist(void)
return result;
}
-static int playlist_insert_func(void *param)
-{
- if (((intptr_t)param == PLAYLIST_REPLACE ||
- ((intptr_t)param == PLAYLIST_INSERT_SHUFFLED && !(audio_status() & AUDIO_STATUS_PLAY))) &&
- !warn_on_pl_erase())
- return 0;
- add_to_playlist((intptr_t)param, false);
- return 0;
-}
-
-static int playlist_queue_func(void *param)
-{
- add_to_playlist((intptr_t)param, true);
- return 0;
-}
-
-static int treeplaylist_wplayback_callback(int action,
- const struct menu_item_ex* this_item,
- struct gui_synclist *this_list);
-
static int treeplaylist_callback(int action,
const struct menu_item_ex *this_item,
struct gui_synclist *this_list);
/* insert items */
-MENUITEM_FUNCTION(i_pl_item, MENU_FUNC_USEPARAM, ID2P(LANG_INSERT),
- playlist_insert_func, (intptr_t*)PLAYLIST_INSERT,
- treeplaylist_wplayback_callback, Icon_Playlist);
-MENUITEM_FUNCTION(i_first_pl_item, MENU_FUNC_USEPARAM, ID2P(LANG_INSERT_FIRST),
- playlist_insert_func, (intptr_t*)PLAYLIST_INSERT_FIRST,
- treeplaylist_wplayback_callback, Icon_Playlist);
-MENUITEM_FUNCTION(i_last_pl_item, MENU_FUNC_USEPARAM, ID2P(LANG_INSERT_LAST),
- playlist_insert_func, (intptr_t*)PLAYLIST_INSERT_LAST,
- treeplaylist_wplayback_callback, Icon_Playlist);
-MENUITEM_FUNCTION(i_shuf_pl_item, MENU_FUNC_USEPARAM,
- ID2P(LANG_INSERT_SHUFFLED), playlist_insert_func,
- (intptr_t*)PLAYLIST_INSERT_SHUFFLED,
+MENUITEM_FUNCTION_W_PARAM(i_pl_item, 0, ID2P(LANG_ADD),
+ add_to_playlist, &addtopl_insert,
treeplaylist_callback, Icon_Playlist);
-MENUITEM_FUNCTION(i_last_shuf_pl_item, MENU_FUNC_USEPARAM,
- ID2P(LANG_INSERT_LAST_SHUFFLED), playlist_insert_func,
- (intptr_t*)PLAYLIST_INSERT_LAST_SHUFFLED,
+MENUITEM_FUNCTION_W_PARAM(i_first_pl_item, 0, ID2P(LANG_PLAY_NEXT),
+ add_to_playlist, &addtopl_insert_first,
treeplaylist_callback, Icon_Playlist);
-/* queue items */
-MENUITEM_FUNCTION(q_pl_item, MENU_FUNC_USEPARAM, ID2P(LANG_QUEUE),
- playlist_queue_func, (intptr_t*)PLAYLIST_INSERT,
+MENUITEM_FUNCTION_W_PARAM(i_last_pl_item, 0, ID2P(LANG_PLAY_LAST),
+ add_to_playlist, &addtopl_insert_last,
treeplaylist_callback, Icon_Playlist);
-MENUITEM_FUNCTION(q_first_pl_item, MENU_FUNC_USEPARAM, ID2P(LANG_QUEUE_FIRST),
- playlist_queue_func, (intptr_t*)PLAYLIST_INSERT_FIRST,
+MENUITEM_FUNCTION_W_PARAM(i_shuf_pl_item, 0, ID2P(LANG_ADD_SHUFFLED),
+ add_to_playlist, &addtopl_insert_shuf,
treeplaylist_callback, Icon_Playlist);
-MENUITEM_FUNCTION(q_last_pl_item, MENU_FUNC_USEPARAM, ID2P(LANG_QUEUE_LAST),
- playlist_queue_func, (intptr_t*)PLAYLIST_INSERT_LAST,
+MENUITEM_FUNCTION_W_PARAM(i_last_shuf_pl_item, 0, ID2P(LANG_PLAY_LAST_SHUFFLED),
+ add_to_playlist, &addtopl_insert_last_shuf,
treeplaylist_callback, Icon_Playlist);
-MENUITEM_FUNCTION(q_shuf_pl_item, MENU_FUNC_USEPARAM,
- ID2P(LANG_QUEUE_SHUFFLED), playlist_queue_func,
- (intptr_t*)PLAYLIST_INSERT_SHUFFLED,
+/* queue items */
+MENUITEM_FUNCTION_W_PARAM(q_pl_item, 0, ID2P(LANG_QUEUE),
+ add_to_playlist, &addtopl_queue,
treeplaylist_callback, Icon_Playlist);
-MENUITEM_FUNCTION(q_last_shuf_pl_item, MENU_FUNC_USEPARAM,
- ID2P(LANG_QUEUE_LAST_SHUFFLED), playlist_queue_func,
- (intptr_t*)PLAYLIST_INSERT_LAST_SHUFFLED,
+MENUITEM_FUNCTION_W_PARAM(q_first_pl_item, 0, ID2P(LANG_QUEUE_FIRST),
+ add_to_playlist, &addtopl_queue_first,
treeplaylist_callback, Icon_Playlist);
-
-/* queue items in submenu */
-MENUITEM_FUNCTION(q_pl_submenu_item, MENU_FUNC_USEPARAM, ID2P(LANG_QUEUE),
- playlist_queue_func, (intptr_t*)PLAYLIST_INSERT,
- NULL, Icon_Playlist);
-MENUITEM_FUNCTION(q_first_pl_submenu_item, MENU_FUNC_USEPARAM, ID2P(LANG_QUEUE_FIRST),
- playlist_queue_func, (intptr_t*)PLAYLIST_INSERT_FIRST,
- NULL, Icon_Playlist);
-MENUITEM_FUNCTION(q_last_pl_submenu_item, MENU_FUNC_USEPARAM, ID2P(LANG_QUEUE_LAST),
- playlist_queue_func, (intptr_t*)PLAYLIST_INSERT_LAST,
- NULL, Icon_Playlist);
-MENUITEM_FUNCTION(q_shuf_pl_submenu_item, MENU_FUNC_USEPARAM,
- ID2P(LANG_QUEUE_SHUFFLED), playlist_queue_func,
- (intptr_t*)PLAYLIST_INSERT_SHUFFLED,
+MENUITEM_FUNCTION_W_PARAM(q_last_pl_item, 0, ID2P(LANG_QUEUE_LAST),
+ add_to_playlist, &addtopl_queue_last,
treeplaylist_callback, Icon_Playlist);
-MENUITEM_FUNCTION(q_last_shuf_pl_submenu_item, MENU_FUNC_USEPARAM,
- ID2P(LANG_QUEUE_LAST_SHUFFLED), playlist_queue_func,
- (intptr_t*)PLAYLIST_INSERT_LAST_SHUFFLED,
+MENUITEM_FUNCTION_W_PARAM(q_shuf_pl_item, 0, ID2P(LANG_QUEUE_SHUFFLED),
+ add_to_playlist, &addtopl_queue_shuf,
+ treeplaylist_callback, Icon_Playlist);
+MENUITEM_FUNCTION_W_PARAM(q_last_shuf_pl_item, 0, ID2P(LANG_QUEUE_LAST_SHUFFLED),
+ add_to_playlist, &addtopl_queue_last_shuf,
treeplaylist_callback, Icon_Playlist);
+/* queue submenu */
MAKE_ONPLAYMENU(queue_menu, ID2P(LANG_QUEUE_MENU),
- treeplaylist_wplayback_callback, Icon_Playlist,
- &q_pl_submenu_item,
- &q_first_pl_submenu_item,
- &q_last_pl_submenu_item,
- &q_shuf_pl_submenu_item,
- &q_last_shuf_pl_submenu_item);
-
-static int treeplaylist_wplayback_callback(int action,
- const struct menu_item_ex* this_item,
- struct gui_synclist *this_list)
-{
- (void)this_list;
- switch (action)
- {
- case ACTION_REQUEST_MENUITEM:
- if ((audio_status() & AUDIO_STATUS_PLAY) &&
- (this_item != &queue_menu ||
- global_settings.show_queue_options == QUEUE_SHOW_IN_SUBMENU))
- return action;
- else
- return ACTION_EXIT_MENUITEM;
- break;
- }
- return action;
-}
+ treeplaylist_callback, Icon_Playlist,
+ &q_first_pl_item,
+ &q_pl_item,
+ &q_shuf_pl_item,
+ &q_last_pl_item,
+ &q_last_shuf_pl_item);
/* replace playlist */
-MENUITEM_FUNCTION(replace_pl_item, MENU_FUNC_USEPARAM, ID2P(LANG_CLEAR_LIST_AND_PLAY_NEXT),
- playlist_insert_func, (intptr_t*)PLAYLIST_REPLACE,
- NULL, Icon_Playlist);
+MENUITEM_FUNCTION_W_PARAM(replace_pl_item, 0, ID2P(LANG_PLAY),
+ add_to_playlist, &addtopl_replace,
+ treeplaylist_callback, Icon_Playlist);
-MENUITEM_FUNCTION(replace_shuf_pl_item, MENU_FUNC_USEPARAM,
- ID2P(LANG_CLEAR_LIST_AND_PLAY_SHUFFLED), playlist_insert_func,
- (intptr_t*)PLAYLIST_INSERT_SHUFFLED,
+MENUITEM_FUNCTION_W_PARAM(replace_shuf_pl_item, 0, ID2P(LANG_PLAY_SHUFFLED),
+ add_to_playlist, &addtopl_replace_shuffled,
treeplaylist_callback, Icon_Playlist);
-MAKE_ONPLAYMENU(tree_playlist_menu, ID2P(LANG_CURRENT_PLAYLIST),
+MAKE_ONPLAYMENU(tree_playlist_menu, ID2P(LANG_PLAYING_NEXT),
treeplaylist_callback, Icon_Playlist,
/* insert */
- &i_pl_item,
&i_first_pl_item,
+ &i_pl_item,
&i_last_pl_item,
&i_shuf_pl_item,
&i_last_shuf_pl_item,
/* queue */
- &q_pl_item,
&q_first_pl_item,
+ &q_pl_item,
&q_last_pl_item,
&q_shuf_pl_item,
&q_last_shuf_pl_item,
@@ -712,77 +463,70 @@ static int treeplaylist_callback(int action,
(void)this_list;
switch (action)
{
- case ACTION_REQUEST_MENUITEM:
- if (this_item == &tree_playlist_menu)
- {
- if ((selected_file_attr & FILE_ATTR_MASK) == FILE_ATTR_AUDIO ||
- (selected_file_attr & FILE_ATTR_MASK) == FILE_ATTR_M3U ||
- (selected_file_attr & ATTR_DIRECTORY))
- return action;
- }
- else if ((this_item == &q_pl_item ||
- this_item == &q_first_pl_item ||
- this_item == &q_last_pl_item) &&
- global_settings.show_queue_options == QUEUE_SHOW_AT_TOPLEVEL &&
- (audio_status() & AUDIO_STATUS_PLAY))
- {
- return action;
- }
- else if (this_item == &i_shuf_pl_item)
- {
- if (global_settings.show_shuffled_adding_options &&
- (audio_status() & AUDIO_STATUS_PLAY))
- {
- return action;
- }
- }
- else if (this_item == &replace_shuf_pl_item)
+ case ACTION_REQUEST_MENUITEM:
+ if (this_item == &tree_playlist_menu)
+ {
+ if ((selected_file_attr & FILE_ATTR_MASK) != FILE_ATTR_AUDIO &&
+ (selected_file_attr & FILE_ATTR_MASK) != FILE_ATTR_M3U &&
+ (selected_file_attr & ATTR_DIRECTORY) == 0)
+ return ACTION_EXIT_MENUITEM;
+ }
+ else if (this_item == &queue_menu)
+ {
+ if (global_settings.show_queue_options != QUEUE_SHOW_IN_SUBMENU)
+ return ACTION_EXIT_MENUITEM;
+
+ /* queueing options only work during playback */
+ if (!(audio_status() & AUDIO_STATUS_PLAY))
+ return ACTION_EXIT_MENUITEM;
+ }
+ else if ((this_item->flags & MENU_TYPE_MASK) == MT_FUNCTION_CALL_W_PARAM &&
+ this_item->function_param->function_w_param == add_to_playlist)
+ {
+ struct add_to_pl_param *param = this_item->function_param->param;
+
+ if (param->queue)
{
- if (global_settings.show_shuffled_adding_options &&
- !(audio_status() & AUDIO_STATUS_PLAY) &&
- ((selected_file_attr & ATTR_DIRECTORY) ||
- ((selected_file_attr & FILE_ATTR_MASK) == FILE_ATTR_M3U)))
- {
- return action;
- }
+ if (global_settings.show_queue_options != QUEUE_SHOW_AT_TOPLEVEL &&
+ !in_queue_submenu)
+ return ACTION_EXIT_MENUITEM;
}
- else if (this_item == &q_shuf_pl_submenu_item ||
- (this_item == &q_shuf_pl_item &&
- global_settings.show_queue_options == QUEUE_SHOW_AT_TOPLEVEL))
+
+ if (param->position == PLAYLIST_INSERT_SHUFFLED ||
+ param->position == PLAYLIST_INSERT_LAST_SHUFFLED)
{
- if (global_settings.show_shuffled_adding_options &&
- (audio_status() & AUDIO_STATUS_PLAY))
- {
- return action;
- }
+ if (!global_settings.show_shuffled_adding_options)
+ return ACTION_EXIT_MENUITEM;
+
+ if ((selected_file_attr & FILE_ATTR_MASK) != FILE_ATTR_M3U &&
+ (selected_file_attr & ATTR_DIRECTORY) == 0)
+ return ACTION_EXIT_MENUITEM;
}
- else if (this_item == &i_last_shuf_pl_item ||
- this_item == &q_last_shuf_pl_submenu_item ||
- (this_item == &q_last_shuf_pl_item &&
- global_settings.show_queue_options == QUEUE_SHOW_AT_TOPLEVEL))
+
+ if (!param->replace)
{
- if (global_settings.show_shuffled_adding_options &&
- (playlist_amount() > 0) &&
- (audio_status() & AUDIO_STATUS_PLAY) &&
- ((selected_file_attr & ATTR_DIRECTORY) ||
- ((selected_file_attr & FILE_ATTR_MASK) == FILE_ATTR_M3U)))
- {
- return action;
- }
+ if (!(audio_status() & AUDIO_STATUS_PLAY))
+ return ACTION_EXIT_MENUITEM;
}
- return ACTION_EXIT_MENUITEM;
- break;
+ }
+
+ break;
+
+ case ACTION_ENTER_MENUITEM:
+ in_queue_submenu = this_item == &queue_menu;
+ break;
}
+
return action;
}
-void onplay_show_playlist_menu(char* path)
+void onplay_show_playlist_menu(const char* path, int attr, void (*playlist_insert_cb))
{
+ context = CONTEXT_STD;
+ ctx_current_playlist_insert = playlist_insert_cb;
selected_file = path;
- if (dir_exists(path))
- selected_file_attr = ATTR_DIRECTORY;
- else
- selected_file_attr = filetype_get_attr(path);
+ selected_file_attr = attr;
+ in_queue_submenu = false;
do_menu(&tree_playlist_menu, NULL, NULL, false);
}
@@ -790,43 +534,33 @@ void onplay_show_playlist_menu(char* path)
static bool cat_add_to_a_playlist(void)
{
return catalog_add_to_a_playlist(selected_file, selected_file_attr,
- false, NULL);
+ false, NULL, ctx_add_to_playlist);
}
static bool cat_add_to_a_new_playlist(void)
{
return catalog_add_to_a_playlist(selected_file, selected_file_attr,
- true, NULL);
-}
-static int clipboard_callback(int action,
- const struct menu_item_ex *this_item,
- struct gui_synclist *this_list);
-
-static bool set_catalogdir(void)
-{
- catalog_set_directory(selected_file);
- settings_save();
- return false;
+ true, NULL, ctx_add_to_playlist);
}
-MENUITEM_FUNCTION(set_catalogdir_item, 0, ID2P(LANG_SET_AS_PLAYLISTCAT_DIR),
- set_catalogdir, NULL, clipboard_callback, Icon_Playlist);
static int cat_playlist_callback(int action,
const struct menu_item_ex *this_item,
struct gui_synclist *this_list);
-MENUITEM_FUNCTION(cat_add_to_list, 0, ID2P(LANG_CATALOG_ADD_TO),
- cat_add_to_a_playlist, 0, NULL, Icon_Playlist);
+MENUITEM_FUNCTION(cat_add_to_list, 0, ID2P(LANG_ADD_TO_EXISTING_PL),
+ cat_add_to_a_playlist, NULL, Icon_Playlist);
MENUITEM_FUNCTION(cat_add_to_new, 0, ID2P(LANG_CATALOG_ADD_TO_NEW),
- cat_add_to_a_new_playlist, 0, NULL, Icon_Playlist);
-MAKE_ONPLAYMENU(cat_playlist_menu, ID2P(LANG_CATALOG),
+ cat_add_to_a_new_playlist, NULL, Icon_Playlist);
+MAKE_ONPLAYMENU(cat_playlist_menu, ID2P(LANG_ADD_TO_PL),
cat_playlist_callback, Icon_Playlist,
- &cat_add_to_list, &cat_add_to_new, &set_catalogdir_item);
+ &cat_add_to_list, &cat_add_to_new);
-void onplay_show_playlist_cat_menu(char* track_name)
+void onplay_show_playlist_cat_menu(const char* track_name, int attr, void (*add_to_pl_cb))
{
+ context = CONTEXT_STD;
+ ctx_add_to_playlist = add_to_pl_cb;
selected_file = track_name;
- selected_file_attr = FILE_ATTR_AUDIO;
+ selected_file_attr = attr;
do_menu(&cat_playlist_menu, NULL, NULL, false);
}
@@ -843,13 +577,6 @@ static int cat_playlist_callback(int action,
{
return ACTION_EXIT_MENUITEM;
}
-#ifdef HAVE_TAGCACHE
- if (context == CONTEXT_ID3DB &&
- ((selected_file_attr & FILE_ATTR_MASK) != FILE_ATTR_AUDIO))
- {
- return ACTION_EXIT_MENUITEM;
- }
-#endif
switch (action)
{
@@ -913,22 +640,6 @@ static bool poll_cancel_action(const char *path)
return ACTION_STD_CANCEL == get_action(CONTEXT_STD, TIMEOUT_NOBLOCK);
}
-static int confirm_overwrite(void)
-{
- static const char *lines[] = { ID2P(LANG_REALLY_OVERWRITE) };
- static const struct text_message message = { lines, 1 };
- return gui_syncyesno_run(&message, NULL, NULL);
-}
-
-static int confirm_delete(const char *file)
-{
- const char *lines[] = { ID2P(LANG_REALLY_DELETE), file };
- const char *yes_lines[] = { ID2P(LANG_DELETING), file };
- const struct text_message message = { lines, 2 };
- const struct text_message yes_message = { yes_lines, 2 };
- return gui_syncyesno_run(&message, &yes_message, NULL);
-}
-
static bool check_new_name(const char *basename)
{
/* at least prevent escapes out of the base directory from keyboard-
@@ -1023,7 +734,7 @@ static int delete_file_dir(void)
{
const char *to_delete=selected_file;
const int to_delete_attr=selected_file_attr;
- if (confirm_delete(to_delete) != YESNO_YES) {
+ if (confirm_delete_yesno(to_delete) != YESNO_YES) {
return 1;
}
@@ -1069,7 +780,7 @@ static int rename_file(void)
size_t pathlen = oldbase - selection;
char *newbase = newname + pathlen;
- if (strlcpy(newname, selection, sizeof (newname)) >= sizeof (newname)) {
+ if (strmemccpy(newname, selection, sizeof (newname)) == NULL) {
/* Too long */
} else if (kbd_input(newbase, sizeof (newname) - pathlen, NULL) < 0) {
rc = OPRC_CANCELLED;
@@ -1400,7 +1111,7 @@ static int clipboard_paste(void)
case RELATE_DIFFERENT:
if (file_exists(target.path)) {
/* If user chooses not to overwrite, cancel */
- if (confirm_overwrite() == YESNO_NO) {
+ if (confirm_overwrite_yesno() == YESNO_NO) {
rc = OPRC_NOOVERWRT;
break;
}
@@ -1441,10 +1152,13 @@ static int clipboard_paste(void)
{
case OPRC_CANCELLED:
splash_cancelled();
+ /* Fallthrough */
case OPRC_SUCCESS:
onplay_result = ONPLAY_RELOAD_DIR;
+ /* Fallthrough */
case OPRC_NOOP:
clipboard_clear_selection(&clipboard);
+ /* Fallthrough */
case OPRC_NOOVERWRT:
break;
default:
@@ -1488,7 +1202,7 @@ static int ratingitem_callback(int action,
return action;
}
MENUITEM_FUNCTION(rating_item, 0, ID2P(LANG_MENU_SET_RATING),
- set_rating_inline, NULL,
+ set_rating_inline,
ratingitem_callback, Icon_Questionmark);
#endif
MENUITEM_RETURNVALUE(plugin_item, ID2P(LANG_OPEN_PLUGIN),
@@ -1521,22 +1235,27 @@ static int view_cue_item_callback(int action,
return action;
}
MENUITEM_FUNCTION(view_cue_item, 0, ID2P(LANG_BROWSE_CUESHEET),
- view_cue, NULL, view_cue_item_callback, Icon_NOICON);
+ view_cue, view_cue_item_callback, Icon_NOICON);
static int browse_id3_wrapper(void)
{
- if (browse_id3())
+ if (get_current_activity() == ACTIVITY_CONTEXTMENU) /* get rid of parent activity */
+ pop_current_activity_without_refresh(); /* when called from ctxt menu */
+
+ if (browse_id3(audio_current_track(),
+ playlist_get_display_index(),
+ playlist_amount(), NULL, 1))
return GO_TO_ROOT;
return GO_TO_PREVIOUS;
}
/* CONTEXT_WPS items */
MENUITEM_FUNCTION(browse_id3_item, MENU_FUNC_CHECK_RETVAL, ID2P(LANG_MENU_SHOW_ID3_INFO),
- browse_id3_wrapper, NULL, NULL, Icon_NOICON);
+ browse_id3_wrapper, NULL, Icon_NOICON);
#ifdef HAVE_PITCHCONTROL
MENUITEM_FUNCTION(pitch_screen_item, 0, ID2P(LANG_PITCH),
- gui_syncpitchscreen_run, NULL, NULL, Icon_Audio);
+ gui_syncpitchscreen_run, NULL, Icon_Audio);
#endif
/* CONTEXT_[TREE|ID3DB] items */
@@ -1545,19 +1264,19 @@ static int clipboard_callback(int action,
struct gui_synclist *this_list);
MENUITEM_FUNCTION(rename_file_item, 0, ID2P(LANG_RENAME),
- rename_file, NULL, clipboard_callback, Icon_NOICON);
+ rename_file, clipboard_callback, Icon_NOICON);
MENUITEM_FUNCTION(clipboard_cut_item, 0, ID2P(LANG_CUT),
- clipboard_cut, NULL, clipboard_callback, Icon_NOICON);
+ clipboard_cut, clipboard_callback, Icon_NOICON);
MENUITEM_FUNCTION(clipboard_copy_item, 0, ID2P(LANG_COPY),
- clipboard_copy, NULL, clipboard_callback, Icon_NOICON);
+ clipboard_copy, clipboard_callback, Icon_NOICON);
MENUITEM_FUNCTION(clipboard_paste_item, 0, ID2P(LANG_PASTE),
- clipboard_paste, NULL, clipboard_callback, Icon_NOICON);
+ clipboard_paste, clipboard_callback, Icon_NOICON);
MENUITEM_FUNCTION(delete_file_item, 0, ID2P(LANG_DELETE),
- delete_file_dir, NULL, clipboard_callback, Icon_NOICON);
+ delete_file_dir, clipboard_callback, Icon_NOICON);
MENUITEM_FUNCTION(delete_dir_item, 0, ID2P(LANG_DELETE_DIR),
- delete_file_dir, NULL, clipboard_callback, Icon_NOICON);
+ delete_file_dir, clipboard_callback, Icon_NOICON);
MENUITEM_FUNCTION(create_dir_item, 0, ID2P(LANG_CREATE_DIR),
- create_dir, NULL, clipboard_callback, Icon_NOICON);
+ create_dir, clipboard_callback, Icon_NOICON);
/* other items */
static bool list_viewers(void)
@@ -1568,62 +1287,138 @@ static bool list_viewers(void)
return false;
}
+#ifdef HAVE_TAGCACHE
+static bool prepare_database_sel(void *param)
+{
+ if (context == CONTEXT_ID3DB &&
+ (selected_file_attr & FILE_ATTR_MASK) != FILE_ATTR_AUDIO)
+ {
+ if (!strcmp(param, "properties"))
+ strmemccpy(selected_file_path, MAKE_ACT_STR(ACTIVITY_DATABASEBROWSER),
+ sizeof(selected_file_path));
+ else if (!tagtree_get_subentry_filename(selected_file_path, MAX_PATH))
+ {
+ onplay_result = ONPLAY_RELOAD_DIR;
+ return false;
+ }
+
+ selected_file = selected_file_path;
+ }
+ return true;
+}
+#endif
+
static bool onplay_load_plugin(void *param)
{
+#ifdef HAVE_TAGCACHE
+ if (!prepare_database_sel(param))
+ return false;
+#endif
int ret = filetype_load_plugin((const char*)param, selected_file);
if (ret == PLUGIN_USB_CONNECTED)
onplay_result = ONPLAY_RELOAD_DIR;
+ else if (ret == PLUGIN_GOTO_PLUGIN)
+ onplay_result = ONPLAY_PLUGIN;
+ else if (ret == PLUGIN_GOTO_WPS)
+ onplay_result = ONPLAY_START_PLAY;
return false;
}
MENUITEM_FUNCTION(list_viewers_item, 0, ID2P(LANG_ONPLAY_OPEN_WITH),
- list_viewers, NULL, clipboard_callback, Icon_NOICON);
-MENUITEM_FUNCTION(properties_item, MENU_FUNC_USEPARAM, ID2P(LANG_PROPERTIES),
+ list_viewers, clipboard_callback, Icon_NOICON);
+MENUITEM_FUNCTION_W_PARAM(properties_item, 0, ID2P(LANG_PROPERTIES),
onplay_load_plugin, (void *)"properties",
clipboard_callback, Icon_NOICON);
+MENUITEM_FUNCTION_W_PARAM(track_info_item, 0, ID2P(LANG_MENU_SHOW_ID3_INFO),
+ onplay_load_plugin, (void *)"properties",
+ clipboard_callback, Icon_NOICON);
+#ifdef HAVE_TAGCACHE
+MENUITEM_FUNCTION_W_PARAM(pictureflow_item, 0, ID2P(LANG_ONPLAY_PICTUREFLOW),
+ onplay_load_plugin, (void *)"pictureflow",
+ clipboard_callback, Icon_NOICON);
+#endif
static bool onplay_add_to_shortcuts(void)
{
shortcuts_add(SHORTCUT_BROWSER, selected_file);
return false;
}
MENUITEM_FUNCTION(add_to_faves_item, 0, ID2P(LANG_ADD_TO_FAVES),
- onplay_add_to_shortcuts, NULL,
+ onplay_add_to_shortcuts,
clipboard_callback, Icon_NOICON);
#if LCD_DEPTH > 1
static bool set_backdrop(void)
{
- strlcpy(global_settings.backdrop_file, selected_file,
- sizeof(global_settings.backdrop_file));
+ path_append(global_settings.backdrop_file, selected_file,
+ PA_SEP_HARD, sizeof(global_settings.backdrop_file));
settings_save();
skin_backdrop_load_setting();
skin_backdrop_show(sb_get_backdrop(SCREEN_MAIN));
return true;
}
MENUITEM_FUNCTION(set_backdrop_item, 0, ID2P(LANG_SET_AS_BACKDROP),
- set_backdrop, NULL, clipboard_callback, Icon_NOICON);
+ set_backdrop, clipboard_callback, Icon_NOICON);
#endif
#ifdef HAVE_RECORDING
static bool set_recdir(void)
{
- strlcpy(global_settings.rec_directory, selected_file,
- sizeof(global_settings.rec_directory));
+ path_append(global_settings.rec_directory, selected_file,
+ PA_SEP_HARD, sizeof(global_settings.rec_directory));
settings_save();
return false;
}
-MENUITEM_FUNCTION(set_recdir_item, 0, ID2P(LANG_SET_AS_REC_DIR),
- set_recdir, NULL, clipboard_callback, Icon_Recording);
+MENUITEM_FUNCTION(set_recdir_item, 0, ID2P(LANG_RECORDING_DIR),
+ set_recdir, clipboard_callback, Icon_Recording);
#endif
static bool set_startdir(void)
{
- snprintf(global_settings.start_directory,
- sizeof(global_settings.start_directory),
- "%s/", selected_file);
+ path_append(global_settings.start_directory, selected_file,
+ PA_SEP_HARD, sizeof(global_settings.start_directory));
+
+ settings_save();
+ return false;
+}
+MENUITEM_FUNCTION(set_startdir_item, 0, ID2P(LANG_START_DIR),
+ set_startdir, clipboard_callback, Icon_file_view_menu);
+
+static bool set_catalogdir(void)
+{
+ catalog_set_directory(selected_file);
+ settings_save();
+ return false;
+}
+MENUITEM_FUNCTION(set_catalogdir_item, 0, ID2P(LANG_PLAYLIST_DIR),
+ set_catalogdir, clipboard_callback, Icon_Playlist);
+
+#ifdef HAVE_TAGCACHE
+static bool set_databasedir(void)
+{
+ path_append(global_settings.tagcache_db_path, selected_file,
+ PA_SEP_SOFT, sizeof(global_settings.tagcache_db_path));
+
+ struct tagcache_stat *tc_stat = tagcache_get_stat();
+ if (strcasecmp(global_settings.tagcache_db_path, tc_stat->db_path))
+ {
+ splash(HZ, ID2P(LANG_PLEASE_REBOOT));
+ }
+
settings_save();
return false;
}
-MENUITEM_FUNCTION(set_startdir_item, 0, ID2P(LANG_SET_AS_START_DIR),
- set_startdir, NULL, clipboard_callback, Icon_file_view_menu);
+MENUITEM_FUNCTION(set_databasedir_item, 0, ID2P(LANG_DATABASE_DIR),
+ set_databasedir, clipboard_callback, Icon_Audio);
+#endif
+
+MAKE_ONPLAYMENU(set_as_dir_menu, ID2P(LANG_SET_AS),
+ clipboard_callback, Icon_NOICON,
+ &set_catalogdir_item,
+#ifdef HAVE_TAGCACHE
+ &set_databasedir_item,
+#endif
+#ifdef HAVE_RECORDING
+ &set_recdir_item,
+#endif
+ &set_startdir_item);
static int clipboard_callback(int action,
const struct menu_item_ex *this_item,
@@ -1645,9 +1440,8 @@ static int clipboard_callback(int action,
#ifdef HAVE_TAGCACHE
if (context == CONTEXT_ID3DB)
{
- if (((selected_file_attr & FILE_ATTR_MASK) ==
- FILE_ATTR_AUDIO) &&
- this_item == &properties_item)
+ if (this_item == &track_info_item ||
+ this_item == &pictureflow_item)
return action;
return ACTION_EXIT_MENUITEM;
}
@@ -1668,7 +1462,10 @@ static int clipboard_callback(int action,
if (this_item == &rename_file_item ||
this_item == &clipboard_cut_item ||
this_item == &clipboard_copy_item ||
- this_item == &properties_item ||
+ (this_item == &track_info_item &&
+ (selected_file_attr & FILE_ATTR_MASK) == FILE_ATTR_AUDIO) ||
+ (this_item == &properties_item &&
+ (selected_file_attr & FILE_ATTR_MASK) != FILE_ATTR_AUDIO) ||
this_item == &add_to_faves_item)
{
return action;
@@ -1678,10 +1475,14 @@ static int clipboard_callback(int action,
/* only for directories */
if (this_item == &delete_dir_item ||
this_item == &set_startdir_item ||
- this_item == &set_catalogdir_item
+ this_item == &set_catalogdir_item ||
+#ifdef HAVE_TAGCACHE
+ this_item == &set_databasedir_item ||
+#endif
#ifdef HAVE_RECORDING
- || this_item == &set_recdir_item
+ this_item == &set_recdir_item ||
#endif
+ this_item == &set_as_dir_menu
)
return action;
}
@@ -1733,7 +1534,7 @@ MAKE_ONPLAYMENU( wps_onplay_menu, ID2P(LANG_ONPLAY_MENU_TITLE),
);
MENUITEM_FUNCTION(view_playlist_item, 0, ID2P(LANG_VIEW),
- view_playlist, NULL,
+ view_playlist,
onplaymenu_callback, Icon_Playlist);
/* used when onplay() is not called in the CONTEXT_WPS context */
@@ -1742,14 +1543,14 @@ MAKE_ONPLAYMENU( tree_onplay_menu, ID2P(LANG_ONPLAY_MENU_TITLE),
&view_playlist_item, &tree_playlist_menu, &cat_playlist_menu,
&rename_file_item, &clipboard_cut_item, &clipboard_copy_item,
&clipboard_paste_item, &delete_file_item, &delete_dir_item,
+ &list_viewers_item, &create_dir_item, &properties_item, &track_info_item,
+#ifdef HAVE_TAGCACHE
+ &pictureflow_item,
+#endif
#if LCD_DEPTH > 1
&set_backdrop_item,
#endif
- &list_viewers_item, &create_dir_item, &properties_item,
-#ifdef HAVE_RECORDING
- &set_recdir_item,
-#endif
- &set_startdir_item, &add_to_faves_item, &file_menu,
+ &add_to_faves_item, &set_as_dir_menu, &file_menu,
);
static int onplaymenu_callback(int action,
const struct menu_item_ex *this_item,
@@ -1783,17 +1584,24 @@ static int onplaymenu_callback(int action,
#ifdef HAVE_HOTKEY
/* direct function calls, no need for menu callbacks */
-static bool delete_item(void)
+static bool hotkey_delete_item(void)
{
#ifdef HAVE_MULTIVOLUME
/* no delete for volumes */
if (selected_file_attr & ATTR_VOLUME)
return false;
#endif
+
+#ifdef HAVE_TAGCACHE
+ if (context == CONTEXT_ID3DB &&
+ (selected_file_attr & FILE_ATTR_MASK) != FILE_ATTR_AUDIO)
+ return false;
+#endif
+
return delete_file_dir();
}
-static bool open_with(void)
+static bool hotkey_open_with(void)
{
/* only open files */
if (selected_file_attr & ATTR_DIRECTORY)
@@ -1805,116 +1613,146 @@ static bool open_with(void)
return list_viewers();
}
-static int playlist_insert_shuffled(void)
+static int hotkey_tree_pl_insert_shuffled(void)
{
if ((audio_status() & AUDIO_STATUS_PLAY) ||
(selected_file_attr & ATTR_DIRECTORY) ||
((selected_file_attr & FILE_ATTR_MASK) == FILE_ATTR_M3U))
{
- playlist_insert_func((intptr_t*)PLAYLIST_INSERT_SHUFFLED);
- return ONPLAY_START_PLAY;
+ add_to_playlist(&addtopl_insert_shuf);
}
+ return ONPLAY_RELOAD_DIR;
+}
+
+static int hotkey_tree_run_plugin(void *param)
+{
+#ifdef HAVE_TAGCACHE
+ if (!prepare_database_sel(param))
+ return ONPLAY_RELOAD_DIR;
+#endif
+ if (filetype_load_plugin((const char*)param, selected_file) == PLUGIN_GOTO_WPS)
+ return ONPLAY_START_PLAY;
return ONPLAY_RELOAD_DIR;
}
-static void hotkey_run_plugin(void)
+static int hotkey_wps_run_plugin(void)
{
open_plugin_run(ID2P(LANG_HOTKEY_WPS));
+ return ONPLAY_OK;
}
-
-struct hotkey_assignment {
- int action; /* hotkey_action */
- int lang_id; /* Language ID */
- struct menu_func func; /* Function to run if this entry is selected */
- int return_code; /* What to return after the function is run */
-};
-
#define HOTKEY_FUNC(func, param) {{(void *)func}, param}
/* Any desired hotkey functions go here, in the enum in onplay.h,
and in the settings menu in settings_list.c. The order here
is not important. */
-static struct hotkey_assignment hotkey_items[] = {
- { HOTKEY_VIEW_PLAYLIST, LANG_VIEW_DYNAMIC_PLAYLIST,
- HOTKEY_FUNC(NULL, NULL),
- ONPLAY_PLAYLIST },
- { HOTKEY_SHOW_TRACK_INFO, LANG_MENU_SHOW_ID3_INFO,
- HOTKEY_FUNC(browse_id3, NULL),
- ONPLAY_RELOAD_DIR },
+static const struct hotkey_assignment hotkey_items[] = {
+ [0]{ .action = HOTKEY_OFF,
+ .lang_id = LANG_OFF,
+ .func = HOTKEY_FUNC(NULL,NULL),
+ .return_code = ONPLAY_RELOAD_DIR,
+ .flags = HOTKEY_FLAG_WPS | HOTKEY_FLAG_TREE },
+ { .action = HOTKEY_VIEW_PLAYLIST,
+ .lang_id = LANG_VIEW_DYNAMIC_PLAYLIST,
+ .func = HOTKEY_FUNC(NULL, NULL),
+ .return_code = ONPLAY_PLAYLIST,
+ .flags = HOTKEY_FLAG_WPS },
+ { .action = HOTKEY_SHOW_TRACK_INFO,
+ .lang_id = LANG_MENU_SHOW_ID3_INFO,
+ .func = HOTKEY_FUNC(browse_id3_wrapper, NULL),
+ .return_code = ONPLAY_RELOAD_DIR,
+ .flags = HOTKEY_FLAG_WPS },
#ifdef HAVE_PITCHCONTROL
- { HOTKEY_PITCHSCREEN, LANG_PITCH,
- HOTKEY_FUNC(gui_syncpitchscreen_run, NULL),
- ONPLAY_RELOAD_DIR },
+ { .action = HOTKEY_PITCHSCREEN,
+ .lang_id = LANG_PITCH,
+ .func = HOTKEY_FUNC(gui_syncpitchscreen_run, NULL),
+ .return_code = ONPLAY_RELOAD_DIR,
+ .flags = HOTKEY_FLAG_WPS | HOTKEY_FLAG_NOSBS },
+#endif
+ { .action = HOTKEY_OPEN_WITH,
+ .lang_id = LANG_ONPLAY_OPEN_WITH,
+ .func = HOTKEY_FUNC(hotkey_open_with, NULL),
+ .return_code = ONPLAY_RELOAD_DIR,
+ .flags = HOTKEY_FLAG_WPS | HOTKEY_FLAG_TREE },
+ { .action = HOTKEY_DELETE,
+ .lang_id = LANG_DELETE,
+ .func = HOTKEY_FUNC(hotkey_delete_item, NULL),
+ .return_code = ONPLAY_RELOAD_DIR,
+ .flags = HOTKEY_FLAG_WPS | HOTKEY_FLAG_TREE },
+ { .action = HOTKEY_INSERT,
+ .lang_id = LANG_ADD,
+ .func = HOTKEY_FUNC(add_to_playlist, (intptr_t*)&addtopl_insert),
+ .return_code = ONPLAY_RELOAD_DIR,
+ .flags = HOTKEY_FLAG_TREE },
+ { .action = HOTKEY_INSERT_SHUFFLED,
+ .lang_id = LANG_ADD_SHUFFLED,
+ .func = HOTKEY_FUNC(hotkey_tree_pl_insert_shuffled, NULL),
+ .return_code = ONPLAY_FUNC_RETURN,
+ .flags = HOTKEY_FLAG_TREE },
+ { .action = HOTKEY_PLUGIN,
+ .lang_id = LANG_OPEN_PLUGIN,
+ .func = HOTKEY_FUNC(hotkey_wps_run_plugin, NULL),
+ .return_code = ONPLAY_FUNC_RETURN,
+ .flags = HOTKEY_FLAG_WPS | HOTKEY_FLAG_NOSBS },
+ { .action = HOTKEY_BOOKMARK,
+ .lang_id = LANG_BOOKMARK_MENU_CREATE,
+ .func = HOTKEY_FUNC(bookmark_create_menu, NULL),
+ .return_code = ONPLAY_OK,
+ .flags = HOTKEY_FLAG_WPS | HOTKEY_FLAG_NOSBS },
+ { .action = HOTKEY_BOOKMARK_LIST,
+ .lang_id = LANG_BOOKMARK_MENU_LIST,
+ .func = HOTKEY_FUNC(bookmark_load_menu, NULL),
+ .return_code = ONPLAY_START_PLAY,
+ .flags = HOTKEY_FLAG_WPS },
+ { .action = HOTKEY_PROPERTIES,
+ .lang_id = LANG_PROPERTIES,
+ .func = HOTKEY_FUNC(hotkey_tree_run_plugin, (void *)"properties"),
+ .return_code = ONPLAY_FUNC_RETURN,
+ .flags = HOTKEY_FLAG_TREE },
+#ifdef HAVE_TAGCACHE
+ { .action = HOTKEY_PICTUREFLOW,
+ .lang_id = LANG_ONPLAY_PICTUREFLOW,
+ .func = HOTKEY_FUNC(hotkey_tree_run_plugin, (void *)"pictureflow"),
+ .return_code = ONPLAY_FUNC_RETURN,
+ .flags = HOTKEY_FLAG_TREE },
#endif
- { HOTKEY_OPEN_WITH, LANG_ONPLAY_OPEN_WITH,
- HOTKEY_FUNC(open_with, NULL),
- ONPLAY_RELOAD_DIR },
- { HOTKEY_DELETE, LANG_DELETE,
- HOTKEY_FUNC(delete_item, NULL),
- ONPLAY_RELOAD_DIR },
- { HOTKEY_INSERT, LANG_INSERT,
- HOTKEY_FUNC(playlist_insert_func, (intptr_t*)PLAYLIST_INSERT),
- ONPLAY_RELOAD_DIR },
- { HOTKEY_INSERT_SHUFFLED, LANG_INSERT_SHUFFLED,
- HOTKEY_FUNC(playlist_insert_shuffled, NULL),
- ONPLAY_RELOAD_DIR },
- { HOTKEY_PLUGIN, LANG_OPEN_PLUGIN,
- HOTKEY_FUNC(hotkey_run_plugin, NULL),
- ONPLAY_OK },
- { HOTKEY_BOOKMARK, LANG_BOOKMARK_MENU_CREATE,
- HOTKEY_FUNC(bookmark_create_menu, NULL),
- ONPLAY_OK },
};
-/* Return the language ID for this action */
-int get_hotkey_lang_id(int action)
+const struct hotkey_assignment *get_hotkey(int action)
{
- int i = ARRAYLEN(hotkey_items);
- while (i--)
+ for (size_t i = ARRAYLEN(hotkey_items) - 1; i < ARRAYLEN(hotkey_items); i--)
{
if (hotkey_items[i].action == action)
- return hotkey_items[i].lang_id;
+ return &hotkey_items[i];
}
-
- return LANG_OFF;
+ return &hotkey_items[0]; /* no valid hotkey set, return HOTKEY_OFF*/
}
/* Execute the hotkey function, if listed */
static int execute_hotkey(bool is_wps)
{
- int i = ARRAYLEN(hotkey_items);
- struct hotkey_assignment *this_item;
const int action = (is_wps ? global_settings.hotkey_wps :
- global_settings.hotkey_tree);
+ global_settings.hotkey_tree);
/* search assignment struct for a match for the hotkey setting */
- while (i--)
+ const struct hotkey_assignment *this_item = get_hotkey(action);
+
+ /* run the associated function (with optional param), if any */
+ const struct menu_func_param func = this_item->func;
+
+ int func_return = ONPLAY_RELOAD_DIR;
+ if (func.function != NULL)
{
- this_item = &hotkey_items[i];
- if (this_item->action == action)
- {
- /* run the associated function (with optional param), if any */
- const struct menu_func func = this_item->func;
- int func_return = ONPLAY_RELOAD_DIR;
- if (func.function != NULL)
- {
- if (func.param != NULL)
- func_return = (*func.function_w_param)(func.param);
- else
- func_return = (*func.function)();
- }
- /* return with the associated code */
- const int return_code = this_item->return_code;
- /* ONPLAY_OK here means to use the function return code */
- if (return_code == ONPLAY_OK)
- return func_return;
- return return_code;
- }
+ if (func.param != NULL)
+ func_return = (*func.function_w_param)(func.param);
+ else
+ func_return = (*func.function)();
}
+ const int return_code = this_item->return_code;
- /* no valid hotkey set, ignore hotkey */
- return ONPLAY_RELOAD_DIR;
+ if (return_code == ONPLAY_FUNC_RETURN)
+ return func_return; /* Use value returned by function */
+ return return_code; /* or return the associated value */
}
#endif /* HOTKEY */
@@ -1923,12 +1761,31 @@ int onplay(char* file, int attr, int from, bool hotkey)
const struct menu_item_ex *menu;
onplay_result = ONPLAY_OK;
context = from;
- if (file == NULL)
- selected_file = NULL;
- else
+ ctx_current_playlist_insert = NULL;
+ selected_file = NULL;
+#ifdef HAVE_TAGCACHE
+ if (context == CONTEXT_ID3DB &&
+ (attr & FILE_ATTR_MASK) != FILE_ATTR_AUDIO)
{
- strlcpy(selected_file_path, file, MAX_PATH);
- selected_file = selected_file_path;
+ ctx_add_to_playlist = tagtree_add_to_playlist;
+ if (file != NULL)
+ {
+ /* add a leading slash so that catalog_add_to_a_playlist
+ later prefills the name when creating a new playlist */
+ snprintf(selected_file_path, MAX_PATH, "/%s", file);
+ selected_file = selected_file_path;
+ }
+ }
+ else
+#endif
+ {
+ ctx_add_to_playlist = NULL;
+ if (file != NULL)
+ {
+ strmemccpy(selected_file_path, file, MAX_PATH);
+ selected_file = selected_file_path;
+ }
+
}
selected_file_attr = attr;
int menu_selection;
@@ -1945,7 +1802,9 @@ int onplay(char* file, int attr, int from, bool hotkey)
else
menu = &tree_onplay_menu;
menu_selection = do_menu(menu, NULL, NULL, false);
- pop_current_activity();
+
+ if (get_current_activity() == ACTIVITY_CONTEXTMENU) /* Activity may have been */
+ pop_current_activity(); /* popped already by menu item */
switch (menu_selection)
{
@@ -1962,3 +1821,9 @@ int onplay(char* file, int attr, int from, bool hotkey)
return onplay_result;
}
}
+
+int get_onplay_context(void)
+{
+ return context;
+}
+
diff --git a/apps/onplay.h b/apps/onplay.h
index 27f0436403..ea1c2e6c38 100644
--- a/apps/onplay.h
+++ b/apps/onplay.h
@@ -21,7 +21,12 @@
#ifndef _ONPLAY_H_
#define _ONPLAY_H_
+#ifdef HAVE_HOTKEY
+#include "menu.h"
+#endif
+
int onplay(char* file, int attr, int from_screen, bool hotkey);
+int get_onplay_context(void);
enum {
ONPLAY_MAINMENU = -1,
@@ -30,27 +35,49 @@ enum {
ONPLAY_START_PLAY,
ONPLAY_PLAYLIST,
ONPLAY_PLUGIN,
+#ifdef HAVE_HOTKEY
+ ONPLAY_FUNC_RETURN, /* for use in hotkey_assignment only */
+#endif
};
#ifdef HAVE_HOTKEY
-int get_hotkey_lang_id(int action);
enum hotkey_action {
HOTKEY_OFF = 0,
HOTKEY_VIEW_PLAYLIST,
+ HOTKEY_PROPERTIES,
+ HOTKEY_PICTUREFLOW,
HOTKEY_SHOW_TRACK_INFO,
HOTKEY_PITCHSCREEN,
HOTKEY_OPEN_WITH,
HOTKEY_DELETE,
+ HOTKEY_BOOKMARK,
+ HOTKEY_PLUGIN,
HOTKEY_INSERT,
HOTKEY_INSERT_SHUFFLED,
- HOTKEY_PLUGIN,
- HOTKEY_BOOKMARK,
+ HOTKEY_BOOKMARK_LIST,
};
+enum hotkey_flags {
+ HOTKEY_FLAG_NONE = 0x0,
+ HOTKEY_FLAG_WPS = 0x1,
+ HOTKEY_FLAG_TREE = 0x2,
+ HOTKEY_FLAG_NOSBS = 0x4,
+};
+
+struct hotkey_assignment {
+ int action; /* hotkey_action */
+ int lang_id; /* Language ID */
+ struct menu_func_param func; /* Function to run if this entry is selected */
+ int16_t return_code; /* What to return after the function is run. */
+ uint16_t flags; /* Flags what context, display options */
+}; /* (Pick ONPLAY_FUNC_RETURN to use function's return value) */
+
+const struct hotkey_assignment *get_hotkey(int action);
#endif
/* needed for the playlist viewer.. eventually clean this up */
-void onplay_show_playlist_cat_menu(char* track_name);
-void onplay_show_playlist_menu(char* path);
+void onplay_show_playlist_cat_menu(const char* track_name, int attr,
+ void (*add_to_pl_cb));
+void onplay_show_playlist_menu(const char* path, int attr, void (*playlist_insert_cb));
#endif
diff --git a/apps/open_plugin.c b/apps/open_plugin.c
index c64661f2e6..afe59b38e3 100644
--- a/apps/open_plugin.c
+++ b/apps/open_plugin.c
@@ -27,225 +27,464 @@
#include "splash.h"
#include "lang.h"
+/* Define LOGF_ENABLE to enable logf output in this file */
+/*#define LOGF_ENABLE*/
+#include "logf.h"
+
#define ROCK_EXT "rock"
-#define ROCK_LEN 5
+#define ROCK_LEN sizeof(ROCK_EXT)
#define OP_EXT "opx"
-#define OP_LEN 4
+#define OP_LEN sizeof(OP_EXT)
-struct open_plugin_entry_t open_plugin_entry = {0};
+static const uint32_t open_plugin_csum = OPEN_PLUGIN_CHECKSUM;
static const int op_entry_sz = sizeof(struct open_plugin_entry_t);
-static int open_plugin_hash_get_entry(uint32_t hash,
- struct open_plugin_entry_t *entry,
- const char* dat_file);
+static const char* strip_rockbox_root(const char *path)
+{
+ int dlen = ROCKBOX_DIR_LEN;
+ if (strncmp(path, ROCKBOX_DIR, dlen) == 0)
+ path+= dlen;
+ return path;
+}
static inline void op_clear_entry(struct open_plugin_entry_t *entry)
{
- if (entry)
+ if (entry == NULL)
+ return;
+ memset(entry, 0, op_entry_sz);
+ entry->lang_id = OPEN_PLUGIN_LANG_INVALID;
+}
+
+static int op_entry_checksum(struct open_plugin_entry_t *entry)
+{
+/*Note: since we use langids as checksums everytime someone moves the lang file
+* around it could mess with our indexing so invalidate entries when this occurs
+*/
+ if (entry == NULL || entry->checksum != open_plugin_csum +
+ (entry->lang_id <= OPEN_PLUGIN_LANG_INVALID ? 0 : LANG_LAST_INDEX_IN_ARRAY))
{
- memset(entry, 0, op_entry_sz);
- entry->lang_id = -1;
+ logf("OP entry bad checksum");
+ return 0;
}
+ return 1;
}
-static int op_update_dat(struct open_plugin_entry_t *entry)
+static int op_find_entry(int fd, struct open_plugin_entry_t *entry,
+ uint32_t hash, int32_t lang_id)
{
- int fd, fd1;
- uint32_t hash;
+ int ret = OPEN_PLUGIN_NOT_FOUND;
+ int record = 0;
+ if (hash == 0)
+ hash = OPEN_PLUGIN_SEED;
+ if (fd >= 0)
+ {
+ logf("OP find_entry *Searching* hash: %x lang_id: %d", hash, lang_id);
- if (!entry || entry->hash == 0)
- return -1;
+ while (read(fd, entry, op_entry_sz) == op_entry_sz)
+ {
+ if (entry->lang_id == lang_id || entry->hash == hash ||
+ (lang_id == OPEN_PLUGIN_LANG_IGNOREALL))/* return first entry found */
+ {
+#if (CONFIG_STORAGE & STORAGE_ATA)
+ /* may have invalid entries but we append the file so continue looking*/
+ if (op_entry_checksum(entry) <= 0)
+ {
+ ret = OPEN_PLUGIN_INVALID_ENTRY;
+ continue;
+ }
+#endif
+ ret = record;
+ /* NULL terminate fields NOTE -- all are actually +1 larger */
+ entry->name[OPEN_PLUGIN_NAMESZ] = '\0';
+ /*entry->key[OPEN_PLUGIN_BUFSZ] = '\0';*/
+ entry->path[OPEN_PLUGIN_BUFSZ] = '\0';
+ entry->param[OPEN_PLUGIN_BUFSZ] = '\0';
+ logf("OP find_entry *Found* hash: %x lang_id: %d",
+ entry->hash, entry->lang_id);
+ logf("OP find_entry rec: %d name: %s %s %s", record,
+ entry->name, entry->path, entry->param);
+ break;
+ }
+ record++;
+ }
+ }
+
+ /* sanity check */
+#if (CONFIG_STORAGE & STORAGE_ATA)
+ if (ret == OPEN_PLUGIN_INVALID_ENTRY ||
+#else
+ if(
+#endif
+ (ret > OPEN_PLUGIN_NOT_FOUND && op_entry_checksum(entry) <= 0))
+ {
+ splashf(HZ * 2, "%s Invalid entry", str(LANG_OPEN_PLUGIN));
+ ret = OPEN_PLUGIN_NOT_FOUND;
+ }
+ if (ret == OPEN_PLUGIN_NOT_FOUND)
+ op_clear_entry(entry);
+
+ return ret;
+}
+
+static int op_update_dat(struct open_plugin_entry_t *entry, bool clear)
+{
+ int fd;
+ uint32_t hash;
+ int32_t lang_id;
+ if (entry == NULL|| entry->hash == 0)
+ {
+ logf("OP update *No entry*");
+ return OPEN_PLUGIN_NOT_FOUND;
+ }
hash = entry->hash;
+ lang_id = entry->lang_id;
+ if (lang_id <= OPEN_PLUGIN_LANG_INVALID)
+ lang_id = OPEN_PLUGIN_LANG_IGNORE;
+
+ logf("OP update hash: %x lang_id: %d", hash, lang_id);
+ logf("OP update name: %s clear: %d", entry->name, (int) clear);
+ logf("OP update %s %s %s", entry->name, entry->path, entry->param);
+
+#if (CONFIG_STORAGE & STORAGE_ATA) /* Harddrive -- update existing */
+ logf("OP update *Updating entries* %s", OPEN_PLUGIN_DAT);
+ fd = open(OPEN_PLUGIN_DAT, O_RDWR | O_CREAT, 0666);
+
+ if (fd < 0)
+ return OPEN_PLUGIN_NOT_FOUND;
+ /* Only read the hash lang id and checksum */
+ uint32_t hash_langid_csum[3] = {0};
+ const off_t hlc_sz = sizeof(hash_langid_csum);
- fd = open(OPEN_PLUGIN_DAT ".tmp", O_WRONLY | O_CREAT | O_TRUNC, 0666);
- if (!fd)
- return -1;
+ uint32_t csum = open_plugin_csum +
+ (lang_id <= OPEN_PLUGIN_LANG_INVALID ? 0 : LANG_LAST_INDEX_IN_ARRAY);
+
+ while (read(fd, &hash_langid_csum, hlc_sz) == hlc_sz)
+ {
+ if ((hash_langid_csum[0] == hash || (int32_t)hash_langid_csum[1] == lang_id) &&
+ hash_langid_csum[2] == csum)
+ {
+ logf("OP update *Entry Exists* hash: %x langid: %d",
+ hash_langid_csum[0], (int32_t)hash_langid_csum[1]);
+ lseek(fd, 0-hlc_sz, SEEK_CUR);/* back to the start of record */
+ break;
+ }
+ lseek(fd, op_entry_sz - hlc_sz, SEEK_CUR); /* finish record */
+ }
+ write(fd, entry, op_entry_sz);
+ close(fd);
+#else /* Everyone else make a temp file */
+ logf("OP update *Copying entries* %s", OPEN_PLUGIN_DAT ".tmp");
+ fd = open(OPEN_PLUGIN_DAT ".tmp", O_RDWR | O_CREAT | O_TRUNC, 0666);
+
+ if (fd < 0)
+ return OPEN_PLUGIN_NOT_FOUND;
write(fd, entry, op_entry_sz);
- fd1 = open(OPEN_PLUGIN_DAT, O_RDONLY);
- if (fd1)
+ int fd1 = open(OPEN_PLUGIN_DAT, O_RDONLY);
+ if (fd1 >= 0)
{
- while (read(fd1, &open_plugin_entry, op_entry_sz) == op_entry_sz)
+ /* copy non-duplicate entries back from original */
+ while (read(fd1, entry, op_entry_sz) == op_entry_sz)
{
- if (open_plugin_entry.hash != hash)
- write(fd, &open_plugin_entry, op_entry_sz);
+ if (entry->hash != hash && entry->lang_id != lang_id &&
+ op_entry_checksum(entry) > 0)
+ {
+ write(fd, entry, op_entry_sz);
+ }
}
close(fd1);
remove(OPEN_PLUGIN_DAT);
}
+ if (!clear) /* retrieve original entry */
+ {
+ logf("OP update *Loading original entry*");
+ lseek(fd, 0, SEEK_SET);
+ op_find_entry(fd, entry, hash, lang_id);
+ }
close(fd);
-
rename(OPEN_PLUGIN_DAT ".tmp", OPEN_PLUGIN_DAT);
+#endif
+
+ if (clear)
+ {
+ logf("OP update *Clearing entry*");
+ op_clear_entry(entry);
+ }
- op_clear_entry(&open_plugin_entry);
return 0;
}
+static int op_load_entry(uint32_t hash, int32_t lang_id,
+ struct open_plugin_entry_t *entry, const char *dat_file)
+{
+ int opret = OPEN_PLUGIN_NOT_FOUND;
+
+ if (entry != NULL)
+ {
+ /* Is the entry we want already loaded? */
+ if(hash != 0 && entry->hash == hash)
+ return OPEN_PLUGIN_NEEDS_FLUSHED;
+
+ if(lang_id <= OPEN_PLUGIN_LANG_INVALID)
+ {
+ lang_id = OPEN_PLUGIN_LANG_IGNORE;
+ if (hash == 0)/* no hash or langid -- returns first entry found */
+ lang_id = OPEN_PLUGIN_LANG_IGNOREALL;
+ }
+ else if(entry->lang_id == lang_id)
+ {
+ return OPEN_PLUGIN_NEEDS_FLUSHED;
+ }
+
+ /* if another entry is loaded; flush it to disk before we destroy it */
+
+ op_update_dat(open_plugin_get_entry(), true);
+
+ logf("OP get_entry hash: %x lang id: %d db: %s", hash, lang_id, dat_file);
+
+ int fd = open(dat_file, O_RDONLY);
+ if(fd < 0)
+ return OPEN_PLUGIN_NOT_FOUND;
+ opret = op_find_entry(fd, entry, hash, lang_id);
+ close(fd);
+ }
+
+ return opret;
+}
+
+/******************************************************************************/
+/******************************************************************************/
+/* ************************************************************************** */
+/* * PUBLIC INTERFACE FUNCTIONS * *********************************************/
+/* ************************************************************************** */
+/******************************************************************************/
+/******************************************************************************/
+
+/* open_plugin_get_entry()
+* returns the internal open_plugin_entry
+*/
+struct open_plugin_entry_t * open_plugin_get_entry(void)
+{
+ /* holds entry data to load/run/store */
+ static struct open_plugin_entry_t open_plugin_entry = {0};
+ return &open_plugin_entry;
+}
+
+/* open_plugin_add_path()
+* adds a plugin path and calling parameters to open_plugin_entry
+* hash of the key is created for later recall of the plugin path and parameters
+* returns hash of the key or 0 on error
+*/
uint32_t open_plugin_add_path(const char *key, const char *plugin, const char *parameter)
{
- int len;
- bool is_valid = false;
+ size_t len;
uint32_t hash;
int32_t lang_id;
char *pos = "\0";
+ struct open_plugin_entry_t *op_entry = open_plugin_get_entry();
- if(!key)
+ if(key == NULL)
{
- op_clear_entry(&open_plugin_entry);
+ logf("OP add_path No Key, *Clearing entry*");
+ op_clear_entry(op_entry);
return 0;
}
lang_id = P2ID((unsigned char*)key);
- key = P2STR((unsigned char *)key);
-
- open_plugin_get_hash(key, &hash);
+ const char *skey = P2STR((unsigned char *)key);
+ logf("OP add_path key: %s lang id: %d", skey, lang_id);
+ open_plugin_get_hash(strip_rockbox_root(skey), &hash);
-
- if(open_plugin_entry.hash != hash)
+ if(op_entry->hash != hash)
{
+ logf("OP add_path *Flush entry*");
/* the entry in ram needs saved */
- op_update_dat(&open_plugin_entry);
+ op_update_dat(op_entry, true);
}
- if (plugin)
+ while (plugin)
{
/* name */
if (path_basename(plugin, (const char **)&pos) == 0)
pos = "\0";
- len = strlcpy(open_plugin_entry.name, pos, OPEN_PLUGIN_NAMESZ);
+ len = strlcpy(op_entry->name, pos, OPEN_PLUGIN_NAMESZ);
if (len > ROCK_LEN && strcasecmp(&(pos[len-ROCK_LEN]), "." ROCK_EXT) == 0)
{
- is_valid = true;
-
/* path */
- strlcpy(open_plugin_entry.path, plugin, OPEN_PLUGIN_BUFSZ);
+ strmemccpy(op_entry->path, plugin, OPEN_PLUGIN_BUFSZ);
- if(parameter)
- strlcpy(open_plugin_entry.param, parameter, OPEN_PLUGIN_BUFSZ);
- else
- open_plugin_entry.param[0] = '\0';
+ if(!parameter)
+ parameter = "";
+ strmemccpy(op_entry->param, parameter, OPEN_PLUGIN_BUFSZ);
}
else if (len > OP_LEN && strcasecmp(&(pos[len-OP_LEN]), "." OP_EXT) == 0)
{
- is_valid = true;
- open_plugin_hash_get_entry(0, &open_plugin_entry, plugin);
+ /* get the entry from the opx file */
+ op_load_entry(0, OPEN_PLUGIN_LANG_IGNORE, op_entry, plugin);
}
+ else
+ {
+ break;
+ }
+ op_entry->hash = hash;
+ op_entry->lang_id = lang_id;
+ op_entry->checksum = open_plugin_csum +
+ (lang_id <= OPEN_PLUGIN_LANG_INVALID ? 0 : LANG_LAST_INDEX_IN_ARRAY);
+ logf("OP add_path name: %s %s %s",
+ op_entry->name, op_entry->path, op_entry->param);
+ return hash;
}
- if (!is_valid)
+ logf("OP add_path Invalid, *Clearing entry*");
+ if (lang_id != LANG_SHORTCUTS) /* from shortcuts menu */
+ splashf(HZ * 2, str(LANG_OPEN_PLUGIN_NOT_A_PLUGIN), pos);
+ op_clear_entry(op_entry);
+ return 0;
+}
+
+/* only displays directories, .rock, and .opx files */
+static bool callback_show_item(char *name, int attr, struct tree_context *tc)
+{
+ (void)name;
+ if(attr & ATTR_DIRECTORY)
{
- if (lang_id != LANG_SHORTCUTS) /* from shortcuts menu */
- splashf(HZ / 2, str(LANG_OPEN_PLUGIN_NOT_A_PLUGIN), pos);
- op_clear_entry(&open_plugin_entry);
- hash = 0;
+ if (strstr(tc->currdir, PLUGIN_DIR) != NULL)
+ return true;
+ tc->is_browsing = false; /* exit immediately */
}
- else
+ else if(attr & FILE_ATTR_ROCK)
{
- open_plugin_entry.hash = hash;
- open_plugin_entry.lang_id = lang_id;
+ return true;
}
-
- return hash;
+ else if(attr & FILE_ATTR_OPX)
+ {
+ return true;
+ }
+ return false;
}
+/* open_plugin_browse()
+* allows the user to browse for a plugin to set to a supplied key
+* if key is a lang_id that is used otherwise a hash of the key is created
+* for later recall of the plugin path
+*/
void open_plugin_browse(const char *key)
{
- struct browse_context browse;
- char tmp_buf[OPEN_PLUGIN_BUFSZ+1];
- open_plugin_get_entry(key, &open_plugin_entry);
-
- if (open_plugin_entry.path[0] == '\0')
- strcpy(open_plugin_entry.path, PLUGIN_DIR"/");
+ logf("%s", __func__);
- browse_context_init(&browse, SHOW_ALL, BROWSE_SELECTONLY, "",
- Icon_Plugin, open_plugin_entry.path, NULL);
-
- browse.buf = tmp_buf;
- browse.bufsize = OPEN_PLUGIN_BUFSZ;
+ char tmp_buf[OPEN_PLUGIN_BUFSZ+1];
+ open_plugin_load_entry(key);
+ struct open_plugin_entry_t *op_entry = open_plugin_get_entry();
+
+ logf("OP browse key: %s name: %s",
+ (key ? P2STR((unsigned char *)key):"No Key"), op_entry->name);
+ logf("OP browse %s %s", op_entry->path, op_entry->param);
+
+ if (op_entry->path[0] == '\0' || !file_exists(op_entry->path))
+ strcpy(op_entry->path, PLUGIN_DIR"/");
+
+ struct browse_context browse = {
+ .dirfilter = SHOW_ALL,
+ .flags = BROWSE_SELECTONLY | BROWSE_NO_CONTEXT_MENU | BROWSE_DIRFILTER,
+ .title = str(LANG_OPEN_PLUGIN),
+ .icon = Icon_Plugin,
+ .root = op_entry->path,
+ .buf = tmp_buf,
+ .bufsize = sizeof(tmp_buf),
+ .callback_show_item = callback_show_item,
+ };
if (rockbox_browse(&browse) == GO_TO_PREVIOUS)
open_plugin_add_path(key, tmp_buf, NULL);
}
-static int open_plugin_hash_get_entry(uint32_t hash,
- struct open_plugin_entry_t *entry,
- const char* dat_file)
+/* open_plugin_load_entry()
+* recall of the plugin path and parameters based on supplied key
+* returns the index in OPEN_PLUGIN_DAT where the entry was found (>= 0)
+* if the entry was found but has not been saved returns OPEN_PLUGIN_NEEDS_FLUSHED
+* otherwise returns OPEN_PLUGIN_NOT_FOUND (< 0) if key was not found
+*/
+int open_plugin_load_entry(const char *key)
{
- int ret = -1, record = -1;
+ if (key == NULL)
+ key = ID2P(LANG_OPEN_PLUGIN_NOT_A_PLUGIN); /* won't be found */
- if (entry)
- {
+ struct open_plugin_entry_t *op_entry = open_plugin_get_entry();
+ int opret;
+ uint32_t hash = 0;
+ int32_t lang_id = P2ID((unsigned char *)key);
+ const char* skey = P2STR((unsigned char *)key); /* string|LANGPTR => string */
- if (hash != 0)
- {
- if(entry->hash == hash) /* hasn't been flushed yet? */
- return 0;
- else
- op_update_dat(&open_plugin_entry);
- }
-
- int fd = open(dat_file, O_RDONLY);
-
- if (fd)
- {
- while (read(fd, entry, op_entry_sz) == op_entry_sz)
- {
- record++;
- if (hash == 0 || entry->hash == hash)
- {
- ret = record;
- break;
- }
- }
- close(fd);
- }
- if (ret < 0)
- {
- memset(entry, 0, op_entry_sz);
- entry->lang_id = -1;
- }
- }
+ /*Note: P2ID() returns -1 if key isnt a valid lang_id */
+ if (lang_id <= OPEN_PLUGIN_LANG_INVALID)
+ open_plugin_get_hash(strip_rockbox_root(skey), &hash); /* in open_plugin.h */
- return ret;
-}
+ opret = op_load_entry(hash, lang_id, op_entry, OPEN_PLUGIN_DAT);
+ logf("OP entry hash: %x lang id: %d ret: %d key: %s", hash, lang_id, opret, skey);
-int open_plugin_get_entry(const char *key, struct open_plugin_entry_t *entry)
-{
- uint32_t hash;
- key = P2STR((unsigned char *)key);
+ if (opret == OPEN_PLUGIN_NOT_FOUND && lang_id > OPEN_PLUGIN_LANG_INVALID)
+ { /* try rb defaults */
+ opret = op_load_entry(hash, lang_id, op_entry, OPEN_RBPLUGIN_DAT);
+ logf("OP rb_entry hash: %x lang id: %d ret: %d key: %s", hash, lang_id, opret, skey);
+ /* add to the user plugin.dat file if found */
+ op_update_dat(op_entry, false);
- open_plugin_get_hash(key, &hash); /* in open_plugin.h */
- return open_plugin_hash_get_entry(hash, entry, OPEN_PLUGIN_DAT);
+ }
+ logf("OP entry ret: %s", (opret == OPEN_PLUGIN_NOT_FOUND ? "Not Found":"Found"));
+ return opret;
}
+/* open_plugin_run()
+* recall of the plugin path and parameters based on supplied key
+* runs the plugin using plugin_load see plugin_load for return values
+*/
int open_plugin_run(const char *key)
{
int ret = 0;
- const char *path;
- const char *param;
+ int opret = open_plugin_load_entry(key);
+ struct open_plugin_entry_t *op_entry = open_plugin_get_entry();
+ if (opret == OPEN_PLUGIN_NEEDS_FLUSHED)
+ op_update_dat(op_entry, false);
+ const char *path = op_entry->path;
+ const char *param = op_entry->param;
- open_plugin_get_entry(key, &open_plugin_entry);
+ logf("OP run key: %s ret: %d name: %s",
+ (key ? P2STR((unsigned char *)key):"No Key"), opret, op_entry->name);
+ logf("OP run: %s %s %s", op_entry->name, path, param);
- path = open_plugin_entry.path;
- param = open_plugin_entry.param;
if (param[0] == '\0')
param = NULL;
+ if (path[0] == '\0' && key)
+ path = P2STR((unsigned char *)key);
- if (path)
- ret = plugin_load(path, param);
+ ret = plugin_load(path, param);
if (ret != GO_TO_PLUGIN)
- op_clear_entry(&open_plugin_entry);
+ op_clear_entry(op_entry);
return ret;
}
+/* open_plugin_cache_flush()
+* saves the current open_plugin_entry to disk
+*/
void open_plugin_cache_flush(void)
{
- op_update_dat(&open_plugin_entry);
+ logf("%s", __func__);
+ struct open_plugin_entry_t *op_entry = open_plugin_get_entry();
+ /* start_in_screen == 0 is 'Previous Screen' it is actually
+ * defined as (GO_TO_PREVIOUS = -2) + 2 for *Legacy?* reasons AFAICT */
+ if (global_settings.start_in_screen == 0 &&
+ global_status.last_screen == GO_TO_PLUGIN &&
+ op_entry->lang_id > OPEN_PLUGIN_LANG_INVALID)
+ {
+ /* flush the last item as LANG_PREVIOUS_SCREEN if the user wants to resume */
+ op_entry->lang_id = LANG_PREVIOUS_SCREEN;
+ }
+ op_update_dat(op_entry, true);
}
#endif /* ndef __PCTOOL__ */
diff --git a/apps/open_plugin.h b/apps/open_plugin.h
index 8c09c4ac58..847a834c0c 100644
--- a/apps/open_plugin.h
+++ b/apps/open_plugin.h
@@ -32,31 +32,55 @@
#ifndef __PCTOOL__
/* open_plugin path lookup */
#define OPEN_PLUGIN_DAT PLUGIN_DIR "/plugin.dat"
+#define OPEN_RBPLUGIN_DAT PLUGIN_DIR "/rb_plugins.dat"
#define OPEN_PLUGIN_BUFSZ MAX_PATH
#define OPEN_PLUGIN_NAMESZ 32
+
+enum {
+ OPEN_PLUGIN_LANG_INVALID = (-1),
+ OPEN_PLUGIN_LANG_IGNORE = (-2),
+ OPEN_PLUGIN_LANG_IGNOREALL = (-3),
+ OPEN_PLUGIN_INVALID_ENTRY = (-1),
+ OPEN_PLUGIN_NOT_FOUND = (-2),
+ OPEN_PLUGIN_NEEDS_FLUSHED = (-3),
+};
+
struct open_plugin_entry_t
{
+/* hash lang_id checksum need to be the first items */
uint32_t hash;
int32_t lang_id;
+ uint32_t checksum;
char name[OPEN_PLUGIN_NAMESZ+1];
/*char key[OPEN_PLUGIN_BUFSZ+1];*/
char path[OPEN_PLUGIN_BUFSZ+1];
char param[OPEN_PLUGIN_BUFSZ+1];
};
+#define OPEN_PLUGIN_CHECKSUM (uint32_t) \
+( \
+ (sizeof(struct open_plugin_entry_t) << 16) + \
+ offsetof(struct open_plugin_entry_t, hash) + \
+ offsetof(struct open_plugin_entry_t, lang_id) + \
+ offsetof(struct open_plugin_entry_t, checksum) + \
+ offsetof(struct open_plugin_entry_t, name) + \
+ offsetof(struct open_plugin_entry_t, path) + \
+ offsetof(struct open_plugin_entry_t, param))
+
+#define OPEN_PLUGIN_SEED 0x811C9DC5; //seed, 2166136261;
inline static void open_plugin_get_hash(const char *key, uint32_t *hash)
{
/* Calculate modified FNV1a hash of string */
const uint32_t p = 16777619;
- *hash = 0x811C9DC5; //seed, 2166136261;
+ *hash = OPEN_PLUGIN_SEED;
while(*key)
*hash = (*key++ ^ *hash) * p;
}
#ifndef PLUGIN
-extern struct open_plugin_entry_t open_plugin_entry;
+struct open_plugin_entry_t* open_plugin_get_entry(void);
uint32_t open_plugin_add_path(const char *key, const char *plugin, const char *parameter);
-int open_plugin_get_entry(const char *key, struct open_plugin_entry_t *entry);
+int open_plugin_load_entry(const char *key);
void open_plugin_browse(const char *key);
int open_plugin_run(const char *key);
void open_plugin_cache_flush(void); /* flush to disk */
diff --git a/apps/pcmbuf.c b/apps/pcmbuf.c
index 773e97cce0..8718d730fb 100644
--- a/apps/pcmbuf.c
+++ b/apps/pcmbuf.c
@@ -741,6 +741,9 @@ void pcmbuf_start_track_change(enum pcm_track_change_type type)
}
}
+ if (auto_skip && global_settings.single_mode != SINGLE_MODE_OFF && !global_settings.party_mode)
+ crossfade = false;
+
if (crossfade)
{
logf("crossfade track change");
diff --git a/apps/playback.c b/apps/playback.c
index c60468a7dd..7ea44a047f 100644
--- a/apps/playback.c
+++ b/apps/playback.c
@@ -172,12 +172,17 @@ static struct mutex id3_mutex SHAREDBSS_ATTR; /* (A,O)*/
#define MAX_MULTIPLE_AA SKINNABLE_SCREENS_COUNT
#ifdef HAVE_ALBUMART
+static int albumart_mode = -1;
+
static struct albumart_slot
{
struct dim dim; /* Holds width, height of the albumart */
int used; /* Counter; increments if something uses it */
} albumart_slots[MAX_MULTIPLE_AA]; /* (A,O) */
+static char last_folder_aa_path[MAX_PATH] = "\0";
+static int last_folder_aa_hid[MAX_MULTIPLE_AA] = {0};
+
#define FOREACH_ALBUMART(i) for (int i = 0; i < MAX_MULTIPLE_AA; i++)
#endif /* HAVE_ALBUMART */
@@ -194,6 +199,7 @@ static enum filling_state
STATE_FINISHED, /* all remaining tracks are fully buffered */
STATE_ENDING, /* audio playback is ending */
STATE_ENDED, /* audio playback is done */
+ STATE_STOPPED, /* buffering is stopped explicitly */
} filling = STATE_IDLE;
/* Track info - holds information about each track in the buffer */
@@ -310,6 +316,8 @@ static unsigned int track_event_flags = TEF_NONE; /* (A, O-) */
/* Pending manual track skip offset */
static int skip_offset = 0; /* (A, O) */
+static bool track_skip_is_manual = false;
+
/* Track change notification */
static struct
{
@@ -339,7 +347,6 @@ enum audio_start_playback_flags
{
AUDIO_START_RESTART = 0x1, /* "Restart" playback (flush _all_ tracks) */
AUDIO_START_NEWBUF = 0x2, /* Mark the audiobuffer as invalid */
- AUDIO_START_REFRESH = 0x80
};
static void audio_start_playback(const struct audio_resume_info *resume_info,
@@ -588,6 +595,20 @@ static bool track_list_commit_buf_info(struct track_buf_info *tbip,
return true;
}
+#ifdef HAVE_ALBUMART
+static inline void clear_cached_aa_handles(int* aa_handles)
+{
+ if (last_folder_aa_path[0] == 0)
+ return;
+
+ FOREACH_ALBUMART(i)
+ {
+ if (aa_handles[i] == last_folder_aa_hid[i])
+ aa_handles[i] = 0;
+ }
+}
+#endif //HAVE_ALBUMART
+
/* Free the track buffer entry and possibly remove it from the list if it
was succesfully added at some point */
static void track_list_free_buf_info(struct track_buf_info *tbip)
@@ -629,6 +650,9 @@ static void track_list_free_buf_info(struct track_buf_info *tbip)
/* No movement allowed during bufclose calls */
buf_pin_handle(hid, true);
+#ifdef HAVE_ALBUMART
+ clear_cached_aa_handles(tbip->info.aa_hid);
+#endif
FOR_EACH_TRACK_INFO_HANDLE(i)
bufclose(tbip->info.handle[i]);
@@ -816,6 +840,21 @@ size_t audio_buffer_available(void)
return MAX(core_size, size);
}
+#ifdef HAVE_ALBUMART
+static void clear_last_folder_album_art(void)
+{
+ if(last_folder_aa_path[0] == 0)
+ return;
+
+ last_folder_aa_path[0] = 0;
+ FOREACH_ALBUMART(i)
+ {
+ bufclose(last_folder_aa_hid[i]);
+ last_folder_aa_hid[i] = 0;
+ }
+}
+#endif
+
/* Set up the audio buffer for playback
* filebuflen must be pre-initialized with the maximum size */
static void audio_reset_buffer_noalloc(
@@ -852,6 +891,10 @@ static void audio_reset_buffer_noalloc(
filebuf += allocsize;
filebuflen -= allocsize;
+#ifdef HAVE_ALBUMART
+ clear_last_folder_album_art();
+#endif
+
buffering_reset(filebuf, filebuflen);
buffer_state = AUDIOBUF_STATE_INITIALIZED;
@@ -995,7 +1038,7 @@ static void audio_reset_buffer(void)
}
if (core_allocatable() < (1 << 10))
talk_buffer_set_policy(TALK_BUFFER_LOOSE); /* back off voice buffer */
- audiobuf_handle = core_alloc_maximum("audiobuf", &filebuflen, &ops);
+ audiobuf_handle = core_alloc_maximum(&filebuflen, &ops);
if (audiobuf_handle > 0)
audio_reset_buffer_noalloc(core_get_data(audiobuf_handle));
@@ -1209,41 +1252,35 @@ static void audio_update_and_announce_next_track(const struct mp3entry *id3_next
/* Bring the user current mp3entry up to date and set a new offset for the
buffered metadata */
-static void playing_id3_sync(struct track_info *user_infop,
- unsigned long elapsed, unsigned long offset)
+static void playing_id3_sync(struct track_info *user_infop, struct audio_resume_info *resume_info, bool skip_resume_adjustments)
{
id3_mutex_lock();
struct mp3entry *id3 = bufgetid3(user_infop->id3_hid);
- struct mp3entry *playing_id3 = id3_get(PLAYING_ID3);
pcm_play_lock();
- unsigned long e = playing_id3->elapsed;
- unsigned long o = playing_id3->offset;
-
- id3_write(PLAYING_ID3, id3);
-
- if (elapsed == (unsigned long)-1)
+ if (id3)
{
- playing_id3->elapsed = e;
- elapsed = 0;
+ if (resume_info)
+ {
+ id3->elapsed = resume_info->elapsed;
+ id3->offset = resume_info->offset;
+ }
+ id3->skip_resume_adjustments = skip_resume_adjustments;
}
+
+ id3_write(PLAYING_ID3, id3);
- if (offset == (unsigned long)-1)
+ if (!resume_info && id3)
{
- playing_id3->offset = o;
- offset = 0;
+ id3->offset = 0;
+ id3->elapsed = 0;
+ id3->skip_resume_adjustments = false;
}
pcm_play_unlock();
- if (id3)
- {
- id3->elapsed = elapsed;
- id3->offset = offset;
- }
-
id3_mutex_unlock();
}
@@ -1481,7 +1518,7 @@ static bool audio_init_codec(struct track_info *track_infop,
enum { AUTORESUMABLE_UNKNOWN = 0, AUTORESUMABLE_TRUE, AUTORESUMABLE_FALSE };
static bool autoresumable(struct mp3entry *id3)
{
- char *endp, *path;
+ char *path;
size_t len;
bool is_resumable;
@@ -1490,7 +1527,7 @@ static bool autoresumable(struct mp3entry *id3)
is_resumable = false;
- if (id3->path)
+ if (*id3->path)
{
for (path = global_settings.autoresume_paths;
*path; /* search terms left? */
@@ -1499,13 +1536,7 @@ static bool autoresumable(struct mp3entry *id3)
if (*path == ':') /* Skip empty search patterns */
continue;
- /* FIXME: As soon as strcspn or strchrnul are made available in
- the core, the following can be made more efficient. */
- endp = strchr(path, ':');
- if (endp)
- len = endp - path;
- else
- len = strlen(path);
+ len = strcspn(path, ":");
/* Note: At this point, len is always > 0 */
@@ -1553,8 +1584,8 @@ static bool audio_start_codec(bool auto_skip)
return false;
}
-#ifdef HAVE_TAGCACHE
- bool autoresume_enable = global_settings.autoresume_enable;
+ #ifdef HAVE_TAGCACHE
+ bool autoresume_enable = !cur_id3->skip_resume_adjustments && global_settings.autoresume_enable;
if (autoresume_enable && !(cur_id3->elapsed || cur_id3->offset))
{
@@ -1604,8 +1635,11 @@ static bool audio_start_codec(bool auto_skip)
and back again will cause accumulation of silent rewinds - that's not
our job to track directly nor could it be in any reasonable way
*/
- resume_rewind_adjust_progress(cur_id3, &cur_id3->elapsed,
- &cur_id3->offset);
+ if (!cur_id3->skip_resume_adjustments)
+ {
+ resume_rewind_adjust_progress(cur_id3, &cur_id3->elapsed, &cur_id3->offset);
+ cur_id3->skip_resume_adjustments = true;
+ }
/* Update the codec API with the metadata and track info */
id3_write(CODEC_ID3, cur_id3);
@@ -1690,15 +1724,47 @@ static bool audio_load_cuesheet(struct track_info *infop,
}
#ifdef HAVE_ALBUMART
+
+void set_albumart_mode(int setting)
+{
+ if (albumart_mode != -1 &&
+ albumart_mode != setting)
+ playback_update_aa_dims();
+ albumart_mode = setting;
+}
+
+static int load_album_art_from_path(char *path, struct bufopen_bitmap_data *user_data, bool is_current_track, int i)
+{
+ user_data->embedded_albumart = NULL;
+
+ bool same_path = strcmp(last_folder_aa_path, path) == 0;
+ if (same_path && last_folder_aa_hid[i] != 0)
+ return last_folder_aa_hid[i];
+
+ // To simplify caching logic a bit we keep track only for first AA path
+ // If other album arts use different path (like dimension specific arts) just skip caching for them
+ bool is_cacheable = i == 0 && (is_current_track || last_folder_aa_path[0] == 0);
+ if (!same_path && is_cacheable)
+ {
+ clear_last_folder_album_art();
+ strcpy(last_folder_aa_path, path);
+ }
+ int hid = bufopen(path, 0, TYPE_BITMAP, user_data);
+ if (hid != ERR_BUFFER_FULL && (same_path || is_cacheable))
+ last_folder_aa_hid[i] = hid;
+ return hid;
+}
+
/* Load any album art for the file - returns false if the buffer is full */
static int audio_load_albumart(struct track_info *infop,
- struct mp3entry *track_id3)
+ struct mp3entry *track_id3, bool is_current_track)
{
FOREACH_ALBUMART(i)
{
struct bufopen_bitmap_data user_data;
int *aa_hid = &infop->aa_hid[i];
int hid = ERR_UNSUPPORTED_TYPE;
+ bool checked_image_file = false;
/* albumart_slots may change during a yield of bufopen,
* but that's no problem */
@@ -1709,23 +1775,36 @@ static int audio_load_albumart(struct track_info *infop,
memset(&user_data, 0, sizeof(user_data));
user_data.dim = &albumart_slots[i].dim;
+ char path[MAX_PATH];
+ if(global_settings.album_art == AA_PREFER_IMAGE_FILE)
+ {
+ if (find_albumart(track_id3, path, sizeof(path),
+ &albumart_slots[i].dim))
+ {
+ hid = load_album_art_from_path(path, &user_data, is_current_track, i);
+ }
+ checked_image_file = true;
+ }
+
/* We can only decode jpeg for embedded AA */
- if (track_id3->has_embedded_albumart && track_id3->albumart.type == AA_TYPE_JPG)
+ if (global_settings.album_art != AA_OFF &&
+ hid < 0 && hid != ERR_BUFFER_FULL &&
+ track_id3->has_embedded_albumart && track_id3->albumart.type == AA_TYPE_JPG)
{
+ if (is_current_track)
+ clear_last_folder_album_art();
user_data.embedded_albumart = &track_id3->albumart;
hid = bufopen(track_id3->path, 0, TYPE_BITMAP, &user_data);
}
- if (hid < 0 && hid != ERR_BUFFER_FULL)
+ if (global_settings.album_art != AA_OFF && !checked_image_file &&
+ hid < 0 && hid != ERR_BUFFER_FULL)
{
/* No embedded AA or it couldn't be loaded - try other sources */
- char path[MAX_PATH];
-
if (find_albumart(track_id3, path, sizeof(path),
&albumart_slots[i].dim))
{
- user_data.embedded_albumart = NULL;
- hid = bufopen(path, 0, TYPE_BITMAP, &user_data);
+ hid = load_album_art_from_path(path, &user_data, is_current_track, i);
}
}
@@ -1872,10 +1951,39 @@ static int audio_load_track(void)
playlist_peek_offset);
/* Get track name from current playlist read position */
+ int fd = -1;
char path_buf[MAX_PATH + 1];
- const char *path = playlist_peek(playlist_peek_offset,
- path_buf,
- sizeof (path_buf));
+ const char *path;
+
+ while (1)
+ {
+ path = playlist_peek(playlist_peek_offset,
+ path_buf,
+ sizeof (path_buf));
+
+ if (!path)
+ break;
+
+ /* Test for broken playlists by probing for the files */
+ if (file_exists(path))
+ {
+ fd = open(path, O_RDONLY);
+ if (fd >= 0)
+ break;
+ }
+ logf("Open failed %s", path);
+
+ /* only skip if failed track has a successor in playlist */
+ if (!playlist_peek(playlist_peek_offset + 1, NULL, 0))
+ break;
+
+ /* Skip invalid entry from playlist */
+ playlist_skip_entry(NULL, playlist_peek_offset);
+
+ /* Sync the playlist if it isn't finished */
+ if (playlist_peek(playlist_peek_offset, NULL, 0))
+ playlist_next(0);
+ }
if (!path)
{
@@ -1911,13 +2019,12 @@ static int audio_load_track(void)
/* Load the metadata for the first unbuffered track */
ub_id3 = id3_get(UNBUFFERED_ID3);
- int fd = open(path, O_RDONLY);
if (fd >= 0)
{
id3_mutex_lock();
- get_metadata(ub_id3, fd, path);
+ if(!get_metadata(ub_id3, fd, path))
+ wipe_mp3entry(ub_id3);
id3_mutex_unlock();
- close(fd);
}
if (filling != STATE_FULL)
@@ -1935,16 +2042,23 @@ static int audio_load_track(void)
{
track_list_free_info(&info);
track_list.in_progress_hid = 0;
+ if (fd >= 0)
+ close(fd);
return LOAD_TRACK_ERR_FAILED;
}
/* Successful load initiation */
track_list.in_progress_hid = info.self_hid;
}
-
+ if (fd >= 0)
+ close(fd);
return LOAD_TRACK_OK;
}
+#ifdef HAVE_PLAY_FREQ
+static bool audio_auto_change_frequency(struct mp3entry *id3, bool play);
+#endif
+
/* Second part of the track loading: We now have the metadata available, so we
can load the codec, the album art and finally the audio data.
This is called on the audio thread after the buffering thread calls the
@@ -1977,6 +2091,24 @@ static int audio_finish_load_track(struct track_info *infop)
goto audio_finish_load_track_exit;
}
+ struct track_info user_cur;
+
+#ifdef HAVE_PLAY_FREQ
+ track_list_user_current(0, &user_cur);
+ bool is_current_user = infop->self_hid == user_cur.self_hid;
+ if (audio_auto_change_frequency(track_id3, is_current_user))
+ {
+ // frequency switch requires full re-buffering, so stop buffering
+ filling = STATE_STOPPED;
+ logf("buffering stopped (current_track: %b, current_user: %b)", infop->self_hid == cur_info.self_hid, is_current_user);
+ if (is_current_user)
+ // audio_finish_load_track_exit not needed as playback restart is already initiated
+ return trackstat;
+
+ goto audio_finish_load_track_exit;
+ }
+#endif
+
/* Try to load a cuesheet for the track */
if (!audio_load_cuesheet(infop, track_id3))
{
@@ -1987,7 +2119,7 @@ static int audio_finish_load_track(struct track_info *infop)
#ifdef HAVE_ALBUMART
/* Try to load album art for the track */
- int retval = audio_load_albumart(infop, track_id3);
+ int retval = audio_load_albumart(infop, track_id3, infop->self_hid == cur_info.self_hid);
if (retval == ERR_BITMAP_TOO_LARGE)
{
/* No space for album art on buffer because the file is larger than the buffer.
@@ -2003,7 +2135,6 @@ static int audio_finish_load_track(struct track_info *infop)
/* All handles available to external routines are ready - audio and codec
information is private */
- struct track_info user_cur;
track_list_user_current(0, &user_cur);
if (infop->self_hid == user_cur.self_hid)
{
@@ -2041,23 +2172,11 @@ static int audio_finish_load_track(struct track_info *infop)
/** Finally, load the audio **/
off_t file_offset = 0;
- if (track_id3->elapsed > track_id3->length)
- track_id3->elapsed = 0;
-
- if ((off_t)track_id3->offset >= buf_filesize(infop->audio_hid))
- track_id3->offset = 0;
-
- logf("%s: set offset for %s to %lu\n", __func__,
- track_id3->title, track_id3->offset);
-
/* Adjust for resume rewind so we know what to buffer - starting the codec
calls it again, so we don't save it (and they shouldn't accumulate) */
unsigned long elapsed, offset;
resume_rewind_adjust_progress(track_id3, &elapsed, &offset);
- logf("%s: Set resume for %s to %lu %lu", __func__,
- track_id3->title, elapsed, offset);
-
enum data_type audiotype = rbcodec_format_is_atomic(track_id3->codectype) ?
TYPE_ATOMIC_AUDIO : TYPE_PACKET_AUDIO;
@@ -2089,6 +2208,19 @@ static int audio_finish_load_track(struct track_info *infop)
if (hid >= 0)
{
infop->audio_hid = hid;
+
+ /*
+ * Fix up elapsed time and offset if past the end
+ */
+ if (track_id3->elapsed > track_id3->length)
+ track_id3->elapsed = 0;
+
+ if ((off_t)track_id3->offset >= buf_filesize(infop->audio_hid))
+ track_id3->offset = 0;
+
+ logf("%s: set resume for %s to %lu %lX", __func__,
+ track_id3->title, track_id3->elapsed, track_id3->offset);
+
if (infop->self_hid == cur_info.self_hid)
{
/* This is the current track to decode - should be started now */
@@ -2130,7 +2262,7 @@ audio_finish_load_track_exit:
playlist_peek_offset--;
}
- if (filling != STATE_FULL)
+ if (filling != STATE_FULL && filling != STATE_STOPPED)
{
/* Load next track - error or not */
track_list.in_progress_hid = 0;
@@ -2301,7 +2433,7 @@ static void audio_on_finish_load_track(int id3_hid)
change otherwise */
bool was_valid = valid_mp3entry(id3_get(PLAYING_ID3));
- playing_id3_sync(&info, -1, -1);
+ playing_id3_sync(&info, NULL, true);
if (!was_valid)
{
@@ -2338,6 +2470,31 @@ static void audio_on_handle_finished(int hid)
}
}
+static inline char* single_mode_get_id3_tag(struct mp3entry *id3)
+{
+ struct mp3entry *valid_id3 = valid_mp3entry(id3);
+ if (valid_id3 == NULL)
+ return NULL;
+
+ switch (global_settings.single_mode)
+ {
+ case SINGLE_MODE_ALBUM:
+ return valid_id3->album;
+ case SINGLE_MODE_ALBUM_ARTIST:
+ return valid_id3->albumartist;
+ case SINGLE_MODE_ARTIST:
+ return valid_id3->artist;
+ case SINGLE_MODE_COMPOSER:
+ return valid_id3->composer;
+ case SINGLE_MODE_GROUPING:
+ return valid_id3->grouping;
+ case SINGLE_MODE_GENRE:
+ return valid_id3->genre_string;
+ }
+
+ return NULL;
+}
+
/* Called to make an outstanding track skip the current track and to send the
transition events */
static void audio_finalise_track_change(void)
@@ -2393,6 +2550,26 @@ static void audio_finalise_track_change(void)
track_id3 = bufgetid3(info.id3_hid);
}
+ if (SINGLE_MODE_OFF != global_settings.single_mode && global_settings.party_mode == 0 &&
+ ((skip_pending == TRACK_SKIP_AUTO) || (skip_pending == TRACK_SKIP_AUTO_NEW_PLAYLIST)))
+ {
+ bool single_mode_do_pause = true;
+ if (SINGLE_MODE_TRACK != global_settings.single_mode)
+ {
+ char *previous_tag = single_mode_get_id3_tag(id3_get(PLAYING_ID3));
+ char *new_tag = single_mode_get_id3_tag(track_id3);
+ single_mode_do_pause = previous_tag == NULL ||
+ new_tag == NULL ||
+ strcmp(previous_tag, new_tag) != 0;
+ }
+
+ if (single_mode_do_pause)
+ {
+ play_status = PLAY_PAUSED;
+ pcmbuf_pause(true);
+ }
+ }
+
id3_write(PLAYING_ID3, track_id3);
/* The skip is technically over */
@@ -2407,6 +2584,11 @@ static void audio_finalise_track_change(void)
id3_mutex_unlock();
audio_playlist_track_change();
+
+#ifdef HAVE_PLAY_FREQ
+ if (filling == STATE_STOPPED)
+ audio_auto_change_frequency(track_id3, true);
+#endif
}
/* Actually begin a transition and take care of the codec change - may complete
@@ -2417,9 +2599,9 @@ static void audio_begin_track_change(enum pcm_track_change_type type,
/* Even if the new track is bad, the old track must be finished off */
pcmbuf_start_track_change(type);
- bool auto_skip = type != TRACK_CHANGE_MANUAL;
+ track_skip_is_manual = (type == TRACK_CHANGE_MANUAL);
- if (!auto_skip)
+ if (track_skip_is_manual)
{
/* Manual track change happens now */
audio_finalise_track_change();
@@ -2438,10 +2620,10 @@ static void audio_begin_track_change(enum pcm_track_change_type type,
return;
/* Everything needed for the codec is ready - start it */
- if (audio_start_codec(auto_skip))
+ if (audio_start_codec(!track_skip_is_manual))
{
- if (!auto_skip)
- playing_id3_sync(&info, -1, -1);
+ if (track_skip_is_manual)
+ playing_id3_sync(&info, NULL, true);
return;
}
}
@@ -2514,6 +2696,12 @@ static void audio_on_codec_complete(int status)
if (have_track)
{
+ if (filling == STATE_STOPPED)
+ {
+ audio_begin_track_change(TRACK_CHANGE_END_OF_DATA, trackstat);
+ return;
+ }
+
/* Track load is not complete - it might have stopped on a
full buffer without reaching the audio handle or we just
arrived at it early
@@ -2605,18 +2793,16 @@ static void audio_start_playback(const struct audio_resume_info *resume_info,
static struct audio_resume_info resume = { 0, 0 };
enum play_status old_status = play_status;
- if (!(flags & AUDIO_START_REFRESH))
+ bool skip_resume_adjustments = false;
+ if (resume_info)
{
- if (resume_info)
- {
- resume.elapsed = resume_info->elapsed;
- resume.offset = resume_info->offset;
- }
- else
- {
- resume.elapsed = 0;
- resume.offset = 0;
- }
+ resume.elapsed = resume_info->elapsed;
+ resume.offset = resume_info->offset;
+ }
+ else
+ {
+ resume.elapsed = 0;
+ resume.offset = 0;
}
if (flags & AUDIO_START_NEWBUF)
@@ -2643,11 +2829,9 @@ static void audio_start_playback(const struct audio_resume_info *resume_info,
left off */
pcmbuf_play_stop();
- if (!(flags & AUDIO_START_REFRESH))
- {
- resume.elapsed = id3_get(PLAYING_ID3)->elapsed;
- resume.offset = id3_get(PLAYING_ID3)->offset;
- }
+ resume.elapsed = id3_get(PLAYING_ID3)->elapsed;
+ resume.offset = id3_get(PLAYING_ID3)->offset;
+ skip_resume_adjustments = id3_get(PLAYING_ID3)->skip_resume_adjustments;
track_list_clear(TRACK_LIST_CLEAR_ALL);
pcmbuf_update_frequency();
@@ -2724,7 +2908,7 @@ static void audio_start_playback(const struct audio_resume_info *resume_info,
/* This is the currently playing track - get metadata, stat */
struct track_info info;
track_list_current(0, &info);
- playing_id3_sync(&info, resume.elapsed, resume.offset);
+ playing_id3_sync(&info, &resume, skip_resume_adjustments);
if (valid_mp3entry(id3_get(PLAYING_ID3)))
{
@@ -2767,6 +2951,7 @@ static void audio_stop_playback(void)
skip_pending = TRACK_SKIP_NONE;
track_event_flags = TEF_NONE;
+ track_skip_is_manual = false;
/* Close all tracks and mark them NULL */
remove_event(BUFFER_EVENT_REBUFFER, buffer_event_rebuffer_callback);
@@ -2780,7 +2965,9 @@ static void audio_stop_playback(void)
play_status = PLAY_STOPPED;
wipe_track_metadata(true);
-
+#ifdef HAVE_ALBUMART
+ clear_last_folder_album_art();
+#endif
/* Go idle */
filling = STATE_IDLE;
cancel_cpu_boost();
@@ -2840,6 +3027,11 @@ static void audio_on_skip(void)
/* Manual skip */
track_event_flags = TEF_NONE;
+ if (toskip == 1 && global_settings.repeat_mode == REPEAT_ONE)
+ {
+ audio_reset_and_rebuffer(TRACK_LIST_KEEP_CURRENT, 1);
+ }
+
/* If there was an auto skip in progress, there will be residual
advancement of the playlist and/or track list so compensation will be
required in order to end up in the right spot */
@@ -2890,7 +3082,7 @@ static void audio_on_skip(void)
track_list_delta += d;
}
}
-
+ track_skip_is_manual = false;
/* Adjust things by how much the playlist was manually moved */
playlist_peek_offset -= playlist_delta;
@@ -2994,6 +3186,12 @@ static void audio_on_ff_rewind(long time)
track_event_flags = TEF_NONE;
+ if (time < 0)
+ {
+ time = id3->length + time;
+ if (time < 0)
+ time = 0;
+ }
/* Send event before clobbering the time if rewinding. */
if (time == 0)
{
@@ -3031,21 +3229,15 @@ static void audio_on_ff_rewind(long time)
struct track_info cur_info;
track_list_current(0, &cur_info);
- /* Track must complete the loading _now_ since a codec and audio
- handle are needed in order to do the seek */
bool finish_load = cur_info.audio_hid < 0;
-
- if (finish_load &&
- audio_finish_load_track(&cur_info) != LOAD_TRACK_READY)
+ if (finish_load)
{
- /* Call above should push any load sequence - no need for
- halt_decoding_track here if no skip was pending here because
- there would not be a codec started if no audio handle was yet
- opened */
- break;
+ // track is not yet loaded so simply update resume details for upcoming finish_load_track and quit
+ playing_id3_sync(&cur_info, &(struct audio_resume_info){ time, 0 }, true);
+ return;
}
- if (pending == TRACK_SKIP_AUTO || finish_load)
+ if (pending == TRACK_SKIP_AUTO)
{
if (!bufreadid3(cur_info.id3_hid, ci_id3) ||
!audio_init_codec(&cur_info, ci_id3))
@@ -3255,7 +3447,7 @@ void audio_playback_handler(struct queue_event *ev)
case Q_AUDIO_REMAKE_AUDIO_BUFFER:
/* buffer needs to be reinitialized */
LOGFQUEUE("playback < Q_AUDIO_REMAKE_AUDIO_BUFFER");
- audio_start_playback(NULL, AUDIO_START_RESTART | AUDIO_START_NEWBUF | (ev->data ? AUDIO_START_REFRESH : 0));
+ audio_start_playback(NULL, AUDIO_START_RESTART | AUDIO_START_NEWBUF);
if (play_status == PLAY_STOPPED)
return; /* just need to change buffer state */
break;
@@ -3295,6 +3487,7 @@ void audio_playback_handler(struct queue_event *ev)
}
/* Fall-through */
case STATE_FINISHED:
+ case STATE_STOPPED:
/* All data was buffered */
cancel_cpu_boost();
/* Fall-through */
@@ -3366,9 +3559,6 @@ static void buffer_event_finished_callback(unsigned short id, void *ev_data)
break;
case TYPE_PACKET_AUDIO:
- /* Strip any useless trailing tags that are left. */
- strip_tags(hid);
- /* Fall-through */
case TYPE_ATOMIC_AUDIO:
LOGFQUEUE("buffering > audio Q_AUDIO_HANDLE_FINISHED: %d", hid);
audio_queue_post(Q_AUDIO_HANDLE_FINISHED, hid);
@@ -3386,9 +3576,8 @@ static void buffer_event_finished_callback(unsigned short id, void *ev_data)
/* Update elapsed time for next PCM insert */
void audio_codec_update_elapsed(unsigned long elapsed)
{
-#ifdef AB_REPEAT_ENABLE
ab_position_report(elapsed);
-#endif
+
/* Save in codec's id3 where it is used at next pcm insert */
id3_get(CODEC_ID3)->elapsed = elapsed;
}
@@ -3585,8 +3774,7 @@ void audio_hard_stop(void)
#ifdef PLAYBACK_VOICE
voice_stop();
#endif
- if (audiobuf_handle > 0)
- audiobuf_handle = core_free(audiobuf_handle);
+ audiobuf_handle = core_free(audiobuf_handle);
}
/* Resume playback if paused */
@@ -3596,6 +3784,13 @@ void audio_resume(void)
audio_queue_send(Q_AUDIO_PAUSE, false);
}
+/* Internal function used by REPEAT_ONE extern playlist.c */
+bool audio_pending_track_skip_is_manual(void)
+{
+ logf("Track change is: %s", track_skip_is_manual ? "Manual": "Auto");
+ return track_skip_is_manual;
+}
+
/* Skip the specified number of tracks forward or backward from the current */
void audio_skip(int offset)
{
@@ -3857,62 +4052,63 @@ static unsigned long audio_guess_frequency(struct mp3entry *id3)
}
}
-static void audio_change_frequency_callback(unsigned short id, void *data)
+static bool audio_auto_change_frequency(struct mp3entry *id3, bool play)
{
- static bool starting_playback = false;
- struct mp3entry *id3;
-
- switch (id)
+ unsigned long guessed_frequency = global_settings.play_frequency == 0 ? audio_guess_frequency(id3) : 0;
+ if (guessed_frequency && mixer_get_frequency() != guessed_frequency)
{
- case PLAYBACK_EVENT_START_PLAYBACK:
- starting_playback = true;
- break;
+ if (!play)
+ return true;
- case PLAYBACK_EVENT_TRACK_CHANGE:
- id3 = ((struct track_event *)data)->id3;
- if (id3 && !global_settings.play_frequency)
- {
- unsigned long guessed_frequency = audio_guess_frequency(id3);
- if (mixer_get_frequency() != guessed_frequency)
- {
#ifdef PLAYBACK_VOICE
- voice_stop();
+ voice_stop();
#endif
- mixer_set_frequency(guessed_frequency);
- audio_queue_post(Q_AUDIO_REMAKE_AUDIO_BUFFER, starting_playback);
- }
- }
- starting_playback = false;
- break;
-
- default:
- break;
+ mixer_set_frequency(guessed_frequency);
+ audio_queue_post(Q_AUDIO_REMAKE_AUDIO_BUFFER, 0);
+ return true;
}
+ return false;
}
-void audio_set_playback_frequency(int setting)
+void audio_set_playback_frequency(unsigned int sample_rate_hz)
{
+ /* sample_rate_hz == 0 is "automatic", and also a sentinel */
#if HAVE_PLAY_FREQ >= 192
- static const unsigned long play_sampr[] = { SAMPR_44, SAMPR_48, SAMPR_88, SAMPR_96, SAMPR_176, SAMPR_192 };
+ static const unsigned int play_sampr[] = {SAMPR_44, SAMPR_48, SAMPR_88, SAMPR_96, SAMPR_176, SAMPR_192, 0 };
#elif HAVE_PLAY_FREQ >= 96
- static const unsigned long play_sampr[] = { SAMPR_44, SAMPR_48, SAMPR_88, SAMPR_96 };
+ static const unsigned int play_sampr[] = {SAMPR_44, SAMPR_48, SAMPR_88, SAMPR_96, 0 };
#elif HAVE_PLAY_FREQ >= 48
- static const unsigned long play_sampr[] = { SAMPR_44, SAMPR_48 };
+ static const unsigned int play_sampr[] = {SAMPR_44, SAMPR_48, 0 };
#else
#error "HAVE_PLAY_FREQ < 48 ??"
#endif
+ const unsigned int *p_sampr = play_sampr;
+ unsigned int sampr = 0;
- if ((unsigned)setting > ARRAYLEN(play_sampr)) /* [0] is "automatic" */
- setting = 0;
+ while (*p_sampr != 0)
+ {
+ if (*p_sampr == sample_rate_hz)
+ {
+ sampr = *p_sampr;
+ break;
+ }
+ p_sampr++;
+ }
- unsigned long playback_sampr = mixer_get_frequency();
- unsigned long sampr = setting ?
- play_sampr[setting - 1] :
- ((audio_status() == AUDIO_STATUS_PLAY) ?
- audio_guess_frequency(audio_current_track()) :
- playback_sampr);
+ if (sampr == 0)
+ {
+ if (audio_status() & (AUDIO_STATUS_PLAY | AUDIO_STATUS_PAUSE))
+ {
+ sampr = audio_guess_frequency(audio_current_track());
+ }
+ else
+ {
+ logf("could not set sample rate to %u hz", sample_rate_hz);
+ return; /* leave as is */
+ }
+ }
- if (sampr != playback_sampr)
+ if (sampr != mixer_get_frequency())
{
mixer_set_frequency(sampr);
LOGFQUEUE("audio >| audio Q_AUDIO_REMAKE_AUDIO_BUFFER");
@@ -3936,10 +4132,6 @@ void INIT_ATTR playback_init(void)
track_list_init();
buffering_init();
pcmbuf_update_frequency();
-#ifdef HAVE_PLAY_FREQ
- add_event(PLAYBACK_EVENT_TRACK_CHANGE, audio_change_frequency_callback);
- add_event(PLAYBACK_EVENT_START_PLAYBACK, audio_change_frequency_callback);
-#endif
#ifdef HAVE_CROSSFADE
/* Set crossfade setting for next buffer init which should be about... */
pcmbuf_request_crossfade_enable(global_settings.crossfade);
diff --git a/apps/playback.h b/apps/playback.h
index a87ef873d0..782f69fa38 100644
--- a/apps/playback.h
+++ b/apps/playback.h
@@ -83,7 +83,10 @@ void audio_set_cuesheet(bool enable);
void audio_set_crossfade(int enable);
#endif
#ifdef HAVE_PLAY_FREQ
-void audio_set_playback_frequency(int setting);
+void audio_set_playback_frequency(unsigned int sample_rate_hz);
+#endif
+#ifdef HAVE_ALBUMART
+void set_albumart_mode(int setting);
#endif
size_t audio_get_filebuflen(void);
diff --git a/apps/playlist.c b/apps/playlist.c
index 7cd264e821..67d59d1aac 100644
--- a/apps/playlist.c
+++ b/apps/playlist.c
@@ -29,12 +29,15 @@
directory, there will be no playlist file.
2. Control file : This file is automatically created when a playlist is
started and contains all the commands done to it.
-
+
The first non-comment line in a control file must begin with
- "P:VERSION:DIR:FILE" where VERSION is the playlist control file version,
+ "P:VERSION:DIR:FILE" where VERSION is the playlist control file version
DIR is the directory where the playlist is located and FILE is the
- playlist filename. For dirplay, FILE will be empty. An empty playlist
- will have both entries as null.
+ playlist filename (without the directory part).
+
+ When there is an on-disk playlist file, both DIR and FILE are nonempty.
+ Dynamically generated playlists (whether by the file browser, database,
+ or another means) have an empty FILE. For dirplay, DIR will be nonempty.
Control file commands:
a. Add track (A:<position>:<last position>:<path to track>)
@@ -67,9 +70,10 @@
flushed to disk when required.
*/
+//#define LOGF_ENABLE
#include <stdio.h>
-#include <stdlib.h>
-#include <ctype.h>
+#include <stdlib.h>
+#include <ctype.h>
#include "string-extra.h"
#include "playlist.h"
#include "ata_idle_notify.h"
@@ -102,19 +106,48 @@
#include "rbunicode.h"
#include "root_menu.h"
#include "plugin.h" /* To borrow a temp buffer to rewrite a .m3u8 file */
-#include "panic.h"
#include "logdiskf.h"
#ifdef HAVE_DIRCACHE
#include "dircache.h"
#endif
+#include "logf.h"
+#include "panic.h"
+
+#if 0//def ROCKBOX_HAS_LOGDISKF
+#undef DEBUGF
+#undef ERRORF
+#undef WARNF
+#undef NOTEF
+#define DEBUGF logf
+#define ERRORF DEBUGF
+#define WARNF DEBUGF
+#define NOTEF DEBUGF
+#endif
+
+/* default load buffer size (should be at least 1 KiB) */
+#define PLAYLIST_LOAD_BUFLEN (32*1024)
+
+/*
+ * Minimum supported version and current version of the control file.
+ * Any versions outside of this range will be rejected by the loader.
+ *
+ * v1 was the initial version when dynamic playlists were first implemented.
+ * v2 was added shortly thereafter and has been used since 2003.
+ * v3 added the (C)lear command and is otherwise identical to v2.
+ * v4 added the (F)lags command.
+ * v5 added index to the (C)lear command. Added PLAYLIST_INSERT_LAST_ROTATED (-8) as
+ * a supported position for (A)dd or (Q)eue commands.
+ */
+#define PLAYLIST_CONTROL_FILE_MIN_VERSION 2
+#define PLAYLIST_CONTROL_FILE_VERSION 5
-#define PLAYLIST_CONTROL_FILE_VERSION 2
+#define PLAYLIST_COMMAND_SIZE (MAX_PATH+12)
/*
Each playlist index has a flag associated with it which identifies what
type of track it is. These flags are stored in the 4 high order bits of
the index.
-
+
NOTE: This limits the playlist file size to a max of 256M.
Bits 31-30:
@@ -131,7 +164,6 @@
*/
#define PLAYLIST_SEEK_MASK 0x0FFFFFFF
#define PLAYLIST_INSERT_TYPE_MASK 0xC0000000
-#define PLAYLIST_QUEUE_MASK 0x20000000
#define PLAYLIST_INSERT_TYPE_PREPEND 0x40000000
#define PLAYLIST_INSERT_TYPE_INSERT 0x80000000
@@ -140,110 +172,161 @@
#define PLAYLIST_QUEUED 0x20000000
#define PLAYLIST_SKIPPED 0x10000000
-struct directory_search_context {
- struct playlist_info* playlist;
- int position;
- bool queue;
- int count;
-};
-
static struct playlist_info current_playlist;
+/* REPEAT_ONE support function from playback.c */
+extern bool audio_pending_track_skip_is_manual(void);
+static inline bool is_manual_skip(void)
+{
+ return audio_pending_track_skip_is_manual();
+}
-static void empty_playlist(struct playlist_info* playlist, bool resume);
-static void new_playlist(struct playlist_info* playlist, const char *dir,
- const char *file);
-static void create_control(struct playlist_info* playlist);
-static int check_control(struct playlist_info* playlist);
-static int recreate_control(struct playlist_info* playlist);
-static void update_playlist_filename(struct playlist_info* playlist,
- const char *dir, const char *file);
-static int add_indices_to_playlist(struct playlist_info* playlist,
- char* buffer, size_t buflen);
-static int add_track_to_playlist(struct playlist_info* playlist,
- const char *filename, int position,
- bool queue, int seek_pos);
-static int directory_search_callback(char* filename, void* context);
-static int remove_track_from_playlist(struct playlist_info* playlist,
- int position, bool write);
-static int randomise_playlist(struct playlist_info* playlist,
- unsigned int seed, bool start_current,
- bool write);
-static int sort_playlist(struct playlist_info* playlist, bool start_current,
- bool write);
-static int get_next_index(const struct playlist_info* playlist, int steps,
- int repeat_mode);
-static void find_and_set_playlist_index(struct playlist_info* playlist,
- unsigned int seek);
-static int compare(const void* p1, const void* p2);
-static int get_filename(struct playlist_info* playlist, int index, int seek,
- bool control_file, char *buf, int buf_length);
-static int get_next_directory(char *dir);
-static int get_next_dir(char *dir, bool is_forward);
-static int get_previous_directory(char *dir);
-static int check_subdir_for_music(char *dir, const char *subdir, bool recurse);
-static ssize_t format_track_path(char *dest, char *src, int buf_length,
- const char *dir);
-static void display_playlist_count(int count, const unsigned char *fmt,
- bool final);
-static void display_buffer_full(void);
-static int flush_cached_control(struct playlist_info* playlist);
-static int update_control(struct playlist_info* playlist,
- enum playlist_command command, int i1, int i2,
- const char* s1, const char* s2, void* data);
-static void sync_control(struct playlist_info* playlist, bool force);
-static int rotate_index(const struct playlist_info* playlist, int index);
+/* Directory Cache*/
+static void dc_init_filerefs(struct playlist_info *playlist,
+ int start, int count)
+{
+#ifdef HAVE_DIRCACHE
+ if (!playlist->dcfrefs_handle)
+ return;
+
+ struct dircache_fileref *dcfrefs = core_get_data(playlist->dcfrefs_handle);
+ int end = start + count;
+
+ for (int i = start; i < end; i++)
+ dircache_fileref_init(&dcfrefs[i]);
+#else
+ (void)playlist;
+ (void)start;
+ (void)count;
+#endif
+}
#ifdef HAVE_DIRCACHE
-#define PLAYLIST_LOAD_POINTERS 1
+#define PLAYLIST_DC_SCAN_START 1
+#define PLAYLIST_DC_SCAN_STOP 2
-static struct event_queue playlist_queue SHAREDBSS_ATTR;
+static struct event_queue playlist_queue;
+static struct queue_sender_list playlist_queue_sender_list;
static long playlist_stack[(DEFAULT_STACK_SIZE + 0x800)/sizeof(long)];
-static const char playlist_thread_name[] = "playlist cachectrl";
+static const char dc_thread_playlist_name[] = "playlist cachectrl";
#endif
-static struct mutex current_playlist_mutex SHAREDBSS_ATTR;
-static struct mutex created_playlist_mutex SHAREDBSS_ATTR;
+#define playlist_read_lock(p) mutex_lock(&(p)->mutex)
+#define playlist_read_unlock(p) mutex_unlock(&(p)->mutex)
+#define playlist_write_lock(p) mutex_lock(&(p)->mutex)
+#define playlist_write_unlock(p) mutex_unlock(&(p)->mutex)
+
+#if defined(PLAYLIST_DEBUG_ACCESS_ERRORS)
+#define notify_access_error() (splashf(HZ*2, "%s %s", \
+ __func__, ID2P(LANG_PLAYLIST_ACCESS_ERROR)))
+#define notify_control_access_error() (splashf(HZ*2, "%s %s", \
+ __func__, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR)))
+#else
+static void notify_access_error(void) {
+ splash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR));
+}
+static void notify_control_access_error(void) {
+ splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
+}
+#endif
+
+/*
+ * Display buffer full message
+ */
+static void notify_buffer_full(void)
+{
+ splash(HZ*2, ID2P(LANG_PLAYLIST_BUFFER_FULL));
+}
+
+static void dc_thread_start(struct playlist_info *playlist, bool is_dirty)
+{
+#ifdef HAVE_DIRCACHE
+ if (playlist == &current_playlist)
+ queue_post(&playlist_queue, PLAYLIST_DC_SCAN_START, is_dirty);
+#else
+ (void)playlist;
+ (void)is_dirty;
+#endif
+}
+static void dc_thread_stop(struct playlist_info *playlist)
+{
#ifdef HAVE_DIRCACHE
-static void copy_filerefs(struct dircache_fileref *dcfto,
- const struct dircache_fileref *dcffrom,
- int count)
+ if (playlist == &current_playlist)
+ queue_send(&playlist_queue, PLAYLIST_DC_SCAN_STOP, 0);
+#else
+ (void)playlist;
+#endif
+}
+
+/*
+ * Open playlist file and return file descriptor or -1 on error.
+ * The fd should not be closed manually. Not thread-safe.
+ */
+static int pl_open_playlist(struct playlist_info *playlist)
{
- if (!dcfto)
- return;
+ if (playlist->fd >= 0)
+ return playlist->fd;
- if (dcffrom)
- memmove(dcfto, dcffrom, count * sizeof (*dcfto));
- else
+ int fd = open_utf8(playlist->filename, O_RDONLY);
+ if (fd < 0)
+ return fd;
+
+ /* Presence of UTF-8 BOM forces UTF-8 encoding. */
+ if (lseek(fd, 0, SEEK_CUR) > 0)
+ playlist->utf8 = true;
+
+ playlist->fd = fd;
+ return fd;
+}
+
+static void pl_close_fd(int *fdptr)
+{
+ int fd = *fdptr;
+ if (fd >= 0)
{
- /* just initialize the destination */
- for (int i = 0; i < count; i++, dcfto++)
- dircache_fileref_init(dcfto);
+ close(fd);
+ *fdptr = -1;
}
}
-#endif /* HAVE_DIRCACHE */
+
+/*
+ * Close any open file descriptor for the playlist file.
+ * Not thread-safe.
+ */
+static void pl_close_playlist(struct playlist_info *playlist)
+{
+ pl_close_fd(&playlist->fd);
+}
+
+/*
+ * Close any open playlist control file descriptor.
+ * Not thread-safe.
+ */
+static void pl_close_control(struct playlist_info *playlist)
+{
+ pl_close_fd(&playlist->control_fd);
+}
/* Check if the filename suggests M3U or M3U8 format. */
-static bool is_m3u8(const char* filename)
+static bool is_m3u8_name(const char* filename)
{
- int len = strlen(filename);
+ char *dot = strrchr(filename, '.');
/* Default to M3U8 unless explicitly told otherwise. */
- return !(len > 4 && strcasecmp(&filename[len - 4], ".m3u") == 0);
+ return (!dot || strcasecmp(dot, ".m3u") != 0);
}
-
-/* Convert a filename in an M3U playlist to UTF-8.
+/* Convert a filename in an M3U playlist to UTF-8.
*
* buf - the filename to convert; can contain more than one line from the
* playlist.
* buf_len - amount of buf that is used.
* buf_max - total size of buf.
* temp - temporary conversion buffer, at least buf_max bytes.
- *
+ *
* Returns the length of the converted filename.
*/
-static int convert_m3u(char* buf, int buf_len, int buf_max, char* temp)
+static int convert_m3u_name(char* buf, int buf_len, int buf_max, char* temp)
{
int i = 0;
char* dest;
@@ -259,10 +342,10 @@ static int convert_m3u(char* buf, int buf_len, int buf_max, char* temp)
{
i--;
}
-
+
buf_len = i;
dest = temp;
-
+
/* Convert char by char, so as to not overflow temp (iso_decode should
* preferably handle this). No more than 4 bytes should be generated for
* each input char.
@@ -271,110 +354,273 @@ static int convert_m3u(char* buf, int buf_len, int buf_max, char* temp)
{
dest = iso_decode(&buf[i], dest, -1, 1);
}
-
+
*dest = 0;
strcpy(buf, temp);
return dest - temp;
}
/*
- * remove any files and indices associated with the playlist
+ * create control file for playlist
*/
-static void empty_playlist(struct playlist_info* playlist, bool resume)
+static void create_control_unlocked(struct playlist_info* playlist)
{
- playlist->filename[0] = '\0';
- playlist->utf8 = true;
+ playlist->control_fd = open(playlist->control_filename,
+ O_CREAT|O_RDWR|O_TRUNC, 0666);
- if(playlist->fd >= 0)
- /* If there is an already open playlist, close it. */
- close(playlist->fd);
- playlist->fd = -1;
+ playlist->control_created = (playlist->control_fd >= 0);
+
+ if (!playlist->control_created)
+ {
+ if (!check_rockboxdir())
+ return; /* No RB directory exists! */
+
+ cond_talk_ids_fq(LANG_PLAYLIST_CONTROL_ACCESS_ERROR);
+ int fd = playlist->control_fd;
+ splashf(HZ*2, "%s (%d)", str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR), fd);
+ }
+}
+
+/*
+ * Rotate indices such that first_index is index 0
+ */
+static int rotate_index(const struct playlist_info* playlist, int index)
+{
+ index -= playlist->first_index;
+ if (index < 0)
+ index += playlist->amount;
+
+ return index;
+}
+
+static void sync_control_unlocked(struct playlist_info* playlist)
+{
+ if (playlist->control_fd >= 0)
+ fsync(playlist->control_fd);
+}
+
+static int update_control_unlocked(struct playlist_info* playlist,
+ enum playlist_command command, int i1, int i2,
+ const char* s1, const char* s2, int *seekpos)
+{
+ int fd = playlist->control_fd;
+ int result;
+
+ lseek(fd, 0, SEEK_END);
+
+ switch (command)
+ {
+ case PLAYLIST_COMMAND_PLAYLIST:
+ result = fdprintf(fd, "P:%d:%s:%s\n", i1, s1, s2);
+ break;
+ case PLAYLIST_COMMAND_ADD:
+ case PLAYLIST_COMMAND_QUEUE:
+ result = fdprintf(fd, "%c:%d:%d:",
+ command == PLAYLIST_COMMAND_ADD ? 'A' : 'Q', i1, i2);
+ if (result > 0)
+ {
+ *seekpos = lseek(fd, 0, SEEK_CUR);
+ result = fdprintf(fd, "%s\n", s1);
+ }
+ break;
+ case PLAYLIST_COMMAND_DELETE:
+ result = fdprintf(fd, "D:%d\n", i1);
+ break;
+ case PLAYLIST_COMMAND_SHUFFLE:
+ result = fdprintf(fd, "S:%d:%d\n", i1, i2);
+ break;
+ case PLAYLIST_COMMAND_UNSHUFFLE:
+ result = fdprintf(fd, "U:%d\n", i1);
+ break;
+ case PLAYLIST_COMMAND_RESET:
+ result = write(fd, "R\n", 2);
+ break;
+ case PLAYLIST_COMMAND_CLEAR:
+ result = fdprintf(fd, "C:%d\n", i1);
+ break;
+ case PLAYLIST_COMMAND_FLAGS:
+ result = fdprintf(fd, "F:%u:%u\n", i1, i2);
+ break;
+ default:
+ return -1;
+ }
+
+ return result;
+}
+
+/*
+ * store directory and name of playlist file
+ */
+static void update_playlist_filename_unlocked(struct playlist_info* playlist,
+ const char *dir, const char *file)
+{
+ char *sep="";
+ int dirlen = strlen(dir);
+
+ playlist->utf8 = is_m3u8_name(file);
+
+ /* If the dir does not end in trailing slash, we use a separator.
+ Otherwise we don't. */
+ if(!dirlen || '/' != dir[dirlen-1])
+ {
+ sep="/";
+ dirlen++;
+ }
+
+ playlist->dirlen = dirlen;
+
+ snprintf(playlist->filename, sizeof(playlist->filename),
+ "%s%s%s", dir, sep, file);
+}
+
+/*
+ * remove any files and indices associated with the playlist
+ */
+static void empty_playlist_unlocked(struct playlist_info* playlist, bool resume)
+{
+ pl_close_playlist(playlist);
if(playlist->control_fd >= 0)
+ {
close(playlist->control_fd);
- playlist->control_fd = -1;
- playlist->control_created = false;
+ }
- playlist->in_ram = false;
+ playlist->filename[0] = '\0';
- if (playlist->buffer)
- playlist->buffer[0] = 0;
+ playlist->seed = 0;
- playlist->buffer_end_pos = 0;
+ playlist->utf8 = true;
+ playlist->control_created = false;
+ playlist->flags = 0;
+
+ playlist->control_fd = -1;
playlist->index = 0;
playlist->first_index = 0;
playlist->amount = 0;
playlist->last_insert_pos = -1;
- playlist->seed = 0;
- playlist->shuffle_modified = false;
- playlist->deleted = false;
- playlist->num_inserted_tracks = 0;
- playlist->started = false;
- playlist->num_cached = 0;
- playlist->pending_control_sync = false;
+ playlist->started = false;
- if (!resume && playlist->current)
+ if (!resume && playlist == &current_playlist)
{
/* start with fresh playlist control file when starting new
playlist */
- create_control(playlist);
+ create_control_unlocked(playlist);
}
}
+int update_playlist_flags_unlocked(struct playlist_info *playlist,
+ unsigned int setf, unsigned int clearf)
+{
+ unsigned int newflags = (playlist->flags & ~clearf) | setf;
+ if (newflags == playlist->flags)
+ return 0;
+
+ playlist->flags = newflags;
+
+ if (playlist->control_fd >= 0)
+ {
+ int res = update_control_unlocked(playlist, PLAYLIST_COMMAND_FLAGS,
+ setf, clearf, NULL, NULL, NULL);
+ if (res < 0)
+ return res;
+
+ sync_control_unlocked(playlist);
+ }
+
+ return 0;
+}
+
/*
- * Initialize a new playlist for viewing/editing/playing. dir is the
- * directory where the playlist is located and file is the filename.
+ * Returns absolute path of track
+ *
+ * dest: output buffer
+ * src: the file name from the playlist
+ * dir: the absolute path to the directory where the playlist resides
+ * dlen used to truncate dir -- supply -1u to ignore
+ *
+ * The type of path in "src" determines what will be written to "dest":
+ *
+ * 1. UNIX-style absolute paths (/foo/bar) remain unaltered
+ * 2. Windows-style absolute paths (C:/foo/bar) will be converted into an
+ * absolute path by replacing the drive letter with the volume that the
+ * *playlist* resides on, ie. the volume in "dir"
+ * 3. Relative paths are converted to absolute paths by prepending "dir".
+ * This also applies to Windows-style relative paths "C:foo/bar" where
+ * the drive letter is accepted but ignored.
*/
-static void new_playlist(struct playlist_info* playlist, const char *dir,
- const char *file)
+static ssize_t format_track_path(char *dest, char *src, int buf_length,
+ const char *dir, size_t dlen)
{
- const char *fileused = file;
- const char *dirused = dir;
- empty_playlist(playlist, false);
-
- if (!fileused)
+ /* Look for the end of the string (includes NULL) */
+ size_t len = strcspn(src, "\r\n");;
+ /* Now work back killing white space */
+ while (len > 0)
{
- fileused = "";
+ int c = src[len - 1];
+ if (c != '\t' && c != ' ')
+ break;
+ len--;
+ }
- if (dirused && playlist->current) /* !current cannot be in_ram */
- playlist->in_ram = true;
- else
- dirused = ""; /* empty playlist */
+ src[len] = '\0';
+
+ /* Replace backslashes with forward slashes */
+ path_correct_separators(src, src);
+
+ /* Handle Windows-style absolute paths */
+ if (path_strip_drive(src, (const char **)&src, true) >= 0 &&
+ src[-1] == PATH_SEPCH)
+ {
+ #ifdef HAVE_MULTIVOLUME
+ const char *p;
+ path_strip_last_volume(dir, &p, false);
+ //dir = strmemdupa(dir, p - dir);
+ dlen = (p-dir); /* empty if no volspec on dir */
+ #else
+ dir = ""; /* only volume is root */
+ #endif
}
-
- update_playlist_filename(playlist, dirused, fileused);
- if (playlist->control_fd >= 0)
+ if (*dir == '\0')
{
- update_control(playlist, PLAYLIST_COMMAND_PLAYLIST,
- PLAYLIST_CONTROL_FILE_VERSION, -1, dirused, fileused, NULL);
- sync_control(playlist, false);
+ dir = PATH_ROOTSTR;
+ dlen = -1u;
}
+
+ len = path_append_ex(dest, dir, dlen, src, buf_length);
+ if (len >= (size_t)buf_length)
+ return -1; /* buffer too small */
+
+ path_remove_dot_segments (dest, dest);
+ logf("%s %s", __func__, dest);
+ return strlen (dest);
}
/*
- * create control file for playlist
+ * Initialize a new playlist for viewing/editing/playing. dir is the
+ * directory where the playlist is located and file is the filename.
*/
-static void create_control(struct playlist_info* playlist)
+static void new_playlist_unlocked(struct playlist_info* playlist,
+ const char *dir, const char *file)
{
- playlist->control_fd = open(playlist->control_filename,
- O_CREAT|O_RDWR|O_TRUNC, 0666);
- if (playlist->control_fd < 0)
- {
- if (check_rockboxdir())
- {
- cond_talk_ids_fq(LANG_PLAYLIST_CONTROL_ACCESS_ERROR);
- splashf(HZ*2, (unsigned char *)"%s (%d)",
- str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR),
- playlist->control_fd);
- }
- playlist->control_created = false;
- }
- else
+ empty_playlist_unlocked(playlist, false);
+
+ /* enable dirplay for the current playlist if there's a DIR but no FILE */
+ if (!file && dir && playlist == &current_playlist)
+ playlist->flags |= PLAYLIST_FLAG_DIRPLAY;
+
+ dir = dir ?: "";
+ file = file ?: "";
+
+ update_playlist_filename_unlocked(playlist, dir, file);
+
+ if (playlist->control_fd >= 0)
{
- playlist->control_created = true;
+ update_control_unlocked(playlist, PLAYLIST_COMMAND_PLAYLIST,
+ PLAYLIST_CONTROL_FILE_VERSION, -1, dir, file, NULL);
+ sync_control_unlocked(playlist);
}
}
@@ -384,9 +630,12 @@ static void create_control(struct playlist_info* playlist)
*/
static int check_control(struct playlist_info* playlist)
{
+ int ret = 0;
+ playlist_write_lock(playlist);
+
if (!playlist->control_created)
{
- create_control(playlist);
+ create_control_unlocked(playlist);
if (playlist->control_fd >= 0)
{
@@ -396,144 +645,47 @@ static int check_control(struct playlist_info* playlist)
playlist->filename[playlist->dirlen-1] = '\0';
- update_control(playlist, PLAYLIST_COMMAND_PLAYLIST,
+ update_control_unlocked(playlist, PLAYLIST_COMMAND_PLAYLIST,
PLAYLIST_CONTROL_FILE_VERSION, -1, dir, file, NULL);
- sync_control(playlist, false);
+ sync_control_unlocked(playlist);
playlist->filename[playlist->dirlen-1] = c;
}
}
if (playlist->control_fd < 0)
- return -1;
+ ret = -1;
- return 0;
+ playlist_write_unlock(playlist);
+ return ret;
}
+
/*
- * recreate the control file based on current playlist entries
+ * Display splash message showing progress of playlist/directory insertion or
+ * save.
*/
-static int recreate_control(struct playlist_info* playlist)
+static void display_playlist_count(int count, const unsigned char *fmt,
+ bool final)
{
- const char file_suffix[] = "_temp\0";
- char temp_file[MAX_PATH + sizeof(file_suffix)];
- int temp_fd = -1;
- int i;
- int result = 0;
-
- temp_file[0] = 0;
+ static long talked_tick = 0;
+ long id = P2ID(fmt);
- if(playlist->control_fd >= 0)
+ if(id >= 0 && global_settings.talk_menu)
{
- char* dir = playlist->filename;
- char* file = playlist->filename+playlist->dirlen;
- char c = playlist->filename[playlist->dirlen-1];
+ long next_tick = talked_tick + (HZ * 5);
- close(playlist->control_fd);
- playlist->control_fd = 0;
-
- snprintf(temp_file, sizeof(temp_file), "%s%s",
- playlist->control_filename, file_suffix);
-
- if (rename(playlist->control_filename, temp_file) < 0)
- return -1;
-
- temp_fd = open(temp_file, O_RDONLY);
- if (temp_fd < 0)
- return -1;
+ if (final || talked_tick == 0)
+ next_tick = current_tick - 1;
- playlist->control_fd = open(playlist->control_filename,
- O_CREAT|O_RDWR|O_TRUNC, 0666);
- if (playlist->control_fd < 0)
+ if(count && TIME_AFTER(current_tick, next_tick))
{
- close(temp_fd);
- return -1;
- }
-
- playlist->filename[playlist->dirlen-1] = '\0';
-
- /* cannot call update_control() because of mutex */
- result = fdprintf(playlist->control_fd, "P:%d:%s:%s\n",
- PLAYLIST_CONTROL_FILE_VERSION, dir, file);
-
- playlist->filename[playlist->dirlen-1] = c;
-
- if (result < 0)
- {
- close(temp_fd);
- return result;
- }
- }
-
- playlist->seed = 0;
- playlist->shuffle_modified = false;
- playlist->deleted = false;
- playlist->num_inserted_tracks = 0;
-
- for (i=0; i<playlist->amount; i++)
- {
- if (playlist->indices[i] & PLAYLIST_INSERT_TYPE_MASK)
- {
- bool queue = playlist->indices[i] & PLAYLIST_QUEUE_MASK;
- char inserted_file[MAX_PATH+1];
-
- lseek(temp_fd, playlist->indices[i] & PLAYLIST_SEEK_MASK,
- SEEK_SET);
- read_line(temp_fd, inserted_file, sizeof(inserted_file));
-
- result = fdprintf(playlist->control_fd, "%c:%d:%d:",
- queue?'Q':'A', i, playlist->last_insert_pos);
- if (result > 0)
- {
- /* save the position in file where name is written */
- int seek_pos = lseek(playlist->control_fd, 0, SEEK_CUR);
-
- result = fdprintf(playlist->control_fd, "%s\n",
- inserted_file);
-
- playlist->indices[i] =
- (playlist->indices[i] & ~PLAYLIST_SEEK_MASK) | seek_pos;
- }
-
- if (result < 0)
- break;
-
- playlist->num_inserted_tracks++;
+ talked_tick = current_tick;
+ talk_number(count, false);
+ talk_id(id, true);
}
}
- close(temp_fd);
- remove(temp_file);
- fsync(playlist->control_fd);
-
- if (result < 0)
- return result;
-
- return 0;
-}
-
-/*
- * store directory and name of playlist file
- */
-static void update_playlist_filename(struct playlist_info* playlist,
- const char *dir, const char *file)
-{
- char *sep="";
- int dirlen = strlen(dir);
-
- playlist->utf8 = is_m3u8(file);
-
- /* If the dir does not end in trailing slash, we use a separator.
- Otherwise we don't. */
- if(!dirlen || '/' != dir[dirlen-1])
- {
- sep="/";
- dirlen++;
- }
-
- playlist->dirlen = dirlen;
-
- snprintf(playlist->filename, sizeof(playlist->filename),
- "%s%s%s", dir, sep, file);
+ splashf(0, P2STR(fmt), count, str(LANG_OFF_ABORT));
}
/*
@@ -542,9 +694,8 @@ static void update_playlist_filename(struct playlist_info* playlist,
static int add_indices_to_playlist(struct playlist_info* playlist,
char* buffer, size_t buflen)
{
- unsigned int nread;
- unsigned int i = 0;
- unsigned int count = 0;
+ ssize_t nread;
+ unsigned int i, count = 0;
bool store_index;
unsigned char *p;
int result = 0;
@@ -552,15 +703,20 @@ static int add_indices_to_playlist(struct playlist_info* playlist,
if (!buflen)
buffer = alloca((buflen = 64));
- if(-1 == playlist->fd)
- playlist->fd = open_utf8(playlist->filename, O_RDONLY);
- if(playlist->fd < 0)
- return -1; /* failure */
- if((i = lseek(playlist->fd, 0, SEEK_CUR)) > 0)
- playlist->utf8 = true; /* Override any earlier indication. */
+ playlist_write_lock(playlist);
- splash(0, ID2P(LANG_WAIT));
+ /* Close and re-open the playlist to ensure we are properly
+ * positioned at the start of the file after any UTF-8 BOM. */
+ pl_close_playlist(playlist);
+ if (pl_open_playlist(playlist) < 0)
+ {
+ result = -1;
+ goto exit;
+ }
+
+ i = lseek(playlist->fd, 0, SEEK_CUR);
+ splash(0, ID2P(LANG_WAIT));
store_index = true;
while(1)
@@ -572,7 +728,7 @@ static int add_indices_to_playlist(struct playlist_info* playlist,
p = (unsigned char *)buffer;
- for(count=0; count < nread; count++,p++) {
+ for(count=0; count < (unsigned int)nread; count++,p++) {
/* Are we on a new line? */
if((*p == '\n') || (*p == '\r'))
@@ -586,16 +742,14 @@ static int add_indices_to_playlist(struct playlist_info* playlist,
if(*p != '#')
{
if ( playlist->amount >= playlist->max_playlist_size ) {
- display_buffer_full();
+ notify_buffer_full();
result = -1;
goto exit;
}
/* Store a new entry */
playlist->indices[ playlist->amount ] = i+count;
- #ifdef HAVE_DIRCACHE
- copy_filerefs(&playlist->dcfrefs[playlist->amount], NULL, 1);
- #endif
+ dc_init_filerefs(playlist, playlist->amount, 1);
playlist->amount++;
}
}
@@ -605,14 +759,371 @@ static int add_indices_to_playlist(struct playlist_info* playlist,
}
exit:
-#ifdef HAVE_DIRCACHE
- queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
-#endif
+ playlist_write_unlock(playlist);
+ return result;
+}
+
+/*
+ * Checks if there are any music files in the dir or any of its
+ * subdirectories. May be called recursively.
+ */
+static int check_subdir_for_music(char *dir, const char *subdir, bool recurse)
+{
+ int result = -1;
+ size_t dirlen = strlen(dir);
+ int num_files = 0;
+ int i;
+ struct entry *files;
+ bool has_music = false;
+ bool has_subdir = false;
+ struct tree_context* tc = tree_get_context();
+
+ if (path_append(dir + dirlen, PA_SEP_HARD, subdir, MAX_PATH - dirlen) >=
+ MAX_PATH - dirlen)
+ {
+ return 0;
+ }
+ if (ft_load(tc, dir) < 0)
+ {
+ return -2;
+ }
+
+ tree_lock_cache(tc);
+ files = tree_get_entries(tc);
+ num_files = tc->filesindir;
+
+ for (i=0; i<num_files; i++)
+ {
+ if (files[i].attr & ATTR_DIRECTORY)
+ has_subdir = true;
+ else if ((files[i].attr & FILE_ATTR_MASK) == FILE_ATTR_AUDIO)
+ {
+ has_music = true;
+ break;
+ }
+ }
+
+ if (has_music)
+ {
+ tree_unlock_cache(tc);
+ return 0;
+ }
+
+ if (has_subdir && recurse)
+ {
+ for (i=0; i<num_files; i++)
+ {
+ if (action_userabort(TIMEOUT_NOBLOCK))
+ {
+ result = -2;
+ break;
+ }
+
+ if (files[i].attr & ATTR_DIRECTORY)
+ {
+ result = check_subdir_for_music(dir, files[i].name, true);
+ if (!result)
+ break;
+ }
+ }
+ }
+ tree_unlock_cache(tc);
+
+ if (result < 0)
+ {
+ if (dirlen)
+ {
+ dir[dirlen] = '\0';
+ }
+ else
+ {
+ strcpy(dir, PATH_ROOTSTR);
+ }
+
+ /* we now need to reload our current directory */
+ if(ft_load(tc, dir) < 0)
+ splash(HZ*2, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
+ }
return result;
}
/*
+ * search through all the directories (starting with the current) to find
+ * one that has tracks to play
+ */
+static int get_next_dir(char *dir, int direction)
+{
+ struct playlist_info* playlist = &current_playlist;
+ int result = -1;
+ char *start_dir = NULL;
+ bool exit = false;
+ struct tree_context* tc = tree_get_context();
+ int saved_dirfilter = *(tc->dirfilter);
+ unsigned int base_len;
+
+ if (global_settings.constrain_next_folder)
+ {
+ /* constrain results to directories below user's start directory */
+ strcpy(dir, global_settings.start_directory);
+ base_len = strlen(dir);
+
+ /* strip any trailing slash from base directory */
+ if (base_len > 0 && dir[base_len - 1] == '/')
+ {
+ base_len--;
+ dir[base_len] = '\0';
+ }
+ }
+ else
+ {
+ /* start from root directory */
+ dir[0] = '\0';
+ base_len = 0;
+ }
+
+ /* process random folder advance */
+ if (global_settings.next_folder == FOLDER_ADVANCE_RANDOM)
+ {
+ int fd = open(ROCKBOX_DIR "/folder_advance_list.dat", O_RDONLY);
+ if (fd >= 0)
+ {
+ int folder_count = 0;
+ ssize_t nread = read(fd,&folder_count,sizeof(int));
+ if ((nread == sizeof(int)) && folder_count)
+ {
+ char buffer[MAX_PATH];
+ /* give up looking for a directory after we've had four
+ times as many tries as there are directories. */
+ unsigned long allowed_tries = folder_count * 4;
+ int i;
+ srand(current_tick);
+ *(tc->dirfilter) = SHOW_MUSIC;
+ tc->sort_dir = global_settings.sort_dir;
+ while (!exit && allowed_tries--)
+ {
+ i = rand() % folder_count;
+ lseek(fd, sizeof(int) + (MAX_PATH * i), SEEK_SET);
+ read(fd, buffer, MAX_PATH);
+ /* is the current dir within our base dir and has music? */
+ if ((base_len == 0 || !strncmp(buffer, dir, base_len))
+ && check_subdir_for_music(buffer, "", false) == 0)
+ exit = true;
+ }
+ close(fd);
+ *(tc->dirfilter) = saved_dirfilter;
+ tc->sort_dir = global_settings.sort_dir;
+ reload_directory();
+ if (exit)
+ {
+ strcpy(dir,buffer);
+ return 0;
+ }
+ }
+ else
+ close(fd);
+ }
+ }
+
+ /* if the current file is within our base dir, use its dir instead */
+ if (base_len == 0 || !strncmp(playlist->filename, dir, base_len))
+ strmemccpy(dir, playlist->filename, playlist->dirlen);
+
+ /* use the tree browser dircache to load files */
+ *(tc->dirfilter) = SHOW_ALL;
+
+ /* set up sorting/direction */
+ tc->sort_dir = global_settings.sort_dir;
+ if (direction < 0)
+ {
+ static const char sortpairs[] =
+ {
+ [SORT_ALPHA] = SORT_ALPHA_REVERSED,
+ [SORT_DATE] = SORT_DATE_REVERSED,
+ [SORT_TYPE] = SORT_TYPE_REVERSED,
+ [SORT_ALPHA_REVERSED] = SORT_ALPHA,
+ [SORT_DATE_REVERSED] = SORT_DATE,
+ [SORT_TYPE_REVERSED] = SORT_TYPE,
+ };
+
+ if ((unsigned)tc->sort_dir < sizeof(sortpairs))
+ tc->sort_dir = sortpairs[tc->sort_dir];
+ }
+
+ while (!exit)
+ {
+ struct entry *files;
+ int num_files = 0;
+ int i;
+
+ if (ft_load(tc, (dir[0]=='\0')?"/":dir) < 0)
+ {
+ exit = true;
+ result = -1;
+ break;
+ }
+
+ tree_lock_cache(tc);
+ files = tree_get_entries(tc);
+ num_files = tc->filesindir;
+
+ for (i=0; i<num_files; i++)
+ {
+ /* user abort */
+ if (action_userabort(TIMEOUT_NOBLOCK))
+ {
+ result = -1;
+ exit = true;
+ break;
+ }
+
+ if (files[i].attr & ATTR_DIRECTORY)
+ {
+ if (!start_dir)
+ {
+ result = check_subdir_for_music(dir, files[i].name, true);
+ if (result != -1)
+ {
+ exit = true;
+ break;
+ }
+ }
+ else if (!strcmp(start_dir, files[i].name))
+ start_dir = NULL;
+ }
+ }
+ tree_unlock_cache(tc);
+
+ if (!exit)
+ {
+ /* we've already descended to the base dir with nothing found,
+ check whether that contains music */
+ if (strlen(dir) <= base_len)
+ {
+ result = check_subdir_for_music(dir, "", true);
+ if (result == -1)
+ /* there's no music files in the base directory,
+ treat as a fatal error */
+ result = -2;
+ break;
+ }
+ else
+ {
+ /* move down to parent directory. current directory name is
+ stored as the starting point for the search in parent */
+ start_dir = strrchr(dir, '/');
+ if (start_dir)
+ {
+ *start_dir = '\0';
+ start_dir++;
+ }
+ else
+ break;
+ }
+ }
+ }
+
+ /* restore dirfilter */
+ *(tc->dirfilter) = saved_dirfilter;
+ tc->sort_dir = global_settings.sort_dir;
+
+ return result;
+}
+
+/*
+ * gets pathname for track at seek index
+ */
+static int get_track_filename(struct playlist_info* playlist, int index,
+ char *buf, int buf_length)
+{
+ int fd;
+ int max = -1;
+ char tmp_buf[MAX_PATH+1];
+ char dir_buf[MAX_PATH+1];
+ bool utf8 = playlist->utf8;
+
+ if (index < 0 || index >= playlist->amount)
+ return -1;
+
+ playlist_write_lock(playlist);
+
+ bool control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
+ unsigned long seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
+
+#ifdef HAVE_DIRCACHE
+ if (playlist->dcfrefs_handle)
+ {
+ struct dircache_fileref *dcfrefs = core_get_data_pinned(playlist->dcfrefs_handle);
+ max = dircache_get_fileref_path(&dcfrefs[index],
+ tmp_buf, sizeof(tmp_buf));
+
+ NOTEF("%s [in DCache]: 0x%x %s", __func__, dcfrefs[index], tmp_buf);
+ core_put_data_pinned(dcfrefs);
+ }
+#endif /* HAVE_DIRCACHE */
+
+ if (max < 0)
+ {
+ if (control_file)
+ {
+ fd = playlist->control_fd;
+ utf8 = true;
+ }
+ else
+ {
+ fd = pl_open_playlist(playlist);
+ }
+
+ if(-1 != fd)
+ {
+ if (lseek(fd, seek, SEEK_SET) != (off_t)seek)
+ max = -1;
+ else
+ {
+ max = read(fd, tmp_buf, MIN((size_t) buf_length, sizeof(tmp_buf)));
+
+ if (max > 0)
+ {
+ /* playlist file may end without a new line - terminate buffer */
+ tmp_buf[MIN(max, (int)sizeof(tmp_buf) - 1)] = '\0';
+
+ /* Use dir_buf as a temporary buffer. Note that dir_buf must
+ * be as large as tmp_buf.
+ */
+ if (!utf8)
+ max = convert_m3u_name(tmp_buf, max,
+ sizeof(tmp_buf), dir_buf);
+
+ NOTEF("%s [in File]: 0x%x %s", __func__, seek, tmp_buf);
+ }
+ }
+ }
+
+ if (max < 0)
+ {
+ playlist_write_unlock(playlist);
+
+ if (usb_detect() == USB_INSERTED)
+ ; /* ignore error on usb plug */
+ else if (control_file)
+ notify_control_access_error();
+ else
+ notify_access_error();
+
+ return max;
+ }
+ }
+
+ playlist_write_unlock(playlist);
+
+ if (format_track_path(buf, tmp_buf, buf_length,
+ playlist->filename, playlist->dirlen) < 0)
+ return -1;
+
+ return 0;
+}
+
+/*
* Utility function to create a new playlist, fill it with the next or
* previous directory, shuffle it if needed, and start playback.
* If play_last is true and direction zero or negative, start playing
@@ -621,16 +1132,10 @@ exit:
static int create_and_play_dir(int direction, bool play_last)
{
char dir[MAX_PATH + 1];
- int res;
+ int res = get_next_dir(dir, direction);
int index = -1;
- if(direction > 0)
- res = get_next_directory(dir);
- else
- res = get_previous_directory(dir);
-
- if (res < 0)
- /* return the error encountered */
+ if (res < 0) /* return the error encountered */
return res;
if (playlist_create(dir, NULL) != -1)
@@ -656,32 +1161,46 @@ static int create_and_play_dir(int direction, bool play_last)
}
/*
- * Removes all tracks, from the playlist, leaving the presently playing
- * track queued.
+ * remove all tracks, leaving the current track queued
*/
-int playlist_remove_all_tracks(struct playlist_info *playlist)
+static int remove_all_tracks_unlocked(struct playlist_info *playlist, bool write)
{
- int result;
+ if (playlist->amount <= 0)
+ return 0;
- if (playlist == NULL)
- playlist = &current_playlist;
+ /* Move current track down to position 0 */
+ playlist->indices[0] = playlist->indices[playlist->index];
+#ifdef HAVE_DIRCACHE
+ if (playlist->dcfrefs_handle)
+ {
+ struct dircache_fileref *dcfrefs =
+ core_get_data(playlist->dcfrefs_handle);
+ dcfrefs[0] = dcfrefs[playlist->index];
+ }
+#endif
- while (playlist->index > 0)
- if ((result = remove_track_from_playlist(playlist, 0, true)) < 0)
- return result;
+ /* Update playlist state as if by remove_track_unlocked() */
+ playlist->first_index = 0;
+ playlist->amount = 1;
+ playlist->indices[0] |= PLAYLIST_QUEUED;
- while (playlist->amount > 1)
- if ((result = remove_track_from_playlist(playlist, 1, true)) < 0)
- return result;
+ if (playlist->last_insert_pos == 0)
+ playlist->last_insert_pos = -1;
+ else
+ playlist->last_insert_pos = 0;
- if (playlist->amount == 1) {
- playlist->indices[0] |= PLAYLIST_QUEUED;
+ if (write && playlist->control_fd >= 0)
+ {
+ update_control_unlocked(playlist, PLAYLIST_COMMAND_CLEAR,
+ playlist->index, -1, NULL, NULL, NULL);
+ sync_control_unlocked(playlist);
}
+ playlist->index = 0;
+
return 0;
}
-
/*
* Add track to playlist at specified position. There are seven special
* positions that can be specified:
@@ -692,6 +1211,8 @@ int playlist_remove_all_tracks(struct playlist_info *playlist)
* PLAYLIST_INSERT_FIRST - Add track immediately after current song, no
* matter what other tracks have been inserted
* PLAYLIST_INSERT_LAST - Add track to end of playlist
+ * PLAYLIST_INSERT_LAST_ROTATED - Add track to end of playlist, by inserting at
+ * first_index, then increasing first_index by 1
* PLAYLIST_INSERT_SHUFFLED - Add track at some random point between the
* current playing track and end of playlist
* PLAYLIST_INSERT_LAST_SHUFFLED - Add tracks in random order to the end of
@@ -699,9 +1220,9 @@ int playlist_remove_all_tracks(struct playlist_info *playlist)
* PLAYLIST_REPLACE - Erase current playlist, Cue the current track
* and inster this track at the end.
*/
-static int add_track_to_playlist(struct playlist_info* playlist,
- const char *filename, int position,
- bool queue, int seek_pos)
+static int add_track_to_playlist_unlocked(struct playlist_info* playlist,
+ const char *filename, int position,
+ bool queue, int seek_pos)
{
int insert_position, orig_position;
unsigned long flags = PLAYLIST_INSERT_TYPE_INSERT;
@@ -711,7 +1232,7 @@ static int add_track_to_playlist(struct playlist_info* playlist,
if (playlist->amount >= playlist->max_playlist_size)
{
- display_buffer_full();
+ notify_buffer_full();
return -1;
}
@@ -744,11 +1265,15 @@ static int add_track_to_playlist(struct playlist_info* playlist,
playlist->last_insert_pos = position;
break;
case PLAYLIST_INSERT_LAST:
- if (playlist->first_index > 0)
- position = insert_position = playlist->first_index;
- else
+ if (playlist->first_index <= 0)
+ {
position = insert_position = playlist->amount;
-
+ playlist->last_insert_pos = position;
+ break;
+ }
+ /* fallthrough */
+ case PLAYLIST_INSERT_LAST_ROTATED:
+ position = insert_position = playlist->first_index;
playlist->last_insert_pos = position;
break;
case PLAYLIST_INSERT_SHUFFLED:
@@ -758,16 +1283,16 @@ static int add_track_to_playlist(struct playlist_info* playlist,
int offset;
int n = playlist->amount -
rotate_index(playlist, playlist->index);
-
+
if (n > 0)
offset = rand() % n;
else
offset = 0;
-
+
position = playlist->index + offset + 1;
if (position >= playlist->amount)
position -= playlist->amount;
-
+
insert_position = position;
}
else
@@ -776,51 +1301,83 @@ static int add_track_to_playlist(struct playlist_info* playlist,
}
case PLAYLIST_INSERT_LAST_SHUFFLED:
{
- position = insert_position = playlist->last_shuffled_start +
- rand() % (playlist->amount - playlist->last_shuffled_start + 1);
+ int playlist_end = playlist->first_index > 0 ?
+ playlist->first_index : playlist->amount;
+
+ int newpos = playlist->last_shuffled_start +
+ rand() % (playlist_end - playlist->last_shuffled_start + 1);
+
+ position = insert_position = newpos;
break;
}
case PLAYLIST_REPLACE:
- if (playlist_remove_all_tracks(playlist) < 0)
+ if (remove_all_tracks_unlocked(playlist, true) < 0)
return -1;
-
- playlist->last_insert_pos = position = insert_position = playlist->index + 1;
+ int newpos = playlist->index + 1;
+ playlist->last_insert_pos = position = insert_position = newpos;
break;
}
-
+
if (queue)
flags |= PLAYLIST_QUEUED;
+#ifdef HAVE_DIRCACHE
+ struct dircache_fileref *dcfrefs = NULL;
+ if (playlist->dcfrefs_handle)
+ dcfrefs = core_get_data(playlist->dcfrefs_handle);
+#else
+ int *dcfrefs = NULL;
+#endif
+
/* shift indices so that track can be added */
for (i=playlist->amount; i>insert_position; i--)
{
playlist->indices[i] = playlist->indices[i-1];
-#ifdef HAVE_DIRCACHE
- if (playlist->dcfrefs)
- playlist->dcfrefs[i] = playlist->dcfrefs[i-1];
-#endif
+ if (dcfrefs)
+ dcfrefs[i] = dcfrefs[i-1];
}
-
+
/* update stored indices if needed */
if (orig_position < 0)
{
if (playlist->amount > 0 && insert_position <= playlist->index &&
- playlist->started)
+ playlist->started && orig_position != PLAYLIST_INSERT_LAST_ROTATED)
playlist->index++;
+ /*
+ * When inserting into a playlist at positions before or equal to first_index
+ * (unless PLAYLIST_PREPEND is specified explicitly), adjust first_index, so
+ * that track insertion near the end does not affect the start of the playlist
+ */
if (playlist->amount > 0 && insert_position <= playlist->first_index &&
orig_position != PLAYLIST_PREPEND && playlist->started)
+ {
+ /*
+ * To ensure proper resuming from control file for a track that is supposed
+ * to be appended, but is inserted at first_index, store position as special
+ * value.
+ * If we were to store the position unchanged, i.e. use first_index,
+ * track would be prepended, instead, after resuming.
+ */
+ if (insert_position == playlist->first_index)
+ position = PLAYLIST_INSERT_LAST_ROTATED;
+
playlist->first_index++;
+ }
}
+ else if (playlist->amount > 0 && insert_position < playlist->first_index &&
+ playlist->started)
+ playlist->first_index++;
if (insert_position < playlist->last_insert_pos ||
- (insert_position == playlist->last_insert_pos && position < 0))
+ (insert_position == playlist->last_insert_pos && position < 0 &&
+ position != PLAYLIST_INSERT_LAST_ROTATED))
playlist->last_insert_pos++;
if (seek_pos < 0 && playlist->control_fd >= 0)
{
- int result = update_control(playlist,
+ int result = update_control_unlocked(playlist,
(queue?PLAYLIST_COMMAND_QUEUE:PLAYLIST_COMMAND_ADD), position,
playlist->last_insert_pos, filename, NULL, &seek_pos);
@@ -829,14 +1386,10 @@ static int add_track_to_playlist(struct playlist_info* playlist,
}
playlist->indices[insert_position] = flags | seek_pos;
-
-#ifdef HAVE_DIRCACHE
- copy_filerefs(&playlist->dcfrefs[insert_position], NULL, 1);
-#endif
+ dc_init_filerefs(playlist, insert_position, 1);
playlist->amount++;
- playlist->num_inserted_tracks++;
-
+
return insert_position;
}
@@ -846,74 +1399,39 @@ static int add_track_to_playlist(struct playlist_info* playlist,
*/
static int directory_search_callback(char* filename, void* context)
{
- struct directory_search_context* c =
- (struct directory_search_context*) context;
- int insert_pos;
-
- insert_pos = add_track_to_playlist(c->playlist, filename, c->position,
- c->queue, -1);
-
- if (insert_pos < 0)
- return -1;
-
- (c->count)++;
-
- /* After first INSERT_FIRST switch to INSERT so that all the
- rest of the tracks get inserted one after the other */
- if (c->position == PLAYLIST_INSERT_FIRST)
- c->position = PLAYLIST_INSERT;
-
- if (((c->count)%PLAYLIST_DISPLAY_COUNT) == 0)
- {
- unsigned char* count_str;
-
- if (c->queue)
- count_str = ID2P(LANG_PLAYLIST_QUEUE_COUNT);
- else
- count_str = ID2P(LANG_PLAYLIST_INSERT_COUNT);
-
- display_playlist_count(c->count, count_str, false);
-
- if ((c->count) == PLAYLIST_DISPLAY_COUNT &&
- (audio_status() & AUDIO_STATUS_PLAY) &&
- c->playlist->started)
- audio_flush_and_reload_tracks();
- }
-
- return 0;
+ return playlist_insert_context_add(context, filename);
}
/*
* remove track at specified position
*/
-static int remove_track_from_playlist(struct playlist_info* playlist,
- int position, bool write)
+static int remove_track_unlocked(struct playlist_info* playlist,
+ int position, bool write)
{
int i;
- bool inserted;
+ int result = 0;
if (playlist->amount <= 0)
return -1;
- inserted = playlist->indices[position] & PLAYLIST_INSERT_TYPE_MASK;
+#ifdef HAVE_DIRCACHE
+ struct dircache_fileref *dcfrefs = NULL;
+ if (playlist->dcfrefs_handle)
+ dcfrefs = core_get_data(playlist->dcfrefs_handle);
+#else
+ int *dcfrefs = NULL;
+#endif
/* shift indices now that track has been removed */
for (i=position; i<playlist->amount; i++)
{
playlist->indices[i] = playlist->indices[i+1];
-#ifdef HAVE_DIRCACHE
- if (playlist->dcfrefs)
- playlist->dcfrefs[i] = playlist->dcfrefs[i+1];
-#endif
+ if (dcfrefs)
+ dcfrefs[i] = dcfrefs[i+1];
}
playlist->amount--;
- if (inserted)
- playlist->num_inserted_tracks--;
- else
- playlist->deleted = true;
-
/* update stored indices if needed */
if (position < playlist->index)
playlist->index--;
@@ -928,30 +1446,48 @@ static int remove_track_from_playlist(struct playlist_info* playlist,
if (write && playlist->control_fd >= 0)
{
- int result = update_control(playlist, PLAYLIST_COMMAND_DELETE,
+ result = update_control_unlocked(playlist, PLAYLIST_COMMAND_DELETE,
position, -1, NULL, NULL, NULL);
+ if (result >= 0)
+ sync_control_unlocked(playlist);
+ }
- if (result < 0)
- return result;
+ return result;
+}
+
+/*
+ * Search for the seek track and set appropriate indices. Used after shuffle
+ * to make sure the current index is still pointing to correct track.
+ */
+static void find_and_set_playlist_index_unlocked(struct playlist_info* playlist,
+ unsigned long seek)
+{
+ int i;
+
+ /* Set the index to the current song */
+ for (i=0; i<playlist->amount; i++)
+ {
+ if (playlist->indices[i] == seek)
+ {
+ playlist->index = playlist->first_index = i;
- sync_control(playlist, false);
+ break;
+ }
}
-
- return 0;
}
/*
* randomly rearrange the array of indices for the playlist. If start_current
* is true then update the index to the new index of the current playing track
*/
-static int randomise_playlist(struct playlist_info* playlist,
- unsigned int seed, bool start_current,
- bool write)
+static int randomise_playlist_unlocked(struct playlist_info* playlist,
+ unsigned int seed, bool start_current,
+ bool write)
{
int count;
int candidate;
- unsigned int current = playlist->indices[playlist->index];
-
+ unsigned long current = playlist->indices[playlist->index];
+
/* seed 0 is used to identify sorted playlist for resume purposes */
if (seed == 0)
seed = 1;
@@ -966,75 +1502,98 @@ static int randomise_playlist(struct playlist_info* playlist,
candidate = rand() % (count + 1);
/* now swap the values at the 'count' and 'candidate' positions */
- int indextmp = playlist->indices[candidate];
+ unsigned long indextmp = playlist->indices[candidate];
playlist->indices[candidate] = playlist->indices[count];
playlist->indices[count] = indextmp;
#ifdef HAVE_DIRCACHE
- if (playlist->dcfrefs)
+ if (playlist->dcfrefs_handle)
{
- struct dircache_fileref dcftmp = playlist->dcfrefs[candidate];
- playlist->dcfrefs[candidate] = playlist->dcfrefs[count];
- playlist->dcfrefs[count] = dcftmp;
+ struct dircache_fileref *dcfrefs = core_get_data(playlist->dcfrefs_handle);
+ struct dircache_fileref dcftmp = dcfrefs[candidate];
+ dcfrefs[candidate] = dcfrefs[count];
+ dcfrefs[count] = dcftmp;
}
#endif
}
if (start_current)
- find_and_set_playlist_index(playlist, current);
+ find_and_set_playlist_index_unlocked(playlist, current);
/* indices have been moved so last insert position is no longer valid */
playlist->last_insert_pos = -1;
playlist->seed = seed;
- if (playlist->num_inserted_tracks > 0 || playlist->deleted)
- playlist->shuffle_modified = true;
if (write)
{
- update_control(playlist, PLAYLIST_COMMAND_SHUFFLE, seed,
+ update_control_unlocked(playlist, PLAYLIST_COMMAND_SHUFFLE, seed,
playlist->first_index, NULL, NULL, NULL);
}
-
+
return 0;
}
/*
+ * used to sort track indices. Sort order is as follows:
+ * 1. Prepended tracks (in prepend order)
+ * 2. Playlist/directory tracks (in playlist order)
+ * 3. Inserted/Appended tracks (in insert order)
+ */
+static int sort_compare_fn(const void* p1, const void* p2)
+{
+ unsigned long* e1 = (unsigned long*) p1;
+ unsigned long* e2 = (unsigned long*) p2;
+ unsigned long flags1 = *e1 & PLAYLIST_INSERT_TYPE_MASK;
+ unsigned long flags2 = *e2 & PLAYLIST_INSERT_TYPE_MASK;
+
+ if (flags1 == flags2)
+ return (*e1 & PLAYLIST_SEEK_MASK) - (*e2 & PLAYLIST_SEEK_MASK);
+ else if (flags1 == PLAYLIST_INSERT_TYPE_PREPEND ||
+ flags2 == PLAYLIST_INSERT_TYPE_APPEND)
+ return -1;
+ else if (flags1 == PLAYLIST_INSERT_TYPE_APPEND ||
+ flags2 == PLAYLIST_INSERT_TYPE_PREPEND)
+ return 1;
+ else if (flags1 && flags2)
+ return (*e1 & PLAYLIST_SEEK_MASK) - (*e2 & PLAYLIST_SEEK_MASK);
+ else
+ return *e1 - *e2;
+}
+
+/*
* Sort the array of indices for the playlist. If start_current is true then
* set the index to the new index of the current song.
* Also while going to unshuffled mode set the first_index to 0.
*/
-static int sort_playlist(struct playlist_info* playlist, bool start_current,
- bool write)
+static int sort_playlist_unlocked(struct playlist_info* playlist,
+ bool start_current, bool write)
{
- unsigned int current = playlist->indices[playlist->index];
+ unsigned long current = playlist->indices[playlist->index];
if (playlist->amount > 0)
qsort((void*)playlist->indices, playlist->amount,
- sizeof(playlist->indices[0]), compare);
+ sizeof(playlist->indices[0]), sort_compare_fn);
#ifdef HAVE_DIRCACHE
/** We need to re-check the song names from disk because qsort can't
* sort two arrays at once :/
* FIXME: Please implement a better way to do this. */
- copy_filerefs(playlist->dcfrefs, NULL, playlist->max_playlist_size);
- queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
+ dc_init_filerefs(playlist, 0, playlist->max_playlist_size);
#endif
if (start_current)
- find_and_set_playlist_index(playlist, current);
+ find_and_set_playlist_index_unlocked(playlist, current);
/* indices have been moved so last insert position is no longer valid */
playlist->last_insert_pos = -1;
- if (!playlist->num_inserted_tracks && !playlist->deleted)
- playlist->shuffle_modified = false;
if (write && playlist->control_fd >= 0)
{
playlist->first_index = 0;
- update_control(playlist, PLAYLIST_COMMAND_UNSHUFFLE,
+ update_control_unlocked(playlist, PLAYLIST_COMMAND_UNSHUFFLE,
playlist->first_index, -1, NULL, NULL, NULL);
}
-
+
return 0;
}
@@ -1046,7 +1605,7 @@ static int calculate_step_count(const struct playlist_info *playlist, int steps)
int i, count, direction;
int index;
int stepped_count = 0;
-
+
if (steps < 0)
{
direction = -1;
@@ -1084,28 +1643,6 @@ static int calculate_step_count(const struct playlist_info *playlist, int steps)
return steps;
}
-/* Marks the index of the track to be skipped that is "steps" away from
- * current playing track.
- */
-void playlist_skip_entry(struct playlist_info *playlist, int steps)
-{
- int index;
-
- if (playlist == NULL)
- playlist = &current_playlist;
-
- /* need to account for already skipped tracks */
- steps = calculate_step_count(playlist, steps);
-
- index = playlist->index + steps;
- if (index < 0)
- index += playlist->amount;
- else if (index >= playlist->amount)
- index -= playlist->amount;
-
- playlist->indices[index] |= PLAYLIST_SKIPPED;
-}
-
/*
* returns the index of the track that is "steps" away from current playing
* track.
@@ -1123,7 +1660,9 @@ static int get_next_index(const struct playlist_info* playlist, int steps,
repeat_mode = global_settings.repeat_mode;
if (repeat_mode == REPEAT_SHUFFLE && playlist->amount <= 1)
+ {
repeat_mode = REPEAT_ALL;
+ }
steps = calculate_step_count(playlist, steps);
switch (repeat_mode)
@@ -1168,7 +1707,7 @@ static int get_next_index(const struct playlist_info* playlist, int steps,
/* second time around so skip the queued files */
for (i=0; i<playlist->amount; i++)
{
- if (playlist->indices[index] & PLAYLIST_QUEUE_MASK)
+ if (playlist->indices[index] & PLAYLIST_QUEUED)
index = (index+1) % playlist->amount;
else
{
@@ -1182,924 +1721,1354 @@ static int get_next_index(const struct playlist_info* playlist, int steps,
}
/* No luck if the whole playlist was bad. */
- if (playlist->indices[next_index] & PLAYLIST_SKIPPED)
+ if (next_index < 0 || next_index >= playlist->amount ||
+ playlist->indices[next_index] & PLAYLIST_SKIPPED)
return -1;
-
- return next_index;
-}
-
-/*
- * Search for the seek track and set appropriate indices. Used after shuffle
- * to make sure the current index is still pointing to correct track.
- */
-static void find_and_set_playlist_index(struct playlist_info* playlist,
- unsigned int seek)
-{
- int i;
-
- /* Set the index to the current song */
- for (i=0; i<playlist->amount; i++)
- {
- if (playlist->indices[i] == seek)
- {
- playlist->index = playlist->first_index = i;
-
- break;
- }
- }
-}
-
-/*
- * used to sort track indices. Sort order is as follows:
- * 1. Prepended tracks (in prepend order)
- * 2. Playlist/directory tracks (in playlist order)
- * 3. Inserted/Appended tracks (in insert order)
- */
-static int compare(const void* p1, const void* p2)
-{
- unsigned long* e1 = (unsigned long*) p1;
- unsigned long* e2 = (unsigned long*) p2;
- unsigned long flags1 = *e1 & PLAYLIST_INSERT_TYPE_MASK;
- unsigned long flags2 = *e2 & PLAYLIST_INSERT_TYPE_MASK;
- if (flags1 == flags2)
- return (*e1 & PLAYLIST_SEEK_MASK) - (*e2 & PLAYLIST_SEEK_MASK);
- else if (flags1 == PLAYLIST_INSERT_TYPE_PREPEND ||
- flags2 == PLAYLIST_INSERT_TYPE_APPEND)
- return -1;
- else if (flags1 == PLAYLIST_INSERT_TYPE_APPEND ||
- flags2 == PLAYLIST_INSERT_TYPE_PREPEND)
- return 1;
- else if (flags1 && flags2)
- return (*e1 & PLAYLIST_SEEK_MASK) - (*e2 & PLAYLIST_SEEK_MASK);
- else
- return *e1 - *e2;
+ return next_index;
}
#ifdef HAVE_DIRCACHE
/**
* Thread to update filename pointers to dircache on background
- * without affecting playlist load up performance. This thread also flushes
- * any pending control commands when the disk spins up.
+ * without affecting playlist load up performance.
*/
-static void playlist_flush_callback(void)
-{
- struct playlist_info *playlist;
- playlist = &current_playlist;
- if (playlist->control_fd >= 0)
- {
- if (playlist->num_cached > 0)
- {
- mutex_lock(playlist->control_mutex);
- flush_cached_control(playlist);
- mutex_unlock(playlist->control_mutex);
- }
- sync_control(playlist, true);
- }
-}
-
-static void playlist_thread(void)
+static void dc_thread_playlist(void)
{
struct queue_event ev;
- bool dirty_pointers = false;
static char tmp[MAX_PATH+1];
- struct playlist_info *playlist;
+ struct playlist_info *playlist = &current_playlist;
+ struct dircache_fileref *dcfrefs;
int index;
- int seek;
- bool control_file;
- int sleep_time = 5;
-
-#ifdef HAVE_DISK_STORAGE
- if (global_settings.disk_spindown > 1 &&
- global_settings.disk_spindown <= 5)
- sleep_time = global_settings.disk_spindown - 1;
-#endif
+ /* Thread starts out stopped */
+ long sleep_time = TIMEOUT_BLOCK;
+ int stop_count = 1;
+ bool is_dirty = false;
while (1)
{
- queue_wait_w_tmo(&playlist_queue, &ev, HZ*sleep_time);
+ queue_wait_w_tmo(&playlist_queue, &ev, sleep_time);
switch (ev.id)
{
- case PLAYLIST_LOAD_POINTERS:
- dirty_pointers = true;
- break ;
+ case PLAYLIST_DC_SCAN_START:
+ if (ev.data)
+ is_dirty = true;
+
+ stop_count--;
+ if (is_dirty && stop_count == 0)
+ {
+ /* Start the background scanning after either the disk
+ * spindown timeout or 5s, whichever is less */
+ sleep_time = 5 * HZ;
+#ifdef HAVE_DISK_STORAGE
+ if (global_settings.disk_spindown > 1 &&
+ global_settings.disk_spindown <= 5)
+ sleep_time = (global_settings.disk_spindown - 1) * HZ;
+#endif
+ }
+ break;
+
+ case PLAYLIST_DC_SCAN_STOP:
+ stop_count++;
+ sleep_time = TIMEOUT_BLOCK;
+ queue_reply(&playlist_queue, 0);
+ break;
- /* Start the background scanning after either the disk spindown
- timeout or 5s, whichever is less */
case SYS_TIMEOUT:
{
- playlist = &current_playlist;
- if (playlist->control_fd >= 0)
+ /* Nothing to do if there are no dcfrefs or tracks */
+ if (!playlist->dcfrefs_handle || playlist->amount <= 0)
{
- if (playlist->num_cached > 0)
- register_storage_idle_func(playlist_flush_callback);
+ is_dirty = false;
+ sleep_time = TIMEOUT_BLOCK;
+ logf("%s: nothing to scan", __func__);
+ break;
}
- if (!playlist->dcfrefs || playlist->amount <= 0)
- break ;
-
- /* Check if previously loaded pointers are intact. */
- if (!dirty_pointers)
- break ;
-
+ /* Retry at a later time if the dircache is not ready */
struct dircache_info info;
dircache_get_info(&info);
-
if (info.status != DIRCACHE_READY)
- break ;
+ {
+ logf("%s: dircache not ready", __func__);
+ break;
+ }
+
+ logf("%s: scan start", __func__);
+#ifdef LOGF_ENABLE
+ long scan_start_tick = current_tick;
+#endif
trigger_cpu_boost();
+ dcfrefs = core_get_data_pinned(playlist->dcfrefs_handle);
- for (index = 0; index < playlist->amount
- && queue_empty(&playlist_queue); index++)
+ for (index = 0; index < playlist->amount; index++)
{
/* Process only pointers that are superficially stale. */
- if (dircache_search(DCS_FILEREF, &playlist->dcfrefs[index], NULL) > 0)
- continue ;
-
- control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
- seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
+ if (dircache_search(DCS_FILEREF, &dcfrefs[index], NULL) > 0)
+ continue;
- /* Load the filename from playlist file. */
- if (get_filename(playlist, index, seek, control_file, tmp,
- sizeof(tmp)) < 0)
+ /* Bail out if a command needs servicing. */
+ if (!queue_empty(&playlist_queue))
{
- break ;
+ logf("%s: scan interrupted", __func__);
+ break;
}
+ /* Load the filename from playlist file. */
+ if (get_track_filename(playlist, index, tmp, sizeof(tmp)) != 0)
+ break;
+
/* Obtain the dircache file entry cookie. */
dircache_search(DCS_CACHED_PATH | DCS_UPDATE_FILEREF,
- &playlist->dcfrefs[index], tmp);
+ &dcfrefs[index], tmp);
/* And be on background so user doesn't notice any delays. */
yield();
}
+ /* If we indexed the whole playlist without being interrupted
+ * then there are no dirty references; go to sleep. */
+ if (index == playlist->amount)
+ {
+ is_dirty = false;
+ sleep_time = TIMEOUT_BLOCK;
+ logf("%s: scan complete", __func__);
+ }
+
+ core_put_data_pinned(dcfrefs);
cancel_cpu_boost();
- if (index == playlist->amount)
- dirty_pointers = false;
-
- break ;
+ logf("%s: %ld ticks", __func__, current_tick - scan_start_tick);
+ break;
}
-
+
case SYS_USB_CONNECTED:
usb_acknowledge(SYS_USB_CONNECTED_ACK);
usb_wait_for_disconnect(&playlist_queue);
- break ;
+ break;
}
}
}
#endif
/*
- * gets pathname for track at seek index
+ * Allocate a temporary buffer for loading playlists
*/
-static int get_filename(struct playlist_info* playlist, int index, int seek,
- bool control_file, char *buf, int buf_length)
+static int alloc_tempbuf(size_t* buflen)
{
- int fd;
- int max = -1;
- char tmp_buf[MAX_PATH+1];
- char dir_buf[MAX_PATH+1];
- bool utf8 = playlist->utf8;
+ /* request a reasonable size first */
+ int handle = core_alloc_ex(PLAYLIST_LOAD_BUFLEN, &buflib_ops_locked);
+ if (handle > 0)
+ {
+ *buflen = PLAYLIST_LOAD_BUFLEN;
+ return handle;
+ }
+
+ /* otherwise, try being unreasonable */
+ return core_alloc_maximum(buflen, &buflib_ops_locked);
+}
+
+/*
+ * Need no movement protection since all 3 allocations are not passed to
+ * other functions which can yield().
+ */
+static int move_callback(int handle, void* current, void* new)
+{
+ (void)handle;
+ struct playlist_info* playlist = &current_playlist;
+ if (current == playlist->indices)
+ playlist->indices = new;
+ return BUFLIB_CB_OK;
+}
+
+
+static struct buflib_callbacks ops = {
+ .move_callback = move_callback,
+ .shrink_callback = NULL,
+};
+
+/******************************************************************************/
+/******************************************************************************/
+/* ************************************************************************** */
+/* * PUBLIC INTERFACE FUNCTIONS * *********************************************/
+/* ************************************************************************** */
+/******************************************************************************/
+/******************************************************************************/
+/*
+ * Initialize playlist entries at startup
+ */
+void playlist_init(void)
+{
+ int handle;
+ struct playlist_info* playlist = &current_playlist;
+ mutex_init(&playlist->mutex);
+
+ strmemccpy(playlist->control_filename, PLAYLIST_CONTROL_FILE,
+ sizeof(playlist->control_filename));
+ playlist->fd = -1;
+ playlist->control_fd = -1;
+ playlist->max_playlist_size = global_settings.max_files_in_playlist;
+
+ handle = core_alloc_ex(playlist->max_playlist_size * sizeof(*playlist->indices), &ops);
+ playlist->indices = core_get_data(handle);
- if (buf_length > MAX_PATH+1)
- buf_length = MAX_PATH+1;
+ empty_playlist_unlocked(playlist, true);
#ifdef HAVE_DIRCACHE
- if (playlist->dcfrefs)
- {
- max = dircache_get_fileref_path(&playlist->dcfrefs[index],
- tmp_buf, sizeof(tmp_buf));
- }
+ playlist->dcfrefs_handle = core_alloc(
+ playlist->max_playlist_size * sizeof(struct dircache_fileref));
+ dc_init_filerefs(playlist, 0, playlist->max_playlist_size);
+
+ unsigned int playlist_thread_id =
+ create_thread(dc_thread_playlist, playlist_stack, sizeof(playlist_stack),
+ 0, dc_thread_playlist_name IF_PRIO(, PRIORITY_BACKGROUND)
+ IF_COP(, CPU));
+
+ queue_init(&playlist_queue, true);
+ queue_enable_queue_send(&playlist_queue,
+ &playlist_queue_sender_list, playlist_thread_id);
+
+ dc_thread_start(&current_playlist, false);
#endif /* HAVE_DIRCACHE */
-
- if (playlist->in_ram && !control_file && max < 0)
+}
+
+/*
+ * Clean playlist at shutdown
+ */
+void playlist_shutdown(void)
+{
+ struct playlist_info* playlist = &current_playlist;
+ playlist_write_lock(playlist);
+
+ if (playlist->control_fd >= 0)
+ pl_close_control(playlist);
+
+ playlist_write_unlock(playlist);
+}
+
+/* returns number of tracks in playlist (includes queued/inserted tracks) */
+int playlist_amount_ex(const struct playlist_info* playlist)
+{
+ if (!playlist)
+ playlist = &current_playlist;
+
+ return playlist->amount;
+}
+
+/* returns number of tracks in current playlist */
+int playlist_amount(void)
+{
+ return playlist_amount_ex(NULL);
+}
+
+/*
+ * Create a new playlist If playlist is not NULL then we're loading a
+ * playlist off disk for viewing/editing. The index_buffer is used to store
+ * playlist indices (required for and only used if !current playlist). The
+ * temp_buffer (if not NULL) is used as a scratchpad when loading indices.
+ *
+ * XXX: This is really only usable by the playlist viewer. Never pass
+ * playlist == NULL, that cannot and will not work.
+ */
+int playlist_create_ex(struct playlist_info* playlist,
+ const char* dir, const char* file,
+ void* index_buffer, int index_buffer_size,
+ void* temp_buffer, int temp_buffer_size)
+{
+ /* Initialize playlist structure */
+ int r = rand() % 10;
+
+ /* Use random name for control file */
+ snprintf(playlist->control_filename, sizeof(playlist->control_filename),
+ "%s.%d", PLAYLIST_CONTROL_FILE, r);
+ playlist->fd = -1;
+ playlist->control_fd = -1;
+
+ if (index_buffer)
{
- max = strlcpy(tmp_buf, (char*)&playlist->buffer[seek], sizeof(tmp_buf));
+ int num_indices = index_buffer_size /
+ playlist_get_required_bufsz(playlist, false, 1);
+
+ if (num_indices > global_settings.max_files_in_playlist)
+ num_indices = global_settings.max_files_in_playlist;
+
+ playlist->max_playlist_size = num_indices;
+ playlist->indices = index_buffer;
+#ifdef HAVE_DIRCACHE
+ playlist->dcfrefs_handle = 0;
+#endif
}
- else if (max < 0)
+ else
{
- mutex_lock(playlist->control_mutex);
+ /* FIXME not sure if it's safe to share index buffers */
+ playlist->max_playlist_size = current_playlist.max_playlist_size;
+ playlist->indices = current_playlist.indices;
+#ifdef HAVE_DIRCACHE
+ playlist->dcfrefs_handle = 0;
+#endif
+ }
- if (control_file)
+ new_playlist_unlocked(playlist, dir, file);
+
+ /* load the playlist file */
+ if (file)
+ add_indices_to_playlist(playlist, temp_buffer, temp_buffer_size);
+
+ return 0;
+}
+
+/*
+ * Create new playlist
+ */
+int playlist_create(const char *dir, const char *file)
+{
+ struct playlist_info* playlist = &current_playlist;
+ int status = 0;
+
+ dc_thread_stop(playlist);
+ playlist_write_lock(playlist);
+
+ new_playlist_unlocked(playlist, dir, file);
+
+ if (file)
+ {
+ size_t buflen;
+ int handle = alloc_tempbuf(&buflen);
+ if (handle > 0)
{
- fd = playlist->control_fd;
- utf8 = true;
+ /* align for faster load times */
+ void* buf = core_get_data(handle);
+ STORAGE_ALIGN_BUFFER(buf, buflen);
+ buflen = ALIGN_DOWN(buflen, 512); /* to avoid partial sector I/O */
+ /* load the playlist file */
+ add_indices_to_playlist(playlist, buf, buflen);
+ core_free(handle);
}
else
{
- if(-1 == playlist->fd)
- playlist->fd = open(playlist->filename, O_RDONLY);
-
- fd = playlist->fd;
+ /* should not happen -- happens if plugin takes audio buffer */
+ splashf(HZ * 2, "%s(): OOM", __func__);
+ status = -1;
}
-
- if(-1 != fd)
- {
-
- if (lseek(fd, seek, SEEK_SET) != seek)
- max = -1;
- else
- {
- max = read(fd, tmp_buf, MIN((size_t) buf_length, sizeof(tmp_buf)));
-
- if (max > 0)
- {
- /* playlist file may end without a new line - terminate buffer */
- tmp_buf[MIN(max, (int)sizeof(tmp_buf) - 1)] = '\0';
+ }
- /* Use dir_buf as a temporary buffer. Note that dir_buf must
- * be as large as tmp_buf.
- */
- if (!utf8)
- max = convert_m3u(tmp_buf, max, sizeof(tmp_buf), dir_buf);
- }
- }
- }
+ playlist_write_unlock(playlist);
+ dc_thread_start(playlist, true);
- mutex_unlock(playlist->control_mutex);
+ return status;
+}
- if (max < 0)
- {
- if (usb_detect() == USB_INSERTED)
- ; /* ignore error on usb plug */
- else if (control_file)
- splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
- else
- splash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR));
+/* Returns false if 'steps' is out of bounds, else true */
+bool playlist_check(int steps)
+{
+ struct playlist_info* playlist = &current_playlist;
- return max;
- }
- }
+ /* always allow folder navigation */
+ if (global_settings.next_folder && playlist_allow_dirplay(playlist))
+ return true;
- strlcpy(dir_buf, playlist->filename, playlist->dirlen);
+ int index = get_next_index(playlist, steps, -1);
- return format_track_path(buf, tmp_buf, buf_length, dir_buf);
+ if (index < 0 && steps >= 0 && global_settings.repeat_mode == REPEAT_SHUFFLE)
+ index = get_next_index(playlist, steps, REPEAT_ALL);
- (void)index;
+ return (index >= 0);
}
-static int get_next_directory(char *dir){
- return get_next_dir(dir, true);
-}
+/*
+ * Close files and delete control file for non-current playlist.
+ */
+void playlist_close(struct playlist_info* playlist)
+{
+ if (!playlist)
+ return;
+
+ playlist_write_lock(playlist);
+
+ pl_close_playlist(playlist);
+ pl_close_control(playlist);
+
+ if (playlist->control_created)
+ {
+ remove(playlist->control_filename);
+ playlist->control_created = false;
+ }
-static int get_previous_directory(char *dir){
- return get_next_dir(dir, false);
+ playlist_write_unlock(playlist);
}
/*
- * search through all the directories (starting with the current) to find
- * one that has tracks to play
+ * Delete track at specified index.
*/
-static int get_next_dir(char *dir, bool is_forward)
+int playlist_delete(struct playlist_info* playlist, int index)
{
- struct playlist_info* playlist = &current_playlist;
- int result = -1;
- char *start_dir = NULL;
- bool exit = false;
- struct tree_context* tc = tree_get_context();
- int saved_dirfilter = *(tc->dirfilter);
- unsigned int base_len;
+ int result = 0;
- if (global_settings.constrain_next_folder)
- {
- /* constrain results to directories below user's start directory */
- strcpy(dir, global_settings.start_directory);
- base_len = strlen(dir);
+ if (!playlist)
+ playlist = &current_playlist;
- /* strip any trailing slash from base directory */
- if (base_len > 0 && dir[base_len - 1] == '/')
- {
- base_len--;
- dir[base_len] = '\0';
- }
- }
- else
- {
- /* start from root directory */
- dir[0] = '\0';
- base_len = 0;
- }
+ dc_thread_stop(playlist);
+ playlist_write_lock(playlist);
- /* process random folder advance */
- if (global_settings.next_folder == FOLDER_ADVANCE_RANDOM)
+ if (check_control(playlist) < 0)
{
- int fd = open(ROCKBOX_DIR "/folder_advance_list.dat", O_RDONLY);
- if (fd >= 0)
- {
- int folder_count = 0;
- read(fd,&folder_count,sizeof(int));
- if (folder_count)
- {
- char buffer[MAX_PATH];
- /* give up looking for a directory after we've had four
- times as many tries as there are directories. */
- unsigned long allowed_tries = folder_count * 4;
- int i;
- srand(current_tick);
- *(tc->dirfilter) = SHOW_MUSIC;
- tc->sort_dir = global_settings.sort_dir;
- while (!exit && allowed_tries--)
- {
- i = rand() % folder_count;
- lseek(fd, sizeof(int) + (MAX_PATH * i), SEEK_SET);
- read(fd, buffer, MAX_PATH);
- /* is the current dir within our base dir and has music? */
- if ((base_len == 0 || !strncmp(buffer, dir, base_len))
- && check_subdir_for_music(buffer, "", false) == 0)
- exit = true;
- }
- close(fd);
- *(tc->dirfilter) = saved_dirfilter;
- tc->sort_dir = global_settings.sort_dir;
- reload_directory();
- if (exit)
- {
- strcpy(dir,buffer);
- return 0;
- }
- }
- else
- close(fd);
- }
+ notify_control_access_error();
+ result = -1;
+ goto out;
}
- /* if the current file is within our base dir, use its dir instead */
- if (base_len == 0 || !strncmp(playlist->filename, dir, base_len))
- strlcpy(dir, playlist->filename, playlist->dirlen);
+ result = remove_track_unlocked(playlist, index, true);
+
+out:
+ playlist_write_unlock(playlist);
+ dc_thread_start(playlist, false);
+
+ if (result != -1 && (audio_status() & AUDIO_STATUS_PLAY) &&
+ playlist->started)
+ audio_flush_and_reload_tracks();
+
+ return result;
+}
+
+/*
+ * Search specified directory for tracks and notify via callback. May be
+ * called recursively.
+ */
+int playlist_directory_tracksearch(const char* dirname, bool recurse,
+ int (*callback)(char*, void*),
+ void* context)
+{
+ char buf[MAX_PATH+1];
+ int result = 0;
+ int num_files = 0;
+ int i;;
+ struct tree_context* tc = tree_get_context();
+ struct tree_cache* cache = &tc->cache;
+ int old_dirfilter = *(tc->dirfilter);
+
+ if (!callback)
+ return -1;
/* use the tree browser dircache to load files */
*(tc->dirfilter) = SHOW_ALL;
- /* set up sorting/direction */
- tc->sort_dir = global_settings.sort_dir;
- if (!is_forward)
+ if (ft_load(tc, dirname) < 0)
{
- static const char sortpairs[] =
- {
- [SORT_ALPHA] = SORT_ALPHA_REVERSED,
- [SORT_DATE] = SORT_DATE_REVERSED,
- [SORT_TYPE] = SORT_TYPE_REVERSED,
- [SORT_ALPHA_REVERSED] = SORT_ALPHA,
- [SORT_DATE_REVERSED] = SORT_DATE,
- [SORT_TYPE_REVERSED] = SORT_TYPE,
- };
-
- if ((unsigned)tc->sort_dir < sizeof(sortpairs))
- tc->sort_dir = sortpairs[tc->sort_dir];
+ splash(HZ*2, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
+ *(tc->dirfilter) = old_dirfilter;
+ return -1;
}
- while (!exit)
- {
- struct entry *files;
- int num_files = 0;
- int i;
+ num_files = tc->filesindir;
- if (ft_load(tc, (dir[0]=='\0')?"/":dir) < 0)
+ /* we've overwritten the dircache so tree browser will need to be
+ reloaded */
+ reload_directory();
+
+ for (i=0; i<num_files; i++)
+ {
+ /* user abort */
+ if (action_userabort(TIMEOUT_NOBLOCK))
{
- exit = true;
result = -1;
break;
}
-
- tree_lock_cache(tc);
- files = tree_get_entries(tc);
- num_files = tc->filesindir;
- for (i=0; i<num_files; i++)
+ struct entry *files = core_get_data(cache->entries_handle);
+ if (files[i].attr & ATTR_DIRECTORY)
{
- /* user abort */
- if (action_userabort(TIMEOUT_NOBLOCK))
+ if (recurse)
{
- result = -1;
- exit = true;
- break;
- }
+ /* recursively add directories */
+ if (path_append(buf, dirname, files[i].name, sizeof(buf))
+ >= sizeof(buf))
+ {
+ continue;
+ }
- if (files[i].attr & ATTR_DIRECTORY)
- {
- if (!start_dir)
+ result = playlist_directory_tracksearch(buf, recurse,
+ callback, context);
+ if (result < 0)
+ break;
+
+ /* we now need to reload our current directory */
+ if(ft_load(tc, dirname) < 0)
{
- result = check_subdir_for_music(dir, files[i].name, true);
- if (result != -1)
- {
- exit = true;
- break;
- }
+ result = -1;
+ break;
+ }
+
+ num_files = tc->filesindir;
+ if (!num_files)
+ {
+ result = -1;
+ break;
}
- else if (!strcmp(start_dir, files[i].name))
- start_dir = NULL;
}
+ else
+ continue;
}
- tree_unlock_cache(tc);
-
- if (!exit)
+ else if ((files[i].attr & FILE_ATTR_MASK) == FILE_ATTR_AUDIO)
{
- /* we've already descended to the base dir with nothing found,
- check whether that contains music */
- if (strlen(dir) <= base_len)
+ if (path_append(buf, dirname, files[i].name, sizeof(buf))
+ >= sizeof(buf))
{
- result = check_subdir_for_music(dir, "", true);
- if (result == -1)
- /* there's no music files in the base directory,
- treat as a fatal error */
- result = -2;
- break;
+ continue;
}
- else
+
+ if (callback(buf, context) != 0)
{
- /* move down to parent directory. current directory name is
- stored as the starting point for the search in parent */
- start_dir = strrchr(dir, '/');
- if (start_dir)
- {
- *start_dir = '\0';
- start_dir++;
- }
- else
- break;
+ result = -1;
+ break;
}
+
+ /* let the other threads work */
+ yield();
}
}
/* restore dirfilter */
- *(tc->dirfilter) = saved_dirfilter;
- tc->sort_dir = global_settings.sort_dir;
+ *(tc->dirfilter) = old_dirfilter;
return result;
}
-/*
- * Checks if there are any music files in the dir or any of its
- * subdirectories. May be called recursively.
- */
-static int check_subdir_for_music(char *dir, const char *subdir, bool recurse)
+
+struct playlist_info *playlist_get_current(void)
{
- int result = -1;
- size_t dirlen = strlen(dir);
- int num_files = 0;
- int i;
- struct entry *files;
- bool has_music = false;
- bool has_subdir = false;
- struct tree_context* tc = tree_get_context();
+ return &current_playlist;
+}
- if (path_append(dir + dirlen, PA_SEP_HARD, subdir, MAX_PATH - dirlen) >=
- MAX_PATH - dirlen)
+/* Returns index of current playing track for display purposes. This value
+ should not be used for resume purposes as it doesn't represent the actual
+ index into the playlist */
+int playlist_get_display_index(void)
+{
+ struct playlist_info* playlist = &current_playlist;
+
+ /* first_index should always be index 0 for display purposes */
+ int index = rotate_index(playlist, playlist->index);
+
+ return (index+1);
+}
+
+/* returns the crc32 of the filename of the track at the specified index */
+unsigned int playlist_get_filename_crc32(struct playlist_info *playlist,
+ int index)
+{
+ const char *basename;
+ char filename[MAX_PATH]; /* path name of mp3 file */
+ if (!playlist)
+ playlist = &current_playlist;
+
+ if (get_track_filename(playlist, index, filename, sizeof(filename)) != 0)
+ return -1;
+
+#ifdef HAVE_MULTIVOLUME
+ /* remove the volume identifier it might change just use the relative part*/
+ path_strip_volume(filename, &basename, false);
+ if (basename == NULL)
+#endif
+ basename = filename;
+ NOTEF("%s: %s", __func__, basename);
+ return crc_32(basename, strlen(basename), -1);
+}
+
+/* returns index of first track in playlist */
+int playlist_get_first_index(const struct playlist_info* playlist)
+{
+ if (!playlist)
+ playlist = &current_playlist;
+
+ return playlist->first_index;
+}
+
+/* returns the playlist filename */
+char *playlist_get_name(const struct playlist_info* playlist, char *buf,
+ int buf_size)
+{
+ if (!playlist)
+ playlist = &current_playlist;
+
+ strmemccpy(buf, playlist->filename, buf_size);
+
+ if (!buf[0])
+ return NULL;
+
+ return buf;
+}
+
+/* return size of buffer needed for playlist to initialize num_indices entries */
+size_t playlist_get_required_bufsz(struct playlist_info* playlist,
+ bool include_namebuf,
+ int num_indices)
+{
+ size_t namebuf = 0;
+
+ if (!playlist)
+ playlist = &current_playlist;
+
+ size_t unit_size = sizeof (*playlist->indices);
+ if (include_namebuf)
+ namebuf = AVERAGE_FILENAME_LENGTH * global_settings.max_files_in_dir;
+
+ return (num_indices * unit_size) + namebuf;
+}
+
+/* Get resume info for current playing song. If return value is -1 then
+ settings shouldn't be saved. */
+int playlist_get_resume_info(int *resume_index)
+{
+ struct playlist_info* playlist = &current_playlist;
+
+ return (*resume_index = playlist->index);
+}
+
+/* returns shuffle seed of playlist */
+int playlist_get_seed(const struct playlist_info* playlist)
+{
+ if (!playlist)
+ playlist = &current_playlist;
+
+ return playlist->seed;
+}
+
+/* Fills info structure with information about track at specified index.
+ Returns 0 on success and -1 on failure */
+int playlist_get_track_info(struct playlist_info* playlist, int index,
+ struct playlist_track_info* info)
+{
+ if (!playlist)
+ playlist = &current_playlist;
+
+ if (get_track_filename(playlist, index,
+ info->filename, sizeof(info->filename)) != 0)
+ return -1;
+
+ info->attr = 0;
+
+ if (playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK)
{
- return 0;
+ if (playlist->indices[index] & PLAYLIST_QUEUED)
+ info->attr |= PLAYLIST_ATTR_QUEUED;
+ else
+ info->attr |= PLAYLIST_ATTR_INSERTED;
}
-
- if (ft_load(tc, dir) < 0)
+
+ if (playlist->indices[index] & PLAYLIST_SKIPPED)
+ info->attr |= PLAYLIST_ATTR_SKIPPED;
+
+ info->index = index;
+ info->display_index = rotate_index(playlist, index) + 1;
+
+ return 0;
+}
+
+/*
+ * initialize an insert context to add tracks to a playlist
+ * don't forget to release it when finished adding files
+ */
+int playlist_insert_context_create(struct playlist_info* playlist,
+ struct playlist_insert_context *context,
+ int position, bool queue, bool progress)
+{
+
+ if (!playlist)
+ playlist = &current_playlist;
+
+ context->playlist = playlist;
+ context->initialized = false;
+
+ dc_thread_stop(playlist);
+ playlist_write_lock(playlist);
+
+ if (check_control(playlist) < 0)
{
- return -2;
+ notify_control_access_error();
+ return -1;
}
-
- tree_lock_cache(tc);
- files = tree_get_entries(tc);
- num_files = tc->filesindir;
- for (i=0; i<num_files; i++)
+ if (position == PLAYLIST_REPLACE)
{
- if (files[i].attr & ATTR_DIRECTORY)
- has_subdir = true;
- else if ((files[i].attr & FILE_ATTR_MASK) == FILE_ATTR_AUDIO)
+ if (remove_all_tracks_unlocked(playlist, true) == 0)
+ position = PLAYLIST_INSERT_LAST;
+ else
{
- has_music = true;
- break;
+ return -1;
}
}
- if (has_music)
+ context->playlist = playlist;
+ context->position = position;
+ context->queue = queue;
+ context->count = 0;
+ context->progress = progress;
+ context->initialized = true;
+
+ if (queue)
+ context->count_langid = LANG_PLAYLIST_QUEUE_COUNT;
+ else
+ context->count_langid = LANG_PLAYLIST_INSERT_COUNT;
+
+ return 0;
+}
+
+/*
+ * add tracks to playlist using opened insert context
+ */
+int playlist_insert_context_add(struct playlist_insert_context *context,
+ const char *filename)
+{
+ struct playlist_insert_context* c = context;
+ int insert_pos;
+
+ insert_pos = add_track_to_playlist_unlocked(c->playlist, filename,
+ c->position, c->queue, -1);
+
+ if (insert_pos < 0)
+ return -1;
+
+ (c->count)++;
+
+ /* After first INSERT_FIRST switch to INSERT so that all the
+ rest of the tracks get inserted one after the other */
+ if (c->position == PLAYLIST_INSERT_FIRST)
+ c->position = PLAYLIST_INSERT;
+
+ if (((c->count)%PLAYLIST_DISPLAY_COUNT) == 0)
{
- tree_unlock_cache(tc);
- return 0;
+ if (c->progress)
+ display_playlist_count(c->count, ID2P(c->count_langid), false);
+
+ if ((c->count) == PLAYLIST_DISPLAY_COUNT &&
+ (audio_status() & AUDIO_STATUS_PLAY) &&
+ c->playlist->started)
+ audio_flush_and_reload_tracks();
}
- if (has_subdir && recurse)
+ return 0;
+}
+
+/*
+ * release opened insert context, sync playlist
+ */
+void playlist_insert_context_release(struct playlist_insert_context *context)
+{
+
+ struct playlist_info* playlist = context->playlist;
+ if (context->initialized)
+ sync_control_unlocked(playlist);
+ if (context->progress)
+ display_playlist_count(context->count, ID2P(context->count_langid), true);
+
+ playlist_write_unlock(playlist);
+ dc_thread_start(playlist, true);
+
+ if ((audio_status() & AUDIO_STATUS_PLAY) && playlist->started)
+ audio_flush_and_reload_tracks();
+}
+
+/*
+ * Insert all tracks from specified directory into playlist.
+ */
+int playlist_insert_directory(struct playlist_info* playlist,
+ const char *dirname, int position, bool queue,
+ bool recurse)
+{
+ int result = -1;
+ struct playlist_insert_context context;
+ result = playlist_insert_context_create(playlist, &context,
+ position, queue, true);
+ if (result >= 0)
{
- for (i=0; i<num_files; i++)
+ cpu_boost(true);
+
+ result = playlist_directory_tracksearch(dirname, recurse,
+ directory_search_callback, &context);
+
+ cpu_boost(false);
+ }
+ playlist_insert_context_release(&context);
+ return result;
+}
+
+/*
+ * Insert all tracks from specified playlist into dynamic playlist.
+ */
+int playlist_insert_playlist(struct playlist_info* playlist, const char *filename,
+ int position, bool queue)
+{
+ int fd = -1;
+ int max;
+ char *dir;
+
+ char temp_buf[MAX_PATH+1];
+ char trackname[MAX_PATH+1];
+
+ int result = -1;
+ bool utf8 = is_m3u8_name(filename);
+
+ struct playlist_insert_context pl_context;
+ cpu_boost(true);
+
+ if (playlist_insert_context_create(playlist, &pl_context, position, queue, true) >= 0)
+ {
+ fd = open_utf8(filename, O_RDONLY);
+ if (fd < 0)
{
+ notify_access_error();
+ goto out;
+ }
+
+ /* we need the directory name for formatting purposes */
+ size_t dirlen = path_dirname(filename, (const char **)&dir);
+ //dir = strmemdupa(dir, dirlen);
+
+ while ((max = read_line(fd, temp_buf, sizeof(temp_buf))) > 0)
+ {
+ /* user abort */
if (action_userabort(TIMEOUT_NOBLOCK))
- {
- result = -2;
break;
- }
- if (files[i].attr & ATTR_DIRECTORY)
+ if (temp_buf[0] != '#' && temp_buf[0] != '\0')
{
- result = check_subdir_for_music(dir, files[i].name, true);
- if (!result)
- break;
+ if (!utf8)
+ {
+ /* Use trackname as a temporay buffer. Note that trackname must
+ * be as large as temp_buf.
+ */
+ max = convert_m3u_name(temp_buf, max, sizeof(temp_buf), trackname);
+ }
+
+ /* we need to format so that relative paths are correctly
+ handled */
+ if (format_track_path(trackname, temp_buf,
+ sizeof(trackname), dir, dirlen) < 0)
+ {
+ goto out;
+ }
+
+ if (playlist_insert_context_add(&pl_context, trackname) < 0)
+ goto out;
}
+
+ /* let the other threads work */
+ yield();
}
}
- tree_unlock_cache(tc);
- if (result < 0)
- {
- if (dirlen)
- {
- dir[dirlen] = '\0';
- }
- else
- {
- strcpy(dir, PATH_ROOTSTR);
- }
+ result = 0;
- /* we now need to reload our current directory */
- if(ft_load(tc, dir) < 0)
- splash(HZ*2, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
- }
+out:
+ close(fd);
+ cpu_boost(false);
+ playlist_insert_context_release(&pl_context);
return result;
}
/*
- * Returns absolute path of track
+ * Insert track into playlist at specified position (or one of the special
+ * positions). Returns position where track was inserted or -1 if error.
*/
-static ssize_t format_track_path(char *dest, char *src, int buf_length,
- const char *dir)
+int playlist_insert_track(struct playlist_info* playlist, const char *filename,
+ int position, bool queue, bool sync)
{
- size_t len = 0;
+ int result;
- /* Look for the end of the string */
- while (1)
- {
- int c = src[len];
- if (c == '\n' || c == '\r' || c == '\0')
- break;
- len++;
- }
+ if (!playlist)
+ playlist = &current_playlist;
- /* Now work back killing white space */
- while (len > 0)
+ dc_thread_stop(playlist);
+ playlist_write_lock(playlist);
+
+ if (check_control(playlist) < 0)
{
- int c = src[len - 1];
- if (c != '\t' && c != ' ')
- break;
- len--;
+ notify_control_access_error();
+ return -1;
}
- src[len] = '\0';
+ result = add_track_to_playlist_unlocked(playlist, filename,
+ position, queue, -1);
- /* Replace backslashes with forward slashes */
- path_correct_separators(src, src);
+ /* Check if we want manually sync later. For example when adding
+ * bunch of files from tagcache, syncing after every file wouldn't be
+ * a good thing to do. */
+ if (sync && result >= 0)
+ playlist_sync(playlist);
- /* Drive letters have no meaning here; handle DOS style drive letter
- * and parse greedily so that:
- *
- * 1) "c:/foo" is fully qualified, use directory volume only
- * 2) "c:foo" is relative to current directory on C, use directory path
- *
- * Assume any volume on the beginning of the directory path is actually
- * the volume on which it resides. This may not be the case if the dir
- * param contains a path such as "/<1>/foo/../../<0>/bar", which refers
- * to "/<0>/bar" (aka "/bar" at this time). *fingers crossed*
- *
- * If any stripped drive spec was absolute, prepend the playlist
- * directory's volume spec, or root if none. Relative paths remain
- * relative and the playlist's directory fully qualifies them. Absolute
- * UNIX-style paths remain unaltered.
- */
- if (path_strip_drive(src, (const char **)&src, true) >= 0 &&
- src[-1] == PATH_SEPCH)
- {
- #ifdef HAVE_MULTIVOLUME
- const char *p;
- path_strip_volume(dir, &p, false);
- dir = strmemdupa(dir, p - dir); /* empty if no volspec on dir */
- #else
- dir = ""; /* only volume is root */
- #endif
- }
+ playlist_write_unlock(playlist);
+ dc_thread_start(playlist, true);
- len = path_append(dest, *dir ? dir : PATH_ROOTSTR, src, buf_length);
- if (len >= (size_t)buf_length)
- return -1; /* buffer too small */
+ return result;
+}
- return len;
+/* returns true if playlist has been modified by the user */
+bool playlist_modified(const struct playlist_info* playlist)
+{
+ if (!playlist)
+ playlist = &current_playlist;
+
+ return !!(playlist->flags & PLAYLIST_FLAG_MODIFIED);
}
/*
- * Display splash message showing progress of playlist/directory insertion or
- * save.
+ * Set the playlist modified status. Should be called to set the flag after
+ * an explicit user action that modifies the playlist. You should not clear
+ * the modified flag without properly warning the user.
*/
-static void display_playlist_count(int count, const unsigned char *fmt,
- bool final)
+void playlist_set_modified(struct playlist_info* playlist, bool modified)
{
- static long talked_tick = 0;
- long id = P2ID(fmt);
- if(global_settings.talk_menu && id>=0)
- {
- if(final || (count && (talked_tick == 0
- || TIME_AFTER(current_tick, talked_tick+5*HZ))))
- {
- talked_tick = current_tick;
- talk_number(count, false);
- talk_id(id, true);
- }
- }
- fmt = P2STR(fmt);
+ if (!playlist)
+ playlist = &current_playlist;
+
+ playlist_write_lock(playlist);
+
+ if (modified)
+ update_playlist_flags_unlocked(playlist, PLAYLIST_FLAG_MODIFIED, 0);
+ else
+ update_playlist_flags_unlocked(playlist, 0, PLAYLIST_FLAG_MODIFIED);
+
+ playlist_write_unlock(playlist);
+}
+
+/* returns true if directory playback features should be enabled */
+bool playlist_allow_dirplay(const struct playlist_info *playlist)
+{
+ if (!playlist)
+ playlist = &current_playlist;
+
+ if (playlist_modified(playlist))
+ return false;
- splashf(0, fmt, count, str(LANG_OFF_ABORT));
+ return !!(playlist->flags & PLAYLIST_FLAG_DIRPLAY);
}
/*
- * Display buffer full message
+ * Returns true if the current playlist is neither
+ * associated with a folder nor with an on-disk playlist.
*/
-static void display_buffer_full(void)
+bool playlist_dynamic_only(void)
{
- splash(HZ*2, ID2P(LANG_PLAYLIST_BUFFER_FULL));
+ /* NOTE: New dynamic playlists currently use root dir ("/")
+ * as their placeholder filename – this could change.
+ */
+ if (!strcmp(current_playlist.filename, "/") &&
+ !(current_playlist.flags & PLAYLIST_FLAG_DIRPLAY))
+ return true;
+
+ return false;
}
/*
- * Flush any cached control commands to disk. Called when playlist is being
- * modified. Returns 0 on success and -1 on failure.
+ * Move track at index to new_index. Tracks between the two are shifted
+ * appropriately. Returns 0 on success and -1 on failure.
*/
-static int flush_cached_control(struct playlist_info* playlist)
+int playlist_move(struct playlist_info* playlist, int index, int new_index)
{
- int result = 0;
- int i;
+ int result = -1;
+ bool queue;
+ bool current = false;
+ int r;
+ struct playlist_track_info info;
+ int idx_cur; /* display index of the currently playing track */
+ int idx_from; /* display index of the track we're moving */
+ int idx_to; /* display index of the position we're moving to */
+ bool displace_current = false;
+ char filename[MAX_PATH];
- if (!playlist->num_cached)
- return 0;
+ if (!playlist)
+ playlist = &current_playlist;
- lseek(playlist->control_fd, 0, SEEK_END);
+ dc_thread_stop(playlist);
+ playlist_write_lock(playlist);
- for (i=0; i<playlist->num_cached; i++)
+ if (check_control(playlist) < 0)
{
- struct playlist_control_cache* cache =
- &(playlist->control_cache[i]);
+ notify_control_access_error();
+ goto out;
+ }
- switch (cache->command)
+ if (index == new_index)
+ goto out;
+
+ if (index == playlist->index)
+ {
+ /* Moving the current track */
+ current = true;
+ }
+ else
+ {
+ /* Get display index of the currently playing track */
+ if (playlist_get_track_info(playlist, playlist->index, &info) != -1)
{
- case PLAYLIST_COMMAND_PLAYLIST:
- result = fdprintf(playlist->control_fd, "P:%d:%s:%s\n",
- cache->i1, cache->s1, cache->s2);
- break;
- case PLAYLIST_COMMAND_ADD:
- case PLAYLIST_COMMAND_QUEUE:
- result = fdprintf(playlist->control_fd, "%c:%d:%d:",
- (cache->command == PLAYLIST_COMMAND_ADD)?'A':'Q',
- cache->i1, cache->i2);
- if (result > 0)
+ idx_cur = info.display_index;
+ /* Get display index of the position we're moving to */
+ if (playlist_get_track_info(playlist, new_index, &info) != -1)
+ {
+ idx_to = info.display_index;
+ /* Get display index of the track we're trying to move */
+ if (playlist_get_track_info(playlist, index, &info) != -1)
{
- /* save the position in file where name is written */
- int* seek_pos = (int *)cache->data;
- *seek_pos = lseek(playlist->control_fd, 0, SEEK_CUR);
- result = fdprintf(playlist->control_fd, "%s\n",
- cache->s1);
+ idx_from = info.display_index;
+ /* Check if moving will displace the current track.
+ Displace happens when moving from after current to
+ before, but also when moving from before to before
+ due to the removal from the original position */
+ if ( ((idx_from > idx_cur) && (idx_to <= idx_cur)) ||
+ ((idx_from < idx_cur) && (idx_to < idx_cur)) )
+ displace_current = true;
}
- break;
- case PLAYLIST_COMMAND_DELETE:
- result = fdprintf(playlist->control_fd, "D:%d\n", cache->i1);
- break;
- case PLAYLIST_COMMAND_SHUFFLE:
- result = fdprintf(playlist->control_fd, "S:%d:%d\n",
- cache->i1, cache->i2);
- break;
- case PLAYLIST_COMMAND_UNSHUFFLE:
- result = fdprintf(playlist->control_fd, "U:%d\n", cache->i1);
- break;
- case PLAYLIST_COMMAND_RESET:
- result = fdprintf(playlist->control_fd, "R\n");
- break;
- default:
- break;
+ }
}
-
- if (result <= 0)
- break;
}
- if (result > 0)
- {
- playlist->num_cached = 0;
- playlist->pending_control_sync = true;
+ queue = playlist->indices[index] & PLAYLIST_QUEUED;
- result = 0;
- }
- else
- {
- result = -1;
- splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_UPDATE_ERROR));
- }
+ if (get_track_filename(playlist, index, filename, sizeof(filename)) != 0)
+ goto out;
- return result;
-}
+ /* We want to insert the track at the position that was specified by
+ new_index. This may be different then new_index because of the
+ shifting that will occur after the delete.
+ We calculate this before we do the remove as it depends on the
+ size of the playlist before the track removal */
+ r = rotate_index(playlist, new_index);
-/*
- * Update control data with new command. Depending on the command, it may be
- * cached or flushed to disk.
- */
-static int update_control(struct playlist_info* playlist,
- enum playlist_command command, int i1, int i2,
- const char* s1, const char* s2, void* data)
-{
- int result = 0;
- struct playlist_control_cache* cache;
- bool flush = false;
+ /* Delete track from original position */
+ result = remove_track_unlocked(playlist, index, true);
+ if (result == -1)
+ goto out;
- mutex_lock(playlist->control_mutex);
+ if (r == 0)/* First index */
+ {
+ new_index = PLAYLIST_PREPEND;
+ }
+ else if (r == playlist->amount)
+ {
+ /* Append */
+ new_index = PLAYLIST_INSERT_LAST;
+ }
+ else /* Calculate index of desired position */
+ {
+ new_index = (r+playlist->first_index)%playlist->amount;
- cache = &(playlist->control_cache[playlist->num_cached++]);
+ if ((new_index < playlist->first_index) && (new_index <= playlist->index))
+ displace_current = true;
+ else if ((new_index >= playlist->first_index) && (playlist->index < playlist->first_index))
+ displace_current = false;
+ }
- cache->command = command;
- cache->i1 = i1;
- cache->i2 = i2;
- cache->s1 = s1;
- cache->s2 = s2;
- cache->data = data;
+ result = add_track_to_playlist_unlocked(playlist, filename,
+ new_index, queue, -1);
+ if (result == -1)
+ goto out;
- switch (command)
+ if (current)
{
- case PLAYLIST_COMMAND_PLAYLIST:
- case PLAYLIST_COMMAND_ADD:
- case PLAYLIST_COMMAND_QUEUE:
-#ifndef HAVE_DIRCACHE
- case PLAYLIST_COMMAND_DELETE:
- case PLAYLIST_COMMAND_RESET:
-#endif
- flush = true;
+ /* Moved the current track */
+ switch (new_index)
+ {
+ case PLAYLIST_PREPEND:
+ playlist->index = playlist->first_index;
+ break;
+ case PLAYLIST_INSERT_LAST:
+ playlist->index = playlist->first_index - 1;
+ if (playlist->index < 0)
+ playlist->index += playlist->amount;
break;
- case PLAYLIST_COMMAND_SHUFFLE:
- case PLAYLIST_COMMAND_UNSHUFFLE:
default:
- /* only flush when needed */
+ playlist->index = new_index;
break;
+ }
+ }
+ else if ((displace_current) && (new_index != PLAYLIST_PREPEND))
+ {
+ /* make the index point to the currently playing track */
+ playlist->index++;
}
- if (flush || playlist->num_cached == PLAYLIST_MAX_CACHE)
- result = flush_cached_control(playlist);
+out:
+ playlist_write_unlock(playlist);
+ dc_thread_start(playlist, true);
+
+ if (result != -1 && playlist->started && (audio_status() & AUDIO_STATUS_PLAY))
+ audio_flush_and_reload_tracks();
- mutex_unlock(playlist->control_mutex);
-
return result;
}
+/* returns full path of playlist (minus extension) */
+char *playlist_name(const struct playlist_info* playlist, char *buf,
+ int buf_size)
+{
+ char *sep;
+
+ if (!playlist)
+ playlist = &current_playlist;
+
+ strmemccpy(buf, playlist->filename+playlist->dirlen, buf_size);
+
+ if (!buf[0])
+ return NULL;
+
+ /* Remove extension */
+ sep = strrchr(buf, '.');
+ if (sep)
+ *sep = 0;
+
+ return buf;
+}
+
/*
- * sync control file to disk
+ * Update indices as track has changed
*/
-static void sync_control(struct playlist_info* playlist, bool force)
+int playlist_next(int steps)
{
-#ifdef HAVE_DIRCACHE
- if (playlist->started && force)
-#else
- (void) force;
+ struct playlist_info* playlist = &current_playlist;
+
+ dc_thread_stop(playlist);
+ playlist_write_lock(playlist);
- if (playlist->started)
+ int index;
+ int repeat_mode = global_settings.repeat_mode;
+ if (repeat_mode == REPEAT_ONE)
+ {
+ if (is_manual_skip())
+ repeat_mode = REPEAT_ALL;
+ }
+ if (steps > 0)
+ {
+#ifdef AB_REPEAT_ENABLE
+ if (repeat_mode != REPEAT_AB && repeat_mode != REPEAT_ONE)
+#else
+ if (repeat_mode != REPEAT_ONE)
#endif
+ {
+ int i, j;
+ /* We need to delete all the queued songs */
+ for (i=0, j=steps; i<j; i++)
+ {
+ index = get_next_index(playlist, i, -1);
+
+ if (index >= 0 && playlist->indices[index] & PLAYLIST_QUEUED)
+ {
+ remove_track_unlocked(playlist, index, true);
+ steps--; /* one less track */
+ }
+ }
+ }
+ } /*steps > 0*/
+ index = get_next_index(playlist, steps, repeat_mode);
+
+ if (index < 0)
{
- if (playlist->pending_control_sync)
+ /* end of playlist... or is it */
+ if (repeat_mode == REPEAT_SHUFFLE && playlist->amount > 1)
{
- mutex_lock(playlist->control_mutex);
- fsync(playlist->control_fd);
- playlist->pending_control_sync = false;
- mutex_unlock(playlist->control_mutex);
+ /* Repeat shuffle mode. Re-shuffle playlist and resume play */
+ playlist->first_index = 0;
+ sort_playlist_unlocked(playlist, false, false);
+ randomise_playlist_unlocked(playlist, current_tick, false, true);
+
+ playlist->started = true;
+ playlist->index = 0;
+ index = 0;
+ }
+ else if (global_settings.next_folder && playlist_allow_dirplay(playlist))
+ {
+ /* we switch playlists here */
+ index = create_and_play_dir(steps, true);
+ if (index >= 0)
+ {
+ playlist->index = index;
+ }
}
+ goto out;
}
-}
-/*
- * Rotate indices such that first_index is index 0
- */
-static int rotate_index(const struct playlist_info* playlist, int index)
-{
- index -= playlist->first_index;
- if (index < 0)
- index += playlist->amount;
+ playlist->index = index;
+
+ if (playlist->last_insert_pos >= 0 && steps > 0)
+ {
+ /* check to see if we've gone beyond the last inserted track */
+ int cur = rotate_index(playlist, index);
+ int last_pos = rotate_index(playlist, playlist->last_insert_pos);
+
+ if (cur > last_pos)
+ {
+ /* reset last inserted track */
+ playlist->last_insert_pos = -1;
+ if (playlist->control_fd >= 0)
+ {
+ int result = update_control_unlocked(playlist,
+ PLAYLIST_COMMAND_RESET,
+ -1, -1, NULL, NULL, NULL);
+ if (result < 0)
+ {
+ index = result;
+ goto out;
+ }
+
+ sync_control_unlocked(playlist);
+ }
+ }
+ }
+
+out:
+ playlist_write_unlock(playlist);
+ dc_thread_start(playlist, true);
return index;
}
-/*
- * Need no movement protection since all 3 allocations are not passed to
- * other functions which can yield().
- */
-static int move_callback(int handle, void* current, void* new)
+/* try playing next or previous folder */
+bool playlist_next_dir(int direction)
{
- (void)handle;
- struct playlist_info* playlist = &current_playlist;
- if (current == playlist->indices)
- playlist->indices = new;
- else if (current == playlist->buffer)
- playlist->buffer = new;
-#ifdef HAVE_DIRCACHE
- else if (current == playlist->dcfrefs)
- playlist->dcfrefs = new;
-#endif /* HAVE_DIRCACHE */
- return BUFLIB_CB_OK;
-}
+ /* not to mess up real playlists */
+ if (!playlist_allow_dirplay(&current_playlist))
+ return false;
+ return create_and_play_dir(direction, false) >= 0;
+}
-static struct buflib_callbacks ops = {
- .move_callback = move_callback,
- .shrink_callback = NULL,
-};
-/*
- * Initialize playlist entries at startup
- */
-void playlist_init(void)
+/* get trackname of track that is "steps" away from current playing track.
+ NULL is used to identify end of playlist */
+const char* playlist_peek(int steps, char* buf, size_t buf_size)
{
- int handle;
struct playlist_info* playlist = &current_playlist;
+ char *temp_ptr;
+ int index = get_next_index(playlist, steps, -1);
- mutex_init(&current_playlist_mutex);
- mutex_init(&created_playlist_mutex);
+ if (index < 0)
+ return NULL;
- playlist->current = true;
- strlcpy(playlist->control_filename, PLAYLIST_CONTROL_FILE,
- sizeof(playlist->control_filename));
- playlist->fd = -1;
- playlist->control_fd = -1;
- playlist->max_playlist_size = global_settings.max_files_in_playlist;
- handle = core_alloc_ex("playlist idx",
- playlist->max_playlist_size * sizeof(int), &ops);
- playlist->indices = core_get_data(handle);
- playlist->buffer_size =
- AVERAGE_FILENAME_LENGTH * global_settings.max_files_in_dir;
- handle = core_alloc_ex("playlist buf",
- playlist->buffer_size, &ops);
- playlist->buffer = core_get_data(handle);
- playlist->buffer_handle = handle;
- playlist->control_mutex = &current_playlist_mutex;
+ /* Just testing - don't care about the file name */
+ if (!buf || !buf_size)
+ return "";
- empty_playlist(playlist, true);
+ if (get_track_filename(playlist, index, buf, buf_size) != 0)
+ return NULL;
-#ifdef HAVE_DIRCACHE
- handle = core_alloc_ex("playlist dc",
- playlist->max_playlist_size * sizeof(*playlist->dcfrefs), &ops);
- playlist->dcfrefs = core_get_data(handle);
- copy_filerefs(playlist->dcfrefs, NULL, playlist->max_playlist_size);
- create_thread(playlist_thread, playlist_stack, sizeof(playlist_stack),
- 0, playlist_thread_name IF_PRIO(, PRIORITY_BACKGROUND)
- IF_COP(, CPU));
+ temp_ptr = buf;
- queue_init(&playlist_queue, true);
-#endif /* HAVE_DIRCACHE */
+ /* remove bogus dirs from beginning of path
+ (workaround for buggy playlist creation tools) */
+ while (temp_ptr)
+ {
+ if (file_exists(temp_ptr))
+ break;
+
+ temp_ptr = strchr(temp_ptr+1, '/');
+ }
+
+ if (!temp_ptr)
+ {
+ /* Even though this is an invalid file, we still need to pass a
+ file name to the caller because NULL is used to indicate end
+ of playlist */
+ return buf;
+ }
+
+ return temp_ptr;
}
/*
- * Clean playlist at shutdown
+ * shuffle currently playing playlist
+ *
+ * TODO: Merge this with playlist_shuffle()?
*/
-void playlist_shutdown(void)
+int playlist_randomise(struct playlist_info* playlist, unsigned int seed,
+ bool start_current)
{
- struct playlist_info* playlist = &current_playlist;
+ int result;
- if (playlist->control_fd >= 0)
- {
- mutex_lock(playlist->control_mutex);
+ if (!playlist)
+ playlist = &current_playlist;
- if (playlist->num_cached > 0)
- flush_cached_control(playlist);
+ dc_thread_stop(playlist);
+ playlist_write_lock(playlist);
- close(playlist->control_fd);
- playlist->control_fd = -1;
+ check_control(playlist);
- mutex_unlock(playlist->control_mutex);
+ result = randomise_playlist_unlocked(playlist, seed, start_current, true);
+ if (result != -1 && (audio_status() & AUDIO_STATUS_PLAY) &&
+ playlist->started)
+ {
+ audio_flush_and_reload_tracks();
}
+
+ playlist_write_unlock(playlist);
+ dc_thread_start(playlist, true);
+
+ return result;
}
/*
- * Create new playlist
+ * Removes all tracks, from the playlist, leaving the presently playing
+ * track queued.
*/
-int playlist_create(const char *dir, const char *file)
+int playlist_remove_all_tracks(struct playlist_info *playlist)
{
- struct playlist_info* playlist = &current_playlist;
+ int result;
- new_playlist(playlist, dir, file);
+ if (playlist == NULL)
+ playlist = &current_playlist;
- if (file)
- {
- /* dummy ops with no callbacks, needed because by
- * default buflib buffers can be moved around which must be avoided */
- static struct buflib_callbacks dummy_ops;
- int handle;
- size_t buflen;
- /* use mp3 buffer for maximum load speed */
- handle = core_alloc_maximum("temp", &buflen, &dummy_ops);
- if (handle > 0)
- {
- /* load the playlist file */
- add_indices_to_playlist(playlist, core_get_data(handle), buflen);
- core_free(handle);
- }
- else
- {
- /* should not happen */
- panicf("%s(): OOM", __func__);
- }
- }
+ dc_thread_stop(playlist);
+ playlist_write_lock(playlist);
- return 0;
+ result = remove_all_tracks_unlocked(playlist, true);
+
+ playlist_write_unlock(playlist);
+ dc_thread_start(playlist, false);
+ return result;
}
-#define PLAYLIST_COMMAND_SIZE (MAX_PATH+12)
+/* playlist_resume helper function
+ * only allows comments (#) and PLAYLIST_COMMAND_PLAYLIST (P)
+ */
+static enum playlist_command pl_cmds_start(char cmd)
+{
+ if (cmd == 'P')
+ return PLAYLIST_COMMAND_PLAYLIST;
+ if (cmd == '#')
+ return PLAYLIST_COMMAND_COMMENT;
+
+ return PLAYLIST_COMMAND_ERROR;
+}
+
+/* playlist resume helper function excludes PLAYLIST_COMMAND_PLAYLIST (P) */
+static enum playlist_command pl_cmds_run(char cmd)
+{
+ switch (cmd)
+ {
+ case 'A':
+ return PLAYLIST_COMMAND_ADD;
+ case 'Q':
+ return PLAYLIST_COMMAND_QUEUE;
+ case 'D':
+ return PLAYLIST_COMMAND_DELETE;
+ case 'S':
+ return PLAYLIST_COMMAND_SHUFFLE;
+ case 'U':
+ return PLAYLIST_COMMAND_UNSHUFFLE;
+ case 'R':
+ return PLAYLIST_COMMAND_RESET;
+ case 'C':
+ return PLAYLIST_COMMAND_CLEAR;
+ case 'F':
+ return PLAYLIST_COMMAND_FLAGS;
+ case '#':
+ return PLAYLIST_COMMAND_COMMENT;
+ default: /* ERROR */
+ break;
+ }
+ return PLAYLIST_COMMAND_ERROR;
+}
/*
* Restore the playlist state based on control file commands. Called to
@@ -2107,35 +3076,49 @@ int playlist_create(const char *dir, const char *file)
*/
int playlist_resume(void)
{
- struct playlist_info* playlist = &current_playlist;
char *buffer;
size_t buflen;
+ size_t readsize;
int handle;
int nread;
int total_read = 0;
int control_file_size = 0;
- bool first = true;
bool sorted = true;
int result = -1;
+ enum playlist_command (*pl_cmd)(char) = &pl_cmds_start;
+
+ splash(0, ID2P(LANG_WAIT));
+ cpu_boost(true);
+
+ struct playlist_info* playlist = &current_playlist;
+ dc_thread_stop(playlist);
+ playlist_write_lock(playlist);
- /* dummy ops with no callbacks, needed because by
- * default buflib buffers can be moved around which must be avoided */
- static struct buflib_callbacks dummy_ops;
- /* use mp3 buffer for maximum load speed */
if (core_allocatable() < (1 << 10))
talk_buffer_set_policy(TALK_BUFFER_LOOSE); /* back off voice buffer */
- handle = core_alloc_maximum("temp", &buflen, &dummy_ops);
+
+#ifdef HAVE_DIRCACHE
+ dircache_wait(); /* we need the dircache to use the files in the playlist */
+#endif
+
+ handle = alloc_tempbuf(&buflen);
if (handle < 0)
- panicf("%s(): OOM", __func__);
+ {
+ splashf(HZ * 2, "%s(): OOM", __func__);
+ goto out;
+ }
+
+ /* align buffer for faster load times */
buffer = core_get_data(handle);
+ STORAGE_ALIGN_BUFFER(buffer, buflen);
+ buflen = ALIGN_DOWN(buflen, 512); /* to avoid partial sector I/O */
- empty_playlist(playlist, true);
+ empty_playlist_unlocked(playlist, true);
- splash(0, ID2P(LANG_WAIT));
playlist->control_fd = open(playlist->control_filename, O_RDWR);
if (playlist->control_fd < 0)
{
- splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
+ notify_control_access_error();
goto out;
}
playlist->control_created = true;
@@ -2143,16 +3126,16 @@ int playlist_resume(void)
control_file_size = filesize(playlist->control_fd);
if (control_file_size <= 0)
{
- splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
+ notify_control_access_error();
goto out;
}
/* read a small amount first to get the header */
- nread = read(playlist->control_fd, buffer,
- PLAYLIST_COMMAND_SIZE<buflen?PLAYLIST_COMMAND_SIZE:buflen);
+ readsize = (PLAYLIST_COMMAND_SIZE<buflen?PLAYLIST_COMMAND_SIZE:buflen);
+ nread = read(playlist->control_fd, buffer, readsize);
if(nread <= 0)
{
- splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
+ notify_control_access_error();
goto out;
}
@@ -2168,136 +3151,147 @@ int playlist_resume(void)
bool newline = true;
bool exit_loop = false;
char *p = buffer;
- char *str1 = NULL;
- char *str2 = NULL;
- char *str3 = NULL;
+ char *strp[3] = {NULL};
+
unsigned long last_tick = current_tick;
+ splash_progress_set_delay(HZ / 2); /* wait 1/2 sec before progress */
bool useraborted = false;
-
- for(count=0; count<nread && !exit_loop && !useraborted; count++,p++)
+ bool queue = false;
+
+ for(count=0; count<nread && !exit_loop; count++,p++)
{
- /* So a splash while we are loading. */
+ /* Show a splash while we are loading. */
+ splash_progress((total_read + count), control_file_size,
+ "%s (%s)", str(LANG_WAIT), str(LANG_OFF_ABORT));
if (TIME_AFTER(current_tick, last_tick + HZ/4))
{
- splashf(0, str(LANG_LOADING_PERCENT),
- (total_read+count)*100/control_file_size,
- str(LANG_OFF_ABORT));
if (action_userabort(TIMEOUT_NOBLOCK))
{
useraborted = true;
+ exit_loop = true;
break;
}
last_tick = current_tick;
}
-
/* Are we on a new line? */
if((*p == '\n') || (*p == '\r'))
{
*p = '\0';
- /* save last_newline in case we need to load more data */
- last_newline = count;
-
switch (current_command)
{
+ case PLAYLIST_COMMAND_ERROR:
+ {
+ /* first non-comment line does not specify playlist */
+ /* ( below handled by pl_cmds_run() ) */
+ /* OR playlist is specified more than once */
+ /* OR unknown command -- pl corrupted?? */
+ result = -12;
+ exit_loop = true;
+ break;
+ }
case PLAYLIST_COMMAND_PLAYLIST:
{
- /* str1=version str2=dir str3=file */
+ /* strp[0]=version strp[1]=dir strp[2]=file */
int version;
- if (!str1)
+ if (!strp[0])
{
- result = -1;
+ result = -2;
exit_loop = true;
break;
}
-
- if (!str2)
- str2 = "";
-
- if (!str3)
- str3 = "";
-
- version = atoi(str1);
-
- if (version != PLAYLIST_CONTROL_FILE_VERSION)
+
+ if (!strp[1])
+ strp[1] = "";
+
+ if (!strp[2])
+ strp[2] = "";
+
+ version = atoi(strp[0]);
+
+ /*
+ * TODO: Playlist control file version upgrades
+ *
+ * If an older version control file is loaded then
+ * the header should be updated to the latest version
+ * in case any incompatible commands are written out.
+ * (It's not a big deal since the error message will
+ * be practically the same either way...)
+ */
+ if (version < PLAYLIST_CONTROL_FILE_MIN_VERSION ||
+ version > PLAYLIST_CONTROL_FILE_VERSION)
{
- result = -1;
+ result = -3;
goto out;
}
-
- update_playlist_filename(playlist, str2, str3);
-
- if (str3[0] != '\0')
+
+ update_playlist_filename_unlocked(playlist, strp[1], strp[2]);
+
+ if (strp[2][0] != '\0')
{
/* NOTE: add_indices_to_playlist() overwrites the
audiobuf so we need to reload control file
data */
add_indices_to_playlist(playlist, buffer, buflen);
}
- else if (str2[0] != '\0')
+ else if (strp[1][0] != '\0')
{
- playlist->in_ram = true;
- resume_directory(str2);
+ playlist->flags |= PLAYLIST_FLAG_DIRPLAY;
}
-
+
/* load the rest of the data */
- first = false;
exit_loop = true;
-
+ readsize = buflen;
+ pl_cmd = &pl_cmds_run;
break;
}
- case PLAYLIST_COMMAND_ADD:
case PLAYLIST_COMMAND_QUEUE:
+ queue = true;
+ /*Fall-through*/
+ case PLAYLIST_COMMAND_ADD:
{
- /* str1=position str2=last_position str3=file */
- int position, last_position;
- bool queue;
-
- if (!str1 || !str2 || !str3)
+ /* strp[0]=position strp[1]=last_position strp[2]=file */
+ if (!strp[0] || !strp[1] || !strp[2])
{
- result = -1;
+ result = -4;
exit_loop = true;
break;
}
-
- position = atoi(str1);
- last_position = atoi(str2);
-
- queue = (current_command == PLAYLIST_COMMAND_ADD)?
- false:true;
-
- /* seek position is based on str3's position in
+
+ int position = atoi(strp[0]);
+ int last_position = atoi(strp[1]);
+
+ /* seek position is based on strp[2]'s position in
buffer */
- if (add_track_to_playlist(playlist, str3, position,
- queue, total_read+(str3-buffer)) < 0)
+ if (add_track_to_playlist_unlocked(playlist, strp[2],
+ position, queue, total_read+(strp[2]-buffer)) < 0)
{
- result = -1;
+ result = -5;
goto out;
}
-
- playlist->last_insert_pos = last_position;
+ playlist->last_insert_pos = last_position;
+ queue = false;
break;
}
case PLAYLIST_COMMAND_DELETE:
{
- /* str1=position */
+ /* strp[0]=position */
int position;
-
- if (!str1)
+
+ if (!strp[0])
{
- result = -1;
+ result = -6;
exit_loop = true;
break;
}
-
- position = atoi(str1);
-
- if (remove_track_from_playlist(playlist, position,
- false) < 0)
+
+ position = atoi(strp[0]);
+
+ if (remove_track_unlocked(playlist, position, false) < 0)
{
- result = -1;
+ result = -7;
goto out;
}
@@ -2305,49 +3299,50 @@ int playlist_resume(void)
}
case PLAYLIST_COMMAND_SHUFFLE:
{
- /* str1=seed str2=first_index */
+ /* strp[0]=seed strp[1]=first_index */
int seed;
-
- if (!str1 || !str2)
+
+ if (!strp[0] || !strp[1])
{
- result = -1;
+ result = -8;
exit_loop = true;
break;
}
-
+
if (!sorted)
{
/* Always sort list before shuffling */
- sort_playlist(playlist, false, false);
+ sort_playlist_unlocked(playlist, false, false);
}
- seed = atoi(str1);
- playlist->first_index = atoi(str2);
-
- if (randomise_playlist(playlist, seed, false,
+ seed = atoi(strp[0]);
+ playlist->first_index = atoi(strp[1]);
+
+ if (randomise_playlist_unlocked(playlist, seed, false,
false) < 0)
{
- result = -1;
+ result = -9;
goto out;
}
sorted = false;
+
break;
}
case PLAYLIST_COMMAND_UNSHUFFLE:
{
- /* str1=first_index */
- if (!str1)
+ /* strp[0]=first_index */
+ if (!strp[0])
{
- result = -1;
+ result = -10;
exit_loop = true;
break;
}
-
- playlist->first_index = atoi(str1);
-
- if (sort_playlist(playlist, false, false) < 0)
+
+ playlist->first_index = atoi(strp[0]);
+
+ if (sort_playlist_unlocked(playlist, false, false) < 0)
{
- result = -1;
+ result = -11;
goto out;
}
@@ -2359,11 +3354,38 @@ int playlist_resume(void)
playlist->last_insert_pos = -1;
break;
}
+ case PLAYLIST_COMMAND_CLEAR:
+ {
+ if (strp[0])
+ playlist->index = atoi(strp[0]);
+ if (remove_all_tracks_unlocked(playlist, false) < 0)
+ {
+ result = -16;
+ goto out;
+ }
+ break;
+ }
+ case PLAYLIST_COMMAND_FLAGS:
+ {
+ if (!strp[0] || !strp[1])
+ {
+ result = -18;
+ exit_loop = true;
+ break;
+ }
+ unsigned int setf = atoi(strp[0]);
+ unsigned int clearf = atoi(strp[1]);
+
+ playlist->flags = (playlist->flags & ~clearf) | setf;
+ break;
+ }
case PLAYLIST_COMMAND_COMMENT:
default:
break;
}
+ /* save last_newline in case we need to load more data */
+ last_newline = count;
newline = true;
/* to ignore any extra newlines */
@@ -2372,61 +3394,13 @@ int playlist_resume(void)
else if(newline)
{
newline = false;
-
- /* first non-comment line must always specify playlist */
- if (first && *p != 'P' && *p != '#')
- {
- result = -1;
- exit_loop = true;
- break;
- }
-
- switch (*p)
- {
- case 'P':
- /* playlist can only be specified once */
- if (!first)
- {
- result = -1;
- exit_loop = true;
- break;
- }
-
- current_command = PLAYLIST_COMMAND_PLAYLIST;
- break;
- case 'A':
- current_command = PLAYLIST_COMMAND_ADD;
- break;
- case 'Q':
- current_command = PLAYLIST_COMMAND_QUEUE;
- break;
- case 'D':
- current_command = PLAYLIST_COMMAND_DELETE;
- break;
- case 'S':
- current_command = PLAYLIST_COMMAND_SHUFFLE;
- break;
- case 'U':
- current_command = PLAYLIST_COMMAND_UNSHUFFLE;
- break;
- case 'R':
- current_command = PLAYLIST_COMMAND_RESET;
- break;
- case '#':
- current_command = PLAYLIST_COMMAND_COMMENT;
- break;
- default:
- result = -1;
- exit_loop = true;
- break;
- }
-
+ current_command = (*pl_cmd)(*p);
str_count = -1;
- str1 = NULL;
- str2 = NULL;
- str3 = NULL;
+ strp[0] = NULL;
+ strp[1] = NULL;
+ strp[2] = NULL;
}
- else if(current_command != PLAYLIST_COMMAND_COMMENT)
+ else if(current_command < PLAYLIST_COMMAND_COMMENT)
{
/* all control file strings are separated with a colon.
Replace the colon with 0 to get proper strings that can be
@@ -2441,13 +3415,9 @@ int playlist_resume(void)
switch (str_count)
{
case 0:
- str1 = p+1;
- break;
case 1:
- str2 = p+1;
- break;
case 2:
- str3 = p+1;
+ strp[str_count] = p+1;
break;
default:
/* allow last string to contain colons */
@@ -2459,9 +3429,9 @@ int playlist_resume(void)
}
}
- if (result < 0)
+ if (result < 0 || current_command == PLAYLIST_COMMAND_ERROR)
{
- splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_INVALID));
+ splashf(HZ*2, "Err: %d, %s", result, str(LANG_PLAYLIST_CONTROL_INVALID));
goto out;
}
@@ -2471,13 +3441,14 @@ int playlist_resume(void)
result = -1;
goto out;
}
+
if (!newline || (exit_loop && count<nread))
{
if ((total_read + count) >= control_file_size)
{
/* no newline at end of control file */
- splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_INVALID));
- result = -1;
+ splashf(HZ*2, "Err: EOF, %s", str(LANG_PLAYLIST_CONTROL_INVALID));
+ result = -15;
goto out;
}
@@ -2489,12 +3460,7 @@ int playlist_resume(void)
total_read += count;
- if (first)
- /* still looking for header */
- nread = read(playlist->control_fd, buffer,
- PLAYLIST_COMMAND_SIZE<buflen?PLAYLIST_COMMAND_SIZE:buflen);
- else
- nread = read(playlist->control_fd, buffer, buflen);
+ nread = read(playlist->control_fd, buffer, readsize);
/* Terminate on EOF */
if(nread <= 0)
@@ -2503,75 +3469,16 @@ int playlist_resume(void)
}
}
-#ifdef HAVE_DIRCACHE
- queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
-#endif
-
out:
+ playlist_write_unlock(playlist);
+ dc_thread_start(playlist, true);
+
talk_buffer_set_policy(TALK_BUFFER_DEFAULT);
core_free(handle);
+ cpu_boost(false);
return result;
}
-/*
- * Add track to in_ram playlist. Used when playing directories.
- */
-int playlist_add(const char *filename)
-{
- struct playlist_info* playlist = &current_playlist;
- int len = strlen(filename);
-
- if((len+1 > playlist->buffer_size - playlist->buffer_end_pos) ||
- (playlist->amount >= playlist->max_playlist_size))
- {
- display_buffer_full();
- return -1;
- }
-
- playlist->indices[playlist->amount] = playlist->buffer_end_pos;
-#ifdef HAVE_DIRCACHE
- copy_filerefs(&playlist->dcfrefs[playlist->amount], NULL, 1);
-#endif
-
- playlist->amount++;
-
- strcpy((char*)&playlist->buffer[playlist->buffer_end_pos], filename);
- playlist->buffer_end_pos += len;
- playlist->buffer[playlist->buffer_end_pos++] = '\0';
-
- return 0;
-}
-
-/* shuffle newly created playlist using random seed. */
-int playlist_shuffle(int random_seed, int start_index)
-{
- struct playlist_info* playlist = &current_playlist;
-
- bool start_current = false;
-
- if (start_index >= 0 && global_settings.play_selected)
- {
- /* store the seek position before the shuffle */
- playlist->index = playlist->first_index = start_index;
- start_current = true;
- }
-
- randomise_playlist(playlist, random_seed, start_current, true);
-
- return playlist->index;
-}
-
-/* returns the crc32 of the filename of the track at the specified index */
-unsigned int playlist_get_filename_crc32(struct playlist_info *playlist,
- int index)
-{
- struct playlist_track_info track_info;
- if (playlist_get_track_info(playlist, index, &track_info) == -1)
- return -1;
-
- return crc_32(track_info.filename, strlen(track_info.filename), -1);
-}
-
/* resume a playlist track with the given crc_32 of the track name. */
void playlist_resume_track(int start_index, unsigned int crc,
unsigned long elapsed, unsigned long offset)
@@ -2579,19 +3486,15 @@ void playlist_resume_track(int start_index, unsigned int crc,
int i;
unsigned int tmp_crc;
struct playlist_info* playlist = &current_playlist;
- tmp_crc = playlist_get_filename_crc32(playlist, start_index);
- if (tmp_crc == crc)
- {
- playlist_start(start_index, elapsed, offset);
- return;
- }
for (i = 0 ; i < playlist->amount; i++)
{
- tmp_crc = playlist_get_filename_crc32(playlist, i);
+ int index = (i + start_index) % playlist->amount;
+
+ tmp_crc = playlist_get_filename_crc32(playlist, index);
if (tmp_crc == crc)
{
- playlist_start(i, elapsed, offset);
+ playlist_start(index, elapsed, offset);
return;
}
}
@@ -2600,195 +3503,182 @@ void playlist_resume_track(int start_index, unsigned int crc,
playlist_start(0, 0, 0);
}
-/* start playing current playlist at specified index/offset */
-void playlist_start(int start_index, unsigned long elapsed,
- unsigned long offset)
+/*
+ * Set the specified playlist as the current.
+ * NOTE: You will get undefined behaviour if something is already playing so
+ * remember to stop before calling this. Also, this call will
+ * effectively close your playlist, making it unusable.
+ */
+int playlist_set_current(struct playlist_info* playlist)
{
- struct playlist_info* playlist = &current_playlist;
+ int result = -1;
- playlist->index = start_index;
+ if (!playlist || (check_control(playlist) < 0))
+ return result;
- playlist->started = true;
- sync_control(playlist, false);
- audio_play(elapsed, offset);
- audio_resume();
-}
+ dc_thread_stop(&current_playlist);
+ playlist_write_lock(&current_playlist);
-/* Returns false if 'steps' is out of bounds, else true */
-bool playlist_check(int steps)
-{
- struct playlist_info* playlist = &current_playlist;
+ empty_playlist_unlocked(&current_playlist, false);
- /* always allow folder navigation */
- if (global_settings.next_folder && playlist->in_ram)
- return true;
+ strmemccpy(current_playlist.filename, playlist->filename,
+ sizeof(current_playlist.filename));
- int index = get_next_index(playlist, steps, -1);
+ current_playlist.utf8 = playlist->utf8;
+ current_playlist.fd = playlist->fd;
- if (index < 0 && steps >= 0 && global_settings.repeat_mode == REPEAT_SHUFFLE)
- index = get_next_index(playlist, steps, REPEAT_ALL);
+ pl_close_control(playlist);
+ pl_close_control(&current_playlist);
- return (index >= 0);
-}
+ remove(current_playlist.control_filename);
+ current_playlist.control_created = false;
-/* get trackname of track that is "steps" away from current playing track.
- NULL is used to identify end of playlist */
-const char* playlist_peek(int steps, char* buf, size_t buf_size)
-{
- struct playlist_info* playlist = &current_playlist;
- int seek;
- char *temp_ptr;
- int index;
- bool control_file;
+ if (rename(playlist->control_filename, current_playlist.control_filename) < 0)
+ goto out;
- index = get_next_index(playlist, steps, -1);
- if (index < 0)
- return NULL;
+ current_playlist.control_fd = open(current_playlist.control_filename,
+ O_RDWR);
- /* Just testing - don't care about the file name */
- if (!buf || !buf_size)
- return "";
+ if (current_playlist.control_fd < 0)
+ goto out;
- control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
- seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
+ current_playlist.control_created = true;
+ current_playlist.dirlen = playlist->dirlen;
- if (get_filename(playlist, index, seek, control_file, buf,
- buf_size) < 0)
- return NULL;
+ if (playlist->indices && playlist->indices != current_playlist.indices)
+ memcpy((void*)current_playlist.indices, (void*)playlist->indices,
+ playlist->max_playlist_size*sizeof(*playlist->indices));
+ dc_init_filerefs(&current_playlist, 0, current_playlist.max_playlist_size);
- temp_ptr = buf;
+ current_playlist.first_index = playlist->first_index;
+ current_playlist.amount = playlist->amount;
+ current_playlist.last_insert_pos = playlist->last_insert_pos;
+ current_playlist.seed = playlist->seed;
+ current_playlist.flags = playlist->flags;
- if (!playlist->in_ram || control_file)
- {
- /* remove bogus dirs from beginning of path
- (workaround for buggy playlist creation tools) */
- while (temp_ptr)
- {
- if (file_exists(temp_ptr))
- break;
+ result = 0;
- temp_ptr = strchr(temp_ptr+1, '/');
- }
+out:
+ playlist_write_unlock(&current_playlist);
+ dc_thread_start(&current_playlist, true);
- if (!temp_ptr)
- {
- /* Even though this is an invalid file, we still need to pass a
- file name to the caller because NULL is used to indicate end
- of playlist */
- return buf;
- }
- }
+ return result;
+}
- return temp_ptr;
+/* set playlist->last_shuffle_start to playlist end for
+ PLAYLIST_INSERT_LAST_SHUFFLED command purposes*/
+void playlist_set_last_shuffled_start(void)
+{
+ struct playlist_info* playlist = &current_playlist;
+ playlist_write_lock(playlist);
+ playlist->last_shuffled_start = playlist->first_index > 0 ?
+ playlist->first_index : playlist->amount;
+ playlist_write_unlock(playlist);
}
-/*
- * Update indices as track has changed
- */
-int playlist_next(int steps)
+/* shuffle newly created playlist using random seed. */
+int playlist_shuffle(int random_seed, int start_index)
{
struct playlist_info* playlist = &current_playlist;
- int index;
+ bool start_current = false;
- if ( (steps > 0)
-#ifdef AB_REPEAT_ENABLE
- && (global_settings.repeat_mode != REPEAT_AB)
-#endif
- && (global_settings.repeat_mode != REPEAT_ONE) )
- {
- int i, j;
+ dc_thread_stop(playlist);
+ playlist_write_lock(playlist);
- /* We need to delete all the queued songs */
- for (i=0, j=steps; i<j; i++)
- {
- index = get_next_index(playlist, i, -1);
-
- if (playlist->indices[index] & PLAYLIST_QUEUE_MASK)
- {
- remove_track_from_playlist(playlist, index, true);
- steps--; /* one less track */
- }
- }
+ if (start_index >= 0 && global_settings.play_selected)
+ {
+ /* store the seek position before the shuffle */
+ playlist->index = playlist->first_index = start_index;
+ start_current = true;
}
- index = get_next_index(playlist, steps, -1);
+ randomise_playlist_unlocked(playlist, random_seed, start_current, true);
- if (index < 0)
- {
- /* end of playlist... or is it */
- if (global_settings.repeat_mode == REPEAT_SHUFFLE &&
- playlist->amount > 1)
- {
- /* Repeat shuffle mode. Re-shuffle playlist and resume play */
- playlist->first_index = 0;
- sort_playlist(playlist, false, false);
- randomise_playlist(playlist, current_tick, false, true);
+ playlist_write_unlock(playlist);
+ dc_thread_start(playlist, true);
- playlist->started = true;
- playlist->index = 0;
- index = 0;
- }
- else if (playlist->in_ram && global_settings.next_folder)
- {
- index = create_and_play_dir(steps, true);
+ return playlist->index;
+}
- if (index >= 0)
- {
- playlist->index = index;
- }
- }
+/* Marks the index of the track to be skipped that is "steps" away from
+ * current playing track.
+ */
+void playlist_skip_entry(struct playlist_info *playlist, int steps)
+{
+ int index;
- return index;
- }
+ if (playlist == NULL)
+ playlist = &current_playlist;
- playlist->index = index;
+ playlist_write_lock(playlist);
- if (playlist->last_insert_pos >= 0 && steps > 0)
- {
- /* check to see if we've gone beyond the last inserted track */
- int cur = rotate_index(playlist, index);
- int last_pos = rotate_index(playlist, playlist->last_insert_pos);
+ /* need to account for already skipped tracks */
+ steps = calculate_step_count(playlist, steps);
- if (cur > last_pos)
- {
- /* reset last inserted track */
- playlist->last_insert_pos = -1;
+ index = playlist->index + steps;
+ if (index < 0)
+ index += playlist->amount;
+ else if (index >= playlist->amount)
+ index -= playlist->amount;
- if (playlist->control_fd >= 0)
- {
- int result = update_control(playlist, PLAYLIST_COMMAND_RESET,
- -1, -1, NULL, NULL, NULL);
+ playlist->indices[index] |= PLAYLIST_SKIPPED;
+ playlist_write_unlock(playlist);
+}
- if (result < 0)
- return result;
+/* sort currently playing playlist */
+int playlist_sort(struct playlist_info* playlist, bool start_current)
+{
+ int result;
- sync_control(playlist, false);
- }
- }
- }
+ if (!playlist)
+ playlist = &current_playlist;
- return index;
+ dc_thread_stop(playlist);
+ playlist_write_lock(playlist);
+
+ check_control(playlist);
+ result = sort_playlist_unlocked(playlist, start_current, true);
+
+ if (result != -1 && (audio_status() & AUDIO_STATUS_PLAY) && playlist->started)
+ audio_flush_and_reload_tracks();
+
+ playlist_write_unlock(playlist);
+ dc_thread_start(playlist, true);
+ return result;
}
-/* try playing next or previous folder */
-bool playlist_next_dir(int direction)
+/* start playing current playlist at specified index/offset */
+void playlist_start(int start_index, unsigned long elapsed,
+ unsigned long offset)
{
- /* not to mess up real playlists */
- if(!current_playlist.in_ram)
- return false;
+ struct playlist_info* playlist = &current_playlist;
- return create_and_play_dir(direction, false) >= 0;
+ playlist_write_lock(playlist);
+
+ playlist->index = start_index;
+ playlist->started = true;
+
+ sync_control_unlocked(playlist);
+
+ playlist_write_unlock(playlist);
+
+ audio_play(elapsed, offset);
+ audio_resume();
}
-/* Get resume info for current playing song. If return value is -1 then
- settings shouldn't be saved. */
-int playlist_get_resume_info(int *resume_index)
+void playlist_sync(struct playlist_info* playlist)
{
- struct playlist_info* playlist = &current_playlist;
+ if (!playlist)
+ playlist = &current_playlist;
- *resume_index = playlist->index;
+ playlist_write_lock(playlist);
- return 0;
+ sync_control_unlocked(playlist);
+
+ playlist_write_unlock(playlist);
+
+ if ((audio_status() & AUDIO_STATUS_PLAY) && playlist->started)
+ audio_flush_and_reload_tracks();
}
/* Update resume info for current playing song. Returns -1 on error. */
@@ -2818,1042 +3708,327 @@ int playlist_update_resume_info(const struct mp3entry* id3)
global_status.resume_elapsed = -1;
global_status.resume_offset = -1;
status_save();
+ return -1;
}
return 0;
}
-/* Returns index of current playing track for display purposes. This value
- should not be used for resume purposes as it doesn't represent the actual
- index into the playlist */
-int playlist_get_display_index(void)
-{
- struct playlist_info* playlist = &current_playlist;
-
- /* first_index should always be index 0 for display purposes */
- int index = rotate_index(playlist, playlist->index);
-
- return (index+1);
-}
-
-/* returns number of tracks in current playlist */
-int playlist_amount(void)
-{
- return playlist_amount_ex(NULL);
-}
-/* set playlist->last_shuffle_start to playlist->amount for
- PLAYLIST_INSERT_LAST_SHUFFLED command purposes*/
-void playlist_set_last_shuffled_start(void)
-{
- struct playlist_info* playlist = &current_playlist;
- playlist->last_shuffled_start = playlist->amount;
-}
-/*
- * Create a new playlist If playlist is not NULL then we're loading a
- * playlist off disk for viewing/editing. The index_buffer is used to store
- * playlist indices (required for and only used if !current playlist). The
- * temp_buffer (if not NULL) is used as a scratchpad when loading indices.
- */
-int playlist_create_ex(struct playlist_info* playlist,
- const char* dir, const char* file,
- void* index_buffer, int index_buffer_size,
- void* temp_buffer, int temp_buffer_size)
-{
- if (!playlist)
- playlist = &current_playlist;
- else
- {
- /* Initialize playlist structure */
- int r = rand() % 10;
- playlist->current = false;
-
- /* Use random name for control file */
- snprintf(playlist->control_filename, sizeof(playlist->control_filename),
- "%s.%d", PLAYLIST_CONTROL_FILE, r);
- playlist->fd = -1;
- playlist->control_fd = -1;
-
- if (index_buffer)
- {
- int num_indices = index_buffer_size /
- playlist_get_required_bufsz(playlist, false, 1);
-
- if (num_indices > global_settings.max_files_in_playlist)
- num_indices = global_settings.max_files_in_playlist;
-
- playlist->max_playlist_size = num_indices;
- playlist->indices = index_buffer;
-#ifdef HAVE_DIRCACHE
- playlist->dcfrefs = (void *)&playlist->indices[num_indices];
-#endif
- }
- else
- {
- playlist->max_playlist_size = current_playlist.max_playlist_size;
- playlist->indices = current_playlist.indices;
-#ifdef HAVE_DIRCACHE
- playlist->dcfrefs = current_playlist.dcfrefs;
-#endif
- }
-
- playlist->buffer_size = 0;
- playlist->buffer_handle = -1;
- playlist->buffer = NULL;
- playlist->control_mutex = &created_playlist_mutex;
- }
-
- new_playlist(playlist, dir, file);
-
- if (file)
- /* load the playlist file */
- add_indices_to_playlist(playlist, temp_buffer, temp_buffer_size);
-
- return 0;
-}
-
-/*
- * Set the specified playlist as the current.
- * NOTE: You will get undefined behaviour if something is already playing so
- * remember to stop before calling this. Also, this call will
- * effectively close your playlist, making it unusable.
- */
-int playlist_set_current(struct playlist_info* playlist)
+static int pl_get_tempname(const char *filename, char *buf, size_t bufsz)
{
- if (!playlist || (check_control(playlist) < 0))
+ if (strlcpy(buf, filename, bufsz) >= bufsz)
return -1;
- empty_playlist(&current_playlist, false);
-
- strlcpy(current_playlist.filename, playlist->filename,
- sizeof(current_playlist.filename));
-
- current_playlist.utf8 = playlist->utf8;
- current_playlist.fd = playlist->fd;
-
- close(playlist->control_fd);
- playlist->control_fd = -1;
- close(current_playlist.control_fd);
- current_playlist.control_fd = -1;
- remove(current_playlist.control_filename);
- current_playlist.control_created = false;
- if (rename(playlist->control_filename,
- current_playlist.control_filename) < 0)
- return -1;
- current_playlist.control_fd = open(current_playlist.control_filename,
- O_RDWR);
- if (current_playlist.control_fd < 0)
+ if (strlcat(buf, "_temp", bufsz) >= bufsz)
return -1;
- current_playlist.control_created = true;
-
- current_playlist.dirlen = playlist->dirlen;
-
- if (playlist->indices && playlist->indices != current_playlist.indices)
- {
- memcpy((void*)current_playlist.indices, (void*)playlist->indices,
- playlist->max_playlist_size*sizeof(int));
-#ifdef HAVE_DIRCACHE
- copy_filerefs(current_playlist.dcfrefs, playlist->dcfrefs,
- playlist->max_playlist_size);
-#endif
- }
-
- current_playlist.first_index = playlist->first_index;
- current_playlist.amount = playlist->amount;
- current_playlist.last_insert_pos = playlist->last_insert_pos;
- current_playlist.seed = playlist->seed;
- current_playlist.shuffle_modified = playlist->shuffle_modified;
- current_playlist.deleted = playlist->deleted;
- current_playlist.num_inserted_tracks = playlist->num_inserted_tracks;
-
- memcpy(current_playlist.control_cache, playlist->control_cache,
- sizeof(current_playlist.control_cache));
- current_playlist.num_cached = playlist->num_cached;
- current_playlist.pending_control_sync = playlist->pending_control_sync;
return 0;
}
-struct playlist_info *playlist_get_current(void)
-{
- return &current_playlist;
-}
-/*
- * Close files and delete control file for non-current playlist.
- */
-void playlist_close(struct playlist_info* playlist)
-{
- if (!playlist)
- return;
-
- if (playlist->fd >= 0) {
- close(playlist->fd);
- playlist->fd = -1;
- }
-
- if (playlist->control_fd >= 0) {
- close(playlist->control_fd);
- playlist->control_fd = -1;
- }
-
- if (playlist->control_created) {
- remove(playlist->control_filename);
- playlist->control_created = false;
- }
-}
-
-void playlist_sync(struct playlist_info* playlist)
-{
- if (!playlist)
- playlist = &current_playlist;
-
- sync_control(playlist, false);
- if ((audio_status() & AUDIO_STATUS_PLAY) && playlist->started)
- audio_flush_and_reload_tracks();
-
-#ifdef HAVE_DIRCACHE
- queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
-#endif
-}
/*
- * Insert track into playlist at specified position (or one of the special
- * positions). Returns position where track was inserted or -1 if error.
- */
-int playlist_insert_track(struct playlist_info* playlist, const char *filename,
- int position, bool queue, bool sync)
-{
- int result;
-
- if (!playlist)
- playlist = &current_playlist;
-
- if (check_control(playlist) < 0)
- {
- splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
- return -1;
- }
-
- result = add_track_to_playlist(playlist, filename, position, queue, -1);
-
- /* Check if we want manually sync later. For example when adding
- * bunch of files from tagcache, syncing after every file wouldn't be
- * a good thing to do. */
- if (sync && result >= 0)
- playlist_sync(playlist);
-
- return result;
-}
-
-/*
- * Insert all tracks from specified directory into playlist.
+ * Save all non-queued tracks to an M3U playlist with the given filename.
+ * On success, the playlist is updated to point to the new playlist file.
+ * On failure, the playlist filename is unchanged, but playlist indices
+ * may be trashed; the current playlist should be reloaded.
+ *
+ * Returns 0 on success, < 0 on error, and > 0 if user canceled.
*/
-int playlist_insert_directory(struct playlist_info* playlist,
- const char *dirname, int position, bool queue,
- bool recurse)
+static int pl_save_playlist(struct playlist_info* playlist,
+ const char *filename,
+ char *tmpbuf, size_t tmpsize)
{
- int result;
- unsigned char *count_str;
- struct directory_search_context context;
-
- if (!playlist)
- playlist = &current_playlist;
+ int fd, index, num_saved;
+ off_t offset;
+ int ret, err;
- if (check_control(playlist) < 0)
- {
- splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
+ if (pl_get_tempname(filename, tmpbuf, tmpsize))
return -1;
- }
-
- if (position == PLAYLIST_REPLACE)
- {
- if (playlist_remove_all_tracks(playlist) == 0)
- position = PLAYLIST_INSERT_LAST;
- else
- return -1;
- }
- if (queue)
- count_str = ID2P(LANG_PLAYLIST_QUEUE_COUNT);
+ /*
+ * We always save playlists as UTF-8. Add a BOM only when
+ * saving to the .m3u file extension.
+ */
+ if (is_m3u8_name(filename))
+ fd = open(tmpbuf, O_CREAT|O_WRONLY|O_TRUNC, 0666);
else
- count_str = ID2P(LANG_PLAYLIST_INSERT_COUNT);
-
- display_playlist_count(0, count_str, false);
-
- context.playlist = playlist;
- context.position = position;
- context.queue = queue;
- context.count = 0;
-
- cpu_boost(true);
-
- result = playlist_directory_tracksearch(dirname, recurse,
- directory_search_callback, &context);
-
- sync_control(playlist, false);
-
- cpu_boost(false);
-
- display_playlist_count(context.count, count_str, true);
-
- if ((audio_status() & AUDIO_STATUS_PLAY) && playlist->started)
- audio_flush_and_reload_tracks();
+ fd = open_utf8(tmpbuf, O_CREAT|O_WRONLY|O_TRUNC);
-#ifdef HAVE_DIRCACHE
- queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
-#endif
-
- return result;
-}
-
-/*
- * Insert all tracks from specified playlist into dynamic playlist.
- */
-int playlist_insert_playlist(struct playlist_info* playlist, const char *filename,
- int position, bool queue)
-{
- int fd;
- int max;
- char *dir;
- unsigned char *count_str;
- char temp_buf[MAX_PATH+1];
- char trackname[MAX_PATH+1];
- int count = 0;
- int result = 0;
- bool utf8 = is_m3u8(filename);
-
- if (!playlist)
- playlist = &current_playlist;
-
- if (check_control(playlist) < 0)
- {
- splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
- return -1;
- }
-
- fd = open_utf8(filename, O_RDONLY);
if (fd < 0)
- {
- splash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR));
return -1;
- }
-
- /* we need the directory name for formatting purposes */
- size_t dirlen = path_dirname(filename, (const char **)&dir);
- dir = strmemdupa(dir, dirlen);
- if (queue)
- count_str = ID2P(LANG_PLAYLIST_QUEUE_COUNT);
- else
- count_str = ID2P(LANG_PLAYLIST_INSERT_COUNT);
+ offset = lseek(fd, 0, SEEK_CUR);
+ index = playlist->first_index;
+ num_saved = 0;
- display_playlist_count(count, count_str, false);
+ display_playlist_count(num_saved, ID2P(LANG_PLAYLIST_SAVE_COUNT), true);
- if (position == PLAYLIST_REPLACE)
+ for (int i = 0; i < playlist->amount; ++i, ++index)
{
- if (playlist_remove_all_tracks(playlist) == 0)
- position = PLAYLIST_INSERT_LAST;
- else return -1;
- }
-
- cpu_boost(true);
+ if (index == playlist->amount)
+ index = 0;
- while ((max = read_line(fd, temp_buf, sizeof(temp_buf))) > 0)
- {
- /* user abort */
- if (action_userabort(TIMEOUT_NOBLOCK))
- break;
-
- if (temp_buf[0] != '#' && temp_buf[0] != '\0')
+ /* TODO: Disabled for now, as we can't restore the playlist state yet */
+ if (false && action_userabort(TIMEOUT_NOBLOCK))
{
- int insert_pos;
-
- if (!utf8)
- {
- /* Use trackname as a temporay buffer. Note that trackname must
- * be as large as temp_buf.
- */
- max = convert_m3u(temp_buf, max, sizeof(temp_buf), trackname);
- }
-
- /* we need to format so that relative paths are correctly
- handled */
- if (format_track_path(trackname, temp_buf, sizeof(trackname),
- dir) < 0)
- {
- result = -1;
- break;
- }
-
- insert_pos = add_track_to_playlist(playlist, trackname, position,
- queue, -1);
-
- if (insert_pos < 0)
- {
- result = -1;
- break;
- }
+ err = 1;
+ goto error;
+ }
- /* After first INSERT_FIRST switch to INSERT so that all the
- rest of the tracks get inserted one after the other */
- if (position == PLAYLIST_INSERT_FIRST)
- position = PLAYLIST_INSERT;
+ /* Do not save queued files to playlist. */
+ if (playlist->indices[index] & PLAYLIST_QUEUED)
+ continue;
- count++;
+ if (get_track_filename(playlist, index, tmpbuf, tmpsize) != 0)
+ {
+ err = -2;
+ goto error;
+ }
- if ((count%PLAYLIST_DISPLAY_COUNT) == 0)
- {
- display_playlist_count(count, count_str, false);
+ /* Update seek offset so it points into the new file. */
+ playlist->indices[index] &= ~PLAYLIST_INSERT_TYPE_MASK;
+ playlist->indices[index] &= ~PLAYLIST_SEEK_MASK;
+ playlist->indices[index] |= offset;
- if (count == PLAYLIST_DISPLAY_COUNT &&
- (audio_status() & AUDIO_STATUS_PLAY) &&
- playlist->started)
- audio_flush_and_reload_tracks();
- }
+ ret = fdprintf(fd, "%s\n", tmpbuf);
+ if (ret < 0)
+ {
+ err = -3;
+ goto error;
}
- /* let the other threads work */
- yield();
+ offset += ret;
+ num_saved++;
+
+ if ((num_saved % PLAYLIST_DISPLAY_COUNT) == 0)
+ display_playlist_count(num_saved, ID2P(LANG_PLAYLIST_SAVE_COUNT), false);
}
+ display_playlist_count(num_saved, ID2P(LANG_PLAYLIST_SAVE_COUNT), true);
close(fd);
+ pl_close_playlist(playlist);
- sync_control(playlist, false);
+ pl_get_tempname(filename, tmpbuf, tmpsize);
+ if (rename(tmpbuf, filename))
+ return -4;
- cpu_boost(false);
+ strcpy(tmpbuf, filename);
+ char *dir = tmpbuf;
+ char *file = strrchr(tmpbuf, '/') + 1;
+ file[-1] = '\0';
- display_playlist_count(count, count_str, true);
+ update_playlist_filename_unlocked(playlist, dir, file);
+ return 0;
- if ((audio_status() & AUDIO_STATUS_PLAY) && playlist->started)
- audio_flush_and_reload_tracks();
+error:
+ close(fd);
+ pl_get_tempname(filename, tmpbuf, tmpsize);
+ remove(tmpbuf);
+ return err;
+}
+
+static void pl_reverse(struct playlist_info *playlist, int start, int end)
+{
+ for (; start < end; start++, end--)
+ {
+ unsigned long index_swap = playlist->indices[start];
+ playlist->indices[start] = playlist->indices[end];
+ playlist->indices[end] = index_swap;
#ifdef HAVE_DIRCACHE
- queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
+ if (playlist->dcfrefs_handle)
+ {
+ struct dircache_fileref *dcfrefs = core_get_data(playlist->dcfrefs_handle);
+ struct dircache_fileref dcf_swap = dcfrefs[start];
+ dcfrefs[start] = dcfrefs[end];
+ dcfrefs[end] = dcf_swap;
+ }
#endif
-
- return result;
+ }
}
/*
- * Delete track at specified index. If index is PLAYLIST_DELETE_CURRENT then
- * we want to delete the current playing track.
+ * Update the control file after saving the playlist under a new name.
+ * A new control file is generated, containing the new playlist filename.
+ * Queued tracks are copied to the new control file.
+ *
+ * On success, the new control file replaces the old control file.
+ * On failure, indices may be trashed and the playlist should be
+ * reloaded. This may not be possible if the playlist was overwritten.
*/
-int playlist_delete(struct playlist_info* playlist, int index)
+static int pl_save_update_control(struct playlist_info* playlist,
+ char *tmpbuf, size_t tmpsize)
{
- int result = 0;
+ int old_fd;
+ int err;
+ char c;
+ bool any_queued = false;
- if (!playlist)
- playlist = &current_playlist;
+ /* Nothing to update if we don't have any control file */
+ if (!playlist->control_created)
+ return 0;
- if (check_control(playlist) < 0)
- {
- splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
+ if (pl_get_tempname(playlist->control_filename, tmpbuf, tmpsize))
return -1;
- }
-
- if (index == PLAYLIST_DELETE_CURRENT)
- index = playlist->index;
- result = remove_track_from_playlist(playlist, index, true);
-
- if (result != -1 && (audio_status() & AUDIO_STATUS_PLAY) &&
- playlist->started)
- audio_flush_and_reload_tracks();
+ /* Close the existing control file, reopen it as read-only */
+ pl_close_control(playlist);
+ old_fd = open(playlist->control_filename, O_RDONLY);
+ if (old_fd < 0)
+ return -2;
- return result;
-}
+ /* Create new control file, pointing it at a tempfile */
+ playlist->control_fd = open(tmpbuf, O_CREAT|O_RDWR|O_TRUNC, 0666);
+ if (playlist->control_fd < 0)
+ {
+ close(old_fd);
+ return -3;
+ }
-/*
- * Move track at index to new_index. Tracks between the two are shifted
- * appropriately. Returns 0 on success and -1 on failure.
- */
-int playlist_move(struct playlist_info* playlist, int index, int new_index)
-{
- int result;
- int seek;
- bool control_file;
- bool queue;
- bool current = false;
- int r;
- struct playlist_track_info info;
- int idx_cur; /* display index of the currently playing track */
- int idx_from; /* display index of the track we're moving */
- int idx_to; /* display index of the position we're moving to */
- bool displace_current = false;
- char filename[MAX_PATH];
+ /* Write out playlist filename */
+ c = playlist->filename[playlist->dirlen-1];
+ playlist->filename[playlist->dirlen-1] = '\0';
- if (!playlist)
- playlist = &current_playlist;
+ err = update_control_unlocked(playlist, PLAYLIST_COMMAND_PLAYLIST,
+ PLAYLIST_CONTROL_FILE_VERSION, -1,
+ playlist->filename,
+ &playlist->filename[playlist->dirlen], NULL);
- if (check_control(playlist) < 0)
- {
- splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
- return -1;
- }
+ playlist->filename[playlist->dirlen-1] = c;
- if (index == new_index)
- return -1;
+ if (err <= 0)
+ return -4;
- if (index == playlist->index)
+ if (playlist->first_index > 0)
{
- /* Moving the current track */
- current = true;
+ /* rotate indices so they'll be in sync with new control file */
+ pl_reverse(playlist, 0, playlist->amount - 1);
+ pl_reverse(playlist, 0, playlist->amount - playlist->first_index - 1);
+ pl_reverse(playlist, playlist->amount - playlist->first_index, playlist->amount - 1);
+
+ playlist->index = rotate_index(playlist, playlist->index);
+ playlist->last_insert_pos = rotate_index(playlist, playlist->last_insert_pos);
+ playlist->first_index = 0;
}
- else
+
+ for (int index = 0; index < playlist->amount; ++index)
{
- /* Get display index of the currently playing track */
- if (playlist_get_track_info(playlist, playlist->index, &info) != -1)
- {
- idx_cur = info.display_index;
- /* Get display index of the position we're moving to */
- if (playlist_get_track_info(playlist, new_index, &info) != -1)
- {
- idx_to = info.display_index;
- /* Get display index of the track we're trying to move */
- if (playlist_get_track_info(playlist, index, &info) != -1)
- {
- idx_from = info.display_index;
- /* Check if moving will displace the current track.
- Displace happens when moving from after current to
- before, but also when moving from before to before
- due to the removal from the original position */
- if ( ((idx_from > idx_cur) && (idx_to <= idx_cur)) ||
- ((idx_from < idx_cur) && (idx_to < idx_cur)) )
- displace_current = true;
- }
- }
- }
- }
-
- control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
- queue = playlist->indices[index] & PLAYLIST_QUEUE_MASK;
- seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
+ /* We only need to update queued files */
+ if (!(playlist->indices[index] & PLAYLIST_QUEUED))
+ continue;
- if (get_filename(playlist, index, seek, control_file, filename,
- sizeof(filename)) < 0)
- return -1;
+ /* Read filename from old control file */
+ lseek(old_fd, playlist->indices[index] & PLAYLIST_SEEK_MASK, SEEK_SET);
+ read_line(old_fd, tmpbuf, tmpsize);
- /* We want to insert the track at the position that was specified by
- new_index. This may be different then new_index because of the
- shifting that will occur after the delete.
- We calculate this before we do the remove as it depends on the
- size of the playlist before the track removal */
- r = rotate_index(playlist, new_index);
+ /* Write it out to the new control file */
+ int seekpos;
+ err = update_control_unlocked(playlist, PLAYLIST_COMMAND_QUEUE,
+ index, playlist->last_insert_pos,
+ tmpbuf, NULL, &seekpos);
+ if (err <= 0)
+ return -5;
- /* Delete track from original position */
- result = remove_track_from_playlist(playlist, index, true);
+ /* Update seek offset for the new control file. */
+ playlist->indices[index] &= ~PLAYLIST_SEEK_MASK;
+ playlist->indices[index] |= seekpos;
+ any_queued = true;
+ }
- if (result != -1)
+ /* Preserve modified state */
+ if (playlist_modified(playlist))
{
- if (r == 0)
- /* First index */
- new_index = PLAYLIST_PREPEND;
- else if (r == playlist->amount)
- /* Append */
- new_index = PLAYLIST_INSERT_LAST;
+ if (any_queued)
+ {
+ err = update_control_unlocked(playlist, PLAYLIST_COMMAND_FLAGS,
+ PLAYLIST_FLAG_MODIFIED, 0, NULL, NULL, NULL);
+ if (err <= 0)
+ return -6;
+ }
else
- /* Calculate index of desired position */
- new_index = (r+playlist->first_index)%playlist->amount;
-
- result = add_track_to_playlist(playlist, filename, new_index, queue,
- -1);
-
- if (result != -1)
{
- if (current)
- {
- /* Moved the current track */
- switch (new_index)
- {
- case PLAYLIST_PREPEND:
- playlist->index = playlist->first_index;
- break;
- case PLAYLIST_INSERT_LAST:
- playlist->index = playlist->first_index - 1;
- if (playlist->index < 0)
- playlist->index += playlist->amount;
- break;
- default:
- playlist->index = new_index;
- break;
- }
- }
- else
- if ((displace_current) && (new_index != PLAYLIST_PREPEND))
- /* make the index point to the currently playing track */
- playlist->index++;
-
- if ((audio_status() & AUDIO_STATUS_PLAY) && playlist->started)
- audio_flush_and_reload_tracks();
+ playlist->flags &= ~PLAYLIST_FLAG_MODIFIED;
}
}
-#ifdef HAVE_DIRCACHE
- queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
-#endif
-
- return result;
-}
-
-/* shuffle currently playing playlist */
-int playlist_randomise(struct playlist_info* playlist, unsigned int seed,
- bool start_current)
-{
- int result;
-
- if (!playlist)
- playlist = &current_playlist;
-
- check_control(playlist);
-
- result = randomise_playlist(playlist, seed, start_current, true);
-
- if (result != -1 && (audio_status() & AUDIO_STATUS_PLAY) &&
- playlist->started)
- audio_flush_and_reload_tracks();
-
- return result;
-}
-
-/* sort currently playing playlist */
-int playlist_sort(struct playlist_info* playlist, bool start_current)
-{
- int result;
-
- if (!playlist)
- playlist = &current_playlist;
-
- check_control(playlist);
-
- result = sort_playlist(playlist, start_current, true);
-
- if (result != -1 && (audio_status() & AUDIO_STATUS_PLAY) &&
- playlist->started)
- audio_flush_and_reload_tracks();
-
- return result;
-}
-
-/* returns true if playlist has been modified */
-bool playlist_modified(const struct playlist_info* playlist)
-{
- if (!playlist)
- playlist = &current_playlist;
-
- if (playlist->shuffle_modified ||
- playlist->deleted ||
- playlist->num_inserted_tracks > 0)
- return true;
-
- return false;
-}
-
-/* returns index of first track in playlist */
-int playlist_get_first_index(const struct playlist_info* playlist)
-{
- if (!playlist)
- playlist = &current_playlist;
-
- return playlist->first_index;
-}
-
-/* returns shuffle seed of playlist */
-int playlist_get_seed(const struct playlist_info* playlist)
-{
- if (!playlist)
- playlist = &current_playlist;
-
- return playlist->seed;
-}
-
-/* returns number of tracks in playlist (includes queued/inserted tracks) */
-int playlist_amount_ex(const struct playlist_info* playlist)
-{
- if (!playlist)
- playlist = &current_playlist;
-
- return playlist->amount;
-}
-
-/* returns full path of playlist (minus extension) */
-char *playlist_name(const struct playlist_info* playlist, char *buf,
- int buf_size)
-{
- char *sep;
-
- if (!playlist)
- playlist = &current_playlist;
-
- strlcpy(buf, playlist->filename+playlist->dirlen, buf_size);
-
- if (!buf[0])
- return NULL;
-
- /* Remove extension */
- sep = strrchr(buf, '.');
- if (sep)
- *sep = 0;
-
- return buf;
-}
-
-/* returns the playlist filename */
-char *playlist_get_name(const struct playlist_info* playlist, char *buf,
- int buf_size)
-{
- if (!playlist)
- playlist = &current_playlist;
-
- strlcpy(buf, playlist->filename, buf_size);
-
- if (!buf[0])
- return NULL;
-
- return buf;
-}
-
-/* return size of buffer needed for playlist to initialize num_indices entries */
-size_t playlist_get_required_bufsz(struct playlist_info* playlist,
- bool include_namebuf,
- int num_indices)
-{
- size_t namebuf = 0;
-
- if (!playlist)
- playlist = &current_playlist;
-
- size_t unit_size = sizeof (*playlist->indices);
- #ifdef HAVE_DIRCACHE
- unit_size += sizeof (*playlist->dcfrefs);
- #endif
- if (include_namebuf)
- namebuf = AVERAGE_FILENAME_LENGTH * global_settings.max_files_in_dir;
+ /* Clear dirplay flag, since we now point at a playlist */
+ playlist->flags &= ~PLAYLIST_FLAG_DIRPLAY;
- return (num_indices * unit_size) + namebuf;
-}
-
-/* Fills info structure with information about track at specified index.
- Returns 0 on success and -1 on failure */
-int playlist_get_track_info(struct playlist_info* playlist, int index,
- struct playlist_track_info* info)
-{
- int seek;
- bool control_file;
-
- if (!playlist)
- playlist = &current_playlist;
-
- if (index < 0 || index >= playlist->amount)
- return -1;
-
- control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
- seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
-
- if (get_filename(playlist, index, seek, control_file, info->filename,
- sizeof(info->filename)) < 0)
- return -1;
+ /* Reset shuffle seed */
+ playlist->seed = 0;
+ if (playlist == &current_playlist)
+ global_settings.playlist_shuffle = false;
- info->attr = 0;
+ pl_close_control(playlist);
+ close(old_fd);
+ remove(playlist->control_filename);
- if (control_file)
- {
- if (playlist->indices[index] & PLAYLIST_QUEUE_MASK)
- info->attr |= PLAYLIST_ATTR_QUEUED;
- else
- info->attr |= PLAYLIST_ATTR_INSERTED;
-
- }
-
- if (playlist->indices[index] & PLAYLIST_SKIPPED)
- info->attr |= PLAYLIST_ATTR_SKIPPED;
-
- info->index = index;
- info->display_index = rotate_index(playlist, index) + 1;
+ /* TODO: Check for errors? The old control file is gone by this point... */
+ pl_get_tempname(playlist->control_filename, tmpbuf, tmpsize);
+ rename(tmpbuf, playlist->control_filename);
+ playlist->control_fd = open(playlist->control_filename, O_RDWR);
+ playlist->control_created = (playlist->control_fd >= 0);
return 0;
}
-/* save the current dynamic playlist to specified file. The
- * temp_buffer (if not NULL) is used as a scratchpad when loading indices
- * (slow if not used). */
-int playlist_save(struct playlist_info* playlist, char *filename,
- void* temp_buffer, size_t temp_buffer_size)
+int playlist_save(struct playlist_info* playlist, char *filename)
{
- int fd;
- int i, index;
- int count = 0;
- char path[MAX_PATH+1];
- char tmp_buf[MAX_PATH+1];
- int result = 0;
- int *seek_buf;
- bool reparse;
+ char save_path[MAX_PATH+1];
+ char tmpbuf[MAX_PATH+1];
ssize_t pathlen;
-
- ALIGN_BUFFER(temp_buffer, temp_buffer_size, sizeof(int));
- seek_buf = temp_buffer;
-
- /* without temp_buffer, or when it's depleted, and we overwrite the current
- * playlist then the newly saved playlist has to be reparsed. With
- * sufficient temp_buffer the indicies be remembered and added without
- * reparsing */
- reparse = temp_buffer_size == 0;
+ int rc = 0;
if (!playlist)
playlist = &current_playlist;
- if (playlist->amount <= 0)
- return -1;
-
- /* use current working directory as base for pathname */
- pathlen = format_track_path(path, filename, sizeof(path), PATH_ROOTSTR);
+ pathlen = format_track_path(save_path, filename,
+ sizeof(save_path), PATH_ROOTSTR, -1u);
if (pathlen < 0)
return -1;
- /* Use temporary pathname and overwrite/rename later */
- if (strlcat(path, "_temp", sizeof(path)) >= sizeof (path))
- return -1;
-
- /* can ignore volatile here, because core_get_data() is called later */
- char* old_buffer = (char*)playlist->buffer;
- size_t old_buffer_size = playlist->buffer_size;
-
- if (is_m3u8(path))
- {
- fd = open_utf8(path, O_CREAT|O_WRONLY|O_TRUNC);
- }
- else
- {
- /* some applications require a BOM to read the file properly */
- fd = open(path, O_CREAT|O_WRONLY|O_TRUNC, 0666);
- }
- if (fd < 0)
- {
- splash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR));
- result = -1;
- goto reset_old_buffer;
- }
-
- display_playlist_count(count, ID2P(LANG_PLAYLIST_SAVE_COUNT), false);
-
cpu_boost(true);
+ dc_thread_stop(playlist);
+ playlist_write_lock(playlist);
- index = playlist->first_index;
- for (i=0; i<playlist->amount; i++)
+ if (playlist->amount <= 0)
{
- bool control_file;
- bool queue;
- int seek;
-
- /* user abort */
- if (action_userabort(TIMEOUT_NOBLOCK))
- {
- result = -1;
- break;
- }
-
- control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
- queue = playlist->indices[index] & PLAYLIST_QUEUE_MASK;
- seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
-
- /* Don't save queued files */
- if (!queue)
- {
- if (get_filename(playlist, index, seek, control_file, tmp_buf,
- MAX_PATH+1) < 0)
- {
- result = -1;
- break;
- }
-
- if (!reparse)
- seek_buf[count] = filesize(fd);
-
- if (fdprintf(fd, "%s\n", tmp_buf) < 0)
- {
- splash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR));
- result = -1;
- break;
- }
-
- count++;
- /* when our temp buffer is depleted we have to fall
- * back to reparsing the playlist (slow) */
- if (count*sizeof(int) >= temp_buffer_size)
- reparse = true;
-
- if ((count % PLAYLIST_DISPLAY_COUNT) == 0)
- display_playlist_count(count, ID2P(LANG_PLAYLIST_SAVE_COUNT),
- false);
-
- yield();
- }
-
- index = (index+1)%playlist->amount;
+ rc = -1;
+ goto error;
}
- close(fd);
- fd = -1;
-
- display_playlist_count(count, ID2P(LANG_PLAYLIST_SAVE_COUNT), true);
-
- if (result >= 0)
+ rc = pl_save_playlist(playlist, save_path, tmpbuf, sizeof(tmpbuf));
+ if (rc < 0)
{
- strmemcpy(tmp_buf, path, pathlen); /* remove "_temp" */
-
- mutex_lock(playlist->control_mutex);
-
- if (!rename(path, tmp_buf))
- {
- fd = open_utf8(tmp_buf, O_RDONLY);
- if (fsamefile(fd, playlist->fd) > 0)
- {
- /* Replace the current playlist with the new one and update
- indices */
- close(playlist->fd);
- playlist->fd = fd;
- fd = -1;
-
- if (!reparse)
- {
- index = playlist->first_index;
- for (i=0, count=0; i<playlist->amount; i++)
- {
- if (!(playlist->indices[index] & PLAYLIST_QUEUE_MASK))
- {
- playlist->indices[index] = seek_buf[count];
- count++;
- }
- index = (index+1)%playlist->amount;
- }
- }
- else
- {
- NOTEF("reparsing current playlist (slow)");
- playlist->amount = 0;
- add_indices_to_playlist(playlist, temp_buffer,
- temp_buffer_size);
- }
-
- /* we need to recreate control because inserted tracks are
- now part of the playlist and shuffle has been invalidated */
- result = recreate_control(playlist);
- }
- }
-
- mutex_unlock(playlist->control_mutex);
+ // TODO: If we fail here, we just need to reparse the old playlist file
+ panicf("Failed to save playlist: %d", rc);
+ goto error;
}
- if (fd >= 0)
- close(fd);
+ /* User cancelled? */
+ if (rc > 0)
+ goto error;
- cpu_boost(false);
-
-reset_old_buffer:
- if (playlist->buffer_handle > 0)
- old_buffer = core_get_data(playlist->buffer_handle);
- playlist->buffer = old_buffer;
- playlist->buffer_size = old_buffer_size;
-
- return result;
-}
-
-/*
- * Search specified directory for tracks and notify via callback. May be
- * called recursively.
- */
-int playlist_directory_tracksearch(const char* dirname, bool recurse,
- int (*callback)(char*, void*),
- void* context)
-{
- char buf[MAX_PATH+1];
- int result = 0;
- int num_files = 0;
- int i;;
- struct tree_context* tc = tree_get_context();
- struct tree_cache* cache = &tc->cache;
- int old_dirfilter = *(tc->dirfilter);
-
- if (!callback)
- return -1;
-
- /* use the tree browser dircache to load files */
- *(tc->dirfilter) = SHOW_ALL;
-
- if (ft_load(tc, dirname) < 0)
+ rc = pl_save_update_control(playlist, tmpbuf, sizeof(tmpbuf));
+ if (rc)
{
- splash(HZ*2, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
- *(tc->dirfilter) = old_dirfilter;
- return -1;
+ // TODO: If we fail here, then there are two possibilities depending on
+ // whether we overwrote the old playlist file:
+ //
+ // - if it still exists, we could reparse it + old control file
+ // - otherwise, we need to selectively reload the old control file
+ // and somehow make use of the new playlist file
+ //
+ // The latter case poses other issues though, like what happens after we
+ // resume, because replaying the old control file over the new playlist
+ // won't work properly. We could simply choose to reset the control file,
+ // seeing as by this point it only contains transient data (queued tracks).
+ panicf("Failed to update control file: %d", rc);
+ goto error;
}
- num_files = tc->filesindir;
-
- /* we've overwritten the dircache so tree browser will need to be
- reloaded */
- reload_directory();
-
- for (i=0; i<num_files; i++)
- {
- /* user abort */
- if (action_userabort(TIMEOUT_NOBLOCK))
- {
- result = -1;
- break;
- }
-
- struct entry *files = core_get_data(cache->entries_handle);
- if (files[i].attr & ATTR_DIRECTORY)
- {
- if (recurse)
- {
- /* recursively add directories */
- if (path_append(buf, dirname, files[i].name, sizeof(buf))
- >= sizeof(buf))
- {
- continue;
- }
-
- result = playlist_directory_tracksearch(buf, recurse,
- callback, context);
- if (result < 0)
- break;
-
- /* we now need to reload our current directory */
- if(ft_load(tc, dirname) < 0)
- {
- result = -1;
- break;
- }
-
- num_files = tc->filesindir;
- if (!num_files)
- {
- result = -1;
- break;
- }
- }
- else
- continue;
- }
- else if ((files[i].attr & FILE_ATTR_MASK) == FILE_ATTR_AUDIO)
- {
- if (path_append(buf, dirname, files[i].name, sizeof(buf))
- >= sizeof(buf))
- {
- continue;
- }
-
- if (callback(buf, context) != 0)
- {
- result = -1;
- break;
- }
-
- /* let the other threads work */
- yield();
- }
- }
-
- /* restore dirfilter */
- *(tc->dirfilter) = old_dirfilter;
-
- return result;
+error:
+ playlist_write_unlock(playlist);
+ dc_thread_start(playlist, true);
+ cpu_boost(false);
+ return rc;
}
diff --git a/apps/playlist.h b/apps/playlist.h
index 2eca7355e4..4d814c7523 100644
--- a/apps/playlist.h
+++ b/apps/playlist.h
@@ -27,15 +27,19 @@
#include "file.h"
#include "kernel.h"
#include "metadata.h"
+#include "rbpaths.h"
+#include "chunk_alloc.h"
#define PLAYLIST_ATTR_QUEUED 0x01
#define PLAYLIST_ATTR_INSERTED 0x02
#define PLAYLIST_ATTR_SKIPPED 0x04
-#define PLAYLIST_MAX_CACHE 16
#define PLAYLIST_DISPLAY_COUNT 10
-#define DEFAULT_DYNAMIC_PLAYLIST_NAME "/dynamic.m3u8"
+#define PLAYLIST_UNTITLED_PREFIX "Playlist "
+
+#define PLAYLIST_FLAG_MODIFIED (1u << 0) /* playlist was manually modified */
+#define PLAYLIST_FLAG_DIRPLAY (1u << 1) /* enable directory skipping */
enum playlist_command {
PLAYLIST_COMMAND_PLAYLIST,
@@ -45,7 +49,10 @@ enum playlist_command {
PLAYLIST_COMMAND_SHUFFLE,
PLAYLIST_COMMAND_UNSHUFFLE,
PLAYLIST_COMMAND_RESET,
- PLAYLIST_COMMAND_COMMENT
+ PLAYLIST_COMMAND_CLEAR,
+ PLAYLIST_COMMAND_FLAGS,
+ PLAYLIST_COMMAND_COMMENT,
+ PLAYLIST_COMMAND_ERROR = PLAYLIST_COMMAND_COMMENT + 1 /* Internal */
};
enum {
@@ -55,64 +62,37 @@ enum {
PLAYLIST_INSERT_FIRST = -4,
PLAYLIST_INSERT_SHUFFLED = -5,
PLAYLIST_REPLACE = -6,
- PLAYLIST_INSERT_LAST_SHUFFLED = -7
-};
-
-enum {
- PLAYLIST_DELETE_CURRENT = -1
-};
-
-struct playlist_control_cache {
- enum playlist_command command;
- int i1;
- int i2;
- const char* s1;
- const char* s2;
- void* data;
+ PLAYLIST_INSERT_LAST_SHUFFLED = -7,
+ PLAYLIST_INSERT_LAST_ROTATED = -8
};
struct playlist_info
{
- bool current; /* current playing playlist */
- char filename[MAX_PATH]; /* path name of m3u playlist on disk */
- char control_filename[MAX_PATH]; /* full path of control file */
bool utf8; /* playlist is in .m3u8 format */
+ bool control_created; /* has control file been created? */
+ unsigned int flags; /* flags for misc. state */
int fd; /* descriptor of the open playlist file */
int control_fd; /* descriptor of the open control file */
- bool control_created; /* has control file been created? */
- int dirlen; /* Length of the path to the playlist file */
- volatile unsigned long *indices; /* array of indices */
-#ifdef HAVE_DIRCACHE
- struct dircache_fileref *dcfrefs; /* Dircache entry shortcuts */
-#endif
int max_playlist_size; /* Max number of files in playlist. Mirror of
global_settings.max_files_in_playlist */
- bool in_ram; /* playlist stored in ram (dirplay) */
- int buffer_handle; /* handle to the below buffer (-1 if non-buflib) */
+ unsigned long *indices; /* array of indices */
- volatile char *buffer;/* buffer for in-ram playlists */
- int buffer_size; /* size of buffer */
- int buffer_end_pos; /* last position where buffer was written */
int index; /* index of current playing track */
int first_index; /* index of first song in playlist */
int amount; /* number of tracks in the index */
int last_insert_pos; /* last position we inserted a track */
- int seed; /* shuffle seed */
- bool shuffle_modified; /* has playlist been shuffled with
- inserted tracks? */
- bool deleted; /* have any tracks been deleted? */
- int num_inserted_tracks; /* number of tracks inserted */
bool started; /* has playlist been started? */
-
- /* cache of playlist control commands waiting to be flushed to
- to disk */
- struct playlist_control_cache control_cache[PLAYLIST_MAX_CACHE];
- int num_cached; /* number of cached entries */
- bool pending_control_sync; /* control file needs to be synced */
-
- struct mutex *control_mutex; /* mutex for control file access */
int last_shuffled_start; /* number of tracks when insert last
shuffled command start */
+ int seed; /* shuffle seed */
+ struct mutex mutex; /* mutex for control file access */
+#ifdef HAVE_DIRCACHE
+ int dcfrefs_handle;
+#endif
+ int dirlen; /* Length of the path to the playlist file */
+ char filename[MAX_PATH]; /* path name of m3u playlist on disk */
+ /* full path of control file (with extra room for extensions) */
+ char control_filename[sizeof(PLAYLIST_CONTROL_FILE) + 8];
};
struct playlist_track_info
@@ -123,12 +103,21 @@ struct playlist_track_info
int display_index; /* index of track for display */
};
+struct playlist_insert_context {
+ struct playlist_info* playlist;
+ int position;
+ bool queue;
+ bool progress;
+ bool initialized;
+ int count;
+ int32_t count_langid;
+};
+
/* Exported functions only for current playlist. */
void playlist_init(void) INIT_ATTR;
void playlist_shutdown(void);
int playlist_create(const char *dir, const char *file);
int playlist_resume(void);
-int playlist_add(const char *filename);
int playlist_shuffle(int random_seed, int start_index);
unsigned int playlist_get_filename_crc32(struct playlist_info *playlist,
int index);
@@ -146,6 +135,7 @@ int playlist_get_display_index(void);
int playlist_amount(void);
void playlist_set_last_shuffled_start(void);
struct playlist_info *playlist_get_current(void);
+bool playlist_dynamic_only(void);
/* Exported functions for all playlists. Pass NULL for playlist_info
structure to work with current playlist. */
@@ -158,6 +148,12 @@ void playlist_close(struct playlist_info* playlist);
void playlist_sync(struct playlist_info* playlist);
int playlist_insert_track(struct playlist_info* playlist, const char *filename,
int position, bool queue, bool sync);
+int playlist_insert_context_create(struct playlist_info* playlist,
+ struct playlist_insert_context *context,
+ int position, bool queue, bool progress);
+int playlist_insert_context_add(struct playlist_insert_context *context,
+ const char *filename);
+void playlist_insert_context_release(struct playlist_insert_context *context);
int playlist_insert_directory(struct playlist_info* playlist,
const char *dirname, int position, bool queue,
bool recurse);
@@ -170,6 +166,8 @@ int playlist_randomise(struct playlist_info* playlist, unsigned int seed,
bool start_current);
int playlist_sort(struct playlist_info* playlist, bool start_current);
bool playlist_modified(const struct playlist_info* playlist);
+void playlist_set_modified(struct playlist_info* playlist, bool modified);
+bool playlist_allow_dirplay(const struct playlist_info* playlist);
int playlist_get_first_index(const struct playlist_info* playlist);
int playlist_get_seed(const struct playlist_info* playlist);
int playlist_amount_ex(const struct playlist_info* playlist);
@@ -181,8 +179,7 @@ size_t playlist_get_required_bufsz(struct playlist_info* playlist,
bool include_namebuf, int num_indices);
int playlist_get_track_info(struct playlist_info* playlist, int index,
struct playlist_track_info* info);
-int playlist_save(struct playlist_info* playlist, char *filename,
- void* temp_buffer, size_t temp_buffer_size);
+int playlist_save(struct playlist_info* playlist, char *filename);
int playlist_directory_tracksearch(const char* dirname, bool recurse,
int (*callback)(char*, void*),
void* context);
diff --git a/apps/playlist_catalog.c b/apps/playlist_catalog.c
index d100023b1b..69bcc54209 100644
--- a/apps/playlist_catalog.c
+++ b/apps/playlist_catalog.c
@@ -46,6 +46,8 @@
#include "talk.h"
#include "playlist_viewer.h"
#include "bookmark.h"
+#include "root_menu.h"
+#include "general.h"
/* Use for recursive directory search */
struct add_track_context {
@@ -53,70 +55,67 @@ struct add_track_context {
int count;
};
+enum catbrowse_status_flags{
+ CATBROWSE_NOTHING = 0,
+ CATBROWSE_CATVIEW,
+ CATBROWSE_PLAYLIST
+};
+
/* keep track of most recently used playlist */
static char most_recent_playlist[MAX_PATH];
+/* we need playlist_dir_length for easy removal of playlist dir prefix */
+static size_t playlist_dir_length;
+/* keep track of what browser(s) are current to prevent reentry */
+static int browser_status = CATBROWSE_NOTHING;
-/* directory where our playlists our stored */
-static char playlist_dir[MAX_PATH];
-static int playlist_dir_length;
-static bool playlist_dir_exists = false;
-
-/* Retrieve playlist directory from config file and verify it exists */
-static bool initialized = false;
-static int initialize_catalog(void)
+static size_t get_directory(char* dirbuf, size_t dirbuf_sz)
{
+ const char *pl_dir = PLAYLIST_CATALOG_DEFAULT_DIR;
- if (!initialized)
+ /* directory config is of the format: "dir: /path/to/dir" */
+ if (global_settings.playlist_catalog_dir[0] != '\0')
{
- bool default_dir = true;
-
- /* directory config is of the format: "dir: /path/to/dir" */
- if (global_settings.playlist_catalog_dir[0] &&
- strcmp(global_settings.playlist_catalog_dir,
- PLAYLIST_CATALOG_DEFAULT_DIR))
- {
- strcpy(playlist_dir, global_settings.playlist_catalog_dir);
- default_dir = false;
- }
-
- /* fall back to default directory if no or invalid config */
- if (default_dir)
- {
- strcpy(playlist_dir, PLAYLIST_CATALOG_DEFAULT_DIR);
- if (!dir_exists(playlist_dir))
- mkdir(playlist_dir);
- }
-
- /* remove duplicate leading '/' */
- if (playlist_dir[0] == '/' && playlist_dir[1] == '/')
- strcpy(playlist_dir, &playlist_dir[1]);
+ pl_dir = global_settings.playlist_catalog_dir;
+ }
- playlist_dir_length = strlen(playlist_dir);
+ return path_append(dirbuf, pl_dir, PA_SEP_SOFT, dirbuf_sz);
+}
- if (dir_exists(playlist_dir))
- {
- playlist_dir_exists = true;
- memset(most_recent_playlist, 0, sizeof(most_recent_playlist));
- initialized = true;
- }
+/* Retrieve playlist directory from config file and verify it exists
+ * attempts to create directory
+ * catalog dir is returned in dirbuf */
+static int initialize_catalog_buf(char* dirbuf, size_t dirbuf_sz)
+{
+ playlist_dir_length = get_directory(dirbuf, dirbuf_sz);
+ if (playlist_dir_length >= dirbuf_sz)
+ {
+ return -2;
}
- if (!playlist_dir_exists)
+ if (!dir_exists(dirbuf))
{
- if (mkdir(playlist_dir) < 0) {
- splashf(HZ*2, ID2P(LANG_CATALOG_NO_DIRECTORY), playlist_dir);
+ if (mkdir(dirbuf) < 0) {
+ splashf(HZ*2, ID2P(LANG_CATALOG_NO_DIRECTORY), dirbuf);
return -1;
}
else {
- playlist_dir_exists = true;
memset(most_recent_playlist, 0, sizeof(most_recent_playlist));
- initialized = true;
}
}
return 0;
}
+/* Retrieve playlist directory from config file and verify it exists
+ * attempts to create directory
+ * Don't inline as we want the stack to be freed ASAP */
+static NO_INLINE int initialize_catalog(void)
+{
+ char playlist_dir[MAX_PATH];
+
+ return initialize_catalog_buf(playlist_dir, sizeof(playlist_dir));
+}
+
void catalog_set_directory(const char* directory)
{
if (directory == NULL)
@@ -125,61 +124,126 @@ void catalog_set_directory(const char* directory)
}
else
{
- strcpy(global_settings.playlist_catalog_dir, directory);
+ path_append(global_settings.playlist_catalog_dir, directory,
+ PA_SEP_SOFT, sizeof(global_settings.playlist_catalog_dir));
}
- initialized = false;
initialize_catalog();
}
-const char* catalog_get_directory(void)
+void catalog_get_directory(char* dirbuf, size_t dirbuf_sz)
{
- if (initialize_catalog() == -1)
- return "";
- return playlist_dir;
+ if (initialize_catalog_buf(dirbuf, dirbuf_sz) < 0)
+ {
+ dirbuf[0] = '\0';
+ return;
+ }
}
/* Display all playlists in catalog. Selected "playlist" is returned.
- If "view" mode is set then we're not adding anything into playlist. */
-static int display_playlists(char* playlist, bool view)
+ * If status is CATBROWSE_CATVIEW then we're not adding anything into playlist */
+static int display_playlists(char* playlist, enum catbrowse_status_flags status)
{
- struct browse_context browse;
- char selected_playlist[MAX_PATH];
+ static bool reopen_last_playlist = false;
+ static int most_recent_selection = 0;
int result = -1;
-
- browse_context_init(&browse, SHOW_M3U,
- BROWSE_SELECTONLY|(view? 0: BROWSE_NO_CONTEXT_MENU),
- str(LANG_CATALOG), NOICON,
- playlist_dir, most_recent_playlist);
-
- browse.buf = selected_playlist;
- browse.bufsize = sizeof(selected_playlist);
+ char selected_playlist[MAX_PATH];
+ selected_playlist[0] = '\0';
+
+ browser_status |= status;
+ bool view = (status == CATBROWSE_CATVIEW);
+
+ struct browse_context browse = {
+ .dirfilter = SHOW_M3U,
+ .flags = BROWSE_SELECTONLY | (view ? 0 : BROWSE_NO_CONTEXT_MENU),
+ .title = str(LANG_CATALOG),
+ .icon = Icon_NOICON,
+ .root = selected_playlist,
+ .selected = &most_recent_playlist[playlist_dir_length + 1],
+ .buf = selected_playlist,
+ .bufsize = sizeof(selected_playlist),
+ };
restart:
+ /* set / restore the root directory for the browser */
+ catalog_get_directory(selected_playlist, sizeof(selected_playlist));
browse.flags &= ~BROWSE_SELECTED;
- rockbox_browse(&browse);
- if (browse.flags & BROWSE_SELECTED)
+ if (view && reopen_last_playlist)
{
- strlcpy(most_recent_playlist, selected_playlist+playlist_dir_length+1,
- sizeof(most_recent_playlist));
-
- if (view)
+ switch (playlist_viewer_ex(most_recent_playlist, &most_recent_selection))
{
-
- if (!bookmark_autoload(selected_playlist))
+ case PLAYLIST_VIEWER_OK:
+ {
+ result = 0;
+ break;
+ }
+ case PLAYLIST_VIEWER_CANCEL:
{
- if (playlist_viewer_ex(selected_playlist) == PLAYLIST_VIEWER_CANCEL)
- goto restart;
+ reopen_last_playlist = false;
+ goto restart;
}
+ case PLAYLIST_VIEWER_USB:
+ case PLAYLIST_VIEWER_MAINMENU:
+ /* Fall through */
+ default:
+ break;
+ }
+ }
+ else /* browse playlist dir */
+ {
+ int browse_ret = rockbox_browse(&browse);
+ if (browse_ret == GO_TO_WPS
+ || (view && browse_ret == GO_TO_PREVIOUS_MUSIC))
result = 0;
+ }
+
+ if (browse.flags & BROWSE_SELECTED) /* User picked a playlist */
+ {
+ if (strcmp(most_recent_playlist, selected_playlist)) /* isn't most recent one */
+ {
+ path_append(most_recent_playlist, selected_playlist,
+ PA_SEP_SOFT, sizeof(most_recent_playlist));
+ most_recent_selection = 0;
+ reopen_last_playlist = false;
}
- else
+
+ if (view) /* display playlist contents or resume bookmark */
+ {
+
+ int res = bookmark_autoload(selected_playlist);
+ if (res == BOOKMARK_DO_RESUME)
+ result = 0;
+ else if (res == BOOKMARK_CANCEL)
+ goto restart;
+ else
+ {
+ switch (playlist_viewer_ex(selected_playlist, &most_recent_selection)) {
+ case PLAYLIST_VIEWER_OK:
+ {
+ reopen_last_playlist = true;
+ result = 0;
+ break;
+ }
+ case PLAYLIST_VIEWER_CANCEL:
+ {
+ goto restart;
+ }
+ case PLAYLIST_VIEWER_USB:
+ case PLAYLIST_VIEWER_MAINMENU:
+ /* Fall through */
+ default:
+ reopen_last_playlist = true;
+ break;
+ }
+ }
+ }
+ else /* we're just adding something to a playlist */
{
result = 0;
- strlcpy(playlist, selected_playlist, MAX_PATH);
+ strmemccpy(playlist, selected_playlist, MAX_PATH);
}
}
-
+ browser_status &= ~status;
return result;
}
@@ -188,7 +252,7 @@ restart:
static void display_insert_count(int count)
{
static long talked_tick = 0;
- if(global_settings.talk_menu && count &&
+ if(global_settings.talk_menu && count &&
(talked_tick == 0 || TIME_AFTER(current_tick, talked_tick+5*HZ)))
{
talked_tick = current_tick;
@@ -229,8 +293,10 @@ static int add_to_playlist(const char* playlist, bool new_playlist,
fd = open(playlist, O_CREAT|O_WRONLY|O_APPEND, 0666);
if(fd < 0)
+ {
+ splash(HZ*2, ID2P(LANG_FAILED));
return result;
-
+ }
/* In case we're in the playlist directory */
reload_directory();
@@ -306,42 +372,85 @@ exit:
close(fd);
return result;
}
-static bool in_cat_viewer = false;
+
bool catalog_view_playlists(void)
{
- bool retval = true;
- if (in_cat_viewer)
+ if ((browser_status & CATBROWSE_CATVIEW) == CATBROWSE_CATVIEW)
return false;
- if (initialize_catalog() == -1)
+ if (initialize_catalog() < 0)
return false;
- in_cat_viewer = true;
- retval = (display_playlists(NULL, true) != -1);
- in_cat_viewer = false;
- return retval;
+ return (display_playlists(NULL, CATBROWSE_CATVIEW) >= 0);
+}
+
+static void apply_playlist_extension(char* buf, size_t buf_size)
+{
+ size_t len = strlen(buf);
+ if(len > 4 && !strcasecmp(&buf[len-4], ".m3u"))
+ strlcat(buf, "8", buf_size);
+ else if(len <= 5 || strcasecmp(&buf[len-5], ".m3u8"))
+ strlcat(buf, ".m3u8", buf_size);
+}
+
+static int remove_extension(char* path)
+{
+ char *dot = strrchr(path, '.');
+ if (dot)
+ *dot = '\0';
+
+ return 0;
+}
+
+
+bool catalog_pick_new_playlist_name(char *pl_name, size_t buf_size,
+ const char* curr_pl_name)
+{
+ char bmark_file[MAX_PATH + 7];
+ bool do_save = false;
+ while (!do_save && !remove_extension(pl_name) &&
+ !kbd_input(pl_name, buf_size - 7, NULL))
+ {
+ do_save = true;
+ apply_playlist_extension(pl_name, buf_size);
+
+ /* warn before overwriting existing (different) playlist */
+ if (!curr_pl_name || strcmp(curr_pl_name, pl_name))
+ {
+ if (file_exists(pl_name))
+ do_save = confirm_overwrite_yesno() == YESNO_YES;
+
+ if (do_save) /* delete bookmark file unrelated to new playlist */
+ {
+ snprintf(bmark_file, sizeof(bmark_file), "%s.bmark", pl_name);
+ if (file_exists(bmark_file))
+ remove(bmark_file);
+ }
+ }
+ }
+ return do_save;
}
-static bool in_add_to_playlist = false;
+static int (*ctx_add_to_playlist)(const char* playlist, bool new_playlist);
bool catalog_add_to_a_playlist(const char* sel, int sel_attr,
- bool new_playlist, char *m3u8name)
+ bool new_playlist, char *m3u8name,
+ void (*add_to_pl_cb))
{
int result;
char playlist[MAX_PATH + 7]; /* room for /.m3u8\0*/
- if (in_add_to_playlist)
+ if ((browser_status & CATBROWSE_PLAYLIST) == CATBROWSE_PLAYLIST)
return false;
- if (initialize_catalog() == -1)
+ if (initialize_catalog_buf(playlist, sizeof(playlist)) < 0)
return false;
if (new_playlist)
{
- size_t len;
if (m3u8name == NULL)
{
const char *name;
/* If sel is empty, root, or playlist directory we use 'all' */
- if (!sel || !strcmp(sel, "/") || !strcmp(sel, playlist_dir))
+ if (!sel || !strcmp(sel, "/") || !strcmp(sel, playlist))
{
sel = "/";
name = "/all";
@@ -349,34 +458,34 @@ bool catalog_add_to_a_playlist(const char* sel, int sel_attr,
else /*If sel is a folder, we prefill the text field with its name*/
name = strrchr(sel, '/');
- snprintf(playlist, MAX_PATH + 1, "%s/%s",
- playlist_dir,
- (name!=NULL && (sel_attr & ATTR_DIRECTORY))?name+1:"");
+ if (name == NULL || ((sel_attr & ATTR_DIRECTORY) != ATTR_DIRECTORY))
+ create_numbered_filename(playlist, playlist, PLAYLIST_UNTITLED_PREFIX,
+ ".m3u8", 1 IF_CNFN_NUM_(, NULL));
+ else
+ {
+ strlcat(playlist, name, sizeof(playlist));
+ apply_playlist_extension(playlist, sizeof(playlist));
+ }
}
else
- strlcpy(playlist, m3u8name, MAX_PATH);
-
- if (kbd_input(playlist, MAX_PATH, NULL))
+ strmemccpy(playlist, m3u8name, sizeof(playlist));
+
+ if (!catalog_pick_new_playlist_name(playlist, sizeof(playlist), NULL))
return false;
-
- len = strlen(playlist);
- if(len > 4 && !strcasecmp(&playlist[len-4], ".m3u"))
- strlcat(playlist, "8", sizeof(playlist));
- else if(len <= 5 || strcasecmp(&playlist[len-5], ".m3u8"))
- strlcat(playlist, ".m3u8", sizeof(playlist));
}
else
{
- in_add_to_playlist = true;
- result = display_playlists(playlist, false);
- in_add_to_playlist = false;
-
- if (result == -1)
+ if (display_playlists(playlist, CATBROWSE_PLAYLIST) < 0)
return false;
}
- if (add_to_playlist(playlist, new_playlist, sel, sel_attr) == 0)
- return true;
+ if (add_to_pl_cb != NULL)
+ {
+ ctx_add_to_playlist = add_to_pl_cb;
+ result = ctx_add_to_playlist(playlist, new_playlist);
+ }
else
- return false;
+ result = add_to_playlist(playlist, new_playlist, sel, sel_attr);
+
+ return (result == 0);
}
diff --git a/apps/playlist_catalog.h b/apps/playlist_catalog.h
index 2e317128b7..f455ef7ebc 100644
--- a/apps/playlist_catalog.h
+++ b/apps/playlist_catalog.h
@@ -22,17 +22,20 @@
#define _PLAYLIST_CATALOG_H_
/* Gets the configured playlist catalog dir */
-const char* catalog_get_directory(void);
+void catalog_get_directory(char* dirbuf, size_t dirbuf_sz);
/* Set the playlist catalog dir */
void catalog_set_directory(const char* directory);
/*
* View list of playlists in catalog.
- * ret : true if no error
+ * ret : true if item was selected
*/
bool catalog_view_playlists(void);
+bool catalog_pick_new_playlist_name(char *pl_name, size_t buf_size,
+ const char* curr_pl_name);
+
/*
* Add something to a playlist (new or select from list of playlists in
* catalog).
@@ -40,10 +43,14 @@ bool catalog_view_playlists(void);
* sel_attr : the attributes that tell what type of file we're adding
* new_playlist : whether we want to create a new playlist or add to an
* existing one.
- * m3u8name : filename to save the playlist to, NULL to show the keyboard
+ * m3u8name : NULL, or filename to show in keyboard picker (include the extension!)
+ * add_to_pl_cb : can be NULL, or a function responsible for handling the
+ * insert operations itself, in case the caller wants full
+ * control over how and what files are actually added.
* ret : true if the file was successfully added
*/
bool catalog_add_to_a_playlist(const char* sel, int sel_attr,
- bool new_playlist, char* m3u8name);
+ bool new_playlist, char* m3u8name,
+ void (*add_to_pl_cb));
#endif
diff --git a/apps/playlist_viewer.c b/apps/playlist_viewer.c
index e0e2e787cd..61a765ca9e 100644
--- a/apps/playlist_viewer.c
+++ b/apps/playlist_viewer.c
@@ -73,6 +73,17 @@ enum direction
BACKWARD
};
+enum pv_onplay_result {
+ PV_ONPLAY_USB,
+ PV_ONPLAY_USB_CLOSED,
+ PV_ONPLAY_WPS_CLOSED,
+ PV_ONPLAY_CLOSED,
+ PV_ONPLAY_ITEM_REMOVED,
+ PV_ONPLAY_CHANGED,
+ PV_ONPLAY_UNCHANGED,
+ PV_ONPLAY_SAVED,
+};
+
struct playlist_buffer
{
char *name_buffer; /* Buffer used to store track names */
@@ -92,13 +103,15 @@ struct playlist_buffer
/* Global playlist viewer settings */
struct playlist_viewer {
- struct playlist_info* playlist; /* playlist being viewed */
+ const char *title; /* Playlist Viewer list title */
+ struct playlist_info* playlist; /* Playlist being viewed */
int num_tracks; /* Number of tracks in playlist */
+ int *initial_selection; /* The initially selected track */
int current_playing_track; /* Index of current playing track */
int selected_track; /* The selected track, relative (first is 0) */
int moving_track; /* The track to move, relative (first is 0)
or -1 if nothing is currently being moved */
- int moving_playlist_index; /* Playlist-relative index (as opposed to
+ int moving_playlist_index; /* Playlist-relative index (as opposed to
viewer-relative index) of moving track */
struct playlist_buffer buffer;
};
@@ -107,6 +120,7 @@ static struct playlist_viewer viewer;
/* Used when viewing playlists on disk */
static struct playlist_info temp_playlist;
+static bool temp_playlist_init = false;
static void playlist_buffer_init(struct playlist_buffer *pb, char *names_buffer,
int names_buffer_size);
@@ -119,14 +133,17 @@ static struct playlist_entry * playlist_buffer_get_track(struct playlist_buffer
int index);
static bool playlist_viewer_init(struct playlist_viewer * viewer,
- const char* filename, bool reload);
+ const char* filename, bool reload,
+ int *most_recent_selection);
static void format_name(char* dest, const char* src);
static void format_line(const struct playlist_entry* track, char* str,
int len);
static bool update_playlist(bool force);
-static int onplay_menu(int index);
+static enum pv_onplay_result onplay_menu(int index);
+
+static void close_playlist_viewer(void);
static void playlist_buffer_init(struct playlist_buffer *pb, char *names_buffer,
int names_buffer_size)
@@ -295,7 +312,7 @@ static struct playlist_entry * playlist_buffer_get_track(struct playlist_buffer
the name_buffer is probably too small to store enough
titles to fill the screen, and preload data in the short
direction.
-
+
If this happens then scrolling performance will probably
be quite low, but it's better then having Data Abort errors */
playlist_buffer_load_entries(pb, index, FORWARD);
@@ -306,7 +323,8 @@ static struct playlist_entry * playlist_buffer_get_track(struct playlist_buffer
/* Initialize the playlist viewer. */
static bool playlist_viewer_init(struct playlist_viewer * viewer,
- const char* filename, bool reload)
+ const char* filename, bool reload,
+ int *most_recent_selection)
{
char* buffer;
size_t buffer_size;
@@ -319,7 +337,7 @@ static bool playlist_viewer_init(struct playlist_viewer * viewer,
}
if (!have_list && (playlist_amount() > 0))
{
- /*If dynamic playlist still exists, view it anyway even
+ /*If dynamic playlist still exists, view it anyway even
if playback has reached the end of the playlist */
have_list = true;
}
@@ -335,7 +353,10 @@ static bool playlist_viewer_init(struct playlist_viewer * viewer,
return false;
if (!filename)
+ {
viewer->playlist = NULL;
+ viewer->title = (char *) str(LANG_PLAYLIST);
+ }
else
{
/* Viewing playlist on disk */
@@ -344,6 +365,14 @@ static bool playlist_viewer_init(struct playlist_viewer * viewer,
char *index_buffer = NULL;
ssize_t index_buffer_size = 0;
+ /* Initialize temp playlist
+ * TODO - move this to playlist.c */
+ if (!temp_playlist_init)
+ {
+ mutex_init(&temp_playlist.mutex);
+ temp_playlist_init = true;
+ }
+
viewer->playlist = &temp_playlist;
/* Separate directory from filename */
@@ -359,6 +388,7 @@ static bool playlist_viewer_init(struct playlist_viewer * viewer,
dir = "/";
file = filename+1;
}
+ viewer->title = file;
if (is_playing)
{
@@ -387,11 +417,12 @@ static bool playlist_viewer_init(struct playlist_viewer * viewer,
viewer->moving_track = -1;
viewer->moving_playlist_index = -1;
+ viewer->initial_selection = most_recent_selection;
if (!reload)
{
if (viewer->playlist)
- viewer->selected_track = 0;
+ viewer->selected_track = most_recent_selection ? *most_recent_selection : 0;
else
viewer->selected_track = playlist_get_display_index() - 1;
}
@@ -479,25 +510,127 @@ static bool update_playlist(bool force)
return true;
}
-/* Menu of playlist commands. Invoked via ON+PLAY on main viewer screen.
- Returns -1 if USB attached, 0 if no playlist change, 1 if playlist
- changed, 2 if a track was removed from the playlist */
-static int onplay_menu(int index)
+static enum pv_onplay_result show_track_info(const struct playlist_entry *current_track)
+{
+ struct mp3entry id3;
+ bool id3_retrieval_successful = false;
+
+ if (!viewer.playlist &&
+ (playlist_get_resume_info(&viewer.current_playing_track) == current_track->index))
+ {
+ copy_mp3entry(&id3, audio_current_track()); /* retrieve id3 from RAM */
+ id3_retrieval_successful = true;
+ }
+ else
+ {
+ int fd = open(current_track->name, O_RDONLY);
+ if (fd >= 0)
+ {
+ if (get_metadata(&id3, fd, current_track->name))
+ id3_retrieval_successful = true;
+ close(fd);
+ }
+ }
+
+ return id3_retrieval_successful &&
+ browse_id3_ex(&id3, viewer.playlist, current_track->display_index,
+ viewer.num_tracks, NULL, 1) ? PV_ONPLAY_USB : PV_ONPLAY_UNCHANGED;
+}
+
+
+#ifdef HAVE_HOTKEY
+static enum pv_onplay_result open_with(const struct playlist_entry *current_track)
{
- int result, ret = 0;
+ char selected_track[MAX_PATH];
+ close_playlist_viewer(); /* don't pop activity yet – relevant for plugin_load */
+
+ strmemccpy(selected_track, current_track->name, sizeof(selected_track));
+
+ int plugin_return = filetype_list_viewers(selected_track);
+ pop_current_activity_without_refresh();
+
+ switch (plugin_return)
+ {
+ case PLUGIN_USB_CONNECTED:
+ return PV_ONPLAY_USB_CLOSED;
+ case PLUGIN_GOTO_WPS:
+ return PV_ONPLAY_WPS_CLOSED;
+ default:
+ return PV_ONPLAY_CLOSED;
+ }
+}
+#endif /* HAVE_HOTKEY */
+
+#ifdef HAVE_TAGCACHE
+static enum pv_onplay_result open_pictureflow(const struct playlist_entry *current_track)
+{
+ char selected_track[MAX_PATH];
+ close_playlist_viewer(); /* don't pop activity yet – relevant for plugin_load */
+
+ strmemccpy(selected_track, current_track->name, sizeof(selected_track));
+ int plugin_return = filetype_load_plugin((void *)"pictureflow", selected_track);
+ pop_current_activity_without_refresh();
+
+ switch (plugin_return)
+ {
+ case PLUGIN_USB_CONNECTED:
+ return PV_ONPLAY_USB_CLOSED;
+ case PLUGIN_GOTO_WPS:
+ return PV_ONPLAY_WPS_CLOSED;
+ default:
+ return PV_ONPLAY_CLOSED;
+ }
+}
+#endif
+
+static enum pv_onplay_result delete_track(int current_track_index,
+ int index, bool current_was_playing)
+{
+ playlist_delete(viewer.playlist, current_track_index);
+ if (current_was_playing)
+ {
+ if (playlist_amount_ex(viewer.playlist) <= 0)
+ audio_stop();
+ else
+ {
+ /* Start playing new track except if it's the lasttrack
+ track in the playlist and repeat mode is disabled */
+ struct playlist_entry *current_track =
+ playlist_buffer_get_track(&viewer.buffer, index);
+ if (current_track->display_index != viewer.num_tracks ||
+ global_settings.repeat_mode == REPEAT_ALL)
+ {
+ audio_play(0, 0);
+ viewer.current_playing_track = -1;
+ }
+ }
+ }
+ return PV_ONPLAY_ITEM_REMOVED;
+}
+
+/* Menu of playlist commands. Invoked via ON+PLAY on main viewer screen. */
+static enum pv_onplay_result onplay_menu(int index)
+{
+ int result, ret = PV_ONPLAY_UNCHANGED;
struct playlist_entry * current_track =
playlist_buffer_get_track(&viewer.buffer, index);
MENUITEM_STRINGLIST(menu_items, ID2P(LANG_PLAYLIST), NULL,
- ID2P(LANG_CURRENT_PLAYLIST), ID2P(LANG_CATALOG),
- ID2P(LANG_REMOVE), ID2P(LANG_MOVE), ID2P(LANG_SHUFFLE),
+ ID2P(LANG_PLAYING_NEXT), ID2P(LANG_ADD_TO_PL),
+ ID2P(LANG_REMOVE), ID2P(LANG_MOVE), ID2P(LANG_MENU_SHOW_ID3_INFO),
+ ID2P(LANG_SHUFFLE),
ID2P(LANG_SAVE),
- ID2P(LANG_PLAYLISTVIEWER_SETTINGS));
- bool current = (current_track->index == viewer.current_playing_track);
+ ID2P(LANG_PLAYLISTVIEWER_SETTINGS)
+#ifdef HAVE_TAGCACHE
+ ,ID2P(LANG_ONPLAY_PICTUREFLOW)
+#endif
+ );
+
+ bool current_was_playing = (current_track->index == viewer.current_playing_track);
result = do_menu(&menu_items, NULL, NULL, false);
if (result == MENU_ATTACHED_USB)
{
- ret = -1;
+ ret = PV_ONPLAY_USB;
}
else if (result >= 0)
{
@@ -509,58 +642,48 @@ static int onplay_menu(int index)
{
case 0:
/* playlist */
- onplay_show_playlist_menu(current_track->name);
- ret = 0;
+ onplay_show_playlist_menu(current_track->name, FILE_ATTR_AUDIO, NULL);
+ ret = PV_ONPLAY_UNCHANGED;
break;
case 1:
/* add to catalog */
- onplay_show_playlist_cat_menu(current_track->name);
- ret = 0;
+ onplay_show_playlist_cat_menu(current_track->name, FILE_ATTR_AUDIO, NULL);
+ ret = PV_ONPLAY_UNCHANGED;
break;
case 2:
- /* delete track */
- playlist_delete(viewer.playlist, current_track->index);
- if (current)
- {
- if (playlist_amount_ex(viewer.playlist) <= 0)
- audio_stop();
- else
- {
- /* Start playing new track except if it's the lasttrack
- track in the playlist and repeat mode is disabled */
- current_track =
- playlist_buffer_get_track(&viewer.buffer, index);
- if (current_track->display_index!=viewer.num_tracks ||
- global_settings.repeat_mode == REPEAT_ALL)
- {
- audio_play(0, 0);
- viewer.current_playing_track = -1;
- }
- }
- }
- ret = 2;
+ ret = delete_track(current_track->index, index, current_was_playing);
break;
case 3:
/* move track */
viewer.moving_track = index;
viewer.moving_playlist_index = current_track->index;
- ret = 0;
+ ret = PV_ONPLAY_UNCHANGED;
break;
case 4:
- /* shuffle */
- playlist_randomise(viewer.playlist, current_tick, false);
- ret = 1;
+ ret = show_track_info(current_track);
break;
case 5:
- /* save playlist */
- save_playlist_screen(viewer.playlist);
- ret = 0;
+ /* shuffle */
+ playlist_sort(viewer.playlist, !viewer.playlist);
+ playlist_randomise(viewer.playlist, current_tick, !viewer.playlist);
+ viewer.selected_track = 0;
+ ret = PV_ONPLAY_CHANGED;
break;
case 6:
+ save_playlist_screen(viewer.playlist);
+ /* playlist indices of current playlist may have changed */
+ ret = viewer.playlist ? PV_ONPLAY_UNCHANGED : PV_ONPLAY_SAVED;
+ break;
+ case 7:
/* playlist viewer settings */
result = do_menu(&viewer_settings_menu, NULL, NULL, false);
- ret = (result == MENU_ATTACHED_USB) ? -1 : 0;
+ ret = (result == MENU_ATTACHED_USB) ? PV_ONPLAY_USB : PV_ONPLAY_UNCHANGED;
+ break;
+#ifdef HAVE_TAGCACHE
+ case 8:
+ ret = open_pictureflow(current_track);
break;
+#endif
}
}
return ret;
@@ -569,7 +692,7 @@ static int onplay_menu(int index)
/* View current playlist */
enum playlist_viewer_result playlist_viewer(void)
{
- return playlist_viewer_ex(NULL);
+ return playlist_viewer_ex(NULL, NULL);
}
static int get_track_num(struct playlist_viewer *local_viewer,
@@ -669,32 +792,82 @@ static int playlist_callback_voice(int selected_item, void *data)
return 0;
}
+static void update_lists(struct gui_synclist * playlist_lists, bool init)
+{
+ if (init)
+ gui_synclist_init(playlist_lists, playlist_callback_name,
+ &viewer, false, 1, NULL);
+ gui_synclist_set_nb_items(playlist_lists, viewer.num_tracks);
+ gui_synclist_set_voice_callback(playlist_lists,
+ global_settings.talk_file?
+ &playlist_callback_voice:NULL);
+ gui_synclist_set_icon_callback(playlist_lists,
+ global_settings.playlist_viewer_icons?
+ &playlist_callback_icons:NULL);
+ gui_synclist_set_title(playlist_lists, viewer.title, Icon_Playlist);
+ gui_synclist_select_item(playlist_lists, viewer.selected_track);
+ gui_synclist_draw(playlist_lists);
+ gui_synclist_speak_item(playlist_lists);
+}
+
+static bool update_viewer_with_changes(struct gui_synclist *playlist_lists, enum pv_onplay_result res)
+{
+ bool exit = false;
+ if (res == PV_ONPLAY_CHANGED ||
+ res == PV_ONPLAY_SAVED ||
+ res == PV_ONPLAY_ITEM_REMOVED)
+ {
+ if (res != PV_ONPLAY_SAVED)
+ playlist_set_modified(viewer.playlist, true);
+
+ if (res == PV_ONPLAY_ITEM_REMOVED)
+ gui_synclist_del_item(playlist_lists);
+
+ update_playlist(true);
+
+ if (viewer.num_tracks <= 0)
+ exit = true;
+
+ if (viewer.selected_track >= viewer.num_tracks)
+ viewer.selected_track = viewer.num_tracks-1;
+ }
+
+ /* the show_icons option in the playlist viewer settings
+ * menu might have changed */
+ update_lists(playlist_lists, false);
+ return exit;
+}
+
+static bool open_playlist_viewer(const char* filename,
+ struct gui_synclist *playlist_lists,
+ bool reload, int *most_recent_selection)
+{
+ push_current_activity(ACTIVITY_PLAYLISTVIEWER);
+
+ if (!playlist_viewer_init(&viewer, filename, reload, most_recent_selection))
+ return false;
+
+ update_lists(playlist_lists, true);
+
+ return true;
+}
+
/* Main viewer function. Filename identifies playlist to be viewed. If NULL,
view current playlist. */
-enum playlist_viewer_result playlist_viewer_ex(const char* filename)
+enum playlist_viewer_result playlist_viewer_ex(const char* filename,
+ int* most_recent_selection)
{
enum playlist_viewer_result ret = PLAYLIST_VIEWER_OK;
bool exit = false; /* exit viewer */
int button;
- bool dirty = false;
struct gui_synclist playlist_lists;
- if (!playlist_viewer_init(&viewer, filename, false))
+
+ if (!open_playlist_viewer(filename, &playlist_lists, false, most_recent_selection))
+ {
+ ret = PLAYLIST_VIEWER_CANCEL;
goto exit;
+ }
- push_current_activity(ACTIVITY_PLAYLISTVIEWER);
- gui_synclist_init(&playlist_lists, playlist_callback_name,
- &viewer, false, 1, NULL);
- gui_synclist_set_voice_callback(&playlist_lists,
- global_settings.talk_file?
- &playlist_callback_voice:NULL);
- gui_synclist_set_icon_callback(&playlist_lists,
- global_settings.playlist_viewer_icons?
- &playlist_callback_icons:NULL);
- gui_synclist_set_nb_items(&playlist_lists, viewer.num_tracks);
- gui_synclist_set_title(&playlist_lists, str(LANG_PLAYLIST), Icon_Playlist);
- gui_synclist_select_item(&playlist_lists, viewer.selected_track);
- gui_synclist_draw(&playlist_lists);
- gui_synclist_speak_item(&playlist_lists);
while (!exit)
{
int track;
@@ -720,8 +893,7 @@ enum playlist_viewer_result playlist_viewer_ex(const char* filename)
}
/* Timeout so we can determine if play status has changed */
- bool res = list_do_action(CONTEXT_TREE, HZ/2,
- &playlist_lists, &button, LIST_WRAP_UNLESS_HELD);
+ bool res = list_do_action(CONTEXT_TREE, HZ/2, &playlist_lists, &button);
/* during moving, another redraw is going to be needed,
* since viewer.selected_track is updated too late (after the first draw)
* drawing the moving item needs it */
@@ -754,7 +926,8 @@ enum playlist_viewer_result playlist_viewer_ex(const char* filename)
else
{
exit = true;
- ret = PLAYLIST_VIEWER_CANCEL;
+ ret = button == ACTION_TREE_WPS ?
+ PLAYLIST_VIEWER_OK : PLAYLIST_VIEWER_CANCEL;
}
break;
}
@@ -778,10 +951,12 @@ enum playlist_viewer_result playlist_viewer_ex(const char* filename)
splashf(HZ, (unsigned char *)"%s %s", str(LANG_MOVE),
str(LANG_FAILED));
}
+
+ playlist_set_modified(viewer.playlist, true);
+
update_playlist(true);
viewer.moving_track = -1;
viewer.moving_playlist_index = -1;
- dirty = true;
}
else if (!viewer.playlist)
{
@@ -797,6 +972,7 @@ enum playlist_viewer_result playlist_viewer_ex(const char* filename)
int start_index = current_track->index;
if (!warn_on_pl_erase())
{
+ gui_synclist_set_title(&playlist_lists, playlist_lists.title, playlist_lists.title_icon);
gui_synclist_draw(&playlist_lists);
break;
}
@@ -807,8 +983,11 @@ enum playlist_viewer_result playlist_viewer_ex(const char* filename)
start_index = playlist_shuffle(current_tick, start_index);
playlist_start(start_index, 0, 0);
+ if (viewer.initial_selection)
+ *(viewer.initial_selection) = viewer.selected_track;
+
/* Our playlist is now the current list */
- if (!playlist_viewer_init(&viewer, NULL, true))
+ if (!playlist_viewer_init(&viewer, NULL, true, NULL))
goto exit;
exit = true;
}
@@ -819,35 +998,31 @@ enum playlist_viewer_result playlist_viewer_ex(const char* filename)
}
case ACTION_STD_CONTEXT:
{
- /* ON+PLAY menu */
- int ret_val;
-
- ret_val = onplay_menu(viewer.selected_track);
+ int pv_onplay_result = onplay_menu(viewer.selected_track);
- if (ret_val < 0)
+ if (pv_onplay_result == PV_ONPLAY_USB)
{
ret = PLAYLIST_VIEWER_USB;
goto exit;
}
- else if (ret_val > 0)
+ else if (pv_onplay_result == PV_ONPLAY_USB_CLOSED)
+ return PLAYLIST_VIEWER_USB;
+ else if (pv_onplay_result == PV_ONPLAY_WPS_CLOSED)
+ return PLAYLIST_VIEWER_OK;
+ else if (pv_onplay_result == PV_ONPLAY_CLOSED)
{
- /* Playlist changed */
- if (ret_val == 2)
- gui_synclist_del_item(&playlist_lists);
- update_playlist(true);
- if (viewer.num_tracks <= 0)
- exit = true;
- if (viewer.selected_track >= viewer.num_tracks)
- viewer.selected_track = viewer.num_tracks-1;
- dirty = true;
+ if (!open_playlist_viewer(filename, &playlist_lists, true, NULL))
+ {
+ ret = PLAYLIST_VIEWER_CANCEL;
+ goto exit;
+ }
+ break;
+ }
+ if (update_viewer_with_changes(&playlist_lists, pv_onplay_result))
+ {
+ exit = true;
+ ret = PLAYLIST_VIEWER_CANCEL;
}
- /* the show_icons option in the playlist viewer settings
- * menu might have changed */
- gui_synclist_set_icon_callback(&playlist_lists,
- global_settings.playlist_viewer_icons?
- &playlist_callback_icons:NULL);
- gui_synclist_draw(&playlist_lists);
- gui_synclist_speak_item(&playlist_lists);
break;
}
case ACTION_STD_MENU:
@@ -857,20 +1032,72 @@ enum playlist_viewer_result playlist_viewer_ex(const char* filename)
case ACTION_STD_QUICKSCREEN:
if (!global_settings.shortcuts_replaces_qs)
{
- quick_screen_quick(button);
+ if (quick_screen_quick(button) ==
+ QUICKSCREEN_GOTO_SHORTCUTS_MENU) /* currently disabled */
+ {
+ /* QuickScreen defers skin updates when popping its activity
+ to switch to Shortcuts Menu, so make up for that here: */
+ FOR_NB_SCREENS(i)
+ skin_update(CUSTOM_STATUSBAR, i, SKIN_REFRESH_ALL);
+ }
update_playlist(true);
- gui_synclist_set_voice_callback(&playlist_lists,
- global_settings.talk_file?
- &playlist_callback_voice:NULL);
- gui_synclist_set_icon_callback(&playlist_lists,
- global_settings.playlist_viewer_icons?
- &playlist_callback_icons:NULL);
- gui_synclist_set_title(&playlist_lists, str(LANG_PLAYLIST), Icon_Playlist);
- gui_synclist_draw(&playlist_lists);
- gui_synclist_speak_item(&playlist_lists);
- break;
+ update_lists(&playlist_lists, true);
}
+ break;
#endif
+#ifdef HAVE_HOTKEY
+ case ACTION_TREE_HOTKEY:
+ {
+ struct playlist_entry *current_track = playlist_buffer_get_track(
+ &viewer.buffer,
+ viewer.selected_track);
+ enum pv_onplay_result (*do_plugin)(const struct playlist_entry *) = NULL;
+#ifdef HAVE_TAGCACHE
+ if (global_settings.hotkey_tree == HOTKEY_PICTUREFLOW)
+ do_plugin = &open_pictureflow;
+#endif
+ if (global_settings.hotkey_tree == HOTKEY_OPEN_WITH)
+ do_plugin = &open_with;
+
+ if (do_plugin != NULL)
+ {
+ int plugin_result = do_plugin(current_track);
+
+ if (plugin_result == PV_ONPLAY_USB_CLOSED)
+ return PLAYLIST_VIEWER_USB;
+ else if (plugin_result == PV_ONPLAY_WPS_CLOSED)
+ return PLAYLIST_VIEWER_OK;
+ else if (!open_playlist_viewer(filename, &playlist_lists, true, NULL))
+ {
+ ret = PLAYLIST_VIEWER_CANCEL;
+ goto exit;
+ }
+ }
+ else if (global_settings.hotkey_tree == HOTKEY_PROPERTIES)
+ {
+ if (show_track_info(current_track) == PV_ONPLAY_USB)
+ {
+ ret = PLAYLIST_VIEWER_USB;
+ goto exit;
+ }
+ update_lists(&playlist_lists, false);
+ }
+ else if (global_settings.hotkey_tree == HOTKEY_DELETE)
+ {
+ if (update_viewer_with_changes(&playlist_lists,
+ delete_track(current_track->index,
+ viewer.selected_track,
+ (current_track->index == viewer.current_playing_track))))
+ {
+ ret = PLAYLIST_VIEWER_CANCEL;
+ exit = true;
+ }
+ }
+ else
+ onplay(current_track->name, FILE_ATTR_AUDIO, CONTEXT_STD, true);
+ break;
+ }
+#endif /* HAVE_HOTKEY */
default:
if(default_event_handler(button) == SYS_USB_CONNECTED)
{
@@ -882,15 +1109,23 @@ enum playlist_viewer_result playlist_viewer_ex(const char* filename)
}
exit:
+ pop_current_activity_without_refresh();
+ close_playlist_viewer();
+ return ret;
+}
+
+static void close_playlist_viewer(void)
+{
talk_shutup();
- pop_current_activity();
if (viewer.playlist)
{
- if(dirty && yesno_pop(ID2P(LANG_SAVE_CHANGES)))
+ if (viewer.initial_selection)
+ *(viewer.initial_selection) = viewer.selected_track;
+
+ if(playlist_modified(viewer.playlist) && yesno_pop(ID2P(LANG_SAVE_CHANGES)))
save_playlist_screen(viewer.playlist);
playlist_close(viewer.playlist);
}
- return ret;
}
static const char* playlist_search_callback_name(int selected_item, void * data,
@@ -926,7 +1161,7 @@ bool search_playlist(void)
struct gui_synclist playlist_lists;
struct playlist_track_info track;
- if (!playlist_viewer_init(&viewer, 0, false))
+ if (!playlist_viewer_init(&viewer, 0, false, NULL))
return ret;
if (kbd_input(search_str, sizeof(search_str), NULL) < 0)
return ret;
@@ -981,8 +1216,7 @@ bool search_playlist(void)
gui_synclist_speak_item(&playlist_lists);
while (!exit)
{
- if (list_do_action(CONTEXT_LIST, HZ/4,
- &playlist_lists, &button, LIST_WRAP_UNLESS_HELD))
+ if (list_do_action(CONTEXT_LIST, HZ/4, &playlist_lists, &button))
continue;
switch (button)
{
diff --git a/apps/playlist_viewer.h b/apps/playlist_viewer.h
index 0a54c1b2cd..2bb1336f4f 100644
--- a/apps/playlist_viewer.h
+++ b/apps/playlist_viewer.h
@@ -24,7 +24,8 @@
#define _PLAYLIST_VIEWER_H_
enum playlist_viewer_result playlist_viewer(void);
-enum playlist_viewer_result playlist_viewer_ex(const char* filename);
+enum playlist_viewer_result playlist_viewer_ex(const char* filename,
+ int* most_recent_selection);
bool search_playlist(void);
enum playlist_viewer_result {
diff --git a/apps/plugin.c b/apps/plugin.c
index a3970a88a7..bd93cb8b5c 100644
--- a/apps/plugin.c
+++ b/apps/plugin.c
@@ -7,7 +7,7 @@
* \/ \/ \/ \/ \/
* $Id$
*
- * Copyright (C) 2002 Björn Stenberg
+ * Copyright (C) 2002 Björn Stenberg
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -46,6 +46,7 @@
#include "pathfuncs.h"
#include "load_code.h"
#include "file.h"
+#include "core_keymap.h"
#if CONFIG_CHARGING
#include "power.h"
@@ -73,7 +74,7 @@ extern unsigned char pluginbuf[];
/* for actual plugins only, not for codecs */
static int plugin_size = 0;
-static bool (*pfn_tsr_exit)(bool reenter) = NULL; /* TSR exit callback */
+static int (*pfn_tsr_exit)(bool reenter) = NULL; /* TSR exit callback */
static char current_plugin[MAX_PATH];
/* NULL if no plugin is loaded, otherwise the handle that lc_open() returned */
static void *current_plugin_handle;
@@ -82,7 +83,7 @@ char *plugin_get_current_filename(void);
static void* plugin_get_audio_buffer(size_t *buffer_size);
static void plugin_release_audio_buffer(void);
-static void plugin_tsr(bool (*exit_callback)(bool));
+static void plugin_tsr(int (*exit_callback)(bool));
#ifdef HAVE_PLUGIN_CHECK_OPEN_CLOSE
/* File handle leak prophylaxis */
@@ -178,10 +179,12 @@ static const struct plugin_api rockbox_api = {
&global_settings,
&global_status,
language_strings,
-
+ &core_bitmaps[0],
/* lcd */
splash,
splashf,
+ splash_progress,
+ splash_progress_set_delay,
#ifdef HAVE_LCD_CONTRAST
lcd_set_contrast,
#endif
@@ -340,20 +343,23 @@ static const struct plugin_api rockbox_api = {
gui_synclist_select_item,
gui_synclist_add_item,
gui_synclist_del_item,
- gui_synclist_limit_scroll,
gui_synclist_do_button,
gui_synclist_set_title,
gui_syncyesno_run,
simplelist_info_init,
simplelist_show_list,
+ yesno_pop,
/* action handling */
+ list_do_action,
get_custom_action,
get_action,
#ifdef HAVE_TOUCHSCREEN
action_get_touchscreen_press,
+ action_get_touchscreen_press_in_vp,
#endif
action_userabort,
+ core_set_keyremap,
/* button */
button_get,
@@ -413,6 +419,7 @@ static const struct plugin_api rockbox_api = {
FS_PREFIX(file_exists),
strip_extension,
crc_32,
+ crc_32r,
filetype_get_attr,
/* dir */
@@ -425,21 +432,24 @@ static const struct plugin_api rockbox_api = {
dir_get_info,
/* browsing */
- browse_context_init,
rockbox_browse,
tree_get_context,
tree_get_entries,
tree_get_entry_at,
set_current_file,
set_dirfilter,
+ onplay_show_playlist_menu,
+ onplay_show_playlist_cat_menu,
+ browse_id3,
/* talking */
talk_id,
+ talk_idarray,
talk_file,
talk_file_or_spell,
talk_dir_or_spell,
talk_number,
- talk_value,
+ talk_value_decimal,
talk_spell,
talk_time,
talk_date,
@@ -515,6 +525,7 @@ static const struct plugin_api rockbox_api = {
queue_wait,
queue_send,
queue_reply,
+ queue_remove_from_head,
#ifdef RB_PROFILE
profile_thread,
@@ -537,6 +548,7 @@ static const struct plugin_api rockbox_api = {
/* strings and memory */
snprintf,
vsnprintf,
+ vuprintf,
strcpy,
strlcpy,
strlen,
@@ -559,6 +571,7 @@ static const struct plugin_api rockbox_api = {
memcmp,
strcasestr,
strtok_r,
+ output_dyn_value,
/* unicode stuff */
utf8decode,
iso_decode,
@@ -579,9 +592,9 @@ static const struct plugin_api rockbox_api = {
buflib_free,
buflib_shrink,
buflib_get_data,
- buflib_get_name,
/* sound */
+ adjust_volume,
sound_set,
sound_current, /*stub*/
sound_default,
@@ -593,6 +606,7 @@ static const struct plugin_api rockbox_api = {
sound_enum_hw_eq_band_setting,
#endif
#if defined (HAVE_PITCHCONTROL)
+ sound_get_pitch,
sound_set_pitch,
#endif
&audio_master_sampr_list[0],
@@ -622,7 +636,10 @@ static const struct plugin_api rockbox_api = {
dsp_eq_enable,
dsp_dither_enable,
#ifdef HAVE_PITCHCONTROL
+ dsp_get_timestretch,
dsp_set_timestretch,
+ dsp_timestretch_enable,
+ dsp_timestretch_available,
#endif
dsp_configure,
dsp_get_config,
@@ -641,6 +658,7 @@ static const struct plugin_api rockbox_api = {
mixer_get_frequency,
pcmbuf_fade,
+ pcmbuf_set_low_latency,
system_sound_play,
keyclick_click,
@@ -657,10 +675,14 @@ static const struct plugin_api rockbox_api = {
tagcache_retrieve,
tagcache_search_finish,
tagcache_get_numeric,
-#if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE)
+ tagcache_get_stat,
+#if defined(HAVE_TC_RAMCACHE)
+ tagcache_is_in_ram,
+#if defined(HAVE_DIRCACHE)
tagcache_fill_tags,
#endif
- tagcache_get_stat,
+#endif
+ tagtree_subentries_do_action,
#endif /* HAVE_TAGCACHE */
#ifdef HAVE_ALBUMART
@@ -669,17 +691,21 @@ static const struct plugin_api rockbox_api = {
/* playback control */
playlist_get_current,
+ playlist_get_resume_info,
+ playlist_get_track_info,
playlist_amount,
playlist_resume,
playlist_resume_track,
+ playlist_set_modified,
playlist_start,
- playlist_add,
playlist_sync,
playlist_remove_all_tracks,
playlist_create,
playlist_insert_track,
playlist_insert_directory,
+ playlist_insert_playlist,
playlist_shuffle,
+ warn_on_pl_erase,
audio_play,
audio_stop,
audio_pause,
@@ -692,6 +718,9 @@ static const struct plugin_api rockbox_api = {
audio_current_track,
audio_flush_and_reload_tracks,
audio_get_file_pos,
+#ifdef PLUGIN_USE_IRAM
+ audio_hard_stop,
+#endif
/* menu */
root_menu_get_options,
@@ -719,6 +748,7 @@ static const struct plugin_api rockbox_api = {
battery_level_safe,
battery_time,
battery_voltage,
+ battery_current,
#if CONFIG_CHARGING
charger_inserted,
# if CONFIG_CHARGING >= CHARGING_MONITOR
@@ -735,6 +765,7 @@ static const struct plugin_api rockbox_api = {
#if (CONFIG_PLATFORM & PLATFORM_NATIVE)
__errno,
#endif
+ led,
srand,
rand,
(void *)qsort,
@@ -744,7 +775,7 @@ static const struct plugin_api rockbox_api = {
#if CONFIG_RTC
mktime,
#endif
-
+ format_time_auto,
#if defined(DEBUG) || defined(SIMULATOR)
debugf,
#endif
@@ -767,7 +798,6 @@ static const struct plugin_api rockbox_api = {
read_jpeg_fd,
#endif
screen_dump_set_hook,
- show_logo,
#ifdef HAVE_WHEEL_POSITION
wheel_status,
@@ -780,7 +810,6 @@ static const struct plugin_api rockbox_api = {
detect_flashed_ramimage,
detect_flashed_romimage,
#endif
- led,
/*plugin*/
plugin_open,
@@ -789,14 +818,25 @@ static const struct plugin_api rockbox_api = {
plugin_release_audio_buffer, /* defined in plugin.c */
plugin_tsr, /* defined in plugin.c */
plugin_get_current_filename,
-#ifdef PLUGIN_USE_IRAM
- audio_hard_stop,
+ plugin_reserve_buffer,
+
+ /* reboot and poweroff */
+ sys_poweroff,
+ sys_reboot,
+
+ /* pathfuncs */
+ fix_path_part,
+#ifdef HAVE_MULTIVOLUME
+ path_strip_volume,
#endif
- crc_32r,
/* new stuff at the end, sort into place next time
the API gets incompatible */
-
+#ifdef HAVE_TAGCACHE
+ tagcache_commit_finalize,
+#endif
+ playlist_get_first_index,
+ playlist_get_display_index,
};
static int plugin_buffer_handle;
@@ -806,23 +846,38 @@ int plugin_load(const char* plugin, const void* parameter)
{
struct plugin_header *p_hdr;
struct lc_header *hdr;
+ const char * resume_plugin = NULL;
+
+ if (!plugin)
+ return PLUGIN_ERROR;
if (current_plugin_handle && pfn_tsr_exit)
{ /* if we have a resident old plugin and a callback */
- if (pfn_tsr_exit(!strcmp(current_plugin, plugin)) == false )
+ bool reenter = (strcmp(current_plugin, plugin) == 0);
+ int exit_status = pfn_tsr_exit(reenter);
+ if (exit_status == PLUGIN_TSR_CONTINUE)
{
/* not allowing another plugin to load */
return PLUGIN_OK;
}
- lc_close(current_plugin_handle);
- current_plugin_handle = pfn_tsr_exit = NULL;
- if (plugin_buffer_handle > 0)
+ else
+ {
+ lc_close(current_plugin_handle);
+ current_plugin_handle = pfn_tsr_exit = NULL;
plugin_buffer_handle = core_free(plugin_buffer_handle);
+
+ if (!reenter)
+ resume_plugin = strdupa(current_plugin);
+ else if (exit_status == PLUGIN_TSR_TERMINATE)
+ return PLUGIN_OK; /* don't even load the new plugin either */
+ }
}
- splash(0, ID2P(LANG_WAIT));
+#ifdef HAVE_DISK_STORAGE
+ if (!storage_disk_is_active())
+ splash(0, ID2P(LANG_WAIT));
+#endif
strcpy(current_plugin, plugin);
-
current_plugin_handle = lc_open(plugin, pluginbuf, PLUGIN_BUFFER_SIZE);
if (current_plugin_handle == NULL) {
splashf(HZ*2, str(LANG_PLUGIN_CANT_OPEN), plugin);
@@ -830,10 +885,8 @@ int plugin_load(const char* plugin, const void* parameter)
}
p_hdr = lc_get_header(current_plugin_handle);
-
hdr = p_hdr ? &p_hdr->lc_hdr : NULL;
-
if (hdr == NULL
|| hdr->magic != PLUGIN_MAGIC
|| hdr->target_id != TARGET_ID
@@ -847,13 +900,15 @@ int plugin_load(const char* plugin, const void* parameter)
splash(HZ*2, ID2P(LANG_PLUGIN_WRONG_MODEL));
return -1;
}
- if (hdr->api_version > PLUGIN_API_VERSION
- || hdr->api_version < PLUGIN_MIN_API_VERSION)
+
+ if (hdr->api_version != PLUGIN_API_VERSION ||
+ p_hdr->api_size > sizeof(struct plugin_api))
{
lc_close(current_plugin_handle);
splash(HZ*2, ID2P(LANG_PLUGIN_WRONG_VERSION));
return -1;
}
+
#if (CONFIG_PLATFORM & PLATFORM_NATIVE)
/* tlsf crashes observed on arm with 0x4 aligned addresses */
plugin_size = ALIGN_UP(hdr->end_addr - pluginbuf, 0x8);
@@ -864,14 +919,16 @@ int plugin_load(const char* plugin, const void* parameter)
*(p_hdr->api) = &rockbox_api;
lcd_set_viewport(NULL);
lcd_clear_display();
- lcd_update();
#ifdef HAVE_REMOTE_LCD
lcd_remote_set_viewport(NULL);
lcd_remote_clear_display();
lcd_remote_update();
#endif
- push_current_activity(ACTIVITY_PLUGIN);
+ if (get_current_activity() == ACTIVITY_WPS)
+ push_activity_without_refresh(ACTIVITY_PLUGIN);
+ else
+ push_current_activity(ACTIVITY_PLUGIN);
/* some plugins assume the entry cache doesn't move and save pointers to it
* they should be fixed properly instead of this lock */
tree_lock_cache(tree_get_context());
@@ -892,14 +949,19 @@ int plugin_load(const char* plugin, const void* parameter)
int rc = p_hdr->entry_point(parameter);
tree_unlock_cache(tree_get_context());
- pop_current_activity();
+
+ pop_current_activity_without_refresh();
+ if (get_current_activity() != ACTIVITY_WPS)
+ {
+ FOR_NB_SCREENS(i)
+ skin_update(CUSTOM_STATUSBAR, i, SKIN_REFRESH_ALL);
+ }
if (!pfn_tsr_exit)
{ /* close handle if plugin is no tsr one */
lc_close(current_plugin_handle);
current_plugin_handle = NULL;
- if (plugin_buffer_handle > 0)
- plugin_buffer_handle = core_free(plugin_buffer_handle);
+ plugin_buffer_handle = core_free(plugin_buffer_handle);
}
talk_buffer_set_policy(TALK_BUFFER_DEFAULT);
@@ -948,9 +1010,36 @@ int plugin_load(const char* plugin, const void* parameter)
if (rc == PLUGIN_ERROR)
splash(HZ*2, str(LANG_PLUGIN_ERROR));
+ if (resume_plugin && rc != PLUGIN_GOTO_PLUGIN && !pfn_tsr_exit)
+ {
+ /*plugin = resume_plugin;*/
+ /*parameter = rockbox_api.plugin_tsr;*/
+ return plugin_load(resume_plugin, rockbox_api.plugin_tsr);
+ }
return rc;
}
+/* For Terminate Stay Resident plugins
+ * Locks buffer_size bytes of the plugin buffer
+ * freed on plugin exit; call plugin_get_buffer first then reserve all
+ * or a portion with plugin_reserve_buffer()
+ * Returns size of buffer remaining */
+size_t plugin_reserve_buffer(size_t buffer_size)
+{
+ size_t locked_size = 0;
+
+ if (current_plugin_handle)
+ {
+ locked_size = ALIGN_UP(plugin_size + buffer_size, 0x8);
+ if (locked_size > PLUGIN_BUFFER_SIZE)
+ locked_size = PLUGIN_BUFFER_SIZE;
+
+ plugin_size = locked_size;
+ }
+
+ return (PLUGIN_BUFFER_SIZE - locked_size);
+}
+
/* Returns a pointer to the portion of the plugin buffer that is not already
being used. If no plugin is loaded, returns the entire plugin buffer */
void* plugin_get_buffer(size_t *buffer_size)
@@ -980,14 +1069,10 @@ void* plugin_get_buffer(size_t *buffer_size)
*/
static void* plugin_get_audio_buffer(size_t *buffer_size)
{
- /* dummy ops with no callbacks, needed because by
- * default buflib buffers can be moved around which must be avoided */
- static struct buflib_callbacks dummy_ops;
if (plugin_buffer_handle <= 0)
{
- plugin_buffer_handle = core_alloc_maximum("plugin audio buf",
- &plugin_buffer_size,
- &dummy_ops);
+ plugin_buffer_handle = core_alloc_maximum(&plugin_buffer_size,
+ &buflib_ops_locked);
}
if (buffer_size)
@@ -1008,12 +1093,12 @@ static void plugin_release_audio_buffer(void)
/* The plugin wants to stay resident after leaving its main function, e.g.
runs from timer or own thread. The callback is registered to later
instruct it to free its resources before a new plugin gets loaded. */
-static void plugin_tsr(bool (*exit_callback)(bool))
+static void plugin_tsr(int (*exit_callback)(bool))
{
pfn_tsr_exit = exit_callback; /* remember the callback for later */
}
-int plugin_open(char *plugin, char *parameter)
+int plugin_open(const char *plugin, const char *parameter)
{
open_plugin_add_path(ID2P(LANG_OPEN_PLUGIN), plugin, parameter);
return PLUGIN_GOTO_PLUGIN;
diff --git a/apps/plugin.h b/apps/plugin.h
index 64ced00bfa..24eb28b2bd 100644
--- a/apps/plugin.h
+++ b/apps/plugin.h
@@ -7,7 +7,7 @@
* \/ \/ \/ \/ \/
* $Id$
*
- * Copyright (C) 2002 Björn Stenberg
+ * Copyright (C) 2002 Björn Stenberg
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -49,7 +49,8 @@
char* strncpy(char *, const char *, size_t);
void* plugin_get_buffer(size_t *buffer_size);
-int plugin_open(char *plugin, char *parameter);
+size_t plugin_reserve_buffer(size_t buffer_size);
+int plugin_open(const char *plugin, const char *parameter);
#ifndef __PCTOOL__
#include "config.h"
@@ -94,7 +95,6 @@ int plugin_open(char *plugin, char *parameter);
#include "scrollbar.h"
#include "jpeg_load.h"
#include "../recorder/bmp.h"
-#include "statusbar.h"
#include "menu.h"
#include "rbunicode.h"
#include "list.h"
@@ -103,6 +103,7 @@ int plugin_open(char *plugin, char *parameter);
#include "buflib.h"
#include "buffering.h"
#include "tagcache.h"
+#include "tagtree.h"
#include "viewport.h"
#include "ata_idle_notify.h"
#include "settings_list.h"
@@ -111,6 +112,9 @@ int plugin_open(char *plugin, char *parameter);
#include "rbpaths.h"
#include "core_alloc.h"
#include "screen_access.h"
+#include "onplay.h"
+#include "screens.h"
+#include "vuprintf.h"
#ifdef HAVE_ALBUMART
#include "albumart.h"
@@ -154,13 +158,12 @@ int plugin_open(char *plugin, char *parameter);
#define PLUGIN_MAGIC 0x526F634B /* RocK */
-/* increase this every time the api struct changes */
-#define PLUGIN_API_VERSION 244
-
-/* update this to latest version if a change to the api struct breaks
- backwards compatibility (and please take the opportunity to sort in any
- new function which are "waiting" at the end of the function table) */
-#define PLUGIN_MIN_API_VERSION 244
+/*
+ * Increment this whenever a change breaks the plugin ABI,
+ * when this happens please take the opportunity to sort in
+ * any new functions "waiting" at the end of the list.
+ */
+#define PLUGIN_API_VERSION 270
/* 239 Marks the removal of ARCHOS HWCODEC and CHARCELL */
@@ -177,6 +180,12 @@ enum plugin_status {
PLUGIN_ERROR = -1,
};
+enum plugin_tsr_status {
+ PLUGIN_TSR_CONTINUE = 0, /* TSR continues running */
+ PLUGIN_TSR_SUSPEND, /* TSR exits but will restart later */
+ PLUGIN_TSR_TERMINATE, /* TSR exits and will not be restarted */
+};
+
/* NOTE: To support backwards compatibility, only add new functions at
the end of the structure. Every time you add a new function,
remember to increase PLUGIN_API_VERSION. If you make changes to the
@@ -188,10 +197,13 @@ struct plugin_api {
struct user_settings* global_settings;
struct system_status *global_status;
unsigned char **language_strings;
+ const struct cbmp_bitmap_info_entry *core_bitmaps;
/* lcd */
void (*splash)(int ticks, const char *str);
void (*splashf)(int ticks, const char *fmt, ...) ATTRIBUTE_PRINTF(2, 3);
+ void (*splash_progress)(int current, int total, const char *fmt, ...) ATTRIBUTE_PRINTF(3, 4);
+ void (*splash_progress_set_delay)(long delay_ticks);
#ifdef HAVE_LCD_CONTRAST
void (*lcd_set_contrast)(int x);
#endif
@@ -381,10 +393,8 @@ struct plugin_api {
int item_number);
void (*gui_synclist_add_item)(struct gui_synclist * lists);
void (*gui_synclist_del_item)(struct gui_synclist * lists);
- void (*gui_synclist_limit_scroll)(struct gui_synclist * lists, bool scroll);
- bool (*gui_synclist_do_button)(struct gui_synclist * lists,
- int *action, enum list_wrap wrap);
- void (*gui_synclist_set_title)(struct gui_synclist *lists, char* title,
+ bool (*gui_synclist_do_button)(struct gui_synclist * lists, int *action);
+ void (*gui_synclist_set_title)(struct gui_synclist *lists, const char* title,
enum themable_icons icon);
enum yesno_res (*gui_syncyesno_run)(const struct text_message * main_message,
const struct text_message * yes_message,
@@ -392,15 +402,20 @@ struct plugin_api {
void (*simplelist_info_init)(struct simplelist_info *info, char* title,
int count, void* data);
bool (*simplelist_show_list)(struct simplelist_info *info);
+ bool (*yesno_pop)(const char* text);
/* action handling */
+ bool (*list_do_action)(int context, int timeout,
+ struct gui_synclist *lists, int *action);
int (*get_custom_action)(int context,int timeout,
const struct button_mapping* (*get_context_map)(int));
int (*get_action)(int context, int timeout);
#ifdef HAVE_TOUCHSCREEN
int (*action_get_touchscreen_press)(short *x, short *y);
+ int (*action_get_touchscreen_press_in_vp)(short *x1, short *y1, struct viewport *vp);
#endif
bool (*action_userabort)(int timeout);
+ int (*core_set_keyremap)(struct button_mapping* core_keymap, int count);
/* button */
long (*button_get)(bool block);
@@ -461,6 +476,7 @@ struct plugin_api {
bool (*file_exists)(const char *path);
char* (*strip_extension)(char* buffer, int buffer_size, const char *filename);
uint32_t (*crc_32)(const void *src, uint32_t len, uint32_t crc32);
+ uint32_t (*crc_32r)(const void *src, uint32_t len, uint32_t crc32);
int (*filetype_get_attr)(const char* file);
@@ -474,10 +490,6 @@ struct plugin_api {
struct dirinfo (*dir_get_info)(DIR *dirp, struct dirent *entry);
/* browsing */
- void (*browse_context_init)(struct browse_context *browse,
- int dirfilter, unsigned flags,
- char *title, enum themable_icons icon,
- const char *root, const char *selected);
int (*rockbox_browse)(struct browse_context *browse);
struct tree_context* (*tree_get_context)(void);
struct entry* (*tree_get_entries)(struct tree_context* t);
@@ -486,8 +498,16 @@ struct plugin_api {
void (*set_current_file)(const char* path);
void (*set_dirfilter)(int l_dirfilter);
+ void (*onplay_show_playlist_menu)(const char* path, int attr, void (*playlist_insert_cb));
+ void (*onplay_show_playlist_cat_menu)(const char* track_name, int attr,
+ void (*add_to_pl_cb));
+ bool (*browse_id3)(struct mp3entry *id3,
+ int playlist_display_index, int playlist_amount,
+ struct tm *modified, int track_ct);
+
/* talking */
int (*talk_id)(int32_t id, bool enqueue);
+ int (*talk_idarray)(const long *idarray, bool enqueue);
int (*talk_file)(const char *root, const char *dir, const char *file,
const char *ext, const long *prefix_ids, bool enqueue);
int (*talk_file_or_spell)(const char *dirname, const char* filename,
@@ -495,7 +515,7 @@ struct plugin_api {
int (*talk_dir_or_spell)(const char* filename,
const long *prefix_ids, bool enqueue);
int (*talk_number)(long n, bool enqueue);
- int (*talk_value)(long n, int unit, bool enqueue);
+ int (*talk_value_decimal)(long n, int unit, int decimals, bool enqueue);
int (*talk_spell)(const char* spell, bool enqueue);
void (*talk_time)(const struct tm *tm, bool enqueue);
void (*talk_date)(const struct tm *tm, bool enqueue);
@@ -582,6 +602,7 @@ struct plugin_api {
intptr_t (*queue_send)(struct event_queue *q, long id,
intptr_t data);
void (*queue_reply)(struct event_queue *q, intptr_t retval);
+ void (*queue_remove_from_head)(struct event_queue *q, long id);
#ifdef RB_PROFILE
void (*profile_thread)(void);
@@ -606,6 +627,7 @@ struct plugin_api {
int (*snprintf)(char *buf, size_t size, const char *fmt, ...)
ATTRIBUTE_PRINTF(3, 4);
int (*vsnprintf)(char *buf, size_t size, const char *fmt, va_list ap);
+ int (*vuprintf)(vuprintf_push_cb push, void *userp, const char *fmt, va_list ap);
char* (*strcpy)(char *dst, const char *src);
size_t (*strlcpy)(char *dst, const char *src, size_t length);
size_t (*strlen)(const char *str);
@@ -628,6 +650,9 @@ struct plugin_api {
int (*memcmp)(const void *s1, const void *s2, size_t n);
char *(*strcasestr) (const char* phaystack, const char* pneedle);
char* (*strtok_r)(char *ptr, const char *sep, char **end);
+ char* (*output_dyn_value)(char *buf, int buf_size, int value,
+ const unsigned char * const *units,
+ unsigned int unit_count, bool binary_scale);
/* unicode stuff */
const unsigned char* (*utf8decode)(const unsigned char *utf8, unsigned short *ucs);
unsigned char* (*iso_decode)(const unsigned char *iso, unsigned char *utf8, int cp, int count);
@@ -642,8 +667,8 @@ struct plugin_api {
size_t (*buflib_available)(struct buflib_context* ctx);
int (*buflib_alloc)(struct buflib_context* ctx, size_t size);
int (*buflib_alloc_ex)(struct buflib_context* ctx, size_t size,
- const char* name, struct buflib_callbacks *ops);
- int (*buflib_alloc_maximum)(struct buflib_context* ctx, const char* name,
+ struct buflib_callbacks *ops);
+ int (*buflib_alloc_maximum)(struct buflib_context* ctx,
size_t* size, struct buflib_callbacks *ops);
void (*buflib_buffer_in)(struct buflib_context* ctx, int size);
void* (*buflib_buffer_out)(struct buflib_context* ctx, size_t* size);
@@ -651,9 +676,9 @@ struct plugin_api {
bool (*buflib_shrink)(struct buflib_context* ctx, int handle,
void* new_start, size_t new_size);
void* (*buflib_get_data)(struct buflib_context* ctx, int handle);
- const char* (*buflib_get_name)(struct buflib_context* ctx, int handle);
/* sound */
+ void (*adjust_volume)(int steps);
void (*sound_set)(int setting, int value);
int (*sound_current)(int setting); /*stub*/
int (*sound_default)(int setting);
@@ -666,6 +691,7 @@ struct plugin_api {
unsigned int band_setting);
#endif /* AUDIOHW_HAVE_EQ */
#if defined (HAVE_PITCHCONTROL)
+ int32_t (*sound_get_pitch)(void);
void (*sound_set_pitch)(int32_t pitch);
#endif
const unsigned long *audio_master_sampr_list;
@@ -700,11 +726,14 @@ struct plugin_api {
void (*dsp_eq_enable)(bool enable);
void (*dsp_dither_enable)(bool enable);
#ifdef HAVE_PITCHCONTROL
- void (*dsp_set_timestretch)(int32_t percent);
+ int32_t (*dsp_get_timestretch)(void);
+ void (*dsp_set_timestretch)(int32_t percent);
+ void (*dsp_timestretch_enable)(bool enabled);
+ bool (*dsp_timestretch_available)(void);
#endif
intptr_t (*dsp_configure)(struct dsp_config *dsp,
unsigned int setting, intptr_t value);
- struct dsp_config * (*dsp_get_config)(enum dsp_ids id);
+ struct dsp_config * (*dsp_get_config)(unsigned int dsp_id);
void (*dsp_process)(struct dsp_config *dsp, struct dsp_buffer *src,
struct dsp_buffer *dst);
@@ -726,6 +755,7 @@ struct plugin_api {
void (*mixer_set_frequency)(unsigned int samplerate);
unsigned int (*mixer_get_frequency)(void);
void (*pcmbuf_fade)(bool fade, bool in);
+ void (*pcmbuf_set_low_latency)(bool state);
void (*system_sound_play)(enum system_sound sound);
void (*keyclick_click)(bool rawbutton, int action);
@@ -746,15 +776,19 @@ struct plugin_api {
void *buffer, long length);
bool (*tagcache_search_add_filter)(struct tagcache_search *tcs,
int tag, int seek);
- bool (*tagcache_get_next)(struct tagcache_search *tcs);
+ bool (*tagcache_get_next)(struct tagcache_search *tcs, char *buf, long size);
bool (*tagcache_retrieve)(struct tagcache_search *tcs, int idxid,
int tag, char *buf, long size);
void (*tagcache_search_finish)(struct tagcache_search *tcs);
long (*tagcache_get_numeric)(const struct tagcache_search *tcs, int tag);
-#if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE)
+ struct tagcache_stat* (*tagcache_get_stat)(void);
+#if defined(HAVE_TC_RAMCACHE)
+ bool (*tagcache_is_in_ram)(void);
+#if defined(HAVE_DIRCACHE)
bool (*tagcache_fill_tags)(struct mp3entry *id3, const char *filename);
#endif
- struct tagcache_stat* (*tagcache_get_stat)(void);
+#endif
+ bool (*tagtree_subentries_do_action)(bool (*action_cb)(const char *file_name));
#endif /* HAVE_TAGCACHE */
#ifdef HAVE_ALBUMART
@@ -764,13 +798,16 @@ struct plugin_api {
/* playback control */
struct playlist_info* (*playlist_get_current)(void);
+ int (*playlist_get_resume_info)(int *resume_index);
+ int (*playlist_get_track_info)(struct playlist_info* playlist, int index,
+ struct playlist_track_info* info);
int (*playlist_amount)(void);
int (*playlist_resume)(void);
void (*playlist_resume_track)(int start_index, unsigned int crc,
unsigned long elapsed, unsigned long offset);
+ void (*playlist_set_modified)(struct playlist_info *playlist, bool modified);
void (*playlist_start)(int start_index, unsigned long elapsed,
unsigned long offset);
- int (*playlist_add)(const char *filename);
void (*playlist_sync)(struct playlist_info* playlist);
int (*playlist_remove_all_tracks)(struct playlist_info *playlist);
int (*playlist_create)(const char *dir, const char *file);
@@ -779,7 +816,10 @@ struct plugin_api {
int (*playlist_insert_directory)(struct playlist_info* playlist,
const char *dirname, int position, bool queue,
bool recurse);
+ int (*playlist_insert_playlist)(struct playlist_info* playlist,
+ const char *filename, int position, bool queue);
int (*playlist_shuffle)(int random_seed, int start_index);
+ bool (*warn_on_pl_erase)(void);
void (*audio_play)(unsigned long elapsed, unsigned long offset);
void (*audio_stop)(void);
void (*audio_pause)(void);
@@ -792,6 +832,9 @@ struct plugin_api {
struct mp3entry* (*audio_current_track)(void);
void (*audio_flush_and_reload_tracks)(void);
int (*audio_get_file_pos)(void);
+#ifdef PLUGIN_USE_IRAM
+ void (*audio_hard_stop)(void);
+#endif
/* menu */
struct menu_table *(*root_menu_get_options)(int *nb_options);
@@ -803,7 +846,7 @@ struct plugin_api {
/* options */
const struct settings_list* (*get_settings_list)(int*count);
- const struct settings_list* (*find_setting)(const void* variable, int *id);
+ const struct settings_list* (*find_setting)(const void* variable);
int (*settings_save)(void);
bool (*option_screen)(const struct settings_list *setting,
struct viewport parent[NB_SCREENS],
@@ -836,6 +879,7 @@ struct plugin_api {
bool (*battery_level_safe)(void);
int (*battery_time)(void);
int (*battery_voltage)(void);
+ int (*battery_current)(void);
#if CONFIG_CHARGING
bool (*charger_inserted)(void);
# if CONFIG_CHARGING >= CHARGING_MONITOR
@@ -852,6 +896,7 @@ struct plugin_api {
#if (CONFIG_PLATFORM & PLATFORM_NATIVE)
int * (*__errno)(void);
#endif
+ void (*led)(bool on);
void (*srand)(unsigned int seed);
int (*rand)(void);
void (*qsort)(void *base, size_t nmemb, size_t size,
@@ -862,7 +907,8 @@ struct plugin_api {
#if CONFIG_RTC
time_t (*mktime)(struct tm *t);
#endif
-
+ const char* (*format_time_auto)(char *buffer, int buf_len, long value,
+ int unit_idx, bool supress_unit);
#if defined(DEBUG) || defined(SIMULATOR)
void (*debugf)(const char *fmt, ...) ATTRIBUTE_PRINTF(1, 2);
#endif
@@ -893,7 +939,6 @@ struct plugin_api {
int format, const struct custom_format *cformat);
#endif
void (*screen_dump_set_hook)(void (*hook)(int fh));
- int (*show_logo)(void);
#ifdef HAVE_WHEEL_POSITION
int (*wheel_status)(void);
@@ -906,24 +951,29 @@ struct plugin_api {
bool (*detect_flashed_ramimage)(void);
bool (*detect_flashed_romimage)(void);
#endif
-
- void (*led)(bool on);
-
/*plugin*/
- int (*plugin_open)(char *path, char *parameter);
+ int (*plugin_open)(const char *path, const char *parameter);
void* (*plugin_get_buffer)(size_t *buffer_size);
void* (*plugin_get_audio_buffer)(size_t *buffer_size);
void (*plugin_release_audio_buffer)(void);
- void (*plugin_tsr)(bool (*exit_callback)(bool reenter));
+ void (*plugin_tsr)(int (*exit_callback)(bool reenter));
char* (*plugin_get_current_filename)(void);
-#ifdef PLUGIN_USE_IRAM
- void (*audio_hard_stop)(void);
+ size_t (*plugin_reserve_buffer)(size_t buffer_size);
+ /* reboot and poweroff */
+ void (*sys_poweroff)(void);
+ void (*sys_reboot)(void);
+ /* pathfuncs */
+ void (*fix_path_part)(char* path, int offset, int count);
+#ifdef HAVE_MULTIVOLUME
+ int (*path_strip_volume)(const char *name, const char **nameptr, bool greedy);
#endif
- uint32_t (*crc_32r)(const void *src, uint32_t len, uint32_t crc32);
-
/* new stuff at the end, sort into place next time
the API gets incompatible */
-
+#ifdef HAVE_TAGCACHE
+ void (*tagcache_commit_finalize)(void);
+#endif
+ int (*playlist_get_first_index)(const struct playlist_info* playlist);
+ int (*playlist_get_display_index)(void);
};
/* plugin header */
@@ -931,6 +981,7 @@ struct plugin_header {
struct lc_header lc_hdr; /* must be the first */
enum plugin_status(*entry_point)(const void*);
const struct plugin_api **api;
+ size_t api_size;
};
#ifdef PLUGIN
@@ -942,14 +993,15 @@ extern unsigned char plugin_end_addr[];
const struct plugin_header __header \
__attribute__ ((section (".header")))= { \
{ PLUGIN_MAGIC, TARGET_ID, PLUGIN_API_VERSION, \
- plugin_start_addr, plugin_end_addr }, plugin__start, &rb };
+ plugin_start_addr, plugin_end_addr, }, \
+ plugin__start, &rb, sizeof(struct plugin_api) };
#else /* PLATFORM_HOSTED */
#define PLUGIN_HEADER \
const struct plugin_api *rb DATA_ATTR; \
const struct plugin_header __header \
__attribute__((visibility("default"))) = { \
- { PLUGIN_MAGIC, TARGET_ID, PLUGIN_API_VERSION, NULL, NULL }, \
- plugin__start, &rb };
+ { PLUGIN_MAGIC, TARGET_ID, PLUGIN_API_VERSION, NULL, NULL }, \
+ plugin__start, &rb, sizeof(struct plugin_api) };
#endif /* CONFIG_PLATFORM */
#endif /* PLUGIN */
diff --git a/apps/plugins/2048.c b/apps/plugins/2048.c
index 1604b73c62..2f4eb4c001 100644
--- a/apps/plugins/2048.c
+++ b/apps/plugins/2048.c
@@ -91,9 +91,16 @@ static const int BACKGROUND_Y = (BASE_Y-MIN_SPACE);
#define KEY_DOWN PLA_DOWN
#define KEY_LEFT PLA_LEFT
#define KEY_RIGHT PLA_RIGHT
-#define KEY_EXIT PLA_CANCEL
#define KEY_UNDO PLA_SELECT
+#if (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_3G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_4G_PAD)
+#define KEY_EXIT PLA_SELECT_REPEAT
+#else
+#define KEY_EXIT PLA_CANCEL
+#endif
+
/* notice how "color" is spelled :P */
#ifdef HAVE_LCD_COLOR
@@ -148,9 +155,7 @@ static inline int rand_range(int min, int max)
/* prepares for exit */
static void cleanup(void)
{
-#ifdef HAVE_BACKLIGHT
backlight_use_settings();
-#endif
}
/* returns 2 or 4 */
@@ -700,9 +705,8 @@ static void init_game(bool newgame)
max_numeral_width = rb->font_get_width(rb->font_get(WHAT_FONT), '0');
#endif
-#ifdef HAVE_BACKLIGHT
backlight_ignore_timeout();
-#endif
+
draw();
}
@@ -811,8 +815,7 @@ static int do_2048_pause_menu(void)
"Help",
"Quit without Saving",
"Quit");
- bool quit = false;
- while(!quit)
+ while(1)
{
switch(rb->do_menu(&menu, &sel, NULL, false))
{
@@ -849,9 +852,10 @@ static int do_2048_pause_menu(void)
}
case 6:
return 3;
+ default:
+ break;
}
}
- return 0;
}
static void exit_handler(void)
@@ -1017,8 +1021,7 @@ static enum plugin_status do_2048_menu(void)
"Help",
"Quit without Saving",
"Quit");
- bool quit = false;
- while(!quit)
+ while(true)
{
switch(rb->do_menu(&menu, &sel, NULL, false))
{
@@ -1065,6 +1068,7 @@ static enum plugin_status do_2048_menu(void)
case 5:
if(confirm_quit())
return PLUGIN_OK;
+ break;
case 6:
if(loaded)
save_game();
@@ -1073,7 +1077,6 @@ static enum plugin_status do_2048_menu(void)
break;
}
}
- return PLUGIN_OK;
}
/* plugin entry point */
diff --git a/apps/plugins/CATEGORIES b/apps/plugins/CATEGORIES
index d3093689f9..5320015772 100644
--- a/apps/plugins/CATEGORIES
+++ b/apps/plugins/CATEGORIES
@@ -22,6 +22,9 @@ clock,apps
codebuster,games
credits,viewers
cube,demos
+dart_scorer,apps
+db_commit,apps
+db_folder_select,viewers
demystify,demos
dice,games
dict,apps
@@ -46,7 +49,10 @@ jackpot,games
jewels,games
jpeg,viewers
keybox,apps
+keyremap,apps
lamp,apps
+lastfm_scrobbler,apps
+lastfm_scrobbler_viewer,viewers
logo,demos
lrcplayer,apps
lua,viewers
@@ -66,6 +72,7 @@ minesweeper,games
mosaique,demos
mp3_encoder,apps
mpegplayer,viewers
+multiboot_select,apps
nim,games
open_plugins,viewers
oscilloscope,demos
@@ -76,8 +83,10 @@ pegbox,games
periodic_table,apps
pictureflow,demos
pitch_detector,apps
+pitch_screen,viewers
pixel-painter,games
plasma,demos
+playing_time,apps
png,viewers
gif,viewers
pong,games
@@ -85,6 +94,7 @@ ppm,viewers
properties,viewers
quake,games
random_folder_advance_config,apps
+rb_info,demos
remote_control,apps
resistor,apps
reversi,games
@@ -167,10 +177,12 @@ test_disk,apps
test_fps,apps
test_grey,apps
test_gfx,apps
+test_kbd,apps
test_resize,apps
test_sampr,apps
test_scanrate,apps
test_touchscreen,apps
+test_usb,apps
test_viewports,apps
test_greylib_bitmap_scale,viewers
text_editor,apps
diff --git a/apps/plugins/SOURCES b/apps/plugins/SOURCES
index 910ffe4161..bf36d50a40 100644
--- a/apps/plugins/SOURCES
+++ b/apps/plugins/SOURCES
@@ -2,18 +2,28 @@
#if !defined(SIMULATOR) && (CONFIG_BATTERY_MEASURE != 0)
battery_bench.c
#endif
+#ifdef HAVE_TAGCACHE
+db_folder_select.c
+tagcache/tagcache.c
+#endif
chessclock.c
credits.c
cube.c
+dart_scorer.c
dict.c
jackpot.c
keybox.c
+keyremap.c
+lastfm_scrobbler.c
+lastfm_scrobbler_viewer.c
logo.c
lrcplayer.c
mosaique.c
main_menu_config.c
+playing_time.c
properties.c
random_folder_advance_config.c
+rb_info.c
rockblox.c
search.c
settings_dumper.c
@@ -48,6 +58,10 @@ lamp.c
pitch_detector.c
#endif
+#ifdef HAVE_PITCHCONTROL
+pitch_screen.c
+#endif
+
mp3_encoder.c
wav2wv.c
@@ -72,8 +86,7 @@ crypt_firmware.c
/* Overlays loaders */
-#if defined(HAVE_LCD_COLOR) && \
- (!defined(LCD_STRIDEFORMAT) || (LCD_STRIDEFORMAT != VERTICAL_STRIDE))
+#if defined(HAVE_LCD_COLOR) && (LCD_STRIDEFORMAT == HORIZONTAL_STRIDE)
#if (PLUGIN_BUFFER_SIZE > 0x14000) && (CONFIG_PLATFORM & (PLATFORM_NATIVE |PLATFORM_HOSTED)) && (defined(CPU_ARM) || defined(CPU_MIPS))
duke3d.c
quake.c
@@ -148,6 +161,11 @@ wormlet.c
announce_status.c
#endif
+/* can't build in the sim for some reason */
+#if defined(HAVE_MULTIBOOT) && !defined(SIMULATOR)
+multiboot_select.c
+#endif
+
/* Plugins needing the grayscale lib on low-depth LCDs */
fire.c
@@ -194,6 +212,7 @@ test_core_jpeg.c
test_disk.c
test_fps.c
test_gfx.c
+test_kbd.c
#if LCD_DEPTH < 4 && !defined(SIMULATOR)
test_scanrate.c
#endif
@@ -210,5 +229,6 @@ test_sampr.c
#ifdef HAVE_TOUCHSCREEN
test_touchscreen.c
#endif
+test_usb.c
test_viewports.c
#endif /* HAVE_TEST_PLUGINS */
diff --git a/apps/plugins/SOURCES.app_build b/apps/plugins/SOURCES.app_build
index ac9f01a896..50c79ec016 100644
--- a/apps/plugins/SOURCES.app_build
+++ b/apps/plugins/SOURCES.app_build
@@ -19,6 +19,7 @@ stopwatch.lua
#ifdef HAVE_TEST_PLUGINS /* enable in advanced build options */
+test_kbd.c
test_fps.c
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
test_boost.c
diff --git a/apps/plugins/SUBDIRS b/apps/plugins/SUBDIRS
index 8479e4b3dd..b9c18b5fd9 100644
--- a/apps/plugins/SUBDIRS
+++ b/apps/plugins/SUBDIRS
@@ -9,8 +9,7 @@ clock
#endif
/* color horizontal-stride LCDs */
-#if defined(HAVE_LCD_COLOR) && \
- (!defined(LCD_STRIDEFORMAT) || (LCD_STRIDEFORMAT != VERTICAL_STRIDE))
+#if defined(HAVE_LCD_COLOR) && (LCD_STRIDEFORMAT == HORIZONTAL_STRIDE)
xworld
/* for duke3d, wolf3d and quake */
@@ -32,6 +31,7 @@ rockboy
#if defined(HAVE_TAGCACHE)
pictureflow
+tagcache
#endif
#if PLUGIN_BUFFER_SIZE > 0x20000
diff --git a/apps/plugins/SUBDIRS.app_build b/apps/plugins/SUBDIRS.app_build
index 1fc283d6c4..fbf83f01da 100644
--- a/apps/plugins/SUBDIRS.app_build
+++ b/apps/plugins/SUBDIRS.app_build
@@ -17,6 +17,7 @@ reversi
#ifdef HAVE_TAGCACHE
pictureflow
+tagcache
#endif
/* For all the swcodec targets */
diff --git a/apps/plugins/alarmclock.c b/apps/plugins/alarmclock.c
index ecafceddc7..535097724f 100644
--- a/apps/plugins/alarmclock.c
+++ b/apps/plugins/alarmclock.c
@@ -136,14 +136,20 @@ enum plugin_status plugin_start(const void* parameter)
pause_audio();
while(!quit) {
+ FOR_NB_SCREENS(i) {
+ draw(rb->screens[i]);
+ }
button = get_button();
+#if (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_3G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_4G_PAD)
+ if (button == PLA_EXIT || button == PLA_CANCEL || button == PLA_UP)
+#else
if (button == PLA_EXIT || button == PLA_CANCEL)
+#endif
quit = true;
- FOR_NB_SCREENS(i) {
- draw(rb->screens[i]);
- }
if (waiting) {
if (rem_seconds() <= 0) {
quit = done = true;
diff --git a/apps/plugins/announce_status.c b/apps/plugins/announce_status.c
index db7b890a51..21518b4d68 100644
--- a/apps/plugins/announce_status.c
+++ b/apps/plugins/announce_status.c
@@ -27,6 +27,7 @@
****************************************************************************/
#include "plugin.h"
+#include "lib/helper.h"
#include "lib/kbd_helper.h"
#include "lib/configfile.h"
@@ -44,6 +45,8 @@
#define CFG_FILE "/VoiceTSR.cfg"
#define CFG_VER 1
+#define THREAD_STACK_SIZE 4*DEFAULT_STACK_SIZE
+
#if CONFIG_RTC
#define K_TIME "DT D1;\n\n"
#define K_DATE "DD D2;\n\n"
@@ -59,7 +62,15 @@
#define K_BATTERY "BP BM B1;\n"
#define K_SLEEP "RS R2 R3;\n"
#define K_RUNTIME "RT R1;"
-#define KEYBD_LAYOUT (K_TIME K_DATE K_TRACK_TA K_TRACK K_TRACK1 K_PLAYLIST K_BATTERY K_SLEEP K_RUNTIME)
+
+static const char keybd_layout[] =
+ K_TIME K_DATE K_TRACK_TA K_TRACK K_TRACK1 K_PLAYLIST K_BATTERY K_SLEEP K_RUNTIME;
+
+/* - each character in keybd_layout will consume one element
+ * - \n does not create a key, but it also consumes one element
+ * - the final null terminator is equivalent to \n
+ * - since sizeof includes the null terminator we don't need +1 for that. */
+static unsigned short kbd_buf[sizeof(keybd_layout)];
/****************** prototypes ******************/
void print_scroll(char* string); /* implements a scrolling screen */
@@ -86,13 +97,10 @@ enum plugin_status plugin_start(const void* parameter); /* entry */
static struct
{
bool exiting; /* signal to the thread that we want to exit */
+ bool resume;
unsigned int id; /* worker thread id */
struct event_queue queue; /* thread event queue */
- long *stack;
- ssize_t stacksize;
- void *buf;
- size_t buf_size;
-
+ long stack[THREAD_STACK_SIZE / sizeof(long)];
} gThread;
static struct
@@ -179,12 +187,13 @@ static void config_reset_voice(void)
}
/****************** helper fuctions ******************/
-
void announce(void)
{
rb->talk_force_shutup();
rb->sleep(HZ / 2);
voice_general_info(false);
+ if (rb->talk_id(VOICE_PAUSE, true) < 0)
+ rb->beep_play(800, 100, 1000);
//rb->talk_force_enqueue_next();
}
@@ -241,8 +250,7 @@ static int announce_menu_cb(int action,
struct gui_synclist *this_list)
{
(void)this_item;
- unsigned short *kbd_p = gThread.buf;
- size_t kbd_bufsz = gThread.buf_size;
+ unsigned short* kbd_p;
int selection = rb->gui_synclist_get_sel_pos(this_list);
@@ -293,7 +301,8 @@ static int announce_menu_cb(int action,
rb->splash(HZ / 2, ID2P(LANG_RESET_DONE_CLEAR));
break;
case 9: /* inspect it */
- if (!kbd_create_layout(KEYBD_LAYOUT, kbd_p, kbd_bufsz))
+ kbd_p = kbd_buf;
+ if (!kbd_create_layout(keybd_layout, kbd_p, sizeof(kbd_buf)))
kbd_p = NULL;
rb->kbd_input(gAnnounce.wps_fmt, MAX_ANNOUNCE_WPS, kbd_p);
@@ -374,7 +383,7 @@ static int settings_menu(void)
break;
case 1:
rb->set_option(rb->str(LANG_ANNOUNCE_ON),
- &gAnnounce.announce_on, INT, announce_options, 2, NULL);
+ &gAnnounce.announce_on, RB_INT, announce_options, 2, NULL);
break;
case 2:
rb->set_int(rb->str(LANG_GROUPING), "", 1,
@@ -385,7 +394,7 @@ static int settings_menu(void)
break;
case 4: /*sep*/
continue;
- case 5:
+ case 5: /* quit the plugin */
return -1;
break;
case 6:
@@ -406,6 +415,7 @@ static int settings_menu(void)
/****************** main thread + helper ******************/
void thread(void)
{
+ bool in_usb = false;
long interval;
long last_tick = *rb->current_tick; /* for 1 sec tick */
@@ -418,6 +428,15 @@ void thread(void)
{
case SYS_USB_CONNECTED:
rb->usb_acknowledge(SYS_USB_CONNECTED_ACK);
+ in_usb = true;
+ break;
+ case SYS_USB_DISCONNECTED:
+ in_usb = false;
+ /*fall through*/
+ case EV_STARTUP:
+ if (!gThread.resume)
+ rb->beep_play(1500, 100, 1000);
+ break;
case EV_EXIT:
return;
case EV_OTHINSTANCE:
@@ -425,59 +444,27 @@ void thread(void)
{
last_tick += interval;
rb->sleep(HZ / 10);
- announce();
+ if (!in_usb) announce();
}
break;
- case EV_STARTUP:
- rb->beep_play(1500, 100, 1000);
- break;
case EV_TRACKCHANGE:
rb->sleep(HZ / 10);
- announce();
+ if (!in_usb) announce();
break;
}
}
}
-void plugin_buffer_init(void)
-{
- if (gThread.buf == 0)
- {
- rb->memset(&gThread, 0, sizeof(gThread));
- gThread.buf = rb->plugin_get_buffer(&gThread.buf_size);
- ALIGN_BUFFER(gThread.buf, gThread.buf_size, sizeof(long));
- }
-}
-
void thread_create(void)
{
- /* init the worker thread */
- gThread.stacksize = gThread.buf_size;
- gThread.buf_size -= gThread.stacksize;
-
- gThread.stack = (long *) gThread.buf;
-
- ALIGN_BUFFER(gThread.stack, gThread.stacksize, sizeof(long));
-
- if (gThread.stacksize < DEFAULT_STACK_SIZE)
- {
- rb->splash(HZ*2, "Out of memory");
- gThread.exiting = true;
- rb->remove_event(PLAYBACK_EVENT_TRACK_CHANGE, playback_event_callback);
- gThread.id = UINT_MAX;
- return;
- }
-
/* put the thread's queue in the bcast list */
rb->queue_init(&gThread.queue, true);
-
- gThread.id = rb->create_thread(thread, gThread.stack, gThread.stacksize,
+ gThread.id = rb->create_thread(thread, gThread.stack, sizeof(gThread.stack),
0, "vTSR"
IF_PRIO(, PRIORITY_BACKGROUND)
IF_COP(, CPU));
rb->queue_post(&gThread.queue, EV_STARTUP, 0);
rb->yield();
-
}
void thread_quit(void)
@@ -487,23 +474,52 @@ void thread_quit(void)
rb->thread_wait(gThread.id);
/* we don't want any more events */
rb->remove_event(PLAYBACK_EVENT_TRACK_CHANGE, playback_event_callback);
+
/* remove the thread's queue from the broadcast list */
rb->queue_delete(&gThread.queue);
gThread.exiting = true;
}
}
+static bool check_user_input(void)
+{
+ int i = 0;
+ rb->button_clear_queue();
+ if (rb->button_get_w_tmo(HZ) > BUTTON_NONE)
+ {
+ while ((rb->button_get(false) & BUTTON_REL) != BUTTON_REL)
+ {
+ if (i & 1)
+ rb->beep_play(800, 100, 1000 - i * (1000 / 15));
+
+ if (++i > 15)
+ {
+ return true;
+ }
+ rb->sleep(HZ / 5);
+ }
+ }
+ return false;
+}
+
/* callback to end the TSR plugin, called before a new one gets loaded */
-static bool exit_tsr(bool reenter)
+static int exit_tsr(bool reenter)
{
if (reenter)
{
rb->queue_post(&gThread.queue, EV_OTHINSTANCE, 0);
- return false; /* dont let it start again */
+
+ /* quit the plugin if user holds a button */
+ if (check_user_input() == true)
+ {
+ if (settings_menu() < 0)
+ return PLUGIN_TSR_TERMINATE; /*kill TSR dont let it start again */
+ }
+ return PLUGIN_TSR_CONTINUE; /* dont let new plugin start*/
}
thread_quit();
- return true;
+ return PLUGIN_TSR_SUSPEND;
}
@@ -511,58 +527,35 @@ static bool exit_tsr(bool reenter)
int plugin_main(const void* parameter)
{
- (void)parameter;
- bool settings = false;
- int i = 0;
+ rb->memset(&gThread, 0, sizeof(gThread));
gAnnounce.index = 0;
gAnnounce.timeout = 0;
-
- rb->splash(HZ / 2, "Announce Status");
-
- if (configfile_load(CFG_FILE, config, gCfg_sz, CFG_VER) < 0)
+ /* Resume plugin ? */
+ if (parameter == rb->plugin_tsr)
{
- /* If the loading failed, save a new config file */
- config_set_defaults();
- configfile_save(CFG_FILE, config, gCfg_sz, CFG_VER);
-
- rb->splash(HZ, ID2P(LANG_HOLD_FOR_SETTINGS));
+ gThread.resume = true;
}
-
- if (gAnnounce.show_prompt)
+ else
{
- if (rb->mixer_channel_status(PCM_MIXER_CHAN_PLAYBACK) != CHANNEL_PLAYING)
+ rb->splash(HZ / 2, "Announce Status");
+ if (gAnnounce.show_prompt)
{
- rb->talk_id(LANG_HOLD_FOR_SETTINGS, false);
- }
- rb->splash(HZ, ID2P(LANG_HOLD_FOR_SETTINGS));
- }
-
- rb->button_clear_queue();
- if (rb->button_get_w_tmo(HZ) > BUTTON_NONE)
- {
- while ((rb->button_get(false) & BUTTON_REL) != BUTTON_REL)
- {
- if (i & 1)
- rb->beep_play(800, 100, 1000);
-
- if (++i > 15)
+ if (rb->mixer_channel_status(PCM_MIXER_CHAN_PLAYBACK) != CHANNEL_PLAYING)
{
- settings = true;
- break;
+ rb->talk_id(LANG_HOLD_FOR_SETTINGS, false);
}
- rb->sleep(HZ / 5);
+ rb->splash(HZ, ID2P(LANG_HOLD_FOR_SETTINGS));
}
- }
- plugin_buffer_init(); /* need buffer for custom keyboard layout */
- if (settings)
- {
- rb->splash(100, ID2P(LANG_SETTINGS));
- int ret = settings_menu();
- if (ret < 0)
- return 0;
+ if (check_user_input() == true)
+ {
+ rb->splash(100, ID2P(LANG_SETTINGS));
+ int ret = settings_menu();
+ if (ret < 0)
+ return 0;
+ }
}
gAnnounce.timeout = *rb->current_tick;
@@ -583,6 +576,18 @@ int plugin_main(const void* parameter)
enum plugin_status plugin_start(const void* parameter)
{
/* now go ahead and have fun! */
+ if (rb->usb_inserted() == true)
+ return PLUGIN_USB_CONNECTED;
+
+ config_set_defaults();
+ if (configfile_load(CFG_FILE, config, gCfg_sz, CFG_VER) < 0)
+ {
+ /* If the loading failed, save a new config file */
+ config_set_defaults();
+ configfile_save(CFG_FILE, config, gCfg_sz, CFG_VER);
+ rb->splash(HZ, ID2P(LANG_HOLD_FOR_SETTINGS));
+ }
+
int ret = plugin_main(parameter);
return (ret==0) ? PLUGIN_OK : PLUGIN_ERROR;
}
@@ -673,7 +678,7 @@ static unsigned char* voice_info_group(unsigned char* current_token, bool testin
/*
Sleep timer and runtime
*/
- int sleep_remaining = sleep_remaining = rb->get_sleep_timer();
+ int sleep_remaining = rb->get_sleep_timer();
int runtime;
current_token++;
@@ -681,7 +686,7 @@ static unsigned char* voice_info_group(unsigned char* current_token, bool testin
if (current_char == 'T')
{
runtime = rb->global_status->runtime;
- rb->talk_value(runtime, UNIT_TIME, true);
+ talk_val(runtime, UNIT_TIME, true);
}
/* prefix suffix connectives */
else if (current_char == '1')
@@ -692,7 +697,7 @@ static unsigned char* voice_info_group(unsigned char* current_token, bool testin
{
if (current_char == 'S')
{
- rb->talk_value(sleep_remaining, UNIT_TIME, true);
+ talk_val(sleep_remaining, UNIT_TIME, true);
}
/* prefix suffix connectives */
else if (current_char == '2')
@@ -727,15 +732,15 @@ static unsigned char* voice_info_group(unsigned char* current_token, bool testin
if (current_char == 'E')
{
- rb->talk_value(elapsed_length, UNIT_TIME, true);
+ talk_val(elapsed_length, UNIT_TIME, true);
}
else if (current_char == 'L')
{
- rb->talk_value(track_length, UNIT_TIME, true);
+ talk_val(track_length, UNIT_TIME, true);
}
else if (current_char == 'R')
{
- rb->talk_value(track_remaining, UNIT_TIME, true);
+ talk_val(track_remaining, UNIT_TIME, true);
}
else if (current_char == 'T' && id3->title)
{
@@ -820,11 +825,11 @@ static unsigned char* voice_info_group(unsigned char* current_token, bool testin
if (current_char == 'P')
{
- rb->talk_value(rb->battery_level(), UNIT_PERCENT, true);
+ talk_val(rb->battery_level(), UNIT_PERCENT, true);
}
else if (current_char == 'M')
{
- rb->talk_value(rb->battery_time() * 60, UNIT_TIME, true);
+ talk_val(rb->battery_time() * 60, UNIT_TIME, true);
}
/* prefix suffix connectives */
else if (current_char == '1')
diff --git a/apps/plugins/battery_bench.c b/apps/plugins/battery_bench.c
index 2534e3bebe..f258492363 100644
--- a/apps/plugins/battery_bench.c
+++ b/apps/plugins/battery_bench.c
@@ -30,7 +30,7 @@
#define EV_EXIT 1337
/* seems to work with 1300, but who knows... */
-#define MIN_THREAD_STACK_SIZE DEFAULT_STACK_SIZE + 0x200
+#define THREAD_STACK_SIZE 4*DEFAULT_STACK_SIZE
#if (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
(CONFIG_KEYPAD == IRIVER_H300_PAD)
@@ -234,6 +234,9 @@
#define BATTERY_ON_TXT "Play"
#define BATTERY_OFF_TXT "Power"
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+/* use touchscreen */
+
#else
#error "No keymap defined!"
#endif
@@ -265,7 +268,10 @@ static struct batt_info
* a power of 2 */
unsigned secs;
int eta;
- unsigned int voltage;
+ int voltage;
+#if CONFIG_BATTERY_MEASURE & CURRENT_MEASURE
+ int current;
+#endif
short level;
unsigned short flags;
} bat[BUF_SIZE/sizeof(struct batt_info)];
@@ -276,19 +282,18 @@ static struct batt_info
static struct
{
unsigned int id; /* worker thread id */
- long *stack;
- ssize_t stacksize;
+ long stack[THREAD_STACK_SIZE / sizeof(long)];
} gThread;
static struct event_queue thread_q SHAREDBSS_ATTR;
static bool in_usb_mode;
static unsigned int buf_idx;
-static bool exit_tsr(bool reenter)
+static int exit_tsr(bool reenter)
{
- bool is_exit;
+ int exit_status;
long button;
- (void)reenter;
+
rb->lcd_clear_display();
rb->lcd_puts_scroll(0, 0, "Batt.Bench is currently running.");
rb->lcd_puts_scroll(0, 1, "Press " BATTERY_OFF_TXT " to cancel the test");
@@ -308,16 +313,17 @@ static bool exit_tsr(bool reenter)
rb->thread_wait(gThread.id);
/* remove the thread's queue from the broadcast list */
rb->queue_delete(&thread_q);
- is_exit = true;
+ exit_status = (reenter ? PLUGIN_TSR_TERMINATE : PLUGIN_TSR_SUSPEND);
+
}
- else is_exit = false;
+ else exit_status = PLUGIN_TSR_CONTINUE;
break;
}
FOR_NB_SCREENS(idx)
rb->screens[idx]->scroll_stop();
- return is_exit;
+ return exit_status;
}
#define BIT_CHARGER 0x1
@@ -366,6 +372,9 @@ static void flush_buffer(void)
rb->fdprintf(fd,
"%02d:%02d:%02d, %05d, %03d%%, "
"%02d:%02d, %04d, "
+#if CONFIG_BATTERY_MEASURE & CURRENT_MEASURE
+ " %04d, "
+#endif
#if CONFIG_CHARGING
" %c"
#if CONFIG_CHARGING >= CHARGING_MONITOR
@@ -380,6 +389,9 @@ static void flush_buffer(void)
HMS(bat[i].secs), bat[i].secs, bat[i].level,
bat[i].eta / 60, bat[i].eta % 60,
bat[i].voltage
+#if CONFIG_BATTERY_MEASURE & CURRENT_MEASURE
+ , bat[i].current
+#endif
#if CONFIG_CHARGING
, (bat[i].flags & BIT_CHARGER) ? 'A' : '-'
#if CONFIG_CHARGING >= CHARGING_MONITOR
@@ -417,6 +429,9 @@ static void thread(void)
bat[buf_idx].level = rb->battery_level();
bat[buf_idx].eta = rb->battery_time();
bat[buf_idx].voltage = rb->battery_voltage();
+#if CONFIG_BATTERY_MEASURE & CURRENT_MEASURE
+ bat[buf_idx].current = rb->battery_current();
+#endif
#if CONFIG_CHARGING || defined(HAVE_USB_POWER)
bat[buf_idx].flags = charge_state();
#endif
@@ -448,6 +463,7 @@ static void thread(void)
in_usb_mode = false;
break;
case SYS_POWEROFF:
+ case SYS_REBOOT:
exit_reason = "power off";
exit = true;
break;
@@ -487,14 +503,19 @@ static void put_centered_str(const char* str, plcdfunc putsxy, int lcd_width, in
enum plugin_status plugin_start(const void* parameter)
{
- (void)parameter;
int button, fd;
+ bool resume = false;
bool on = false;
start_tick = *rb->current_tick;
int i;
const char *msgs[] = { "Battery Benchmark","Check file", BATTERY_LOG,
"for more info", BATTERY_ON_TXT, BATTERY_OFF_TXT " - quit" };
- rb->lcd_clear_display();
+
+ if (parameter == rb->plugin_tsr)
+ {
+ resume = true;
+ on = true;
+ }
rb->lcd_clear_display();
rb->lcd_setfont(FONT_SYSFIXED);
@@ -514,36 +535,38 @@ enum plugin_status plugin_start(const void* parameter)
rb->lcd_remote_putsxy,LCD_REMOTE_WIDTH,2);
rb->lcd_remote_update();
#endif
-
- do
+ if (!resume)
{
- button = rb->button_get(true);
- switch (button)
+ do
{
- case BATTERY_ON:
-#ifdef BATTERY_RC_ON
- case BATTERY_RC_ON:
-#endif
- on = true;
- break;
- case BATTERY_OFF:
-#ifdef BATTERY_RC_OFF
- case BATTERY_RC_OFF:
-#endif
- return PLUGIN_OK;
-
- default:
- if (rb->default_event_handler(button) == SYS_USB_CONNECTED)
- return PLUGIN_USB_CONNECTED;
- }
- }while(!on);
-
+ button = rb->button_get(true);
+ switch (button)
+ {
+ case BATTERY_ON:
+ #ifdef BATTERY_RC_ON
+ case BATTERY_RC_ON:
+ #endif
+ on = true;
+ break;
+ case BATTERY_OFF:
+ #ifdef BATTERY_RC_OFF
+ case BATTERY_RC_OFF:
+ #endif
+ return PLUGIN_OK;
+
+ default:
+ if (rb->default_event_handler(button) == SYS_USB_CONNECTED)
+ return PLUGIN_USB_CONNECTED;
+ }
+ }while(!on);
+ }
fd = rb->open(BATTERY_LOG, O_RDONLY);
if (fd < 0)
{
fd = rb->open(BATTERY_LOG, O_RDWR | O_CREAT, 0666);
if (fd >= 0)
{
+
rb->fdprintf(fd,
"# This plugin will log your battery performance in a\n"
"# file (%s) every minute.\n"
@@ -565,13 +588,21 @@ enum plugin_status plugin_start(const void* parameter)
,MODEL_NAME,rb->rbversion);
rb->fdprintf(fd, "# Battery type: %d mAh Buffer Entries: %d\n",
- rb->global_settings->battery_capacity, (int)BUF_ELEMENTS);
+#if BATTERY_CAPACITY_INC > 0
+ rb->global_settings->battery_capacity,
+#else
+ BATTERY_CAPACITY_DEFAULT,
+#endif
+ (int)BUF_ELEMENTS);
rb->fdprintf(fd, "# Rockbox has been running for %02d:%02d:%02d\n",
HMS((unsigned)start_tick/HZ));
rb->fdprintf(fd,
"# Time:, Seconds:, Level:, Time Left:, Voltage[mV]:"
+#if CONFIG_BATTERY_MEASURE & CURRENT_MEASURE
+ ", Current[mA]:"
+#endif
#if CONFIG_CHARGING
", C:"
#endif
@@ -604,25 +635,9 @@ enum plugin_status plugin_start(const void* parameter)
}
rb->memset(&gThread, 0, sizeof(gThread));
- void *buf;
- size_t buf_size;
- buf = rb->plugin_get_buffer(&buf_size);
- ALIGN_BUFFER(buf, buf_size, sizeof(long));
- rb->memset(buf, 0, buf_size);
-
- gThread.stacksize = buf_size;
- gThread.stack = (long *) buf;
-
- if (gThread.stacksize < MIN_THREAD_STACK_SIZE)
- {
- rb->splash(HZ*2, "Out of memory");
- gThread.id = UINT_MAX;
- return PLUGIN_ERROR;
- }
-
rb->queue_init(&thread_q, true); /* put the thread's queue in the bcast list */
- gThread.id = rb->create_thread(thread, gThread.stack,
- gThread.stacksize, 0, "Battery Benchmark"
+ gThread.id = rb->create_thread(thread, gThread.stack, sizeof(gThread.stack),
+ 0, "Battery Benchmark"
IF_PRIO(, PRIORITY_BACKGROUND)
IF_COP(, CPU));
diff --git a/apps/plugins/bitmaps/mono/SOURCES b/apps/plugins/bitmaps/mono/SOURCES
index e6a24b2bc5..eb00bd9e8a 100644
--- a/apps/plugins/bitmaps/mono/SOURCES
+++ b/apps/plugins/bitmaps/mono/SOURCES
@@ -19,7 +19,8 @@ bubbles_bubble.138x110x1.bmp
((LCD_WIDTH == 176) && (LCD_HEIGHT == 220))
bubbles_bubble.220x176x1.bmp
#elif ((LCD_WIDTH == 320) && (LCD_HEIGHT == 240)) || \
- ((LCD_WIDTH == 240) && (LCD_HEIGHT >= 320))
+ ((LCD_WIDTH == 240) && (LCD_HEIGHT >= 320)) || \
+ ((LCD_WIDTH == 360) && (LCD_HEIGHT == 400))
bubbles_bubble.320x240x1.bmp
#elif ((LCD_WIDTH == 640) && (LCD_HEIGHT == 480)) || \
((LCD_WIDTH == 480) && (LCD_HEIGHT == 640))
diff --git a/apps/plugins/bitmaps/native/SOURCES b/apps/plugins/bitmaps/native/SOURCES
index 48eb18dfa4..814845dc5b 100644
--- a/apps/plugins/bitmaps/native/SOURCES
+++ b/apps/plugins/bitmaps/native/SOURCES
@@ -8,7 +8,11 @@ _2048_background.224x224x24.bmp
_2048_tiles.36x36x24.bmp
_2048_background.168x168x24.bmp
#elif MIN(LCD_WIDTH, LCD_HEIGHT)>=132 || MIN(LCD_WIDTH, LCD_HEIGHT)>=128
+#if (LCD_DEPTH > 2)
_2048_tiles.26x26x24.bmp
+#else
+_2048_tiles.26x26x2.bmp
+#endif
_2048_background.121x121x24.bmp
#elif MIN(LCD_WIDTH, LCD_HEIGHT)>=110
_2048_tiles.22x22x24.bmp
@@ -158,6 +162,9 @@ jackpot_slots.30x420x1.bmp
((LCD_WIDTH >= 480) && (LCD_HEIGHT >= 640))
bubbles_emblem.640x480x16.bmp
bubbles_background.640x480x16.bmp
+#elif (LCD_WIDTH >= 360) && (LCD_HEIGHT >= 400)
+bubbles_emblem.360x400x16.bmp
+bubbles_background.360x400x16.bmp
#elif (LCD_WIDTH >= 320) && (LCD_HEIGHT >= 240)
bubbles_emblem.320x240x16.bmp
bubbles_background.320x240x16.bmp
@@ -388,7 +395,9 @@ invadrox_shield.22x16x16.bmp
invadrox_ufo.16x7x16.bmp
invadrox_ufo_explode.21x8x16.bmp
invadrox_numbers.50x7x16.bmp
-#if LCD_WIDTH == 320
+#if LCD_WIDTH == 360 && LCD_HEIGHT == 400
+invadrox_background.360x400x16.bmp
+#elif LCD_WIDTH == 320
invadrox_background.320x240x16.bmp
#elif LCD_WIDTH == 240
invadrox_background.240x320x16.bmp
@@ -457,6 +466,8 @@ jewels.220x176x16.bmp
jewels.320x240x16.bmp
#elif (LCD_WIDTH == 320) && (LCD_HEIGHT == 240)
jewels.320x240x16.bmp
+#elif (LCD_WIDTH == 360) && (LCD_HEIGHT == 400)
+jewels.360x400x16.bmp
#elif ((LCD_WIDTH == 640) && (LCD_HEIGHT == 480)) || \
((LCD_WIDTH == 480) && (LCD_HEIGHT == 640))
jewels.640x480x16.bmp
@@ -537,8 +548,7 @@ pegbox_pieces.9x7x1.bmp
#endif
/* Puzzles */
-#if defined(HAVE_LCD_COLOR) && \
- (!defined(LCD_STRIDEFORMAT) || (LCD_STRIDEFORMAT != VERTICAL_STRIDE))
+#if defined(HAVE_LCD_COLOR) && (LCD_STRIDEFORMAT == HORIZONTAL_STRIDE)
puzzles_cursor.11x16x24.bmp
#endif
@@ -546,6 +556,8 @@ puzzles_cursor.11x16x24.bmp
#if LCD_DEPTH >= 16 /* colour versions*/
#if (LCD_WIDTH == 640) && (LCD_HEIGHT == 480)
rockblox_background.640x480x16.bmp
+#elif (LCD_WIDTH == 360) && (LCD_HEIGHT == 400)
+rockblox_background.360x400x16.bmp
#elif (LCD_WIDTH >= 320) && (LCD_HEIGHT >= 240)
rockblox_background.320x240x16.bmp
#elif (LCD_WIDTH == 240) && (LCD_HEIGHT >= 320)
@@ -603,6 +615,12 @@ snake2_header2.640x480x16.bmp
snake2_left.640x480x16.bmp
snake2_right.640x480x16.bmp
snake2_bottom.640x480x16.bmp
+#elif (LCD_WIDTH >= 360) && (LCD_HEIGHT >= 400) && (LCD_DEPTH >= 16)
+snake2_header1.360x400x16.bmp
+snake2_header2.360x400x16.bmp
+snake2_left.360x400x16.bmp
+snake2_right.360x400x16.bmp
+snake2_bottom.360x400x16.bmp
#elif (LCD_WIDTH >= 320) && (LCD_HEIGHT >= 240) && (LCD_DEPTH >= 16)
snake2_header1.320x240x16.bmp
snake2_header2.320x240x16.bmp
@@ -874,7 +892,8 @@ superdom_boarditems.176x132x16.bmp
#elif (LCD_WIDTH == 320 && LCD_HEIGHT == 240)
superdom_boarditems.320x240x16.bmp
#elif ((LCD_WIDTH == 240) && (LCD_HEIGHT == 320)) || \
- ((LCD_WIDTH == 240) && (LCD_HEIGHT == 400))
+ ((LCD_WIDTH == 240) && (LCD_HEIGHT == 400)) || \
+ ((LCD_WIDTH == 360) && (LCD_HEIGHT == 400))
superdom_boarditems.240x320x16.bmp
#elif (LCD_WIDTH == 480 && LCD_HEIGHT == 640)
superdom_boarditems.480x640x16.bmp
@@ -911,6 +930,8 @@ sliding_puzzle.132x132x16.bmp
sliding_puzzle.176x176x16.bmp
#elif SMALLER_DIMENSION <= 240
sliding_puzzle.240x240x16.bmp
+#elif SMALLER_DIMENSION <= 360
+sliding_puzzle.360x360x16.bmp
#elif SMALLER_DIMENSION <= 480
sliding_puzzle.480x480x16.bmp
#endif
@@ -952,6 +973,45 @@ rockboxlogo.91x32x1.bmp
#endif
#endif
+/* Credits logo */
+#if (LCD_DEPTH == 1)
+#if (LCD_WIDTH == 160)
+creditslogo.160x53x1.bmp
+#elif (LCD_WIDTH == 128)
+creditslogo.128x42x1.bmp
+#else
+creditslogo.112x30x1.bmp
+#endif
+#elif (LCD_WIDTH == 96) && (LCD_DEPTH >= 16)
+creditslogo.96x30x16.bmp
+#elif (LCD_WIDTH == 128) && (LCD_DEPTH == 2)
+creditslogo.128x42x2.bmp
+#elif (LCD_WIDTH == 128) && (LCD_DEPTH >= 16)
+creditslogo.128x40x16.bmp
+#elif (LCD_WIDTH == 132) && (LCD_DEPTH >= 16)
+creditslogo.132x40x16.bmp
+#elif (LCD_WIDTH == 138) && (LCD_DEPTH >= 2)
+creditslogo.138x46x2.bmp
+#elif (LCD_WIDTH == 160) && (LCD_DEPTH == 2)
+creditslogo.160x53x2.bmp
+#elif (LCD_WIDTH == 320) && (LCD_DEPTH == 2)
+creditslogo.160x53x2.bmp
+#elif (LCD_WIDTH == 160) && (LCD_DEPTH >= 16)
+creditslogo.160x50x16.bmp
+#elif (LCD_WIDTH == 176) && (LCD_DEPTH >= 16)
+creditslogo.176x54x16.bmp
+#elif (LCD_WIDTH == 220) && (LCD_DEPTH >= 16)
+creditslogo.220x68x16.bmp
+#elif (LCD_WIDTH == 240) && (LCD_DEPTH >= 16)
+creditslogo.240x74x16.bmp
+#elif (LCD_WIDTH >= 320) && (LCD_WIDTH < 480) && (LCD_DEPTH >= 16)
+creditslogo.320x98x16.bmp
+#elif (LCD_WIDTH >= 480) && (LCD_WIDTH < 640) && (LCD_DEPTH >= 16)
+creditslogo.480x149x16.bmp
+#elif (LCD_WIDTH >= 640) && (LCD_DEPTH >= 16)
+creditslogo.640x198x16.bmp
+#endif
+
/* Pitch detector */
/* The following preprocessor condition must match the condition */
/* for pitch detector from plugins/SOURCES */
diff --git a/apps/plugins/bitmaps/native/_2048_tiles.26x26x2.bmp b/apps/plugins/bitmaps/native/_2048_tiles.26x26x2.bmp
new file mode 100644
index 0000000000..4b764c3e14
--- /dev/null
+++ b/apps/plugins/bitmaps/native/_2048_tiles.26x26x2.bmp
Binary files differ
diff --git a/apps/plugins/bitmaps/native/bubbles_background.360x400x16.bmp b/apps/plugins/bitmaps/native/bubbles_background.360x400x16.bmp
new file mode 100644
index 0000000000..46d8133911
--- /dev/null
+++ b/apps/plugins/bitmaps/native/bubbles_background.360x400x16.bmp
Binary files differ
diff --git a/apps/plugins/bitmaps/native/bubbles_emblem.360x400x16.bmp b/apps/plugins/bitmaps/native/bubbles_emblem.360x400x16.bmp
new file mode 100644
index 0000000000..d34e466749
--- /dev/null
+++ b/apps/plugins/bitmaps/native/bubbles_emblem.360x400x16.bmp
Binary files differ
diff --git a/apps/plugins/bitmaps/native/creditslogo.112x30x1.bmp b/apps/plugins/bitmaps/native/creditslogo.112x30x1.bmp
new file mode 100644
index 0000000000..c414ffb27b
--- /dev/null
+++ b/apps/plugins/bitmaps/native/creditslogo.112x30x1.bmp
Binary files differ
diff --git a/apps/plugins/bitmaps/native/creditslogo.128x40x16.bmp b/apps/plugins/bitmaps/native/creditslogo.128x40x16.bmp
new file mode 100644
index 0000000000..228dbddbd5
--- /dev/null
+++ b/apps/plugins/bitmaps/native/creditslogo.128x40x16.bmp
Binary files differ
diff --git a/apps/plugins/bitmaps/native/creditslogo.128x42x1.bmp b/apps/plugins/bitmaps/native/creditslogo.128x42x1.bmp
new file mode 100644
index 0000000000..27d8e5fc26
--- /dev/null
+++ b/apps/plugins/bitmaps/native/creditslogo.128x42x1.bmp
Binary files differ
diff --git a/apps/plugins/bitmaps/native/creditslogo.128x42x2.bmp b/apps/plugins/bitmaps/native/creditslogo.128x42x2.bmp
new file mode 100644
index 0000000000..fa12aa4bd7
--- /dev/null
+++ b/apps/plugins/bitmaps/native/creditslogo.128x42x2.bmp
Binary files differ
diff --git a/apps/plugins/bitmaps/native/creditslogo.132x40x16.bmp b/apps/plugins/bitmaps/native/creditslogo.132x40x16.bmp
new file mode 100644
index 0000000000..865a8ed59d
--- /dev/null
+++ b/apps/plugins/bitmaps/native/creditslogo.132x40x16.bmp
Binary files differ
diff --git a/apps/plugins/bitmaps/native/creditslogo.138x46x2.bmp b/apps/plugins/bitmaps/native/creditslogo.138x46x2.bmp
new file mode 100644
index 0000000000..4b5d7beb16
--- /dev/null
+++ b/apps/plugins/bitmaps/native/creditslogo.138x46x2.bmp
Binary files differ
diff --git a/apps/plugins/bitmaps/native/creditslogo.160x50x16.bmp b/apps/plugins/bitmaps/native/creditslogo.160x50x16.bmp
new file mode 100644
index 0000000000..59e472def1
--- /dev/null
+++ b/apps/plugins/bitmaps/native/creditslogo.160x50x16.bmp
Binary files differ
diff --git a/apps/plugins/bitmaps/native/creditslogo.160x53x1.bmp b/apps/plugins/bitmaps/native/creditslogo.160x53x1.bmp
new file mode 100644
index 0000000000..7e34b23c6b
--- /dev/null
+++ b/apps/plugins/bitmaps/native/creditslogo.160x53x1.bmp
Binary files differ
diff --git a/apps/plugins/bitmaps/native/creditslogo.160x53x2.bmp b/apps/plugins/bitmaps/native/creditslogo.160x53x2.bmp
new file mode 100644
index 0000000000..240cf10686
--- /dev/null
+++ b/apps/plugins/bitmaps/native/creditslogo.160x53x2.bmp
Binary files differ
diff --git a/apps/plugins/bitmaps/native/creditslogo.176x54x16.bmp b/apps/plugins/bitmaps/native/creditslogo.176x54x16.bmp
new file mode 100644
index 0000000000..bf43a65e42
--- /dev/null
+++ b/apps/plugins/bitmaps/native/creditslogo.176x54x16.bmp
Binary files differ
diff --git a/apps/plugins/bitmaps/native/creditslogo.220x68x16.bmp b/apps/plugins/bitmaps/native/creditslogo.220x68x16.bmp
new file mode 100644
index 0000000000..1dc68a09f2
--- /dev/null
+++ b/apps/plugins/bitmaps/native/creditslogo.220x68x16.bmp
Binary files differ
diff --git a/apps/plugins/bitmaps/native/creditslogo.240x74x16.bmp b/apps/plugins/bitmaps/native/creditslogo.240x74x16.bmp
new file mode 100644
index 0000000000..fd335b2fb8
--- /dev/null
+++ b/apps/plugins/bitmaps/native/creditslogo.240x74x16.bmp
Binary files differ
diff --git a/apps/plugins/bitmaps/native/creditslogo.320x98x16.bmp b/apps/plugins/bitmaps/native/creditslogo.320x98x16.bmp
new file mode 100644
index 0000000000..26dcac89c3
--- /dev/null
+++ b/apps/plugins/bitmaps/native/creditslogo.320x98x16.bmp
Binary files differ
diff --git a/apps/plugins/bitmaps/native/creditslogo.480x149x16.bmp b/apps/plugins/bitmaps/native/creditslogo.480x149x16.bmp
new file mode 100644
index 0000000000..c73d9374e4
--- /dev/null
+++ b/apps/plugins/bitmaps/native/creditslogo.480x149x16.bmp
Binary files differ
diff --git a/apps/plugins/bitmaps/native/creditslogo.640x198x16.bmp b/apps/plugins/bitmaps/native/creditslogo.640x198x16.bmp
new file mode 100644
index 0000000000..ec6a16da5d
--- /dev/null
+++ b/apps/plugins/bitmaps/native/creditslogo.640x198x16.bmp
Binary files differ
diff --git a/apps/plugins/bitmaps/native/creditslogo.96x30x16.bmp b/apps/plugins/bitmaps/native/creditslogo.96x30x16.bmp
new file mode 100644
index 0000000000..e032ebae34
--- /dev/null
+++ b/apps/plugins/bitmaps/native/creditslogo.96x30x16.bmp
Binary files differ
diff --git a/apps/plugins/bitmaps/native/invadrox_background.360x400x16.bmp b/apps/plugins/bitmaps/native/invadrox_background.360x400x16.bmp
new file mode 100644
index 0000000000..14c687a940
--- /dev/null
+++ b/apps/plugins/bitmaps/native/invadrox_background.360x400x16.bmp
Binary files differ
diff --git a/apps/plugins/bitmaps/native/jewels.360x400x16.bmp b/apps/plugins/bitmaps/native/jewels.360x400x16.bmp
new file mode 100644
index 0000000000..5cd35f2162
--- /dev/null
+++ b/apps/plugins/bitmaps/native/jewels.360x400x16.bmp
Binary files differ
diff --git a/apps/plugins/bitmaps/native/rockblox_background.360x400x16.bmp b/apps/plugins/bitmaps/native/rockblox_background.360x400x16.bmp
new file mode 100644
index 0000000000..b2b1289045
--- /dev/null
+++ b/apps/plugins/bitmaps/native/rockblox_background.360x400x16.bmp
Binary files differ
diff --git a/apps/plugins/bitmaps/native/sliding_puzzle.360x360x16.bmp b/apps/plugins/bitmaps/native/sliding_puzzle.360x360x16.bmp
new file mode 100644
index 0000000000..e74e3ecb6f
--- /dev/null
+++ b/apps/plugins/bitmaps/native/sliding_puzzle.360x360x16.bmp
Binary files differ
diff --git a/apps/plugins/bitmaps/native/snake2_bottom.360x400x16.bmp b/apps/plugins/bitmaps/native/snake2_bottom.360x400x16.bmp
new file mode 100644
index 0000000000..f6da503ab3
--- /dev/null
+++ b/apps/plugins/bitmaps/native/snake2_bottom.360x400x16.bmp
Binary files differ
diff --git a/apps/plugins/bitmaps/native/snake2_header1.360x400x16.bmp b/apps/plugins/bitmaps/native/snake2_header1.360x400x16.bmp
new file mode 100644
index 0000000000..f88acf2571
--- /dev/null
+++ b/apps/plugins/bitmaps/native/snake2_header1.360x400x16.bmp
Binary files differ
diff --git a/apps/plugins/bitmaps/native/snake2_header2.360x400x16.bmp b/apps/plugins/bitmaps/native/snake2_header2.360x400x16.bmp
new file mode 100644
index 0000000000..2acccd36b4
--- /dev/null
+++ b/apps/plugins/bitmaps/native/snake2_header2.360x400x16.bmp
Binary files differ
diff --git a/apps/plugins/bitmaps/native/snake2_left.360x400x16.bmp b/apps/plugins/bitmaps/native/snake2_left.360x400x16.bmp
new file mode 100644
index 0000000000..eac0c8e601
--- /dev/null
+++ b/apps/plugins/bitmaps/native/snake2_left.360x400x16.bmp
Binary files differ
diff --git a/apps/plugins/bitmaps/native/snake2_right.360x400x16.bmp b/apps/plugins/bitmaps/native/snake2_right.360x400x16.bmp
new file mode 100644
index 0000000000..316f40cb4c
--- /dev/null
+++ b/apps/plugins/bitmaps/native/snake2_right.360x400x16.bmp
Binary files differ
diff --git a/apps/plugins/bitmaps/remote_native/SOURCES b/apps/plugins/bitmaps/remote_native/SOURCES
index b3cc2157ff..2142674c67 100644
--- a/apps/plugins/bitmaps/remote_native/SOURCES
+++ b/apps/plugins/bitmaps/remote_native/SOURCES
@@ -22,3 +22,10 @@ remote_rockboxlogo.91x32x1.bmp
#elif (LCD_REMOTE_DEPTH == 2)
remote_rockboxlogo.91x32x2.bmp
#endif
+
+/* Credits logo */
+#if (LCD_REMOTE_DEPTH == 1)
+remote_creditslogo.128x42x1.bmp
+#elif (LCD_REMOTE_DEPTH == 2)
+remote_creditslogo.128x42x2.bmp
+#endif
diff --git a/apps/plugins/bitmaps/remote_native/remote_creditslogo.128x42x1.bmp b/apps/plugins/bitmaps/remote_native/remote_creditslogo.128x42x1.bmp
new file mode 100644
index 0000000000..27d8e5fc26
--- /dev/null
+++ b/apps/plugins/bitmaps/remote_native/remote_creditslogo.128x42x1.bmp
Binary files differ
diff --git a/apps/plugins/bitmaps/remote_native/remote_creditslogo.128x42x2.bmp b/apps/plugins/bitmaps/remote_native/remote_creditslogo.128x42x2.bmp
new file mode 100644
index 0000000000..fa12aa4bd7
--- /dev/null
+++ b/apps/plugins/bitmaps/remote_native/remote_creditslogo.128x42x2.bmp
Binary files differ
diff --git a/apps/plugins/blackjack.c b/apps/plugins/blackjack.c
index 4242f5a89f..3065802716 100644
--- a/apps/plugins/blackjack.c
+++ b/apps/plugins/blackjack.c
@@ -185,22 +185,6 @@ enum {
#define BJACK_RIGHT BUTTON_RIGHT
#define BJACK_LEFT BUTTON_LEFT
-#elif CONFIG_KEYPAD == TATUNG_TPJ1022_PAD
-#define BJACK_SELECT_NAME "MAIN"
-#define BJACK_STAY_NAME "MENU"
-#define BJACK_QUIT_NAME "POWER"
-#define BJACK_DOUBLE_NAME "DOWN"
-#define BJACK_SELECT BUTTON_MAIN
-#define BJACK_QUIT BUTTON_POWER
-#define BJACK_MAX (BUTTON_REC|BUTTON_UP)
-#define BJACK_MIN (BUTTON_REC|BUTTON_DOWN)
-#define BJACK_STAY BUTTON_MENU
-#define BJACK_DOUBLEDOWN BUTTON_DOWN
-#define BJACK_UP BUTTON_UP
-#define BJACK_DOWN BUTTON_DOWN
-#define BJACK_RIGHT BUTTON_RIGHT
-#define BJACK_LEFT BUTTON_LEFT
-
#elif CONFIG_KEYPAD == GIGABEAT_S_PAD
#define BJACK_SELECT_NAME "PLAY"
#define BJACK_STAY_NAME "VOL-"
@@ -607,6 +591,10 @@ enum {
#define BJACK_RIGHT BUTTON_RIGHT
#define BJACK_LEFT BUTTON_LEFT
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+#define BJACK_QUIT BUTTON_POWER
+#define BJACK_QUIT_NAME "QUIT"
+
#else
#error No keymap defined!
#endif
diff --git a/apps/plugins/bounce.c b/apps/plugins/bounce.c
index e6f29817d6..a42a0af6ba 100644
--- a/apps/plugins/bounce.c
+++ b/apps/plugins/bounce.c
@@ -34,10 +34,6 @@
static const struct button_mapping *plugin_contexts[] = { pla_main_ctx };
/* We set button maping with PLA */
-#define BOUNCE_UP PLA_UP
-#define BOUNCE_UP_REPEAT PLA_UP_REPEAT
-#define BOUNCE_DOWN PLA_DOWN
-#define BOUNCE_DOWN_REPEAT PLA_DOWN_REPEAT
#ifdef HAVE_SCROLLWHEEL
#define BOUNCE_LEFT PLA_SCROLL_BACK
@@ -52,7 +48,22 @@ static const struct button_mapping *plugin_contexts[] = { pla_main_ctx };
#endif
#define BOUNCE_QUIT PLA_EXIT
+
+#if (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_3G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_4G_PAD)
+#define BOUNCE_QUIT2 PLA_UP
+#define BOUNCE_DOWN PLA_LEFT
+#define BOUNCE_DOWN_REPEAT PLA_LEFT_REPEAT
+#define BOUNCE_UP PLA_RIGHT
+#define BOUNCE_UP_REPEAT PLA_RIGHT_REPEAT
+#else
#define BOUNCE_QUIT2 PLA_CANCEL
+#define BOUNCE_DOWN PLA_DOWN
+#define BOUNCE_DOWN_REPEAT PLA_DOWN_REPEAT
+#define BOUNCE_UP PLA_UP
+#define BOUNCE_UP_REPEAT PLA_UP_REPEAT
+#endif
#define BOUNCE_MODE PLA_SELECT
#define LETTER_WIDTH 11
@@ -471,6 +482,10 @@ enum plugin_status plugin_start(const void* parameter)
#if (CONFIG_KEYPAD == SAMSUNG_YH92X_PAD) || \
(CONFIG_KEYPAD == SAMSUNG_YH820_PAD)
"[Rew] to stop";
+#elif (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_3G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_4G_PAD)
+ "[Menu] to stop";
#else
"[Off] to stop";
#endif
diff --git a/apps/plugins/brickmania.c b/apps/plugins/brickmania.c
index 92f8d4d161..a26ff8edd8 100644
--- a/apps/plugins/brickmania.c
+++ b/apps/plugins/brickmania.c
@@ -111,8 +111,7 @@
#elif CONFIG_KEYPAD == SANSA_C200_PAD || \
CONFIG_KEYPAD == SANSA_CLIP_PAD || \
-CONFIG_KEYPAD == SANSA_M200_PAD || \
-CONFIG_KEYPAD == SANSA_CONNECT_PAD
+CONFIG_KEYPAD == SANSA_M200_PAD
#define QUIT BUTTON_POWER
#define LEFT BUTTON_LEFT
#define RIGHT BUTTON_RIGHT
@@ -122,6 +121,18 @@ CONFIG_KEYPAD == SANSA_CONNECT_PAD
#define UP BUTTON_UP
#define DOWN BUTTON_DOWN
+#elif CONFIG_KEYPAD == SANSA_CONNECT_PAD
+#define QUIT BUTTON_POWER
+#define LEFT BUTTON_LEFT
+#define RIGHT BUTTON_RIGHT
+#define SELECT BUTTON_SELECT
+#define UP BUTTON_UP
+#define DOWN BUTTON_DOWN
+
+#define SCROLL_FWD(x) ((x) & BUTTON_SCROLL_FWD)
+#define SCROLL_BACK(x) ((x) & BUTTON_SCROLL_BACK)
+
+
#elif CONFIG_KEYPAD == IRIVER_H10_PAD
#define QUIT BUTTON_POWER
#define LEFT BUTTON_LEFT
@@ -350,6 +361,9 @@ CONFIG_KEYPAD == SANSA_CONNECT_PAD
#define UP BUTTON_UP
#define DOWN BUTTON_DOWN
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+#define QUIT BUTTON_POWER
+
#else
#error No keymap defined!
#endif
@@ -1551,7 +1565,7 @@ static int brickmania_menu(void)
brickmania_init_game(true);
return 0;
case 2:
- rb->set_option("Difficulty", &difficulty, INT,
+ rb->set_option("Difficulty", &difficulty, RB_INT,
options, 2, NULL);
break;
case 3:
@@ -2519,10 +2533,10 @@ enum plugin_status plugin_start(const void* parameter)
#if LCD_DEPTH > 1
rb->lcd_set_backdrop(NULL);
#endif
-#ifdef HAVE_BACKLIGHT
+
/* Turn off backlight timeout */
backlight_ignore_timeout();
-#endif
+
/* now go ahead and have fun! */
rb->srand( *rb->current_tick );
brickmania_loadgame();
@@ -2551,9 +2565,9 @@ enum plugin_status plugin_start(const void* parameter)
configfile_save(CONFIG_FILE_NAME,config,1,0);
/* Restore user's original backlight setting */
rb->lcd_setfont(FONT_UI);
-#ifdef HAVE_BACKLIGHT
+
/* Turn on backlight timeout (revert to settings) */
backlight_use_settings();
-#endif
+
return PLUGIN_OK;
}
diff --git a/apps/plugins/bubbles.c b/apps/plugins/bubbles.c
index 50de10fa23..a64093492f 100644
--- a/apps/plugins/bubbles.c
+++ b/apps/plugins/bubbles.c
@@ -76,10 +76,9 @@ enum {
#endif
#define ANGLE_STEP 2
-#define ANGLE_STEP_REP 4
+#define ANGLE_STEP_REP 6
#define BUBBLES_QUIT1 PLA_EXIT
-#define BUBBLES_QUIT2 PLA_CANCEL
/* these are better off shooting with up */
#if (CONFIG_KEYPAD == SAMSUNG_YH820_PAD) \
@@ -92,10 +91,19 @@ enum {
#define BUBBLES_FIRE PLA_UP
#define BUBBLES_FIRE_REPEAT PLA_UP_REPEAT
#define BUBBLES_PAUSE PLA_SELECT
+#define BUBBLES_QUIT2 PLA_CANCEL
+#elif (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_3G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_4G_PAD)
+#define BUBBLES_FIRE PLA_SELECT
+#define BUBBLES_FIRE_REPEAT PLA_SELECT_REPEAT
+#define BUBBLES_PAUSE PLA_DOWN
+#define BUBBLES_QUIT2 PLA_UP
#else
#define BUBBLES_FIRE PLA_SELECT
#define BUBBLES_FIRE_REPEAT PLA_SELECT_REPEAT
#define BUBBLES_PAUSE PLA_UP
+#define BUBBLES_QUIT2 PLA_CANCEL
#endif
/* external bitmaps */
@@ -120,6 +128,17 @@ enum {
#define XOFS 128
#define MAX_FPS 40
+#elif (LCD_WIDTH == 360) && (LCD_HEIGHT == 400)
+#define XOFS 86
+#define YOFS 100
+#define SCORE_TXT_X 28
+#define SCORE_TXT_WIDTH 32
+#define SCORE_TXT_Y 48
+#define LEVEL_TXT_X 27
+#define LEVEL_TXT_WIDTH 32
+#define LEVEL_TXT_Y 5
+#define MAX_FPS 40
+
/* 22x22 bubbles (iPod Video) */
#elif (LCD_HEIGHT == 240) && (LCD_WIDTH == 320)
#define XOFS 72
@@ -2356,12 +2375,14 @@ static int bubbles_handlebuttons(struct game_context* bb, bool animblock,
switch(button){
case BUBBLES_LEFT_REP:
if(bb->angle > MIN_ANGLE) bb->angle -= ANGLE_STEP_REP;
+ break;
case BUBBLES_LEFT: /* change angle to the left */
if(bb->angle > MIN_ANGLE) bb->angle -= ANGLE_STEP;
break;
case BUBBLES_RIGHT_REP:
if(bb->angle < MAX_ANGLE) bb->angle += ANGLE_STEP_REP;
+ break;
case BUBBLES_RIGHT: /* change angle to the right */
if(bb->angle < MAX_ANGLE) bb->angle += ANGLE_STEP;
break;
diff --git a/apps/plugins/calculator.c b/apps/plugins/calculator.c
index cbaea8aa7e..743192dab6 100644
--- a/apps/plugins/calculator.c
+++ b/apps/plugins/calculator.c
@@ -533,6 +533,9 @@ F3: equal to "="
#define CALCULATOR_CALC BUTTON_MENU
#define CALCULATOR_CLEAR BUTTON_BACK
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+#define CALCULATOR_QUIT BUTTON_POWER
+
#else
#error No keymap defined!
#endif
@@ -729,12 +732,15 @@ static void oneOperand(void);
static void drawLines(void);
static void drawButtons(int group);
+#ifndef _WIN32
double strtod(const char *nptr, char **endptr);
+#endif
long long atoll(const char *nptr);
/* -----------------------------------------------------------------------
Standard library function
----------------------------------------------------------------------- */
+#ifndef _WIN32
double strtod(const char *nptr, char **endptr)
{
double out;
@@ -752,6 +758,7 @@ double strtod(const char *nptr, char **endptr)
case '.':
case '\'':
end=1;
+ /* fallthrough */
default:
nptr++;
}
@@ -770,6 +777,7 @@ double strtod(const char *nptr, char **endptr)
*endptr=(char *) nptr;
return out;
}
+#endif
// WARNING Unsafe: Use strtoll instead
long long atoll(const char *nptr)
@@ -1684,6 +1692,7 @@ static void typingProcess(void){
clearInput();
*typingbufPointer = '0';
typingbufPointer++;
+ /* Fallthrough */
case cal_typing:
calStatus = cal_dotted;
*typingbufPointer = '.';
diff --git a/apps/plugins/calendar.c b/apps/plugins/calendar.c
index e7f221a3c9..6d1091159f 100644
--- a/apps/plugins/calendar.c
+++ b/apps/plugins/calendar.c
@@ -39,14 +39,12 @@
#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
(CONFIG_KEYPAD == IPOD_3G_PAD) || \
(CONFIG_KEYPAD == IPOD_1G2G_PAD)
-#define CALENDAR_QUIT (BUTTON_SELECT|BUTTON_MENU)
+#define CALENDAR_QUIT (BUTTON_MENU|BUTTON_REL)
#define CALENDAR_SELECT (BUTTON_SELECT|BUTTON_REL)
#define CALENDAR_NEXT_WEEK BUTTON_SCROLL_FWD
#define CALENDAR_PREV_WEEK BUTTON_SCROLL_BACK
#define CALENDAR_NEXT_DAY BUTTON_RIGHT
#define CALENDAR_PREV_DAY BUTTON_LEFT
-#define CALENDAR_NEXT_MONTH BUTTON_PLAY
-#define CALENDAR_PREV_MONTH (BUTTON_MENU|BUTTON_REL)
#elif (CONFIG_KEYPAD == IAUDIO_X5M5_PAD)
#define CALENDAR_QUIT BUTTON_POWER
@@ -421,6 +419,9 @@
#define CALENDAR_NEXT_MONTH BUTTON_VOL_UP
#define CALENDAR_PREV_MONTH BUTTON_VOL_DOWN
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+/* use touchscreen */
+
#else
#error "No keypad setting."
#endif
@@ -910,7 +911,7 @@ static bool edit_memo(int change, struct shown *shown)
case 6: /* weekday */
rb->set_option("First Day of Week", &info.first_wday,
- INT, modes, 7, NULL);
+ RB_INT, modes, 7, NULL);
break;
case 7: /* playback control */
@@ -961,7 +962,7 @@ static bool view_events(int selected, struct shown *shown)
while (!exit)
{
button = rb->get_action(CONTEXT_LIST, TIMEOUT_BLOCK);
- rb->gui_synclist_do_button(&gui_memos, &button, LIST_WRAP_UNLESS_HELD);
+ rb->gui_synclist_do_button(&gui_memos, &button);
switch (button)
{
@@ -1092,17 +1093,18 @@ enum plugin_status plugin_start(const void* parameter)
case CALENDAR_QUIT:
exit = true;
break;
-
+#ifdef CALENDAR_NEXT_MONTH
case CALENDAR_NEXT_MONTH:
case CALENDAR_NEXT_MONTH | BUTTON_REPEAT:
next_month(&shown, 0);
break;
-
+#endif
+#ifdef CALENDAR_PREV_MONTH
case CALENDAR_PREV_MONTH:
case CALENDAR_PREV_MONTH | BUTTON_REPEAT:
prev_month(&shown, 0);
break;
-
+#endif
case CALENDAR_NEXT_WEEK:
case CALENDAR_NEXT_WEEK | BUTTON_REPEAT:
next_day(&shown, 7);
diff --git a/apps/plugins/chessbox/chessbox.c b/apps/plugins/chessbox/chessbox.c
index 397fd0e559..089cf7c400 100644
--- a/apps/plugins/chessbox/chessbox.c
+++ b/apps/plugins/chessbox/chessbox.c
@@ -222,6 +222,7 @@ static void cb_wt_callback ( void ) {
button = rb->button_get(false);
switch (button) {
case SYS_POWEROFF:
+ case SYS_REBOOT:
cb_sysevent = button;
#ifdef CB_RC_QUIT
case CB_RC_QUIT:
@@ -585,6 +586,7 @@ static struct cb_command cb_get_viewer_command (void) {
button = rb->button_get(true);
switch (button) {
case SYS_POWEROFF:
+ case SYS_REBOOT:
cb_sysevent = button;
#ifdef CB_RC_QUIT
case CB_RC_QUIT:
@@ -780,6 +782,7 @@ static bool cb_start_viewer(const char* filename){
break;
case COMMAND_QUIT:
exit_app = true;
+ /* fallthrough */
case COMMAND_RETURN:
exit_viewer = true;
break;
@@ -847,6 +850,7 @@ static struct cb_command cb_getcommand (void) {
button = rb->button_get(true);
switch (button) {
case SYS_POWEROFF:
+ case SYS_REBOOT:
cb_sysevent = button;
#ifdef CB_RC_QUIT
case CB_RC_QUIT:
diff --git a/apps/plugins/chessbox/chessbox_pgn.c b/apps/plugins/chessbox/chessbox_pgn.c
index 0d9da441b1..bb35bec726 100644
--- a/apps/plugins/chessbox/chessbox_pgn.c
+++ b/apps/plugins/chessbox/chessbox_pgn.c
@@ -678,7 +678,6 @@ struct pgn_game_node* pgn_show_game_list(struct pgn_game_node* first_game){
if (rb->global_settings->talk_menu)
rb->gui_synclist_set_voice_callback(&games_list, speak_game_selection);
rb->gui_synclist_set_nb_items(&games_list, i);
- rb->gui_synclist_limit_scroll(&games_list, true);
rb->gui_synclist_select_item(&games_list, 0);
rb->gui_synclist_draw(&games_list);
@@ -687,9 +686,8 @@ struct pgn_game_node* pgn_show_game_list(struct pgn_game_node* first_game){
while (true) {
curr_selection = rb->gui_synclist_get_sel_pos(&games_list);
button = rb->get_action(CONTEXT_LIST,TIMEOUT_BLOCK);
- if (rb->gui_synclist_do_button(&games_list,&button,LIST_WRAP_OFF)){
+ if (rb->gui_synclist_do_button(&games_list, &button))
continue;
- }
switch (button) {
case ACTION_STD_OK:
return get_game_info(curr_selection, first_game);
diff --git a/apps/plugins/chessbox/chessbox_pgn.h b/apps/plugins/chessbox/chessbox_pgn.h
index 827f045a4b..7d0cb795cf 100644
--- a/apps/plugins/chessbox/chessbox_pgn.h
+++ b/apps/plugins/chessbox/chessbox_pgn.h
@@ -33,7 +33,7 @@
#define CB_PLAY (BUTTON_SELECT | BUTTON_PLAY)
#define CB_LEVEL (BUTTON_SELECT | BUTTON_RIGHT)
#define CB_RESTART (BUTTON_SELECT | BUTTON_LEFT)
-#define CB_MENU (BUTTON_SELECT | BUTTON_MENU)
+#define CB_MENU (BUTTON_SELECT | BUTTON_REPEAT)
#define CB_SCROLL_UP (BUTTON_SCROLL_FWD|BUTTON_REPEAT)
#define CB_SCROLL_DOWN (BUTTON_SCROLL_BACK|BUTTON_REPEAT)
#define CB_SCROLL_LEFT (BUTTON_LEFT|BUTTON_REPEAT)
@@ -581,6 +581,9 @@
#define CB_SCROLL_LEFT (BUTTON_LEFT|BUTTON_REPEAT)
#define CB_SCROLL_RIGHT (BUTTON_RIGHT|BUTTON_REPEAT)
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+/* use touchscreen */
+
#else
#error No keymap defined!
#endif
diff --git a/apps/plugins/chessclock.c b/apps/plugins/chessclock.c
index 91b04e713a..3d03cb9f6a 100644
--- a/apps/plugins/chessclock.c
+++ b/apps/plugins/chessclock.c
@@ -395,6 +395,9 @@
#define CHC_SETTINGS_OK BUTTON_SELECT
#define CHC_SETTINGS_CANCEL BUTTON_POWER
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+#define CHC_QUIT BUTTON_POWER
+
#else
#error No keymap defined!
#endif
diff --git a/apps/plugins/chip8.c b/apps/plugins/chip8.c
index 31866acd10..ac7f998690 100644
--- a/apps/plugins/chip8.c
+++ b/apps/plugins/chip8.c
@@ -1291,6 +1291,9 @@ CONFIG_KEYPAD == MROBE500_PAD
#define CHIP8_KEY6 BUTTON_RIGHT
#define CHIP8_KEY8 BUTTON_BACK
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+/* use touchscreen */
+
#else
#error No keymap defined!
#endif
diff --git a/apps/plugins/chopper.c b/apps/plugins/chopper.c
index 70763a1b67..78cc292147 100644
--- a/apps/plugins/chopper.c
+++ b/apps/plugins/chopper.c
@@ -523,19 +523,18 @@ static void chopAddBlock(int x,int y,int sx,int sy, int indexOverride)
static void chopAddParticle(int x,int y,int sx,int sy)
{
- int i=0;
-
- while(mParticles[i].bIsActive && i < NUMBER_OF_PARTICLES)
- i++;
-
- if(i==NUMBER_OF_PARTICLES)
- return;
-
- mParticles[i].bIsActive = 1;
- mParticles[i].iWorldX = x;
- mParticles[i].iWorldY = y;
- mParticles[i].iSpeedX = sx;
- mParticles[i].iSpeedY = sy;
+ for(int i = 0; i < NUMBER_OF_PARTICLES; ++i)
+ {
+ if(!mParticles[i].bIsActive)
+ {
+ mParticles[i].bIsActive = 1;
+ mParticles[i].iWorldX = x;
+ mParticles[i].iWorldY = y;
+ mParticles[i].iSpeedX = sx;
+ mParticles[i].iSpeedY = sy;
+ return;
+ }
+ }
}
static void chopGenerateBlockIfNeeded(void)
@@ -785,7 +784,7 @@ static int chopMenu(int menunum)
res = -1;
break;
case 2:
- rb->set_option("Level", &iLevelMode, INT, levels, 2, NULL);
+ rb->set_option("Level", &iLevelMode, RB_INT, levels, 2, NULL);
break;
case 3:
playback_control(NULL);
@@ -1084,10 +1083,10 @@ enum plugin_status plugin_start(const void* parameter)
rb->lcd_set_foreground(LCD_WHITE);
#endif
-#ifdef HAVE_BACKLIGHT
+
/* Turn off backlight timeout */
backlight_ignore_timeout();
-#endif
+
rb->srand( *rb->current_tick );
@@ -1099,10 +1098,10 @@ enum plugin_status plugin_start(const void* parameter)
configfile_save(CFG_FILE, config, 1, 0);
rb->lcd_setfont(FONT_UI);
-#ifdef HAVE_BACKLIGHT
+
/* Turn on backlight timeout (revert to settings) */
backlight_use_settings();
-#endif
+
return ret;
}
diff --git a/apps/plugins/clix.c b/apps/plugins/clix.c
index c127a300f2..06813f3a4d 100644
--- a/apps/plugins/clix.c
+++ b/apps/plugins/clix.c
@@ -66,9 +66,7 @@
#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
(CONFIG_KEYPAD == IPOD_3G_PAD) || \
(CONFIG_KEYPAD == IPOD_1G2G_PAD)
-#define CLIX_BUTTON_QUIT (BUTTON_SELECT | BUTTON_MENU)
-#define CLIX_BUTTON_UP BUTTON_MENU
-#define CLIX_BUTTON_DOWN BUTTON_PLAY
+#define CLIX_BUTTON_QUIT BUTTON_MENU
#define CLIX_BUTTON_SCROLL_FWD BUTTON_SCROLL_FWD
#define CLIX_BUTTON_SCROLL_BACK BUTTON_SCROLL_BACK
#define CLIX_BUTTON_CLICK BUTTON_SELECT
@@ -316,6 +314,9 @@
#define CLIX_BUTTON_RIGHT BUTTON_RIGHT
#define CLIX_BUTTON_CLICK BUTTON_SELECT
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+#define CLIX_BUTTON_QUIT BUTTON_POWER
+
#else
#error "no keymap"
#endif
@@ -918,8 +919,10 @@ static int clix_handle_game(struct clix_game_state_t* state)
case CLIX_BUTTON_SCROLL_BACK:
case CLIX_BUTTON_SCROLL_BACK|BUTTON_REPEAT:
#endif
+#ifdef CLIX_BUTTON_UP
case CLIX_BUTTON_UP:
case CLIX_BUTTON_UP|BUTTON_REPEAT:
+#endif
if( state->y == 0 ||
state->board[ XYPOS( state->x, state->y - 1)] ==
CC_BLACK
@@ -935,8 +938,10 @@ static int clix_handle_game(struct clix_game_state_t* state)
case CLIX_BUTTON_SCROLL_FWD:
case CLIX_BUTTON_SCROLL_FWD|BUTTON_REPEAT:
#endif
+#ifdef CLIX_BUTTON_DOWN
case CLIX_BUTTON_DOWN:
case CLIX_BUTTON_DOWN|BUTTON_REPEAT:
+#endif
if( state->y == (BOARD_HEIGHT - 1))
state->y = 0;
else
diff --git a/apps/plugins/clock/clock.c b/apps/plugins/clock/clock.c
index d287c75598..c61b466aba 100644
--- a/apps/plugins/clock/clock.c
+++ b/apps/plugins/clock/clock.c
@@ -44,13 +44,19 @@ const struct button_mapping* plugin_contexts[]={
#define ACTION_COUNTER_TOGGLE PLA_SELECT
#define ACTION_COUNTER_RESET PLA_SELECT_REPEAT
-#define ACTION_MENU PLA_CANCEL
#define ACTION_MODE_NEXT PLA_RIGHT
#define ACTION_MODE_NEXT_REPEAT PLA_RIGHT_REPEAT
#define ACTION_MODE_PREV PLA_LEFT
#define ACTION_MODE_PREV_REPEAT PLA_LEFT_REPEAT
+#if (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_3G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_4G_PAD)
+#define ACTION_MENU PLA_UP
+#else
+#define ACTION_MENU PLA_CANCEL
#define ACTION_SKIN_NEXT PLA_UP
#define ACTION_SKIN_NEXT_REPEAT PLA_UP_REPEAT
+#endif
#define ACTION_SKIN_PREV PLA_DOWN
#define ACTION_SKIN_PREV_REPEAT PLA_DOWN_REPEAT
@@ -165,10 +171,12 @@ enum plugin_status plugin_start(const void* parameter){
case ACTION_SKIN_PREV:
clock_settings_skin_next(&clock_settings);
break;
+#if defined(ACTION_SKIN_NEXT) && defined(ACTION_SKIN_NEXT_REPEAT)
case ACTION_SKIN_NEXT_REPEAT:
case ACTION_SKIN_NEXT:
clock_settings_skin_previous(&clock_settings);
break;
+#endif
case ACTION_MENU:
clock_draw_restore_colors();
exit_clock=main_menu();
diff --git a/apps/plugins/clock/clock_menu.c b/apps/plugins/clock/clock_menu.c
index a597664f49..8b7a2c82be 100644
--- a/apps/plugins/clock/clock_menu.c
+++ b/apps/plugins/clock/clock_menu.c
@@ -81,17 +81,17 @@ static void menu_analog_settings(void)
switch(result){
case 0:
rb->set_option("Show Date", &clock_settings.analog.show_date,
- BOOL, noyes_text, 2, NULL);
+ RB_BOOL, noyes_text, 2, NULL);
break;
case 1:
rb->set_option("Show Second Hand",
&clock_settings.analog.show_seconds,
- BOOL, noyes_text, 2, NULL);
+ RB_BOOL, noyes_text, 2, NULL);
break;
case 2:
rb->set_option("Show Border",
&clock_settings.analog.show_border,
- BOOL, noyes_text, 2, NULL);
+ RB_BOOL, noyes_text, 2, NULL);
break;
}
}
@@ -112,12 +112,12 @@ static void menu_digital_settings(void){
case 0:
rb->set_option("Show Seconds",
&clock_settings.digital.show_seconds,
- BOOL, noyes_text, 2, NULL);
+ RB_BOOL, noyes_text, 2, NULL);
break;
case 1:
rb->set_option("Blinking Colon",
&clock_settings.digital.blinkcolon,
- BOOL, noyes_text, 2, NULL);
+ RB_BOOL, noyes_text, 2, NULL);
break;
}
}
@@ -129,7 +129,7 @@ static void menu_digital_settings(void){
static void confirm_reset(void){
int result=0;
- rb->set_option("Reset all settings?", &result, INT, noyes_text, 2, NULL);
+ rb->set_option("Reset all settings?", &result, RB_INT, noyes_text, 2, NULL);
if(result == 1){ /* reset! */
clock_settings_reset(&clock_settings);
@@ -157,16 +157,16 @@ static void menu_general_settings(void){
case 0:
rb->set_option("Hour format",
&clock_settings.general.hour_format,
- INT, hour_format_text, 2, NULL);
+ RB_INT, hour_format_text, 2, NULL);
break;
case 1:
rb->set_option("Date format",
&clock_settings.general.date_format,
- INT, date_format_text, 4, NULL);
+ RB_INT, date_format_text, 4, NULL);
break;
case 2:
rb->set_option("Show Counter", &clock_settings.general.show_counter,
- BOOL, noyes_text, 2, NULL);
+ RB_BOOL, noyes_text, 2, NULL);
break;
case 3:
confirm_reset();
@@ -180,7 +180,7 @@ static void menu_general_settings(void){
case 5:
rb->set_option("Save On Exit",
&clock_settings.general.save_settings,
- BOOL, noyes_text, 2, NULL);
+ RB_BOOL, noyes_text, 2, NULL);
/* if we no longer save on exit,
we better save now to remember that */
@@ -190,14 +190,14 @@ static void menu_general_settings(void){
case 6:
rb->set_option("Backlight Settings",
&clock_settings.general.backlight,
- INT, backlight_settings_text, 3, NULL);
+ RB_INT, backlight_settings_text, 3, NULL);
apply_backlight_setting(clock_settings.general.backlight);
break;
case 7:
rb->set_option("Idle Poweroff (temporary)",
&clock_settings.general.idle_poweroff,
- BOOL, idle_poweroff_text, 2, NULL);
+ RB_BOOL, idle_poweroff_text, 2, NULL);
break;
}
}
diff --git a/apps/plugins/codebuster.c b/apps/plugins/codebuster.c
index 956991575d..401ce6085f 100644
--- a/apps/plugins/codebuster.c
+++ b/apps/plugins/codebuster.c
@@ -438,6 +438,13 @@ enum plugin_status plugin_start(const void* parameter) {
if (button == PLA_SELECT)
break;
+#if (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_3G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_4G_PAD)
+ if (button == PLA_UP) /* Menu button */
+ button = PLA_CANCEL;
+#endif
+
switch (button) {
/* Exit */
diff --git a/apps/plugins/credits.c b/apps/plugins/credits.c
index 3374a33415..f4716651fd 100644
--- a/apps/plugins/credits.c
+++ b/apps/plugins/credits.c
@@ -21,7 +21,19 @@
#include "plugin.h"
#include "lib/helper.h"
-
+#ifdef HAVE_REMOTE_LCD
+#define REMOTE_WIDTH LCD_REMOTE_WIDTH
+#define REMOTE_HEIGHT LCD_REMOTE_HEIGHT
+#include "pluginbitmaps/remote_creditslogo.h"
+#define REMOTE_LOGO_WIDTH BMPWIDTH_remote_creditslogo
+#define REMOTE_LOGO_HEIGHT BMPHEIGHT_remote_creditslogo
+#define REMOTE_LOGO (const fb_remote_data*)remote_creditslogo
+#endif /* HAVE_REMOTE_LCD */
+
+#define LOGO (const fb_data*)creditslogo
+#include "pluginbitmaps/creditslogo.h"
+#define LOGO_WIDTH BMPWIDTH_creditslogo
+#define LOGO_HEIGHT BMPHEIGHT_creditslogo
static const char* const credits[] = {
#include "credits.raw" /* generated list of names from docs/CREDITS */
@@ -56,13 +68,13 @@ static int update_rowpos(int action, int cur_pos, int rows_per_screen, int tot_r
case ACTION_STD_NEXTREPEAT:
cur_pos++;
break;
- }
+ }
if(cur_pos > tot_rows - rows_per_screen)
cur_pos = 0;
if(cur_pos < 0)
cur_pos = tot_rows - rows_per_screen;
-
+
return cur_pos;
}
@@ -150,7 +162,7 @@ static void roll_credits(void)
if(stop_autoscroll(action))
break;
}
-
+
/* process user actions (if any) */
if(ACTION_STD_CANCEL == action)
return;
@@ -193,7 +205,7 @@ static void roll_credits(void)
rb->lcd_set_drawmode(DRMODE_SOLID);
rb->lcd_putsxy(namepos, font_h*(i+1), name);
rb->lcd_update_rect(0, font_h*(i+1), LCD_WIDTH, font_h);
-
+
/* exit on keypress, react to scrolling */
action = rb->get_action(CONTEXT_LIST, HZ/ANIM_SPEED);
if(stop_autoscroll(action))
@@ -214,7 +226,7 @@ static void roll_credits(void)
rb->lcd_putsxy(CREDITS_TARGETPOS, 0, elapsednames);
if (j+i < NUM_VISIBLE_LINES) /* takes care of trail on loop */
rb->lcd_update_rect(0, 0, LCD_WIDTH, font_h);
-
+
for(namepos = 0-name_w; namepos <= name_targetpos;
namepos += (name_targetpos - namepos + 14) / 7)
{
@@ -224,7 +236,7 @@ static void roll_credits(void)
rb->lcd_putsxy(namepos, font_h*(i+1), name);
rb->lcd_update_rect(0, font_h*(i+1), LCD_WIDTH, font_h);
rb->lcd_update_rect(CREDITS_TARGETPOS, 0, credits_w,font_h);
-
+
/* stop on keypress */
action = rb->get_action(CONTEXT_LIST, HZ/ANIM_SPEED);
if(stop_autoscroll(action))
@@ -236,14 +248,14 @@ static void roll_credits(void)
} /* for(i=0; i<NUM_VISIBLE_LINES; i++) */
if(stop_autoscroll(action))
break;
-
+
action = rb->get_action(CONTEXT_LIST, HZ*PAUSE_TIME);
if(stop_autoscroll(action))
break;
j+=i; /* no user intervention, draw the next screen-full */
} /* while(j < numnames) */
-
+
/* handle the keypress that we intercepted during autoscroll */
if(ACTION_STD_CANCEL == action)
return;
@@ -263,14 +275,14 @@ static void roll_credits(void)
j+NUM_VISIBLE_LINES, numnames);
rb->lcd_getstringsize(elapsednames, &credits_w, NULL);
rb->lcd_putsxy(CREDITS_TARGETPOS, 0, elapsednames);
-
+
for(i=0; i<NUM_VISIBLE_LINES; i++)
rb->lcd_putsxyf(0, font_h*(i+1), "%s", credits[j+i]);
rb->lcd_update();
rb->yield();
-
+
/* wait for user action */
action = rb->get_action(CONTEXT_LIST, TIMEOUT_BLOCK);
if(ACTION_STD_CANCEL == action)
@@ -279,7 +291,7 @@ static void roll_credits(void)
}
return; /* exit without animation */
}
-
+
action = rb->get_action(CONTEXT_LIST, HZ*3);
if(ACTION_STD_CANCEL == action)
return;
@@ -299,25 +311,78 @@ static void roll_credits(void)
}
}
+int show_logo(void)
+{
+ unsigned char version[32];
+ int font_h, ver_w;
+ rb->snprintf(version, sizeof(version), "Ver. %s", rb->rbversion);
+ ver_w = rb->font_getstringsize(version, NULL, &font_h, FONT_SYSFIXED);
+ rb->lcd_clear_display();
+ rb->lcd_setfont(FONT_SYSFIXED);
+#if defined(SANSA_CLIP) || defined(SANSA_CLIPV2) || defined(SANSA_CLIPPLUS)
+ /* display the logo in the blue area of the screen (bottom 48 pixels) */
+ if (ver_w > LCD_WIDTH)
+ rb->lcd_puts_scroll(0, 0, version);
+ else
+ rb->lcd_putsxy((LCD_WIDTH/2) - (ver_w/2), 0, version);
+ rb->lcd_bitmap(LOGO, (LCD_WIDTH - LOGO_WIDTH) / 2, 16,
+ LOGO_WIDTH, LOGO_HEIGHT);
+#else
+ rb->lcd_bitmap(LOGO, (LCD_WIDTH - LOGO_WIDTH) / 2, 10,
+ LOGO_WIDTH, LOGO_HEIGHT);
+ if (ver_w > LCD_WIDTH)
+ rb->lcd_puts_scroll(0, (LCD_HEIGHT-font_h) / font_h, version);
+ else
+ rb->lcd_putsxy((LCD_WIDTH/2) - (ver_w/2), LCD_HEIGHT-font_h, version);
+#endif
+ rb->lcd_setfont(FONT_UI);
+ rb->lcd_update();
+#ifdef HAVE_REMOTE_LCD
+ rb->lcd_remote_clear_display();
+ rb->lcd_remote_bitmap(REMOTE_LOGO, 0, 10,
+ REMOTE_LOGO_WIDTH, REMOTE_LOGO_HEIGHT);
+ rb->lcd_remote_setfont(FONT_SYSFIXED);
+ if (ver_w > LCD_REMOTE_WIDTH)
+ rb->lcd_remote_puts_scroll(0, (LCD_REMOTE_HEIGHT-font_h) / font_h, version);
+ else
+ rb->lcd_remote_putsxy((LCD_REMOTE_WIDTH/2) - (ver_w/2),
+ LCD_REMOTE_HEIGHT-font_h, version);
+ rb->lcd_remote_setfont(FONT_UI);
+ rb->lcd_remote_update();
+#endif
+
+ return 0;
+}
+
enum plugin_status plugin_start(const void* parameter)
{
(void)parameter;
-#ifdef HAVE_BACKLIGHT
+
/* Turn off backlight timeout */
backlight_ignore_timeout();
+
+#if LCD_DEPTH >= 16
+ rb->lcd_set_foreground (LCD_WHITE);
+ rb->lcd_set_background (LCD_BLACK);
#endif
- rb->show_logo();
+ show_logo();
- /* Show the logo for about 3 secs allowing the user to stop */
- if(!rb->action_userabort(3*HZ))
+ /* Show the logo for about 5 secs allowing the user to stop */
+ if(!rb->action_userabort(5*HZ))
+ {
+ rb->lcd_scroll_stop();
roll_credits();
+ }
+
+#ifdef HAVE_REMOTE_LCD
+ rb->lcd_remote_scroll_stop();
+#endif
-#ifdef HAVE_BACKLIGHT
/* Turn on backlight timeout (revert to settings) */
backlight_use_settings();
-#endif
+
return PLUGIN_OK;
}
diff --git a/apps/plugins/cube.c b/apps/plugins/cube.c
index 857f2415d4..43318f5aee 100644
--- a/apps/plugins/cube.c
+++ b/apps/plugins/cube.c
@@ -52,12 +52,14 @@
#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
(CONFIG_KEYPAD == IPOD_3G_PAD) || \
(CONFIG_KEYPAD == IPOD_1G2G_PAD)
-#define CUBE_QUIT (BUTTON_SELECT | BUTTON_MENU)
+#define CUBE_QUIT_PRE BUTTON_MENU
+#define CUBE_QUIT (BUTTON_MENU | BUTTON_REL)
#define CUBE_NEXT BUTTON_RIGHT
#define CUBE_PREV BUTTON_LEFT
#define CUBE_INC BUTTON_SCROLL_FWD
#define CUBE_DEC BUTTON_SCROLL_BACK
-#define CUBE_MODE BUTTON_MENU
+#define CUBE_MODE_PRE BUTTON_MENU
+#define CUBE_MODE (BUTTON_MENU | BUTTON_REPEAT)
#define CUBE_PAUSE BUTTON_PLAY
#define CUBE_HIGHSPEED_PRE BUTTON_SELECT
#define CUBE_HIGHSPEED (BUTTON_SELECT | BUTTON_REL)
@@ -400,6 +402,9 @@
#define CUBE_PAUSE BUTTON_PLAY
#define CUBE_HIGHSPEED BUTTON_BACK
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+/* use touchscreen */
+
#else
#error No keymap defined!
#endif
@@ -726,6 +731,7 @@ enum plugin_status plugin_start(const void* parameter)
int button;
#if defined(CUBE_MODE_PRE) || \
+ defined(CUBE_QUIT_PRE) || \
defined(CUBE_PAUSE_PRE) || \
defined(CUBE_HIGHSPEED_PRE)
int lastbutton = BUTTON_NONE;
@@ -900,6 +906,10 @@ enum plugin_status plugin_start(const void* parameter)
case CUBE_RC_QUIT:
#endif
case CUBE_QUIT:
+#ifdef CUBE_QUIT_PRE
+ if (lastbutton != CUBE_QUIT_PRE)
+ break;
+#endif
quit = true;
break;
@@ -908,6 +918,7 @@ enum plugin_status plugin_start(const void* parameter)
break;
}
#if defined(CUBE_MODE_PRE) || \
+ defined(CUBE_QUIT_PRE) || \
defined(CUBE_PAUSE_PRE) || \
defined(CUBE_HIGHSPEED_PRE)
if (button != BUTTON_NONE)
diff --git a/apps/plugins/dart_scorer.c b/apps/plugins/dart_scorer.c
new file mode 100644
index 0000000000..1e8dd8f37b
--- /dev/null
+++ b/apps/plugins/dart_scorer.c
@@ -0,0 +1,601 @@
+#include "plugin.h"
+#include "lib/display_text.h"
+#include "lib/helper.h"
+#include "lib/playback_control.h"
+#include "lib/pluginlib_exit.h"
+#include "lib/pluginlib_actions.h"
+
+#define BUTTON_ROWS 6
+#define BUTTON_COLS 5
+
+#define REC_HEIGHT (int)(LCD_HEIGHT / (BUTTON_ROWS + 1))
+#define REC_WIDTH (int)(LCD_WIDTH / BUTTON_COLS)
+
+#define Y_7_POS (LCD_HEIGHT) /* Leave room for the border */
+#define Y_6_POS (Y_7_POS - REC_HEIGHT) /* y6 = 63 */
+#define Y_5_POS (Y_6_POS - REC_HEIGHT) /* y5 = 53 */
+#define Y_4_POS (Y_5_POS - REC_HEIGHT) /* y4 = 43 */
+#define Y_3_POS (Y_4_POS - REC_HEIGHT) /* y3 = 33 */
+#define Y_2_POS (Y_3_POS - REC_HEIGHT) /* y2 = 23 */
+#define Y_1_POS (Y_2_POS - REC_HEIGHT) /* y1 = 13 */
+#define Y_0_POS 0 /* y0 = 0 */
+
+#define X_0_POS 0 /* x0 = 0 */
+#define X_1_POS (X_0_POS + REC_WIDTH) /* x1 = 22 */
+#define X_2_POS (X_1_POS + REC_WIDTH) /* x2 = 44 */
+#define X_3_POS (X_2_POS + REC_WIDTH) /* x3 = 66 */
+#define X_4_POS (X_3_POS + REC_WIDTH) /* x4 = 88 */
+#define X_5_POS (X_4_POS + REC_WIDTH) /* x5 = 110, column 111 left blank */
+
+#if (CONFIG_KEYPAD == IPOD_1G2G_PAD) || (CONFIG_KEYPAD == IPOD_3G_PAD) || (CONFIG_KEYPAD == IPOD_4G_PAD)
+#define DARTS_QUIT PLA_SELECT_REPEAT
+#else
+#define DARTS_QUIT PLA_CANCEL
+#endif
+#define DARTS_SELECT PLA_SELECT
+#define DARTS_RIGHT PLA_RIGHT
+#define DARTS_LEFT PLA_LEFT
+#define DARTS_UP PLA_UP
+#define DARTS_DOWN PLA_DOWN
+#define DARTS_RRIGHT PLA_RIGHT_REPEAT
+#define DARTS_RLEFT PLA_LEFT_REPEAT
+#define DARTS_RUP PLA_UP_REPEAT
+#define DARTS_RDOWN PLA_DOWN_REPEAT
+
+#define RESUME_FILE PLUGIN_GAMES_DATA_DIR "/dart_scorer.save"
+/* leave first line blank on bitmap display, for pause icon */
+#define FIRST_LINE 1
+
+#define NUM_PLAYERS 2
+#define MAX_UNDO 100
+
+static const struct button_mapping *plugin_contexts[] = {pla_main_ctx};
+
+/* game data structures */
+enum game_mode
+{
+ five,
+ three
+};
+static struct settings_struct
+{
+ enum game_mode mode;
+ int scores[2];
+ bool turn;
+ int throws;
+ int history[MAX_UNDO];
+ int history_ptr;
+} settings;
+
+/* temporary data */
+static bool loaded = false; /* has a save been loaded? */
+int btn_row, btn_col; /* current position index for button */
+int prev_btn_row, prev_btn_col; /* previous cursor position */
+unsigned char *buttonChar[6][5] = {
+ {"", "Single", "Double", "Triple", ""},
+ {"1", "2", "3", "4", "5"},
+ {"6", "7", "8", "9", "10"},
+ {"11", "12", "13", "14", "15"},
+ {"16", "17", "18", "19", "20"},
+ {"", "Missed", "Bull", "Undo", ""}};
+int modifier;
+
+static int do_dart_scorer_pause_menu(void);
+static void drawButtons(void);
+
+/* First, increases *dimen1 by dimen1_delta modulo dimen1_modulo.
+ If dimen1 wraps, increases *dimen2 by dimen2_delta modulo dimen2_modulo.
+*/
+static void move_with_wrap_and_shift(
+ int *dimen1, int dimen1_delta, int dimen1_modulo,
+ int *dimen2, int dimen2_delta, int dimen2_modulo)
+{
+ bool wrapped = false;
+
+ *dimen1 += dimen1_delta;
+ if (*dimen1 < 0)
+ {
+ *dimen1 = dimen1_modulo - 1;
+ wrapped = true;
+ }
+ else if (*dimen1 >= dimen1_modulo)
+ {
+ *dimen1 = 0;
+ wrapped = true;
+ }
+
+ if (wrapped)
+ {
+ /* Make the dividend always positive to be sure about the result.
+ Adding dimen2_modulo does not change it since we do it modulo. */
+ *dimen2 = (*dimen2 + dimen2_modulo + dimen2_delta) % dimen2_modulo;
+ }
+}
+
+static void drawButtons()
+{
+ int i, j, w, h;
+ for (i = 0; i <= 5; i++)
+ {
+ for (j = 0; j <= 4; j++)
+ {
+ unsigned char button_text[16];
+ char *selected_prefix = (i == 0 && modifier > 0 && j == modifier) ? "*" : "";
+ rb->snprintf(button_text, sizeof(button_text), "%s%s", selected_prefix, buttonChar[i][j]);
+ rb->lcd_getstringsize(button_text, &w, &h);
+ if (i == btn_row && j == btn_col) /* selected item */
+ rb->lcd_set_drawmode(DRMODE_SOLID);
+ else
+ rb->lcd_set_drawmode(DRMODE_SOLID | DRMODE_INVERSEVID);
+ rb->lcd_fillrect(X_0_POS + j * REC_WIDTH,
+ Y_1_POS + i * REC_HEIGHT,
+ REC_WIDTH, REC_HEIGHT + 1);
+ if (i == btn_row && j == btn_col) /* selected item */
+ rb->lcd_set_drawmode(DRMODE_SOLID | DRMODE_INVERSEVID);
+ else
+ rb->lcd_set_drawmode(DRMODE_SOLID);
+ rb->lcd_putsxy(X_0_POS + j * REC_WIDTH + (REC_WIDTH - w) / 2,
+ Y_1_POS + i * REC_HEIGHT + (REC_HEIGHT - h) / 2 + 1,
+ button_text);
+ }
+ }
+ rb->lcd_set_drawmode(DRMODE_SOLID);
+}
+
+static void draw(void)
+{
+ rb->lcd_clear_display();
+
+ char buf[32];
+
+ int x = 5;
+ int y = 10;
+ for (int i = 0; i < NUM_PLAYERS; ++i, x = x + 95)
+ {
+ char *turn_marker = (i == settings.turn) ? "*" : "";
+ rb->snprintf(buf, sizeof(buf), "%sPlayer %d: %d", turn_marker, i + 1, settings.scores[i]);
+ rb->lcd_putsxy(x, y, buf);
+ }
+ int throws_x = (LCD_WIDTH / 2) - 10;
+ char throws_buf[3];
+ for (int i = 0; i < settings.throws; ++i, throws_x += 5)
+ {
+ rb->strcat(throws_buf, "1");
+ rb->lcd_putsxy(throws_x, y, "|");
+ }
+
+ drawButtons();
+
+ rb->lcd_update();
+}
+
+/*****************************************************************************
+* save_game() saves the current game state.
+******************************************************************************/
+static void save_game(void)
+{
+ int fd = rb->open(RESUME_FILE, O_WRONLY | O_CREAT, 0666);
+ if (fd < 0)
+ return;
+
+ rb->write(fd, &settings, sizeof(struct settings_struct));
+
+ rb->close(fd);
+ rb->lcd_update();
+}
+
+/* load_game() loads the saved game and returns load success.*/
+static bool load_game(void)
+{
+ signed int fd;
+ bool loaded = false;
+
+ /* open game file */
+ fd = rb->open(RESUME_FILE, O_RDONLY);
+ if (fd < 0)
+ return false;
+
+ /* read in saved game */
+ if (rb->read(fd, &settings, sizeof(struct settings_struct)) == (long)sizeof(struct settings_struct))
+ {
+ loaded = true;
+ }
+
+ rb->close(fd);
+
+ return loaded;
+ return false;
+}
+
+/* asks the user if they wish to quit */
+static bool confirm_quit(void)
+{
+ const struct text_message prompt = {(const char *[]){"Are you sure?", "This will clear your current game."}, 2};
+ enum yesno_res response = rb->gui_syncyesno_run(&prompt, NULL, NULL);
+ if (response == YESNO_NO)
+ return false;
+ else
+ return true;
+}
+
+/* displays the help text */
+static bool do_help(void)
+{
+
+#ifdef HAVE_LCD_COLOR
+ rb->lcd_set_foreground(LCD_WHITE);
+ rb->lcd_set_background(LCD_BLACK);
+#endif
+
+ rb->lcd_setfont(FONT_UI);
+
+ static char *help_text[] = {"Dart Scorer", "", "", "Keep score of your darts game."};
+
+ struct style_text style[] = {
+ {0, TEXT_CENTER | TEXT_UNDERLINE},
+ };
+
+ return display_text(ARRAYLEN(help_text), help_text, style, NULL, true);
+}
+
+static void undo(void)
+{
+ if (!settings.history_ptr)
+ {
+ rb->splash(HZ * 2, "Out of undos!");
+ return;
+ }
+
+ /* jumping back to previous player? */
+ int turn = settings.throws == 3 ? !settings.turn : settings.turn;
+ if (turn != settings.turn)
+ {
+ settings.throws = 0;
+ settings.turn ^= true;
+ }
+
+ if (settings.history[settings.history_ptr - 1] >= 0)
+ {
+ settings.scores[turn] += settings.history[--settings.history_ptr];
+ ++settings.throws;
+ }
+ else
+ {
+ /*
+ negative history means we bust. negative filled for all skipped throws
+ from being bust so consume back until no more
+ */
+ for (; settings.throws < 3 && settings.history[settings.history_ptr - 1] < 0; --settings.history_ptr, ++settings.throws)
+ {
+ }
+ }
+}
+
+static void init_game(bool newgame)
+{
+ if (newgame)
+ {
+ /* initialize the game context */
+ modifier = 1;
+ btn_row = 1;
+ btn_col = 0;
+
+ int game_mode = -1;
+ MENUITEM_STRINGLIST(menu, "Game Mode", NULL, "501", "301");
+ while (game_mode < 0)
+ {
+ switch (rb->do_menu(&menu, &game_mode, NULL, false))
+ {
+ case 0:
+ {
+ settings.mode = five;
+ break;
+ }
+ case 1:
+ {
+ settings.mode = three;
+ break;
+ }
+ }
+
+ for (int i = 0; i < NUM_PLAYERS; ++i)
+ {
+ settings.scores[i] = (settings.mode == five) ? 501 : 301;
+ }
+ settings.turn = false;
+ settings.throws = 3;
+ settings.history_ptr = 0;
+ rb->lcd_clear_display();
+ }
+ }
+}
+
+/* main game loop */
+static enum plugin_status do_game(bool newgame)
+{
+ init_game(newgame);
+ draw();
+
+ while (1)
+ {
+ /* wait for button press */
+ int button = pluginlib_getaction(-1, plugin_contexts, ARRAYLEN(plugin_contexts));
+ unsigned char *selected = buttonChar[btn_row][btn_col];
+ switch (button)
+ {
+ case DARTS_SELECT:
+ if ((!rb->strcmp(selected, "")) || (!rb->strcmp(selected, "Single")))
+ modifier = 1;
+ else if (!rb->strcmp(selected, "Double"))
+ modifier = 2;
+ else if (!rb->strcmp(selected, "Triple"))
+ modifier = 3;
+ else if (!rb->strcmp(selected, "Undo"))
+ {
+ undo();
+ continue;
+ }
+ else
+ {
+ /* main logic of score keeping */
+ if (modifier == 0)
+ modifier = 1;
+ int hit = (!rb->strcmp(selected, "Bull")) ? 25 : rb->atoi(selected);
+ if (hit == 25 && modifier == 3)
+ {
+ /* no triple bullseye! */
+ rb->splash(HZ * 2, "Triple Bull... Don't be silly!");
+ continue;
+ }
+ hit *= modifier;
+ if (hit > settings.scores[settings.turn])
+ {
+ rb->splash(HZ * 2, "Bust! End of turn.");
+ for (int i = 0; i < settings.throws; ++i)
+ settings.history[settings.history_ptr++] = -1;
+ settings.throws = 0;
+ }
+ else if (hit == settings.scores[settings.turn] - 1)
+ {
+ rb->splash(HZ * 2, "1 left! Must checkout with a double. End of turn.");
+ for (int i = 0; i < settings.throws; ++i)
+ settings.history[settings.history_ptr++] = -1;
+ settings.throws = 0;
+ }
+ else if (hit == settings.scores[settings.turn] && modifier != 2)
+ {
+ rb->splash(HZ * 2, "Must checkout with a double! End of turn.");
+ for (int i = 0; i < settings.throws; ++i)
+ settings.history[settings.history_ptr++] = -1;
+ settings.throws = 0;
+ }
+ else
+ {
+ settings.scores[settings.turn] -= hit;
+ --settings.throws;
+ settings.history[settings.history_ptr++] = hit;
+ modifier = 1;
+ if (!settings.scores[settings.turn])
+ goto GAMEOVER;
+ }
+
+ if (!settings.throws)
+ {
+ settings.throws = 3;
+ settings.turn ^= true;
+ }
+ }
+ break;
+ case DARTS_LEFT:
+ case DARTS_RLEFT:
+ move_with_wrap_and_shift(
+ &btn_col, -1, BUTTON_COLS,
+ &btn_row, 0, BUTTON_ROWS);
+ break;
+ case DARTS_RIGHT:
+ case DARTS_RRIGHT:
+ move_with_wrap_and_shift(
+ &btn_col, 1, BUTTON_COLS,
+ &btn_row, 0, BUTTON_ROWS);
+ break;
+#ifdef DARTS_UP
+ case DARTS_UP:
+ case DARTS_RUP:
+#ifdef HAVE_SCROLLWHEEL
+ case PLA_SCROLL_BACK:
+ case PLA_SCROLL_BACK_REPEAT:
+#endif
+ move_with_wrap_and_shift(
+ &btn_row, -1, BUTTON_ROWS,
+ &btn_col, 0, BUTTON_COLS);
+ break;
+#endif
+#ifdef DARTS_DOWN
+ case DARTS_DOWN:
+ case DARTS_RDOWN:
+#ifdef HAVE_SCROLLWHEEL
+ case PLA_SCROLL_FWD:
+ case PLA_SCROLL_FWD_REPEAT:
+#endif
+ move_with_wrap_and_shift(
+ &btn_row, 1, BUTTON_ROWS,
+ &btn_col, 0, BUTTON_COLS);
+ break;
+#endif
+ case DARTS_QUIT:
+ switch (do_dart_scorer_pause_menu())
+ {
+ case 0: /* resume */
+ break;
+ case 1:
+ init_game(true);
+ continue;
+ case 2: /* quit w/o saving */
+ rb->remove(RESUME_FILE);
+ return PLUGIN_ERROR;
+ case 3: /* save & quit */
+ save_game();
+ return PLUGIN_ERROR;
+ break;
+ }
+ break;
+ default:
+ {
+ exit_on_usb(button); /* handle poweroff and USB */
+ break;
+ }
+ }
+ draw();
+ }
+
+GAMEOVER:
+ rb->splashf(HZ * 3, "Gameover. Player %d wins!", settings.turn + 1);
+
+ return PLUGIN_OK;
+}
+
+/* decide if this_item should be shown in the main menu */
+/* used to hide resume option when there is no save */
+static int mainmenu_cb(int action,
+ const struct menu_item_ex *this_item,
+ struct gui_synclist *this_list)
+{
+ (void)this_list;
+ int idx = ((intptr_t)this_item);
+ if (action == ACTION_REQUEST_MENUITEM && !loaded && (idx == 0 || idx == 5))
+ return ACTION_EXIT_MENUITEM;
+ return action;
+}
+
+/* show the pause menu */
+static int do_dart_scorer_pause_menu(void)
+{
+ int sel = 0;
+ MENUITEM_STRINGLIST(menu, "Dart Scorer", NULL,
+ "Resume Game",
+ "Start New Game",
+ "Playback Control",
+ "Help",
+ "Quit without Saving",
+ "Quit");
+ while (1)
+ {
+ switch (rb->do_menu(&menu, &sel, NULL, false))
+ {
+ case 0:
+ {
+ rb->splash(HZ * 2, "Resume");
+ return 0;
+ }
+ case 1:
+ {
+ if (!confirm_quit())
+ break;
+ else
+ {
+ rb->splash(HZ * 2, "New Game");
+ return 1;
+ }
+ }
+ case 2:
+ playback_control(NULL);
+ break;
+ case 3:
+ do_help();
+ break;
+ case 4: /* quit w/o saving */
+ {
+ if (!confirm_quit())
+ break;
+ else
+ {
+ return 2;
+ }
+ }
+ case 5:
+ return 3;
+ default:
+ break;
+ }
+ }
+}
+
+/* show the main menu */
+static enum plugin_status do_dart_scorer_menu(void)
+{
+ int sel = 0;
+ loaded = load_game();
+ MENUITEM_STRINGLIST(menu,
+ "Dart Scorer Menu",
+ mainmenu_cb,
+ "Resume Game",
+ "Start New Game",
+ "Playback Control",
+ "Help",
+ "Quit without Saving",
+ "Quit");
+ while (true)
+ {
+ switch (rb->do_menu(&menu, &sel, NULL, false))
+ {
+ case 0: /* Start new game or resume a game */
+ case 1:
+ {
+ if (sel == 1 && loaded)
+ {
+ if (!confirm_quit())
+ break;
+ }
+ enum plugin_status ret = do_game(sel == 1);
+ switch (ret)
+ {
+ case PLUGIN_OK:
+ {
+ loaded = false;
+ rb->remove(RESUME_FILE);
+ break;
+ }
+ case PLUGIN_USB_CONNECTED:
+ save_game();
+ return ret;
+ case PLUGIN_ERROR: /* exit without menu */
+ return PLUGIN_OK;
+ default:
+ break;
+ }
+ break;
+ }
+ case 2:
+ playback_control(NULL);
+ break;
+ case 3:
+ do_help();
+ break;
+ case 4:
+ if (confirm_quit())
+ return PLUGIN_OK;
+ break;
+ case 5:
+ if (loaded)
+ save_game();
+ return PLUGIN_OK;
+ default:
+ break;
+ }
+ }
+}
+
+/* prepares for exit */
+static void cleanup(void)
+{
+ backlight_use_settings();
+}
+
+enum plugin_status plugin_start(const void *parameter)
+{
+ (void)parameter;
+ /* now start the game menu */
+ enum plugin_status ret = do_dart_scorer_menu();
+ cleanup();
+ return ret;
+}
diff --git a/apps/plugins/db_folder_select.c b/apps/plugins/db_folder_select.c
new file mode 100644
index 0000000000..7f51e520cb
--- /dev/null
+++ b/apps/plugins/db_folder_select.c
@@ -0,0 +1,652 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ *
+ * Copyright (C) 2012 Jonathan Gordon
+ * Copyright (C) 2012 Thomas Martitz
+* * Copyright (C) 2021 William Wilgus
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#include "plugin.h"
+#ifdef ROCKBOX_HAS_LOGF
+#define logf rb->logf
+#else
+#define logf(...) do { } while(0)
+#endif
+
+/*
+ * Order for changing child states:
+ * 1) expand folder (skip to 3 if empty, skip to 4 if cannot be opened)
+ * 2) collapse and select
+ * 3) unselect (skip to 1)
+ * 4) do nothing
+ */
+
+enum child_state {
+ EXPANDED,
+ SELECTED,
+ COLLAPSED,
+ EACCESS,
+};
+
+struct child {
+ char* name;
+ struct folder *folder;
+ enum child_state state;
+};
+
+struct folder {
+ char *name;
+ struct child *children;
+ struct folder* previous;
+ uint16_t children_count;
+ uint16_t depth;
+};
+
+static char *buffer_front, *buffer_end;
+
+static struct
+{
+ int32_t len; /* keeps count versus maxlen to give buffer full notification */
+ uint32_t val; /* hash of all selected items */
+ char buf[3];/* address used as identifier -- only \0 written to it */
+ char maxlen_exceeded; /*0,1*/
+} hashed;
+
+static inline void get_hash(const char *key, uint32_t *hash, int len)
+{
+ *hash = rb->crc_32(key, len, *hash);
+}
+
+static char* folder_alloc(size_t size)
+{
+ char* retval;
+ /* 32-bit aligned */
+ size = ALIGN_UP(size, 4);
+ if (buffer_front + size > buffer_end)
+ {
+ return NULL;
+ }
+ retval = buffer_front;
+ buffer_front += size;
+ return retval;
+}
+
+static char* folder_alloc_from_end(size_t size)
+{
+ if (buffer_end - size < buffer_front)
+ {
+ return NULL;
+ }
+ buffer_end -= size;
+ return buffer_end;
+}
+#if 0
+/* returns the buffer size required to store the path + \0 */
+static int get_full_pathsz(struct folder *start)
+{
+ int reql = 0;
+ struct folder *next = start;
+ do
+ {
+ reql += rb->strlen(next->name) + 1;
+ } while ((next = next->previous));
+
+ if (start->name[0] != '/') reql--;
+ if (--reql < 0) reql = 0;
+ return reql;
+}
+#endif
+
+static size_t get_full_path(struct folder *start, char *dst, size_t dst_sz)
+{
+ size_t pos = 0;
+ struct folder *prev, *cur = NULL, *next = start;
+ dst[0] = '\0'; /* for rb->strlcat to do its thing */
+ /* First traversal R->L mutate nodes->previous to point at child */
+ while (next->previous != NULL) /* stop at the root */
+ {
+#define PATHMUTATE() \
+ ({ \
+ prev = cur; \
+ cur = next; \
+ next = cur->previous;\
+ cur->previous = prev; \
+ })
+ PATHMUTATE();
+ }
+ /*swap the next and cur nodes to reverse direction */
+ prev = next;
+ next = cur;
+ cur = prev;
+ /* Second traversal L->R mutate nodes->previous to point back at parent
+ * copy strings to buf as they go by */
+ while (next != NULL)
+ {
+ PATHMUTATE();
+ pos = rb->strlcat(dst, cur->name, dst_sz);
+ /* do not append slash to paths starting with slash */
+ if (cur->name[0] != '/')
+ pos = rb->strlcat(dst, "/", dst_sz);
+ }
+ logf("get_full_path: (%d)[%s]", (int)pos, dst);
+ return pos;
+#undef PATHMUTATE
+}
+
+/* support function for rb->qsort() */
+static int compare(const void* p1, const void* p2)
+{
+ struct child *left = (struct child*)p1;
+ struct child *right = (struct child*)p2;
+ return rb->strcasecmp(left->name, right->name);
+}
+
+static struct folder* load_folder(struct folder* parent, char *folder)
+{
+ DIR *dir;
+ char fullpath[MAX_PATH];
+
+ struct dirent *entry;
+ int child_count = 0;
+ char *first_child = NULL;
+ size_t len = 0;
+
+ struct folder* this = (struct folder*)folder_alloc(sizeof(struct folder));
+ if (this == NULL)
+ goto fail;
+
+ if (parent)
+ {
+ len = get_full_path(parent, fullpath, sizeof(fullpath));
+ if (len >= sizeof(fullpath))
+ goto fail;
+ }
+ rb->strlcpy(&fullpath[len], folder, sizeof(fullpath) - len);
+ logf("load_folder: [%s]", fullpath);
+
+ dir = rb->opendir(fullpath);
+ if (dir == NULL)
+ goto fail;
+ this->previous = parent;
+ this->name = folder;
+ this->children = NULL;
+ this->children_count = 0;
+ if (parent)
+ this->depth = parent->depth + 1;
+
+ while ((entry = rb->readdir(dir))) {
+ /* skip anything not a directory */
+ if ((rb->dir_get_info(dir, entry).attribute & ATTR_DIRECTORY) == 0) {
+ continue;
+ }
+ /* skip . and .. */
+ char *dn = entry->d_name;
+ if ((dn[0] == '.') && (dn[1] == '\0' || (dn[1] == '.' && dn[2] == '\0')))
+ continue;
+ /* copy entry name to end of buffer, save pointer */
+ int len = rb->strlen((char *)entry->d_name);
+ char *name = folder_alloc_from_end(len+1); /*for NULL*/
+ if (name == NULL)
+ {
+ rb->closedir(dir);
+ goto fail;
+ }
+ memcpy(name, (char *)entry->d_name, len+1);
+ child_count++;
+ first_child = name;
+ }
+ rb->closedir(dir);
+ /* now put the names in the array */
+ this->children = (struct child*)folder_alloc(sizeof(struct child) * child_count);
+
+ if (this->children == NULL)
+ goto fail;
+
+ while (child_count)
+ {
+ struct child *child = &this->children[this->children_count++];
+ child->name = first_child;
+ child->folder = NULL;
+ child->state = COLLAPSED;
+ while(*first_child++ != '\0'){};/* move to next name entry */
+ child_count--;
+ }
+ rb->qsort(this->children, this->children_count, sizeof(struct child), compare);
+
+ return this;
+fail:
+ return NULL;
+}
+
+struct folder* load_root(void)
+{
+ static struct child root_child;
+ /* reset the root for each call */
+ root_child.name = "/";
+ root_child.folder = NULL;
+ root_child.state = COLLAPSED;
+
+ static struct folder root = {
+ .name = "",
+ .children = &root_child,
+ .children_count = 1,
+ .depth = 0,
+ .previous = NULL,
+ };
+
+ return &root;
+}
+
+static int count_items(struct folder *start)
+{
+ int count = 0;
+ int i;
+
+ for (i=0; i<start->children_count; i++)
+ {
+ struct child *foo = &start->children[i];
+ if (foo->state == EXPANDED)
+ count += count_items(foo->folder);
+ count++;
+ }
+ return count;
+}
+
+static struct child* find_index(struct folder *start, int index, struct folder **parent)
+{
+ int i = 0;
+ *parent = NULL;
+
+ while (i < start->children_count)
+ {
+ struct child *foo = &start->children[i];
+ if (i == index)
+ {
+ *parent = start;
+ return foo;
+ }
+ i++;
+ if (foo->state == EXPANDED)
+ {
+ struct child *bar = find_index(foo->folder, index - i, parent);
+ if (bar)
+ {
+ return bar;
+ }
+ index -= count_items(foo->folder);
+ }
+ }
+ return NULL;
+}
+
+static const char * folder_get_name(int selected_item, void * data,
+ char * buffer, size_t buffer_len)
+{
+ struct folder *root = (struct folder*)data;
+ struct folder *parent;
+ struct child *this = find_index(root, selected_item , &parent);
+
+ char *buf = buffer;
+ if ((int)buffer_len > parent->depth)
+ {
+ int i = parent->depth;
+ while(--i > 0) /* don't indent the parent /folders */
+ *buf++ = '\t';
+ }
+ *buf = '\0';
+ rb->strlcat(buffer, this->name, buffer_len);
+
+ if (this->state == EACCESS)
+ { /* append error message to the entry if unaccessible */
+ size_t len = rb->strlcat(buffer, " ( ", buffer_len);
+ if (buffer_len > len)
+ {
+ rb->snprintf(&buffer[len], buffer_len - len, rb->str(LANG_READ_FAILED), ")");
+ }
+ }
+
+ return buffer;
+}
+
+static enum themable_icons folder_get_icon(int selected_item, void * data)
+{
+ struct folder *root = (struct folder*)data;
+ struct folder *parent;
+ struct child *this = find_index(root, selected_item, &parent);
+
+ switch (this->state)
+ {
+ case SELECTED:
+ return Icon_Cursor;
+ case COLLAPSED:
+ return Icon_Folder;
+ case EXPANDED:
+ return Icon_Submenu;
+ case EACCESS:
+ return Icon_Questionmark;
+ }
+ return Icon_NOICON;
+}
+
+static int child_set_state_expand(struct child *this, struct folder *parent)
+{
+ int newstate = EACCESS;
+ if (this->folder == NULL)
+ this->folder = load_folder(parent, this->name);
+
+ if (this->folder != NULL)
+ {
+ if(this->folder->children_count == 0)
+ newstate = SELECTED;
+ else
+ newstate = EXPANDED;
+ }
+ this->state = newstate;
+ return newstate;
+}
+
+static int folder_action_callback(int action, struct gui_synclist *list)
+{
+ struct folder *root = (struct folder*)list->data;
+ struct folder *parent;
+ struct child *this = find_index(root, list->selected_item, &parent), *child;
+ int i;
+
+ if (action == ACTION_STD_OK)
+ {
+ switch (this->state)
+ {
+ case EXPANDED:
+ this->state = SELECTED;
+ break;
+ case SELECTED:
+ this->state = COLLAPSED;
+ break;
+ case COLLAPSED:
+ child_set_state_expand(this, parent);
+ break;
+ case EACCESS:
+ /* cannot open, do nothing */
+ return action;
+ }
+ action = ACTION_REDRAW;
+ }
+ else if (action == ACTION_STD_CONTEXT)
+ {
+ switch (this->state)
+ {
+ case EXPANDED:
+ for (i = 0; i < this->folder->children_count; i++)
+ {
+ child = &this->folder->children[i];
+ switch (child->state)
+ {
+ case SELECTED:
+ case EXPANDED:
+ child->state = COLLAPSED;
+ break;
+ case COLLAPSED:
+ child->state = SELECTED;
+ break;
+ case EACCESS:
+ break;
+ }
+ }
+ break;
+ case SELECTED:
+ case COLLAPSED:
+ if (child_set_state_expand(this, parent) != EACCESS)
+ {
+ for (i = 0; i < (this->folder->children_count); i++)
+ {
+ child = &this->folder->children[i];
+ child->state = SELECTED;
+ }
+ }
+ break;
+ case EACCESS:
+ /* cannot open, do nothing */
+ return action;
+ }
+ action = ACTION_REDRAW;
+ }
+ if (action == ACTION_REDRAW)
+ list->nb_items = count_items(root);
+ return action;
+}
+
+static struct child* find_from_filename(const char* filename, struct folder *root)
+{
+ if (!root)
+ return NULL;
+ const char *slash = rb->strchr(filename, '/');
+ struct child *this;
+
+ /* filenames beginning with a / are specially treated as the
+ * loop below can't handle them. they can only occur on the first,
+ * and not recursive, calls to this function.*/
+ if (filename[0] == '/') /* in the loop nothing starts with '/' */
+ {
+ logf("find_from_filename [%s]", filename);
+ /* filename begins with /. in this case root must be the
+ * top level folder */
+ this = &root->children[0];
+ if (filename[1] == '\0')
+ { /* filename == "/" */
+ return this;
+ }
+ else /* filename == "/XXX/YYY". cascade down */
+ goto cascade;
+ }
+
+ for (int i = 0; i < root->children_count; i++)
+ {
+ this = &root->children[i];
+ /* when slash == NULL n will be really large but \0 stops the compare */
+ if (rb->strncasecmp(this->name, filename, slash - filename) == 0)
+ {
+ if (slash == NULL)
+ { /* filename == XXX */
+ return this;
+ }
+ else
+ goto cascade;
+ }
+ }
+ return NULL;
+
+cascade:
+ /* filename == XXX/YYY. cascade down */
+ child_set_state_expand(this, root);
+ while (slash[0] == '/') slash++; /* eat slashes */
+ return find_from_filename(slash, this->folder);
+}
+
+static int select_paths(struct folder* root, const char* filenames)
+{
+ /* Takes a list of filenames in a ':' delimited string
+ splits filenames at the ':' character loads each into buffer
+ selects each file in the folder list
+
+ if last item or only item the rest of the string is copied to the buffer
+ *End the last item WITHOUT the ':' character /.rockbox/eqs:/.rockbox/wps\0*
+ */
+ char buf[MAX_PATH];
+ const int buflen = sizeof(buf);
+
+ const char *fnp = filenames;
+ const char *lastfnp = fnp;
+ const char *sstr;
+ off_t len;
+
+ while (fnp)
+ {
+ fnp = rb->strchr(fnp, ':');
+ if (fnp)
+ {
+ len = fnp - lastfnp;
+ fnp++;
+ }
+ else /* no ':' get the rest of the string */
+ len = rb->strlen(lastfnp);
+
+ sstr = lastfnp;
+ lastfnp = fnp;
+ if (len <= 0 || len > buflen)
+ continue;
+ rb->strlcpy(buf, sstr, len + 1);
+ struct child *item = find_from_filename(buf, root);
+ if (item)
+ item->state = SELECTED;
+ }
+
+ return 0;
+}
+
+static void save_folders_r(struct folder *root, char* dst, size_t maxlen, size_t buflen)
+{
+ size_t len;
+ struct folder *curfolder;
+ char* name;
+
+ for (int i = 0; i < root->children_count; i++)
+ {
+ struct child *this = &root->children[i];
+ if (this->state == SELECTED)
+ {
+ if (this->folder == NULL)
+ {
+ curfolder = root;
+ name = this->name;
+ logf("save_folders_r: this->name[%s]", name);
+ }
+ else
+ {
+ curfolder = this->folder->previous;
+ name = this->folder->name;
+ logf("save_folders_r: this->folder->name[%s]", name);
+ }
+
+ len = get_full_path(curfolder, buffer_front, buflen);
+
+ if (len + 2 >= buflen)
+ continue;
+
+ len += rb->snprintf(&buffer_front[len], buflen - len, "%s:", name);
+ logf("save_folders_r: [%s]", buffer_front);
+ if (dst != hashed.buf)
+ {
+ int dlen = rb->strlen(dst);
+ if (dlen + len >= maxlen)
+ continue;
+ rb->strlcpy(&dst[dlen], buffer_front, maxlen - dlen);
+ }
+ else
+ {
+ if (hashed.len + len >= maxlen)
+ {
+ hashed.maxlen_exceeded = 1;
+ continue;
+ }
+ get_hash(buffer_front, &hashed.val, len);
+ hashed.len += len;
+ }
+ }
+ else if (this->state == EXPANDED)
+ save_folders_r(this->folder, dst, maxlen, buflen);
+ }
+}
+
+static uint32_t save_folders(struct folder *root, char* dst, size_t maxlen)
+{
+ hashed.len = 0;
+ hashed.val = 0;
+ hashed.maxlen_exceeded = 0;
+ size_t len = buffer_end - buffer_front;
+ dst[0] = '\0';
+ save_folders_r(root, dst, maxlen, len);
+ len = rb->strlen(dst);
+ /* fix trailing ':' */
+ if (len > 1) dst[len-1] = '\0';
+ /*Notify - user will probably not see save dialog if nothing new got added*/
+ if (hashed.maxlen_exceeded > 0) rb->splash(HZ *2, ID2P(LANG_SHOWDIR_BUFFER_FULL));
+ return hashed.val;
+}
+
+bool folder_select(char * header_text, char* setting, int setting_len)
+{
+ struct folder *root;
+ struct simplelist_info info;
+ size_t buf_size;
+
+ buffer_front = rb->plugin_get_buffer(&buf_size);
+ buffer_end = buffer_front + buf_size;
+ logf("folder_select %d bytes free", (int)(buffer_end - buffer_front));
+ root = load_root();
+
+ logf("folders in: %s", setting);
+ /* Load previous selection(s) */
+ select_paths(root, setting);
+ /* get current hash to check for changes later */
+ uint32_t hash = save_folders(root, hashed.buf, setting_len);
+ rb->simplelist_info_init(&info, header_text,
+ count_items(root), root);
+ info.get_name = folder_get_name;
+ info.action_callback = folder_action_callback;
+ info.get_icon = folder_get_icon;
+ rb->simplelist_show_list(&info);
+ logf("folder_select %d bytes free", (int)(buffer_end - buffer_front));
+ /* done editing. check for changes */
+ if (hash != save_folders(root, hashed.buf, setting_len))
+ { /* prompt for saving changes and commit if yes */
+ if (rb->yesno_pop(ID2P(LANG_SAVE_CHANGES)))
+ {
+ save_folders(root, setting, setting_len);
+ rb->settings_save();
+ logf("folders out: %s", setting);
+ return true;
+ }
+ }
+ return false;
+}
+
+/* plugin entry point */
+enum plugin_status plugin_start(const void* parameter)
+{
+ (void) parameter;
+
+ if(parameter)
+ {
+
+ if (rb->strcmp(parameter, rb->str(LANG_AUTORESUME)) == 0)
+ {
+ if (folder_select(rb->str(LANG_AUTORESUME),
+ rb->global_settings->autoresume_paths,
+ MAX_PATHNAME+1))
+ {
+ return 1;
+ }
+ }
+ }
+ else if (folder_select(rb->str(LANG_SELECT_FOLDER),
+ rb->global_settings->tagcache_scan_paths,
+ sizeof(rb->global_settings->tagcache_scan_paths)))
+ {
+ return 1;
+ }
+
+ return PLUGIN_OK;
+}
diff --git a/apps/plugins/demystify.c b/apps/plugins/demystify.c
index 74537e94f8..cb8e4ddf72 100644
--- a/apps/plugins/demystify.c
+++ b/apps/plugins/demystify.c
@@ -37,26 +37,42 @@
#define MIN_POLYGONS 1
/* Key assignement */
-#define DEMYSTIFY_QUIT PLA_CANCEL
#ifdef HAVE_SCROLLWHEEL
+#if (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_3G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_4G_PAD)
+#define DEMYSTIFY_QUIT PLA_UP
+#else
+#define DEMYSTIFY_QUIT PLA_CANCEL
+#endif
+
#define DEMYSTIFY_INCREASE_SPEED PLA_SCROLL_FWD
#define DEMYSTIFY_DECREASE_SPEED PLA_SCROLL_BACK
#define DEMYSTIFY_INCREASE_SPEED_REPEAT PLA_SCROLL_FWD_REPEAT
#define DEMYSTIFY_DECREASE_SPEED_REPEAT PLA_SCROLL_BACK_REPEAT
+
+#define DEMYSTIFY_ADD_POLYGON PLA_RIGHT
+#define DEMYSTIFY_REMOVE_POLYGON PLA_LEFT
+#define DEMYSTIFY_ADD_POLYGON_REPEAT PLA_RIGHT_REPEAT
+#define DEMYSTIFY_REMOVE_POLYGON_REPEAT PLA_LEFT_REPEAT
+
#else
+
+#define DEMYSTIFY_QUIT PLA_CANCEL
+
#define DEMYSTIFY_INCREASE_SPEED PLA_RIGHT
#define DEMYSTIFY_DECREASE_SPEED PLA_LEFT
#define DEMYSTIFY_INCREASE_SPEED_REPEAT PLA_RIGHT_REPEAT
#define DEMYSTIFY_DECREASE_SPEED_REPEAT PLA_LEFT_REPEAT
-#endif
#define DEMYSTIFY_ADD_POLYGON PLA_UP
#define DEMYSTIFY_REMOVE_POLYGON PLA_DOWN
#define DEMYSTIFY_ADD_POLYGON_REPEAT PLA_UP_REPEAT
#define DEMYSTIFY_REMOVE_POLYGON_REPEAT PLA_DOWN_REPEAT
+#endif
const struct button_mapping *plugin_contexts[]
= {pla_main_ctx,
#if defined(HAVE_REMOTE_LCD)
@@ -262,12 +278,10 @@ static void polygons_draw(struct polygon_fifo * polygons, struct screen * displa
static void cleanup(void)
{
-#ifdef HAVE_BACKLIGHT
+
backlight_use_settings();
-#ifdef HAVE_REMOTE_LCD
remote_backlight_use_settings();
-#endif
-#endif
+
}
#ifdef HAVE_LCD_COLOR
@@ -438,12 +452,10 @@ enum plugin_status plugin_start(const void* parameter)
#if LCD_DEPTH > 1
rb->lcd_set_backdrop(NULL);
#endif
-#ifdef HAVE_BACKLIGHT
+
backlight_ignore_timeout();
-#ifdef HAVE_REMOTE_LCD
remote_backlight_ignore_timeout();
-#endif
-#endif
+
ret = plugin_main();
return ret;
diff --git a/apps/plugins/dice.c b/apps/plugins/dice.c
index 622c58d71d..8f2315cde8 100644
--- a/apps/plugins/dice.c
+++ b/apps/plugins/dice.c
@@ -28,7 +28,14 @@
#define INITIAL_NB_DICES 1
#define INITIAL_NB_SIDES 2 /* corresponds to 6 sides in the array */
+
+#if (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_3G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_4G_PAD)
+#define DICE_QUIT PLA_UP
+#else
#define DICE_QUIT PLA_CANCEL
+#endif
#define DICE_ROLL PLA_SELECT
@@ -194,7 +201,7 @@ static bool dice_menu(struct dices * dice) {
break;
case 2:
- rb->set_option("Number of Sides", &sides_index, INT,
+ rb->set_option("Number of Sides", &sides_index, RB_INT,
nb_sides_option,
sizeof(nb_sides_values)/sizeof(int), NULL);
dice->nb_sides=nb_sides_values[sides_index];
diff --git a/apps/plugins/disktidy.c b/apps/plugins/disktidy.c
index 4f2bf6e992..e018c3f80d 100644
--- a/apps/plugins/disktidy.c
+++ b/apps/plugins/disktidy.c
@@ -176,7 +176,7 @@ static void tidy_load_file(const char* file)
str++;
unsigned i = find_file_string(str, last_group);
- tidy_types[i].remove = rb->strcmp(remove, "yes");
+ tidy_types[i].remove = !rb->strcmp(remove, "yes");
if (i >= tidy_type_count)
{
diff --git a/apps/plugins/doom/d_deh.c b/apps/plugins/doom/d_deh.c
index 1a399e3b49..0a67aa0aad 100644
--- a/apps/plugins/doom/d_deh.c
+++ b/apps/plugins/doom/d_deh.c
@@ -63,7 +63,7 @@ char* strlwr(char* str)
typedef struct {
const byte *inp; // Pointer to string
size_t size; // Bytes remaining in string
- int fd; // Current file descriptor
+ int fd; // Current file descriptor
} DEHFILE;
// killough 10/98: emulate IO whether input really comes from a file or not
@@ -2868,6 +2868,7 @@ boolean deh_GetData(char *s, char *k, uint_64_t *l, char **strval, int fpout)
if (*t == '=') break;
buffer[i] = *t; // copy it
}
+ if (i == 0) i = 1; /* Just in case */
buffer[--i] = '\0'; // terminate the key before the '='
if (!*t) // end of string with no equal sign
{
diff --git a/apps/plugins/doom/i_video.c b/apps/plugins/doom/i_video.c
index 2381a7614d..79f3212467 100644
--- a/apps/plugins/doom/i_video.c
+++ b/apps/plugins/doom/i_video.c
@@ -453,6 +453,9 @@ void I_ShutdownGraphics(void)
#define DOOMBUTTON_MAP BUTTON_BOTTOMRIGHT
#elif CONFIG_KEYPAD == SANSA_CONNECT_PAD
+#define DOOMBUTTON_SCROLLWHEEL
+#define DOOMBUTTON_SCROLLWHEEL_CC BUTTON_SCROLL_BACK
+#define DOOMBUTTON_SCROLLWHEEL_CW BUTTON_SCROLL_FWD
#define DOOMBUTTON_UP BUTTON_UP
#define DOOMBUTTON_DOWN BUTTON_DOWN
#define DOOMBUTTON_LEFT BUTTON_LEFT
@@ -614,6 +617,10 @@ void I_ShutdownGraphics(void)
#define DOOMBUTTON_WEAPON BUTTON_VOL_UP
#define DOOMBUTTON_MAP BUTTON_VOL_DOWN
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+#define DOOMBUTTON_ESC BUTTON_POWER
+#define DOOMBUTTON_MAP BUTTON_PREV
+
#else
#error Keymap not defined!
#endif
diff --git a/apps/plugins/doom/p_ceilng.c b/apps/plugins/doom/p_ceilng.c
index db5a701b1a..f83bdc0260 100644
--- a/apps/plugins/doom/p_ceilng.c
+++ b/apps/plugins/doom/p_ceilng.c
@@ -121,6 +121,7 @@ void T_MoveCeiling (ceiling_t* ceiling)
// crushers reverse direction at the top
case silentCrushAndRaise:
S_StartSound((mobj_t *)&ceiling->sector->soundorg,sfx_pstop);
+ /* fallthrough */
case genSilentCrusher:
case genCrusher:
case fastCrushAndRaise:
@@ -177,8 +178,10 @@ void T_MoveCeiling (ceiling_t* ceiling)
// except generalized ones, reset speed, start back up
case silentCrushAndRaise:
S_StartSound((mobj_t *)&ceiling->sector->soundorg,sfx_pstop);
+ /* fallthrough */
case crushAndRaise:
ceiling->speed = CEILSPEED;
+ /* fallthrough */
case fastCrushAndRaise:
ceiling->direction = 1;
break;
diff --git a/apps/plugins/doom/p_floor.c b/apps/plugins/doom/p_floor.c
index f4dbcd04f5..953e4dd09d 100644
--- a/apps/plugins/doom/p_floor.c
+++ b/apps/plugins/doom/p_floor.c
@@ -503,6 +503,7 @@ int EV_DoFloor
case raiseFloorCrush:
floor->crush = true;
+ /* fallthrough */
case raiseFloor:
floor->direction = 1;
floor->sector = sec;
diff --git a/apps/plugins/doom/rockdoom.c b/apps/plugins/doom/rockdoom.c
index 6594859c08..04817d4722 100644
--- a/apps/plugins/doom/rockdoom.c
+++ b/apps/plugins/doom/rockdoom.c
@@ -490,7 +490,7 @@ int Oset_keys()
else
{
*keys[result]=translatekey(*keys[result]);
- rb->set_option(menu_[result], keys[result], INT, doomkeys, numdoomkeys, NULL );
+ rb->set_option(menu_[result], keys[result], RB_INT, doomkeys, numdoomkeys, NULL );
*keys[result]=translatekey(*keys[result]);
}
}
@@ -540,7 +540,7 @@ static bool Doptions()
if(result==0)
Oset_keys();
else if (result > 0)
- rb->set_option(menu_[result], options[result-1], INT, onoff, 2, NULL );
+ rb->set_option(menu_[result], options[result-1], RB_INT, onoff, 2, NULL );
else
menuquit=1;
}
@@ -620,7 +620,7 @@ int doom_menu()
result = rb->do_menu(&menu, &selected, NULL, false);
switch (result) {
case 0: /* Game picker */
- rb->set_option("Game WAD", &gamever, INT, names, status, NULL );
+ rb->set_option("Game WAD", &gamever, RB_INT, names, status, NULL );
break;
case 1: /* Addon picker */
@@ -722,9 +722,9 @@ enum plugin_status plugin_start(const void* parameter)
systemvol= rb->global_settings->volume-rb->global_settings->volume%mod;
general_translucency = default_translucency; // phares
-#ifdef HAVE_BACKLIGHT
+
backlight_ignore_timeout();
-#endif
+
#ifdef RB_PROFILE
rb->profile_thread();
#endif
@@ -738,9 +738,9 @@ enum plugin_status plugin_start(const void* parameter)
#ifdef RB_PROFILE
rb->profstop();
#endif
-#ifdef HAVE_BACKLIGHT
+
backlight_use_settings();
-#endif
+
M_SaveDefaults ();
I_Quit(); // Make SURE everything was closed out right
diff --git a/apps/plugins/fft/fft.c b/apps/plugins/fft/fft.c
index 35498227bf..4d36302ddf 100644
--- a/apps/plugins/fft/fft.c
+++ b/apps/plugins/fft/fft.c
@@ -40,13 +40,22 @@ GREY_INFO_STRUCT
/* this set the context to use with PLA */
static const struct button_mapping *plugin_contexts[] = { pla_main_ctx };
-#define FFT_PREV_GRAPH PLA_LEFT
-#define FFT_NEXT_GRAPH PLA_RIGHT
-#define FFT_ORIENTATION PLA_CANCEL
-#define FFT_WINDOW PLA_SELECT
-#define FFT_AMP_SCALE PLA_UP
-#define FFT_FREQ_SCALE PLA_DOWN
-#define FFT_QUIT PLA_EXIT
+#define FFT_PREV_GRAPH PLA_LEFT
+#define FFT_NEXT_GRAPH PLA_RIGHT
+#define FFT_ORIENTATION PLA_CANCEL
+#define FFT_WINDOW PLA_SELECT_REL
+#define FFT_AMP_SCALE PLA_SELECT_REPEAT
+#define FFT_AMP_SCALE_PRE PLA_SELECT
+#define FFT_FREQ_SCALE PLA_DOWN
+
+
+#if (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_3G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_4G_PAD)
+#define FFT_QUIT PLA_UP
+#else
+#define FFT_QUIT PLA_EXIT
+#endif
#ifdef HAVE_LCD_COLOR
#include "pluginbitmaps/fft_colors.h"
@@ -1184,9 +1193,8 @@ static void fft_cleanup(void)
#ifndef HAVE_LCD_COLOR
grey_release();
#endif
-#ifdef HAVE_BACKLIGHT
+
backlight_use_settings();
-#endif
/* save settings if changed */
if (rb->memcmp(&fft, &fft_disk, sizeof(fft)))
@@ -1237,9 +1245,8 @@ static bool fft_setup(void)
mylcd_clear_display();
myosd_lcd_update();
#endif
-#ifdef HAVE_BACKLIGHT
+
backlight_ignore_timeout();
-#endif
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
rb->trigger_cpu_boost();
@@ -1259,6 +1266,10 @@ enum plugin_status plugin_start(const void* parameter)
if(!fft_setup())
return PLUGIN_ERROR;
+#if defined(FFT_AMP_SCALE_PRE)
+ int lastbutton = BUTTON_NONE;
+#endif
+
while(run)
{
long delay = fft_draw();
@@ -1302,6 +1313,10 @@ enum plugin_status plugin_start(const void* parameter)
break;
case FFT_AMP_SCALE:
+#ifdef FFT_AMP_SCALE_PRE
+ if (lastbutton != FFT_AMP_SCALE_PRE)
+ break;
+#endif
if (++fft.amp_scale >= FFT_MAX_AS)
fft.amp_scale = FFT_MIN_AS;
@@ -1330,6 +1345,11 @@ enum plugin_status plugin_start(const void* parameter)
exit_on_usb(button);
break;
}
+
+#if defined(FFT_AMP_SCALE_PRE)
+ if (button != BUTTON_NONE)
+ lastbutton = button;
+#endif
}
return PLUGIN_OK;
diff --git a/apps/plugins/fire.c b/apps/plugins/fire.c
index f3e6fb35e4..5cdf4a2d3a 100644
--- a/apps/plugins/fire.c
+++ b/apps/plugins/fire.c
@@ -56,10 +56,17 @@ const struct button_mapping* plugin_contexts[]= {
};
#define FIRE_QUIT PLA_CANCEL
-#define FIRE_QUIT2 PLA_EXIT
#define FIRE_SWITCH_FLAMES_TYPE PLA_LEFT
#define FIRE_SWITCH_FLAMES_MOVING PLA_RIGHT
+#if (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_3G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_4G_PAD)
+#define FIRE_QUIT2 PLA_UP
+#else
+#define FIRE_QUIT2 PLA_EXIT
+#endif
+
#ifdef HAVE_SCROLLWHEEL
#define FIRE_INCREASE_MULT PLA_SCROLL_FWD
#define FIRE_INCREASE_MULT_REP PLA_SCROLL_FWD_REPEAT
@@ -279,10 +286,9 @@ static void cleanup(void *parameter)
#ifndef HAVE_LCD_COLOR
grey_release();
#endif
-#ifdef HAVE_BACKLIGHT
+
/* Turn on backlight timeout (revert to settings) */
backlight_use_settings();
-#endif
}
@@ -371,10 +377,9 @@ enum plugin_status plugin_start(const void* parameter)
#if LCD_DEPTH > 1
rb->lcd_set_backdrop(NULL);
#endif
-#ifdef HAVE_BACKLIGHT
+
/* Turn off backlight timeout */
backlight_ignore_timeout();
-#endif
#if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_PAL256)
rb->lcd_set_mode(LCD_MODE_PAL256);
diff --git a/apps/plugins/fireworks.c b/apps/plugins/fireworks.c
index b7dad0d8ba..5f547ed5e0 100644
--- a/apps/plugins/fireworks.c
+++ b/apps/plugins/fireworks.c
@@ -35,10 +35,17 @@ static const struct button_mapping *plugin_contexts[] = { pla_main_ctx };
/* We use PLA */
#define BTN_EXIT PLA_EXIT
-#define BTN_MENU PLA_CANCEL
#define BTN_FIRE PLA_SELECT
#define BTN_FIRE_REPEAT PLA_SELECT_REPEAT
+#if (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_3G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_4G_PAD)
+#define BTN_MENU PLA_UP
+#else
+#define BTN_MENU PLA_CANCEL
+#endif
+
/* The lowdown on source terminology:
* a ROCKET is launched from the LCD bottom.
* FIREWORKs are ejected from the rocket when it explodes. */
@@ -305,32 +312,32 @@ static void fireworks_menu(void)
break;
case 1:
- rb->set_option("Auto-Fire", &autofire_delay, INT,
+ rb->set_option("Auto-Fire", &autofire_delay, RB_INT,
autofire_delay_settings, 15, NULL);
break;
case 2:
rb->set_option("Particles Per Firework", &particles_per_firework,
- INT, particle_settings, 8, NULL);
+ RB_INT, particle_settings, 8, NULL);
break;
case 3:
- rb->set_option("Particle Life", &particle_life, INT,
+ rb->set_option("Particle Life", &particle_life, RB_INT,
particle_life_settings, 9, NULL);
break;
case 4:
- rb->set_option("Gravity", &gravity, INT,
+ rb->set_option("Gravity", &gravity, RB_INT,
gravity_settings, 4, NULL);
break;
case 5:
- rb->set_option("Show Rockets", &show_rockets, INT,
+ rb->set_option("Show Rockets", &show_rockets, RB_INT,
rocket_settings, 3, NULL);
break;
case 6:
- rb->set_option("FPS (Speed)", &frames_per_second, INT,
+ rb->set_option("FPS (Speed)", &frames_per_second, RB_INT,
fps_settings, 9, NULL);
break;
@@ -358,9 +365,9 @@ enum plugin_status plugin_start(const void* parameter)
/* set everything up.. no BL timeout, no backdrop,
white-text-on-black-background. */
-#ifdef HAVE_BACKLIGHT
+
backlight_ignore_timeout();
-#endif
+
#if LCD_DEPTH > 1
rb->lcd_set_backdrop(NULL);
rb->lcd_set_background(LCD_BLACK);
@@ -524,10 +531,10 @@ enum plugin_status plugin_start(const void* parameter)
break;
}
}
-#ifdef HAVE_BACKLIGHT
+
/* Turn on backlight timeout (revert to settings) */
backlight_use_settings();
-#endif
+
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
rb->cpu_boost(false);
#endif
diff --git a/apps/plugins/flipit.c b/apps/plugins/flipit.c
index f6fb059f91..278c77e188 100644
--- a/apps/plugins/flipit.c
+++ b/apps/plugins/flipit.c
@@ -49,7 +49,7 @@
#define FLIPIT_DOWN BUTTON_PLAY
#define FLIPIT_NEXT BUTTON_SCROLL_FWD
#define FLIPIT_PREV BUTTON_SCROLL_BACK
-#define FLIPIT_QUIT (BUTTON_SELECT | BUTTON_MENU)
+#define FLIPIT_QUIT (BUTTON_SELECT | BUTTON_REPEAT)
#define FLIPIT_SHUFFLE (BUTTON_SELECT | BUTTON_LEFT)
#define FLIPIT_SOLVE (BUTTON_SELECT | BUTTON_PLAY)
#define FLIPIT_STEP_BY_STEP (BUTTON_SELECT | BUTTON_RIGHT)
@@ -496,6 +496,9 @@
#define FLIPIT_STEP_BY_STEP BUTTON_VOL_UP
#define FLIPIT_TOGGLE BUTTON_SELECT
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+/* use touchscreen */
+
#else
#error No keymap defined!
#endif
@@ -838,7 +841,7 @@ enum plugin_status plugin_start(const void* parameter)
#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
(CONFIG_KEYPAD == IPOD_3G_PAD) || \
(CONFIG_KEYPAD == IPOD_1G2G_PAD)
- rb->lcd_putsxy(2, 8, "[S-MENU] to stop");
+ rb->lcd_putsxy(2, 8, "Long [SELECT] to stop");
rb->lcd_putsxy(2, 18, "[SELECT] toggle");
rb->lcd_putsxy(2, 28, "[S-LEFT] shuffle");
rb->lcd_putsxy(2, 38, "[S-PLAY] solution");
diff --git a/apps/plugins/fractals/fractal.h b/apps/plugins/fractals/fractal.h
index b64bf942fe..8432546c1e 100644
--- a/apps/plugins/fractals/fractal.h
+++ b/apps/plugins/fractals/fractal.h
@@ -40,7 +40,7 @@
#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
(CONFIG_KEYPAD == IPOD_3G_PAD) || \
(CONFIG_KEYPAD == IPOD_1G2G_PAD)
-#define FRACTAL_QUIT (BUTTON_SELECT | BUTTON_MENU)
+#define FRACTAL_QUIT (BUTTON_SELECT | BUTTON_REPEAT)
#define FRACTAL_UP BUTTON_MENU
#define FRACTAL_DOWN BUTTON_PLAY
#define FRACTAL_LEFT BUTTON_LEFT
@@ -504,6 +504,9 @@
#define FRACTAL_PRECISION_DEC BUTTON_BACK
#define FRACTAL_RESET BUTTON_PLAY
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+#define FRACTAL_QUIT BUTTON_POWER
+
#else
#error No keymap defined!
#endif
diff --git a/apps/plugins/goban/goban.c b/apps/plugins/goban/goban.c
index 7b378eebff..b041f7cd73 100644
--- a/apps/plugins/goban/goban.c
+++ b/apps/plugins/goban/goban.c
@@ -524,6 +524,7 @@ plugin_start (const void *parameter)
case BUTTON_NONE:
is_idle = true;
+ /* fallthrough */
default:
if (rb->default_event_handler (btn) == SYS_USB_CONNECTED)
{
diff --git a/apps/plugins/imageviewer/gif/gif.c b/apps/plugins/imageviewer/gif/gif.c
index 31fd11d9a6..0521c29e3a 100644
--- a/apps/plugins/imageviewer/gif/gif.c
+++ b/apps/plugins/imageviewer/gif/gif.c
@@ -72,7 +72,7 @@ static void draw_image_rect(struct image_info *info,
static int img_mem(int ds)
{
struct gif_decoder *p_decoder = &decoder;
- return p_decoder->native_img_size/ds;
+ return (p_decoder->native_img_size/ds + 3) & ~3;
}
static int load_image(char *filename, struct image_info *info,
@@ -132,6 +132,8 @@ static int load_image(char *filename, struct image_info *info,
time = *rb->current_tick - time;
}
+ gif_decoder_destroy_memory_pool(p_decoder);
+
if (!iv->running_slideshow && !p_decoder->error)
{
rb->snprintf(print, sizeof(print), " %ld.%02ld sec ", time/HZ, time%HZ);
@@ -142,6 +144,9 @@ static int load_image(char *filename, struct image_info *info,
if (p_decoder->error)
{
+ if (p_decoder->error == D_GIF_ERR_NOT_ENOUGH_MEM)
+ return PLUGIN_OUTOFMEM;
+
rb->splashf(HZ, "%s", GifErrorString(p_decoder->error));
return PLUGIN_ERROR;
}
@@ -157,12 +162,9 @@ static int load_image(char *filename, struct image_info *info,
img_size = (p_decoder->native_img_size*p_decoder->frames_count + 3) & ~3;
disp_size = (sizeof(unsigned char *)*p_decoder->frames_count*4 + 3) & ~3;
+ /* No memory to allocate disp matrix */
if (memory_size < img_size + disp_size)
- {
- /* No memory to allocate disp matrix */
- rb->splashf(HZ, "%s", GifErrorString(D_GIF_ERR_NOT_ENOUGH_MEM));
- return PLUGIN_ERROR;
- }
+ return PLUGIN_OUTOFMEM;
disp = (unsigned char **)(p_decoder->mem + img_size);
disp_buf = (unsigned char *)disp + disp_size;
@@ -210,7 +212,7 @@ static int get_image(struct image_info *info, int frame, int ds)
/* assign image buffer */
if (ds > 1)
{
- if (!iv->running_slideshow)
+ if (!iv->running_slideshow && (info->frames_count == 1))
{
rb->lcd_putsf(0, 3, "resizing %d*%d", info->width, info->height);
rb->lcd_update();
diff --git a/apps/plugins/imageviewer/gif/gif_decoder.c b/apps/plugins/imageviewer/gif/gif_decoder.c
index f57eab1cf1..a1fda43a46 100644
--- a/apps/plugins/imageviewer/gif/gif_decoder.c
+++ b/apps/plugins/imageviewer/gif/gif_decoder.c
@@ -115,6 +115,11 @@ void gif_decoder_init(struct gif_decoder *d, void *mem, size_t size)
init_memory_pool(d->mem_size, d->mem);
}
+void gif_decoder_destroy_memory_pool(struct gif_decoder *d)
+{
+ destroy_memory_pool(d->mem);
+}
+
void gif_open(char *filename, struct gif_decoder *d)
{
if ((GifFile = DGifOpenFileName(filename, &d->error)) == NULL)
@@ -181,6 +186,7 @@ void gif_decode(struct gif_decoder *d,
{
/* error allocating temp space */
d->error = D_GIF_ERR_NOT_ENOUGH_MEM;
+ DGifCloseFile(GifFile);
return;
}
@@ -196,7 +202,7 @@ void gif_decode(struct gif_decoder *d,
if (pixels_buffer[0] == NULL)
{
d->error = D_GIF_ERR_NOT_ENOUGH_MEM;
- return;
+ goto free_and_return;
}
/* Global background color */
@@ -215,7 +221,7 @@ void gif_decode(struct gif_decoder *d,
if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR)
{
d->error = GifFile->Error;
- return;
+ goto free_and_return;
}
switch (RecordType)
@@ -225,7 +231,7 @@ void gif_decode(struct gif_decoder *d,
if (DGifGetImageDesc(GifFile) == GIF_ERROR)
{
d->error = GifFile->Error;
- return;
+ goto free_and_return;
}
/* Image Position relative to canvas */
@@ -239,7 +245,7 @@ void gif_decode(struct gif_decoder *d,
GifFile->SColorMap == NULL)
{
d->error = D_GIF_ERR_NO_COLOR_MAP;
- return;
+ goto free_and_return;
}
/* sanity check */
@@ -247,7 +253,7 @@ void gif_decode(struct gif_decoder *d,
GifFile->Image.Top+GifFile->Image.Height>GifFile->SHeight)
{
d->error = D_GIF_ERR_DATA_TOO_BIG;
- return;
+ goto free_and_return;
}
if (GifFile->Image.GCB &&
@@ -275,7 +281,7 @@ void gif_decode(struct gif_decoder *d,
if (DGifGetLine(GifFile, Line, Width) == GIF_ERROR)
{
d->error = GifFile->Error;
- return;
+ goto free_and_return;
}
gif2pixels(Line, pixels_buffer[buf_idx],
@@ -294,14 +300,14 @@ void gif_decode(struct gif_decoder *d,
if (DGifGetLine(GifFile, Line, Width) == GIF_ERROR)
{
d->error = GifFile->Error;
- return;
+ goto free_and_return;
}
gif2pixels(Line, pixels_buffer[buf_idx],
Row + i, Col, Width);
if (pf_progress != NULL)
- pf_progress(25*(i+1), 100);
+ pf_progress((i+1), Height);
}
}
@@ -310,7 +316,7 @@ void gif_decode(struct gif_decoder *d,
if (out == NULL)
{
d->error = D_GIF_ERR_NOT_ENOUGH_MEM;
- return;
+ goto free_and_return;
}
bm.data = out + d->native_img_size*d->frames_count;
@@ -350,7 +356,7 @@ void gif_decode(struct gif_decoder *d,
GIF_ERROR)
{
d->error = GifFile->Error;
- return;
+ goto free_and_return;
}
if (ExtCode == GRAPHICS_EXT_FUNC_CODE)
@@ -364,7 +370,7 @@ void gif_decode(struct gif_decoder *d,
GifFile->Image.GCB) == GIF_ERROR)
{
d->error = GifFile->Error;
- return;
+ goto free_and_return;
}
d->delay = GifFile->Image.GCB->DelayTime;
}
@@ -375,7 +381,7 @@ void gif_decode(struct gif_decoder *d,
if (DGifGetExtensionNext(GifFile, &Extension) == GIF_ERROR)
{
d->error = GifFile->Error;
- return;
+ goto free_and_return;
}
}
break;
@@ -391,6 +397,10 @@ void gif_decode(struct gif_decoder *d,
if (DGifCloseFile(GifFile) == GIF_ERROR)
{
d->error = GifFile->Error;
+ free(pixels_buffer[0]);
+ if (pixels_buffer[1])
+ free(pixels_buffer[1]);
+ free(Line);
return;
}
@@ -472,4 +482,15 @@ void gif_decode(struct gif_decoder *d,
}
}
#endif
+ return;
+
+free_and_return:
+ if (Line)
+ free(Line);
+ if (pixels_buffer[0])
+ free(pixels_buffer[0]);
+ if (pixels_buffer[1])
+ free(pixels_buffer[1]);
+ DGifCloseFile(GifFile);
+ return;
}
diff --git a/apps/plugins/imageviewer/gif/gif_decoder.h b/apps/plugins/imageviewer/gif/gif_decoder.h
index ce8a3fa292..6af775a324 100644
--- a/apps/plugins/imageviewer/gif/gif_decoder.h
+++ b/apps/plugins/imageviewer/gif/gif_decoder.h
@@ -30,6 +30,7 @@ struct gif_decoder {
};
void gif_decoder_init(struct gif_decoder *decoder, void *mem, size_t size);
+void gif_decoder_destroy_memory_pool(struct gif_decoder *d);
void gif_open(char *filename, struct gif_decoder *d);
void gif_decode(struct gif_decoder *d, void (*pf_progress)(int current, int total));
diff --git a/apps/plugins/imageviewer/image_decoder.c b/apps/plugins/imageviewer/image_decoder.c
index eab1c01dbc..0c1776daaa 100644
--- a/apps/plugins/imageviewer/image_decoder.c
+++ b/apps/plugins/imageviewer/image_decoder.c
@@ -155,7 +155,10 @@ const struct image_decoder *load_decoder(struct loader_info *loader_info)
goto error_close;
}
- if (lc_hdr->api_version != IMGDEC_API_VERSION)
+ if (lc_hdr->api_version != IMGDEC_API_VERSION ||
+ hdr->img_api_size > sizeof(struct imgdec_api) ||
+ hdr->plugin_api_version != PLUGIN_API_VERSION ||
+ hdr->plugin_api_size > sizeof(struct plugin_api))
{
rb->splashf(2*HZ, "%s decoder: Incompatible version.", name);
goto error_close;
diff --git a/apps/plugins/imageviewer/imageviewer.c b/apps/plugins/imageviewer/imageviewer.c
index 4b1a982438..d1a512c4fd 100644
--- a/apps/plugins/imageviewer/imageviewer.c
+++ b/apps/plugins/imageviewer/imageviewer.c
@@ -127,6 +127,9 @@ static int curfile = -1, direction = DIR_NEXT, entries = 0;
/* list of the supported image files */
static char **file_pt;
+/* progress update tick */
+static long next_progress_tick;
+
static const struct image_decoder *imgdec = NULL;
static enum image_type image_type = IMAGE_UNKNOWN;
@@ -192,7 +195,11 @@ static int change_filename(int direct)
return PLUGIN_ERROR;
}
- rb->strcpy(rb->strrchr(np_file, '/')+1, file_pt[curfile]);
+ size_t np_file_length = rb->strlen(np_file);
+ size_t np_file_name_length = rb->strlen(rb->strrchr(np_file, '/')+1);
+ size_t avail_length = sizeof(np_file) - (np_file_length - np_file_name_length);
+
+ rb->snprintf(rb->strrchr(np_file, '/')+1, avail_length, "%s", file_pt[curfile]);
return PLUGIN_OTHER;
}
@@ -223,15 +230,15 @@ static bool set_option_dithering(void)
[DITHER_DIFFUSION] = { STR(LANG_DIFFUSION) },
};
- rb->set_option(rb->str(LANG_DITHERING), &settings.jpeg_dither_mode, INT,
+ rb->set_option(rb->str(LANG_DITHERING), &settings.jpeg_dither_mode, RB_INT,
dithering, DITHER_NUM_MODES, NULL);
return false;
}
MENUITEM_FUNCTION(grayscale_item, 0, ID2P(LANG_GRAYSCALE),
- set_option_grayscale, NULL, NULL, Icon_NOICON);
+ set_option_grayscale, NULL, Icon_NOICON);
MENUITEM_FUNCTION(dithering_item, 0, ID2P(LANG_DITHERING),
- set_option_dithering, NULL, NULL, Icon_NOICON);
+ set_option_dithering, NULL, Icon_NOICON);
MAKE_MENU(display_menu, "Display Options", NULL, Icon_NOICON,
&grayscale_item, &dithering_item);
@@ -283,7 +290,7 @@ static int show_menu(void) /* return 1 to quit */
case MIID_RETURN:
break;
case MIID_TOGGLE_SS_MODE:
- rb->set_option(rb->str(LANG_SLIDESHOW_MODE), &iv_api.slideshow_enabled, BOOL,
+ rb->set_option(rb->str(LANG_SLIDESHOW_MODE), &iv_api.slideshow_enabled, RB_BOOL,
slideshow , 2, NULL);
break;
case MIID_CHANGE_SS_MODE:
@@ -346,7 +353,7 @@ static int show_menu(void) /* return 1 to quit */
static int ask_and_get_audio_buffer(const char *filename)
{
int button;
-#if defined(IMGVIEW_ZOOM_PRE)
+#if defined(IMGVIEW_ZOOM_PRE) || defined(IMGVIEW_QUIT_PRE)
int lastbutton = BUTTON_NONE;
#endif
rb->lcd_setfont(FONT_SYSFIXED);
@@ -385,6 +392,10 @@ static int ask_and_get_audio_buffer(const char *filename)
#endif
#ifdef IMGVIEW_QUIT
case IMGVIEW_QUIT:
+#ifdef IMGVIEW_QUIT_PRE
+ if (lastbutton != IMGVIEW_QUIT_PRE)
+ break;
+#endif
#endif
case IMGVIEW_MENU:
return PLUGIN_OK;
@@ -417,7 +428,7 @@ static int ask_and_get_audio_buffer(const char *filename)
== SYS_USB_CONNECTED)
return PLUGIN_USB_CONNECTED;
}
-#if defined(IMGVIEW_ZOOM_PRE)
+#if defined(IMGVIEW_ZOOM_PRE) || defined(IMGVIEW_QUIT_PRE)
if (button != BUTTON_NONE)
lastbutton = button;
#endif
@@ -428,7 +439,14 @@ static int ask_and_get_audio_buffer(const char *filename)
/* callback updating a progress meter while image decoding */
static void cb_progress(int current, int total)
{
- rb->yield(); /* be nice to the other threads */
+ /* do not yield or update the progress bar if we did so too recently */
+ long now = *rb->current_tick;
+ if(!TIME_AFTER(now, next_progress_tick))
+ return;
+
+ /* limit to 20fps */
+ next_progress_tick = now + HZ/20;
+
#ifndef USEGSLIB
/* in slideshow mode, keep gui interference to a minimum */
const int size = (!iv_api.running_slideshow ? 8 : 4);
@@ -442,6 +460,8 @@ static void cb_progress(int current, int total)
total, 0, current, HORIZONTAL);
rb->lcd_update_rect(0, LCD_HEIGHT-size, LCD_WIDTH, size);
}
+
+ rb->yield(); /* be nice to the other threads */
}
#define VSCROLL (LCD_HEIGHT/8)
@@ -556,14 +576,19 @@ static void pan_view_down(struct image_info *info)
}
/* interactively scroll around the image */
-static int scroll_bmp(struct image_info *info)
+static int scroll_bmp(struct image_info *info, bool initial_frame)
{
static long ss_timeout = 0;
int button;
#if defined(IMGVIEW_ZOOM_PRE) || defined(IMGVIEW_MENU_PRE) \
- || defined(IMGVIEW_SLIDE_SHOW_PRE)
- int lastbutton = BUTTON_NONE;
+ || defined(IMGVIEW_SLIDE_SHOW_PRE) || defined(IMGVIEW_QUIT_PRE)
+ static int lastbutton;
+ if (initial_frame)
+ lastbutton = BUTTON_NONE;
+
+#else
+ (void) initial_frame;
#endif
if (!ss_timeout && iv_api.slideshow_enabled)
@@ -600,6 +625,7 @@ static int scroll_bmp(struct image_info *info)
if (entries > 1 && info->width <= LCD_WIDTH
&& info->height <= LCD_HEIGHT)
return change_filename(DIR_PREV);
+ /* fallthrough */
case IMGVIEW_LEFT | BUTTON_REPEAT:
pan_view_left(info);
break;
@@ -608,17 +634,26 @@ static int scroll_bmp(struct image_info *info)
if (entries > 1 && info->width <= LCD_WIDTH
&& info->height <= LCD_HEIGHT)
return change_filename(DIR_NEXT);
+ /* fallthrough */
case IMGVIEW_RIGHT | BUTTON_REPEAT:
pan_view_right(info);
break;
case IMGVIEW_UP:
case IMGVIEW_UP | BUTTON_REPEAT:
+#ifdef IMGVIEW_SCROLL_UP
+ case IMGVIEW_SCROLL_UP:
+ case IMGVIEW_SCROLL_UP | BUTTON_REPEAT:
+#endif
pan_view_up(info);
break;
case IMGVIEW_DOWN:
case IMGVIEW_DOWN | BUTTON_REPEAT:
+#ifdef IMGVIEW_SCROLL_DOWN
+ case IMGVIEW_SCROLL_DOWN:
+ case IMGVIEW_SCROLL_DOWN | BUTTON_REPEAT:
+#endif
pan_view_down(info);
break;
@@ -719,6 +754,10 @@ static int scroll_bmp(struct image_info *info)
#ifdef IMGVIEW_QUIT
case IMGVIEW_QUIT:
+#ifdef IMGVIEW_QUIT_PRE
+ if (lastbutton != IMGVIEW_QUIT_PRE)
+ break;
+#endif
return PLUGIN_OK;
break;
#endif
@@ -730,7 +769,8 @@ static int scroll_bmp(struct image_info *info)
break;
} /* switch */
-#if defined(IMGVIEW_ZOOM_PRE) || defined(IMGVIEW_MENU_PRE) || defined(IMGVIEW_SLIDE_SHOW_PRE)
+#if defined(IMGVIEW_ZOOM_PRE) || defined(IMGVIEW_MENU_PRE) ||\
+ defined(IMGVIEW_SLIDE_SHOW_PRE) || defined(IMGVIEW_QUIT_PRE)
if (button != BUTTON_NONE)
lastbutton = button;
#endif
@@ -896,6 +936,7 @@ static int load_and_show(char* filename, struct image_info *info)
/* used to loop through subimages in animated gifs */
int frame = 0;
+ bool initial_frame = true;
do /* loop the image prepare and decoding when zoomed */
{
status = imgdec->get_image(info, frame, ds); /* decode or fetch from cache */
@@ -913,7 +954,8 @@ static int load_and_show(char* filename, struct image_info *info)
rb->lcd_update();
}
- mylcd_ub_clear_display();
+ if (frame == 0)
+ mylcd_ub_clear_display();
imgdec->draw_image_rect(info, 0, 0,
info->width-info->x, info->height-info->y);
mylcd_ub_update();
@@ -927,7 +969,8 @@ static int load_and_show(char* filename, struct image_info *info)
*/
while (1)
{
- status = scroll_bmp(info);
+ status = scroll_bmp(info, initial_frame);
+ initial_frame = false;
if (status == ZOOM_IN)
{
@@ -954,6 +997,7 @@ static int load_and_show(char* filename, struct image_info *info)
get_view(info, &cx, &cy);
cx /= zoom; /* prepare the position in the new image */
cy /= zoom;
+ mylcd_ub_clear_display();
}
else
continue;
@@ -966,6 +1010,10 @@ static int load_and_show(char* filename, struct image_info *info)
break;
}
+#ifdef USEGSLIB
+ if (info->frames_count <= 1)
+ grey_show(false); /* switch off overlay */
+#endif
rb->lcd_clear_display();
}
while (status > PLUGIN_OTHER);
@@ -1032,10 +1080,8 @@ enum plugin_status plugin_start(const void* parameter)
ARRAYLEN(config), IMGVIEW_SETTINGS_MINVERSION);
rb->memcpy(&old_settings, &settings, sizeof (settings));
-#ifdef HAVE_BACKLIGHT
/* Turn off backlight timeout */
backlight_ignore_timeout();
-#endif
#if LCD_DEPTH > 1
rb->lcd_set_backdrop(NULL);
@@ -1062,10 +1108,8 @@ enum plugin_status plugin_start(const void* parameter)
rb->storage_spindown(rb->global_settings->disk_spindown);
#endif
-#ifdef HAVE_BACKLIGHT
/* Turn on backlight timeout (revert to settings) */
backlight_use_settings();
-#endif
#ifdef USEGSLIB
grey_release(); /* deinitialize */
diff --git a/apps/plugins/imageviewer/imageviewer.h b/apps/plugins/imageviewer/imageviewer.h
index 19b5db15bb..ac15df5960 100644
--- a/apps/plugins/imageviewer/imageviewer.h
+++ b/apps/plugins/imageviewer/imageviewer.h
@@ -136,14 +136,17 @@ struct image_decoder {
int x, int y, int width, int height);
};
-#define IMGDEC_API_VERSION (PLUGIN_API_VERSION << 4 | 0)
+#define IMGDEC_API_VERSION 1
/* image decoder header */
struct imgdec_header {
struct lc_header lc_hdr; /* must be the first */
const struct image_decoder *decoder;
const struct plugin_api **api;
+ unsigned short plugin_api_version;
+ size_t plugin_api_size;
const struct imgdec_api **img_api;
+ size_t img_api_size;
};
#ifdef IMGDEC
@@ -157,15 +160,18 @@ extern const struct image_decoder image_decoder;
const struct imgdec_header __header \
__attribute__ ((section (".header")))= { \
{ PLUGIN_MAGIC, TARGET_ID, IMGDEC_API_VERSION, \
- plugin_start_addr, plugin_end_addr }, &image_decoder, &rb, &iv };
+ plugin_start_addr, plugin_end_addr, }, &image_decoder, \
+ &rb, PLUGIN_API_VERSION, sizeof(struct plugin_api), \
+ &iv, sizeof(struct imgdec_api) };
#else /* PLATFORM_HOSTED */
#define IMGDEC_HEADER \
const struct plugin_api *rb DATA_ATTR; \
const struct imgdec_api *iv DATA_ATTR; \
const struct imgdec_header __header \
__attribute__((visibility("default"))) = { \
- { PLUGIN_MAGIC, TARGET_ID, IMGDEC_API_VERSION, \
- NULL, NULL }, &image_decoder, &rb, &iv };
+ { PLUGIN_MAGIC, TARGET_ID, IMGDEC_API_VERSION, NULL, NULL }, \
+ &image_decoder, &rb, PLUGIN_API_VERSION, sizeof(struct plugin_api), \
+ &iv, sizeof(struct imgdec_api), };
#endif /* CONFIG_PLATFORM */
#endif
diff --git a/apps/plugins/imageviewer/imageviewer_button.h b/apps/plugins/imageviewer/imageviewer_button.h
index d588de95ca..d324f93292 100644
--- a/apps/plugins/imageviewer/imageviewer_button.h
+++ b/apps/plugins/imageviewer/imageviewer_button.h
@@ -53,8 +53,10 @@
#define IMGVIEW_RIGHT BUTTON_RIGHT
#define IMGVIEW_NEXT (BUTTON_SELECT | BUTTON_RIGHT)
#define IMGVIEW_PREVIOUS (BUTTON_SELECT | BUTTON_LEFT)
-#define IMGVIEW_MENU (BUTTON_SELECT | BUTTON_MENU)
-#define IMGVIEW_QUIT (BUTTON_SELECT | BUTTON_PLAY)
+#define IMGVIEW_MENU_PRE BUTTON_SELECT
+#define IMGVIEW_MENU (BUTTON_SELECT | BUTTON_REPEAT)
+#define IMGVIEW_QUIT_PRE BUTTON_SELECT
+#define IMGVIEW_QUIT (BUTTON_SELECT | BUTTON_REL)
#elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
#define IMGVIEW_ZOOM_PRE BUTTON_SELECT
@@ -529,7 +531,9 @@
#define IMGVIEW_ZOOM_IN BUTTON_VOL_UP
#define IMGVIEW_ZOOM_OUT BUTTON_VOL_DOWN
#define IMGVIEW_UP BUTTON_UP
+#define IMGVIEW_SCROLL_UP BUTTON_SCROLL_BACK
#define IMGVIEW_DOWN BUTTON_DOWN
+#define IMGVIEW_SCROLL_DOWN BUTTON_SCROLL_FWD
#define IMGVIEW_LEFT BUTTON_LEFT
#define IMGVIEW_RIGHT BUTTON_RIGHT
#define IMGVIEW_NEXT BUTTON_BACK
@@ -539,6 +543,9 @@
#define IMGVIEW_MENU BUTTON_POWER
#define IMGVIEW_SLIDE_SHOW BUTTON_PLAY
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+/* use touchscreen */
+
#else
#error No keymap defined!
#endif
diff --git a/apps/plugins/imageviewer/jpeg/jpeg_decoder.c b/apps/plugins/imageviewer/jpeg/jpeg_decoder.c
index b014fa0ed1..ef7d5ddb3d 100644
--- a/apps/plugins/imageviewer/jpeg/jpeg_decoder.c
+++ b/apps/plugins/imageviewer/jpeg/jpeg_decoder.c
@@ -61,14 +61,9 @@ INLINE unsigned range_limit(int value)
return value;
#else
value += 128;
-
- if ((unsigned)value <= 255)
- return value;
-
- if (value < 0)
- return 0;
-
- return 255;
+ if(value < 0) return 0;
+ if(value > 255) return 255;
+ return value;
#endif
}
diff --git a/apps/plugins/imageviewer/jpeg/yuv2rgb.c b/apps/plugins/imageviewer/jpeg/yuv2rgb.c
index 61d7fd6487..3e7f08d8bc 100644
--- a/apps/plugins/imageviewer/jpeg/yuv2rgb.c
+++ b/apps/plugins/imageviewer/jpeg/yuv2rgb.c
@@ -238,7 +238,7 @@ static fb_data (* const pixel_funcs[COLOUR_NUM_MODES][DITHER_NUM_MODES])(void) =
};
/* These defines are used fornormal horizontal strides and vertical strides. */
-#if defined(LCD_STRIDEFORMAT) && LCD_STRIDEFORMAT == VERTICAL_STRIDE
+#if LCD_STRIDEFORMAT == VERTICAL_STRIDE
#define LCDADDR(x, y) (lcd_fb + LCD_HEIGHT*(x) + (y))
#define ROWENDOFFSET (width*LCD_HEIGHT)
#define ROWOFFSET (1)
diff --git a/apps/plugins/imageviewer/ppm/ppm_decoder.c b/apps/plugins/imageviewer/ppm/ppm_decoder.c
index 4a86be1a3a..ccb208b80b 100644
--- a/apps/plugins/imageviewer/ppm/ppm_decoder.c
+++ b/apps/plugins/imageviewer/ppm/ppm_decoder.c
@@ -177,7 +177,7 @@ static int read_ppm_row(int fd, struct ppm_info *ppm, int row)
int col;
int r, g, b;
#ifdef HAVE_LCD_COLOR
-#if defined(LCD_STRIDEFORMAT) && LCD_STRIDEFORMAT == VERTICAL_STRIDE
+#if LCD_STRIDEFORMAT == VERTICAL_STRIDE
fb_data *dst = (fb_data *) ppm->buf + row;
const int stride = ppm->x;
#else
diff --git a/apps/plugins/invadrox.c b/apps/plugins/invadrox.c
index c28ab7e830..d130ab6108 100644
--- a/apps/plugins/invadrox.c
+++ b/apps/plugins/invadrox.c
@@ -141,14 +141,6 @@
#define RIGHT BUTTON_RIGHT
#define FIRE BUTTON_SELECT
-#elif CONFIG_KEYPAD == TATUNG_TPJ1022_PAD
-
-/* TODO: Figure out which buttons to use for Tatung Elio TPJ-1022 */
-#define QUIT BUTTON_AB
-#define LEFT BUTTON_LEFT
-#define RIGHT BUTTON_RIGHT
-#define FIRE BUTTON_MENU
-
#elif CONFIG_KEYPAD == GIGABEAT_S_PAD
#define QUIT BUTTON_BACK
@@ -296,6 +288,9 @@ CONFIG_KEYPAD == MROBE500_PAD
#define RIGHT BUTTON_RIGHT
#define FIRE BUTTON_SELECT
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+/* use touchscreen */
+
#else
#error INVADROX: Unsupported keypad
#endif
@@ -614,7 +609,7 @@ CONFIG_KEYPAD == MROBE500_PAD
#elif (LCD_WIDTH == 220) && (LCD_HEIGHT == 176)
-/* TPJ1022, H300, iPod Color: 220x176x16
+/* H300, iPod Color: 220x176x16
* ============================
* X: 0p padding at left/right gives 220p playfield in middle.
* 8p "border" gives 204p actual playfield. UFO use full 220p.
@@ -648,6 +643,23 @@ CONFIG_KEYPAD == MROBE500_PAD
#define LIVES_X 8
#define MAX_Y 15
+#elif (LCD_WIDTH == 360) && (LCD_HEIGHT == 400)
+
+/* Shanling Q1
+ */
+#define ARCADISH_GRAPHICS
+#define PLAYFIELD_X 32
+#define SHIP_Y (PLAYFIELD_Y - 3 * SHIP_HEIGHT)
+#define ALIEN_START_Y (UFO_Y + 3 * ALIEN_HEIGHT)
+/* Redefine SCORE_Y */
+#undef SCORE_Y
+#define SCORE_Y 80
+#define SCORENUM_X (PLAYFIELD_X + NUMBERS_WIDTH)
+#define SCORENUM_Y SCORE_Y + (2 * (FONT_HEIGHT + 1) + 1)
+#define HISCORENUM_X (LCD_WIDTH - PLAYFIELD_X - 1 - 6 * NUMBERS_WIDTH - 5 * NUM_SPACING)
+#define SHIELD_Y (PLAYFIELD_Y - 6 * SHIP_HEIGHT)
+#define LIVES_X 10
+#define MAX_Y 18
#else
#error INVADROX: Unsupported LCD type
@@ -773,7 +785,7 @@ static fb_data *lcd_fb;
/* No standard get_pixel function yet, use this hack instead */
#if (LCD_DEPTH >= 8)
-#if defined(LCD_STRIDEFORMAT) && LCD_STRIDEFORMAT == VERTICAL_STRIDE
+#if LCD_STRIDEFORMAT == VERTICAL_STRIDE
static inline fb_data get_pixel(int x, int y)
{
return lcd_fb[x*LCD_HEIGHT+y];
diff --git a/apps/plugins/jackpot.c b/apps/plugins/jackpot.c
index 78f74568ca..fd878509cf 100644
--- a/apps/plugins/jackpot.c
+++ b/apps/plugins/jackpot.c
@@ -25,6 +25,13 @@
#include "lib/pluginlib_exit.h"
+#if (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_3G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_4G_PAD)
+#define JACKPOT_QUIT PLA_UP
+#else
+#define JACKPOT_QUIT PLA_CANCEL
+#endif
const struct button_mapping* plugin_contexts[]={pla_main_ctx};
#define NB_PICTURES 9
@@ -248,7 +255,7 @@ enum plugin_status plugin_start(const void* parameter)
plugin_contexts, ARRAYLEN(plugin_contexts));
switch ( action )
{
- case PLA_CANCEL:
+ case JACKPOT_QUIT:
return PLUGIN_OK;
case PLA_SELECT:
jackpot_play_turn(&game);
diff --git a/apps/plugins/jewels.c b/apps/plugins/jewels.c
index 7f7965b07d..3a65e8ba64 100644
--- a/apps/plugins/jewels.c
+++ b/apps/plugins/jewels.c
@@ -49,9 +49,9 @@
#define JEWELS_PREV BUTTON_SCROLL_BACK
#define JEWELS_NEXT BUTTON_SCROLL_FWD
#define JEWELS_SELECT BUTTON_SELECT
-#define JEWELS_CANCEL (BUTTON_SELECT | BUTTON_MENU)
+#define JEWELS_CANCEL (BUTTON_SELECT | BUTTON_REPEAT)
#define HK_SELECT "SELECT"
-#define HK_CANCEL "SEL + MENU"
+#define HK_CANCEL "Long SELECT"
#elif (CONFIG_KEYPAD == IPOD_3G_PAD)
#define JEWELS_LEFT BUTTON_LEFT
@@ -377,6 +377,9 @@ CONFIG_KEYPAD == MROBE500_PAD
#define HK_SELECT "SELECT"
#define HK_CANCEL "BACK"
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+/* use touchscreen */
+
#else
#error No keymap defined!
#endif
@@ -1478,7 +1481,7 @@ static int jewels_game_menu(struct game_context* bj, bool ingame)
jewels_init(bj);
return 0;
case 2:
- rb->set_option("Mode", &bj->tmp_type, INT, mode, 2, NULL);
+ rb->set_option("Mode", &bj->tmp_type, RB_INT, mode, 2, NULL);
break;
case 3:
if(jewels_help())
diff --git a/apps/plugins/keybox.c b/apps/plugins/keybox.c
index 1689321abe..cb2e23a94a 100644
--- a/apps/plugins/keybox.c
+++ b/apps/plugins/keybox.c
@@ -75,7 +75,7 @@ static void encrypt_buffer(char *buf, size_t size, uint32_t *key);
static void decrypt_buffer(char *buf, size_t size, uint32_t *key);
/* the following two functions are the reference TEA implementation by
- David Wheeler and Roger Needham taken from
+ David Wheeler and Roger Needham taken from
http://en.wikipedia.org/wiki/Tiny_Encryption_Algorithm */
static void do_encrypt(uint32_t* v, uint32_t* k)
@@ -567,7 +567,7 @@ static int keybox(void)
{
rb->gui_synclist_draw(&kb_list);
button = rb->get_action(CONTEXT_LIST, TIMEOUT_BLOCK);
- if (rb->gui_synclist_do_button(&kb_list, &button, LIST_WRAP_ON))
+ if (rb->gui_synclist_do_button(&kb_list, &button))
continue;
switch (button)
@@ -659,7 +659,6 @@ enum plugin_status plugin_start(const void *parameter)
rb->gui_synclist_set_title(&kb_list, "Keybox", NOICON);
rb->gui_synclist_set_icon_callback(&kb_list, NULL);
rb->gui_synclist_set_nb_items(&kb_list, 0);
- rb->gui_synclist_limit_scroll(&kb_list, false);
rb->gui_synclist_select_item(&kb_list, 0);
init_ll();
diff --git a/apps/plugins/keyremap.c b/apps/plugins/keyremap.c
new file mode 100644
index 0000000000..202d5fcfa4
--- /dev/null
+++ b/apps/plugins/keyremap.c
@@ -0,0 +1,2228 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// __ \_/ ___\| |/ /| __ \ / __ \ \/ /
+ * Jukebox | | ( (__) ) \___| ( | \_\ ( (__) ) (
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2022 William Wilgus
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "plugin.h"
+#include "lang_enum.h"
+
+#include "lib/action_helper.h"
+#include "lib/button_helper.h"
+#include "lib/pluginlib_actions.h"
+#include "lib/printcell_helper.h"
+#include "lib/kbd_helper.h"
+
+#ifdef ROCKBOX_HAS_LOGF
+#define logf rb->logf
+#else
+#define logf(...) do { } while(0)
+#endif
+
+/* CORE_KEYREMAP_FILE */
+#include "../core_keymap.h"
+#define KMFDIR ROCKBOX_DIR
+#define KMFEXT1 ".kmf"
+#define KMFEXT2 ".kmf.old"
+#define KMFUSER "user_keyremap"
+#define MAX_BUTTON_COMBO 5
+#define MAX_BUTTON_NAME 32
+#define MAX_MENU_NAME 64
+#define TEST_COUNTDOWN_MS 1590
+
+struct context_flags {
+ char * name;
+ int flag;
+};
+
+/* flags added to context_name[] */
+static struct context_flags context_flags[] = {
+ {"UNKNOWN", 0},/* index 0 is an Error */
+#ifndef HAS_BUTTON_HOLD
+ {"LOCKED", CONTEXT_LOCKED},
+#endif
+ /*{"PLUGIN", CONTEXT_PLUGIN}, need a custom action list and a way to supply */
+#if BUTTON_REMOTE != 0
+ {"REMOTE", CONTEXT_REMOTE},
+#ifndef HAS_BUTTON_HOLD
+ {"REMOTE_LOCKED", CONTEXT_REMOTE | CONTEXT_LOCKED},
+#endif
+#endif /* BUTTON_REMOTE != 0 */
+};
+
+static struct keyremap_buffer_t {
+ char * buffer;
+ size_t buf_size;
+ char *front;
+ char *end;
+} keyremap_buffer;
+
+
+struct action_mapping_t {
+ int context;
+ int display_pos;
+ struct button_mapping map;
+};
+
+static struct user_context_data_t {
+ struct action_mapping_t *ctx_map;
+ int ctx_count;
+ struct action_mapping_t *act_map;
+ int act_count;
+} ctx_data;
+
+/* set keys keymap */
+static struct setkeys_data_t {
+ /* save state in the set keys action view list */
+ int view_columns;
+ int view_lastcol;
+ uint32_t crc32;
+} keyset;
+
+/* test keys data */
+static struct testkey_data_t {
+ struct button_mapping *keymap;
+ int action;
+ int context;
+ int index;
+ int countdown;
+} keytest;
+
+static struct context_menu_data_t {
+ char *menuid;
+ int ctx_index;
+ int act_index;
+ int act_edit_index;
+ //const char * ctx_fmt;
+ const char * act_fmt;
+} ctx_menu_data;
+#define ACTIONFMT_LV0 "$%s$%s$%s$%s"
+#define ACTVIEW_HEADER "$Context$Action$Button$PreBtn"
+
+#define GOTO_ACTION_DEFAULT_HANDLER (PLUGIN_OK + 1)
+#define MENU_ID(x) (((void*)&mainmenu[x]))
+#define MENU_MAX_DEPTH 4
+/* this enum sets menu order */
+enum {
+ M_ROOT = 0,
+ M_SETKEYS,
+ M_TESTKEYS,
+ M_RESETKEYS,
+ M_EXPORTKEYS,
+ M_IMPORTKEYS,
+ M_SAVEKEYS,
+ M_LOADKEYS,
+ M_DELKEYS,
+ M_TMPCORE,
+ M_SETCORE,
+ M_DELCORE,
+ M_EXIT,
+ M_LAST_MAINITEM, //MAIN MENU ITEM COUNT
+/*Menus not directly accessible from main menu*/
+ M_ACTIONS = M_LAST_MAINITEM,
+ M_BUTTONS,
+ M_CONTEXTS,
+ M_CONTEXT_EDIT,
+ M_LAST_ITEM, //ITEM COUNT
+};
+
+struct mainmenu { const char *name; void *menuid; int index; int items;};
+static struct mainmenu mainmenu[M_LAST_ITEM] = {
+#define MENU_ITEM(ID, NAME, COUNT) [ID]{NAME, MENU_ID(ID), (int)ID, (int)COUNT}
+MENU_ITEM(M_ROOT, "Key Remap Plugin", M_LAST_MAINITEM - 1),
+MENU_ITEM(M_SETKEYS, "Edit Keymap", 1),
+MENU_ITEM(M_TESTKEYS, "Test Keymap", 4),
+MENU_ITEM(M_RESETKEYS, "Reset Keymap", 1),
+MENU_ITEM(M_EXPORTKEYS, "Export Text Keymap", 1),
+MENU_ITEM(M_IMPORTKEYS, "Import Text Keymap", 1),
+MENU_ITEM(M_SAVEKEYS, "Save Native Keymap", 1),
+MENU_ITEM(M_LOADKEYS, "Load Native Keymaps", 1),
+MENU_ITEM(M_DELKEYS, "Delete Keymaps", 1),
+MENU_ITEM(M_TMPCORE, "Temp Core Remap", 1),
+MENU_ITEM(M_SETCORE, "Set Core Remap", 1),
+MENU_ITEM(M_DELCORE, "Remove Core Remap", 1),
+MENU_ITEM(M_EXIT, ID2P(LANG_MENU_QUIT), 0),
+MENU_ITEM(M_ACTIONS, "Actions", LAST_ACTION_PLACEHOLDER),
+MENU_ITEM(M_BUTTONS, "Buttons", -1), /* Set at runtime in plugin_start: */
+MENU_ITEM(M_CONTEXTS, "Contexts", LAST_CONTEXT_PLACEHOLDER * ARRAYLEN(context_flags)),
+MENU_ITEM(M_CONTEXT_EDIT, "", 5),
+#undef MENU_ITEM
+};
+
+DIR* kmffiles_dirp = NULL;
+static int core_savecount = 0;/* so we don't overwrite the last .old file with revisions */
+
+/* global lists, for everything */
+static struct gui_synclist lists;
+
+static void menu_useract_set_positions(void);
+static size_t lang_strlcpy(char * dst, const char *src, size_t len)
+{
+ unsigned char **language_strings = rb->language_strings;
+ return rb->strlcpy(dst, P2STR((unsigned char*)src), len);
+}
+
+/* Menu stack macros */
+static int mlastsel[MENU_MAX_DEPTH * 2 + 1] = {0};
+#define PUSH_MENU(id, item) \
+ if(mlastsel[0] < MENU_MAX_DEPTH * 2) {mlastsel[++mlastsel[0]] = id;mlastsel[++mlastsel[0]] = item;}
+#define POP_MENU(id, item) { item = (mlastsel[0] > 0 ? mlastsel[mlastsel[0]--]:0); \
+ id = (mlastsel[0] > 0 ? mlastsel[mlastsel[0]--]:0); }
+#define PEEK_MENU_ITEM(item) { item = (mlastsel[0] > 0 ? mlastsel[mlastsel[0]]:0);}
+#define PEEK_MENU_ID(id) {id = (mlastsel[0] > 1 ? mlastsel[mlastsel[0]-1]:0);}
+#define PEEK_MENU(id, item) PEEK_MENU_ITEM(item) PEEK_MENU_ID(id)
+#define SET_MENU_ITEM(item) \
+ if(mlastsel[0] > 1) {mlastsel[mlastsel[0]] = item;}
+
+static struct mainmenu *mainitem(int selected_item)
+{
+ static struct mainmenu empty = {0};
+ if (selected_item >= 0 && selected_item < (int) ARRAYLEN(mainmenu))
+ return &mainmenu[selected_item];
+ else
+ return &empty;
+}
+/* Forward Declarations */
+static const char *edit_keymap_name_cb(int selected_item, void* data,char* buf, size_t buf_len);
+static int keyremap_import_file(char *filenamebuf, size_t bufsz);
+static void synclist_set(int id, int selected_item, int items, int sel_size);
+
+static int prompt_filename(char *buf, size_t bufsz)
+{
+#define KBD_LAYOUT "abcdefghijklmnop\nqrstuvwxyz |()[]\n1234567890 /._-+\n\n" \
+ "\nABCDEFGHIJKLMNOP\nQRSTUVWXYZ |()[]\n1234567890 /._-+"
+ unsigned short kbd[sizeof(KBD_LAYOUT) + 10];
+ unsigned short *kbd_p = kbd;
+ if (!kbd_create_layout(KBD_LAYOUT, kbd, sizeof(kbd)))
+ kbd_p = NULL;
+
+#undef KBD_LAYOUT
+ return rb->kbd_input(buf, bufsz, kbd_p) + 1;
+}
+
+static void synclist_set_update(int id, int selected_item, int items, int sel_size)
+{
+ SET_MENU_ITEM(lists.selected_item + 1); /* update selected for previous menu*/
+ synclist_set(id, selected_item, items, sel_size);
+}
+
+/* returns the actual context & index of the flag is passed in *flagidx */
+static int ctx_strip_flagidx(int ctx, int *flagidx)
+{
+ int ctx_out = ctx % (LAST_CONTEXT_PLACEHOLDER);
+ *flagidx = 0;
+ if (ctx_out != ctx)
+ {
+ while (ctx >= LAST_CONTEXT_PLACEHOLDER)
+ {
+ (*flagidx)++;
+ ctx -= LAST_CONTEXT_PLACEHOLDER;
+ }
+ if (*flagidx >= (int)ARRAYLEN(context_flags))
+ *flagidx = 0; /* unknown flag */
+
+ logf("%s ctx: (%d) %s flag idx: (%d) %s\n", __func__,
+ ctx, context_name(ctx), *flagidx, context_flags[*flagidx].name);
+ }
+ return ctx_out;
+}
+
+/* combines context name and flag name using '_' to join them */
+static char *ctx_name_and_flag(int index)
+{
+ static char ctx_namebuf[MAX_MENU_NAME];
+ char *ctx_name = "?";
+ int flagidx;
+ int ctx = ctx_strip_flagidx(index, &flagidx);
+ if (flagidx == 0)
+ {
+ ctx_name = context_name(ctx);
+ }
+ else if (flagidx >= 0 && flagidx < (int)ARRAYLEN(context_flags))
+ {
+ rb->snprintf(ctx_namebuf, sizeof(ctx_namebuf), "%s_%s",
+ context_name(ctx), context_flags[flagidx].name);
+ ctx_name = ctx_namebuf;
+ }
+ return ctx_name;
+}
+
+static int btnval_to_index(unsigned int btnvalue)
+{
+ int index = -1;
+ for (int i = 0; i < available_button_count; i++)
+ {
+ const struct available_button *btn = &available_buttons[i];
+ if (btnvalue == btn->value)
+ {
+ index = i;
+ break;
+ }
+ }
+ return index;
+}
+
+static int btnval_to_name(char *buf, size_t bufsz, unsigned int btnvalue)
+{
+ int index = btnval_to_index(btnvalue);
+ if (index >= 0)
+ {
+ return rb->snprintf(buf, bufsz, "%s", available_buttons[index].name);
+ }
+ else /* this provides names for button combos e.g.(BUTTON_UP|BUTTON_REPEAT) */
+ {
+ int res = get_button_names(buf, bufsz, btnvalue);
+ if (res > 0 && res <= (int)bufsz)
+ return res;
+ }
+ return rb->snprintf(buf, bufsz, "%s", "BUTTON_UNKNOWN");
+}
+
+static int keyremap_check_extension(const char* filename)
+{
+ int found = 0;
+ unsigned int len = rb->strlen(filename);
+ if (len > sizeof(KMFEXT1) &&
+ rb->strcmp(&filename[len - sizeof(KMFEXT1) + 1], KMFEXT1) == 0)
+ {
+ found = 1;
+ }
+ else if (len > sizeof(KMFEXT2) &&
+ rb->strcmp(&filename[len - sizeof(KMFEXT2) + 1], KMFEXT2) == 0)
+ {
+ found = 2;
+ }
+ return found;
+}
+
+static int keyremap_count_files(const char *directory)
+{
+ int nfiles = 0;
+ DIR* kmfdirp = NULL;
+ kmfdirp = rb->opendir(directory);
+ if (kmfdirp != NULL)
+ {
+ struct dirent *entry;
+ while ((entry = rb->readdir(kmfdirp)))
+ {
+ /* skip directories */
+ if ((rb->dir_get_info(kmfdirp, entry).attribute & ATTR_DIRECTORY) != 0)
+ continue;
+ if (keyremap_check_extension(entry->d_name) > 0)
+ nfiles++;
+ }
+ rb->closedir(kmfdirp);
+ }
+ return nfiles;
+}
+
+static void keyremap_reset_buffer(void)
+{
+ keyremap_buffer.front = keyremap_buffer.buffer;
+ keyremap_buffer.end = keyremap_buffer.front + keyremap_buffer.buf_size;
+ rb->memset(keyremap_buffer.front, 0, keyremap_buffer.buf_size);
+ ctx_data.ctx_map = (struct action_mapping_t*)keyremap_buffer.front;
+ ctx_data.ctx_count = 0;
+ ctx_data.act_map = (struct action_mapping_t*) keyremap_buffer.end;
+ ctx_data.act_count = 0;
+
+}
+
+static size_t keyremap_write_entries(int fd, struct button_mapping *data, int entry_count)
+{
+ if (fd < 0 || entry_count <= 0 || !data)
+ goto fail;
+ size_t bytes_req = sizeof(struct button_mapping) * entry_count;
+ size_t bytes = rb->write(fd, data, bytes_req);
+ if (bytes == bytes_req)
+ return bytes_req;
+fail:
+ return 0;
+}
+
+static size_t keyremap_write_header(int fd, int entry_count)
+{
+ if (fd < 0 || entry_count < 0)
+ goto fail;
+ struct button_mapping header = {KEYREMAP_VERSION, KEYREMAP_HEADERID, entry_count};
+ return keyremap_write_entries(fd, &header, 1);
+fail:
+ return 0;
+}
+
+static int keyremap_open_file(const char *filename, int *fd, size_t *fsize)
+{
+ int count = 0;
+
+ while (filename && fd && fsize)
+ {
+ *fsize = 0;
+ *fd = rb->open(filename, O_RDONLY);
+ if (*fd)
+ {
+ *fsize = rb->filesize(*fd);
+
+ count = *fsize / sizeof(struct button_mapping);
+
+ if (count * sizeof(struct button_mapping) != *fsize)
+ {
+ count = -10;
+ break;
+ }
+
+ if (count > 1)
+ {
+ struct button_mapping header = {0};
+ rb->read(*fd, &header, sizeof(struct button_mapping));
+ if (KEYREMAP_VERSION == header.action_code &&
+ KEYREMAP_HEADERID == header.button_code &&
+ header.pre_button_code == count)
+ {
+ count--;
+ *fsize -= sizeof(struct button_mapping);
+ }
+ else /* Header mismatch */
+ {
+ count = -20;
+ break;
+ }
+ }
+ }
+ break;
+ }
+ return count;
+}
+
+static int keyremap_map_is_valid(struct action_mapping_t *amap, int context)
+{
+ int ret = (amap->context == context ? 1: 0);
+ struct button_mapping entry = amap->map;
+ if (entry.action_code == (int)CONTEXT_STOPSEARCHING ||
+ entry.button_code == BUTTON_NONE)
+ {
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static struct button_mapping *keyremap_create_temp(int *entries)
+{
+ logf("%s()", __func__);
+ struct button_mapping *tempkeymap;
+ int action_offset = 1; /* start of action entries (ctx_count + 1)*/
+ int entry_count = 1;/* (ctx_count + ctx_count + act_count) */
+ int index = 0;
+ int i, j;
+
+ /* count includes a single stop sentinel for the list of contexts */
+ /* and a stop sentinel for each group of action remaps as well */
+ for (i = 0; i < ctx_data.ctx_count; i++)
+ {
+ int actions_this_ctx = 0;
+ int context = ctx_data.ctx_map[i].context;
+ /* how many actions are contained in each context? */
+ for (j = 0; j < ctx_data.act_count; j++)
+ {
+ if (keyremap_map_is_valid(&ctx_data.act_map[j], context) > 0)
+ {
+ actions_this_ctx++;
+ }
+ }
+ /* only count contexts with remapped actions */
+ if (actions_this_ctx != 0)
+ {
+ action_offset++;
+ entry_count += actions_this_ctx + 2;
+ }
+ }
+ size_t keymap_bytes = (entry_count) * sizeof(struct button_mapping);
+ *entries = entry_count;
+ logf("%s() entry count: %d", __func__, entry_count);
+ logf("keyremap bytes: %zu, avail: %zu", keymap_bytes,
+ (keyremap_buffer.end - keyremap_buffer.front));
+ if (keyremap_buffer.front + keymap_bytes < keyremap_buffer.end)
+ {
+ tempkeymap = (struct button_mapping *) keyremap_buffer.front;
+ rb->memset(tempkeymap, 0, keymap_bytes);
+ struct button_mapping *entry = &tempkeymap[index++];
+ for (i = 0; i < ctx_data.ctx_count; i++)
+ {
+ int actions_this_ctx = 0;
+ int flagidx;
+ int context = ctx_data.ctx_map[i].context;
+ /* how many actions are contained in each context? */
+ for (j = 0; j < ctx_data.act_count; j++)
+ {
+ if (keyremap_map_is_valid(&ctx_data.act_map[j], context) > 0)
+ {
+ actions_this_ctx += 1;
+ }
+ }
+ /*Don't save contexts with no actions */
+ if (actions_this_ctx == 0){ continue; }
+
+ /* convert context x flag to context | flag */
+ context = ctx_strip_flagidx(ctx_data.ctx_map[i].context, &flagidx);
+ context |= context_flags[flagidx].flag;
+
+ entry->action_code = CORE_CONTEXT_REMAP(context);
+ entry->button_code = action_offset; /* offset of first action entry */
+ entry->pre_button_code = actions_this_ctx; /* entries (excluding sentinel) */
+ entry = &tempkeymap[index++];
+ logf("keyremap found context: %d index: %d entries: %d",
+ context, action_offset, actions_this_ctx);
+ action_offset += actions_this_ctx + 1; /* including sentinel */
+ }
+ /* context sentinel */
+ entry->action_code = CONTEXT_STOPSEARCHING;;
+ entry->button_code = BUTTON_NONE;
+ entry->pre_button_code = BUTTON_NONE;
+ entry = &tempkeymap[index++];
+
+ for (i = 0; i < ctx_data.ctx_count; i++)
+ {
+ int actions_this_ctx = 0;
+ int context = ctx_data.ctx_map[i].context;
+
+ for (int j = 0; j < ctx_data.act_count; j++)
+ {
+ if (keyremap_map_is_valid(&ctx_data.act_map[j], context) > 0)
+ {
+ struct button_mapping map = ctx_data.act_map[j].map;
+ entry->action_code = map.action_code;
+ entry->button_code = map.button_code;
+ entry->pre_button_code = map.pre_button_code;
+ entry = &tempkeymap[index++];
+ actions_this_ctx++;
+ logf("keyremap: found ctx: %d, act: %d btn: %d pbtn: %d",
+ context, map.action_code, map.button_code, map.pre_button_code);
+ }
+ }
+ /*Don't save sentinel for contexts with no actions */
+ if (actions_this_ctx == 0){ continue; }
+
+ /* action sentinel */
+ entry->action_code = CONTEXT_STOPSEARCHING;;
+ entry->button_code = BUTTON_NONE;
+ entry->pre_button_code = BUTTON_NONE;
+ entry = &tempkeymap[index++];
+ }
+ }
+ else
+ {
+ rb->splashf(HZ *2, "Out of Memory");
+ logf("keyremap: create temp OOM");
+ *entries = 0;
+ tempkeymap = NULL;
+ }
+
+ return tempkeymap;
+}
+
+static int keyremap_save_current(const char *filename)
+{
+ int status = 0;
+ int entry_count;/* (ctx_count + ctx_count + act_count + 1) */
+
+ struct button_mapping *keymap = keyremap_create_temp(&entry_count);
+
+ if (keymap == NULL || entry_count <= 3) /* there should be atleast 4 entries */
+ return status;
+ keyset.crc32 =
+ rb->crc_32(keymap, entry_count * sizeof(struct button_mapping), 0xFFFFFFFF);
+ int keyfd = rb->open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+ if (keyremap_write_header(keyfd, entry_count + 1) > 0)
+ {
+ if (keyremap_write_entries(keyfd, keymap, entry_count) > 0)
+ status = 1;
+ }
+ rb->close(keyfd);
+
+ return status;
+}
+
+static void keyremap_save_user_keys(bool notify)
+{
+ char buf[MAX_PATH];
+ int i = 0;
+ do
+ {
+ i++;
+ rb->snprintf(buf, sizeof(buf), "%s/%s%d%s", KMFDIR, KMFUSER, i, KMFEXT1);
+ } while (i < 100 && rb->file_exists(buf));
+
+ if (notify && prompt_filename(buf, sizeof(buf)) <= 0)
+ {
+ return;
+ }
+
+ if (keyremap_save_current(buf) == 0)
+ {
+ logf("Error Saving");
+ if(notify)
+ rb->splash(HZ *2, "Error Saving");
+ }
+ else if (notify)
+ rb->splashf(HZ *2, "Saved %s", buf);
+}
+
+static int keyremap_export_current(char *filenamebuf, size_t bufsz)
+{
+ filenamebuf[bufsz - 1] = '\0';
+ int i, j;
+ int ctx_count = 0;
+ size_t entrylen;
+
+ int entry_count = ctx_data.ctx_count + ctx_data.act_count + 1;
+
+ if (entry_count < 3) /* the header is not counted should be at least 3 entries */
+ {
+ logf("%s: Not enough entries", __func__);
+ return 0;
+ }
+ int fd = rb->open(filenamebuf, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+
+ if (fd < 0)
+ return -1;
+ rb->fdprintf(fd, "# Key Remap\n# Device: %s\n" \
+ "# Entries: %d\n\n", MODEL_NAME, entry_count - 1);
+ rb->fdprintf(fd, "# Each entry should be PROPER_CASE and on its own line\n" \
+ "# Comments run to end of line \n");
+
+ for (i = 0; i <= entry_count; i++)
+ {
+ entrylen = 0;
+ rb->memset(filenamebuf, 0, bufsz);
+ edit_keymap_name_cb(i, MENU_ID(M_EXPORTKEYS), filenamebuf, bufsz);
+ if (i == 0)
+ {
+ ctx_menu_data.act_fmt = " {%s%s, %s, %s},\n\n";
+ continue;
+ }
+ else if (i == entry_count)
+ {
+ rb->strlcpy(filenamebuf, "}\n\n", bufsz);
+ }
+ char last = '\0';
+ for (j = 0; j < (int)bufsz ;j++)
+ {
+ char ch = filenamebuf[j];
+ if (ch == '$' && last == '\0') /*CONTEXT*/
+ {
+ if (ctx_count > 0)
+ rb->fdprintf(fd, "}\n");
+ ctx_count++;
+ filenamebuf[j] = '\n';
+ }
+ if (ch == '\n' && last == '\n')
+ {
+ entrylen = j;
+ break;
+ }
+ else if (ch == '\0')
+ filenamebuf[j] = ',';
+ last = ch;
+ }
+
+ size_t bytes = rb->write(fd, filenamebuf, entrylen);
+ if (bytes != entrylen)
+ {
+ entry_count = -2;
+ goto fail;
+ }
+ }
+
+fail:
+ rb->close(fd);
+
+ return entry_count;
+}
+
+static void keyremap_export_user_keys(void)
+{
+ const bool notify = true;
+ char buf[MAX_PATH];
+ int i = 0;
+ do
+ {
+ i++;
+ rb->snprintf(buf, sizeof(buf), "%s/%s%d%s", "", KMFUSER, i, ".txt");
+ } while (i < 100 && rb->file_exists(buf));
+
+ if (notify && prompt_filename(buf, sizeof(buf)) <= 0)
+ {
+ return;
+ }
+
+ if (keyremap_export_current(buf, sizeof(buf)) <= 0)
+ {
+ if(notify)
+ rb->splash(HZ *2, "Error Saving");
+ }
+ else if (notify)
+ {
+ rb->snprintf(buf, sizeof(buf), "%s/%s%d%s", "", KMFUSER, i, ".txt");
+ rb->splashf(HZ *2, "Saved %s", buf);
+ }
+}
+
+static void keyremap_import_user_keys(void)
+{
+ char buf[MAX_PATH];
+ struct browse_context browse = {
+ .dirfilter = SHOW_ALL,
+ .flags = BROWSE_SELECTONLY,
+ .title = "Select Keymap",
+ .icon = Icon_Plugin,
+ .buf = buf,
+ .bufsize = sizeof(buf),
+ .root = "/",
+ };
+
+ if (rb->rockbox_browse(&browse) == GO_TO_PREVIOUS)
+ {
+ int ret = keyremap_import_file(buf, sizeof(buf));
+ if (ret <= 0)
+ {
+ keyremap_reset_buffer();
+ rb->splash(HZ *2, "Error Opening");
+ }
+ else
+ rb->splashf(HZ * 2, "Loaded Text Keymap ");
+ }
+}
+
+static int keymap_add_context_entry(int context)
+{
+ int remap_context = CORE_CONTEXT_REMAP(context);
+ for (int i = 0; i < ctx_data.ctx_count; i++)
+ {
+ if (ctx_data.ctx_map[i].map.action_code == remap_context)
+ goto fail; /* context exists */
+ }
+ if (keyremap_buffer.front + sizeof(struct action_mapping_t) > keyremap_buffer.end)
+ goto fail;
+ keyremap_buffer.front += sizeof(struct action_mapping_t);
+ ctx_data.ctx_map[ctx_data.ctx_count].context = context;
+ ctx_data.ctx_map[ctx_data.ctx_count].display_pos = -1;
+ ctx_data.ctx_map[ctx_data.ctx_count].map.action_code = remap_context;
+ ctx_data.ctx_map[ctx_data.ctx_count].map.button_code = 0;
+ ctx_data.ctx_map[ctx_data.ctx_count].map.pre_button_code = 0;
+ ctx_data.ctx_count++;
+ menu_useract_set_positions();
+ return ctx_data.ctx_count;
+fail:
+ return 0;
+}
+
+static int keymap_add_button_entry(int context, int action_code,
+ int button_code, int pre_button_code)
+{
+ bool hasctx = false;
+ for (int i = 0; i < ctx_data.ctx_count; i++)
+ {
+ if (ctx_data.ctx_map[i].context == context)
+ {
+ hasctx = true;
+ break;
+ }
+ }
+ if (!hasctx || keyremap_buffer.end - sizeof(struct action_mapping_t) < keyremap_buffer.front)
+ goto fail;
+ keyremap_buffer.end -= sizeof(struct action_mapping_t);
+ ctx_data.act_map = (struct action_mapping_t*) keyremap_buffer.end;
+ ctx_data.act_map[0].context = context;
+ ctx_data.act_map[0].display_pos = -1;
+ ctx_data.act_map[0].map.action_code = action_code;
+ ctx_data.act_map[0].map.button_code = button_code;
+ ctx_data.act_map[0].map.pre_button_code = pre_button_code;
+ ctx_data.act_count++;
+ menu_useract_set_positions();
+ return ctx_data.act_count;
+fail:
+ return 0;
+}
+
+static int get_action_from_str(char *pfield, size_t bufsz)
+{
+ int act = -1;
+ for (int i=0;i < LAST_ACTION_PLACEHOLDER; i++)
+ {
+ if (rb->strncasecmp(pfield, action_name(i), bufsz) == 0)
+ {
+ logf("Action Found: %s (%d)", pfield, i);
+ act = i;
+ break;
+ }
+ }
+ return act;
+}
+
+static int get_button_from_str(char *pfield, size_t bufsz)
+{
+ int btn = -1;
+ for (int i=0;i < available_button_count; i++)
+ {
+ const struct available_button* abtn = &available_buttons[i];
+ if (rb->strncasecmp(pfield, abtn->name, bufsz) == 0)
+ {
+ logf("Button Found: %s (%lx)", abtn->name, abtn->value);
+ btn = abtn->value;
+ break;
+ }
+ }
+ if (btn < 0) /* Not Fatal */
+ {
+ logf("Invalid Button %s", pfield);
+ rb->splashf(HZ, "Invalid Button %s", pfield);
+ }
+ return btn;
+}
+
+static int parse_action_import_entry(int context, char * pbuf, size_t bufsz)
+{
+ size_t bufleft;
+ int count = 0;
+ char ch;
+ char *pfirst = NULL;
+ char *pfield;
+ int field = -1;
+ int act = -1;
+ int button = BUTTON_NONE;
+ int prebtn = BUTTON_NONE;
+ while ((ch = *(pbuf)) != '\0')
+ {
+ pfield = NULL; /* Valid names */
+ if ((ch >= 'A' && ch <= 'Z') || ch == '_')
+ {
+ if (pfirst == NULL)
+ pfirst = pbuf;
+ }
+ else if (ch == ',')
+ {
+ if (pfirst != NULL)
+ {
+ field++;
+ pfield = pfirst;
+ pfirst = NULL;
+ *pbuf = '\0';
+ }
+ }
+ else if (ch == ' ' || ch == '|'){;}
+ else
+ pfirst = NULL;
+
+ if (field == 1 && pfirst != NULL && pbuf[1] == '\0')
+ {
+ field++;
+ pfield = pfirst;
+ pfirst = NULL;
+ }
+
+ if (pfield != NULL)
+ {
+ char *pf;
+
+ if (field == 0) /* action */
+ {
+ char *pact = pfield;
+ pf = pfield;
+ while ((ch = *(pf)) != '\0')
+ {
+ if(ch == ' ')
+ *pf = '\0';
+ else
+ pf++;
+ }
+ bufleft = bufsz - (pact - pbuf);
+ act = get_action_from_str(pact, bufleft);
+
+ if (act < 0)
+ {
+ logf("Error Action Expected: %s", pact);
+ return -1;
+ }
+ }
+ else if (field == 1 || field == 2) /* button / pre_btn */
+ {
+ char *pbtn = pfield;
+ pf = pfield;
+ while ((ch = *(pf)) != '\0')
+ {
+ if (pf[1] == '\0') /* last item? */
+ {
+ pf++;
+ ch = '|';
+ pfield = NULL;
+ }
+ else if (ch == ' ' || ch == '|')
+ {
+ *pf = '\0';
+ }
+
+ if(ch == '|')
+ {
+ bufleft = bufsz - (pbtn - pbuf);
+ int btn = get_button_from_str(pbtn, bufleft);
+
+ if (btn > BUTTON_NONE)
+ {
+ if (field == 1)
+ button |= btn;
+ else if (field == 2)
+ prebtn |= btn;
+ }
+
+ if (pfield != NULL)
+ {
+ pf++;
+ while ((ch = *(pf)) != '\0')
+ {
+ if (*pf == ' ')
+ pf++;
+ else
+ break;
+ }
+ pbtn = pf;
+ }
+ }
+ else
+ pf++;
+ }
+
+ if (act < 0)
+ {
+ logf("Error Action Expected: %s", pfield);
+ return -1;
+ }
+ }
+
+ pfield = NULL;
+ }
+
+ pbuf++;
+ }
+ if (field == 2)
+ {
+ count = keymap_add_button_entry(context, act, button, prebtn);
+ if (count > 0)
+ {
+ logf("Added: Ctx: %d, Act: %d, Btn: %d PBtn: %d",
+ context, act, button, prebtn);
+ }
+ }
+ return count;
+}
+
+static int keyremap_import_file(char *filenamebuf, size_t bufsz)
+{
+ size_t bufleft;
+ int count = 0;
+ int line = 0;
+ filenamebuf[bufsz - 1] = '\0';
+ logf("keyremap: import %s", filenamebuf);
+ int fd = rb->open(filenamebuf, O_RDONLY);
+ if (fd < 0)
+ return -1;
+
+ char ch;
+ char *pbuf;
+ char *pfirst;
+
+ char *pctx = NULL;
+ char *pact;
+ int ctx = -1;
+
+ keyremap_reset_buffer();
+next_line:
+ while (rb->read_line(fd, filenamebuf, (int) bufsz) > 0)
+ {
+ line++;
+
+
+ pbuf = filenamebuf;
+ pfirst = NULL;
+ pact = NULL;
+ char *pcomment = rb->strchr(pbuf, '#');
+ if (pcomment != NULL)
+ {
+ logf("ln: %d: Skipped Comment: %s", line, pcomment);
+ *pcomment = '\0';
+ }
+
+ while ((ch = *(pbuf)) != '\0')
+ {
+ /* PARSE CONTEXT = { */
+ if ((ch >= 'A' && ch <= 'Z') || ch == '_')
+ {
+ if (pfirst == NULL)
+ pfirst = pbuf;
+ }
+ else if (ch == ' ')
+ {
+ if (ctx < 0 && pfirst != NULL)
+ {
+ *pbuf = '\0';
+ pctx = pfirst;
+ pfirst = NULL;
+ }
+ }
+ else if (ch == '=')
+ {
+ if (ctx < 0 && pfirst != NULL)
+ {
+ *pbuf = '\0';
+ pbuf++;
+ pctx = pfirst;
+ pfirst = NULL;
+ }
+ while ((ch = *(pbuf)) != '\0')
+ {
+ if (ch == '{')
+ break;
+ pbuf++;
+ }
+ if (ch == '{' && pctx != NULL)
+ {
+ bufleft = bufsz - (pctx - filenamebuf);
+ ctx = -1;
+ int ctx_x_flag_count = (LAST_CONTEXT_PLACEHOLDER
+ * ARRAYLEN(context_flags));
+
+ for (int i=0;i < ctx_x_flag_count ;i++)
+ {
+ /* context x flag */
+ if (rb->strncasecmp(pctx, ctx_name_and_flag(i), bufleft) == 0)
+ {
+ logf("ln: %d: Context Found: %s (%d)", line, pctx, i);
+ if (keymap_add_context_entry(i) <= 0)
+ logf("ln: %d: Context Exists: %s (%d)", line, pctx, i);
+ ctx = i;
+ goto next_line;
+
+ }
+ }
+ logf("ln: %d: ERROR { Context Expected got: %s", line, pctx);
+ goto fail;
+ }
+ }
+ else if (ch == '}')
+ {
+ if (ctx >= 0)
+ ctx = -1;
+ else
+ {
+ logf("ln: %d: ERROR no context, unexpected close {", line);
+ goto fail;
+ }
+ }
+ else if (ch == '{') /* PARSE FIELDS { ACTION, BUTTON, PREBTN } */
+ {
+ int res = 0;
+ if (ctx >= 0)
+ {
+ pfirst = pbuf;
+
+ while ((ch = *(pbuf)) != '\0')
+ {
+ if (ch == '}')
+ {
+ pact = pfirst + 1;
+ pfirst = NULL;
+ *pbuf = '\0';
+ pbuf = "";
+ continue;
+ }
+ pbuf++;
+ }
+ if (pact != NULL)
+ {
+ bufleft = bufsz - (pact - filenamebuf);
+ logf("ln: %d: Entry Found: {%s} (%d)", line, pact, 0);
+ res = parse_action_import_entry(ctx, pact, bufleft);
+ }
+ }
+ if (res <= 0)
+ {
+ logf("ln: %d: ERROR action entry expected", line);
+ goto fail;
+ }
+ else
+ {
+ pbuf = "";
+ continue;
+ }
+ }
+ else
+ pfirst = NULL;
+ pbuf++;
+ }
+
+ }
+ rb->close(fd);
+ count = ctx_data.ctx_count + ctx_data.act_count;
+ return count;
+
+fail:
+ rb->close(fd);
+ rb->splashf(HZ * 2, "Error @ line %d", line);
+ return 0;
+}
+
+static int keyremap_load_file(const char *filename)
+{
+ logf("keyremap: load %s", filename);
+ int fd = -1;
+ size_t fsize = 0;
+ size_t bytes;
+ struct button_mapping entry;
+ int count = keyremap_open_file(filename, &fd, &fsize);
+ logf("keyremap: entries %d", count);
+ /* actions are indexed from the first entry after the header save this pos */
+ off_t firstpos = rb->lseek(fd, 0, SEEK_CUR);
+ off_t ctxpos = firstpos;
+
+ if (count > 0)
+ {
+ keyremap_reset_buffer();
+ while(--count > 0)
+ {
+ rb->lseek(fd, ctxpos, SEEK_SET); /* next context remap entry */
+ bytes = rb->read(fd, &entry, sizeof(struct button_mapping));
+ ctxpos = rb->lseek(fd, 0, SEEK_CUR);
+ if (bytes != sizeof(struct button_mapping))
+ {
+ count = -10;
+ goto fail;
+ }
+ if (entry.action_code == (int)CONTEXT_STOPSEARCHING)
+ {
+ logf("keyremap: end of context entries ");
+ break;
+ }
+ if ((entry.action_code & CONTEXT_REMAPPED) == CONTEXT_REMAPPED)
+ {
+ int context = (entry.action_code & ~CONTEXT_REMAPPED);
+ for (int i = ARRAYLEN(context_flags) - 1; i > 0; i--) /* don't check idx 0*/
+ {
+ /* convert context | flag to context x flag */
+ if ((context & context_flags[i].flag) == context_flags[i].flag)
+ {
+ logf("found ctx flag %s", context_flags[i].name);
+ context &= ~context_flags[i].flag;
+ context += i * LAST_CONTEXT_PLACEHOLDER;
+ }
+ }
+ int offset = entry.button_code;
+ int entries = entry.pre_button_code;
+ if (offset == 0 || entries <= 0)
+ {
+ logf("keyremap: error reading offset");
+ count = -15;
+ goto fail;
+ }
+ logf("keyremap found context: %d file offset: %d entries: %d",
+ context, offset, entries);
+
+ keymap_add_context_entry(context);
+
+ off_t entrypos = firstpos + (offset * sizeof(struct button_mapping));
+ rb->lseek(fd, entrypos, SEEK_SET);
+ for (int i = 0; i < entries; i++)
+ {
+ bytes = rb->read(fd, &entry, sizeof(struct button_mapping));
+ if (bytes == sizeof(struct button_mapping))
+ {
+ int act = entry.action_code;
+ int button = entry.button_code;
+ int prebtn = entry.pre_button_code;
+
+ if (act == (int)CONTEXT_STOPSEARCHING || button == BUTTON_NONE)
+ {
+ logf("keyremap: entry invalid");
+ goto fail;
+ }
+ logf("keyremap: found ctx: %d, act: %d btn: %d pbtn: %d",
+ context, act, button, prebtn);
+ keymap_add_button_entry(context, act, button, prebtn);
+ }
+ else
+ goto fail;
+ }
+ }
+ else
+ {
+ logf("keyremap: Invalid context entry");
+ keyremap_reset_buffer();
+ count = -20;
+ goto fail;
+ }
+ }
+ }
+
+ int entries = 0;
+ struct button_mapping *keymap = keyremap_create_temp(&entries);
+ if (keymap != NULL)
+ {
+ keyset.crc32 =
+ rb->crc_32(keymap, entries * sizeof(struct button_mapping), 0xFFFFFFFF);
+ }
+fail:
+ rb->close(fd);
+ if (count <= 0)
+ rb->splashf(HZ * 2, "Error Loading %sz", filename);
+ return count;
+}
+
+static const struct button_mapping* test_get_context_map(int context)
+{
+ (void)context;
+
+ if (keytest.keymap != NULL && keytest.context >= 0)
+ {
+ int mapidx = keytest.keymap[keytest.index].button_code;
+ int mapcnt = keytest.keymap[keytest.index].pre_button_code;
+ /* make fallthrough to the test context*/
+ keytest.keymap[mapidx + mapcnt].action_code = keytest.context;
+ static const struct button_mapping *testctx[] = { NULL };
+ testctx[0] = &keytest.keymap[mapidx];
+ return testctx[0];
+ }
+ else
+ return NULL;
+}
+
+static const char *kmffiles_name_cb(int selected_item, void* data,
+ char* buf, size_t buf_len)
+{
+ /* found kmf filenames returned by each call kmffiles_dirp keeps state
+ * selected_item = 0 resets state */
+
+ (void)data;
+ buf[0] = '\0';
+ if (selected_item == 0)
+ {
+ rb->closedir(kmffiles_dirp);
+ kmffiles_dirp = rb->opendir(KMFDIR);
+ }
+ if (kmffiles_dirp != NULL)
+ {
+ struct dirent *entry;
+ while ((entry = rb->readdir(kmffiles_dirp)))
+ {
+ /* skip directories */
+ if ((rb->dir_get_info(kmffiles_dirp, entry).attribute & ATTR_DIRECTORY) != 0)
+ continue;
+ if (keyremap_check_extension(entry->d_name) > 0)
+ {
+ rb->snprintf(buf, buf_len, "%s", entry->d_name);
+ return buf;
+ }
+ }
+ }
+ return "Error!";
+}
+
+static void menu_useract_set_positions(void)
+{
+ /* responsible for item ordering to display action edit interface */
+ int display_pos = 0; /* start at item 0*/
+ int i, j;
+ for (i = 0; i < ctx_data.ctx_count; i++)
+ {
+ int context = ctx_data.ctx_map[i].context;
+ ctx_data.ctx_map[i].display_pos = display_pos++;
+ /* how many actions are contained in this context? */
+ for (j = 0; j < ctx_data.act_count; j++)
+ {
+ if (ctx_data.act_map[j].context == context)
+ {
+ ctx_data.act_map[j].display_pos = display_pos++;
+ }
+ }
+ }
+}
+
+static const char *menu_useract_items_cb(int selected_item, void* data,
+ char* buf, size_t buf_len)
+{
+ static char buf_button[MAX_BUTTON_NAME * MAX_BUTTON_COMBO];
+ static char buf_prebtn[MAX_BUTTON_NAME * MAX_BUTTON_COMBO];
+ int i;
+ int szbtn = sizeof("BUTTON");
+ int szctx = sizeof("CONTEXT");
+ int szact = sizeof("ACTION");
+ const char* ctxfmt = "%s";
+
+ if (data == MENU_ID(M_EXPORTKEYS))
+ {
+ szbtn = 0;
+ szctx = 0;
+ szact = 0;
+ ctxfmt = "$%s = {\n\n";
+ }
+ buf[0] = '\0';
+ for(i = 0; i < ctx_data.ctx_count; i ++)
+ {
+ if (ctx_data.ctx_map[i].display_pos == selected_item)
+ {
+ if (ctx_data.act_count == 0)
+ rb->snprintf(buf, buf_len, "%s$%s",
+ ctx_name_and_flag(ctx_data.ctx_map[i].context),
+ "Select$to add$actions");
+ else
+ rb->snprintf(buf, buf_len, ctxfmt, ctx_name_and_flag(ctx_data.ctx_map[i].context));
+ return buf;
+ }
+ }
+ for(i = 0; i < ctx_data.act_count; i ++)
+ {
+ if (ctx_data.act_map[i].display_pos == selected_item)
+ {
+ int context = ctx_data.act_map[i].context;
+ char ctxbuf[action_helper_maxbuffer];
+ char *pctxbuf = "\0";
+ char *pactname;
+ if (data != MENU_ID(M_EXPORTKEYS))
+ {
+ pctxbuf = ctxbuf;
+ rb->snprintf(ctxbuf, sizeof(ctxbuf), ctxfmt, ctx_name_and_flag(context));
+ pctxbuf += szctx;//sizeof("CONTEXT")
+ }
+ struct button_mapping * bm = &ctx_data.act_map[i].map;
+ pactname = action_name(bm->action_code);
+ pactname += szact;//sizeof("ACTION")
+ /* BUTTON & PRE_BUTTON */
+ btnval_to_name(buf_button, sizeof(buf_button), bm->button_code);
+ btnval_to_name(buf_prebtn, sizeof(buf_prebtn), bm->pre_button_code);
+
+ rb->snprintf(buf, buf_len, ctx_menu_data.act_fmt, pctxbuf,
+ pactname, buf_button + szbtn, buf_prebtn + szbtn);
+ return buf;
+ }
+ }
+ return "Error!";
+}
+
+static const char *edit_keymap_name_cb(int selected_item, void* data,
+ char* buf, size_t buf_len)
+{
+ buf[0] = '\0';
+ if (selected_item == 0)
+ {
+ rb->snprintf(buf, buf_len, "Add Context");
+ ctx_menu_data.act_index = -1;
+ ctx_menu_data.ctx_index = -1;
+ ctx_menu_data.act_fmt = ACTIONFMT_LV0;
+ }
+ else if (ctx_data.ctx_count > 0)
+ {
+ return menu_useract_items_cb(selected_item - 1, data, buf, buf_len);
+ }
+ return buf;
+}
+
+static const char *test_keymap_name_cb(int selected_item, void* data,
+ char* buf, size_t buf_len)
+{
+ (void)data;
+ buf[0] = '\0';
+ if (keytest.context >= 0)
+ {
+ if (selected_item == 0)
+ rb->snprintf(buf, buf_len, "< %s >", ctx_name_and_flag(keytest.context));
+ else if (selected_item == 1)
+ {
+ if (keytest.countdown >= 10)
+ rb->snprintf(buf, buf_len, "Testing %d", keytest.countdown / 10);
+ else
+ rb->snprintf(buf, buf_len, "Start test");
+ }
+ else if (selected_item == 2)
+ rb->snprintf(buf, buf_len, "%s", action_name(keytest.action));
+ }
+ return buf;
+}
+
+static const char* list_get_name_cb(int selected_item, void* data,
+ char* buf, size_t buf_len)
+{
+ buf[0] = '\0';
+ const struct mainmenu *cur = (struct mainmenu *) data;
+ if (data == MENU_ID(M_ROOT))
+ return mainitem(selected_item + 1)->name;
+ else if (selected_item >= cur->items - 1)
+ {
+ return ID2P(LANG_BACK);
+ }
+ if (data == MENU_ID(M_SETKEYS))
+ {
+ return edit_keymap_name_cb(selected_item, data, buf, buf_len);
+ }
+ else if (data == MENU_ID(M_BUTTONS))
+ {
+ const struct available_button *btn = &available_buttons[selected_item];
+ rb->snprintf(buf, buf_len, "%s: [0x%X] ", btn->name, (unsigned int) btn->value);
+ return buf;
+ }
+ else if (data == MENU_ID(M_ACTIONS))
+ {
+ return action_name(selected_item);
+ }
+ else if (data == MENU_ID(M_CONTEXTS))
+ {
+ return ctx_name_and_flag(selected_item);
+ }
+ else if (data == MENU_ID(M_DELKEYS) || data == MENU_ID(M_LOADKEYS))
+ {
+ /* need to iterate the callback for the items off screen to
+ * keep ordering, this limits the menu to only the main screen :( */
+ int start_item = lists.start_item[SCREEN_MAIN];
+ if (start_item != 0 && start_item == selected_item)
+ {
+ for (int i = 0; i < start_item; i++)
+ kmffiles_name_cb(i, data, buf, buf_len);
+ }
+ return kmffiles_name_cb(selected_item, data, buf, buf_len);
+ }
+ else if (data == MENU_ID(M_TESTKEYS))
+ {
+ return test_keymap_name_cb(selected_item, data, buf, buf_len);
+ }
+ return buf;
+}
+
+static int list_voice_cb(int list_index, void* data)
+{
+ if (!rb->global_settings->talk_menu)
+ return -1;
+
+ if (data == MENU_ID(M_ROOT))
+ {
+ const char * name = mainitem(list_index)->name;
+ long id = P2ID((const unsigned char *)name);
+ if(id>=0)
+ rb->talk_id(id, true);
+ else
+ rb->talk_spell(name, true);
+ }
+ else if(data == MENU_ID(M_SETKEYS))
+ {
+ char buf[MAX_MENU_NAME];
+ int selcol = printcell_get_column_selected();
+ const char* name = printcell_get_column_text(selcol, buf, sizeof(buf));
+ long id = P2ID((const unsigned char *)name);
+ if(id>=0)
+ rb->talk_id(id, true);
+ else
+ rb->talk_spell(name, true);
+ }
+ else
+ {
+ char buf[MAX_MENU_NAME];
+ const char* name = list_get_name_cb(list_index, data, buf, sizeof(buf));
+ long id = P2ID((const unsigned char *)name);
+ if(id>=0)
+ rb->talk_id(id, true);
+ else
+ rb->talk_spell(name, true);
+ }
+ return 0;
+}
+
+int menu_action_root(int *action, int selected_item, bool* exit, struct gui_synclist *lists)
+{
+#ifdef ROCKBOX_HAS_LOGF
+ char logfnamebuf[64];
+#endif
+
+ if (*action == ACTION_STD_OK)
+ {
+ struct mainmenu *cur = mainitem(selected_item + 1);
+ if (cur != NULL)
+ {
+#ifdef ROCKBOX_HAS_LOGF
+ lang_strlcpy(logfnamebuf, cur->name, sizeof(logfnamebuf));
+ logf("Root select menu -> %s(%d) ", logfnamebuf, cur->index);
+#endif
+ if (cur->menuid == MENU_ID(M_SETKEYS))
+ {
+ keyset.view_lastcol = -1;
+ }
+ else if (cur->menuid == MENU_ID(M_TMPCORE))
+ {
+ int entry_count;/* (ctx_count + ctx_count + act_count + 1) */
+ struct button_mapping *keymap = keyremap_create_temp(&entry_count);
+ if (rb->core_set_keyremap(keymap, entry_count) >= 0)
+ rb->splash(HZ *2, "Keymap Applied");
+ else
+ rb->splash(HZ *2, "Error Applying");
+
+ goto default_handler;
+ }
+ else if (cur->menuid == MENU_ID(M_SETCORE))
+ {
+ if (rb->file_exists(CORE_KEYREMAP_FILE) && 0 == core_savecount++)
+ {
+ rb->rename(CORE_KEYREMAP_FILE, CORE_KEYREMAP_FILE".old"); /* make a backup */
+ }
+ if (keyremap_save_current(CORE_KEYREMAP_FILE) == 0)
+ rb->splash(HZ *2, "Error Saving");
+ else
+ {
+ rb->splash(HZ *2, "Saved");
+ int entry_count;/* (ctx_count + ctx_count + act_count + 1) */
+ struct button_mapping *keymap = keyremap_create_temp(&entry_count);
+ rb->core_set_keyremap(keymap, entry_count);
+ }
+ goto default_handler;
+ }
+ else if (cur->menuid == MENU_ID(M_DELCORE))
+ {
+ rb->core_set_keyremap(NULL, -1);
+ if (rb->file_exists(CORE_KEYREMAP_FILE))
+ {
+ rb->rename(CORE_KEYREMAP_FILE, KMFDIR "/core_deleted" KMFEXT2);
+ rb->splash(HZ *2, "Removed");
+ }
+ else
+ rb->splash(HZ *2, "Error Removing");
+
+ goto default_handler;
+ }
+ else if (cur->menuid == MENU_ID(M_SAVEKEYS))
+ {
+ keyremap_save_user_keys(true);
+ goto default_handler;
+ }
+ else if (cur->menuid == MENU_ID(M_EXPORTKEYS))
+ {
+ keyremap_export_user_keys();
+ goto default_handler;
+ }
+ else if (cur->menuid == MENU_ID(M_IMPORTKEYS))
+ {
+ keyremap_import_user_keys();
+ goto default_handler;
+ }
+ else if (cur->menuid == MENU_ID(M_DELKEYS) ||
+ cur->menuid == MENU_ID(M_LOADKEYS))
+ {
+ cur->items = keyremap_count_files(KMFDIR) + 1;
+ }
+ else if (cur->menuid == MENU_ID(M_TESTKEYS))
+ {
+ int entries = 0;
+ keytest.keymap = keyremap_create_temp(&entries);
+ if (entries > 0)
+ {
+ struct button_mapping *entry = &keytest.keymap[0];
+ if (entry->action_code != (int)CONTEXT_STOPSEARCHING
+ && (entry->action_code & CONTEXT_REMAPPED) == CONTEXT_REMAPPED)
+ {
+ keytest.context = (entry->action_code & ~CONTEXT_REMAPPED);
+ }
+ else
+ keytest.context = -1;
+ }
+ else
+ {
+ keytest.keymap = NULL;
+ keytest.context = -1;
+ }
+ keytest.action = ACTION_NONE;
+ keytest.countdown = 0;
+ }
+ else if (cur->menuid == MENU_ID(M_RESETKEYS))
+ {
+ if (rb->yesno_pop("Delete Current Entries?") == true)
+ {
+ keyremap_reset_buffer();
+ }
+ goto default_handler;
+ }
+ }
+
+ if (cur->menuid == NULL || cur->menuid == MENU_ID(M_EXIT))
+ {
+ logf("Root menu %s", (cur->menuid) == NULL ? "NULL":"Exit");
+ *action = ACTION_STD_CANCEL;
+ *exit = true;
+ }
+ else
+ {
+#ifdef ROCKBOX_HAS_LOGF
+ lang_strlcpy(logfnamebuf, cur->name, sizeof(logfnamebuf));
+ logf("Root load menu -> %s(%d) ", logfnamebuf, cur->index);
+#endif
+ synclist_set_update(cur->index, 0, cur->items, 1);
+ rb->gui_synclist_draw(lists);
+ *action = ACTION_NONE;
+ }
+ }
+
+ return PLUGIN_OK;
+default_handler:
+ return GOTO_ACTION_DEFAULT_HANDLER;
+}
+
+int menu_action_setkeys(int *action, int selected_item, bool* exit, struct gui_synclist *lists)
+{
+ (void) exit;
+ int i;
+ struct mainmenu *cur = (struct mainmenu *)lists->data;
+ if (*action == ACTION_STD_OK)
+ {
+ if (selected_item == 0) /*add_context*/
+ {
+ const struct mainmenu *mainm = &mainmenu[M_CONTEXTS];
+ synclist_set_update(mainm->index, 0, mainm->items, 1);
+ rb->gui_synclist_draw(lists);
+ goto default_handler;
+ }
+ else if (selected_item < lists->nb_items - 1)/* not back*/
+ {
+ bool add_action = false;
+ for (i = 0; i < ctx_data.ctx_count; i++)
+ {
+ if (ctx_data.ctx_map[i].display_pos == selected_item - 1)
+ {
+ add_action = true;
+ break;
+ }
+ }
+
+ if (add_action)
+ {
+ keymap_add_button_entry(ctx_data.ctx_map[i].context, ACTION_NONE, BUTTON_NONE, BUTTON_NONE);
+ cur->items++;
+ lists->nb_items++;
+ goto default_handler;
+ }
+ else
+ {
+ keyset.view_lastcol = printcell_increment_column(1, true);
+ *action = ACTION_NONE;
+ }
+ }
+ }
+ else if (*action == ACTION_STD_CANCEL)
+ {
+ keyset.view_lastcol = printcell_increment_column(-1, true);
+ if (keyset.view_lastcol != keyset.view_columns - 1)
+ {
+ *action = ACTION_NONE;
+ }
+ }
+ else if (*action == ACTION_STD_CONTEXT)
+ {
+ int col = keyset.view_lastcol;
+ for (i = 0; i < ctx_data.act_count; i++)
+ {
+ if (ctx_data.act_map[i].display_pos == selected_item - 1)
+ {
+ int context = ctx_data.act_map[i].context;
+ int act = ctx_data.act_map[i].map.action_code;
+ int button = ctx_data.act_map[i].map.button_code;
+ int prebtn = ctx_data.act_map[i].map.pre_button_code;
+
+ if (col < 0)
+ {
+ rb->splashf(HZ, "short press increments columns");
+
+ }
+ else if (col == 0) /* Context */
+ {
+ const struct mainmenu *mainm = &mainmenu[M_CONTEXTS];
+ synclist_set_update(mainm->index, context, mainm->items, 1);
+ rb->gui_synclist_draw(lists);
+ goto default_handler;
+
+ }
+ else if (col == 1) /* Action */
+ {
+ const struct mainmenu *mainm = &mainmenu[M_ACTIONS];
+ synclist_set_update(mainm->index, act, mainm->items, 1);
+ rb->gui_synclist_draw(lists);
+ goto default_handler;
+ }
+ else if (col == 2) /* Button */
+ {
+ const struct mainmenu *mainm = &mainmenu[M_BUTTONS];
+ int btnidx = btnval_to_index(button);
+ synclist_set_update(mainm->index, btnidx, mainm->items, 1);
+ rb->gui_synclist_draw(lists);
+ goto default_handler;
+ }
+ else if (col == 3) /* PreBtn */
+ {
+ const struct mainmenu *mainm = &mainmenu[M_BUTTONS];
+ int pbtnidx = btnval_to_index(prebtn);
+ synclist_set_update(mainm->index, pbtnidx, mainm->items, 1);
+ rb->gui_synclist_draw(lists);
+ goto default_handler;
+ }
+ }
+ }
+ }
+ return PLUGIN_OK;
+default_handler:
+ return GOTO_ACTION_DEFAULT_HANDLER;
+}
+
+int menu_action_testkeys(int *action, int selected_item, bool* exit, struct gui_synclist *lists)
+{
+ (void) exit;
+ (void) lists;
+ static int last_count = 0;
+ if (keytest.keymap != NULL && keytest.countdown >= 10 && keytest.context >= 0)
+ {
+ int newact = rb->get_custom_action(CONTEXT_PLUGIN, HZ / 10, test_get_context_map);
+ if (newact == ACTION_NONE)
+ {
+ keytest.countdown--;
+ if (last_count - keytest.countdown > 10)
+ keytest.action = newact;
+ }
+ else
+ {
+ last_count = keytest.countdown;
+ keytest.action = newact;
+ }
+ *action = ACTION_REDRAW;
+ goto default_handler;
+ }
+
+ if (*action == ACTION_STD_CANCEL && selected_item == 0 && keytest.keymap != NULL)
+ {
+ keytest.index -= 2;
+ if (keytest.index < 0)
+ keytest.index = 0;
+ *action = ACTION_STD_OK;
+ }
+
+ if (*action == ACTION_STD_OK)
+ {
+ if (selected_item == 0)
+ {
+ if (keytest.keymap != NULL)
+ {
+ struct button_mapping *entry = &keytest.keymap[++keytest.index];
+ if (entry->action_code != (int) CONTEXT_STOPSEARCHING
+ && (entry->action_code & CONTEXT_REMAPPED) == CONTEXT_REMAPPED)
+ {
+ keytest.context = (entry->action_code & ~CONTEXT_REMAPPED);
+ }
+ else
+ {
+ entry = &keytest.keymap[0];
+ if (entry->action_code != (int)CONTEXT_STOPSEARCHING
+ && (entry->action_code & CONTEXT_REMAPPED) == CONTEXT_REMAPPED)
+ {
+ keytest.context = (entry->action_code & ~CONTEXT_REMAPPED);
+ keytest.index = 0;
+ }
+ else
+ {
+ keytest.keymap = NULL;
+ keytest.context = -1;
+ keytest.index = -1;
+ keytest.action = ACTION_NONE;
+ keytest.countdown = 0;
+ }
+ }
+ }
+ }
+ else if (selected_item == 1)
+ {
+ keytest.countdown = TEST_COUNTDOWN_MS / 10;
+ keytest.action = ACTION_NONE;
+ }
+ }
+
+ return PLUGIN_OK;
+default_handler:
+ return GOTO_ACTION_DEFAULT_HANDLER;
+}
+
+int menu_action_listfiles(int *action, int selected_item, bool* exit, struct gui_synclist *lists)
+{
+ (void) exit;
+ int i;
+ if (*action == ACTION_STD_OK)
+ {
+ struct mainmenu *cur = (struct mainmenu *)lists->data;
+ if (selected_item >= (cur->items) - 1)/*back*/
+ {
+ *action = ACTION_STD_CANCEL;
+ }
+ else
+ {
+ /* iterate to the desired file */
+ char buf[MAX_PATH];
+ int len = rb->snprintf(buf, sizeof(buf), "%s/", KMFDIR);
+ if (len < (int) sizeof(buf))
+ {
+ char *name = &buf[len];
+ size_t bufleft = sizeof(buf) - len;
+ list_get_name_cb(0, lists->data, name, bufleft);
+ for (i = 1; i <= selected_item; i++)
+ {
+ list_get_name_cb(i, lists->data, name, bufleft);
+ }
+
+ if (lists->data == MENU_ID(M_DELKEYS))
+ {
+ const char *lines[] = {ID2P(LANG_DELETE), buf};
+ const struct text_message message={lines, 2};
+ if (rb->gui_syncyesno_run(&message, NULL, NULL)==YESNO_YES)
+ {
+ rb->remove(buf);
+ cur->items--;
+ lists->nb_items--;
+ *action = ACTION_NONE;
+ }
+ }
+ else if (lists->data == MENU_ID(M_LOADKEYS))
+ {
+ if (keyremap_load_file(buf) > 0)
+ {
+ rb->splashf(HZ * 2, "Loaded %s ", buf);
+ *action = ACTION_STD_CANCEL;
+ }
+ }
+ }
+ }
+ }
+ return PLUGIN_OK;
+}
+
+int menu_action_submenus(int *action, int selected_item, bool* exit, struct gui_synclist *lists)
+{
+#ifdef ROCKBOX_HAS_LOGF
+ char logfnamebuf[64];
+#endif
+ (void) exit;
+ if (*action == ACTION_STD_OK)
+ {
+ struct mainmenu *cur = (struct mainmenu *)lists->data;
+ if (selected_item >= (cur->items) - 1)/*back*/
+ {
+ *action = ACTION_STD_CANCEL;
+ }
+ else if (lists->data == MENU_ID(M_ACTIONS))
+ {
+#ifdef ROCKBOX_HAS_LOGF
+ const char *name = list_get_name_cb(selected_item, lists->data, logfnamebuf, sizeof(logfnamebuf));
+ logf("ACT %s %d (0x%X)", name, selected_item, selected_item);
+#endif
+ int id, item;
+ POP_MENU(id, item);
+ POP_MENU(id, item);
+ const struct mainmenu *lastm = &mainmenu[id];
+
+ if (id == M_SETKEYS)
+ {
+ for (int i = 0; i < ctx_data.act_count; i++)
+ {
+ if (ctx_data.act_map[i].display_pos == item - 2)
+ {
+ int col = keyset.view_lastcol;
+ struct button_mapping * bm = &ctx_data.act_map[i].map;
+ if (col == 1) /* Action */
+ {
+ bm->action_code = selected_item;
+ }
+ break;
+ }
+ }
+ }
+ synclist_set(lastm->index, item-1, lastm->items, 1);
+ rb->gui_synclist_draw(lists);
+ }
+ else if (lists->data == MENU_ID(M_CONTEXTS))
+ {
+#ifdef ROCKBOX_HAS_LOGF
+ const char *name = list_get_name_cb(selected_item, lists->data, logfnamebuf, sizeof(logfnamebuf));
+ logf("CTX %s %d (0x%X)", name, selected_item, selected_item);
+#endif
+ int id, item;
+ POP_MENU(id, item);
+ POP_MENU(id, item);
+ struct mainmenu *lastm = &mainmenu[id];
+ if (id == M_SETKEYS)
+ {
+ bool found = false;
+ int newctx = selected_item;
+
+ for (int i = 0; i < ctx_data.act_count; i++)
+ {
+ if (ctx_data.act_map[i].display_pos == item - 2)
+ {
+ int col = keyset.view_lastcol;
+ if (col == 0) /* Context */
+ {
+ ctx_data.act_map[i].context = newctx;
+ /* check if this context exists (if not create it) */
+ for (int j = 0; j < ctx_data.ctx_count; j++)
+ {
+ if (ctx_data.ctx_map[j].context == newctx)
+ {
+ found = true;;
+ break;
+ }
+ }
+ }
+ break;
+ }
+ }
+ if (found == false)
+ {
+ keymap_add_context_entry(newctx);
+ lastm->items++;
+ }
+ }
+ synclist_set(lastm->index, item-1, lastm->items, 1);
+ rb->gui_synclist_draw(lists);
+ }
+ else if (lists->data == MENU_ID(M_BUTTONS))
+ {
+#ifdef ROCKBOX_HAS_LOGF
+ const char *name = list_get_name_cb(selected_item, lists->data, logfnamebuf, sizeof(logfnamebuf));
+ logf("BTN %s", name);
+#endif
+ int id, item;
+ POP_MENU(id, item);
+ POP_MENU(id, item);
+ const struct mainmenu *lastm = &mainmenu[id];
+
+ if (id == M_SETKEYS)
+ {
+ for (int i = 0; i < ctx_data.act_count; i++)
+ {
+ if (ctx_data.act_map[i].display_pos == item - 2)
+ {
+ int col = keyset.view_lastcol;
+ struct button_mapping * bm = &ctx_data.act_map[i].map;
+ const struct available_button *btn = &available_buttons[selected_item];
+ if (col == 2) /* BUTTON*/
+ {
+ if (btn->value == BUTTON_NONE)
+ bm->button_code = btn->value;
+ else
+ bm->button_code |= btn->value;
+ }
+ else if (col == 3) /* PREBTN */
+ {
+ if (btn->value == BUTTON_NONE)
+ bm->pre_button_code = btn->value;
+ else
+ bm->pre_button_code |= btn->value;
+ }
+ break;
+ }
+ }
+ }
+ synclist_set(lastm->index, item-1, lastm->items, 1);
+ rb->gui_synclist_draw(lists);
+ }
+ }
+ return PLUGIN_OK;
+}
+
+static void cleanup(void *parameter)
+{
+ (void)parameter;
+ keyremap_save_user_keys(false);
+}
+
+int menu_action_cb(int *action, int selected_item, bool* exit, struct gui_synclist *lists)
+{
+ int status = PLUGIN_OK;
+ /* Top Level Menu Actions */
+ if (lists->data == MENU_ID(M_ROOT))
+ {
+ status = menu_action_root(action, selected_item, exit, lists);
+ }
+ else if (lists->data == MENU_ID(M_SETKEYS))
+ {
+ status = menu_action_setkeys(action, selected_item, exit, lists);
+ }
+ else if (lists->data == MENU_ID(M_TESTKEYS))
+ {
+ status = menu_action_testkeys(action, selected_item, exit, lists);
+ }
+ else if (lists->data == MENU_ID(M_DELKEYS))
+ {
+ status = menu_action_listfiles(action, selected_item, exit, lists);
+ }
+ else if (lists->data == MENU_ID(M_LOADKEYS))
+ {
+ status = menu_action_listfiles(action, selected_item, exit, lists);
+ }
+
+ if (status == GOTO_ACTION_DEFAULT_HANDLER)
+ goto default_handler;
+ /* Submenu Actions */
+ menu_action_submenus(action, selected_item, exit, lists);
+
+ /* Global cancel */
+ if (*action == ACTION_STD_CANCEL)
+ {
+ logf("CANCEL BUTTON");
+ if (lists->data == MENU_ID(M_TESTKEYS))
+ {
+ keytest.keymap = NULL;
+ keytest.context = -1;
+ }
+
+ if (lists->data != MENU_ID(M_ROOT))
+ {
+ int id, item;
+ POP_MENU(id, item);
+ POP_MENU(id, item);
+ const struct mainmenu *lastm = &mainmenu[id];
+ synclist_set(lastm->index, item-1, lastm->items, 1);
+ rb->gui_synclist_draw(lists);
+ }
+ else
+ {
+ /* save changes? */
+ if (ctx_data.act_count + ctx_data.ctx_count >= 2)
+ {
+ int entries = 0;
+ struct button_mapping *keymap = keyremap_create_temp(&entries);
+ if (keymap != NULL && keyset.crc32 != rb->crc_32(keymap,
+ entries * sizeof(struct button_mapping), 0xFFFFFFFF))
+ {
+ if (rb->yesno_pop("Save Keymap?") == true)
+ {
+ keyremap_save_user_keys(true);
+ goto default_handler;
+ }
+ }
+ }
+ *exit = true;
+ }
+ }
+default_handler:
+ if (rb->default_event_handler_ex(*action, cleanup, NULL) == SYS_USB_CONNECTED)
+ {
+ *exit = true;
+ return PLUGIN_USB_CONNECTED;
+ }
+ return PLUGIN_OK;
+}
+
+static void synclist_set(int id, int selected_item, int items, int sel_size)
+{
+ void* menu_id = MENU_ID(id);
+ static char menu_title[64]; /* title is used by every menu */
+ PUSH_MENU(id, selected_item);
+
+ if (items <= 0)
+ return;
+ if (selected_item < 0)
+ selected_item = 0;
+
+ list_voice_cb(0, menu_id);
+ rb->gui_synclist_init(&lists,list_get_name_cb,
+ menu_id, false, sel_size, NULL);
+
+ rb->gui_synclist_set_icon_callback(&lists,NULL);
+ rb->gui_synclist_set_voice_callback(&lists, list_voice_cb);
+ rb->gui_synclist_set_nb_items(&lists,items);
+ rb->gui_synclist_select_item(&lists, selected_item);
+ printcell_enable(false);
+
+ if (menu_id == MENU_ID(M_ROOT))
+ {
+ lang_strlcpy(menu_title, mainmenu[M_ROOT].name, sizeof(menu_title));
+ rb->gui_synclist_set_title(&lists, menu_title, Icon_Plugin);
+ }
+ else if (menu_id == MENU_ID(M_SETKEYS))
+ {
+ keyset.view_columns = printcell_set_columns(&lists, NULL,
+ ACTVIEW_HEADER, Icon_Rockbox);
+ printcell_enable(true);
+ int curcol = printcell_get_column_selected();
+ if (keyset.view_lastcol >= keyset.view_columns)
+ keyset.view_lastcol = -1;
+ /* restore column position */
+ while (keyset.view_lastcol > -1 && curcol != keyset.view_lastcol)
+ {
+ curcol = printcell_increment_column(1, true);
+ }
+ keyset.view_lastcol = curcol;
+ }
+ else
+ {
+ int id;
+ PEEK_MENU_ID(id);
+ lang_strlcpy(menu_title, mainitem(id)->name, sizeof(menu_title));
+ rb->gui_synclist_set_title(&lists, menu_title, Icon_Submenu_Entered);
+ /* if (menu_title[0] == '$'){ printcell_enable(true); } */
+ }
+}
+
+static void keyremap_set_buffer(void* buffer, size_t buf_size)
+{
+ /* set up the keyremap action buffer
+ * contexts start at the front and work towards the back
+ * actions start at the back and work towards front
+ * if they meet buffer is out of space (checked by ctx & btn add entry fns)
+ */
+ keyremap_buffer.buffer = buffer;
+ keyremap_buffer.buf_size = buf_size;
+ keyremap_reset_buffer();
+}
+
+enum plugin_status plugin_start(const void* parameter)
+{
+ int ret = PLUGIN_OK;
+ int selected_item = -1;
+ int action;
+ bool redraw = true;
+ bool exit = false;
+ if (parameter)
+ {
+ //
+ }
+
+ size_t buf_size;
+ void* buffer = rb->plugin_get_buffer(&buf_size);
+ keyremap_set_buffer(buffer, buf_size);
+
+ mainmenu[M_BUTTONS].items = available_button_count;
+ /* add back item to each submenu */
+ for (int i = 1; i < M_LAST_ITEM; i++)
+ mainmenu[i].items += 1;
+
+#if 0
+ keymap_add_context_entry(CONTEXT_WPS);
+ keymap_add_button_entry(CONTEXT_WPS, ACTION_WPS_PLAY, BUTTON_VOL_UP, BUTTON_NONE);
+ keymap_add_button_entry(CONTEXT_WPS, ACTION_WPS_STOP, BUTTON_VOL_DOWN | BUTTON_REPEAT, BUTTON_NONE);
+
+ keymap_add_context_entry(CONTEXT_MAINMENU);
+ keymap_add_button_entry(CONTEXT_MAINMENU, ACTION_STD_PREV, BUTTON_VOL_UP, BUTTON_NONE);
+ keymap_add_button_entry(CONTEXT_MAINMENU, ACTION_STD_NEXT, BUTTON_VOL_DOWN, BUTTON_NONE);
+
+ keymap_add_context_entry(CONTEXT_STD);
+ keymap_add_button_entry(CONTEXT_STD, ACTION_STD_OK, BUTTON_VOL_UP, BUTTON_NONE);
+ keymap_add_button_entry(CONTEXT_STD, ACTION_STD_OK, BUTTON_VOL_DOWN, BUTTON_NONE);
+#endif
+
+ keyset.crc32 = 0;
+ keyset.view_lastcol = -1;
+ if (!exit)
+ {
+ const struct mainmenu *mainm = &mainmenu[M_ROOT];
+ synclist_set(mainm->index, 0, mainm->items, 1);
+ rb->gui_synclist_draw(&lists);
+
+ while (!exit)
+ {
+ action = rb->get_action(CONTEXT_LIST, HZ / 10);
+
+ if (action == ACTION_NONE)
+ {
+ if (redraw)
+ {
+ action = ACTION_REDRAW;
+ redraw = false;
+ }
+ }
+ else
+ redraw = true;
+
+ ret = menu_action_cb(&action, selected_item, &exit, &lists);
+ if (rb->gui_synclist_do_button(&lists, &action))
+ continue;
+ selected_item = rb->gui_synclist_get_sel_pos(&lists);
+
+ mainmenu[M_SETKEYS].items = ctx_data.ctx_count + ctx_data.act_count + 2;
+ }
+ }
+ rb->closedir(kmffiles_dirp);
+
+ return ret;
+}
+
+
+
+#if 0 /* Alt Example */
+static int write_keyremap(const char*filename, struct button_mapping *remap_data, int count)
+{
+ size_t res = 0;
+ if (!filename || !remap_data || count <= 0)
+ goto fail;
+ int fd = rb->open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+ if (fd < 0)
+ goto fail;
+ if (keyremap_write_header(fd, count + 1) > 0)
+ {
+ res = keyremap_write_entries(fd, remap_data, count);
+ res += sizeof(struct button_mapping); /*for header */
+ }
+fail:
+ rb->close(fd);
+ return res;
+}
+
+void alt_example(void)
+{
+ struct button_mapping kmap[8];
+
+ kmap[0].action_code = CORE_CONTEXT_REMAP(CONTEXT_WPS);
+ kmap[0].button_code = 3; /*OFFSET*/
+ kmap[0].pre_button_code = 1; /*COUNT*/
+
+ kmap[1].action_code = CORE_CONTEXT_REMAP(CONTEXT_MAINMENU);
+ kmap[1].button_code = 5; /*OFFSET*/
+ kmap[1].pre_button_code = 2; /*COUNT*/
+
+ kmap[2].action_code = CONTEXT_STOPSEARCHING;
+ kmap[2].button_code = BUTTON_NONE;
+ kmap[2].pre_button_code = BUTTON_NONE;
+
+ kmap[3].action_code = ACTION_WPS_PLAY;
+ kmap[3].button_code = BUTTON_VOL_UP;
+ kmap[3].pre_button_code = BUTTON_NONE;
+
+ kmap[4].action_code = CONTEXT_STOPSEARCHING;
+ kmap[4].button_code = BUTTON_NONE;
+ kmap[4].pre_button_code = BUTTON_NONE;
+
+ kmap[5].action_code = ACTION_STD_NEXT;
+ kmap[5].button_code = BUTTON_VOL_DOWN;
+ kmap[5].pre_button_code = BUTTON_NONE;
+
+ kmap[6].action_code = ACTION_STD_PREV;
+ kmap[6].button_code = BUTTON_VOL_UP;
+ kmap[6].pre_button_code = BUTTON_NONE;
+
+ kmap[7].action_code = CONTEXT_STOPSEARCHING;
+ kmap[7].button_code = BUTTON_NONE;
+ kmap[7].pre_button_code = BUTTON_NONE;
+
+ write_keyremap(CORE_KEYREMAP_FILE, kmap, 8);
+
+ return ret;
+}
+#endif
diff --git a/apps/plugins/lamp.c b/apps/plugins/lamp.c
index 6c9ae6626d..5aa06f4d2d 100644
--- a/apps/plugins/lamp.c
+++ b/apps/plugins/lamp.c
@@ -58,7 +58,14 @@ static const struct button_mapping *plugin_contexts[] = { pla_main_ctx };
#define LAMP_EXIT PLA_EXIT
+
+#if (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_3G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_4G_PAD)
+#define LAMP_EXIT2 PLA_UP
+#else
#define LAMP_EXIT2 PLA_CANCEL
+#endif
#ifdef HAVE_LCD_COLOR
@@ -103,9 +110,8 @@ enum plugin_status plugin_start(const void* parameter)
int current_brightness = MAX_BRIGHTNESS_SETTING;
backlight_brightness_set(MAX_BRIGHTNESS_SETTING);
#endif /* HAVE_BACKLIGHT_BRIGHTNESS */
-#ifdef HAVE_BUTTONLIGHT_BRIGHTNESS
+
buttonlight_brightness_set(MAX_BRIGHTNESS_SETTING);
-#endif /* HAVE_BUTTONLIGHT_BRIGHTNESS */
#ifdef HAVE_LCD_INVERT
#ifdef HAVE_NEGATIVE_LCD
@@ -116,9 +122,8 @@ enum plugin_status plugin_start(const void* parameter)
#endif /* HAVE_LCD_INVERT */
backlight_force_on();
-#ifdef HAVE_BUTTON_LIGHT
buttonlight_force_on();
-#endif /* HAVE_BUTTON_LIGHT */
+
rb->lcd_clear_display();
rb->lcd_update();
@@ -207,20 +212,17 @@ enum plugin_status plugin_start(const void* parameter)
/* restore */
backlight_use_settings();
-#ifdef HAVE_BUTTON_LIGHT
+
buttonlight_use_settings();
-#endif /* HAVE_BUTTON_LIGHT */
#ifdef HAVE_LCD_INVERT
rb->lcd_set_invert_display(rb->global_settings->invert);
#endif /* HAVE_LCD_INVERT */
-#ifdef HAVE_BACKLIGHT_BRIGHTNESS
+
backlight_brightness_use_setting();
-#endif /* HAVE_BACKLIGHT_BRIGHTNESS */
-#ifdef HAVE_BUTTONLIGHT_BRIGHTNESS
+
buttonlight_brightness_use_setting();
-#endif /* HAVE_BUTTONLIGHT_BRIGHTNESS */
#if LCD_DEPTH > 1
rb->lcd_set_background(bg_color);
diff --git a/apps/plugins/lastfm_scrobbler.c b/apps/plugins/lastfm_scrobbler.c
new file mode 100644
index 0000000000..2c0597656c
--- /dev/null
+++ b/apps/plugins/lastfm_scrobbler.c
@@ -0,0 +1,938 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2006-2008 Robert Keevil
+ * Converted to Plugin
+ * Copyright (C) 2022 William Wilgus
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+/* Scrobbler Plugin
+Audioscrobbler spec at: (use wayback machine)
+http://www.audioscrobbler.net/wiki/Portable_Player_Logging
+* EXCERPT:
+* The first lines of .scrobbler.log should be header lines, indicated by the leading '#' character:
+
+#AUDIOSCROBBLER/1.1
+#TZ/[UNKNOWN|UTC]
+#CLIENT/<IDENTIFICATION STRING>
+
+Where 1.1 is the version for this file format
+
+ If the device knows what timezone it is in,
+ it must convert all logged times to UTC (aka GMT+0)
+ eg: #TZ/UTC
+ If the device knows the time, but not the timezone
+ eg: #TZ/UNKNOWN
+
+<IDENTIFICATION STRING> should be replaced by the name/model of the hardware device
+ and the revision of the software producing the log file.
+
+After the header lines, simply append one line of text for every song
+ that is played or skipped.
+
+The following fields comprise each line, and are tab (\t)
+ separated (strip any tab characters from the data):
+
+ - artist name
+ - album name (optional)
+ - track name
+ - track position on album (optional)
+ - song duration in seconds
+ - rating (L if listened at least 50% or S if skipped)
+ - unix timestamp when song started playing
+ - MusicBrainz Track ID (optional)
+lines should be terminated with \n
+Example
+(listened to enter sandman, skipped cowboys, listened to the pusher) :
+ #AUDIOSCROBBLER/1.0
+ #TZ/UTC
+ #CLIENT/Rockbox h3xx 1.1
+ Metallica Metallica Enter Sandman 1 365 L 1143374412 62c2e20a?-559e-422f-a44c-9afa7882f0c4?
+ Portishead Roseland NYC Live Cowboys 2 312 S 1143374777 db45ed76-f5bf-430f-a19f-fbe3cd1c77d3
+ Steppenwolf Live The Pusher 12 350 L 1143374779 58ddd581-0fcc-45ed-9352-25255bf80bfb?
+ If the data for optional fields is not available to you, leave the field blank (\t\t).
+ All strings should be written as UTF-8, although the file does not use a BOM.
+ All fields except those marked (optional) above are required.
+*/
+
+#include "plugin.h"
+#include "lib/configfile.h"
+
+#ifndef UNTAGGED
+ #define UNTAGGED "<UNTAGGED>"
+#endif
+
+#ifdef ROCKBOX_HAS_LOGF
+#define logf rb->logf
+#else
+#define logf(...) do { } while(0)
+#endif
+
+/****************** constants ******************/
+#define EV_EXIT MAKE_SYS_EVENT(SYS_EVENT_CLS_PRIVATE, 0xFF)
+#define EV_FLUSHCACHE MAKE_SYS_EVENT(SYS_EVENT_CLS_PRIVATE, 0xFE)
+#define EV_USER_ERROR MAKE_SYS_EVENT(SYS_EVENT_CLS_PRIVATE, 0xFD)
+#define EV_STARTUP MAKE_SYS_EVENT(SYS_EVENT_CLS_PRIVATE, 0x01)
+#define EV_TRACKCHANGE MAKE_SYS_EVENT(SYS_EVENT_CLS_PRIVATE, 0x02)
+#define EV_TRACKFINISH MAKE_SYS_EVENT(SYS_EVENT_CLS_PRIVATE, 0x03)
+
+#define ERR_NONE (0)
+#define ERR_WRITING_FILE (-1)
+#define ERR_ENTRY_LENGTH (-2)
+#define ERR_WRITING_DATA (-3)
+
+/* increment this on any code change that effects output */
+#define SCROBBLER_VERSION "1.1"
+
+#define SCROBBLER_REVISION " $Revision$"
+
+#define SCROBBLER_BAD_ENTRY "# FAILED - "
+
+/* longest entry I've had is 323, add a safety margin */
+#define SCROBBLER_CACHE_LEN (512)
+#define SCROBBLER_MAX_CACHE (32 * SCROBBLER_CACHE_LEN)
+
+#define SCROBBLER_MAX_TRACK_MRU (32) /* list of hashes to detect repeats */
+
+#define ITEM_HDR "#ARTIST #ALBUM #TITLE #TRACKNUM #LENGTH #RATING #TIMESTAMP #MUSICBRAINZ_TRACKID\n"
+
+#define CFG_FILE "/lastfm_scrobbler.cfg"
+#define CFG_VER 1
+
+#if CONFIG_RTC
+static time_t timestamp;
+#define BASE_FILENAME HOME_DIR "/.scrobbler.log"
+#define HDR_STR_TIMELESS
+#define get_timestamp() ((long)timestamp)
+#define record_timestamp() ((void)(timestamp = rb->mktime(rb->get_time())))
+#else /* !CONFIG_RTC */
+#define HDR_STR_TIMELESS " Timeless"
+#define BASE_FILENAME HOME_DIR "/.scrobbler-timeless.log"
+#define get_timestamp() (0l)
+#define record_timestamp() ({})
+#endif /* CONFIG_RTC */
+
+#define THREAD_STACK_SIZE 4*DEFAULT_STACK_SIZE
+
+/****************** prototypes ******************/
+enum plugin_status plugin_start(const void* parameter); /* entry */
+void play_tone(unsigned int frequency, unsigned int duration);
+/****************** globals ******************/
+/* communication to the worker thread */
+static struct
+{
+ bool exiting; /* signal to the thread that we want to exit */
+ bool hide_reentry; /* we may return on WPS fail, hide next invocation */
+ unsigned int id; /* worker thread id */
+ struct event_queue queue; /* thread event queue */
+ struct queue_sender_list queue_send;
+ long stack[THREAD_STACK_SIZE / sizeof(long)];
+} gThread;
+
+struct cache_entry
+{
+ size_t len;
+ uint32_t crc;
+ char buf[ ];
+};
+
+static struct scrobbler_cache
+{
+ int entries;
+ char *buf;
+ size_t pos;
+ size_t size;
+ bool pending;
+ bool force_flush;
+ struct mutex mtx;
+} gCache;
+
+static struct scrobbler_cfg
+{
+ int uniqct;
+ int savepct;
+ int minms;
+ int beeplvl;
+ bool playback;
+ bool verbose;
+} gConfig;
+
+static struct configdata config[] =
+{
+ #define MAX_MRU (SCROBBLER_MAX_TRACK_MRU)
+ {TYPE_INT, 0, MAX_MRU, { .int_p = &gConfig.uniqct }, "UniqCt", NULL},
+ {TYPE_INT, 0, 100, { .int_p = &gConfig.savepct }, "SavePct", NULL},
+ {TYPE_INT, 0, 10000, { .int_p = &gConfig.minms }, "MinMs", NULL},
+ {TYPE_BOOL, 0, 1, { .bool_p = &gConfig.playback }, "Playback", NULL},
+ {TYPE_BOOL, 0, 1, { .bool_p = &gConfig.verbose }, "Verbose", NULL},
+ {TYPE_INT, 0, 10, { .int_p = &gConfig.beeplvl }, "BeepLvl", NULL},
+ #undef MAX_MRU
+};
+const int gCfg_sz = sizeof(config)/sizeof(*config);
+
+/****************** config functions *****************/
+static void config_set_defaults(void)
+{
+ gConfig.uniqct = SCROBBLER_MAX_TRACK_MRU;
+ gConfig.savepct = 50;
+ gConfig.minms = 500;
+ gConfig.playback = false;
+ gConfig.verbose = true;
+ gConfig.beeplvl = 10;
+}
+
+static int config_settings_menu(void)
+{
+ int selection = 0;
+
+ static uint32_t crc = 0;
+
+ struct viewport parentvp[NB_SCREENS];
+ FOR_NB_SCREENS(l)
+ {
+ rb->viewport_set_defaults(&parentvp[l], l);
+ rb->viewport_set_fullscreen(&parentvp[l], l);
+ }
+
+ #define MENUITEM_STRINGLIST_CUSTOM(name, str, callback, ... ) \
+ static const char *name##_[] = {__VA_ARGS__}; \
+ static const struct menu_callback_with_desc name##__ = \
+ {callback,str, Icon_NOICON}; \
+ struct menu_item_ex name = \
+ {MT_RETURN_ID|MENU_HAS_DESC| \
+ MENU_ITEM_COUNT(sizeof( name##_)/sizeof(*name##_)), \
+ { .strings = name##_},{.callback_and_desc = & name##__}};
+
+ MENUITEM_STRINGLIST_CUSTOM(settings_menu, ID2P(LANG_SETTINGS), NULL,
+ ID2P(LANG_RESUME_PLAYBACK),
+ "Save Threshold",
+ "Minimum Elapsed",
+ "Verbose",
+ "Beep Level",
+ "Unique Track MRU",
+ ID2P(LANG_REVERT_TO_DEFAULT_SETTINGS),
+ ID2P(VOICE_BLANK),
+ ID2P(LANG_CANCEL_0),
+ ID2P(LANG_SAVE_EXIT));
+
+ #undef MENUITEM_STRINGLIST_CUSTOM
+
+ const int items = MENU_GET_COUNT(settings_menu.flags);
+ const unsigned int flags = settings_menu.flags & (~MENU_ITEM_COUNT(MENU_COUNT_MASK));
+ if (crc == 0)
+ {
+ crc = rb->crc_32(&gConfig, sizeof(struct scrobbler_cfg), 0xFFFFFFFF);
+ }
+
+ do {
+ if (crc == rb->crc_32(&gConfig, sizeof(struct scrobbler_cfg), 0xFFFFFFFF))
+ {
+ /* hide save item -- there are no changes to save */
+ settings_menu.flags = flags|MENU_ITEM_COUNT((items - 1));
+ }
+ else
+ {
+ settings_menu.flags = flags|MENU_ITEM_COUNT(items);
+ }
+ selection=rb->do_menu(&settings_menu,&selection, parentvp, true);
+ switch(selection) {
+
+ case 0: /* resume playback on plugin start */
+ rb->set_bool(rb->str(LANG_RESUME_PLAYBACK), &gConfig.playback);
+ break;
+ case 1: /* % of track played to indicate listened status */
+ rb->set_int("Save Threshold", "%", UNIT_PERCENT,
+ &gConfig.savepct, NULL, 10, 0, 100, NULL );
+ break;
+ case 2: /* tracks played less than this will not be logged */
+ rb->set_int("Minimum Elapsed", "ms", UNIT_MS,
+ &gConfig.minms, NULL, 100, 0, 10000, NULL );
+ break;
+ case 3: /* suppress non-error messages */
+ rb->set_bool("Verbose", &gConfig.verbose);
+ break;
+ case 4: /* set volume of start-up beep */
+ rb->set_int("Beep Level", "", UNIT_INT,
+ &gConfig.beeplvl, NULL, 1, 0, 10, NULL);
+ play_tone(1500, 100);
+ break;
+ case 5: /* keep a list of tracks to prevent repeat [Skipped] entries */
+ rb->set_int("Unique Track MRU Size", "", UNIT_INT,
+ &gConfig.uniqct, NULL, 1, 0, SCROBBLER_MAX_TRACK_MRU, NULL);
+ break;
+ case 6: /* set defaults */
+ {
+ const struct text_message prompt = {
+ (const char*[]){ ID2P(LANG_AUDIOSCROBBLER),
+ ID2P(LANG_REVERT_TO_DEFAULT_SETTINGS)}, 2};
+ if(rb->gui_syncyesno_run(&prompt, NULL, NULL) == YESNO_YES)
+ {
+ config_set_defaults();
+ if (gConfig.verbose)
+ rb->splash(HZ, ID2P(LANG_REVERT_TO_DEFAULT_SETTINGS));
+ }
+ break;
+ }
+ case 7: /*sep*/
+ continue;
+ case 8: /* Cancel */
+ return -1;
+ break;
+ case 9: /* Save & exit */
+ {
+ int res = configfile_save(CFG_FILE, config, gCfg_sz, CFG_VER);
+ if (res >= 0)
+ {
+ crc = rb->crc_32(&gConfig, sizeof(struct scrobbler_cfg), 0xFFFFFFFF);
+ logf("SCROBBLER: cfg saved %s %d bytes", CFG_FILE, gCfg_sz);
+ return PLUGIN_OK;
+ }
+ logf("SCROBBLER: cfg FAILED (%d) %s", res, CFG_FILE);
+ return PLUGIN_ERROR;
+ }
+ case MENU_ATTACHED_USB:
+ return PLUGIN_USB_CONNECTED;
+ default:
+ return PLUGIN_OK;
+ }
+ } while ( selection >= 0 );
+ return 0;
+}
+
+/****************** helper fuctions ******************/
+void play_tone(unsigned int frequency, unsigned int duration)
+{
+ if (gConfig.beeplvl > 0)
+ rb->beep_play(frequency, duration, 100 * gConfig.beeplvl);
+}
+
+int scrobbler_init_cache(void)
+{
+ memset(&gCache, 0, sizeof(struct scrobbler_cache));
+ gCache.buf = rb->plugin_get_buffer(&gCache.size);
+
+ /* we need to reserve the space we want for our use in TSR plugins since
+ * someone else could call plugin_get_buffer() and corrupt our memory */
+ size_t reqsz = SCROBBLER_MAX_CACHE;
+ gCache.size = PLUGIN_BUFFER_SIZE - rb->plugin_reserve_buffer(reqsz);
+
+ if (gCache.size < reqsz)
+ {
+ logf("SCROBBLER: OOM , %ld < req:%ld", gCache.size, reqsz);
+ return -1;
+ }
+ gCache.force_flush = true;
+ rb->mutex_init(&gCache.mtx);
+ logf("SCROBBLER: Initialized");
+ return 1;
+}
+
+static inline size_t cache_get_entry_size(int str_len)
+{
+ /* entry_sz consists of the cache entry + str_len + \0NULL terminator */
+ return ALIGN_UP(str_len + 1 + sizeof(struct cache_entry), alignof(struct cache_entry));
+}
+
+static inline const char* str_chk_valid(const char *s, const char *alt)
+{
+ return (s != NULL ? s : alt);
+}
+
+static bool track_is_unique(uint32_t hash1, uint32_t hash2)
+{
+ bool is_unique = false;
+ static uint8_t mru_len = 0;
+
+ struct hash64 { uint32_t hash1; uint32_t hash2; };
+
+ static struct hash64 hash_mru[SCROBBLER_MAX_TRACK_MRU];
+ struct hash64 i = {0};
+ struct hash64 itmp;
+ uint8_t mru;
+
+ if (mru_len > gConfig.uniqct)
+ mru_len = gConfig.uniqct;
+
+ if (gConfig.uniqct < 1)
+ return true;
+
+ /* Search in MRU */
+ for (mru = 0; mru < mru_len; mru++)
+ {
+ /* Items shifted >> 1 */
+ itmp = i;
+ i = hash_mru[mru];
+ hash_mru[mru] = itmp;
+
+ /* Found in MRU */
+ if ((i.hash1 == hash1) && (i.hash2 == hash2))
+ {
+ logf("SCROBBLER: hash [%x, %x] found in MRU @ %d", i.hash1, i.hash2, mru);
+ goto Found;
+ }
+ }
+
+ /* Add MRU entry */
+ is_unique = true;
+ if (mru_len < SCROBBLER_MAX_TRACK_MRU && mru_len < gConfig.uniqct)
+ {
+ hash_mru[mru_len] = i;
+ mru_len++;
+ }
+ else
+ {
+ logf("SCROBBLER: hash [%x, %x] evicted from MRU", i.hash1, i.hash2);
+ }
+
+ i = (struct hash64){.hash1 = hash1, .hash2 = hash2};
+ logf("SCROBBLER: hash [%x, %x] added to MRU[%d]", i.hash1, i.hash2, mru_len);
+
+Found:
+
+ /* Promote MRU item to top of MRU */
+ hash_mru[0] = i;
+
+ return is_unique;
+}
+
+static void get_scrobbler_filename(char *path, size_t size)
+{
+ int used;
+
+ used = rb->snprintf(path, size, "/%s", BASE_FILENAME);
+
+ if (used >= (int)size)
+ {
+ logf("%s: not enough buffer space for log filename", __func__);
+ rb->memset(path, 0, size);
+ }
+}
+
+static void scrobbler_write_cache(void)
+{
+ int i;
+ int fd;
+ logf("%s", __func__);
+ char scrobbler_file[MAX_PATH];
+
+ rb->mutex_lock(&gCache.mtx);
+
+ get_scrobbler_filename(scrobbler_file, sizeof(scrobbler_file));
+
+ /* If the file doesn't exist, create it.
+ Check at each write since file may be deleted at any time */
+ if(!rb->file_exists(scrobbler_file))
+ {
+ fd = rb->open(scrobbler_file, O_RDWR | O_CREAT, 0666);
+ if(fd >= 0)
+ {
+ /* write file header */
+ rb->fdprintf(fd, "#AUDIOSCROBBLER/" SCROBBLER_VERSION "\n"
+ "#TZ/UNKNOWN\n" "#CLIENT/Rockbox "
+ TARGET_NAME SCROBBLER_REVISION
+ HDR_STR_TIMELESS "\n");
+ rb->fdprintf(fd, ITEM_HDR);
+
+ rb->close(fd);
+ }
+ else
+ {
+ logf("SCROBBLER: cannot create log file (%s)", scrobbler_file);
+ }
+ }
+
+ int entries = gCache.entries;
+ size_t used = gCache.pos;
+ size_t pos = 0;
+ /* clear even if unsuccessful - we don't want to overflow the buffer */
+ gCache.pos = 0;
+ gCache.entries = 0;
+
+ /* write the cache entries */
+ fd = rb->open(scrobbler_file, O_WRONLY | O_APPEND);
+ if(fd >= 0)
+ {
+ logf("SCROBBLER: writing %d entries", entries);
+ /* copy cached data to storage */
+ uint32_t prev_crc = 0x0;
+ uint32_t crc;
+ size_t entry_sz, len;
+ bool err = false;
+
+ for (i = 0; i < entries && pos < used; i++)
+ {
+ logf("SCROBBLER: write %d read pos [%ld]", i, pos);
+
+ struct cache_entry *entry = (struct cache_entry*)&gCache.buf[pos];
+
+ entry_sz = cache_get_entry_size(entry->len);
+ crc = rb->crc_32(entry->buf, entry->len, 0xFFFFFFFF) ^ prev_crc;
+ prev_crc = crc;
+
+ len = rb->strlen(entry->buf);
+ logf("SCROBBLER: write entry %d sz [%ld] len [%ld]", i, entry_sz, len);
+
+ if (len != entry->len || crc != entry->crc) /* the entry is corrupted */
+ {
+ rb->write(fd, SCROBBLER_BAD_ENTRY, sizeof(SCROBBLER_BAD_ENTRY)-1);
+ logf("SCROBBLER: Bad entry %d", i);
+ if(!err)
+ {
+ rb->queue_post(&gThread.queue, EV_USER_ERROR, ERR_WRITING_DATA);
+ err = true;
+ }
+ }
+
+ logf("SCROBBLER: writing %s", entry->buf);
+
+ if (rb->write(fd, entry->buf, len) != (ssize_t)len)
+ break;
+
+ if (entry->buf[len - 1] != '\n')
+ rb->write(fd, "\n", 1); /* ensure newline termination */
+
+ pos += entry_sz;
+ }
+ rb->close(fd);
+ }
+ else
+ {
+ logf("SCROBBLER: error writing file");
+ rb->queue_post(&gThread.queue, EV_USER_ERROR, ERR_WRITING_FILE);
+ }
+ rb->mutex_unlock(&gCache.mtx);
+}
+
+#if USING_STORAGE_CALLBACK
+static void scrobbler_flush_callback(void)
+{
+ if(gCache.pos == 0)
+ return;
+#if (CONFIG_STORAGE & STORAGE_ATA)
+ else
+#else
+ if ((gCache.pos >= SCROBBLER_MAX_CACHE / 2) || gCache.force_flush == true)
+#endif
+ {
+ gCache.force_flush = false;
+ logf("%s", __func__);
+ scrobbler_write_cache();
+ }
+}
+#endif
+
+static unsigned long scrobbler_get_threshold(unsigned long length)
+{
+ /* length is assumed to be in miliseconds */
+ return length / 100 * gConfig.savepct;
+}
+
+static int create_log_entry(const struct mp3entry *id,
+ struct cache_entry *entry, int *trk_info_len)
+{
+ #define SEP "\t"
+ #define EOL "\n"
+ char* artist = id->artist ? id->artist : id->albumartist;
+ char rating = 'S'; /* Skipped */
+ if (id->elapsed >= scrobbler_get_threshold(id->length))
+ rating = 'L'; /* Listened */
+
+ char tracknum[11] = { "" };
+
+ if (id->tracknum > 0)
+ rb->snprintf(tracknum, sizeof (tracknum), "%d", id->tracknum);
+
+ int ret = rb->snprintf(entry->buf,
+ SCROBBLER_CACHE_LEN,
+ "%s"SEP"%s"SEP"%s"SEP"%s"SEP"%d%n"SEP"%c"SEP"%ld"SEP"%s"EOL"",
+ str_chk_valid(artist, UNTAGGED),
+ str_chk_valid(id->album, ""),
+ str_chk_valid(id->title, id->path),
+ tracknum,
+ (int)(id->length / 1000),
+ trk_info_len, /* receives len of the string written so far */
+ rating,
+ get_timestamp(),
+ str_chk_valid(id->mb_track_id, ""));
+
+ #undef SEP
+ #undef EOL
+ return ret;
+}
+
+static void scrobbler_add_to_cache(const struct mp3entry *id)
+{
+ logf("%s", __func__);
+ int trk_info_len = 0;
+
+ if (id->elapsed < (unsigned long) gConfig.minms)
+ {
+ logf("SCROBBLER: skipping entry < %d ms: %s", gConfig.minms, id->path);
+ return;
+ }
+
+ rb->mutex_lock(&gCache.mtx);
+
+ /* not enough room left to guarantee next entry will fit so flush the cache */
+ if ( gCache.pos > SCROBBLER_MAX_CACHE - SCROBBLER_CACHE_LEN )
+ scrobbler_write_cache();
+
+ logf("SCROBBLER: add_to_cache[%d] write pos[%ld]", gCache.entries, gCache.pos);
+ /* use prev_crc to allow whole buffer to be checked for consistency */
+ static uint32_t prev_crc = 0x0;
+ if (gCache.pos == 0)
+ prev_crc = 0x0;
+
+ void *buf = &gCache.buf[gCache.pos];
+ memset(buf, 0, SCROBBLER_CACHE_LEN);
+
+ struct cache_entry *entry = buf;
+
+ int ret = create_log_entry(id, entry, &trk_info_len);
+
+ if (ret <= 0 || (size_t) ret >= SCROBBLER_CACHE_LEN)
+ {
+ logf("SCROBBLER: entry too long:");
+ logf("SCROBBLER: %s", id->path);
+ rb->queue_post(&gThread.queue, EV_USER_ERROR, ERR_ENTRY_LENGTH);
+ }
+ else if (ret > 0)
+ {
+ /* first generate a crc over the static portion of the track info data
+ this and a crc of the filename will be used to detect repeat entries
+ */
+ static uint32_t last_crc = 0;
+ uint32_t crc_entry = rb->crc_32(entry->buf, trk_info_len, 0xFFFFFFFF);
+ uint32_t crc_path = rb->crc_32(id->path, rb->strlen(id->path), 0xFFFFFFFF);
+ bool is_unique = track_is_unique(crc_entry, crc_path);
+ bool is_listened = (id->elapsed >= scrobbler_get_threshold(id->length));
+
+ if (is_unique || is_listened)
+ {
+ /* finish calculating the CRC of the whole entry */
+ const void *src = entry->buf + trk_info_len;
+ entry->crc = rb->crc_32(src, ret - trk_info_len, crc_entry) ^ prev_crc;
+ prev_crc = entry->crc;
+ entry->len = ret;
+
+ /* since Listened entries are written regardless
+ make sure this isn't a direct repeat */
+ if ((entry->crc ^ crc_path) != last_crc)
+ {
+
+ if (is_listened)
+ last_crc = (entry->crc ^ crc_path);
+ else
+ last_crc = 0;
+
+ size_t entry_sz = cache_get_entry_size(ret);
+
+ logf("SCROBBLER: Added (#%d) sz[%ld] len[%d], %s",
+ gCache.entries, entry_sz, ret, entry->buf);
+
+ gCache.entries++;
+ /* increase pos by string len + null terminator + sizeof entry */
+ gCache.pos += entry_sz;
+
+#if USING_STORAGE_CALLBACK
+ rb->register_storage_idle_func(scrobbler_flush_callback);
+#endif
+ }
+ }
+ else
+ logf("SCROBBLER: skipping repeat entry: %s", id->path);
+ }
+ rb->mutex_unlock(&gCache.mtx);
+}
+
+static void scrobbler_flush_cache(void)
+{
+ logf("%s", __func__);
+ /* Add any pending entries to the cache */
+ if (gCache.pending)
+ {
+ logf("SCROBBLER: pending entry");
+ gCache.pending = false;
+ if (rb->audio_status())
+ scrobbler_add_to_cache(rb->audio_current_track());
+ }
+
+ /* Write the cache to disk if needed */
+ if (gCache.pos > 0)
+ {
+ scrobbler_write_cache();
+ }
+}
+
+static void track_change_event(unsigned short id, void *ev_data)
+{
+ (void)id;
+ logf("%s", __func__);
+ struct mp3entry *id3 = ((struct track_event *)ev_data)->id3;
+
+ /* check if track was resumed > %threshold played ( likely got saved ) */
+ if ((id3->elapsed > scrobbler_get_threshold(id3->length)))
+ {
+ gCache.pending = false;
+ logf("SCROBBLER: skipping file %s", id3->path);
+ }
+ else
+ {
+ logf("SCROBBLER: add pending %s",id3->path);
+ record_timestamp();
+ gCache.pending = true;
+ }
+}
+
+#ifdef ROCKBOX_HAS_LOGF
+static const char* track_event_info(struct track_event* te)
+{
+
+ static const char *strflags[] = {"TEF_NONE", "TEF_CURRENT",
+ "TEF_AUTOSKIP", "TEF_CUR|ASKIP",
+ "TEF_REWIND", "TEF_CUR|REW",
+ "TEF_ASKIP|REW", "TEF_CUR|ASKIP|REW"};
+/*TEF_NONE = 0x0, no flags are set
+* TEF_CURRENT = 0x1, event is for the current track
+* TEF_AUTO_SKIP = 0x2, event is sent in context of auto skip
+* TEF_REWIND = 0x4, interpret as rewind, id3->elapsed is the
+ position before the seek back to 0
+*/
+ logf("SCROBBLER: flag %d", te->flags);
+ return strflags[te->flags&0x7];
+}
+#endif
+
+static void track_finish_event(unsigned short id, void *ev_data)
+{
+ (void)id;
+ struct track_event *te = ((struct track_event *)ev_data);
+ logf("%s %s %s", __func__, gCache.pending?"True":"False", track_event_info(te));
+ /* add entry using the currently ending track */
+ if (gCache.pending && (te->flags & TEF_CURRENT) && !(te->flags & TEF_REWIND))
+ {
+ gCache.pending = false;
+
+ scrobbler_add_to_cache(te->id3);
+ }
+}
+
+/****************** main thread + helpers ******************/
+static void events_unregister(void)
+{
+ /* we don't want any more events */
+ rb->remove_event(PLAYBACK_EVENT_TRACK_CHANGE, track_change_event);
+ rb->remove_event(PLAYBACK_EVENT_TRACK_FINISH, track_finish_event);
+}
+
+static void events_register(void)
+{
+ rb->add_event(PLAYBACK_EVENT_TRACK_CHANGE, track_change_event);
+ rb->add_event(PLAYBACK_EVENT_TRACK_FINISH, track_finish_event);
+}
+
+void thread(void)
+{
+ bool in_usb = false;
+
+ struct queue_event ev;
+ while (!gThread.exiting)
+ {
+ rb->queue_wait(&gThread.queue, &ev);
+
+ switch (ev.id)
+ {
+ case SYS_USB_CONNECTED:
+ scrobbler_flush_cache();
+ rb->usb_acknowledge(SYS_USB_CONNECTED_ACK);
+ in_usb = true;
+ break;
+ case SYS_USB_DISCONNECTED:
+ in_usb = false;
+ /*fall through*/
+ case EV_STARTUP:
+ logf("SCROBBLER: Thread Started");
+ events_register();
+ play_tone(1500, 100);
+ break;
+ case SYS_POWEROFF:
+ logf("SYS_POWEROFF");
+ /*fall through*/
+ case SYS_REBOOT:
+ gCache.force_flush = true;
+ /*fall through*/
+ case EV_EXIT:
+#if USING_STORAGE_CALLBACK
+ rb->unregister_storage_idle_func(scrobbler_flush_callback, false);
+#endif
+ if (!in_usb)
+ scrobbler_flush_cache();
+
+ events_unregister();
+ return;
+ case EV_FLUSHCACHE:
+ scrobbler_flush_cache();
+ rb->queue_reply(&gThread.queue, 0);
+ break;
+ case EV_USER_ERROR:
+ if (!in_usb)
+ {
+ if (ev.data == ERR_WRITING_FILE)
+ rb->splash(HZ, "SCROBBLER: error writing log");
+ else if (ev.data == ERR_ENTRY_LENGTH)
+ rb->splash(HZ, "SCROBBLER: error entry too long");
+ else if (ev.data == ERR_WRITING_DATA)
+ rb->splash(HZ, "SCROBBLER: error bad entry data");
+ }
+ break;
+ default:
+ logf("default %ld", ev.id);
+ break;
+ }
+ }
+}
+
+void thread_create(void)
+{
+ /* put the thread's queue in the broadcast list */
+ rb->queue_init(&gThread.queue, true);
+ gThread.id = rb->create_thread(thread, gThread.stack, sizeof(gThread.stack),
+ 0, "Last.Fm_TSR"
+ IF_PRIO(, PRIORITY_BACKGROUND)
+ IF_COP(, CPU));
+ rb->queue_enable_queue_send(&gThread.queue, &gThread.queue_send, gThread.id);
+ rb->queue_post(&gThread.queue, EV_STARTUP, 0);
+ rb->yield();
+}
+
+void thread_quit(void)
+{
+ if (!gThread.exiting) {
+ gThread.exiting = true;
+ rb->queue_post(&gThread.queue, EV_EXIT, 0);
+ rb->thread_wait(gThread.id);
+ /* remove the thread's queue from the broadcast list */
+ rb->queue_delete(&gThread.queue);
+ }
+}
+
+/* callback to end the TSR plugin, called before a new plugin gets loaded */
+static int plugin_exit_tsr(bool reenter)
+{
+ MENUITEM_STRINGLIST(menu, ID2P(LANG_AUDIOSCROBBLER), NULL, ID2P(LANG_SETTINGS),
+ "Flush Cache", "Exit Plugin", ID2P(LANG_BACK));
+
+ const struct text_message quit_prompt = {
+ (const char*[]){ ID2P(LANG_AUDIOSCROBBLER),
+ "is currently running.",
+ "Quit scrobbler?" }, 3
+ };
+
+ if (gThread.hide_reentry &&
+ (rb->audio_status() & (AUDIO_STATUS_PLAY | AUDIO_STATUS_PAUSE)) == 0)
+ {
+ gThread.hide_reentry = false;
+ return PLUGIN_TSR_CONTINUE;
+ }
+
+ while(true)
+ {
+ int result = reenter ? rb->do_menu(&menu, NULL, NULL, false) : 2;
+ switch(result)
+ {
+ case 0: /* settings */
+ config_settings_menu();
+ break;
+ case 1: /* flush cache */
+ if (gCache.entries > 0)
+ {
+ rb->queue_send(&gThread.queue, EV_FLUSHCACHE, 0);
+ if (gConfig.verbose)
+ rb->splashf(2*HZ, "%s Cache Flushed", rb->str(LANG_AUDIOSCROBBLER));
+ }
+ break;
+
+ case 2: /* exit plugin - quit */
+ if(rb->gui_syncyesno_run(&quit_prompt, NULL, NULL) == YESNO_YES)
+ {
+ scrobbler_flush_cache();
+ thread_quit();
+ return (reenter ? PLUGIN_TSR_TERMINATE : PLUGIN_TSR_SUSPEND);
+ }
+ /* Fall Through */
+ case 3: /* back to menu */
+ return PLUGIN_TSR_CONTINUE;
+ }
+ }
+}
+
+/****************** main ******************/
+static int plugin_main(const void* parameter)
+{
+ struct scrobbler_cfg cfg;
+ rb->memcpy(&cfg, &gConfig, sizeof(struct scrobbler_cfg)); /* store settings */
+
+ /* Resume plugin ? -- silences startup */
+ if (parameter == rb->plugin_tsr)
+ {
+ gConfig.beeplvl = 0;
+ gConfig.playback = false;
+ gConfig.verbose = false;
+ }
+
+ rb->memset(&gThread, 0, sizeof(gThread));
+ if (gConfig.verbose)
+ rb->splashf(HZ / 2, "%s Started",rb->str(LANG_AUDIOSCROBBLER));
+ logf("%s: %s Started", __func__, rb->str(LANG_AUDIOSCROBBLER));
+
+ rb->plugin_tsr(plugin_exit_tsr); /* stay resident */
+
+ thread_create();
+ rb->memcpy(&gConfig, &cfg, sizeof(struct scrobbler_cfg)); /*restore settings */
+
+ if (gConfig.playback)
+ {
+ gThread.hide_reentry = true;
+ return PLUGIN_GOTO_WPS;
+ }
+ return PLUGIN_OK;
+}
+
+/***************** Plugin Entry Point *****************/
+
+enum plugin_status plugin_start(const void* parameter)
+{
+ /* now go ahead and have fun! */
+ if (rb->usb_inserted() == true)
+ return PLUGIN_USB_CONNECTED;
+
+ if (scrobbler_init_cache() < 0)
+ return PLUGIN_ERROR;
+
+ config_set_defaults();
+
+ if (configfile_load(CFG_FILE, config, gCfg_sz, CFG_VER) < 0)
+ {
+ /* If the loading failed, save a new config file */
+ configfile_save(CFG_FILE, config, gCfg_sz, CFG_VER);
+ if (gConfig.verbose)
+ rb->splash(HZ, ID2P(LANG_REVERT_TO_DEFAULT_SETTINGS));
+ }
+
+ int ret = plugin_main(parameter);
+ return ret;
+}
diff --git a/apps/plugins/lastfm_scrobbler_viewer.c b/apps/plugins/lastfm_scrobbler_viewer.c
new file mode 100644
index 0000000000..c35ba64918
--- /dev/null
+++ b/apps/plugins/lastfm_scrobbler_viewer.c
@@ -0,0 +1,1033 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// __ \_/ ___\| |/ /| __ \ / __ \ \/ /
+ * Jukebox | | ( (__) ) \___| ( | \_\ ( (__) ) (
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2023 William Wilgus
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "plugin.h"
+#include "lang_enum.h"
+
+#include "lib/printcell_helper.h"
+
+#include "lib/configfile.h"
+#define CFG_FILE "/lastfm_scrobbler_viewer.cfg"
+#define TMP_FILE ""PLUGIN_DATA_DIR "/lscrobbler_viewer_%d.tmp"
+#define CFG_VER 1
+
+#ifdef ROCKBOX_HAS_LOGF
+#define logf rb->logf
+#else
+#define logf(...) do { } while(0)
+#endif
+
+/*#ARTIST #ALBUM #TITLE #TRACKNUM #LENGTH #RATING #TIMESTAMP #MUSICBRAINZ_TRACKID*/
+
+#define SCROBBLE_HDR_FMT "$*%d$%s$*%d$%s$*%d$%s$Track#$Length$Rating$TimeStamp$TrackId"
+//#define SCROBBLE_HDR "$*128$Artist$*128$Album$*128$Title$Track#$Length$Rating$TimeStamp$TrackId"
+
+#define SCROBBLER_MIN_COLUMNS (6) /* a valid scrobbler file should have at least this many columns */
+
+#define GOTO_ACTION_DEFAULT_HANDLER (PLUGIN_OK + 1)
+#define CANCEL_CONTEXT_MENU (PLUGIN_OK + 2)
+#define QUIT_CONTEXT_MENU (PLUGIN_OK + 3)
+/* global lists, for everything */
+static struct gui_synclist lists;
+
+/* printcell data for the current file */
+struct printcell_data_t {
+ int view_columns;
+ int view_lastcol;
+
+ int items_buffered;
+ int items_total;
+ int fd_cur;
+ char *filename;
+
+ char *buf;
+ size_t buf_size;
+ off_t buf_used;
+ char header[PRINTCELL_MAXLINELEN];
+
+};
+
+enum e_find_type {
+ FIND_ALL = 0,
+ FIND_EXCLUDE,
+ FIND_EXCLUDE_CASE,
+ FIND_EXCLUDE_ANY,
+ FIND_INCLUDE,
+ FIND_INCLUDE_CASE,
+ FIND_INCLUDE_ANY,
+ FIND_CUSTOM,
+};
+
+static void synclist_set(int selected_item, int items, int sel_size, struct printcell_data_t *pc_data);
+static void pc_data_set_header(struct printcell_data_t *pc_data);
+
+static void browse_file(char *buf, size_t bufsz)
+{
+ struct browse_context browse = {
+ .dirfilter = SHOW_ALL,
+ .flags = BROWSE_SELECTONLY,
+ .title = "Select a scrobbler log file",
+ .icon = Icon_Playlist,
+ .buf = buf,
+ .bufsize = bufsz,
+ .root = "/",
+ };
+
+ if (rb->rockbox_browse(&browse) != GO_TO_PREVIOUS)
+ {
+ buf[0] = '\0';
+ }
+}
+
+static struct plugin_config
+{
+ bool separator;
+ bool talk;
+ int col_width;
+ int hidecol_flags;
+} gConfig;
+
+static struct configdata config[] =
+{
+ {TYPE_BOOL, 0, 1, { .bool_p = &gConfig.separator }, "Cell Separator", NULL},
+ {TYPE_BOOL, 0, 1, { .bool_p = &gConfig.talk }, "Voice", NULL},
+ {TYPE_INT, 32, LCD_WIDTH, { .int_p = &gConfig.col_width }, "Cell Width", NULL},
+ {TYPE_INT, INT_MIN, INT_MAX, { .int_p = &gConfig.hidecol_flags }, "Hidden Columns", NULL},
+};
+const int gCfg_sz = sizeof(config)/sizeof(*config);
+/****************** config functions *****************/
+static void config_set_defaults(void)
+{
+ gConfig.col_width = MIN(LCD_WIDTH, 128);
+ gConfig.hidecol_flags = 0;
+ gConfig.separator = true;
+ gConfig.talk = rb->global_settings->talk_menu;
+}
+
+static void config_save(void)
+{
+ configfile_save(CFG_FILE, config, gCfg_sz, CFG_VER);
+}
+
+static int config_settings_menu(struct printcell_data_t *pc_data)
+{
+ int selection = 0;
+
+ struct viewport parentvp[NB_SCREENS];
+ FOR_NB_SCREENS(l)
+ {
+ rb->viewport_set_defaults(&parentvp[l], l);
+ rb->viewport_set_fullscreen(&parentvp[l], l);
+ }
+
+ MENUITEM_STRINGLIST(settings_menu, ID2P(LANG_SETTINGS), NULL,
+ ID2P(LANG_LIST_SEPARATOR),
+ "Cell Width",
+ ID2P(LANG_VOICE),
+ ID2P(VOICE_BLANK),
+ ID2P(VOICE_BLANK),
+ ID2P(LANG_CANCEL_0),
+ ID2P(LANG_SAVE_EXIT));
+
+ do {
+ selection=rb->do_menu(&settings_menu,&selection, parentvp, true);
+ switch(selection) {
+
+ case 0:
+ rb->set_bool(rb->str(LANG_LIST_SEPARATOR), &gConfig.separator);
+ break;
+ case 1:
+ rb->set_int("Cell Width", "", UNIT_INT,
+ &gConfig.col_width, NULL, 1, 32, LCD_WIDTH, NULL );
+ break;
+ case 2:
+ rb->set_bool(rb->str(LANG_VOICE), &gConfig.talk);
+ break;
+ case 3:
+ continue;
+ case 4: /*sep*/
+ continue;
+ case 5:
+ return -1;
+ break;
+ case 6:
+ {
+ pc_data_set_header(pc_data);
+ synclist_set(0, pc_data->items_total, 1, pc_data);
+ int res = configfile_save(CFG_FILE, config, gCfg_sz, CFG_VER);
+ if (res >= 0)
+ {
+ logf("Cfg saved %s %d bytes", CFG_FILE, gCfg_sz);
+ return PLUGIN_OK;
+ }
+ logf("Cfg FAILED (%d) %s", res, CFG_FILE);
+ return PLUGIN_ERROR;
+ }
+ case MENU_ATTACHED_USB:
+ return PLUGIN_USB_CONNECTED;
+ default:
+ return PLUGIN_OK;
+ }
+ } while ( selection >= 0 );
+ return 0;
+}
+
+/* open file pass back file descriptor and return file size */
+static size_t file_open(const char *filename, int *fd)
+{
+ size_t fsize = 0;
+
+ if (filename && fd)
+ {
+ *fd = rb->open(filename, O_RDONLY);
+ if (*fd >= 0)
+ {
+ fsize = rb->filesize(*fd);
+ }
+ }
+ return fsize;
+}
+
+static const char* list_get_name_cb(int selected_item, void* data,
+ char* buf, size_t buf_len)
+{
+ const int slush_pos = 32; /* entries around the current entry to keep buffered */
+ /* keep track of the last position in the buffer */
+ static int buf_line_num = 0;
+ static off_t buf_last_pos = 0;
+ /* keep track of the last position in the file */
+ static int file_line_num = 0;
+ static off_t file_last_seek = 0;
+
+ if (selected_item < 0)
+ {
+ logf("[%s] Reset positions", __func__);
+ buf_line_num = 0;
+ buf_last_pos = 0;
+ file_line_num = 0;
+ file_last_seek = 0;
+ return "";
+ }
+
+ int line_num;
+ struct printcell_data_t *pc_data = (struct printcell_data_t*) data;
+ bool found = false;
+
+ if (pc_data->buf && selected_item < pc_data->items_buffered)
+ {
+ int buf_pos;
+
+ if (buf_line_num > selected_item || buf_last_pos >= pc_data->buf_used
+ || buf_line_num < 0)
+ {
+ logf("[%s] Set pos buffer 0", __func__);
+ buf_line_num = 0;
+ buf_last_pos = 0;
+ }
+ buf_pos = buf_last_pos;
+ line_num = buf_line_num;
+
+ while (buf_pos < pc_data->buf_used
+ && line_num < pc_data->items_buffered)
+ {
+ size_t len = rb->strlen(&pc_data->buf[buf_pos]);
+ if(line_num == selected_item)
+ {
+ rb->strlcpy(buf, &pc_data->buf[buf_pos], MIN(buf_len, len));
+ logf("(%d) in buffer: %s", line_num, buf);
+ found = true;
+ break;
+ }
+ else
+ {
+ buf_pos += len + 1; /* need to go past the NULL terminator */
+ line_num++;
+
+ if (buf_line_num + slush_pos < selected_item)
+ {
+ logf("[%s] Set pos buffer %d", __func__, line_num);
+ buf_line_num = line_num;
+ buf_last_pos = buf_pos;
+ }
+ }
+ }
+ }
+
+ /* didn't find the item try the file */
+ if(!found && pc_data->fd_cur >= 0)
+ {
+ int fd = pc_data->fd_cur;
+
+ if (file_line_num < 0 || file_line_num > selected_item)
+ {
+ logf("[%s] Set seek file 0", __func__);
+ file_line_num = 0;
+ file_last_seek = 0;
+ }
+
+ rb->lseek(fd, file_last_seek, SEEK_SET);
+ line_num = file_line_num;
+
+ while ((rb->read_line(fd, buf, buf_len)) > 0)
+ {
+ if(buf[0] == '#')
+ continue;
+ if(line_num == selected_item)
+ {
+ logf("(%d) in file: %s", line_num, buf);
+ found = true;
+ break;
+ }
+ else
+ {
+ line_num++;
+
+ if (file_line_num + slush_pos < selected_item)
+ {
+ logf("[%s] Set seek file %d", __func__, line_num);
+ file_line_num = line_num;
+ file_last_seek = rb->lseek(fd, 0, SEEK_CUR);
+ }
+ }
+ }
+ }
+
+ if(!found)
+ {
+ logf("(%d) Not Found!", selected_item);
+ buf_line_num = -1;
+ file_line_num = -1;
+ buf[0] = '\0';
+ }
+ return buf;
+}
+
+static int list_voice_cb(int list_index, void* data)
+{
+ (void) list_index;
+ struct printcell_data_t *pc_data = (struct printcell_data_t*) data;
+ if (!gConfig.talk)
+ return -1;
+
+ int selcol = printcell_get_column_selected();
+ char buf[MAX_PATH];
+ char* name;
+ long id;
+
+ if (pc_data->view_lastcol != selcol)
+ {
+ name = printcell_get_title_text(selcol, buf, sizeof(buf));
+
+ id = P2ID((const unsigned char *)name);
+
+ if(id>=0)
+ rb->talk_id(id, true);
+ else if (selcol >= 0)
+ {
+ switch (selcol)
+ {
+ case 0:
+ rb->talk_id(LANG_ID3_ARTIST, true);
+ break;
+ case 1:
+ rb->talk_id(LANG_ID3_ALBUM, true);
+ break;
+ case 2:
+ rb->talk_id(LANG_ID3_TITLE, true);
+ break;
+ case 3:
+ rb->talk_id(LANG_ID3_TRACKNUM, true);
+ break;
+ case 4:
+ rb->talk_id(LANG_ID3_LENGTH, true);
+ break;
+
+ default:
+ rb->talk_spell(name, true);
+ break;
+ }
+ }
+ else
+ rb->talk_id(LANG_ALL, true);
+
+ rb->talk_id(VOICE_PAUSE, true);
+ }
+
+ name = printcell_get_column_text(selcol, buf, sizeof(buf));
+
+ id = P2ID((const unsigned char *)name);
+
+ if(id>=0)
+ rb->talk_id(id, true);
+ else if (selcol >= 0)
+ rb->talk_spell(name, true);
+
+ return 0;
+}
+
+static enum themable_icons list_icon_cb(int selected_item, void *data)
+{
+ struct printcell_data_t *pc_data = (struct printcell_data_t*) data;
+ if (lists.selected_item == selected_item)
+ {
+ if (pc_data->view_lastcol < 0)
+ return Icon_Config;
+ return Icon_Audio;
+ }
+ return Icon_NOICON;
+}
+
+/* load file entries into pc_data buffer, file should already be opened
+ * and will be closed if the whole file was buffered */
+static int file_load_entries(struct printcell_data_t *pc_data)
+{
+ int items = 0;
+ int count = 0;
+ int buffered = 0;
+ unsigned int pos = 0;
+ bool comment = false;
+ char ch;
+ int fd = pc_data->fd_cur;
+ if (fd < 0)
+ return 0;
+
+ rb->lseek(fd, 0, SEEK_SET);
+
+ while (rb->read(fd, &ch, 1) > 0)
+ {
+ if (count++ == 0 && ch == '#') /* skip comments */
+ comment = true;
+ else if (!comment && ch != '\r' && pc_data->buf_size > pos)
+ pc_data->buf[pos++] = ch;
+
+ if (items == 0 && pos > PRINTCELL_MAXLINELEN * 2)
+ break;
+
+ if (ch == '\n')
+ {
+ if (!comment)
+ {
+ pc_data->buf[pos] = '\0';
+ if (pc_data->buf_size > pos)
+ {
+ pos++;
+ buffered++;
+ }
+ items++;
+ }
+ comment = false;
+ count = 0;
+ rb->yield();
+ }
+ }
+
+ logf("[%s] items: %d buffered: %d", __func__, items, buffered);
+
+ pc_data->items_total = items;
+ pc_data->items_buffered = buffered;
+ pc_data->buf[pos] = '\0';
+ pc_data->buf_used = pos;
+
+ if (items == buffered) /* whole file fit into buffer; close file */
+ {
+ rb->close(pc_data->fd_cur);
+ pc_data->fd_cur = -1;
+ }
+
+ list_get_name_cb(-1, NULL, NULL, 0); /* prime name cb */
+ return items;
+}
+
+static int filter_items(struct printcell_data_t *pc_data,
+ enum e_find_type find_type, int col)
+{
+ /* saves filtered items to a temp file and loads it */
+ int fd;
+ bool reload = false;
+ char buf[PRINTCELL_MAXLINELEN];
+ char find_exclude_buf[PRINTCELL_MAXLINELEN];
+ int selcol = printcell_get_column_selected();
+ char *find_exclude = printcell_get_column_text(selcol, find_exclude_buf,
+ sizeof(find_exclude_buf));
+ const char colsep = '\t';
+ int find_len = rb->strlen(find_exclude);
+
+ if (find_type == FIND_CUSTOM || find_len == 0)
+ {
+ int option = 0;
+ struct opt_items find_types[] = {
+ {"Exclude", -1}, {"Exclude Case Sensitive", -1},
+ {"Exclude Any", -1}, {"Include", -1},
+ {"Include Case Sensitive", -1}, {"Include Any", -1}
+ };
+ if (rb->set_option("Find Type", &option, RB_INT,
+ find_types, 6, NULL))
+ {
+ return 0;
+ }
+ switch (option)
+ {
+ case 0:
+ find_type = FIND_EXCLUDE;
+ break;
+ case 1:
+ find_type = FIND_EXCLUDE_CASE;
+ break;
+ case 2:
+ find_type = FIND_EXCLUDE_ANY;
+ break;
+ case 3:
+ find_type = FIND_INCLUDE;
+ break;
+ case 4:
+ find_type = FIND_INCLUDE_CASE;
+ break;
+ case 5:
+ find_type = FIND_INCLUDE_ANY;
+ break;
+ default:
+ find_type = FIND_ALL;
+ break;
+ }
+
+ /* copy data to beginning of buf */
+ rb->memmove(find_exclude_buf, find_exclude, find_len);
+ find_exclude_buf[find_len] = '\0';
+
+ if (rb->kbd_input(find_exclude_buf, sizeof(find_exclude_buf), NULL) < 0)
+ return -1;
+ find_exclude = find_exclude_buf;
+ find_len = rb->strlen(find_exclude);
+ }
+
+ char tmp_filename[MAX_PATH];
+ static int tmp_num = 0;
+ rb->snprintf(tmp_filename, sizeof(tmp_filename), TMP_FILE, tmp_num);
+ tmp_num++;
+
+ fd = rb->open(tmp_filename, O_RDWR | O_CREAT | O_TRUNC, 0666);
+ if(fd >= 0)
+ {
+ rb->splash_progress_set_delay(HZ * 5);
+ for (int i = 0; i < pc_data->items_total; i++)
+ {
+ rb->splash_progress(i, pc_data->items_total, "Filtering...");
+ const char * data = list_get_name_cb(i, pc_data, buf, sizeof(buf));
+
+ if (find_type != FIND_ALL)
+ {
+ int index = col;
+
+ if (index < 0)
+ index = 0;
+ if (find_len > 0)
+ {
+ char *bcol = buf;
+ while (*bcol != '\0' && index > 0)
+ {
+ if (*bcol == colsep)
+ index--;
+ bcol++;
+ }
+ if (index > 0)
+ continue;
+
+ if (find_type == FIND_EXCLUDE)
+ {
+ if (rb->strncasecmp(bcol, find_exclude, find_len) == 0)
+ {
+ logf("[%s] exclude [%s]", find_exclude, bcol);
+ continue;
+ }
+ }
+ else if (find_type == FIND_INCLUDE)
+ {
+ if (rb->strncasecmp(bcol, find_exclude, find_len) != 0)
+ {
+ logf("%s include %s", find_exclude, bcol);
+ continue;
+ }
+ }
+ else if (find_type == FIND_EXCLUDE_CASE)
+ {
+ if (rb->strncmp(bcol, find_exclude, find_len) == 0)
+ {
+ logf("[%s] exclude case [%s]", find_exclude, bcol);
+ continue;
+ }
+ }
+ else if (find_type == FIND_INCLUDE_CASE)
+ {
+ if (rb->strncmp(bcol, find_exclude, find_len) != 0)
+ {
+ logf("%s include case %s", find_exclude, bcol);
+ continue;
+ }
+ }
+ else if (find_type == FIND_EXCLUDE_ANY)
+ {
+ bool found = false;
+ while (*bcol != '\0' && *bcol != colsep)
+ {
+ if (rb->strncasecmp(bcol, find_exclude, find_len) == 0)
+ {
+ logf("[%s] exclude any [%s]", find_exclude, bcol);
+ found = true;
+ break;
+ }
+ bcol++;
+ }
+ if (found)
+ continue;
+ }
+ else if (find_type == FIND_INCLUDE_ANY)
+ {
+ bool found = false;
+ while (*bcol != '\0' && *bcol != colsep)
+ {
+ if (rb->strncasecmp(bcol, find_exclude, find_len) == 0)
+ {
+ found = true;
+ logf("[%s] include any [%s]", find_exclude, bcol);
+ break;
+ }
+ bcol++;
+ }
+ if (!found)
+ continue;
+ }
+ }
+ }
+ int len = rb->strlen(data);
+ if (len > 0)
+ {
+ logf("writing [%d bytes][%s]", len + 1, data);
+ rb->write(fd, data, len);
+ rb->write(fd, "\n", 1);
+ }
+ }
+ reload = true;
+ }
+
+ if (reload)
+ {
+ if (pc_data->fd_cur >= 0)
+ rb->close(pc_data->fd_cur);
+
+ pc_data->fd_cur = fd;
+ int items = file_load_entries(pc_data);
+ if (items >= 0)
+ {
+ lists.selected_item = 0;
+ rb->gui_synclist_set_nb_items(&lists, items);
+ }
+ }
+ logf("Col text '%s'", find_exclude);
+ logf("text '%s'", find_exclude_buf);
+
+ return pc_data->items_total;
+}
+
+static int scrobbler_context_menu(struct printcell_data_t *pc_data)
+{
+ struct viewport parentvp[NB_SCREENS];
+ FOR_NB_SCREENS(l)
+ {
+ rb->viewport_set_defaults(&parentvp[l], l);
+ rb->viewport_set_fullscreen(&parentvp[l], l);
+ }
+
+ int col = printcell_get_column_selected();
+ int selection = 0;
+ int items = 0;
+ uint32_t visible = printcell_get_column_visibility(-1);
+ bool hide_col = PRINTCELL_COLUMN_IS_VISIBLE(visible, col);
+
+ char namebuf[PRINTCELL_MAXLINELEN];
+ enum e_find_type find_type = FIND_ALL;
+
+ char *colname = pc_data->filename;
+ if (col >= 0)
+ colname = printcell_get_title_text(col, namebuf, sizeof(namebuf));
+
+#define MENUITEM_STRINGLIST_CUSTOM(name, str, callback, ... ) \
+ const char *name##_[] = {__VA_ARGS__}; \
+ const struct menu_callback_with_desc name##__ = \
+ {callback,str, Icon_NOICON}; \
+ const struct menu_item_ex name = \
+ {MT_RETURN_ID|MENU_HAS_DESC| \
+ MENU_ITEM_COUNT(sizeof( name##_)/sizeof(*name##_)), \
+ { .strings = name##_},{.callback_and_desc = & name##__}};
+
+ const char *menu_item[4];
+
+ menu_item[0]= hide_col ? "Hide":"Show";
+ menu_item[1]= "Exclude";
+ menu_item[2]= "Include";
+ menu_item[3]= "Custom Filter";
+
+ if (col == -1)
+ {
+ menu_item[0]= hide_col ? "Hide All":"Show All";
+ menu_item[1]= "Open";
+ menu_item[2]= "Reload";
+ menu_item[3]= ID2P(LANG_SETTINGS);
+ }
+
+ MENUITEM_STRINGLIST_CUSTOM(context_menu, colname, NULL,
+ menu_item[0],
+ menu_item[1],
+ menu_item[2],
+ menu_item[3],
+ ID2P(VOICE_BLANK),
+ ID2P(LANG_CANCEL_0),
+ ID2P(LANG_MENU_QUIT));
+
+#undef MENUITEM_STRINGLIST_CUSTOM
+ do {
+ selection=rb->do_menu(&context_menu,&selection, parentvp, true);
+ switch(selection) {
+
+ case 0:
+ {
+ printcell_set_column_visible(col, !hide_col);
+ if (hide_col)
+ {
+ do
+ {
+ col = printcell_increment_column(1, true);
+ } while (col >= 0 && printcell_get_column_visibility(col) == 1);
+ pc_data->view_lastcol = col;
+ }
+ gConfig.hidecol_flags = printcell_get_column_visibility(-1);
+ break;
+ }
+ case 1: /* Exclude / Open */
+ {
+ if (col == -1)/*Open*/
+ {
+ char buf[MAX_PATH];
+ browse_file(buf, sizeof(buf));
+ if (rb->file_exists(buf))
+ {
+ rb->strlcpy(pc_data->filename, buf, MAX_PATH);
+ if (pc_data->fd_cur >= 0)
+ rb->close(pc_data->fd_cur);
+ if (file_open(pc_data->filename, &pc_data->fd_cur) > 0)
+ items = file_load_entries(pc_data);
+ if (items >= 0)
+ synclist_set(0, items, 1, pc_data);
+ }
+ else
+ rb->splash(HZ *2, "Error Opening");
+
+ return CANCEL_CONTEXT_MENU;
+ }
+
+ find_type = FIND_EXCLUDE;
+ }
+ /* fall-through */
+ case 2: /* Include / Reload */
+ {
+ if (col == -1) /*Reload*/
+ {
+ if (pc_data->fd_cur >= 0)
+ rb->close(pc_data->fd_cur);
+ if (file_open(pc_data->filename, &pc_data->fd_cur) > 0)
+ items = file_load_entries(pc_data);
+ if (items >= 0)
+ rb->gui_synclist_set_nb_items(&lists, items);
+ return CANCEL_CONTEXT_MENU;
+ }
+
+ if (find_type == FIND_ALL)
+ find_type = FIND_INCLUDE;
+ /* fall-through */
+ }
+ case 3: /*Custom Filter / Settings */
+ {
+ if (col == -1)/*Settings*/
+ return config_settings_menu(pc_data);
+
+ if (find_type == FIND_ALL)
+ find_type = FIND_CUSTOM;
+ items = filter_items(pc_data, find_type, col);
+
+ if (items >= 0)
+ rb->gui_synclist_set_nb_items(&lists, items);
+ break;
+ }
+ case 4: /*sep*/
+ continue;
+ case 5:
+ return CANCEL_CONTEXT_MENU;
+ break;
+ case 6: /* Quit */
+ return QUIT_CONTEXT_MENU;
+ break;
+ case MENU_ATTACHED_USB:
+ return PLUGIN_USB_CONNECTED;
+ default:
+ return PLUGIN_OK;
+ }
+ } while ( selection < 0 );
+ return 0;
+}
+
+static void cleanup(void *parameter)
+{
+ struct printcell_data_t *pc_data = (struct printcell_data_t*) parameter;
+ if (pc_data->fd_cur >= 0)
+ rb->close(pc_data->fd_cur);
+ pc_data->fd_cur = -1;
+}
+
+static void menu_action_printcell(int *action, int selected_item, bool* exit, struct gui_synclist *lists)
+{
+ (void) exit;
+ struct printcell_data_t *pc_data = (struct printcell_data_t*) lists->data;
+ if (*action == ACTION_STD_OK)
+ {
+ if (selected_item < lists->nb_items)
+ {
+ pc_data->view_lastcol = printcell_increment_column(1, true);
+ *action = ACTION_NONE;
+ }
+ }
+ else if (*action == ACTION_STD_CANCEL)
+ {
+ pc_data->view_lastcol = printcell_increment_column(-1, true);
+ if (pc_data->view_lastcol != pc_data->view_columns - 1)
+ {
+ *action = ACTION_NONE;
+ }
+ }
+ else if (*action == ACTION_STD_CONTEXT)
+ {
+ int ctxret = scrobbler_context_menu(pc_data);
+ if (ctxret == QUIT_CONTEXT_MENU)
+ *exit = true;
+ }
+}
+
+int menu_action_cb(int *action, int selected_item, bool* exit, struct gui_synclist *lists)
+{
+
+ menu_action_printcell(action, selected_item, exit, lists);
+
+ if (rb->default_event_handler_ex(*action, cleanup, lists->data) == SYS_USB_CONNECTED)
+ {
+ *exit = true;
+ return PLUGIN_USB_CONNECTED;
+ }
+ return PLUGIN_OK;
+}
+
+static int count_max_columns(int items, char delimeter,
+ int expected_cols, struct printcell_data_t *pc_data)
+{
+ int max_cols = 0;
+ int cols = 0;
+ char buf[PRINTCELL_MAXLINELEN];
+ for (int i = 0; i < items; i++)
+ {
+ const char *txt = list_get_name_cb(i, pc_data, buf, sizeof(buf));
+ while (*txt != '\0')
+ {
+ if (*txt == delimeter)
+ {
+ cols++;
+ if (cols == expected_cols)
+ {
+ max_cols = cols;
+ break;
+ }
+ }
+ txt++;
+ }
+
+ if(max_cols < expected_cols && i > 32)
+ break;
+
+ if (cols > max_cols)
+ max_cols = cols;
+ cols = 0;
+ }
+ return max_cols;
+}
+
+static void synclist_set(int selected_item, int items, int sel_size, struct printcell_data_t *pc_data)
+{
+ if (items <= 0)
+ return;
+ if (selected_item < 0)
+ selected_item = 0;
+
+ rb->gui_synclist_init(&lists,list_get_name_cb,
+ pc_data, false, sel_size, NULL);
+
+ rb->gui_synclist_set_icon_callback(&lists, list_icon_cb);
+ rb->gui_synclist_set_voice_callback(&lists, list_voice_cb);
+ rb->gui_synclist_set_nb_items(&lists,items);
+ rb->gui_synclist_select_item(&lists, selected_item);
+
+ struct printcell_settings pcs = {.cell_separator = gConfig.separator,
+ .title_delimeter = '$',
+ .text_delimeter = '\t',
+ .hidecol_flags = gConfig.hidecol_flags};
+
+ pc_data->view_columns = printcell_set_columns(&lists, &pcs,
+ pc_data->header, Icon_Rockbox);
+ printcell_enable(true);
+
+
+ int max_cols = count_max_columns(items, pcs.text_delimeter,
+ SCROBBLER_MIN_COLUMNS, pc_data);
+ if (max_cols < SCROBBLER_MIN_COLUMNS) /* not a scrobbler file? */
+ {
+ rb->gui_synclist_set_voice_callback(&lists, NULL);
+ pc_data->view_columns = printcell_set_columns(&lists, NULL,
+ "$*512$", Icon_Questionmark);
+ }
+
+ int curcol = printcell_get_column_selected();
+ while (curcol >= 0)
+ curcol = printcell_increment_column(-1, false);
+
+ if (pc_data->view_lastcol >= pc_data->view_columns)
+ pc_data->view_lastcol = -1;
+
+ /* restore column position */
+ while (pc_data->view_lastcol > -1 && curcol != pc_data->view_lastcol)
+ {
+ curcol = printcell_increment_column(1, true);
+ }
+ pc_data->view_lastcol = curcol;
+ list_voice_cb(0, pc_data);
+}
+
+static void pc_data_set_header(struct printcell_data_t *pc_data)
+{
+ int col_w = gConfig.col_width;
+ rb->snprintf(pc_data->header, PRINTCELL_MAXLINELEN,
+ SCROBBLE_HDR_FMT, col_w, rb->str(LANG_ID3_ARTIST),
+ col_w, rb->str(LANG_ID3_ALBUM),
+ col_w, rb->str(LANG_ID3_TITLE));
+}
+
+enum plugin_status plugin_start(const void* parameter)
+{
+ int ret = PLUGIN_OK;
+ int selected_item = -1;
+ int action;
+ int items = 0;
+#if CONFIG_RTC
+ static char filename[MAX_PATH] = HOME_DIR "/.scrobbler.log";
+#else /* !CONFIG_RTC */
+ static char filename[MAX_PATH] = HOME_DIR "/.scrobbler-timeless.log";
+#endif /* CONFIG_RTC */
+ bool redraw = true;
+ bool exit = false;
+
+ static struct printcell_data_t printcell_data;
+
+ if (parameter)
+ {
+ rb->strlcpy(filename, (const char*)parameter, MAX_PATH);
+ filename[MAX_PATH - 1] = '\0';
+ }
+
+ if (!rb->file_exists(filename))
+ {
+ browse_file(filename, sizeof(filename));
+ if (!rb->file_exists(filename))
+ {
+ rb->splash(HZ, "No Scrobbler file Goodbye.");
+ return PLUGIN_ERROR;
+ }
+
+ }
+
+ config_set_defaults();
+ if (configfile_load(CFG_FILE, config, gCfg_sz, CFG_VER) < 0)
+ {
+ /* If the loading failed, save a new config file */
+ config_set_defaults();
+ configfile_save(CFG_FILE, config, gCfg_sz, CFG_VER);
+
+ rb->splash(HZ, ID2P(LANG_REVERT_TO_DEFAULT_SETTINGS));
+ }
+
+ rb->memset(&printcell_data, 0, sizeof (struct printcell_data_t));
+ printcell_data.fd_cur = -1;
+ printcell_data.view_lastcol = -1;
+
+ if (rb->file_exists(filename))
+ {
+ if (file_open(filename, &printcell_data.fd_cur) == 0)
+ printcell_data.fd_cur = -1;
+ else
+ {
+ size_t buf_size;
+ printcell_data.buf = rb->plugin_get_buffer(&buf_size);
+ printcell_data.buf_size = buf_size;
+ printcell_data.buf_used = 0;
+ printcell_data.filename = filename;
+ items = file_load_entries(&printcell_data);
+ }
+ }
+ int col_w = gConfig.col_width;
+ rb->snprintf(printcell_data.header, PRINTCELL_MAXLINELEN,
+ SCROBBLE_HDR_FMT, col_w, rb->str(LANG_ID3_ARTIST),
+ col_w, rb->str(LANG_ID3_ALBUM),
+ col_w, rb->str(LANG_ID3_TITLE));
+
+ if (!exit && items > 0)
+ {
+ synclist_set(0, items, 1, &printcell_data);
+ rb->gui_synclist_draw(&lists);
+
+ while (!exit)
+ {
+ action = rb->get_action(CONTEXT_LIST, HZ / 10);
+
+ if (action == ACTION_NONE)
+ {
+ if (redraw)
+ {
+ action = ACTION_REDRAW;
+ redraw = false;
+ }
+ }
+ else
+ redraw = true;
+
+ ret = menu_action_cb(&action, selected_item, &exit, &lists);
+ if (rb->gui_synclist_do_button(&lists, &action))
+ continue;
+ selected_item = rb->gui_synclist_get_sel_pos(&lists);
+
+ }
+ }
+ config_save();
+ cleanup(&printcell_data);
+ return ret;
+}
diff --git a/apps/plugins/lib/SOURCES b/apps/plugins/lib/SOURCES
index 811771e0ca..09a8b1c5ed 100644
--- a/apps/plugins/lib/SOURCES
+++ b/apps/plugins/lib/SOURCES
@@ -1,7 +1,11 @@
sha1.c
gcc-support.c
pluginlib_actions.c
+action_helper.c
+button_helper.c
helper.c
+icon_helper.c
+arg_helper.c
md5.c
jhash.c
configfile.c
@@ -10,6 +14,7 @@ rgb_hsv.c
highscore.c
simple_viewer.c
display_text.c
+printcell_helper.c
strncpy.c
stdio_compat.c
@@ -64,3 +69,9 @@ kbd_helper.c
#ifdef HAVE_TOUCHSCREEN
pluginlib_touchscreen.c
#endif
+
+id3.c
+
+#ifdef HAVE_TAGCACHE
+mul_id3.c
+#endif
diff --git a/apps/plugins/lib/action_helper.c b/apps/plugins/lib/action_helper.c
new file mode 100644
index 0000000000..906051c1ea
--- /dev/null
+++ b/apps/plugins/lib/action_helper.c
@@ -0,0 +1 @@
+/*DUMMY_FILE_DONT_CHANGEME*/
diff --git a/apps/plugins/lib/action_helper.h b/apps/plugins/lib/action_helper.h
new file mode 100644
index 0000000000..53f5c840f8
--- /dev/null
+++ b/apps/plugins/lib/action_helper.h
@@ -0,0 +1,34 @@
+/***************************************************************************
+* __________ __ ___.
+* Open \______ \ ____ ____ | | _\_ |__ _______ ___
+* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+* \/ \/ \/ \/ \/
+* $Id$
+*
+* Copyright (C) 2021 William Wilgus
+*
+*
+* This program is free software; you can redistribute it and/or
+* modify it under the terms of the GNU General Public License
+* as published by the Free Software Foundation; either version 2
+* of the License, or (at your option) any later version.
+*
+* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+* KIND, either express or implied.
+*
+****************************************************************************/
+/* action_helper provides a way to turn numeric action/context into strings
+* the file action_helper.c is generated at compile time
+* ACTION_ and CONTEXT_ are stripped from the strings and replaced when
+* action_name and context_name are called,
+* NOTE: both share the same static buffer sized as the largest string possible
+*/
+#ifndef _ACTION_HELPER_H_
+#define _ACTION_HELPER_H_
+extern const size_t action_helper_maxbuffer;
+char* action_name(int action);
+char* context_name(int context);
+
+#endif /* _ACTION_HELPER_H_ */
diff --git a/apps/plugins/lib/action_helper.pl b/apps/plugins/lib/action_helper.pl
new file mode 100755
index 0000000000..742419e23b
--- /dev/null
+++ b/apps/plugins/lib/action_helper.pl
@@ -0,0 +1,211 @@
+#!/usr/bin/env perl
+############################################################################
+# __________ __ ___.
+# Open \______ \ ____ ____ | | _\_ |__ _______ ___
+# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+# \/ \/ \/ \/ \/
+# $action_helper$
+#
+# Copyright (C) 2021 William Wilgus
+#
+# All files in this archive are subject to the GNU General Public License.
+# See the file COPYING in the source tree root for full license agreement.
+#
+# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+# KIND, either express or implied.
+#
+############################################################################
+#expects -E source input on STDIN
+use strict;
+use warnings;
+
+my @actions = ();
+my @contexts = ();
+my @action_offset = ();
+my @context_offset = ();
+my $action_ct = 0;
+my $context_ct = 0;
+my $len_max_action = 0;
+my $len_max_context = 0;
+my $len_min_action = -1;
+my $len_min_context = -1;
+while(my $line = <STDIN>)
+{
+ chomp($line);
+ if($line =~ /^\s*(ACTION_[^\s]+)(\s*=.*)?,\s*$/)
+ {
+ $actions[$action_ct] = $1;
+ $action_ct++;
+ }
+ elsif($line =~ /^\s*(LAST_ACTION_PLACEHOLDER)(\s*=.*)?,\s*$/)
+ { #special case don't save actual name
+ $actions[$action_ct] = "";
+ $action_ct++;
+ }
+ elsif($line =~ /^\s*(PLA_[^\s]+)(\s*=.*)?,\s*$/)
+ {
+ $actions[$action_ct] = $1;
+ $action_ct++;
+ }
+ elsif($line =~ /^\s*(CONTEXT_[^\s]+)(\s*=.*)?,\s*$/)
+ {
+ $contexts[$context_ct] = $1;
+ $context_ct++;
+ }
+}
+
+print <<EOF
+/* Don't change this file! */
+/* It is automatically generated of action.h */
+#include "plugin.h"
+#include "action_helper.h"
+EOF
+;
+#dump actions
+my $offset = 0;
+print "static const char action_names[]= \n";
+for(my $i = 0; $i < $action_ct; $i++){
+ my $act = $actions[$i];
+ $act =~ s/ACTION_USB_HID_/%s/ig; # strip the common part
+ $act =~ s/ACTION_/%s/ig; # strip the common part
+ my $actlen = length($act);
+ if ($actlen < $len_min_action or $len_min_action == -1){
+ $len_min_action = $actlen;
+ }
+ if ($actions[$i] ne $act){
+ printf "/*%s*/\"%s\\0\"\n", substr($actions[$i], 0, -($actlen - 2)), $act;
+ } else {
+ print "\"$act\\0\" \n";
+ }
+ my $slen = length($actions[$i]) + 1; #NULL terminator
+ if ($slen > $len_max_action) { $len_max_action = $slen; }
+ push(@action_offset, {'name' => $actions[$i], 'offset' => $offset});
+ $offset += length($act) + 1; # NULL terminator
+}
+printf "\"\";/* %d + \\0 */\n\n", $offset;
+@actions = ();
+
+#dump contexts
+$offset = 0;
+print "static const char context_names[]= \n";
+for(my $i = 0; $i < $context_ct; $i++){
+ my $ctx = $contexts[$i];
+ $ctx =~ s/CONTEXT_/%s/ig; # strip the common part
+ my $ctxlen = length($ctx);
+
+ if ($ctxlen < 5){
+ $ctx = $contexts[$i];
+ $ctxlen = length($ctx);
+ }
+
+ if ($ctxlen < $len_min_context or $len_min_context == -1){
+ $len_min_context = $ctxlen;
+ }
+ if ($contexts[$i] ne $ctx){
+ printf "/*%s*/\"%s\\0\"\n", substr($contexts[$i], 0, -($ctxlen - 2)), $ctx;
+ } else {
+ print "\"$ctx\\0\" \n";
+ }
+ my $slen = length($contexts[$i]) + 1; # NULL terminator
+ if ($slen > $len_max_context) { $len_max_context = $slen; }
+ push(@context_offset, {'name' => $contexts[$i], 'offset' => $offset});
+ $offset += length($ctx) + 1; # NULL terminator
+}
+printf "\"\";/* %d + \\0 */\n\n", $offset;
+@contexts = ();
+
+printf "#define ACTION_CT %d\n", $action_ct;
+print "static const uint16_t action_offsets[ACTION_CT] = {\n";
+foreach my $define (@action_offset)
+{
+ printf("%d, /*%s*/\n", @$define{'offset'}, @$define{'name'});
+}
+print "};\n\n";
+@action_offset = ();
+
+printf "#define CONTEXT_CT %d\n", $context_ct;
+print "#if 0 /* context_names is small enough to walk the string instead */\n";
+print "static const uint16_t context_offsets[CONTEXT_CT] = {\n";
+foreach my $define (@context_offset)
+{
+ printf("%d, /*%s*/\n", @$define{'offset'}, @$define{'name'});
+}
+print "};\n#endif\n\n";
+@context_offset = ();
+
+printf "#define ACTIONBUFSZ %d\n", $len_max_action;
+printf "#define CONTEXTBUFSZ %d\n\n", $len_max_context;
+
+if ($len_max_action > $len_max_context)
+{
+ print "const size_t action_helper_maxbuffer = ACTIONBUFSZ;\n";
+ print "static char name_buf[ACTIONBUFSZ];\n";
+}
+else
+{
+ print "const size_t action_helper_maxbuffer = CONTEXTBUFSZ;\n";
+ print "static char name_buf[CONTEXTBUFSZ];\n";
+}
+print <<EOF
+
+char* action_name(int action)
+{
+ if (action >= 0 && action < ACTION_CT)
+ {
+ uint16_t offset = action_offsets[action];
+ const char *act = &action_names[offset];
+ if (action < ACTION_USB_HID_FIRST)
+ rb->snprintf(name_buf, ACTIONBUFSZ, act, "ACTION_");
+ else
+ rb->snprintf(name_buf, ACTIONBUFSZ, act, "ACTION_USB_HID_");
+ }
+ else
+ rb->snprintf(name_buf, ACTIONBUFSZ, "ACTION_UNKNOWN");
+ return name_buf;
+}
+
+/* walk string increment offset for each NULL if desired offset found, return */
+static const char *context_getoffset(int offset)
+{
+ const char *names = context_names;
+ const size_t len = sizeof(context_names) - 1;
+ int current = 0;
+ if (offset > 0)
+ {
+ const char *pos = names;
+ const char *end = names + len;
+ while (pos < end)
+ {
+ if (*pos++ == '\\0')
+ {
+ current++;
+ if (offset == current)
+ return pos;
+ pos += $len_min_context; /* each string is at least this long */
+ }
+ }
+ }
+ return names;
+}
+
+char* context_name(int context)
+{
+ const char *ctx;
+ if (context >= 0 && context < CONTEXT_CT)
+ {
+#if 0
+ uint16_t offset = context_offsets[context];
+ ctx = &context_names[offset];
+#else
+ ctx = context_getoffset(context);
+#endif
+ }
+ else
+ ctx = "%sUNKNOWN";
+ rb->snprintf(name_buf, CONTEXTBUFSZ, ctx, "CONTEXT_");
+ return name_buf;
+}
+EOF
+;
diff --git a/apps/plugins/lib/arg_helper.c b/apps/plugins/lib/arg_helper.c
new file mode 100644
index 0000000000..3ea5ba714d
--- /dev/null
+++ b/apps/plugins/lib/arg_helper.c
@@ -0,0 +1,312 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 William Wilgus
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "plugin.h"
+#include "arg_helper.h"
+
+#ifndef logf
+#define logf(...) {}
+#endif
+
+#define SWCHAR '-'
+#define DECSEPCHAR '.'
+#ifdef PLUGIN
+ #define strchr rb->strchr
+#endif
+int string_parse(const char **parameter, char* buf, size_t buf_sz)
+{
+/* fills buf with a string upto buf_sz, null terminates the buffer
+ * strings break on WS by default but can be enclosed in single or double quotes
+ * opening and closing quotes will not be included in the buffer but will be counted
+ * use alternating quotes if you really want them included '"text"' or "'text'"
+ * failure to close the string will result in eating all remaining args till \0
+ * If buffer full remaining chars are discarded till stopchar or \0 is reached */
+
+ char stopchar = ' ';
+ char stopchars[] = "\'\"";
+ int skipped = 0;
+ int found = 0;
+ const char* start = *parameter;
+
+ if (strchr(stopchars, *start))
+ {
+ logf("stop char %c\n", *start);
+ stopchar = *start;
+ skipped++;
+ start++;
+ }
+ while (*start && *start != stopchar)
+ {
+ if (buf_sz > 1)
+ {
+ *buf++ = *start;
+ buf_sz--;
+ }
+ found++;
+ start++;
+ }
+ if (*start == stopchar && skipped)
+ {
+ start++;
+ skipped++;
+ }
+
+ *buf = '\0';
+
+ if (found > 0)
+ *parameter = start;
+ else
+ skipped = 0;
+
+ return found + skipped;
+}
+
+int char_parse(const char **parameter, char* character)
+{
+/* passes *character a single character eats remaining non-WS characters */
+ char buf[2];
+ int ret = string_parse(parameter, buf, sizeof(buf));
+ if (ret && character)
+ *character = buf[0];
+ return ret;
+
+}
+
+int bool_parse(const char **parameter, bool *choice)
+{
+/* determine true false using the first character the rest are skipped/ignored */
+ int found = 0;
+ const char tf_val[]="fn0ty1";/* false chars on left f/t should be balanced fffttt */
+ const char* start = *parameter;
+
+
+ char c = tolower(*start);
+ const char *tfval = strchr(tf_val, c);
+ while(isalnum(*++start)) {;}
+
+ if (tfval)
+ {
+ found = start - (*parameter);
+ *parameter = start;
+ }
+
+ if (choice)
+ *choice = (tfval - tf_val) > (signed int) (sizeof(tf_val) / 2) - 1;
+
+ return found;
+}
+
+int longnum_parse(const char **parameter, long *number, long *decimal)
+{
+/* passes number and or decimal portion of number base 10 only..
+ fractional portion is scaled by ARGPARSE_FRAC_DEC_MULTIPLIER
+ Example (if ARGPARSE_FRAC_DEC_MULTIPLIER = 10 000)
+ meaning .0009 returns 9 , 9 / 10000 = .0009
+ .009 returns 90
+ .099 returns 990
+ .09 returns 900
+ .9 returns 9000
+ .9999 returns 9999
+*/
+
+ long num = 0;
+ long dec = 0;
+ int found = 0;
+ int neg = 0;
+ int digits = 0;
+ //logf ("n: %s\n", *parameter);
+ const char *start = *parameter;
+
+ if (*start == '-')
+ {
+ neg = 1;
+ start++;
+ }
+ while (isdigit(*start))
+ {
+ found++;
+ num = num *10 + *start - '0';
+ start++;
+ }
+
+ if (*start == DECSEPCHAR)
+ {
+ start++;
+ while(*start == '0')
+ {
+ digits++;
+ start++;
+ }
+ while (isdigit(*start))
+ {
+ dec = dec *10 + *start - '0';
+ digits++;
+ start++;
+ }
+ if (decimal && digits <= ARGPARSE_MAX_FRAC_DIGITS)
+ {
+ if(digits < ARGPARSE_MAX_FRAC_DIGITS)
+ {
+ digits = ARGPARSE_MAX_FRAC_DIGITS - digits;
+ while (digits--)
+ dec *= 10;
+ }
+ }
+ else
+ dec = -1; /* error */
+ }
+
+ if (found > 0)
+ {
+ found = start - (*parameter);
+ *parameter = start;
+ }
+
+ if(number)
+ *number = neg ? -num : num;
+
+ if (decimal)
+ *decimal = dec;
+
+ return found;
+}
+
+int num_parse(const char **parameter, int *number, int *decimal)
+{
+ long num, dec;
+ int ret = longnum_parse(parameter, &num, &dec);
+ if(number)
+ *number = num;
+ if (decimal)
+ *decimal = dec;
+ return ret;
+}
+
+/*
+*argparse(const char *parameter, int parameter_len,
+* int (*arg_callback)(char argchar, const char **parameter))
+* parameter : constant char string of arguments
+* parameter_len : may be set to -1 if your parameter string is NULL (\0) terminated
+* arg_callback : function gets called for each SWCHAR found in the parameter string
+* Note: WS at beginning is stripped, **parameter starts at the first NON WS char
+* return 0 for arg_callback to quit parsing immediately
+*/
+void argparse(const char *parameter, int parameter_len, int (*arg_callback)(char argchar, const char **parameter))
+{
+ bool lastchr;
+ char argchar;
+ const char *start = parameter;
+ while (parameter_len < 0 || (parameter - start) < parameter_len)
+ {
+ switch (*parameter++)
+ {
+ case SWCHAR:
+ {
+ if ((*parameter) == '\0')
+ return;
+ logf ("%s\n",parameter);
+ argchar = *parameter;
+ lastchr = (*(parameter + 1) == '\0');
+ while (*++parameter || lastchr)
+ {
+ lastchr = false;
+ if (isspace(*parameter))
+ continue; /* eat spaces at beginning */
+ if (!arg_callback(argchar, &parameter))
+ return;
+ break;
+ }
+ break;
+ }
+ case '\0':
+ {
+ if (parameter_len <= 0)
+ return;
+ }
+ }
+ }
+}
+
+/* EXAMPLE USAGE
+argparse("-n 42 -N 9.9 -n -78.9009 -f -P /rockbox/path/f -s 'Yestest' -B false -B 0 -B true -b n -by -b 1-c ops -c s -k", -1, &arg_callback);
+
+int arg_callback(char argchar, const char **parameter)
+{
+ int ret;
+ int num, dec;
+ char c;
+ char buf[32];
+ bool bret;
+ logf ("Arg: %c\n", argchar);
+ switch (tolower(argchar))
+ {
+ case 'k' :
+ logf("Option K!");
+ break;
+ case 'c' :
+ ret = char_parse(parameter, &c);
+ if (ret)
+ {
+ logf ("Val: %c\n", c);
+ logf("ate %d chars\n", ret);
+ }
+ break;
+
+ case 'n' :
+ ret = num_parse(parameter, &num, &dec);
+ if (ret)
+ {
+ logf ("Val: %d.%d\n", num, dec);
+ logf("ate %d chars\n", ret);
+ }
+ break;
+ case 's' :
+ ret = string_parse(parameter, buf, sizeof(buf));
+ if (ret)
+ {
+ logf ("Val: %s\n", buf);
+ logf("ate %d chars\n", ret);
+ }
+ break;
+ case 'p' :
+ ret = string_parse(parameter, buf, sizeof(buf));
+ if (ret)
+ {
+ logf ("Path: %s\n", buf);
+ logf("ate %d chars\n", ret);
+ }
+ break;
+ case 'b' :
+ ret = bool_parse(parameter, &bret);
+ if (ret)
+ {
+ logf ("Val: %s\n", bret ? "true" : "false");
+ logf("ate %d chars\n", ret);
+ }
+ break;
+ default :
+ logf ("Unknown switch '%c'\n",argchar);
+ //return 0;
+ }
+ return 1;
+}
+*/
+
diff --git a/apps/plugins/lib/arg_helper.h b/apps/plugins/lib/arg_helper.h
new file mode 100644
index 0000000000..2cf94ba1dd
--- /dev/null
+++ b/apps/plugins/lib/arg_helper.h
@@ -0,0 +1,60 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 William Wilgus
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#ifndef _LIB_ARG_HELPER_H_
+#define _LIB_ARG_HELPER_H_
+
+#include "plugin.h"
+
+#define ARGPARSE_MAX_FRAC_DIGITS 9 /* Uses 30 bits max (0.999999999) */
+
+#define ARGP_EXP(a, b) (a ##E## b)
+#define ARGP_FRAC_DEC_MULTIPLIER(n) ARGP_EXP(1,n) /*1x10^n*/
+#define ARGPARSE_FRAC_DEC_MULTIPLIER \
+ (long)ARGP_FRAC_DEC_MULTIPLIER(ARGPARSE_MAX_FRAC_DIGITS)
+
+/* fills buf with a string upto buf_sz, null terminates the buffer
+ * strings break on WS by default but can be enclosed in single or double quotes
+ * opening and closing quotes will not be included in the buffer but will be counted
+ * use alternating quotes if you really want them included '"text"' or "'text'"
+ * failure to close the string will result in eating all remaining args till \0
+ * If buffer full remaining chars are discarded till stopchar or \0 is reached */
+int string_parse(const char **parameter, char* buf, size_t buf_sz);
+/* passes *character a single character eats remaining non-WS characters */
+int char_parse(const char **parameter, char* character);
+/* determine true false using the first character the rest are skipped/ignored */
+int bool_parse(const char **parameter, bool *choice);
+/* passes number and or decimal portion of number base 10 only.. */
+int longnum_parse(const char **parameter, long *number, long *decimal);
+int num_parse(const char **parameter, int *number, int *decimal);
+
+/*
+*argparse(const char *parameter, int parameter_len,
+* int (*arg_callback)(char argchar, const char **parameter))
+* parameter : constant char string of arguments
+* parameter_len : may be set to -1 if your parameter string is NULL (\0) terminated
+* arg_callback : function gets called for each SWCHAR found in the parameter string
+* Note: WS at beginning is stripped, **parameter starts at the first NON WS char
+* return 0 for arg_callback to quit parsing immediately
+*/
+void argparse(const char *parameter, int parameter_len,
+ int (*arg_callback)(char argchar, const char **parameter));
+
+#endif /* _LIB_ARG_HELPER_H_ */
diff --git a/apps/plugins/lib/bmp_smooth_scale.c b/apps/plugins/lib/bmp_smooth_scale.c
index c5f258cdbf..378ff96448 100644
--- a/apps/plugins/lib/bmp_smooth_scale.c
+++ b/apps/plugins/lib/bmp_smooth_scale.c
@@ -78,7 +78,7 @@ void smooth_resize_bitmap(struct bitmap *src_bmp, struct bitmap *dest_bmp)
fb_data *sptr, *dptr;
int x, y, end;
int val_y = 0, val_x;
-#if defined(LCD_STRIDEFORMAT) && LCD_STRIDEFORMAT == VERTICAL_STRIDE
+#if LCD_STRIDEFORMAT == VERTICAL_STRIDE
const int sw = src_bmp->height;
const int sh = src_bmp->width;
const int dw = dest_bmp->height;
diff --git a/apps/plugins/lib/button_helper.c b/apps/plugins/lib/button_helper.c
new file mode 100644
index 0000000000..906051c1ea
--- /dev/null
+++ b/apps/plugins/lib/button_helper.c
@@ -0,0 +1 @@
+/*DUMMY_FILE_DONT_CHANGEME*/
diff --git a/apps/plugins/lib/button_helper.h b/apps/plugins/lib/button_helper.h
new file mode 100644
index 0000000000..4087ba898a
--- /dev/null
+++ b/apps/plugins/lib/button_helper.h
@@ -0,0 +1,40 @@
+/***************************************************************************
+* __________ __ ___.
+* Open \______ \ ____ ____ | | _\_ |__ _______ ___
+* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+* \/ \/ \/ \/ \/
+* $Id$
+*
+* Copyright (C) 2021 William Wilgus
+*
+*
+* This program is free software; you can redistribute it and/or
+* modify it under the terms of the GNU General Public License
+* as published by the Free Software Foundation; either version 2
+* of the License, or (at your option) any later version.
+*
+* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+* KIND, either express or implied.
+*
+****************************************************************************/
+#ifndef _BUTTON_HELPER_H_
+#define _BUTTON_HELPER_H_
+struct available_button
+{
+ const char* name;
+ unsigned long value;
+};
+
+/* *available_buttons is holding a pointer to the first element of an array
+ * of struct available_button it is set up in such a way due to the file being
+ * generated at compile time you can still call it as such though
+* eg available_buttons[0] or available_buttons[available_button_count] (NULL SENTINEL, 0)*/
+
+extern const size_t button_helper_maxbuffer;
+extern const struct available_button * const available_buttons;
+extern const int available_button_count;
+
+int get_button_names(char *buf, size_t bufsz, unsigned long button);
+#endif /* _BUTTON_HELPER_H_ */
diff --git a/apps/plugins/lib/button_helper.pl b/apps/plugins/lib/button_helper.pl
new file mode 100755
index 0000000000..192df18d7f
--- /dev/null
+++ b/apps/plugins/lib/button_helper.pl
@@ -0,0 +1,103 @@
+#!/usr/bin/env perl
+############################################################################
+# __________ __ ___.
+# Open \______ \ ____ ____ | | _\_ |__ _______ ___
+# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+# \/ \/ \/ \/ \/
+# $Id$
+#
+# Copyright (C) 2009 by Maurus Cuelenaere
+# Copyright (C) 2021 by William Wilgus
+#
+# All files in this archive are subject to the GNU General Public License.
+# See the file COPYING in the source tree root for full license agreement.
+#
+# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+# KIND, either express or implied.
+#
+############################################################################
+#expects -dM -E source input on STDIN
+use strict;
+use warnings;
+my $svnrev = '$Revision$';
+my @buttons = ();
+my $count = 1; #null sentinel
+my $val;
+my $def;
+my $len_max_button = 0;
+while(my $line = <STDIN>)
+{
+ chomp($line);
+ if($line =~ /^#define (BUTTON_[^\s]+) (.+)$/)
+ {
+ $def = "{\"$1\", $2},\n";
+ my $slen = length($1) + 1; # NULL terminator
+ if ($slen > $len_max_button) { $len_max_button = $slen; }
+ $val = $2;
+ if($val =~ /^0/)
+ {
+ $val = oct($val)
+ }
+ else
+ {
+ $val = 0xFFFFFFFF; #only used for sorting
+ }
+ push(@buttons, {'name' => $1, 'value' => $val, 'def' => $def});
+ $count = $count + 1;
+ }
+}
+my @sorted = sort { @$a{'value'} <=> @$b{'value'} } @buttons;
+print <<EOF
+/* Don't change this file! */
+/* It is automatically generated of button.h */
+#include "plugin.h"
+#include "button.h"
+#include "button_helper.h"
+
+const size_t button_helper_maxbuffer = $len_max_button;
+
+static const struct available_button buttons[$count] = {
+EOF
+;
+$count--; # don't count the sentinel
+foreach my $button (@sorted)
+{
+ printf " %s", @$button{'def'};
+}
+
+print <<EOF
+ {"\\0", 0} /* sentinel */
+};
+const int available_button_count = $count;
+const struct available_button * const available_buttons = buttons;
+
+int get_button_names(char *buf, size_t bufsz, unsigned long button)
+{
+ int len = 0;
+ buf[0] = '\\0';
+ const struct available_button *btn = buttons;
+ while(btn->name[0] != '\\0')
+ {
+ if(btn->value == 0)
+ {
+ if (button == 0)
+ {
+ buf[0] = '\\0';
+ len = rb->strlcat(buf, btn->name, bufsz);
+ return len;
+ }
+ }
+ else if ((button & btn->value) == btn->value)
+ {
+ if (len > 0)
+ rb->strlcat(buf, " | ", bufsz);
+ len = rb->strlcat(buf, btn->name, bufsz);
+ }
+ btn++;
+ }
+ return len;
+}
+EOF
+;
diff --git a/apps/plugins/lib/helper.c b/apps/plugins/lib/helper.c
index f36c01b23e..92d9ec905e 100644
--- a/apps/plugins/lib/helper.c
+++ b/apps/plugins/lib/helper.c
@@ -22,6 +22,12 @@
#include "plugin.h"
#include "helper.h"
+int talk_val(long n, int unit, bool enqueue)
+{
+ #define NODECIMALS 0
+ return rb->talk_value_decimal(n, unit, NODECIMALS, enqueue);
+}
+
#ifdef HAVE_BACKLIGHT
/* Force the backlight on */
void backlight_force_on(void)
@@ -52,7 +58,12 @@ void backlight_use_settings(void)
backlight_timeout_plugged);
#endif /* CONFIG_CHARGING */
}
-#endif /* HAVE_BACKLIGHT */
+#else /* HAVE_BACKLIGHT */
+/* DUMMY FUNCTIONS */
+void backlight_force_on(void){}
+void backlight_ignore_timeout(void){}
+void backlight_use_settings(void){}
+#endif /* !HAVE_BACKLIGHT */
#ifdef HAVE_SW_POWEROFF
static bool original_sw_poweroff_state = true;
@@ -67,7 +78,11 @@ void sw_poweroff_restore(void)
{
rb->button_set_sw_poweroff_state(original_sw_poweroff_state);
}
-#endif
+#else /* HAVE_SW_POWEROFF */
+/* DUMMY FUNCTIONS */
+void sw_poweroff_disable(void){}
+void sw_poweroff_restore(void){}
+#endif /* !HAVE_SW_POWEROFF */
#ifdef HAVE_REMOTE_LCD
/* Force the backlight on */
@@ -100,7 +115,12 @@ void remote_backlight_use_settings(void)
remote_backlight_timeout_plugged);
#endif /* CONFIG_CHARGING */
}
-#endif /* HAVE_REMOTE_LCD */
+#else /* HAVE_REMOTE_LCD */
+/* DUMMY FUNCTIONS */
+void remote_backlight_force_on(void){}
+void remote_backlight_ignore_timeout(void){}
+void remote_backlight_use_settings(void){}
+#endif /* !HAVE_REMOTE_LCD */
#ifdef HAVE_BUTTON_LIGHT
/* Force the buttonlight on */
@@ -127,7 +147,13 @@ void buttonlight_use_settings(void)
{
rb->buttonlight_set_timeout(rb->global_settings->buttonlight_timeout);
}
-#endif /* HAVE_BUTTON_LIGHT */
+#else /* HAVE_BUTTON_LIGHT */
+/* DUMMY FUNCTIONS */
+void buttonlight_force_on(void){}
+void buttonlight_force_off(void){}
+void buttonlight_ignore_timeout(void){}
+void buttonlight_use_settings(void){}
+#endif /* !HAVE_BUTTON_LIGHT */
#ifdef HAVE_BACKLIGHT_BRIGHTNESS
void backlight_brightness_set(int brightness)
@@ -139,7 +165,15 @@ void backlight_brightness_use_setting(void)
{
rb->backlight_set_brightness(rb->global_settings->brightness);
}
-#endif /* HAVE_BACKLIGHT_BRIGHTNESS */
+#else /* HAVE_BACKLIGHT_BRIGHTNESS */
+/* DUMMY FUNCTIONS */
+void backlight_brightness_set(int brightness)
+{
+ (void)brightness;
+}
+void backlight_brightness_use_setting(void){}
+
+#endif /* !HAVE_BACKLIGHT_BRIGHTNESS */
#ifdef HAVE_BUTTONLIGHT_BRIGHTNESS
void buttonlight_brightness_set(int brightness)
@@ -151,4 +185,12 @@ void buttonlight_brightness_use_setting(void)
{
rb->buttonlight_set_brightness(rb->global_settings->buttonlight_brightness);
}
-#endif /* HAVE_BUTTONLIGHT_BRIGHTNESS */
+#else /* HAVE_BUTTONLIGHT_BRIGHTNESS */
+/* DUMMY FUNCTIONS */
+void buttonlight_brightness_set(int brightness)
+{
+ (void)brightness;
+}
+
+void buttonlight_brightness_use_setting(void){}
+#endif /* !HAVE_BUTTONLIGHT_BRIGHTNESS */
diff --git a/apps/plugins/lib/helper.h b/apps/plugins/lib/helper.h
index f2e9187a96..6aee4dc581 100644
--- a/apps/plugins/lib/helper.h
+++ b/apps/plugins/lib/helper.h
@@ -23,6 +23,18 @@
#include "plugin.h"
+#ifndef MAX_BRIGHTNESS_SETTING
+#define MAX_BRIGHTNESS_SETTING 0
+#endif
+#ifndef MIN_BRIGHTNESS_SETTING
+#define MIN_BRIGHTNESS_SETTING 0
+#endif
+#ifndef DEFAULT_BRIGHTNESS_SETTING
+#define DEFAULT_BRIGHTNESS_SETTING 0
+#endif
+
+int talk_val(long n, int unit, bool enqueue);
+
/**
* Backlight on/off operations
*/
@@ -30,39 +42,29 @@ void backlight_force_on(void);
void backlight_ignore_timeout(void);
void backlight_use_settings(void);
-#ifdef HAVE_SW_POWEROFF
/**
* Disable and restore software poweroff (i.e. holding PLAY on iPods).
* Only call _restore() if _disable() was called earlier!
*/
void sw_poweroff_disable(void);
void sw_poweroff_restore(void);
-#endif
-#ifdef HAVE_REMOTE_LCD
void remote_backlight_force_on(void);
void remote_backlight_ignore_timeout(void);
void remote_backlight_use_settings(void);
-#endif
-#ifdef HAVE_BUTTON_LIGHT
void buttonlight_force_on(void);
void buttonlight_force_off(void);
void buttonlight_ignore_timeout(void);
void buttonlight_use_settings(void);
-#endif
/**
* Backlight brightness adjustment settings
*/
-#ifdef HAVE_BACKLIGHT_BRIGHTNESS
void backlight_brightness_set(int brightness);
void backlight_brightness_use_setting(void);
-#endif
-#ifdef HAVE_BUTTONLIGHT_BRIGHTNESS
void buttonlight_brightness_set(int brightness);
void buttonlight_brightness_use_setting(void);
-#endif /* HAVE_BUTTONLIGHT_BRIGHTNESS */
#endif /* _LIB_HELPER_H_ */
diff --git a/apps/plugins/lib/highscore.c b/apps/plugins/lib/highscore.c
index 3aae955bfc..04faf27891 100644
--- a/apps/plugins/lib/highscore.c
+++ b/apps/plugins/lib/highscore.c
@@ -124,11 +124,16 @@ void highscore_show(int position, struct highscore *scores, int num_scores,
bool show_level)
{
int i, w, h;
-#ifdef HAVE_LCD_COLOR
+#if defined(HAVE_LCD_COLOR) || LCD_DEPTH >= 2
unsigned bgcolor = rb->lcd_get_background();
unsigned fgcolor = rb->lcd_get_foreground();
+#ifdef HAVE_LCD_COLOR
rb->lcd_set_background(LCD_BLACK);
rb->lcd_set_foreground(LCD_WHITE);
+#else
+ rb->lcd_set_background(LCD_WHITE);
+ rb->lcd_set_foreground(LCD_BLACK);
+#endif
#endif
rb->lcd_clear_display();
@@ -173,7 +178,7 @@ void highscore_show(int position, struct highscore *scores, int num_scores,
rb->button_clear_queue();
rb->button_get(true);
rb->lcd_setfont(FONT_SYSFIXED);
-#ifdef HAVE_LCD_COLOR
+#if defined(HAVE_LCD_COLOR) || LCD_DEPTH >= 2
rb->lcd_set_background(bgcolor);
rb->lcd_set_foreground(fgcolor);
#endif
diff --git a/apps/plugins/lib/icon_helper.c b/apps/plugins/lib/icon_helper.c
new file mode 100644
index 0000000000..857bddb128
--- /dev/null
+++ b/apps/plugins/lib/icon_helper.c
@@ -0,0 +1,48 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 William Wilgus
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "plugin.h"
+#include "icon_helper.h"
+
+const unsigned char* cbmp_get_icon(unsigned int cbmp_fmt, unsigned int index, int *width, int *height)
+{
+ const unsigned char* bmp = NULL;
+ while (cbmp_fmt < CBMP_BitmapFormatLast)
+ {
+ const struct cbmp_bitmap_info_entry *cbmp = &rb->core_bitmaps[cbmp_fmt];
+ if (index > cbmp->count)
+ break;
+ int w = cbmp->width;
+ int h = cbmp->height;
+ /* ((height/CHAR_BIT) Should always be 1 thus far */
+
+ off_t offset = (((unsigned)h/CHAR_BIT) * (index * w));
+ bmp = cbmp->pbmp + offset;
+
+ if (width)
+ *width = w;
+ if (height)
+ *height = h;
+ break;
+ }
+
+ return bmp;
+}
diff --git a/apps/plugins/lib/icon_helper.h b/apps/plugins/lib/icon_helper.h
new file mode 100644
index 0000000000..e30a607a3f
--- /dev/null
+++ b/apps/plugins/lib/icon_helper.h
@@ -0,0 +1,26 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 William Wilgus
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#ifndef _LIB_ICON_HELPER_H_
+#define _LIB_ICON_HELPER_H_
+
+#include "plugin.h"
+const unsigned char* cbmp_get_icon(unsigned int cbmp_fmt, unsigned int index, int *width, int *height);
+#endif /* _LIB_ICON_HELPER_H_ */
diff --git a/apps/plugins/lib/id3.c b/apps/plugins/lib/id3.c
new file mode 100644
index 0000000000..b0202b1d9c
--- /dev/null
+++ b/apps/plugins/lib/id3.c
@@ -0,0 +1,39 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2023 Christian Soffke
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#include "plugin.h"
+
+/* Fills mp3entry with metadata retrieved from RAM, if possible, or by reading from
+ * the file directly. Note that the tagcache only stores a subset of metadata and
+ * will thus not return certain properties of the file, such as frequency, size, or
+ * codec.
+ */
+bool retrieve_id3(struct mp3entry *id3, const char* file)
+{
+#if defined (HAVE_TAGCACHE) && defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE)
+ if (rb->tagcache_fill_tags(id3, file))
+ {
+ rb->strlcpy(id3->path, file, sizeof(id3->path));
+ return true;
+ }
+#endif
+
+ return !rb->mp3info(id3, file);
+}
diff --git a/apps/scrobbler.h b/apps/plugins/lib/id3.h
index a3d1b361df..6ae1688798 100644
--- a/apps/scrobbler.h
+++ b/apps/plugins/lib/id3.h
@@ -7,7 +7,7 @@
* \/ \/ \/ \/ \/
* $Id$
*
- * Copyright (C) 2006 Robert Keevil
+ * Copyright (C) 2023 Christian Soffke
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -18,12 +18,9 @@
* KIND, either express or implied.
*
****************************************************************************/
+#ifndef ID3_H
+#define ID3_H
-#ifndef __SCROBBLER_H__
-#define __SCROBBLER_H__
+bool retrieve_id3(struct mp3entry *id3, const char* file);
-int scrobbler_init(void);
-void scrobbler_shutdown(bool poweroff);
-bool scrobbler_is_enabled(void);
-
-#endif /* __SCROBBLER_H__ */
+#endif /* ID3_H */
diff --git a/apps/plugins/lib/kbd_helper.c b/apps/plugins/lib/kbd_helper.c
index e3a844a993..f99282575d 100644
--- a/apps/plugins/lib/kbd_helper.c
+++ b/apps/plugins/lib/kbd_helper.c
@@ -34,11 +34,12 @@
* success returns size of buffer used
* failure returns 0
*/
-int kbd_create_layout(char *layout, unsigned short *buf, int bufsz)
+int kbd_create_layout(const char *layout, unsigned short *buf, int bufsz)
{
unsigned short *pbuf;
const unsigned char *p = layout;
int len = 0;
+ int total_len = 0;
pbuf = buf;
while (*p && (pbuf - buf + (ptrdiff_t) sizeof(unsigned short)) < bufsz)
{
@@ -47,6 +48,7 @@ int kbd_create_layout(char *layout, unsigned short *buf, int bufsz)
{
*pbuf = len;
pbuf += len+1;
+ total_len += len + 1;
len = 0;
}
else
@@ -57,7 +59,9 @@ int kbd_create_layout(char *layout, unsigned short *buf, int bufsz)
{
*pbuf = len;
pbuf[len+1] = 0xFEFF; /* mark end of characters */
- return len + 1;
+ total_len += len + 1;
+ return total_len * sizeof(unsigned short);
}
+
return 0;
}
diff --git a/apps/plugins/lib/kbd_helper.h b/apps/plugins/lib/kbd_helper.h
index 90443cbf3f..ee2ce7551c 100644
--- a/apps/plugins/lib/kbd_helper.h
+++ b/apps/plugins/lib/kbd_helper.h
@@ -22,6 +22,6 @@
#define KBD_HELPER_H
/* create a custom keyboard layout for kbd_input */
-int kbd_create_layout(char *layout, unsigned short *buf, int bufsz);
+int kbd_create_layout(const char *layout, unsigned short *buf, int bufsz);
#endif /* KBD_HELPER_H */
diff --git a/apps/plugins/lib/keymaps.h b/apps/plugins/lib/keymaps.h
index b660d4d85e..2cbca9e5ad 100644
--- a/apps/plugins/lib/keymaps.h
+++ b/apps/plugins/lib/keymaps.h
@@ -255,6 +255,15 @@
#define BTN_FIRE BUTTON_SELECT
#define BTN_PAUSE BUTTON_POWER
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+#define BTN_FIRE BUTTON_CENTER
+#define BTN_PAUSE BUTTON_POWER
+#define BTN_HAVE_DIAGONAL
+#define BTN_DOWN_LEFT BUTTON_BOTTOMLEFT
+#define BTN_DOWN_RIGHT BUTTON_BOTTOMRIGHT
+#define BTN_UP_LEFT BUTTON_TOPLEFT
+#define BTN_UP_RIGHT BUTTON_TOPRIGHT
+
#else
#error Unsupported keypad
#endif
@@ -272,7 +281,8 @@
#elif (CONFIG_KEYPAD != COWON_D2_PAD) && \
(CONFIG_KEYPAD != DX50_PAD) && \
(CONFIG_KEYPAD != ONDAVX777_PAD) && \
- (CONFIG_KEYPAD != CREATIVE_ZENXFI2_PAD)
+ (CONFIG_KEYPAD != CREATIVE_ZENXFI2_PAD) && \
+ (CONFIG_KEYPAD != SHANLING_Q1_PAD)
#define BTN_FIRE BUTTON_BOTTOMLEFT
#define BTN_PAUSE BUTTON_TOPLEFT
#endif
diff --git a/apps/plugins/lib/mul_id3.c b/apps/plugins/lib/mul_id3.c
new file mode 100644
index 0000000000..edf44f7282
--- /dev/null
+++ b/apps/plugins/lib/mul_id3.c
@@ -0,0 +1,174 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2023 Christian Soffke
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#include "plugin.h"
+#include "mul_id3.h"
+
+struct multiple_tracks_id3 {
+ unsigned long long filesize;
+ unsigned long long length;
+ unsigned long frequency;
+ unsigned int artist_hash;
+ unsigned int composer_hash;
+ unsigned int albumartist_hash;
+ unsigned int grouping_hash;
+ unsigned int comment_hash;
+ unsigned int album_hash;
+ unsigned int genre_hash;
+ unsigned int codectype;
+ unsigned int bitrate;
+ int year;
+ bool vbr;
+};
+
+static struct multiple_tracks_id3 mul_id3;
+
+
+/* Calculate modified FNV hash of string
+ * has good avalanche behaviour and uniform distribution
+ * see http://home.comcast.net/~bretm/hash/ */
+static unsigned int mfnv(char *str)
+{
+ const unsigned int p = 16777619;
+ unsigned int hash = 0x811C9DC5; // 2166136261;
+
+ if (!str)
+ return 0;
+
+ while(*str)
+ hash = (hash ^ *str++) * p;
+ hash += hash << 13;
+ hash ^= hash >> 7;
+ hash += hash << 3;
+ hash ^= hash >> 17;
+ hash += hash << 5;
+ return hash;
+}
+
+static void init_mul_id3(void)
+{
+ mul_id3.artist_hash = 0;
+ mul_id3.album_hash = 0;
+ mul_id3.genre_hash = 0;
+ mul_id3.composer_hash = 0;
+ mul_id3.albumartist_hash = 0;
+ mul_id3.grouping_hash = 0;
+ mul_id3.comment_hash = 0;
+ mul_id3.codectype = 0;
+ mul_id3.vbr = false;
+ mul_id3.bitrate = 0;
+ mul_id3.frequency = 0;
+ mul_id3.length = 0;
+ mul_id3.filesize = 0;
+ mul_id3.year = 0;
+}
+
+void collect_id3(struct mp3entry *id3, bool is_first_track)
+{
+ if (is_first_track)
+ {
+ init_mul_id3();
+ mul_id3.artist_hash = mfnv(id3->artist);
+ mul_id3.album_hash = mfnv(id3->album);
+ mul_id3.genre_hash = mfnv(id3->genre_string);
+ mul_id3.composer_hash = mfnv(id3->composer);
+ mul_id3.albumartist_hash = mfnv(id3->albumartist);
+ mul_id3.grouping_hash = mfnv(id3->grouping);
+ mul_id3.comment_hash = mfnv(id3->comment);
+ mul_id3.codectype = id3->codectype;
+ mul_id3.vbr = id3->vbr;
+ mul_id3.bitrate = id3->bitrate;
+ mul_id3.frequency = id3->frequency;
+ mul_id3.year = id3->year;
+ }
+ else
+ {
+ if (mul_id3.artist_hash && (mfnv(id3->artist) != mul_id3.artist_hash))
+ mul_id3.artist_hash = 0;
+ if (mul_id3.album_hash && (mfnv(id3->album) != mul_id3.album_hash))
+ mul_id3.album_hash = 0;
+ if (mul_id3.genre_hash && (mfnv(id3->genre_string) != mul_id3.genre_hash))
+ mul_id3.genre_hash = 0;
+ if (mul_id3.composer_hash && (mfnv(id3->composer) != mul_id3.composer_hash))
+ mul_id3.composer_hash = 0;
+ if (mul_id3.albumartist_hash && (mfnv(id3->albumartist) !=
+ mul_id3.albumartist_hash))
+ mul_id3.albumartist_hash = 0;
+ if (mul_id3.grouping_hash && (mfnv(id3->grouping) != mul_id3.grouping_hash))
+ mul_id3.grouping_hash = 0;
+ if (mul_id3.comment_hash && (mfnv(id3->comment) != mul_id3.comment_hash))
+ mul_id3.comment_hash = 0;
+
+ if (mul_id3.codectype && (id3->codectype != mul_id3.codectype))
+ mul_id3.codectype = AFMT_UNKNOWN;
+ if (mul_id3.bitrate && (id3->bitrate != mul_id3.bitrate ||
+ id3->vbr != mul_id3.vbr))
+ mul_id3.bitrate = 0;
+ if (mul_id3.frequency && (id3->frequency != mul_id3.frequency))
+ mul_id3.frequency = 0;
+ if (mul_id3.year && (id3->year != mul_id3.year))
+ mul_id3.year = 0;
+ }
+ mul_id3.length += id3->length;
+ mul_id3.filesize += id3->filesize;
+}
+
+/* (!) Note scale factor applied to returned metadata:
+ * - Unit for filesize will be KiB instead of Bytes
+ * - Unit for length will be s instead of ms
+ *
+ * Use result only as input for browse_id3,
+ * with the track_ct parameter set to > 1.
+ */
+void finalize_id3(struct mp3entry *id3)
+{
+ id3->path[0] = '\0';
+ id3->title = NULL;
+ if (!mul_id3.artist_hash)
+ id3->artist = NULL;
+ if (!mul_id3.album_hash)
+ id3->album = NULL;
+ if (!mul_id3.genre_hash)
+ id3->genre_string = NULL;
+ if (!mul_id3.composer_hash)
+ id3->composer = NULL;
+ if (!mul_id3.albumartist_hash)
+ id3->albumartist = NULL;
+ if (!mul_id3.grouping_hash)
+ id3->grouping = NULL;
+ if (!mul_id3.comment_hash)
+ id3->comment = NULL;
+ id3->disc_string = NULL;
+ id3->track_string = NULL;
+ id3->year_string = NULL;
+ id3->year = mul_id3.year;
+ mul_id3.length /= 1000;
+ mul_id3.filesize >>= 10;
+ id3->length = mul_id3.length > ULONG_MAX ? 0 : mul_id3.length;
+ id3->filesize = mul_id3.filesize > INT_MAX ? 0 : mul_id3.filesize;
+ id3->frequency = mul_id3.frequency;
+ id3->bitrate = mul_id3.bitrate;
+ id3->codectype = mul_id3.codectype;
+ id3->vbr = mul_id3.vbr;
+ id3->discnum = 0;
+ id3->tracknum = 0;
+ id3->track_level = 0;
+ id3->album_level = 0;
+}
diff --git a/apps/plugins/lib/mul_id3.h b/apps/plugins/lib/mul_id3.h
new file mode 100644
index 0000000000..d08095de5c
--- /dev/null
+++ b/apps/plugins/lib/mul_id3.h
@@ -0,0 +1,27 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2023 Christian Soffke
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#ifndef MUL_ID3_H
+#define MUL_ID3_H
+
+void collect_id3(struct mp3entry *id3, bool is_first_track);
+void finalize_id3(struct mp3entry *id3);
+
+#endif /* MUL_ID3_H */
diff --git a/apps/plugins/lib/osd.c b/apps/plugins/lib/osd.c
index 7d6e10a410..99f77da7dc 100644
--- a/apps/plugins/lib/osd.c
+++ b/apps/plugins/lib/osd.c
@@ -106,10 +106,10 @@ static struct osd grey_osd;
# error Unknown 2-bit format; please define macros
# endif /* LCD_PIXELFORMAT */
#elif LCD_DEPTH == 16
-# if defined(LCD_STRIDEFORMAT) && LCD_STRIDEFORMAT == VERTICAL_STRIDE
+# if LCD_STRIDEFORMAT == VERTICAL_STRIDE
# define _OSD_HEIGHT2BYTES(h) ((h)*2)
# define _OSD_BYTES2HEIGHT(b) ((b)/2)
-# else /* !defined(LCD_STRIDEFORMAT) || LCD_STRIDEFORMAT != VERTICAL_STRIDE */
+# else /* LCD_STRIDEFORMAT != VERTICAL_STRIDE */
# define _OSD_WIDTH2BYTES(w) ((w)*2)
# define _OSD_BYTES2WIDTH(b) ((b)/2)
# endif /* end stride type selection */
@@ -160,7 +160,7 @@ static void * _osd_lcd_init_buffers(struct osd *osd, unsigned flags,
rb->viewport_set_fullscreen(&osd->vp, SCREEN_MAIN);
-#if defined(LCD_STRIDEFORMAT) && LCD_STRIDEFORMAT == VERTICAL_STRIDE
+#if LCD_STRIDEFORMAT == VERTICAL_STRIDE
int colbytes = _OSD_HEIGHT2BYTES(LCD_HEIGHT);
int bytecols = *bufsize / colbytes;
int w = _OSD_BYTES2WIDTH(bytecols);
@@ -193,7 +193,7 @@ static void * _osd_lcd_init_buffers(struct osd *osd, unsigned flags,
w = _OSD_BYTES2WIDTH(_OSD_WIDTH2BYTES(w));
osd->lcd_bitmap_stride = _OSD_BYTES2HEIGHT(_OSD_HEIGHT2BYTES(LCD_HEIGHT));
osd->back_bitmap_stride = h;
-#else /* !defined(LCD_STRIDEFORMAT) || LCD_STRIDEFORMAT != VERTICAL_STRIDE */
+#else /* LCD_STRIDEFORMAT != VERTICAL_STRIDE */
int rowbytes = _OSD_WIDTH2BYTES(LCD_WIDTH);
int byterows = *bufsize / rowbytes;
int w = _OSD_BYTES2WIDTH(rowbytes);
diff --git a/apps/plugins/lib/overlay.c b/apps/plugins/lib/overlay.c
index 0ecc1bf3e7..065273534e 100644
--- a/apps/plugins/lib/overlay.c
+++ b/apps/plugins/lib/overlay.c
@@ -83,9 +83,8 @@ enum plugin_status run_overlay(const void* parameter,
goto error_close;
}
-
- if (hdr->api_version > PLUGIN_API_VERSION
- || hdr->api_version < PLUGIN_MIN_API_VERSION)
+ if (hdr->api_version != PLUGIN_API_VERSION ||
+ p_hdr->api_size > sizeof(struct plugin_api))
{
rb->splashf(2*HZ, "%s overlay: Incompatible version.", name);
goto error_close;
diff --git a/apps/plugins/lib/playback_control.c b/apps/plugins/lib/playback_control.c
index 363033b1f2..3e97916400 100644
--- a/apps/plugins/lib/playback_control.c
+++ b/apps/plugins/lib/playback_control.c
@@ -65,21 +65,21 @@ static bool nexttrack(void)
static bool volume(void)
{
const struct settings_list* vol =
- rb->find_setting(&rb->global_settings->volume, NULL);
+ rb->find_setting(&rb->global_settings->volume);
return rb->option_screen((struct settings_list*)vol, parentvp, false, "Volume");
}
static bool shuffle(void)
{
const struct settings_list* shuffle =
- rb->find_setting(&rb->global_settings->playlist_shuffle, NULL);
+ rb->find_setting(&rb->global_settings->playlist_shuffle);
return rb->option_screen((struct settings_list*)shuffle, parentvp, false, "Shuffle");
}
static bool repeat_mode(void)
{
const struct settings_list* repeat =
- rb->find_setting(&rb->global_settings->repeat_mode, NULL);
+ rb->find_setting(&rb->global_settings->repeat_mode);
int old_repeat = rb->global_settings->repeat_mode;
rb->option_screen((struct settings_list*)repeat, parentvp, false, "Repeat");
@@ -91,19 +91,19 @@ static bool repeat_mode(void)
return false;
}
MENUITEM_FUNCTION(prevtrack_item, 0, ID2P(LANG_PREVTRACK),
- prevtrack, NULL, NULL, Icon_NOICON);
+ prevtrack, NULL, Icon_NOICON);
MENUITEM_FUNCTION(playpause_item, 0, ID2P(LANG_PLAYPAUSE),
- play, NULL, NULL, Icon_NOICON);
+ play, NULL, Icon_NOICON);
MENUITEM_FUNCTION(stop_item, 0, ID2P(LANG_STOP_PLAYBACK),
- stop, NULL, NULL, Icon_NOICON);
+ stop, NULL, Icon_NOICON);
MENUITEM_FUNCTION(nexttrack_item, 0, ID2P(LANG_NEXTTRACK),
- nexttrack, NULL, NULL, Icon_NOICON);
+ nexttrack, NULL, Icon_NOICON);
MENUITEM_FUNCTION(volume_item, 0, ID2P(LANG_CHANGE_VOLUME),
- volume, NULL, NULL, Icon_NOICON);
+ volume, NULL, Icon_NOICON);
MENUITEM_FUNCTION(shuffle_item, 0, ID2P(LANG_CHANGE_SHUFFLE_MODE),
- shuffle, NULL, NULL, Icon_NOICON);
+ shuffle, NULL, Icon_NOICON);
MENUITEM_FUNCTION(repeat_mode_item, 0, ID2P(LANG_CHANGE_REPEAT_MODE),
- repeat_mode, NULL, NULL, Icon_NOICON);
+ repeat_mode, NULL, Icon_NOICON);
MAKE_MENU(playback_control_menu, ID2P(LANG_PLAYBACK_CONTROL), NULL, Icon_NOICON,
&prevtrack_item, &playpause_item, &stop_item, &nexttrack_item,
&volume_item, &shuffle_item, &repeat_mode_item);
diff --git a/apps/plugins/lib/pluginlib_actions.c b/apps/plugins/lib/pluginlib_actions.c
index 028472d9a8..4115177eaa 100644
--- a/apps/plugins/lib/pluginlib_actions.c
+++ b/apps/plugins/lib/pluginlib_actions.c
@@ -499,6 +499,8 @@ const struct button_mapping pla_main_ctx[] =
{PLA_SELECT, BUTTON_SELECT, BUTTON_NONE},
{PLA_SELECT_REL, BUTTON_SELECT|BUTTON_REL, BUTTON_SELECT},
{PLA_SELECT_REPEAT, BUTTON_SELECT|BUTTON_REPEAT, BUTTON_NONE},
+#elif (CONFIG_KEYPAD == SHANLING_Q1_PAD)
+ {PLA_EXIT, BUTTON_POWER, BUTTON_NONE},
#else
# ifndef HAVE_TOUCHSCREEN
# error pluginlib_actions: No actions defined
diff --git a/apps/plugins/lib/pluginlib_bmp.c b/apps/plugins/lib/pluginlib_bmp.c
index f3edfbf425..82f84b05af 100644
--- a/apps/plugins/lib/pluginlib_bmp.c
+++ b/apps/plugins/lib/pluginlib_bmp.c
@@ -94,7 +94,7 @@ int save_bmp_file( char* filename, struct bitmap *bm )
*/
void simple_resize_bitmap(struct bitmap *src, struct bitmap *dst)
{
-#if defined(LCD_STRIDEFORMAT) && (LCD_STRIDEFORMAT == VERTICAL_STRIDE)
+#if LCD_STRIDEFORMAT == VERTICAL_STRIDE
const int srcw = src->height;
const int srch = src->width;
const int dstw = dst->height;
diff --git a/apps/plugins/lib/printcell_helper.c b/apps/plugins/lib/printcell_helper.c
new file mode 100644
index 0000000000..48b8b2c9d2
--- /dev/null
+++ b/apps/plugins/lib/printcell_helper.c
@@ -0,0 +1,681 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 by William Wilgus
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+/* spreadsheet cells for rockbox lists */
+#include "plugin.h"
+#include "lib/printcell_helper.h"
+
+#define COLUMN_ENDLEN 3
+#define TITLE_FLAG 0xFF
+#define SELECTED_FLAG 0x1
+
+#if LCD_DEPTH == 1
+#define BAR_WIDTH (1)
+#else
+#define BAR_WIDTH (COLUMN_ENDLEN)
+#endif
+
+struct printcell_info_t {
+ struct gui_synclist *gui_list; /* list to display */
+ int offw[NB_SCREENS]; /* padding between column boundries and text */
+ int iconw[NB_SCREENS]; /* width of an icon */
+ int selcol_offw[NB_SCREENS]; /* offset width calculated for selected item */
+ int totalcolw[NB_SCREENS]; /* total width of all columns */
+ int firstcolxw[NB_SCREENS]; /* first column x + width, save recalculating */
+ int ncols; /* number of columns */
+ int selcol; /* selected column (-1 to ncols-1) */
+ uint32_t hidecol_flags; /*bits 0-31 set bit to 1 to hide a column (1<<col#) */
+ uint16_t colw[NB_SCREENS][PRINTCELL_MAX_COLUMNS]; /* width of title text
+ or MIN(or user defined width / screen width) */
+ char title[PRINTCELL_MAXLINELEN]; /* title buffer */
+ char titlesep; /* character that separates title column items (ex '$') */
+ char colsep; /* character that separates text column items (ex ',') */
+ bool separator; /* draw grid */
+};
+
+static struct printcell_info_t printcell;
+
+static void parse_dsptext(char splitchr, int ncols, const char *dsp_text,
+ char* buffer, size_t bufsz, uint16_t *sidx)
+{
+ /*Internal function loads sidx with split offsets indexing
+ the buffer of null terminated strings, splits on 'splitchr'
+ _assumptions_:
+ dsp_text[len - 1] = \0,
+ sidx[PRINTCELL_MAX_COLUMNS]
+ */
+ int i = 0;
+ size_t j = 0;
+ int ch = splitchr; /* first column $ is optional */
+ if (*dsp_text == splitchr)
+ dsp_text++;
+ /* add null to the start of the text buffer */
+ buffer[j++] = '\0';
+ do
+ {
+ if (ch == splitchr)
+ {
+ sidx[i] = j; /* save start index and copy next column to the buffer */
+ while (*dsp_text != '\0' && *dsp_text != splitchr && j < (bufsz - 1))
+ {
+ buffer[j++] = *dsp_text++;
+ }
+ buffer[j++] = '\0';
+
+ i++;
+ if (i >= ncols || j >= (bufsz - 1))
+ break;
+ }
+ ch = *dsp_text++;
+ } while (ch != '\0');
+ while (i < ncols)
+ sidx[i++] = 0; /* point to null */
+}
+
+static void draw_selector(struct screen *display, struct line_desc *linedes,
+ int selected_flag, int selected_col,
+ int separator_height, int x, int y, int w, int h)
+{
+ /* Internal function draws the currently selected items row & column styling */
+ if (!(separator_height > 0 || (selected_flag & SELECTED_FLAG)))
+ return;
+ y--;
+ h++;
+ int linestyle = linedes->style & _STYLE_DECO_MASK;
+ bool invert = (selected_flag == SELECTED_FLAG && linestyle >= STYLE_COLORBAR);
+ if (invert || (linestyle & STYLE_INVERT) == STYLE_INVERT)
+ {
+ display->set_drawmode(DRMODE_SOLID | DRMODE_INVERSEVID);
+ }
+
+ if (selected_col == printcell.selcol)
+ {
+ if (selected_flag & SELECTED_FLAG)
+ {
+ /* expand left and right bars to show selected column */
+ display->fillrect(x, y, BAR_WIDTH, h);
+ display->fillrect(x + w - BAR_WIDTH + 1, y, BAR_WIDTH, h);
+ display->set_drawmode(DRMODE_FG);
+ }
+ else
+ {
+ /* only draw left and right bars */
+ display->drawrect(x + 1, y, 1, h);
+ display->drawrect(x + w - 1, y, 1, h);
+ return;
+ }
+ }
+ else if (printcell.selcol < 0)
+ {
+ if (selected_flag == SELECTED_FLAG)
+ {
+ if (selected_col > 0)
+ x--;
+ w++;
+ }
+ }
+ /* draw whole rect outline */
+ display->drawrect(x + 1, y, w - 1, h);
+}
+
+static inline void set_cell_width(struct viewport *vp, int max_w, int new_w)
+{
+ /* Internal function sets cell width if less than the max width */
+ if (new_w > max_w)
+ vp->width = max_w;
+ else
+ vp->width = new_w;
+ vp->width -= COLUMN_ENDLEN;
+}
+
+static inline int printcells(struct screen *display, char* buffer,
+ uint16_t *sidx, struct line_desc *linedes,
+ struct viewport *vp, int vp_w, int separator,
+ int x, int y, int offw, int selected_flag, int last_col,
+ bool scroll, bool is_title)
+{
+ /* Internal function prints remaining cells */
+ int text_offset = offw + offw;
+ int screen = display->screen_type;
+ int height = linedes->height;
+ int selsep = (selected_flag == 0) ? 0: separator;
+ uint16_t *screencolwidth = printcell.colw[screen];
+
+ for(int i = 1; i <= last_col; i++)
+ {
+ int ny = y;
+ int nw = screencolwidth[i] + text_offset;
+ int offx = 0;
+
+ if (i == last_col || x + nw >= vp_w - offw + 1)
+ { /* not enough space for next column use up excess */
+ if (nw < (vp_w - x))
+ {
+ if (is_title)
+ offx = ((vp_w - x) - nw) / 2;
+ nw = vp_w - x;
+ }
+ }
+
+ if (!PRINTCELL_COLUMN_IS_VISIBLE(printcell.hidecol_flags, i))
+ nw = 0;
+
+ int nx = x + nw;
+ char *buftext;
+
+ if (nx > 0 && nw > offw && x < vp_w)
+ {
+ set_cell_width(vp, vp_w, nx);
+
+ if (i == printcell.selcol)
+ {
+ linedes->scroll = (selected_flag > 0);
+ linedes->separator_height = selsep;
+ }
+ else
+ {
+ if (vp_w < x + text_offset)
+ {
+ scroll = false;
+ }
+ linedes->scroll = scroll;
+ linedes->separator_height = separator;
+ }
+ buftext = &buffer[sidx[i]];
+ display->put_line(x + offw + offx, ny, linedes, "$t", buftext);
+ vp->width += COLUMN_ENDLEN + 1;
+ draw_selector(display, linedes, selected_flag, i, separator, x, ny, nw, height);
+ }
+ x = nx;
+ }
+ return x;
+}
+
+static inline int calcvisible(int screen, int vp_w, int text_offset, int sbwidth)
+{
+ /* Internal function determine how many of the previous colums can be shown */
+ uint16_t *screencolwidth = printcell.colw[screen];
+ int screenicnwidth = printcell.iconw[screen];
+ int offset = 0;
+ int selcellw = 0;
+ if (printcell.selcol >= 0)
+ selcellw = screencolwidth[printcell.selcol] + text_offset;
+ int maxw = vp_w - (sbwidth + selcellw + 1);
+
+ for (int i = printcell.selcol - 1; i >= 0; i--)
+ {
+ int cw = screencolwidth[i] + text_offset;
+
+ if (!PRINTCELL_COLUMN_IS_VISIBLE(printcell.hidecol_flags, i))
+ cw = 0;
+
+ if (i == 0)
+ cw += screenicnwidth;
+ if (offset > 0 || cw > maxw)
+ offset += cw; /* can not display this cell -- everything left goes here too */
+ else
+ maxw -= cw; /* can display this cell subtract from the max width */
+ }
+ return offset;
+}
+
+static void printcell_listdraw_fn(struct list_putlineinfo_t *list_info)
+{
+/* Internal function callback from the list, draws items as they are requested */
+#define ICON_PADDING 1
+#define ICON_PADDING_S "1"
+ struct screen *display = list_info->display;
+ int screen = display->screen_type;
+ int col_offset_width = printcell.offw[screen];
+ int text_offset = col_offset_width + col_offset_width;
+
+ static char printcell_buffer[PRINTCELL_MAXLINELEN];
+ static uint16_t sidx[PRINTCELL_MAX_COLUMNS]; /*indexes zero terminated strings in buffer*/
+
+ struct gui_synclist *list = list_info->list;
+ int offset_pos = list_info->list->offset_position[screen];
+ int x = list_info->x - offset_pos;
+ int y = list_info->y;
+ int line_indent = list_info->item_indent;
+ int item_offset = list_info->item_offset;
+ int icon = list_info->icon;
+ int icon_w = list_info->icon_width;
+ bool is_title = list_info->is_title;
+ bool is_selected = list_info->is_selected;
+ bool show_cursor = list_info->show_cursor;
+ bool have_icons = list_info->have_icons;
+ struct line_desc *linedes = list_info->linedes;
+ const char *dsp_text = list_info->dsp_text;
+ struct viewport *vp = list_info->vp;
+ int line = list_info->line;
+
+ int selected_flag = ((is_selected || is_title) ?
+ (is_title ? TITLE_FLAG : SELECTED_FLAG) : 0);
+ bool scroll_items = ((selected_flag == TITLE_FLAG) ||
+ (printcell.selcol < 0 && selected_flag > 0));
+
+ /* save for restore */
+ int vp_w = vp->width;
+ int saved_separator_height = linedes->separator_height;
+ bool saved_scroll = linedes->scroll;
+
+ linedes->separator_height = 0;
+ int separator = saved_separator_height;
+
+ if (printcell.separator || (selected_flag & SELECTED_FLAG))
+ separator = 1;
+
+ int nx = x;
+ int last_col = printcell.ncols - 1;
+ int hidden_w = 0;
+ int nw, colxw;
+ char *buftext;
+ printcell_buffer[0] = '\0';
+
+ uint16_t *screencolwidth = printcell.colw[screen];
+ if (printcell.hidecol_flags > 0)
+ {
+ for (int i = 0; i < printcell.ncols; i++)
+ {
+ if (PRINTCELL_COLUMN_IS_VISIBLE(printcell.hidecol_flags, i))
+ last_col = i;
+ else
+ hidden_w += (screencolwidth[i] + text_offset);
+ }
+ }
+
+ if (is_title)
+ {
+ parse_dsptext(printcell.titlesep, printcell.ncols, dsp_text,
+ printcell_buffer, sizeof(printcell_buffer), sidx);
+
+ buftext = &printcell_buffer[sidx[0]]; /* set to first column text */
+ int sbwidth = 0;
+ if (rb->global_settings->scrollbar == SCROLLBAR_LEFT)
+ sbwidth = rb->global_settings->scrollbar_width;
+
+ printcell.iconw[screen] = have_icons ? ICON_PADDING + icon_w : 0;
+
+ if (printcell.selcol_offw[screen] == 0 && printcell.selcol > 0)
+ printcell.selcol_offw[screen] = calcvisible(screen, vp_w, text_offset, sbwidth);
+
+ nx -= printcell.selcol_offw[screen];
+
+ nw = screencolwidth[0] + printcell.iconw[screen] + text_offset;
+
+ if (!PRINTCELL_COLUMN_IS_VISIBLE(printcell.hidecol_flags, 0))
+ nw = printcell.iconw[screen] - 1;
+ nw += sbwidth;
+
+ colxw = nx + nw;
+ printcell.firstcolxw[screen] = colxw; /* save position of first column for subsequent items */
+
+ if (colxw > 0)
+ {
+ set_cell_width(vp, vp_w, colxw);
+ linedes->separator_height = separator;
+
+ if (have_icons)
+ {
+ display->put_line(nx + (COLUMN_ENDLEN/2), y, linedes,
+ "$"ICON_PADDING_S"I$t", icon, buftext);
+ }
+ else
+ {
+ display->put_line(nx + col_offset_width, y, linedes, "$t", buftext);
+ }
+ }
+ }
+ else
+ {
+ parse_dsptext(printcell.colsep, printcell.ncols, dsp_text,
+ printcell_buffer, sizeof(printcell_buffer), sidx);
+
+ buftext = &printcell_buffer[sidx[0]]; /* set to first column text */
+ int cursor = Icon_NOICON;
+ nx -= printcell.selcol_offw[screen];
+
+ if (selected_flag & SELECTED_FLAG)
+ {
+ cursor = Icon_Cursor;
+ /* limit length of selection if columns don't reach end */
+ int maxw = nx + printcell.totalcolw[screen] + printcell.iconw[screen];
+ maxw += text_offset * printcell.ncols;
+ maxw -= hidden_w;
+
+ if (vp_w > maxw)
+ vp->width = maxw;
+ /* display a blank line first to draw selector across all cells */
+ display->put_line(x, y, linedes, "$t", "");
+ }
+
+ //nw = screencolwidth[0] + printcell.iconw[screen] + text_offset;
+ colxw = printcell.firstcolxw[screen] - vp->x; /* match title spacing */
+ nw = colxw - nx;
+ if (colxw > 0)
+ {
+ set_cell_width(vp, vp_w, colxw);
+ if (printcell.selcol == 0 && selected_flag == 0)
+ linedes->separator_height = 0;
+ else
+ {
+ linedes->scroll = printcell.selcol == 0 || scroll_items;
+ linedes->separator_height = separator;
+ }
+ if (show_cursor && have_icons)
+ {
+ /* the list can have both, one of or neither of cursor and item icons,
+ * if both don't apply icon padding twice between the icons */
+ display->put_line(nx, y,
+ linedes, "$*s$"ICON_PADDING_S"I$i$"ICON_PADDING_S"s$*t",
+ line_indent, cursor, icon, item_offset, buftext);
+ }
+ else if (show_cursor || have_icons)
+ {
+ display->put_line(nx, y, linedes, "$*s$"ICON_PADDING_S"I$*t", line_indent,
+ show_cursor ? cursor:icon, item_offset, buftext);
+ }
+ else
+ {
+ display->put_line(nx + col_offset_width, y, linedes,
+ "$*s$*t", line_indent, item_offset, buftext);
+ }
+ }
+ }
+
+ if (colxw > 0) /* draw selector for first column (title or items) */
+ {
+ vp->width += COLUMN_ENDLEN + 1;
+ draw_selector(display, linedes, selected_flag, 0,
+ separator, nx, y, nw, linedes->height);
+ }
+ nx += nw;
+ /* display remaining cells */
+ printcells(display, printcell_buffer, sidx, linedes, vp, vp_w, separator,
+ nx, y, col_offset_width, selected_flag, last_col, scroll_items, is_title);
+
+ /* draw a line at the bottom of the list */
+ if (separator > 0 && line == list->nb_items - 1)
+ display->hline(x, LCD_WIDTH, y + linedes->height - 1);
+
+ /* restore settings */
+ linedes->scroll = saved_scroll;
+ linedes->separator_height = saved_separator_height;
+ display->set_drawmode(DRMODE_FG);
+ vp->width = vp_w;
+}
+
+void printcell_enable(bool enable)
+{
+ if (!printcell.gui_list)
+ return;
+ struct gui_synclist *gui_list = printcell.gui_list;
+#ifdef HAVE_LCD_COLOR
+ static int list_sep_color = INT_MIN;
+ if (enable)
+ {
+ if (list_sep_color == INT_MIN)
+ list_sep_color = rb->global_settings->list_separator_color;
+ rb->global_settings->list_separator_color = rb->global_settings->fg_color;
+ gui_list->callback_draw_item = printcell_listdraw_fn;
+ }
+ else
+ {
+ gui_list->callback_draw_item = NULL;
+ if (list_sep_color != INT_MIN)
+ rb->global_settings->list_separator_color = list_sep_color;
+ list_sep_color = INT_MIN;
+ }
+#else
+ if (enable)
+ gui_list->callback_draw_item = printcell_listdraw_fn;
+ else
+ gui_list->callback_draw_item = NULL;
+#endif
+
+}
+
+int printcell_increment_column(int increment, bool wrap)
+{
+ if (!printcell.gui_list)
+ return -1;
+ struct gui_synclist *gui_list = printcell.gui_list;
+ int item = printcell.selcol + increment;
+ int imin = -1;
+ int imax = printcell.ncols - 1;
+ if(wrap)
+ {
+ imin = imax;
+ imax = -1;
+ }
+
+ if (item < -1)
+ item = imin;
+ else if (item >= printcell.ncols)
+ item = imax;
+
+ if (item != printcell.selcol)
+ {
+ FOR_NB_SCREENS(n) /* offset needs recalculated */
+ printcell.selcol_offw[n] = 0;
+ printcell.selcol = item;
+
+ rb->gui_synclist_draw(gui_list);
+ rb->gui_synclist_speak_item(gui_list);
+ }
+ return item;
+}
+
+int printcell_get_column_selected(void)
+{
+ if (!printcell.gui_list)
+ return -1;
+ return printcell.selcol;
+}
+
+uint32_t printcell_get_column_visibility(int col)
+{
+ if (col >= 0)
+ return (PRINTCELL_COLUMN_IS_VISIBLE(printcell.hidecol_flags, col) ? 0:1);
+ else /* return flag of all columns */
+ return printcell.hidecol_flags;
+}
+
+void printcell_set_column_visible(int col, bool visible)
+{
+ /* visible columns have 0 for the column bit hidden columns have the bit set */
+ if (col >= 0)
+ {
+ if (visible)
+ printcell.hidecol_flags &= ~(PRINTCELL_COLUMN_FLAG(col));
+ else
+ printcell.hidecol_flags |= PRINTCELL_COLUMN_FLAG(col);
+ }
+ else
+ {
+ if (visible) /* set to everything visible */
+ printcell.hidecol_flags = 0;
+ else /* set to everything hidden */
+ printcell.hidecol_flags = ((uint32_t)-1);
+ }
+}
+
+int printcell_set_columns(struct gui_synclist *gui_list,
+ struct printcell_settings * pcs,
+ char * title, enum themable_icons icon)
+{
+
+ if (title == NULL)
+ title = "$PRINTCELL NOT SETUP";
+
+ if (pcs == NULL) /* DEFAULTS */
+ {
+#if LCD_DEPTH > 1
+ /* If line sep is set to automatic then outline cells */
+ bool sep = (rb->global_settings->list_separator_height < 0);
+#else
+ bool sep = (rb->global_settings->cursor_style == 0);
+#endif
+ pcs = &(struct printcell_settings){ .cell_separator = sep,
+ .title_delimeter = '$',
+ .text_delimeter = '$',
+ .hidecol_flags = 0};
+ }
+
+ uint16_t sidx[PRINTCELL_MAX_COLUMNS]; /* starting position of column in title string */
+ int width, height, user_minwidth;
+ int i = 0;
+ size_t j = 0;
+ rb->memset(&printcell, 0, sizeof(struct printcell_info_t));
+
+ printcell.gui_list = gui_list;
+ printcell.separator = pcs->cell_separator;
+ printcell.titlesep = pcs->title_delimeter;
+ printcell.colsep = pcs->text_delimeter;
+ printcell.hidecol_flags = pcs->hidecol_flags;
+
+ int ch = printcell.titlesep; /* first column $ is optional */
+
+ FOR_NB_SCREENS(n)
+ {
+ rb->screens[n]->getstringsize("W", &width, &height);
+ printcell.offw[n] = width; /* set column text offset */
+ }
+
+ if (*title == printcell.titlesep)
+ title++;
+ do
+ {
+ if (ch == printcell.titlesep)
+ {
+ printcell.title[j++] = ch;
+ user_minwidth = 0;
+ if (*title == '*')/* user wants a minimum size for this column */
+ {
+ char *dspst = title++; /* store starting position in case this is wrong */
+ while(isdigit(*title))
+ {
+ user_minwidth = 10*user_minwidth + *title - '0';
+ title++;
+ }
+ if (*title != printcell.titlesep) /* user forgot titlesep or wants to display '*' */
+ {
+ title = dspst;
+ user_minwidth = 0;
+ }
+ else
+ title++;
+ }
+
+ sidx[i] = j;
+
+ while (*title != '\0'
+ && *title != printcell.titlesep
+ && j < PRINTCELL_MAXLINELEN - 1)
+ {
+ printcell.title[j++] = *title++;
+ }
+
+ FOR_NB_SCREENS(n)
+ {
+ rb->screens[n]->getstringsize(&printcell.title[sidx[i]],
+ &width, &height);
+
+ if (width < user_minwidth)
+ width = user_minwidth;
+
+ if (width > LCD_WIDTH)
+ width = LCD_WIDTH;
+
+ printcell.colw[n][i] = width;
+ printcell.totalcolw[n] += width;
+ }
+ if (++i >= PRINTCELL_MAX_COLUMNS - 1)
+ break;
+ }
+ ch = *title++;
+ } while (ch != '\0');
+ printcell.ncols = i;
+ printcell.title[j] = '\0';
+ printcell.selcol = -1;
+
+ rb->gui_synclist_set_title(gui_list, printcell.title, icon);
+ return printcell.ncols;
+}
+
+char *printcell_get_title_text(int selcol, char *buf, size_t bufsz)
+{
+ /* note offsets are calculated everytime this function is called
+ * shouldn't be used in hot code paths */
+ int index = 0;
+ buf[0] = '\0';
+ if (selcol < 0) /* return entire string incld col formatting '$'*/
+ return printcell.title;
+
+ if (selcol < printcell.ncols)
+ {
+ uint16_t sidx[PRINTCELL_MAX_COLUMNS]; /*indexes zero terminated strings in buffer*/
+ parse_dsptext(printcell.titlesep, selcol + 1, printcell.title, buf, bufsz, sidx);
+ index = sidx[selcol];
+ }
+ return &buf[index];
+}
+
+char *printcell_get_column_text(int selcol, char *buf, size_t bufsz)
+{
+ int index = 0;
+ char *bpos;
+ struct gui_synclist *gui_list = printcell.gui_list;
+
+ if (gui_list && gui_list->callback_draw_item == printcell_listdraw_fn)
+ {
+ int col = selcol;
+ int item = gui_list->selected_item;
+ void *data = gui_list->data;
+
+ if (col < printcell.ncols
+ && gui_list->callback_get_item_name(item, data, buf, bufsz) == buf)
+ {
+ bpos = buf;
+ if (col < 0) /* return entire string incld col formatting '$'*/
+ {
+ return bpos;
+ }
+ bpos++; /* Skip sep/NULL */
+
+ while(bpos < &buf[bufsz - 1])
+ {
+ if (*bpos == printcell.colsep || *bpos == '\0')
+ {
+ if (col-- == 0)
+ goto success;
+ index = bpos - buf + 1; /* Skip sep/NULL */
+ }
+ bpos++;
+ }
+ }
+ }
+/*failure*/
+ bpos = buf;
+ index = 0;
+success:
+ *bpos = '\0';
+ return &buf[index];
+}
diff --git a/apps/plugins/lib/printcell_helper.h b/apps/plugins/lib/printcell_helper.h
new file mode 100644
index 0000000000..f58e73c0a5
--- /dev/null
+++ b/apps/plugins/lib/printcell_helper.h
@@ -0,0 +1,87 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 William Wilgus
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifndef _PRINTCELL_LIST_H_
+#define _PRINTCELL_LIST_H_
+#ifndef PRINTCELL_MAX_COLUMNS
+#define PRINTCELL_MAX_COLUMNS 16 /* Max 32 (hidecol_flags)*/
+#endif
+
+#define PRINTCELL_MAXLINELEN MAX_PATH
+#define PC_COL_FLAG(col) ((uint32_t)(col >= 0 \
+ && col < PRINTCELL_MAX_COLUMNS) ? 1u<<col : -1u)
+
+#define PRINTCELL_COLUMN_IS_VISIBLE(flag, col) ((flag & PC_COL_FLAG(col)) == 0)
+#define PRINTCELL_COLUMN_FLAG(col) (PC_COL_FLAG(col))
+
+struct printcell_settings
+{
+ bool cell_separator;
+ char title_delimeter;
+ char text_delimeter;
+ uint32_t hidecol_flags;
+};
+
+/* Printcell initialization - Sets title and calculates cell widths
+* by default each column is identified by '$' character
+* ex 3 columns title = "Col1$Col2$Col3" also accepts $*WIDTH$
+* ex 3 columns varying width title = "$*64$Col1$*128$Col2$Col3
+* supplying struct printcell_settings pcs allows changing default settings
+* supply NULL to use defaults
+*
+* Returns number of columns
+*/
+int printcell_set_columns(struct gui_synclist *gui_list,
+ struct printcell_settings * pcs,
+ char * title, enum themable_icons icon);
+
+/* Sets the printcell function enabled (use after initializing with set_column)
+ * Note you should call printcell_enable(false) if the list might be reused */
+void printcell_enable(bool enable);
+
+/* Increments the current selected column negative increment is allowed
+ returns the selected column
+ range: -1(no selection) to ncols - 1 */
+int printcell_increment_column(int increment, bool wrap);
+
+/* Return index of the currently selected column (-1 to ncols - 1) */
+int printcell_get_column_selected(void);
+
+/* Return the text of currently selected column buffer should be sized
+ * for max item len, buf[PRINTCELL_MAXLINELEN] is a safe bet */
+char *printcell_get_column_text(int selcol, char *buf, size_t bufsz);
+
+/* Return the text of currently selected column title should be sized
+ * for max item len, buf[PRINTCELL_MAXLINELEN] is a safe bet */
+char *printcell_get_title_text(int selcol, char *buf, size_t bufsz);
+
+
+/* Hide or show a specified column - supply col = -1 to affect all columns */
+void printcell_set_column_visible(int col, bool visible);
+
+/* Return visibility of a specified column
+* returns (1 visible or 0 hidden)
+* if supply col == -1 a flag with visibility of all columns will be returned
+* NOTE: flag denotes a hidden column by a 1 in the column bit (1 << col#)
+* PRINTCELL_COLUMN_IS_VISIBLE(flag,col) macro will convert to bool
+*/
+uint32_t printcell_get_column_visibility(int col);
+#endif /*_PRINTCELL_LIST_H_*/
diff --git a/apps/plugins/lib/wrappers.h b/apps/plugins/lib/wrappers.h
index b6fbd51a39..761854fa05 100644
--- a/apps/plugins/lib/wrappers.h
+++ b/apps/plugins/lib/wrappers.h
@@ -53,6 +53,7 @@
#define strlen rb->strlen
#define strlcpy rb->strlcpy
#define strrchr rb->strrchr
+#define fix_path_part rb->fix_path_part
#endif
diff --git a/apps/plugins/lib/xlcd_scroll.c b/apps/plugins/lib/xlcd_scroll.c
index 5ac4a366e8..906f4eaae1 100644
--- a/apps/plugins/lib/xlcd_scroll.c
+++ b/apps/plugins/lib/xlcd_scroll.c
@@ -30,7 +30,7 @@
static const unsigned short patterns[4] = {0xFFFF, 0xFF00, 0x00FF, 0x0000};
#endif
-#if defined(LCD_STRIDEFORMAT) && LCD_STRIDEFORMAT == VERTICAL_STRIDE
+#if LCD_STRIDEFORMAT == VERTICAL_STRIDE
void xlcd_scroll_left(int count)
{
/*size_t dst_stride;*/
@@ -668,4 +668,4 @@ void xlcd_scroll_down(int count)
}
#endif /* LCD_PIXELFORMAT, LCD_DEPTH */
-#endif /* defined(LCD_STRIDEFORMAT) && LCD_STRIDEFORMAT == VERTICAL_STRIDE */
+#endif /* LCD_STRIDEFORMAT == VERTICAL_STRIDE */
diff --git a/apps/plugins/logo.c b/apps/plugins/logo.c
index 984a65aa34..9df73a9d0b 100644
--- a/apps/plugins/logo.c
+++ b/apps/plugins/logo.c
@@ -49,15 +49,27 @@ static const struct button_mapping *plugin_contexts[]
/* We use PLA */
#define LP_QUIT PLA_EXIT
-#define LP_QUIT2 PLA_CANCEL
#define LP_DEC_X PLA_LEFT
#define LP_DEC_X_REPEAT PLA_LEFT_REPEAT
#define LP_INC_X PLA_RIGHT
#define LP_INC_X_REPEAT PLA_RIGHT_REPEAT
+
+#if (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_3G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_4G_PAD)
+#define LP_QUIT2 PLA_UP
+#define LP_DEC_Y PLA_SCROLL_BACK
+#define LP_DEC_Y_REPEAT PLA_SCROLL_BACK_REPEAT
+#define LP_INC_Y PLA_SCROLL_FWD
+#define LP_INC_Y_REPEAT PLA_SCROLL_FWD_REPEAT
+#else
+#define LP_QUIT2 PLA_CANCEL
#define LP_DEC_Y PLA_DOWN
#define LP_DEC_Y_REPEAT PLA_DOWN_REPEAT
#define LP_INC_Y PLA_UP
#define LP_INC_Y_REPEAT PLA_UP_REPEAT
+#endif
+
enum plugin_status plugin_start(const void* parameter) {
int button;
diff --git a/apps/plugins/lrcplayer.c b/apps/plugins/lrcplayer.c
index 2abfbef6c9..d341e6b7a5 100644
--- a/apps/plugins/lrcplayer.c
+++ b/apps/plugins/lrcplayer.c
@@ -227,6 +227,7 @@ static int lrc_set_time(const char *title, const char *unit, long *pval,
case PLA_UP_REPEAT:
case PLA_DOWN_REPEAT:
mult *= 10;
+ /* fallthrough */
case PLA_DOWN:
case PLA_UP:
if (button == PLA_DOWN_REPEAT || button == PLA_DOWN)
@@ -427,8 +428,8 @@ static struct lrc_brpos *calc_brpos(struct lrc_line *lrc_line, int i)
int nword;
int word_count, word_width;
const unsigned char *str;
- }
- sp,
+ }
+ sp,
cr;
lrc_buffer_used = (lrc_buffer_used+3)&~3; /* 4 bytes aligned */
@@ -642,22 +643,6 @@ static void init_time_tag(void)
* /ddd.lrc
*/
-/* taken from apps/recorder/albumart.c */
-static void fix_filename(char* name)
-{
- static const char invalid_chars[] = "*/:<>?\\|";
-
- while (1)
- {
- if (*name == 0)
- return;
- if (*name == '"')
- *name = '\'';
- else if (rb->strchr(invalid_chars, *name))
- *name = '_';
- name++;
- }
-}
static bool find_lrc_file_helper(const char *base_dir)
{
char fname[MAX_PATH];
@@ -677,7 +662,7 @@ static bool find_lrc_file_helper(const char *base_dir)
if (current.id3->title && rb->strcmp(names[0], current.id3->title))
{
rb->strlcpy(fname, current.id3->title, sizeof(fname));
- fix_filename(fname);
+ rb->fix_path_part(fname, 0, sizeof(fname) - 1);
names[1] = fname;
}
@@ -2077,8 +2062,7 @@ static int timetag_editor(void)
while (!exit)
{
button = rb->get_action(CONTEXT_TREE, TIMEOUT_BLOCK);
- if (rb->gui_synclist_do_button(&gui_editor, &button,
- LIST_WRAP_UNLESS_HELD))
+ if (rb->gui_synclist_do_button(&gui_editor, &button))
continue;
switch (button)
@@ -2304,7 +2288,7 @@ static bool lrc_display_menu(void)
usb = rb->set_bool("Wipe", &prefs.wipe);
break;
case LRC_MENU_ALIGN:
- usb = rb->set_option("Alignment", &prefs.align, INT,
+ usb = rb->set_option("Alignment", &prefs.align, RB_INT,
align_names, 3, NULL);
break;
case LRC_MENU_LINE_MODE:
@@ -2361,7 +2345,7 @@ static bool lrc_lyrics_menu(void)
case LRC_MENU_ENCODING:
prefs.encoding++;
old_val = prefs.encoding;
- usb = rb->set_option("Encoding", &prefs.encoding, INT,
+ usb = rb->set_option("Encoding", &prefs.encoding, RB_INT,
cp_names, NUM_CODEPAGES+1, NULL);
if (prefs.encoding != old_val)
{
@@ -2436,7 +2420,6 @@ static bool lrc_debug_menu(void)
{
struct simplelist_info info;
rb->simplelist_info_init(&info, "Debug Menu", 6, NULL);
- info.hide_selection = true;
info.scroll_all = true;
info.get_name = lrc_debug_data;
return rb->simplelist_show_list(&info);
@@ -2642,16 +2625,10 @@ static int handle_button(void)
ff_rewind(0, false);
break;
case ACTION_WPS_VOLDOWN:
- limit = rb->sound_min(SOUND_VOLUME);
- if (--rb->global_settings->volume < limit)
- rb->global_settings->volume = limit;
- rb->sound_set(SOUND_VOLUME, rb->global_settings->volume);
+ rb->adjust_volume(-1);
break;
case ACTION_WPS_VOLUP:
- limit = rb->sound_max(SOUND_VOLUME);
- if (++rb->global_settings->volume > limit)
- rb->global_settings->volume = limit;
- rb->sound_set(SOUND_VOLUME, rb->global_settings->volume);
+ rb->adjust_volume(1);
break;
case ACTION_WPS_CONTEXT:
ret = LRC_GOTO_EDITOR;
diff --git a/apps/plugins/lua/Makefile b/apps/plugins/lua/Makefile
index dcf2910c36..a9c8c22099 100644
--- a/apps/plugins/lua/Makefile
+++ b/apps/plugins/lua/Makefile
@@ -50,7 +50,7 @@ ifdef APP_TYPE
# This is the SDL simulator version
$(OUTPUT): $(OBJS)
- $(call PRINTS,LD $(@F))$(CC) $(CFLAGS) $(SHARED_LDFLAG) $(OBJS) -L$(BUILDDIR) -lplugin -o $@
+ $(call PRINTS,LD $(@F))$(CC) $(CFLAGS) $(SHARED_LDFLAGS) $(OBJS) -L$(BUILDDIR) -lplugin -o $@
ifeq ($(findstring CYGWIN,$(UNAME)),CYGWIN)
# 'x' must be kept or you'll have "Win32 error 5"
# $ fgrep 5 /usr/include/w32api/winerror.h | head -1
diff --git a/apps/plugins/lua/include_lua/playlist.lua b/apps/plugins/lua/include_lua/playlist.lua
index e11b30fe7d..dfa839aab1 100644
--- a/apps/plugins/lua/include_lua/playlist.lua
+++ b/apps/plugins/lua/include_lua/playlist.lua
@@ -28,7 +28,7 @@ rb.playlist_amount = function()
return rb.playlist("amount")
end
rb.playlist_add = function (filename)
- return rb.playlist("add", filename)
+ return rb.playlist("insert_track", filename, rb.PLAYLIST_INSERT_LAST, false, true)
end
rb.playlist_create = function(dir, filename)
return rb.playlist("create", dir, filename)
diff --git a/apps/plugins/lua/lauxlib.c b/apps/plugins/lua/lauxlib.c
index b8332427f0..9a5939aff9 100644
--- a/apps/plugins/lua/lauxlib.c
+++ b/apps/plugins/lua/lauxlib.c
@@ -803,8 +803,10 @@ static int panic (lua_State *L) {
LUALIB_API lua_State *luaL_newstate (void) {
lua_State *L = lua_newstate(l_alloc, NULL);
- lua_setallocf(L, l_alloc, L); /* allocator needs lua_State. */
- if (L) lua_atpanic(L, &panic);
+ if (L){
+ lua_setallocf(L, l_alloc, L); /* allocator needs lua_State. */
+ lua_atpanic(L, &panic);
+ }
return L;
}
diff --git a/apps/plugins/lua/lcode.c b/apps/plugins/lua/lcode.c
index 18102d460e..bf031436e8 100644
--- a/apps/plugins/lua/lcode.c
+++ b/apps/plugins/lua/lcode.c
@@ -802,8 +802,10 @@ static unsigned char *growLineInfo(FuncState *fs) {
unsigned char, MAX_INT, "code size overflow");
p = &f->packedlineinfo[start];
- memset(p, INFO_FILL_BYTE, f->sizelineinfo - start);
- f->packedlineinfo[f->sizelineinfo - 1] = '\0';
+ if (p && f->sizelineinfo > 0) {
+ memset(p, INFO_FILL_BYTE, f->sizelineinfo - start);
+ f->packedlineinfo[f->sizelineinfo - 1] = '\0';
+ }
return p;
}
diff --git a/apps/plugins/lua/lmathlib.c b/apps/plugins/lua/lmathlib.c
index 56c79afced..839d2014ad 100644
--- a/apps/plugins/lua/lmathlib.c
+++ b/apps/plugins/lua/lmathlib.c
@@ -96,7 +96,10 @@ static int math_floor (lua_State *L) {
static int math_fmod (lua_State *L) {
/* Was: lua_pushnumber(L, fmod(luaL_checknumber(L, 1), luaL_checknumber(L, 2))); */
- lua_pushnumber(L, luaL_checknumber(L, 1) % luaL_checknumber(L, 2));
+ lua_Number n = luaL_checknumber(L, 1);
+ lua_Number d = luaL_checknumber(L, 2);
+ luaL_argcheck(L, d != 0, 2, "division by zero");
+ lua_pushnumber(L, n % d);
return 1;
}
diff --git a/apps/plugins/lua/lparser.c b/apps/plugins/lua/lparser.c
index 23d3972036..06c62cedde 100644
--- a/apps/plugins/lua/lparser.c
+++ b/apps/plugins/lua/lparser.c
@@ -359,6 +359,8 @@ static void open_func (LexState *ls, FuncState *fs) {
static void close_func (LexState *ls) {
+ if (!ls || !ls->fs || !ls->fs->f)
+ return;
lua_State *L = ls->L;
FuncState *fs = ls->fs;
Proto *f = fs->f;
diff --git a/apps/plugins/lua/rbdefines_helper.pl b/apps/plugins/lua/rbdefines_helper.pl
index e788855e87..095ec55515 100755
--- a/apps/plugins/lua/rbdefines_helper.pl
+++ b/apps/plugins/lua/rbdefines_helper.pl
@@ -50,10 +50,13 @@ if ($def_type eq "rb_defines") {
'^SYS_(TIMEOUT|POWEROFF|BATTERY_UPDATE)$',
'^SYS_USB_(DIS|)CONNECTED$',
'^HOME_DIR$',
+ '^PLUGIN(_OK|_USB_CONNECTED|_POWEROFF|_GOTO_WPS|_GOTO_PLUGIN)$',
'^PLUGIN_DIR$',
'^PLUGIN(_APPS_|_GAMES_|_)DATA_DIR$',
'^ROCKBOX_DIR$',
'^STYLE_(NONE|DEFAULT|INVERT|COLORBAR|GRADIENT|COLORED)',
+ '^CORE_KEYMAP_FILE$',
+ 'CONTEXT_(STOPSEARCHING|REMOTE|CUSTOM|CUSTOM2|PLUGIN|REMAPPED)$',
'^VIEWERS_DATA_DIR$');
}
elsif ($def_type eq "sound_defines") {
diff --git a/apps/plugins/lua/rockaux.c b/apps/plugins/lua/rockaux.c
index 929dea798b..c4af2b3e96 100644
--- a/apps/plugins/lua/rockaux.c
+++ b/apps/plugins/lua/rockaux.c
@@ -257,7 +257,7 @@ int filetol(int fd, long *num)
{
case '-':
{
- if (retn) /* 0 preceeds, this negative sign must be in error */
+ if (retn > 0) /* 0 preceeds, this negative sign must be in error */
goto get_digits;
neg = true;
continue;
diff --git a/apps/plugins/lua/rocklib.c b/apps/plugins/lua/rocklib.c
index 2672d446fc..070fdb4991 100644
--- a/apps/plugins/lua/rocklib.c
+++ b/apps/plugins/lua/rocklib.c
@@ -317,14 +317,15 @@ RB_WRAP(splash_scroller)
RB_WRAP(playlist)
{
/* just passes NULL to work with the current playlist */
- enum e_playlist {PLAYL_AMOUNT = 0, PLAYL_ADD, PLAYL_CREATE,
+ enum e_playlist {PLAYL_AMOUNT = 0, PLAYL_CREATE,
PLAYL_START, PLAYL_RESUMETRACK, PLAYL_RESUME,
PLAYL_SHUFFLE, PLAYL_SYNC, PLAYL_REMOVEALLTRACKS,
- PLAYL_INSERTTRACK, PLAYL_INSERTDIRECTORY, PLAYL_ECOUNT};
+ PLAYL_INSERTTRACK, PLAYL_INSERTDIRECTORY, PLAYL_INSERTPLAYL,
+ PLAYL_ECOUNT};
- const char *playlist_option[] = {"amount", "add", "create", "start", "resume_track",
+ const char *playlist_option[] = {"amount", "create", "start", "resume_track",
"resume", "shuffle", "sync", "remove_all_tracks",
- "insert_track", "insert_directory", NULL};
+ "insert_track", "insert_directory", "insert_playlist", NULL};
const char *filename, *dir;
int result = 0;
@@ -338,10 +339,6 @@ RB_WRAP(playlist)
case PLAYL_AMOUNT:
result = rb->playlist_amount();
break;
- case PLAYL_ADD:
- filename = luaL_checkstring(L, 2);
- result = rb->playlist_add(filename);
- break;
case PLAYL_CREATE:
dir = luaL_checkstring(L, 2);
filename = luaL_checkstring(L, 3);
@@ -388,6 +385,12 @@ RB_WRAP(playlist)
recurse = lua_toboolean(L, 5); /* default to false */
result = rb->playlist_insert_directory(NULL, dir, pos, queue, recurse);
break;
+ case PLAYL_INSERTPLAYL:
+ filename = luaL_checkstring(L, 2); /* only required parameter */
+ pos = luaL_optint(L, 3, 0);
+ queue = lua_toboolean(L, 4); /* default to false */
+ result = rb->playlist_insert_playlist(NULL, filename, pos, queue);
+ break;
}
yield();
@@ -923,12 +926,6 @@ RB_WRAP(restart_lua)
return -1;
}
-RB_WRAP(show_logo)
-{
- rb->show_logo();
- return 0;
-}
-
RB_WRAP(mem_stats)
{
/* used, allocd, free = rb.mem_stats() */
@@ -1025,7 +1022,6 @@ static const luaL_Reg rocklib[] =
/* MISC */
RB_FUNC(restart_lua),
- RB_FUNC(show_logo),
RB_FUNC(mem_stats),
{NULL, NULL}
@@ -1083,6 +1079,7 @@ LUALIB_API int luaopen_rock(lua_State *L)
RB_CONSTANT(SYS_USB_DISCONNECTED),
RB_CONSTANT(SYS_TIMEOUT),
RB_CONSTANT(SYS_POWEROFF),
+ RB_CONSTANT(SYS_REBOOT),
RB_CONSTANT(SYS_CHARGER_CONNECTED),
RB_CONSTANT(SYS_CHARGER_DISCONNECTED),
diff --git a/apps/plugins/lua/rocklib_events.c b/apps/plugins/lua/rocklib_events.c
index 1c13a6758f..52e87f3d61 100644
--- a/apps/plugins/lua/rocklib_events.c
+++ b/apps/plugins/lua/rocklib_events.c
@@ -253,7 +253,9 @@ static int lua_rev_callback(lua_State *L, struct cb_data *evt)
lua_pushlightuserdata(L, evt->data);
lua_status = lua_resume(L, 2); /* call the saved function */
- if (lua_status == LUA_YIELD) /* coroutine.yield() disallowed */
+ if (lua_status == LUA_SUCCESS)
+ lua_settop(L, 0); /* eat any value(s) returned */
+ else if (lua_status == LUA_YIELD) /* coroutine.yield() disallowed */
luaL_where(L, 1); /* push error string on stack */
return lua_status;
@@ -421,7 +423,7 @@ static void init_event_thread(bool init, struct event_data *ev_data)
0,
EVENT_THREAD
IF_PRIO(, PRIORITY_SYSTEM)
- IF_COP(, COP));
+ IF_COP(, CPU));
/* Timer is used to poll waiting events */
if (!rb->timer_register(1, NULL, EV_TIMER_FREQ, rev_timer_isr IF_COP(, CPU)))
diff --git a/apps/plugins/lua/rocklib_img.c b/apps/plugins/lua/rocklib_img.c
index b0ca769ca4..68e5325ce0 100644
--- a/apps/plugins/lua/rocklib_img.c
+++ b/apps/plugins/lua/rocklib_img.c
@@ -380,7 +380,7 @@ static inline fb_data* rli_get_element(struct rocklua_image* img, int x, int y)
pixel_to_native(x, y, &x, &y);
-#if defined(LCD_STRIDEFORMAT) && LCD_STRIDEFORMAT == VERTICAL_STRIDE
+#if LCD_STRIDEFORMAT == VERTICAL_STRIDE
/* column major address */
size_t data_address = (stride * (x - 1)) + (y - 1);
diff --git a/apps/plugins/lua/rocklua.c b/apps/plugins/lua/rocklua.c
index 3909f3008f..3cf0fce945 100644
--- a/apps/plugins/lua/rocklua.c
+++ b/apps/plugins/lua/rocklua.c
@@ -175,9 +175,10 @@ static int loadfile_newstate(lua_State **L, const char *filename)
static void lua_atexit(void)
{
char *filename;
-
+ int err_n;
if(Ls && lua_gettop(Ls) > 1)
{
+ err_n = lua_tointeger(Ls, -1); /* os.exit? */
if (Ls == lua_touserdata(Ls, -1)) /* signal from restart_lua */
{
filename = (char *) malloc((MAX_PATH * 2) + 1);
@@ -195,7 +196,12 @@ static void lua_atexit(void)
free(filename);
plugin_start(NULL);
}
- else if (lua_tointeger(Ls, -1) != 0) /* os.exit */
+ else if (err_n >= PLUGIN_USB_CONNECTED) /* INTERNAL PLUGIN RETVAL */
+ {
+ lua_close(Ls);
+ _exit(err_n); /* don't call exit handler */
+ }
+ else if (err_n != 0)
{
ERR_RUN:
lu_status = LUA_ERRRUN;
@@ -205,7 +211,7 @@ ERR_RUN:
else
lua_close(Ls);
}
- _exit(0); /* don't call exit handler */
+ _exit(PLUGIN_OK); /* don't call exit handler */
}
/* split filename at argchar
diff --git a/apps/plugins/lua/strftime.c b/apps/plugins/lua/strftime.c
index c6152bf492..cc110469bf 100644
--- a/apps/plugins/lua/strftime.c
+++ b/apps/plugins/lua/strftime.c
@@ -35,6 +35,9 @@ size_t strftime ( char* dst, size_t max, const char* format, const struct tm* t
const char* src;
unsigned long no;
char buf [5];
+#if CONFIG_RTC
+ char sbuf[101];
+#endif
if (!max) return 0;
for ( ; *format != '\0'; format++ ) {
@@ -83,7 +86,6 @@ again:
case 's': {
#if CONFIG_RTC
time_t t = rb->mktime((struct tm*)tm);
- char sbuf[101];
char* c;
sbuf[100]=0;
for (c=sbuf+99; c>sbuf; --c) {
diff --git a/apps/plugins/lua/tlsf_helper.c b/apps/plugins/lua/tlsf_helper.c
index 52ef269bcd..664d87c34e 100644
--- a/apps/plugins/lua/tlsf_helper.c
+++ b/apps/plugins/lua/tlsf_helper.c
@@ -48,7 +48,7 @@ static size_t check_sentinel(void* buf, size_t size)
{
unused++;
while(++i < sz && b[i] == SENTINEL(i) && ++unused)
- ;;
+ {;;}
}
return unused * sizeof(sentinel);
}
diff --git a/apps/plugins/lua_scripts/dbgettags.lua b/apps/plugins/lua_scripts/dbgettags.lua
index ec6e29a330..8e9f26393d 100644
--- a/apps/plugins/lua_scripts/dbgettags.lua
+++ b/apps/plugins/lua_scripts/dbgettags.lua
@@ -28,13 +28,14 @@ local CANCEL_BUTTON = rb.actions.PLA_CANCEL
local sINVALIDDATABASE = "Invalid Database"
local sERROROPENING = "Error opening"
--- tag cache header
-local sTCVERSION = string.char(0x0F)
-local sTCHEADER = string.reverse("TCH" .. sTCVERSION)
-local DATASZ = 4 -- int32_t
-local TCHSIZE = 3 * DATASZ -- 3 x int32_t
-
-local function bytesLE_n(str)
+-- tag cache header
+sTCVERSION = string.char(0x10)
+sTCHEADER = string.reverse("TCH" .. sTCVERSION)
+DATASZ = 4 -- int32_t
+TCHSIZE = 3 * DATASZ -- 3 x int32_t
+
+-- Converts array of bytes to proper endian
+function bytesLE_n(str)
str = str or ""
local tbyte={str:byte(1, -1)}
local bpos = 1
diff --git a/apps/plugins/lua_scripts/random_playlist.lua b/apps/plugins/lua_scripts/random_playlist.lua
new file mode 100644
index 0000000000..b4fd216981
--- /dev/null
+++ b/apps/plugins/lua_scripts/random_playlist.lua
@@ -0,0 +1,558 @@
+--[[ Lua RB Random Playlist -- random_playlist.lua V 1.0
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 William Wilgus
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+]]
+--[[ random_playlist
+ This script opens the users database file containg track path + filenames
+ first it reads the database file making an index of tracks
+ [for large playlists it only saves an index every [10|100|1000] tracks.
+ tracks will be incrementally loaded along with the results of the entries
+ traversed but the garbage collector will erase them when needed]
+
+ next tracks are choosen at random and added either to an in-ram playlist
+ using plugin functions OR
+ to a on disk playlist using a table as a write buffer
+ the user can also choose to play the playlist in either case
+]]
+
+require ("actions")
+require("dbgettags")
+get_tags = nil -- unneeded
+
+-- User defaults
+local playlistpath = "/Playlists"
+local max_tracks = 500; -- size of playlist to create
+local min_repeat = 500; -- this many songs before a repeat
+local play_on_success = true;
+local playlist_name = "random_playback.m3u8"
+--program vars
+local playlist_handle
+local t_playlistbuf -- table for playlist write buffer
+
+-- Random integer function
+local random = math.random; -- ref random(min, max)
+math.randomseed(rb.current_tick()); -- some kind of randomness
+
+-- Button definitions
+local CANCEL_BUTTON = rb.actions.PLA_CANCEL
+local OK_BUTTON = rb.actions.PLA_SELECT
+local ADD_BUTTON = rb.actions.PLA_UP
+local ADD_BUTTON_RPT = rb.actions.PLA_UP_REPEAT or ADD_BUTTON
+local SUB_BUTTON = rb.actions.PLA_DOWN
+local SUB_BUTTON_RPT = rb.actions.PLA_DOWN_REPEAT or SUB_BUTTON
+-- remove action and context tables to free some ram
+rb.actions = nil
+rb.contexts = nil
+-- Program strings
+local sINITDATABASE = "Initialize Database"
+local sHEADERTEXT = "Random Playlist"
+local sPLAYLISTERROR = "Playlist Error!"
+local sSEARCHINGFILES = "Searching for Files.."
+local sERROROPENFMT = "Error Opening %s"
+local sINVALIDDBFMT = "Invalid Database %s"
+local sPROGRESSHDRFMT = "%d \\ %d Tracks"
+local sGOODBYE = "Goodbye"
+
+-- Gets size of text
+local function text_extent(msg, font)
+ font = font or rb.FONT_UI
+ return rb.font_getstringsize(msg, font)
+end
+
+local function _setup_random_playlist(tag_entries, play, savepl, min_repeat, trackcount)
+ -- Setup string tables
+ local tPLAYTEXT = {"Play? [ %s ] (up/dn)", "true = play tracks on success"}
+ local tSAVETEXT = {"Save to disk? [ %s ] (up/dn)",
+ "true = tracks saved to",
+ playlist_name};
+ local tREPEATTEXT = {"Repeat hist? [ %d ] (up/dn)","higher = less repeated songs"}
+ local tPLSIZETEXT = {"Find [ %d ] tracks? (up/dn)",
+ "Warning may overwrite dynamic playlist",
+ "Press back to cancel"};
+ -- how many lines can we fit on the screen?
+ local res, w, h = text_extent("I")
+ h = h + 5 -- increase spacing in the setup menu
+ local max_w = rb.LCD_WIDTH / w
+ local max_h = rb.LCD_HEIGHT - h
+ local y = 0
+
+ -- User Setup Menu
+ local action, ask, increment
+ local t_desc = {scroll = true} -- scroll the setup items
+
+ -- Clears screen and adds title and icon, called first..
+ function show_setup_header()
+ local desc = {icon = 2, show_icons = true, scroll = true} -- 2 == Icon_Playlist
+ rb.lcd_clear_display()
+ rb.lcd_put_line(1, 0, sHEADERTEXT, desc)
+ end
+
+ -- Display up to 3 items and waits for user action -- returns action
+ function ask_user_action(desc, ln1, ln2, ln3)
+ if ln1 then rb.lcd_put_line(1, h, ln1, desc) end
+ if ln2 then rb.lcd_put_line(1, h + h, ln2, desc) end
+ if ln3 then rb.lcd_put_line(1, h + h + h, ln3, desc) end
+ rb.lcd_hline(1,rb.LCD_WIDTH - 1, h - 5);
+ rb.lcd_update()
+
+ local act = rb.get_plugin_action(-1); -- Blocking wait for action
+ -- handle magnitude of the increment here so consumer fn doesn't need to
+ if act == ADD_BUTTON_RPT and act ~= ADD_BUTTON then
+ increment = increment + 1
+ if increment > 1000 then increment = 1000 end
+ act = ADD_BUTTON
+ elseif act == SUB_BUTTON_RPT and act ~= SUB_BUTTON then
+ increment = increment + 1
+ if increment > 1000 then increment = 1000 end
+ act = SUB_BUTTON
+ else
+ increment = 1;
+ end
+
+ return act
+ end
+
+ -- Play the playlist on successful completion true/false?
+ function setup_get_play()
+ action = ask_user_action(tdesc,
+ string.format(tPLAYTEXT[1], tostring(play)),
+ tPLAYTEXT[2]);
+ if action == ADD_BUTTON then
+ play = true
+ elseif action == SUB_BUTTON then
+ play = false
+ end
+ end
+
+ -- Save the playlist to disk true/false?
+ function setup_get_save()
+ action = ask_user_action(tdesc,
+ string.format(tSAVETEXT[1], tostring(savepl)),
+ tSAVETEXT[2], tSAVETEXT[3]);
+ if action == ADD_BUTTON then
+ savepl = true
+ elseif action == SUB_BUTTON then
+ savepl = false
+ elseif action == OK_BUTTON then
+ ask = setup_get_play;
+ setup_get_save = nil
+ action = 0
+ end
+ end
+
+ -- Repeat song buffer list of previously added tracks 0-??
+ function setup_get_repeat()
+ if min_repeat >= trackcount then min_repeat = trackcount - 1 end
+ if min_repeat >= tag_entries then min_repeat = tag_entries - 1 end
+ action = ask_user_action(t_desc,
+ string.format(tREPEATTEXT[1],min_repeat),
+ tREPEATTEXT[2]);
+ if action == ADD_BUTTON then
+ min_repeat = min_repeat + increment
+ elseif action == SUB_BUTTON then -- MORE REPEATS LESS RAM USED
+ if min_repeat < increment then increment = 1 end
+ min_repeat = min_repeat - increment
+ if min_repeat < 0 then min_repeat = 0 end
+ elseif action == OK_BUTTON then
+ ask = setup_get_save;
+ setup_get_repeat = nil
+ action = 0
+ end
+ end
+
+ -- How many tracks to find
+ function setup_get_playlist_size()
+ action = ask_user_action(t_desc,
+ string.format(tPLSIZETEXT[1], trackcount),
+ tPLSIZETEXT[2],
+ tPLSIZETEXT[3]);
+ if action == ADD_BUTTON then
+ trackcount = trackcount + increment
+ elseif action == SUB_BUTTON then
+ if trackcount < increment then increment = 1 end
+ trackcount = trackcount - increment
+ if trackcount < 1 then trackcount = 1 end
+ elseif action == OK_BUTTON then
+ ask = setup_get_repeat;
+ setup_get_playlist_size = nil
+ action = 0
+ end
+ end
+ ask = setup_get_playlist_size; -- \!FIRSTRUN!/
+
+ repeat -- SETUP MENU LOOP
+ show_setup_header()
+ ask()
+ rb.lcd_scroll_stop() -- I'm still wary of not doing this..
+ collectgarbage("collect")
+ if action == CANCEL_BUTTON then rb.lcd_scroll_stop(); return nil end
+ until (action == OK_BUTTON)
+
+ return play, savepl, min_repeat, trackcount;
+end
+--[[ manually create a playlist
+playlist is created initially by creating a new file (or erasing old)
+and adding the BOM]]
+--deletes existing file and creates a new playlist
+local function playlist_create(filename)
+ local filehandle = io.open(filename, "w+") --overwrite
+ if not filehandle then
+ rb.splash(rb.HZ, "Error opening " .. filename)
+ return false
+ end
+ t_playlistbuf = {}
+ filehandle:write("\239\187\191") -- Write BOM --"\xEF\xBB\xBF"
+ playlist_handle = filehandle
+ return true
+end
+
+-- writes track path to a buffer must be later flushed to playlist file
+local function playlist_insert(trackpath)
+ local bufp = #t_playlistbuf + 1
+ t_playlistbuf[bufp] = trackpath
+ bufp = bufp + 1
+ t_playlistbuf[bufp] = "\n"
+ return bufp
+end
+
+-- flushes playlist buffer to file
+local function playlist_flush()
+ playlist_handle:write(table.concat(t_playlistbuf))
+ t_playlistbuf = {}
+end
+
+-- closes playlist file descriptor
+local function playlist_finalize()
+ playlist_handle:close()
+ return true
+end
+
+--[[ Given the filenameDB file [database]
+ creates a random dynamic playlist with a default savename of [playlist]
+ containing [trackcount] tracks, played on completion if [play] is true]]
+local function create_random_playlist(database, playlist, trackcount, play, savepl)
+ if not database or not playlist or not trackcount then return end
+ if not play then play = false end
+ if not savepl then savepl = false end
+
+ local playlist_handle
+ local playlistisfinalized = false
+ local file = io.open('/' .. database or "", "r") --read
+ if not file then rb.splash(100, string.format(sERROROPENFMT, database)) return end
+
+ local fsz = file:seek("end")
+ local fbegin
+ local posln = 0
+ local tag_len = TCHSIZE
+
+ local anchor_index
+ local ANCHOR_INTV
+ local track_index = setmetatable({},{__mode = "v"}) --[[ weak table values
+ this allows them to be garbage collected as space is needed / rebuilt as needed ]]
+
+ -- Read character function sets posln as file position
+ function readchrs(count)
+ if posln >= fsz then return nil end
+ file:seek("set", posln)
+ posln = posln + count
+ return file:read(count)
+ end
+
+ -- Check the header and get size + #entries
+ local tagcache_header = readchrs(DATASZ) or ""
+ local tagcache_sz = readchrs(DATASZ) or ""
+ local tagcache_entries = readchrs(DATASZ) or ""
+
+ if tagcache_header ~= sTCHEADER or
+ bytesLE_n(tagcache_sz) ~= (fsz - TCHSIZE) then
+ rb.splash(100, string.format(sINVALIDDBFMT, database))
+ return
+ end
+
+ local tag_entries = bytesLE_n(tagcache_entries)
+
+ play, savepl, min_repeat, trackcount = _setup_random_playlist(
+ tag_entries, play, savepl, min_repeat, trackcount);
+ _setup_random_playlist = nil
+
+ if savepl == false then
+ -- Use the rockbox playlist functions to add tracks to in-ram playlist
+ playlist_create = function(filename)
+ return (rb.playlist("create", playlistpath .. "/", playlist) >= 0)
+ end
+ playlist_insert = function(str)
+ return rb.playlist("insert_track", str)
+ end
+ playlist_flush = function() end
+ playlist_finalize = function()
+ return (rb.playlist("amount") >= trackcount)
+ end
+ end
+ if not playlist_create(playlistpath .. "/" .. playlist) then return end
+ collectgarbage("collect")
+
+ -- how many lines can we fit on the screen?
+ local res, w, h = text_extent("I")
+ local max_w = rb.LCD_WIDTH / w
+ local max_h = rb.LCD_HEIGHT - h
+ local y = 0
+ rb.lcd_clear_display()
+
+ function get_tracks_random()
+ local tries, idxp
+
+ local tracks = 0
+ local str = ""
+ local t_lru = {}
+ local lru_widx = 1
+ local lru_max = min_repeat
+ if lru_max >= tag_entries then lru_max = tag_entries / 2 + 1 end
+
+ function do_progress_header()
+ rb.lcd_put_line(1, 0, string.format(sPROGRESSHDRFMT,tracks, trackcount))
+ rb.lcd_update()
+ --rb.sleep(300)
+ end
+
+ function show_progress()
+ local sdisp = str:match("([^/]+)$") or "?" --just the track name
+ rb.lcd_put_line(1, y, sdisp:sub(1, max_w));-- limit string length
+ y = y + h
+ if y >= max_h then
+ do_progress_header()
+ rb.lcd_clear_display()
+ playlist_flush(playlist_handle)
+ rb.yield()
+ y = h
+ end
+ end
+
+ -- check for repeated tracks
+ function check_lru(val)
+ if lru_max <= 0 or val == nil then return 0 end --user wants all repeats
+ local rv
+ local i = 1
+ repeat
+ rv = t_lru[i]
+ if rv == nil then
+ break;
+ elseif rv == val then
+ return i
+ end
+ i = i + 1
+ until (i == lru_max)
+ return 0
+ end
+
+ -- add a track to the repeat list (overwrites oldest if full)
+ function push_lru(val)
+ t_lru[lru_widx] = val
+ lru_widx = lru_widx + 1
+ if lru_widx > lru_max then lru_widx = 1 end
+ end
+
+ function get_index()
+ if ANCHOR_INTV > 1 then
+ get_index =
+ function(plidx)
+ local p = track_index[plidx]
+ if p == nil then
+ parse_database_offsets(plidx)
+ end
+ return track_index[plidx][1]
+ end
+ else -- all tracks are indexed
+ get_index =
+ function(plidx)
+ return track_index[plidx]
+ end
+ end
+ end
+
+ get_index() --init get_index fn
+ -- Playlist insert loop
+ while true do
+ str = nil
+ tries = 0
+ repeat
+ idxp = random(1, tag_entries)
+ tries = tries + 1 -- prevent endless loops
+ until check_lru(idxp) == 0 or tries > fsz -- check for recent repeats
+
+ posln = get_index(idxp)
+
+ tag_len = bytesLE_n(readchrs(DATASZ))
+ posln = posln + DATASZ -- idx = bytesLE_n(readchrs(DATASZ))
+ str = readchrs(tag_len) or "\0" -- Read the database string
+ str = str:match("^(%Z+)%z$") -- \0 terminated string
+
+ -- Insert track into playlist
+ if str ~= nil then
+ tracks = tracks + 1
+ show_progress()
+ push_lru(idxp) -- add to repeat list
+ if playlist_insert(str) < 0 then
+ rb.sleep(rb.HZ) --rb playlist fn display own message wait for that
+ rb.splash(rb.HZ, sPLAYLISTERROR)
+ break; -- ERROR, PLAYLIST FULL?
+ end
+ end
+
+ if tracks >= trackcount then
+ playlist_flush()
+ do_progress_header()
+ break
+ end
+
+ -- check for cancel non-blocking
+ if rb.get_plugin_action(0) == CANCEL_BUTTON then
+ break
+ end
+ end
+ end -- get_files
+
+ function build_anchor_index()
+ -- index every n files
+ ANCHOR_INTV = 1 -- for small db we can put all the entries in ram
+ local ent = tag_entries / 100 -- more than 1000 will be incrementally loaded
+ while ent >= 10 do -- need to reduce the size of the anchor index?
+ ent = ent / 10
+ ANCHOR_INTV = ANCHOR_INTV * 10
+ end -- should be power of 10 (10, 100, 1000..)
+ --grab an index for every ANCHOR_INTV entries
+ local aidx={}
+ local acount = 0
+ local next_idx = 1
+ local index = 1
+ local tlen
+ if ANCHOR_INTV == 1 then acount = 1 end
+ while index <= tag_entries and posln < fsz do
+ if next_idx == index then
+ acount = acount + 1
+ next_idx = acount * ANCHOR_INTV
+ aidx[index] = posln
+ else -- fill the weak table, we already did the work afterall
+ track_index[index] = {posln} -- put vals inside table to make them collectable
+ end
+ index = index + 1
+ tlen = bytesLE_n(readchrs(DATASZ))
+ posln = posln + tlen + DATASZ
+ end
+ return aidx
+ end
+
+ function parse_database_offsets(plidx)
+ local tlen
+ -- round to nearest anchor entry that is less than plidx
+ local aidx = (plidx / ANCHOR_INTV) * ANCHOR_INTV
+ local cidx = aidx
+ track_index[cidx] = {anchor_index[aidx] or fbegin};
+ -- maybe we can use previous work to get closer to the desired offset
+ while track_index[cidx] ~= nil and cidx <= plidx do
+ cidx = cidx + 1 --keep seeking till we find an empty entry
+ end
+ posln = track_index[cidx - 1][1]
+ while cidx <= plidx do --[[ walk the remaining entries from the last known
+ & save the entries on the way to our desired entry ]]
+ tlen = bytesLE_n(readchrs(DATASZ))
+ posln = posln + tlen + DATASZ
+ track_index[cidx] = {posln} -- put vals inside table to make them collectable
+ if posln >= fsz then posln = fbegin end
+ cidx = cidx + 1
+ end
+ end
+
+ if trackcount ~= nil then
+ rb.splash(10, sSEARCHINGFILES)
+ fbegin = posln --Mark the beginning for later loops
+ tag_len = 0
+ anchor_index = build_anchor_index() -- index track offsets
+ if ANCHOR_INTV == 1 then
+ -- all track indexes are in ram
+ track_index = anchor_index
+ anchor_index = nil
+ end
+--[[ --profiling
+ local starttime = rb.current_tick();
+ get_tracks_random()
+ local endtime = rb.current_tick();
+ rb.splash(1000, (endtime - starttime) .. " ticks");
+ end
+ if (false) then
+--]]
+ get_tracks_random()
+ playlistisfinalized = playlist_finalize(playlist_handle)
+ end
+
+ file:close()
+ collectgarbage("collect")
+ if trackcount and play == true and playlistisfinalized == true then
+ rb.audio("stop")
+ rb.yield()
+ if savepl == true then
+ rb.playlist("create", playlistpath .. "/", playlist)
+ rb.playlist("insert_playlist", playlistpath .. "/" .. playlist)
+ rb.sleep(rb.HZ)
+ end
+ rb.playlist("start", 0, 0, 0)
+ end
+
+end -- playlist_create
+
+local function main()
+ if not rb.file_exists(rb.ROCKBOX_DIR .. "/database_4.tcd") then
+ rb.splash(rb.HZ, sINITDATABASE)
+ os.exit(1);
+ end
+ if rb.cpu_boost then rb.cpu_boost(true) end
+ rb.backlight_force_on()
+ if not rb.dir_exists(playlistpath) then
+ luadir.mkdir(playlistpath)
+ end
+ rb.lcd_clear_display()
+ rb.lcd_update()
+ collectgarbage("collect")
+ create_random_playlist(rb.ROCKBOX_DIR .. "/database_4.tcd",
+ playlist_name, max_tracks, play_on_success);
+ -- Restore user backlight settings
+ rb.backlight_use_settings()
+ if rb.cpu_boost then rb.cpu_boost(false) end
+ rb.sleep(rb.HZ)
+ rb.splash(rb.HZ * 2, sGOODBYE)
+--[[
+local used, allocd, free = rb.mem_stats()
+local lu = collectgarbage("count")
+local fmt = function(t, v) return string.format("%s: %d Kb\n", t, v /1024) end
+
+-- this is how lua recommends to concat strings rather than ..
+local s_t = {}
+s_t[1] = "rockbox:\n"
+s_t[2] = fmt("Used ", used)
+s_t[3] = fmt("Allocd ", allocd)
+s_t[4] = fmt("Free ", free)
+s_t[5] = "\nlua:\n"
+s_t[6] = fmt("Used", lu * 1024)
+s_t[7] = "\n\nNote that the rockbox used count is a high watermark"
+rb.splash_scroller(10 * rb.HZ, table.concat(s_t)) --]]
+
+end --MAIN
+
+main() -- BILGUS
diff --git a/apps/plugins/lua_scripts/return2WPS.lua b/apps/plugins/lua_scripts/return2WPS.lua
new file mode 100644
index 0000000000..9202237691
--- /dev/null
+++ b/apps/plugins/lua_scripts/return2WPS.lua
@@ -0,0 +1,19 @@
+--[[
+ __________ __ ___.
+ Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ \/ \/ \/ \/ \/
+ $Id$
+ Example Lua Return to WPS on exit
+ Copyright (C) 2021 William Wilgus
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+ This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ KIND, either express or implied.
+]]--
+
+os.exit(rb.PLUGIN_GOTO_WPS)
diff --git a/apps/plugins/main_menu_config.c b/apps/plugins/main_menu_config.c
index f66165e63d..a5488ed2c0 100644
--- a/apps/plugins/main_menu_config.c
+++ b/apps/plugins/main_menu_config.c
@@ -171,7 +171,7 @@ enum plugin_status plugin_start(const void* parameter)
struct gui_synclist list;
bool done = false;
int action, cur_sel;
-
+
menu_table = rb->root_menu_get_options(&menu_item_count);
load_from_cfg();
@@ -188,7 +188,7 @@ enum plugin_status plugin_start(const void* parameter)
{
cur_sel = rb->gui_synclist_get_sel_pos(&list);
action = rb->get_action(CONTEXT_LIST,TIMEOUT_BLOCK);
- if (rb->gui_synclist_do_button(&list,&action,LIST_WRAP_UNLESS_HELD))
+ if (rb->gui_synclist_do_button(&list, &action))
continue;
switch (action)
diff --git a/apps/plugins/matrix.c b/apps/plugins/matrix.c
index 1b2f6d465a..6e96aae10c 100644
--- a/apps/plugins/matrix.c
+++ b/apps/plugins/matrix.c
@@ -52,6 +52,14 @@
/* this set the context to use with PLA */
static const struct button_mapping *plugin_contexts[] = { pla_main_ctx };
+#if (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_3G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_4G_PAD)
+#define MATRIX_EXIT2 PLA_UP
+#else
+#define MATRIX_EXIT2 PLA_CANCEL
+#endif
+
#ifdef HAVE_SCROLLWHEEL
#define MATRIX_SLEEP_MORE PLA_SCROLL_BACK
#define MATRIX_SLEEP_MORE_REPEAT PLA_SCROLL_BACK_REPEAT
@@ -65,7 +73,6 @@ static const struct button_mapping *plugin_contexts[] = { pla_main_ctx };
#endif /* HAVE_SCROLLWHEEL */
#define MATRIX_PAUSE PLA_SELECT
#define MATRIX_EXIT PLA_EXIT
-#define MATRIX_EXIT2 PLA_CANCEL
#define SLEEP HZ/50
diff --git a/apps/plugins/maze.c b/apps/plugins/maze.c
index 20d5c82495..ebb83ab15c 100644
--- a/apps/plugins/maze.c
+++ b/apps/plugins/maze.c
@@ -37,11 +37,14 @@
/* key assignments */
-#if (CONFIG_KEYPAD == IPOD_3G_PAD)
+#if (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_3G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_4G_PAD)
# define MAZE_NEW (BUTTON_SELECT | BUTTON_REPEAT)
# define MAZE_NEW_PRE BUTTON_SELECT
# define MAZE_QUIT BUTTON_MENU
-# define MAZE_SOLVE (BUTTON_SELECT | BUTTON_PLAY)
+# define MAZE_SOLVE (BUTTON_SELECT | BUTTON_REL)
+# define MAZE_SOLVE_PRE BUTTON_SELECT
# define MAZE_RIGHT BUTTON_RIGHT
# define MAZE_RIGHT_REPEAT BUTTON_RIGHT|BUTTON_REPEAT
# define MAZE_LEFT BUTTON_LEFT
@@ -491,17 +494,15 @@ static void maze_move_player_left(struct maze* maze)
enum plugin_status plugin_start(const void* parameter)
{
int button;
-#ifdef MAZE_NEW_PRE
+#if defined(MAZE_NEW_PRE) || defined(MAZE_SOLVE_PRE)
int lastbutton = BUTTON_NONE;
#endif
int quit = 0;
struct maze maze;
(void)parameter;
-#ifdef HAVE_BACKLIGHT
/* Turn off backlight timeout */
backlight_ignore_timeout();
-#endif
/* Seed the RNG */
rb->srand(*rb->current_tick);
@@ -546,6 +547,10 @@ enum plugin_status plugin_start(const void* parameter)
maze_draw(&maze, rb->screens[i]);
break;
case MAZE_SOLVE:
+#ifdef MAZE_SOLVE_PRE
+ if(lastbutton != MAZE_SOLVE_PRE)
+ break;
+#endif
maze_solve(&maze);
FOR_NB_SCREENS(i)
maze_draw(&maze, rb->screens[i]);
@@ -585,14 +590,13 @@ enum plugin_status plugin_start(const void* parameter)
}
break;
}
-#ifdef MAZE_NEW_PRE
+#if defined(MAZE_NEW_PRE) || defined(MAZE_SOLVE_PRE)
if( button != BUTTON_NONE )
lastbutton = button;
#endif
}
/* Turn on backlight timeout (revert to settings) */
-#ifdef HAVE_BACKLIGHT
backlight_use_settings();
-#endif
+
return ((quit == 1) ? PLUGIN_OK : PLUGIN_USB_CONNECTED);
}
diff --git a/apps/plugins/mazezam.c b/apps/plugins/mazezam.c
index cd7b6e22a8..1183f8f502 100644
--- a/apps/plugins/mazezam.c
+++ b/apps/plugins/mazezam.c
@@ -27,7 +27,9 @@
/* Include standard plugin macro */
-#if (CONFIG_KEYPAD == IPOD_3G_PAD)
+#if (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_3G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_4G_PAD)
# define MAZEZAM_MENU BUTTON_MENU
# define MAZEZAM_RIGHT BUTTON_RIGHT
# define MAZEZAM_LEFT BUTTON_LEFT
@@ -256,9 +258,7 @@ static void store_lcd_settings(void)
******************************************************************************/
static void restore_lcd_settings(void) {
/* Turn on backlight timeout (revert to settings) */
-#ifdef HAVE_BACKLIGHT
backlight_use_settings();
-#endif
/* Restore the old settings */
#if LCD_DEPTH > 1
@@ -272,10 +272,9 @@ static void restore_lcd_settings(void) {
* Adjust the LCD settings to suit MazezaM levels
******************************************************************************/
static void plugin_lcd_settings(void) {
-#ifdef HAVE_BACKLIGHT
/* Turn off backlight timeout */
backlight_ignore_timeout();
-#endif
+
/* Set the new settings */
#ifdef HAVE_LCD_COLOR
rb->lcd_set_background(MAZEZAM_BG_COLOR);
diff --git a/apps/plugins/metronome.c b/apps/plugins/metronome.c
index a6b4181df2..9d61c067fd 100644
--- a/apps/plugins/metronome.c
+++ b/apps/plugins/metronome.c
@@ -82,7 +82,13 @@ enum metronome_errors
#define MET_SYNC
#endif
+#if (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_3G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_4G_PAD)
+#define METRONOME_QUIT PLA_UP
+#else
#define METRONOME_QUIT PLA_EXIT
+#endif
#ifdef HAVE_SCROLLWHEEL
#define METRONOME_VOL_UP PLA_SCROLL_FWD
@@ -1663,11 +1669,13 @@ enum plugin_status plugin_start(const void* file)
break;
case METRONOME_LEFT:
bpm_step_counter = 0;
+ /* fallthrough */
case METRONOME_LEFT_REP:
change_bpm(-1);
break;
case METRONOME_RIGHT:
bpm_step_counter = 0;
+ /* fallthrough */
case METRONOME_RIGHT_REP:
change_bpm(1);
break;
diff --git a/apps/plugins/midi/midiplay.c b/apps/plugins/midi/midiplay.c
index a28d1d3862..6345d3c741 100644
--- a/apps/plugins/midi/midiplay.c
+++ b/apps/plugins/midi/midiplay.c
@@ -325,6 +325,9 @@
#define MIDI_VOL_DOWN BUTTON_VOL_DOWN
#define MIDI_PLAYPAUSE BUTTON_PLAY
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+/* use touchscreen */
+
#else
#error No keymap defined!
#endif
diff --git a/apps/plugins/mikmod/mikmod.c b/apps/plugins/mikmod/mikmod.c
index bb295f1029..5179848549 100644
--- a/apps/plugins/mikmod/mikmod.c
+++ b/apps/plugins/mikmod/mikmod.c
@@ -623,7 +623,7 @@ static int settings_menu(void)
break;
case 6:
- rb->set_option(rb->str(LANG_MIKMOD_SAMPLERATE), &(settings.sample_rate), INT, sr_names,
+ rb->set_option(rb->str(LANG_MIKMOD_SAMPLERATE), &(settings.sample_rate), RB_INT, sr_names,
HW_NUM_FREQ, NULL);
applysettings();
break;
@@ -682,7 +682,9 @@ static int main_menu(void)
/* double buffering thread */
static void thread(void)
{
- struct queue_event ev;
+ struct queue_event ev = {
+ .id = 0,
+ };
while (1)
{
@@ -708,7 +710,6 @@ static void mm_errorhandler(void)
static int playfile(char* filename)
{
- int vol = 0;
int button;
int retval = PLUGIN_OK;
bool changingpos = false;
@@ -787,13 +788,8 @@ static int playfile(char* filename)
}
break;
}
- vol = rb->global_settings->volume;
- if (vol < rb->sound_max(SOUND_VOLUME))
- {
- vol++;
- rb->sound_set(SOUND_VOLUME, vol);
- rb->global_settings->volume = vol;
- }
+
+ rb->adjust_volume(1);
break;
case ACTION_WPS_VOLDOWN:
@@ -806,13 +802,8 @@ static int playfile(char* filename)
}
break;
}
- vol = rb->global_settings->volume;
- if (vol > rb->sound_min(SOUND_VOLUME))
- {
- vol--;
- rb->sound_set(SOUND_VOLUME, vol);
- rb->global_settings->volume = vol;
- }
+
+ rb->adjust_volume(-1);
break;
case ACTION_WPS_SKIPPREV:
diff --git a/apps/plugins/minesweeper.c b/apps/plugins/minesweeper.c
index 19a6b99f12..a3c718d79f 100644
--- a/apps/plugins/minesweeper.c
+++ b/apps/plugins/minesweeper.c
@@ -443,6 +443,9 @@ CONFIG_KEYPAD == MROBE500_PAD
# define MINESWP_DISCOVER (BUTTON_SELECT|BUTTON_REPEAT)
# define MINESWP_INFO BUTTON_MENU
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+/* use touchscreen */
+
#else
#error No keymap defined!
#endif
diff --git a/apps/plugins/mosaique.c b/apps/plugins/mosaique.c
index caf5346dc5..cf3f42521a 100644
--- a/apps/plugins/mosaique.c
+++ b/apps/plugins/mosaique.c
@@ -37,10 +37,17 @@
static const struct button_mapping *plugin_contexts[] = { pla_main_ctx };
#define MOSAIQUE_QUIT PLA_EXIT
-#define MOSAIQUE_QUIT2 PLA_CANCEL
-#define MOSAIQUE_SPEED PLA_UP
+#define MOSAIQUE_SPEED PLA_RIGHT
#define MOSAIQUE_RESTART PLA_SELECT
+#if (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_3G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_4G_PAD)
+#define MOSAIQUE_QUIT2 PLA_UP
+#else
+#define MOSAIQUE_QUIT2 PLA_CANCEL
+#endif
+
enum plugin_status plugin_start(const void* parameter)
{
int button;
diff --git a/apps/plugins/mp3_encoder.c b/apps/plugins/mp3_encoder.c
index db10185c91..99671815ca 100644
--- a/apps/plugins/mp3_encoder.c
+++ b/apps/plugins/mp3_encoder.c
@@ -2580,6 +2580,9 @@ CONFIG_KEYPAD == MROBE500_PAD
#define MP3ENC_DONE BUTTON_POWER
#define MP3ENC_SELECT BUTTON_SELECT
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+/* use touchscreen */
+
#else
#error No keymap defined!
#endif
diff --git a/apps/plugins/mpegplayer/libmpeg2/header.c b/apps/plugins/mpegplayer/libmpeg2/header.c
index 001cafe7d5..b40193a338 100644
--- a/apps/plugins/mpegplayer/libmpeg2/header.c
+++ b/apps/plugins/mpegplayer/libmpeg2/header.c
@@ -307,6 +307,7 @@ static int sequence_ext (mpeg2dec_t * mpeg2dec)
return 1;
case 2: /* 4:2:0 */
sequence->chroma_height >>= 1;
+ /* fallthrough */
case 4: /* 4:2:2 */
sequence->chroma_width >>= 1;
}
diff --git a/apps/plugins/mpegplayer/libmpeg2/idct_arm.S b/apps/plugins/mpegplayer/libmpeg2/idct_arm.S
index 97a87a8b59..90eb5031c7 100644
--- a/apps/plugins/mpegplayer/libmpeg2/idct_arm.S
+++ b/apps/plugins/mpegplayer/libmpeg2/idct_arm.S
@@ -43,8 +43,8 @@
ldrsh r7, [r0, #12] /* d2 */
ldrsh r8, [r0, #14] /* d3 */
orrs r9, r2, r3
- orreqs r9, r4, r5
- orreqs r9, r6, r7
+ orrseq r9, r4, r5
+ orrseq r9, r6, r7
cmpeq r8, #0
bne 2f
mov r1, r1, asl #15
@@ -320,7 +320,7 @@ mpeg2_idct_copy:
mpeg2_idct_add:
cmp r0, #129
mov r0, r1
- ldreqsh r1, [r0, #0]
+ ldrsheq r1, [r0, #0]
bne 1f
and r1, r1, #0x70
cmp r1, #0x40
diff --git a/apps/plugins/mpegplayer/libmpeg2/idct_armv6.S b/apps/plugins/mpegplayer/libmpeg2/idct_armv6.S
index dc53cbd7bd..a259721410 100644
--- a/apps/plugins/mpegplayer/libmpeg2/idct_armv6.S
+++ b/apps/plugins/mpegplayer/libmpeg2/idct_armv6.S
@@ -19,6 +19,7 @@
*
****************************************************************************/
+#include "config.h"
.global mpeg2_idct_copy
.type mpeg2_idct_copy, %function
@@ -228,7 +229,7 @@ mpeg2_idct_copy:
mpeg2_idct_add:
cmp r0, #129
mov r0, r1
- ldreqsh r1, [r0, #0]
+ ldrsheq r1, [r0, #0]
bne 1f
and r1, r1, #0x70
cmp r1, #0x40
@@ -260,7 +261,7 @@ mpeg2_idct_add:
strd r4, [r1] @ r4, r5
add r1, r1, r2
cmp r0, r3
- ldrlod r8, [r1] @ r8, r9
+ ldrdlo r8, [r1] @ r8, r9
blo 2b
ldmfd sp!, {r4-r11, pc}
@@ -291,7 +292,7 @@ mpeg2_idct_add:
strd r0, [r2] @ r0, r1
add r2, r2, r3
cmp r2, r12
- ldrlod r0, [r2] @ r0, r1
+ ldrdlo r0, [r2] @ r0, r1
blo 4b
ldmfd sp!, {r4, pc}
diff --git a/apps/plugins/mpegplayer/mpeg_misc.c b/apps/plugins/mpegplayer/mpeg_misc.c
index c85285f4f8..31f0644212 100644
--- a/apps/plugins/mpegplayer/mpeg_misc.c
+++ b/apps/plugins/mpegplayer/mpeg_misc.c
@@ -192,6 +192,7 @@ int mpeg_sysevent_callback(int btn,
{
case SYS_USB_CONNECTED:
case SYS_POWEROFF:
+ case SYS_REBOOT:
mpeg_sysevent_id = btn;
return ACTION_STD_CANCEL;
}
diff --git a/apps/plugins/mpegplayer/mpeg_misc.h b/apps/plugins/mpegplayer/mpeg_misc.h
index e04db0e19d..233b815493 100644
--- a/apps/plugins/mpegplayer/mpeg_misc.h
+++ b/apps/plugins/mpegplayer/mpeg_misc.h
@@ -53,12 +53,14 @@ enum state_enum
#define CMP_3_CONST(_a, _b) \
({ int _x; \
asm volatile ( \
+ BEGIN_ARM_ASM_SYNTAX_UNIFIED \
"ldrb %[x], [%[a], #0] \n" \
"eors %[x], %[x], %[b0] \n" \
- "ldreqb %[x], [%[a], #1] \n" \
- "eoreqs %[x], %[x], %[b1] \n" \
- "ldreqb %[x], [%[a], #2] \n" \
- "eoreqs %[x], %[x], %[b2] \n" \
+ "ldrbeq %[x], [%[a], #1] \n" \
+ "eorseq %[x], %[x], %[b1] \n" \
+ "ldrbeq %[x], [%[a], #2] \n" \
+ "eorseq %[x], %[x], %[b2] \n" \
+ END_ARM_ASM_SYNTAX_UNIFIED \
: [x]"=&r"(_x) \
: [a]"r"(_a), \
[b0]"i"(((_b) >> 24) & 0xff), \
@@ -70,14 +72,16 @@ enum state_enum
#define CMP_4_CONST(_a, _b) \
({ int _x; \
asm volatile ( \
+ BEGIN_ARM_ASM_SYNTAX_UNIFIED \
"ldrb %[x], [%[a], #0] \n" \
"eors %[x], %[x], %[b0] \n" \
- "ldreqb %[x], [%[a], #1] \n" \
- "eoreqs %[x], %[x], %[b1] \n" \
- "ldreqb %[x], [%[a], #2] \n" \
- "eoreqs %[x], %[x], %[b2] \n" \
- "ldreqb %[x], [%[a], #3] \n" \
- "eoreqs %[x], %[x], %[b3] \n" \
+ "ldrbeq %[x], [%[a], #1] \n" \
+ "eorseq %[x], %[x], %[b1] \n" \
+ "ldrbeq %[x], [%[a], #2] \n" \
+ "eorseq %[x], %[x], %[b2] \n" \
+ "ldrbeq %[x], [%[a], #3] \n" \
+ "eorseq %[x], %[x], %[b3] \n" \
+ END_ARM_ASM_SYNTAX_UNIFIED \
: [x]"=&r"(_x) \
: [a]"r"(_a), \
[b0]"i"(((_b) >> 24) & 0xff), \
diff --git a/apps/plugins/mpegplayer/mpeg_settings.c b/apps/plugins/mpegplayer/mpeg_settings.c
index 505f3aa33d..6464f37217 100644
--- a/apps/plugins/mpegplayer/mpeg_settings.c
+++ b/apps/plugins/mpegplayer/mpeg_settings.c
@@ -354,6 +354,9 @@ struct mpeg_settings settings;
#define MPEG_START_TIME_DOWN BUTTON_DOWN
#define MPEG_START_TIME_EXIT BUTTON_POWER
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+#define MPEG_START_TIME_EXIT BUTTON_POWER
+
#else
#error No keymap defined!
#endif
@@ -985,8 +988,8 @@ static int get_start_time(uint32_t duration)
mpegplayer_iram_preserve();
#endif
rb->talk_disable(false);
- rb->talk_value(resume_time / TS_SECOND, UNIT_TIME, false);
- rb->talk_value(resume_time * 100 / duration, UNIT_PERCENT, true);
+ talk_val(resume_time / TS_SECOND, UNIT_TIME, false);
+ talk_val(resume_time * 100 / duration, UNIT_PERCENT, true);
}
sliding = false;
}
@@ -1196,7 +1199,7 @@ static void display_options(void)
#if MPEG_OPTION_DITHERING_ENABLED
case MPEG_OPTION_DITHERING:
result = (settings.displayoptions & LCD_YUV_DITHER) ? 1 : 0;
- mpeg_set_option(rb->str(LANG_DITHERING), &result, INT, noyes, 2, NULL);
+ mpeg_set_option(rb->str(LANG_DITHERING), &result, RB_INT, noyes, 2, NULL);
settings.displayoptions =
(settings.displayoptions & ~LCD_YUV_DITHER)
| ((result != 0) ? LCD_YUV_DITHER : 0);
@@ -1205,17 +1208,17 @@ static void display_options(void)
#endif /* MPEG_OPTION_DITHERING_ENABLED */
case MPEG_OPTION_DISPLAY_FPS:
- mpeg_set_option(rb->str(LANG_DISPLAY_FPS), &settings.showfps, INT,
+ mpeg_set_option(rb->str(LANG_DISPLAY_FPS), &settings.showfps, RB_INT,
noyes, 2, NULL);
break;
case MPEG_OPTION_LIMIT_FPS:
- mpeg_set_option(rb->str(LANG_LIMIT_FPS), &settings.limitfps, INT,
+ mpeg_set_option(rb->str(LANG_LIMIT_FPS), &settings.limitfps, RB_INT,
noyes, 2, NULL);
break;
case MPEG_OPTION_SKIP_FRAMES:
- mpeg_set_option(rb->str(LANG_SKIP_FRAMES), &settings.skipframes, INT,
+ mpeg_set_option(rb->str(LANG_SKIP_FRAMES), &settings.skipframes, RB_INT,
noyes, 2, NULL);
break;
@@ -1266,31 +1269,31 @@ static void audio_options(void)
switch (result)
{
case MPEG_AUDIO_TONE_CONTROLS:
- mpeg_set_option(rb->str(LANG_TONE_CONTROLS), &settings.tone_controls, INT,
+ mpeg_set_option(rb->str(LANG_TONE_CONTROLS), &settings.tone_controls, RB_INT,
globaloff, 2, NULL);
sync_audio_setting(result, false);
break;
case MPEG_AUDIO_CHANNEL_MODES:
mpeg_set_option(rb->str(LANG_CHANNEL_CONFIGURATION), &settings.channel_modes,
- INT, globaloff, 2, NULL);
+ RB_INT, globaloff, 2, NULL);
sync_audio_setting(result, false);
break;
case MPEG_AUDIO_CROSSFEED:
- mpeg_set_option(rb->str(LANG_CROSSFEED), &settings.crossfeed, INT,
+ mpeg_set_option(rb->str(LANG_CROSSFEED), &settings.crossfeed, RB_INT,
globaloff, 2, NULL);
sync_audio_setting(result, false);
break;
case MPEG_AUDIO_EQUALIZER:
- mpeg_set_option(rb->str(LANG_EQUALIZER), &settings.equalizer, INT,
+ mpeg_set_option(rb->str(LANG_EQUALIZER), &settings.equalizer, RB_INT,
globaloff, 2, NULL);
sync_audio_setting(result, false);
break;
case MPEG_AUDIO_DITHERING:
- mpeg_set_option(rb->str(LANG_DITHERING), &settings.dithering, INT,
+ mpeg_set_option(rb->str(LANG_DITHERING), &settings.dithering, RB_INT,
globaloff, 2, NULL);
sync_audio_setting(result, false);
break;
@@ -1319,7 +1322,7 @@ static void resume_options(void)
};
mpeg_set_option(rb->str(LANG_MENU_RESUME_OPTIONS), &settings.resume_options,
- INT, items, MPEG_RESUME_NUM_OPTIONS, NULL);
+ RB_INT, items, MPEG_RESUME_NUM_OPTIONS, NULL);
}
static void clear_resume_count(void)
@@ -1366,7 +1369,7 @@ static void mpeg_settings(void)
case MPEG_SETTING_PLAY_MODE:
mpeg_set_option(rb->str(LANG_MENU_PLAY_MODE), &settings.play_mode,
- INT, singleall, 2, NULL);
+ RB_INT, singleall, 2, NULL);
break;
case MPEG_SETTING_CLEAR_RESUMES:
diff --git a/apps/plugins/mpegplayer/mpegplayer.c b/apps/plugins/mpegplayer/mpegplayer.c
index 34eafd5d9c..654a348959 100644
--- a/apps/plugins/mpegplayer/mpegplayer.c
+++ b/apps/plugins/mpegplayer/mpegplayer.c
@@ -486,6 +486,9 @@ CONFIG_KEYPAD == SANSA_M200_PAD
#define MPEG_RW BUTTON_LEFT
#define MPEG_FF BUTTON_RIGHT
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+/* use touchscreen */
+
#else
#error No keymap defined!
#endif
@@ -1212,10 +1215,9 @@ static void osd_lcd_enable_hook(unsigned short id, void* param)
static void osdbacklight_hw_on_video_mode(bool video_on)
{
if (video_on) {
-#ifdef HAVE_BACKLIGHT
/* Turn off backlight timeout */
backlight_ignore_timeout();
-#endif
+
#if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
rb->remove_event(LCD_EVENT_ACTIVATION, osd_lcd_enable_hook);
#endif
@@ -1223,10 +1225,8 @@ static void osdbacklight_hw_on_video_mode(bool video_on)
#if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
rb->add_event(LCD_EVENT_ACTIVATION, osd_lcd_enable_hook);
#endif
-#ifdef HAVE_BACKLIGHT
/* Revert to user's backlight settings */
backlight_use_settings();
-#endif
}
}
diff --git a/apps/plugins/mpegplayer/video_out_rockbox.c b/apps/plugins/mpegplayer/video_out_rockbox.c
index ee5c3400c5..331383843b 100644
--- a/apps/plugins/mpegplayer/video_out_rockbox.c
+++ b/apps/plugins/mpegplayer/video_out_rockbox.c
@@ -369,7 +369,7 @@ void stretch_image_plane(const uint8_t * src, uint8_t *dst, int stride,
bool vo_draw_frame_thumb(uint8_t * const * buf, const struct vo_rect *rc)
{
void *mem;
- size_t bufsize;
+ size_t bufsize = 0;
uint8_t *yuv[3];
struct vo_rect thumb_rc;
int thumb_width, thumb_height;
diff --git a/apps/plugins/multiboot_select.c b/apps/plugins/multiboot_select.c
new file mode 100644
index 0000000000..2922136548
--- /dev/null
+++ b/apps/plugins/multiboot_select.c
@@ -0,0 +1,358 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2022 Aidan MacDonald
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "plugin.h"
+
+/* should be more than enough */
+#define MAX_ROOTS 128
+
+static enum plugin_status plugin_status = PLUGIN_OK;
+static char tmpbuf[MAX_PATH+1];
+static char tmpbuf2[MAX_PATH+1];
+static char cur_root[MAX_PATH];
+static char roots[MAX_ROOTS][MAX_PATH];
+static int nroots;
+
+/* Read a redirect file and return the path */
+static char* read_redirect_file(const char* filename)
+{
+ int fd = rb->open(filename, O_RDONLY);
+ if(fd < 0)
+ return NULL;
+
+ ssize_t ret = rb->read(fd, tmpbuf, sizeof(tmpbuf));
+ rb->close(fd);
+ if(ret < 0 || (size_t)ret >= sizeof(tmpbuf))
+ return NULL;
+
+ /* relative paths are ignored */
+ if(tmpbuf[0] != '/')
+ ret = 0;
+
+ /* remove trailing control chars (newlines etc.) */
+ for(ssize_t i = ret - 1; i >= 0 && tmpbuf[i] < 0x20; --i)
+ tmpbuf[i] = '\0';
+
+ return tmpbuf;
+}
+
+/* Search for a redirect file, like get_redirect_dir() */
+static const char* read_redirect(void)
+{
+ for(int vol = NUM_VOLUMES-1; vol >= MULTIBOOT_MIN_VOLUME; --vol) {
+ rb->snprintf(tmpbuf, sizeof(tmpbuf), "/<%d>/%s", vol, BOOT_REDIR);
+ const char* path = read_redirect_file(tmpbuf);
+ if(path && path[0] == '/') {
+ /* prepend the volume because that's what we expect */
+ rb->snprintf(tmpbuf2, sizeof(tmpbuf2), "/<%d>%s", vol, path);
+ return tmpbuf2;
+ }
+ }
+
+ tmpbuf[0] = '\0';
+ return tmpbuf;
+}
+
+/* Remove all redirect files except for the one on volume 'exclude_vol' */
+static int clear_redirect(int exclude_vol)
+{
+ int ret = 0;
+ for(int vol = MULTIBOOT_MIN_VOLUME; vol < NUM_VOLUMES; ++vol) {
+ if(vol == exclude_vol)
+ continue;
+
+ rb->snprintf(tmpbuf, sizeof(tmpbuf), "/<%d>/%s", vol, BOOT_REDIR);
+ if(rb->file_exists(tmpbuf) && rb->remove(tmpbuf) < 0)
+ ret = -1;
+ }
+
+ return ret;
+}
+
+/* Save a path to the redirect file */
+static int write_redirect(const char* abspath)
+{
+ /* get the volume (required) */
+ const char* path = abspath;
+ int vol = rb->path_strip_volume(abspath, &path, false);
+ if(path == abspath)
+ return -1;
+
+ /* remove all other redirect files */
+ if(clear_redirect(vol))
+ return -1;
+
+ /* open the redirect file on the same volume */
+ rb->snprintf(tmpbuf, sizeof(tmpbuf), "/<%d>/%s", vol, BOOT_REDIR);
+ int fd = rb->open(tmpbuf, O_WRONLY|O_CREAT|O_TRUNC, 0644);
+ if(fd < 0)
+ return fd;
+
+ size_t pathlen = rb->strlen(path);
+ ssize_t ret = rb->write(fd, path, pathlen);
+ if(ret < 0 || (size_t)ret != pathlen) {
+ rb->close(fd);
+ return -1;
+ }
+
+ ret = rb->write(fd, "\n", 1);
+ rb->close(fd);
+ if(ret != 1)
+ return -1;
+
+ return 0;
+}
+
+/* Check if the firmware file is valid
+ * TODO: this should at least check model number or something */
+static bool check_firmware(const char* path)
+{
+ return rb->file_exists(path);
+}
+
+static int root_compare(const void* a, const void* b)
+{
+ const char* as = a;
+ const char* bs = b;
+ return rb->strcmp(as, bs);
+}
+
+/* Scan the filesystem for possible redirect targets. To prevent this from
+ * taking too long we only check the directories in the root of each volume
+ * look check for a rockbox firmware binary underneath /dir/.rockbox. If it
+ * exists then we report /<vol#>/dir as a root. */
+static int find_roots(void)
+{
+ const char* bootdir = *BOOTDIR == '/' ? &BOOTDIR[1] : BOOTDIR;
+ nroots = 0;
+
+ for(int vol = MULTIBOOT_MIN_VOLUME; vol < NUM_VOLUMES; ++vol) {
+ rb->snprintf(tmpbuf, sizeof(tmpbuf), "/<%d>", vol);
+
+ /* try to open the volume root; ignore failures since they'll
+ * occur if the volume is unmounted */
+ DIR* dir = rb->opendir(tmpbuf);
+ if(!dir)
+ continue;
+
+ struct dirent* ent;
+ while((ent = rb->readdir(dir))) {
+ /* skip non-directories */
+ if ((rb->dir_get_info(dir, ent).attribute & ATTR_DIRECTORY) == 0) {
+ continue;
+ }
+
+ const char *dname = ent->d_name;
+ /* check for bootdir in the root of the volume */
+ if (rb->strcmp(bootdir, dname) == 0) {
+ dname = "";
+ }
+
+ int r = rb->snprintf(tmpbuf, sizeof(tmpbuf), "/<%d>/%s/%s/%s",
+ vol, dname, bootdir, BOOTFILE);
+
+ if(r < 0 || (size_t)r >= sizeof(tmpbuf))
+ continue;
+
+ if(check_firmware(tmpbuf)) {
+ rb->snprintf(roots[nroots], MAX_PATH, "/<%d>/%s",
+ vol, dname);
+ nroots += 1;
+
+ /* quit if we hit the maximum */
+ if(nroots == MAX_ROOTS) {
+ vol = NUM_VOLUMES;
+ break;
+ }
+ }
+ }
+
+ rb->closedir(dir);
+ }
+
+ rb->qsort(roots, nroots, MAX_PATH, root_compare);
+ return nroots;
+}
+
+static const char* picker_get_name_cb(int selected, void* data,
+ char* buffer, size_t buffer_len)
+{
+ (void)data;
+ (void)buffer;
+ (void)buffer_len;
+ return roots[selected];
+}
+
+static int picker_action_cb(int action, struct gui_synclist* lists)
+{
+ (void)lists;
+ if(action == ACTION_STD_OK)
+ action = ACTION_STD_CANCEL;
+ return action;
+}
+
+static bool show_picker_menu(int* selection)
+{
+ struct simplelist_info info;
+ rb->simplelist_info_init(&info, "Select new root", nroots, NULL);
+ info.selection = *selection;
+ info.get_name = picker_get_name_cb;
+ info.action_callback = picker_action_cb;
+ if(rb->simplelist_show_list(&info))
+ return true;
+
+ if(0 <= info.selection && info.selection < nroots)
+ *selection = info.selection;
+
+ return false;
+}
+
+enum {
+ MB_SELECT_ROOT,
+ MB_CLEAR_REDIRECT,
+ MB_CURRENT_ROOT,
+ MB_SAVE_AND_EXIT,
+ MB_SAVE_AND_REBOOT,
+ MB_EXIT,
+ MB_NUM_ITEMS,
+};
+
+static const char* menu_get_name_cb(int selected, void* data,
+ char* buffer, size_t buffer_len)
+{
+ (void)data;
+
+ switch(selected) {
+ case MB_SELECT_ROOT:
+ return "Select root";
+
+ case MB_CLEAR_REDIRECT:
+ return "Clear redirect";
+
+ case MB_CURRENT_ROOT:
+ if(cur_root[0]) {
+ rb->snprintf(buffer, buffer_len, "Using root: %s", cur_root);
+ return buffer;
+ }
+
+ return "Using default root";
+
+ case MB_SAVE_AND_EXIT:
+ return "Save and Exit";
+
+ case MB_SAVE_AND_REBOOT:
+ return "Save and Reboot";
+
+ case MB_EXIT:
+ default:
+ return "Exit";
+ }
+}
+
+static int menu_action_cb(int action, struct gui_synclist* lists)
+{
+ int selected = rb->gui_synclist_get_sel_pos(lists);
+
+ if(action != ACTION_STD_OK)
+ return action;
+
+ switch(selected) {
+ case MB_SELECT_ROOT: {
+ if(find_roots() <= 0) {
+ rb->splashf(3*HZ, "No roots found");
+ break;
+ }
+
+ int root_sel = nroots-1;
+ for(; root_sel > 0; --root_sel)
+ if(!rb->strcmp(roots[root_sel], cur_root))
+ break;
+
+ if(show_picker_menu(&root_sel))
+ return SYS_USB_CONNECTED;
+
+ rb->strcpy(cur_root, roots[root_sel]);
+ action = ACTION_REDRAW;
+ } break;
+
+ case MB_CLEAR_REDIRECT:
+ if(cur_root[0]) {
+ memset(cur_root, 0, sizeof(cur_root));
+ rb->splashf(HZ, "Cleared redirect");
+ }
+
+ action = ACTION_REDRAW;
+ break;
+
+ case MB_SAVE_AND_REBOOT:
+ case MB_SAVE_AND_EXIT: {
+ int ret;
+ if(cur_root[0])
+ ret = write_redirect(cur_root);
+ else
+ ret = clear_redirect(-1);
+
+ if(ret < 0)
+ rb->splashf(3*HZ, "Couldn't save settings");
+ else
+ rb->splashf(HZ, "Settings saved");
+
+ action = ACTION_STD_CANCEL;
+ plugin_status = (ret < 0) ? PLUGIN_ERROR : PLUGIN_OK;
+
+ if(ret >= 0 && selected == MB_SAVE_AND_REBOOT)
+ rb->sys_reboot();
+ } break;
+
+ case MB_EXIT:
+ return ACTION_STD_CANCEL;
+
+ default:
+ action = ACTION_REDRAW;
+ break;
+ }
+
+ return action;
+}
+
+static bool show_menu(void)
+{
+ struct simplelist_info info;
+ rb->simplelist_info_init(&info, "Multiboot Settings", MB_NUM_ITEMS, NULL);
+ info.get_name = menu_get_name_cb;
+ info.action_callback = menu_action_cb;
+ return rb->simplelist_show_list(&info);
+}
+
+enum plugin_status plugin_start(const void* param)
+{
+ (void)param;
+
+ /* load the current root */
+ const char* myroot = read_redirect();
+ rb->strcpy(cur_root, myroot);
+
+ /* display the menu */
+ if(show_menu())
+ return PLUGIN_USB_CONNECTED;
+ else
+ return plugin_status;
+}
diff --git a/apps/plugins/open_plugins.c b/apps/plugins/open_plugins.c
index f9133f91bb..b608aff789 100644
--- a/apps/plugins/open_plugins.c
+++ b/apps/plugins/open_plugins.c
@@ -49,7 +49,8 @@
static int fd_dat;
static struct gui_synclist lists;
struct open_plugin_entry_t op_entry;
-const off_t op_entry_sz = sizeof(struct open_plugin_entry_t);
+static const uint32_t open_plugin_csum = OPEN_PLUGIN_CHECKSUM;
+static const off_t op_entry_sz = sizeof(struct open_plugin_entry_t);
/* we only need the names for the first menu so don't bother reading paths yet */
const off_t op_name_sz = OPEN_PLUGIN_NAMESZ + (op_entry.name - (char*)&op_entry);
@@ -86,14 +87,23 @@ static size_t pathbasename(const char *name, const char **nameptr)
*nameptr = q;
return r - q;
}
+static int op_entry_checksum(void)
+{
+ if (op_entry.checksum != open_plugin_csum +
+ (op_entry.lang_id <= OPEN_PLUGIN_LANG_INVALID ? 0 : LANG_LAST_INDEX_IN_ARRAY))
+ {
+ return 0;
+ }
+ return 1;
+}
static bool op_entry_read(int fd, int selected_item, off_t data_sz)
{
rb->memset(&op_entry, 0, op_entry_sz);
op_entry.lang_id = -1;
- return ((selected_item >= 0) &&
+ return ((selected_item >= 0) && (fd >= 0) &&
(rb->lseek(fd, selected_item * op_entry_sz, SEEK_SET) >= 0) &&
- (rb->read(fd, &op_entry, data_sz) == data_sz));
+ (rb->read(fd, &op_entry, data_sz) == data_sz) && op_entry_checksum() > 0);
}
static bool op_entry_read_name(int fd, int selected_item)
@@ -112,13 +122,14 @@ static int op_entry_read_opx(const char *path)
if(len > OP_LEN && rb->strcasecmp(&((path)[len-OP_LEN]), "." OP_EXT) == 0)
{
fd_opx = rb->open(path, O_RDONLY);
- if (fd_opx)
+ if (fd_opx >= 0)
{
filesize = rb->filesize(fd_opx);
ret = filesize;
if (filesize == op_entry_sz && !op_entry_read(fd_opx, 0, op_entry_sz))
ret = 0;
-
+ else if (op_entry_checksum() <= 0)
+ ret = 0;
rb->close(fd_opx);
}
}
@@ -131,7 +142,7 @@ static void op_entry_export(int selection)
int fd = -1;
char filename [MAX_PATH + 1];
- if (!op_entry_read(fd_dat, selection, op_entry_sz))
+ if (!op_entry_read(fd_dat, selection, op_entry_sz) || op_entry_checksum() <= 0)
goto failure;
rb->snprintf(filename, MAX_PATH, "%s/%s", PLUGIN_APPS_DIR, op_entry.name);
@@ -161,6 +172,12 @@ failure:
}
+static void op_entry_set_checksum(void)
+{
+ op_entry.checksum = open_plugin_csum +
+ (op_entry.lang_id <= OPEN_PLUGIN_LANG_INVALID ? 0 : LANG_LAST_INDEX_IN_ARRAY);
+}
+
static void op_entry_set_name(void)
{
char tmp_buf[OPEN_PLUGIN_NAMESZ+1];
@@ -172,17 +189,20 @@ static void op_entry_set_name(void)
static int op_entry_set_path(void)
{
int ret = 0;
- struct browse_context browse;
char tmp_buf[OPEN_PLUGIN_BUFSZ+1];
if (op_entry.path[0] == '\0')
rb->strcpy(op_entry.path, PLUGIN_DIR"/");
- rb->browse_context_init(&browse, SHOW_ALL, BROWSE_SELECTONLY, rb->str(LANG_ADD),
- Icon_Plugin, op_entry.path, NULL);
-
- browse.buf = tmp_buf;
- browse.bufsize = OPEN_PLUGIN_BUFSZ;
+ struct browse_context browse = {
+ .dirfilter = SHOW_ALL,
+ .flags = BROWSE_SELECTONLY | BROWSE_DIRFILTER,
+ .title = rb->str(LANG_ADD),
+ .icon = Icon_Plugin,
+ .root = op_entry.path,
+ .buf = tmp_buf,
+ .bufsize = sizeof(tmp_buf),
+ };
if (rb->rockbox_browse(&browse) == GO_TO_PREVIOUS)
{
@@ -196,7 +216,6 @@ static int op_entry_set_path(void)
static int op_entry_set_param_path(void)
{
int ret = 0;
- struct browse_context browse;
char tmp_buf[OPEN_PLUGIN_BUFSZ+1];
if (op_entry.param[0] == '\0')
@@ -204,11 +223,15 @@ static int op_entry_set_param_path(void)
else
rb->strcpy(tmp_buf, op_entry.param);
- rb->browse_context_init(&browse, SHOW_ALL, BROWSE_SELECTONLY, "",
- Icon_Plugin, tmp_buf, NULL);
-
- browse.buf = tmp_buf;
- browse.bufsize = OPEN_PLUGIN_BUFSZ;
+ struct browse_context browse = {
+ .dirfilter = SHOW_ALL,
+ .flags = BROWSE_SELECTONLY | BROWSE_DIRFILTER,
+ .title = rb->str(LANG_PARAMETER),
+ .icon = Icon_Plugin,
+ .root = tmp_buf,
+ .buf = tmp_buf,
+ .bufsize = sizeof(tmp_buf),
+ };
if (rb->rockbox_browse(&browse) == GO_TO_PREVIOUS)
{
@@ -277,12 +300,12 @@ static int op_entry_transfer(int fd, int fd_tmp,
void *data)
{
int entries = -1;
- if (fd_tmp && fd && rb->lseek(fd, 0, SEEK_SET) == 0)
+ if (fd_tmp >= 0 && fd >= 0 && rb->lseek(fd, 0, SEEK_SET) == 0)
{
entries = 0;
while (rb->read(fd, &op_entry, op_entry_sz) == op_entry_sz)
{
- if (compfn && compfn(&op_entry, entries, data) > 0)
+ if (compfn && compfn(&op_entry, entries, data) > 0 && op_entry_checksum() > 0)
{
rb->write(fd_tmp, &op_entry, op_entry_sz);
entries++;
@@ -296,6 +319,7 @@ static uint32_t op_entry_add_path(const char *key, const char *plugin, const cha
{
int len;
uint32_t hash;
+ uint32_t newhash;
char *pos = "";;
int fd_tmp = -1;
use_key = (use_key == true && key != NULL);
@@ -309,7 +333,8 @@ static uint32_t op_entry_add_path(const char *key, const char *plugin, const cha
{
/* need to keep the old hash so we can remove the old entry */
hash = op_entry.hash;
- open_plugin_get_hash(plugin, &op_entry.hash);
+ open_plugin_get_hash(plugin, &newhash);
+ op_entry.hash = newhash;
}
else
hash = op_entry.hash;
@@ -352,9 +377,12 @@ static uint32_t op_entry_add_path(const char *key, const char *plugin, const cha
/* hash on the parameter path if it is a file */
if (op_entry.lang_id <0 && key == op_entry.path &&
rb->file_exists(op_entry.param))
- open_plugin_get_hash(op_entry.path, &op_entry.hash);
+ {
+ open_plugin_get_hash(op_entry.path, &newhash);
+ op_entry.hash = newhash;
+ }
}
-
+ op_entry_set_checksum();
rb->write(fd_tmp, &op_entry, op_entry_sz); /* add new entry first */
}
else if(op_entry_read_opx(plugin) == op_entry_sz)
@@ -369,13 +397,13 @@ static uint32_t op_entry_add_path(const char *key, const char *plugin, const cha
open_plugin_get_hash(op_entry.path, &hash);
op_entry.hash = hash;
-
+ op_entry_set_checksum();
rb->write(fd_tmp, &op_entry, op_entry_sz); /* add new entry first */
}
else
{
if (op_entry.lang_id != LANG_SHORTCUTS)
- rb->splashf(HZ / 2, rb->str(LANG_OPEN_PLUGIN_NOT_A_PLUGIN), pos);
+ rb->splashf(HZ * 2, rb->str(LANG_OPEN_PLUGIN_NOT_A_PLUGIN), pos);
return 0;
}
}
@@ -384,6 +412,7 @@ static uint32_t op_entry_add_path(const char *key, const char *plugin, const cha
{
rb->close(fd_tmp);
rb->close(fd_dat);
+ fd_dat = -1;
rb->remove(OPEN_PLUGIN_DAT);
rb->rename(OPEN_PLUGIN_DAT ".tmp", OPEN_PLUGIN_DAT);
}
@@ -440,12 +469,12 @@ static void op_entry_remove(int selection)
static void op_entry_remove_empty(void)
{
bool resave = false;
- if (fd_dat && rb->lseek(fd_dat, 0, SEEK_SET) == 0)
+ if (fd_dat >= 0 && rb->lseek(fd_dat, 0, SEEK_SET) == 0)
{
while (resave == false &&
rb->read(fd_dat, &op_entry, op_entry_sz) == op_entry_sz)
{
- if (op_entry.hash == 0)
+ if (op_entry.hash == 0 || !op_entry_checksum())
resave = true;
}
}
@@ -461,6 +490,7 @@ static void op_entry_remove_empty(void)
{
rb->close(fd_tmp);
rb->close(fd_dat);
+ fd_dat = -1;
rb->remove(OPEN_PLUGIN_DAT);
rb->rename(OPEN_PLUGIN_DAT ".tmp", OPEN_PLUGIN_DAT);
}
@@ -495,7 +525,7 @@ static const char* list_get_name_cb(int selected_item, void* data,
char* buf, size_t buf_len)
{
/*TODO memoize names so we don't keep reading the disk when not necessary */
- if (data == &MENU_ID_MAIN)
+ if (data == (void*) &MENU_ID_MAIN) /* check address */
{
if (op_entry_read_name(fd_dat, selected_item))
{
@@ -549,7 +579,7 @@ static const char* list_get_name_cb(int selected_item, void* data,
static int list_voice_cb(int list_index, void* data)
{
- if (data == &MENU_ID_MAIN)
+ if (data == (void*) &MENU_ID_MAIN) /* check address */
{
if (op_entry_read_name(fd_dat, list_index))
{
@@ -602,7 +632,6 @@ static void synclist_set(char* menu_id, int selection, int items, int sel_size)
rb->gui_synclist_set_icon_callback(&lists,NULL);
rb->gui_synclist_set_voice_callback(&lists, list_voice_cb);
rb->gui_synclist_set_nb_items(&lists,items);
- rb->gui_synclist_limit_scroll(&lists,true);
rb->gui_synclist_select_item(&lists, selection);
list_voice_cb(selection, menu_id);
}
@@ -661,7 +690,7 @@ static void edit_menu(int selection)
{
action = rb->get_action(CONTEXT_LIST,TIMEOUT_BLOCK);
- if (rb->gui_synclist_do_button(&lists,&action,LIST_WRAP_UNLESS_HELD))
+ if (rb->gui_synclist_do_button(&lists, &action))
continue;
selected_item = rb->gui_synclist_get_sel_pos(&lists);
switch (action)
@@ -819,6 +848,12 @@ reopen_datfile:
}/* OP_EXT */
}
+ for (int i = items - 1; i > 0 && !exit; i--)
+ {
+ if (!op_entry_read(fd_dat, i, op_entry_sz))
+ items--;
+ }
+
if (items < 1 && !exit)
{
char* cur_filename = rb->plugin_get_current_filename();
@@ -840,11 +875,11 @@ reopen_datfile:
synclist_set(MENU_ID_MAIN, selection, items, 1);
rb->gui_synclist_draw(&lists);
- while (!exit)
+ while (!exit && fd_dat >= 0)
{
action = rb->get_action(CONTEXT_LIST,TIMEOUT_BLOCK);
- if (rb->gui_synclist_do_button(&lists,&action,LIST_WRAP_UNLESS_HELD))
+ if (rb->gui_synclist_do_button(&lists, &action))
continue;
selection = rb->gui_synclist_get_sel_pos(&lists);
switch (action)
@@ -871,6 +906,7 @@ reopen_datfile:
}
break;
case ACTION_STD_CANCEL:
+ case ACTION_STD_MENU:
{
selection = -2;
exit = true;
diff --git a/apps/plugins/oscilloscope.c b/apps/plugins/oscilloscope.c
index 881295d6ab..00d03fb03e 100644
--- a/apps/plugins/oscilloscope.c
+++ b/apps/plugins/oscilloscope.c
@@ -47,12 +47,14 @@
#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || (CONFIG_KEYPAD == IPOD_3G_PAD) || \
(CONFIG_KEYPAD == IPOD_1G2G_PAD)
-#define OSCILLOSCOPE_QUIT (BUTTON_SELECT | BUTTON_MENU)
-#define OSCILLOSCOPE_DRAWMODE (BUTTON_SELECT | BUTTON_PLAY)
+#define OSCILLOSCOPE_QUIT BUTTON_MENU
+#define OSCILLOSCOPE_GRAPHMODE_PRE BUTTON_SELECT
+#define OSCILLOSCOPE_GRAPHMODE (BUTTON_SELECT | BUTTON_REL)
+#define OSCILLOSCOPE_DRAWMODE_PRE BUTTON_SELECT
+#define OSCILLOSCOPE_DRAWMODE (BUTTON_SELECT | BUTTON_REPEAT)
#define OSCILLOSCOPE_ADVMODE (BUTTON_SELECT | BUTTON_RIGHT)
#define OSCILLOSCOPE_ORIENTATION (BUTTON_SELECT | BUTTON_LEFT)
-#define OSCILLOSCOPE_GRAPHMODE BUTTON_MENU
-#define OSCILLOSCOPE_PAUSE BUTTON_PLAY
+#define OSCILLOSCOPE_PAUSE BUTTON_PLAY | BUTTON_REL
#define OSCILLOSCOPE_SPEED_UP BUTTON_RIGHT
#define OSCILLOSCOPE_SPEED_DOWN BUTTON_LEFT
#define OSCILLOSCOPE_VOL_UP BUTTON_SCROLL_FWD
@@ -548,6 +550,9 @@
#define OSCILLOSCOPE_VOL_UP BUTTON_VOL_UP
#define OSCILLOSCOPE_VOL_DOWN BUTTON_VOL_DOWN
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+/* use touchscreen */
+
#else
#error No keymap defined!
#endif
@@ -1936,10 +1941,9 @@ static void osc_cleanup(void)
rb->lcd_set_foreground(LCD_DEFAULT_FG);
rb->lcd_set_background(LCD_DEFAULT_BG);
#endif
-#ifdef HAVE_BACKLIGHT
+
/* Turn on backlight timeout (revert to settings) */
backlight_use_settings();
-#endif
/* save settings if changed */
if (rb->memcmp(&osc, &osc_disk, sizeof(osc)))
@@ -1972,10 +1976,9 @@ static void osc_setup(void)
mixer_sampr = rb->mixer_get_frequency();
#endif
-#ifdef HAVE_BACKLIGHT
/* Turn off backlight timeout */
backlight_ignore_timeout();
-#endif
+
graphmode_setup();
}
diff --git a/apps/plugins/otp.c b/apps/plugins/otp.c
index 4d302563fb..356e1e5eb6 100644
--- a/apps/plugins/otp.c
+++ b/apps/plugins/otp.c
@@ -208,17 +208,16 @@ static int base32_encode(const uint8_t *data, int length, uint8_t *result,
static bool browse( char *dst, int dst_size, const char *start )
{
- struct browse_context browse;
-
- rb->browse_context_init(&browse, SHOW_ALL,
- BROWSE_SELECTONLY|BROWSE_NO_CONTEXT_MENU,
- NULL, NOICON, start, NULL);
-
- browse.buf = dst;
- browse.bufsize = dst_size;
+ struct browse_context browse = {
+ .dirfilter = SHOW_ALL,
+ .flags = BROWSE_SELECTONLY | BROWSE_NO_CONTEXT_MENU,
+ .icon = Icon_NOICON,
+ .root = start,
+ .buf = dst,
+ .bufsize = dst_size,
+ };
rb->rockbox_browse(&browse);
-
return (browse.flags & BROWSE_SELECTED);
}
diff --git a/apps/plugins/pacbox/pacbox.c b/apps/plugins/pacbox/pacbox.c
index 71c9751cad..b306503862 100755
--- a/apps/plugins/pacbox/pacbox.c
+++ b/apps/plugins/pacbox/pacbox.c
@@ -266,7 +266,7 @@ static bool pacbox_menu(void)
{
case PBMI_DIFFICULTY:
new_setting=settings.difficulty;
- rb->set_option("Difficulty", &new_setting, INT,
+ rb->set_option("Difficulty", &new_setting, RB_INT,
difficulty_options , 2, NULL);
if (new_setting != settings.difficulty) {
settings.difficulty=new_setting;
@@ -275,7 +275,7 @@ static bool pacbox_menu(void)
break;
case PBMI_PACMEN_PER_GAME:
new_setting=settings.numlives;
- rb->set_option("Pacmen Per Game", &new_setting, INT,
+ rb->set_option("Pacmen Per Game", &new_setting, RB_INT,
numlives_options , 4, NULL);
if (new_setting != settings.numlives) {
settings.numlives=new_setting;
@@ -284,7 +284,7 @@ static bool pacbox_menu(void)
break;
case PBMI_BONUS_LIFE:
new_setting=settings.bonus;
- rb->set_option("Bonus Life", &new_setting, INT,
+ rb->set_option("Bonus Life", &new_setting, RB_INT,
bonus_options , 4, NULL);
if (new_setting != settings.bonus) {
settings.bonus=new_setting;
@@ -293,7 +293,7 @@ static bool pacbox_menu(void)
break;
case PBMI_GHOST_NAMES:
new_setting=settings.ghostnames;
- rb->set_option("Ghost Names", &new_setting, INT,
+ rb->set_option("Ghost Names", &new_setting, RB_INT,
ghostname_options , 2, NULL);
if (new_setting != settings.ghostnames) {
settings.ghostnames=new_setting;
@@ -301,16 +301,16 @@ static bool pacbox_menu(void)
}
break;
case PBMI_DISPLAY_FPS:
- rb->set_option("Display FPS",&settings.showfps,INT,
+ rb->set_option("Display FPS",&settings.showfps, RB_INT,
noyes, 2, NULL);
break;
case PBMI_SOUND:
- rb->set_option("Sound",&settings.sound, INT,
+ rb->set_option("Sound",&settings.sound, RB_INT,
noyes, 2, NULL);
break;
#ifdef AI
case PBMI_AI:
- rb->set_option("AI",&settings.ai, INT,
+ rb->set_option("AI",&settings.ai, RB_INT,
noyes, 2, NULL);
break;
#endif
@@ -809,8 +809,8 @@ enum plugin_status plugin_start(const void* parameter)
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
rb->cpu_boost(false);
#endif
-#ifdef HAVE_BACKLIGHT
+
backlight_use_settings();
-#endif
+
return PLUGIN_OK;
}
diff --git a/apps/plugins/pacbox/pacbox.h b/apps/plugins/pacbox/pacbox.h
index a3d42b099c..aa8c41fa95 100644
--- a/apps/plugins/pacbox/pacbox.h
+++ b/apps/plugins/pacbox/pacbox.h
@@ -398,6 +398,16 @@
#define PACMAN_1UP BUTTON_VOL_UP
#define PACMAN_COIN BUTTON_PLAY
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+#define PACMAN_UP BUTTON_TOPMIDDLE
+#define PACMAN_DOWN BUTTON_BOTTOMMIDDLE
+#define PACMAN_LEFT BUTTON_MIDLEFT
+#define PACMAN_RIGHT BUTTON_MIDRIGHT
+#define PACMAN_MENU BUTTON_TOPLEFT
+#define PACMAN_1UP BUTTON_BOTTOMLEFT
+#define PACMAN_2UP BUTTON_BOTTOMRIGHT
+#define PACMAN_COIN BUTTON_CENTER
+
#else
#error Keymap not defined!
diff --git a/apps/plugins/pdbox/PDa/src/m_glob.c b/apps/plugins/pdbox/PDa/src/m_glob.c
index 9a036a984e..43f7ca8510 100644
--- a/apps/plugins/pdbox/PDa/src/m_glob.c
+++ b/apps/plugins/pdbox/PDa/src/m_glob.c
@@ -16,7 +16,7 @@ void glob_quit(void *dummy);
void glob_dsp(void *dummy, t_symbol *s, int argc, t_atom *argv);
void glob_meters(void *dummy, t_floatarg f);
void glob_key(void *dummy, t_symbol *s, int ac, t_atom *av);
-void glob_audiostatus(void *dummy);
+void glob_audiostatus(void);
void glob_finderror(t_pd *dummy);
void glob_audio_properties(t_pd *dummy, t_floatarg flongform);
void glob_audio_dialog(t_pd *dummy, t_symbol *s, int argc, t_atom *argv);
diff --git a/apps/plugins/pdbox/PDa/src/m_obj.c b/apps/plugins/pdbox/PDa/src/m_obj.c
index d06caa12fd..8d4947f202 100644
--- a/apps/plugins/pdbox/PDa/src/m_obj.c
+++ b/apps/plugins/pdbox/PDa/src/m_obj.c
@@ -272,6 +272,9 @@ static int outlet_eventno;
recursion */
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Warray-bounds"
+#if __GNUC__ >= 13
+#pragma GCC diagnostic ignored "-Wdangling-pointer"
+#endif
void outlet_setstacklim(void)
{
char c;
diff --git a/apps/plugins/pdbox/pdbox.c b/apps/plugins/pdbox/pdbox.c
index f21913788b..08236bfeba 100644
--- a/apps/plugins/pdbox/pdbox.c
+++ b/apps/plugins/pdbox/pdbox.c
@@ -229,7 +229,7 @@ enum plugin_status plugin_start(const void* parameter)
0, /* FIXME Which flags? */
"PD core"
IF_PRIO(, PRIORITY_REALTIME)
- IF_COP(, COP));
+ IF_COP(, CPU));
gui_thread_id =
rb->create_thread(&gui_thread,
diff --git a/apps/plugins/pegbox.c b/apps/plugins/pegbox.c
index f089c38023..8b88aad052 100644
--- a/apps/plugins/pegbox.c
+++ b/apps/plugins/pegbox.c
@@ -65,7 +65,7 @@
(CONFIG_KEYPAD == IPOD_3G_PAD) || \
(CONFIG_KEYPAD == IPOD_1G2G_PAD)
#define PEGBOX_SELECT (BUTTON_SELECT|BUTTON_RIGHT)
-#define PEGBOX_QUIT (BUTTON_SELECT|BUTTON_PLAY)
+#define PEGBOX_QUIT (BUTTON_SELECT|BUTTON_REPEAT)
#define PEGBOX_RESTART (BUTTON_SELECT|BUTTON_LEFT)
#define PEGBOX_LVL_UP (BUTTON_SELECT|BUTTON_MENU)
#define PEGBOX_UP BUTTON_MENU
@@ -74,7 +74,7 @@
#define PEGBOX_LEFT BUTTON_LEFT
#define SELECT_TEXT "SELECT+RIGHT"
-#define QUIT_TEXT "SELECT+PLAY"
+#define QUIT_TEXT "Long SELECT"
#define RESTART_TEXT "SELECT+LEFT"
#define LVL_UP_TEXT "SELECT+MENU"
#define LVL_DOWN_TEXT "-"
@@ -711,6 +711,9 @@ CONFIG_KEYPAD == MROBE500_PAD
#define LVL_UP_TEXT "VOL+"
#define LVL_DOWN_TEXT "VOL-"
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+/* use touchscreen */
+
#else
#error "Unsupported keymap!"
#endif
diff --git a/apps/plugins/periodic_table.c b/apps/plugins/periodic_table.c
index b77dd07432..2dd84baacd 100644
--- a/apps/plugins/periodic_table.c
+++ b/apps/plugins/periodic_table.c
@@ -619,7 +619,6 @@ enum plugin_status plugin_start(const void* parameter)
switch (button)
{
case PERIODIC_KEY_SELECT:
- break;
case PERIODIC_KEY_MENU:
return PLUGIN_OK;
break;
diff --git a/apps/plugins/pictureflow/pictureflow.c b/apps/plugins/pictureflow/pictureflow.c
index 9731d92d35..87ad1a403f 100644
--- a/apps/plugins/pictureflow/pictureflow.c
+++ b/apps/plugins/pictureflow/pictureflow.c
@@ -33,6 +33,7 @@
#include "lib/grey.h"
#include "lib/mylcd.h"
#include "lib/feature_wrappers.h"
+#include "lib/id3.h"
/******************************* Globals ***********************************/
static fb_data *lcd_fb;
@@ -44,6 +45,7 @@ static fb_data *lcd_fb;
#if PF_PLAYBACK_CAPABLE
#include "lib/playback_control.h"
+#include "lib/mul_id3.h"
#endif
#define PF_PREV ACTION_STD_PREV
@@ -55,14 +57,23 @@ static fb_data *lcd_fb;
#define PF_BACK ACTION_STD_CANCEL
#define PF_MENU ACTION_STD_MENU
#define PF_WPS ACTION_TREE_WPS
+#define PF_JMP ACTION_LISTTREE_PGDOWN
+#define PF_JMP_PREV ACTION_LISTTREE_PGUP
#define PF_QUIT (LAST_ACTION_PLACEHOLDER + 1)
#define PF_TRACKLIST (LAST_ACTION_PLACEHOLDER + 2)
+#define PF_SORTING_NEXT (LAST_ACTION_PLACEHOLDER + 3)
+#define PF_SORTING_PREV (LAST_ACTION_PLACEHOLDER + 4)
#if defined(HAVE_SCROLLWHEEL) || CONFIG_KEYPAD == IRIVER_H10_PAD || \
CONFIG_KEYPAD == MPIO_HD300_PAD
+#if (CONFIG_KEYPAD != IPOD_1G2G_PAD) \
+ && (CONFIG_KEYPAD != IPOD_3G_PAD) \
+ && (CONFIG_KEYPAD != IPOD_4G_PAD) \
+ && (CONFIG_KEYPAD != FIIO_M3K_PAD)
#define USE_CORE_PREVNEXT
#endif
+#endif
#ifndef USE_CORE_PREVNEXT
/* scrollwheel targets use the wheel, just as they do in lists,
@@ -81,6 +92,18 @@ const struct button_mapping pf_context_album_scroll[] =
{PF_PREV_REPEAT, BUTTON_RC_REW|BUTTON_REPEAT,BUTTON_NONE},
{PF_NEXT, BUTTON_RC_FF, BUTTON_NONE},
{PF_NEXT_REPEAT, BUTTON_RC_FF|BUTTON_REPEAT, BUTTON_NONE},
+#elif (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_3G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_4G_PAD) \
+ || (CONFIG_KEYPAD == FIIO_M3K_PAD)
+ {PF_JMP_PREV, BUTTON_LEFT, BUTTON_NONE},
+ {PF_JMP_PREV, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE},
+ {PF_JMP, BUTTON_RIGHT, BUTTON_NONE},
+ {PF_JMP, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE},
+ {ACTION_NONE, BUTTON_LEFT|BUTTON_REL, BUTTON_LEFT},
+ {ACTION_NONE, BUTTON_RIGHT|BUTTON_REL, BUTTON_RIGHT},
+ {ACTION_NONE, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_LEFT},
+ {ACTION_NONE, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_RIGHT},
#elif defined(BUTTON_LEFT) && defined(BUTTON_RIGHT)
{PF_PREV, BUTTON_LEFT, BUTTON_NONE},
{PF_PREV_REPEAT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE},
@@ -130,14 +153,18 @@ const struct button_mapping pf_context_buttons[] =
{PF_QUIT, BUTTON_POWER, BUTTON_NONE},
#elif (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
|| (CONFIG_KEYPAD == IPOD_3G_PAD) \
- || (CONFIG_KEYPAD == IPOD_4G_PAD) \
- || (CONFIG_KEYPAD == MPIO_HD300_PAD)
+ || (CONFIG_KEYPAD == IPOD_4G_PAD)
+ {PF_MENU, BUTTON_MENU|BUTTON_REPEAT, BUTTON_MENU},
+ {PF_QUIT, BUTTON_MENU|BUTTON_REL, BUTTON_MENU},
+ {PF_SORTING_NEXT, BUTTON_SELECT|BUTTON_MENU, BUTTON_NONE},
+ {PF_SORTING_PREV, BUTTON_SELECT|BUTTON_PLAY, BUTTON_NONE},
+#elif CONFIG_KEYPAD == MPIO_HD300_PAD
{PF_QUIT, BUTTON_MENU|BUTTON_REPEAT, BUTTON_MENU},
#elif CONFIG_KEYPAD == IAUDIO_M3_PAD
{PF_QUIT, BUTTON_RC_REC, BUTTON_NONE},
#elif CONFIG_KEYPAD == MEIZU_M6SL_PAD
{PF_QUIT, BUTTON_MENU|BUTTON_REPEAT, BUTTON_MENU},
-#elif CONFIG_KEYPAD == IRIVER_H100_PAD || CONFIG_KEYPAD == IRIVER_H300_PAD
+#elif CONFIG_KEYPAD == IRIVER_H100_PAD || CONFIG_KEYPAD == IRIVER_H300_PAD
{PF_QUIT, BUTTON_OFF, BUTTON_NONE},
#elif CONFIG_KEYPAD == PBELL_VIBE500_PAD
{PF_QUIT, BUTTON_REC, BUTTON_NONE},
@@ -149,11 +176,13 @@ const struct button_mapping pf_context_buttons[] =
{PF_TRACKLIST, BUTTON_FFWD|BUTTON_REL, BUTTON_FFWD},
{PF_WPS, BUTTON_PLAY|BUTTON_REPEAT, BUTTON_PLAY},
#elif CONFIG_KEYPAD == FIIO_M3K_PAD
- {PF_PREV, BUTTON_LEFT, BUTTON_NONE},
- {PF_PREV_REPEAT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE},
- {PF_NEXT, BUTTON_RIGHT, BUTTON_NONE},
- {PF_NEXT_REPEAT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE},
+ {PF_JMP_PREV, BUTTON_LEFT, BUTTON_NONE},
+ {PF_JMP_PREV, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE},
+ {PF_JMP, BUTTON_RIGHT, BUTTON_NONE},
+ {PF_JMP, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE},
{PF_MENU, BUTTON_POWER|BUTTON_REL, BUTTON_POWER},
+ {PF_SORTING_NEXT, BUTTON_VOL_UP, BUTTON_NONE},
+ {PF_SORTING_PREV, BUTTON_VOL_DOWN, BUTTON_NONE},
{PF_QUIT, BUTTON_POWER|BUTTON_REPEAT, BUTTON_POWER},
{PF_CONTEXT, BUTTON_MENU|BUTTON_REL, BUTTON_MENU},
{PF_TRACKLIST, BUTTON_MENU|BUTTON_REPEAT, BUTTON_MENU},
@@ -245,7 +274,6 @@ typedef fb_data pix_t;
/* some magic numbers for cache_version. */
#define CACHE_REBUILD 0
-#define CACHE_UPDATE 1
/* Error return values */
#define SUCCESS 0
@@ -255,7 +283,7 @@ typedef fb_data pix_t;
#define ERROR_USER_ABORT -4
/* current version for cover cache */
-#define CACHE_VERSION 3
+#define CACHE_VERSION 4
#define CONFIG_VERSION 1
#define CONFIG_FILE "pictureflow.cfg"
#define INDEX_HDR "PFID"
@@ -277,8 +305,14 @@ struct pf_config_t
int cache_version;
int show_album_name;
+ int sort_albums_by;
+ int year_sort_order;
+ bool show_year;
+
bool resize;
bool show_fps;
+
+ bool update_albumart;
};
struct pf_index_t {
@@ -311,6 +345,7 @@ struct pf_track_t {
int list_y;
int list_h;
size_t borrowed;
+ size_t used;
struct track_data *index;
char *names;
};
@@ -344,6 +379,7 @@ struct slide_cache {
struct album_data {
int name_idx; /* offset to the album name */
int artist_idx; /* offset to the artist name */
+ int year; /* album year */
long artist_seek; /* artist taglist position */
long seek; /* album taglist position */
};
@@ -427,6 +463,31 @@ static char* show_album_name_conf[] =
"both bottom",
};
+enum sort_albums_by_values {
+ SORT_BY_ARTIST_AND_NAME = 0,
+ SORT_BY_ARTIST_AND_YEAR,
+ SORT_BY_YEAR,
+ SORT_BY_NAME,
+
+ SORT_VALUES_SIZE
+};
+static char* sort_albums_by_conf[] =
+{
+ "artist + name",
+ "artist + year",
+ "year",
+ "name"
+};
+enum year_sort_order_values {
+ ASCENDING = 0,
+ DESCENDING
+};
+static char* year_sort_order_conf[] =
+{
+ "ascending",
+ "descending"
+};
+
#define MAX_SPACING 40
#define MAX_MARGIN 80
@@ -451,6 +512,13 @@ static struct configdata config[] =
{ TYPE_INT, 0, 999999, { .int_p = &pf_cfg.last_album }, "last album", NULL },
{ TYPE_INT, 0, 1, { .int_p = &pf_cfg.backlight_mode }, "backlight", NULL },
{ TYPE_INT, 0, 999999, { .int_p = &aa_cache.idx }, "art cache pos", NULL },
+ { TYPE_INT, 0, 999999, { .int_p = &aa_cache.inspected }, "art cache inspected", NULL },
+ { TYPE_ENUM, 0, 4, { .int_p = &pf_cfg.sort_albums_by }, "sort albums by",
+ sort_albums_by_conf },
+ { TYPE_ENUM, 0, 2, { .int_p = &pf_cfg.year_sort_order }, "year order",
+ year_sort_order_conf },
+ { TYPE_BOOL, 0, 1, { .bool_p = &pf_cfg.show_year }, "show year", NULL },
+ { TYPE_BOOL, 0, 1, { .bool_p = &pf_cfg.update_albumart }, "update albumart", NULL }
};
#define CONFIG_NUM_ITEMS (sizeof(config) / sizeof(struct configdata))
@@ -471,6 +539,8 @@ static int itilt;
static PFreal offsetX;
static PFreal offsetY;
static int number_of_slides;
+static bool is_initial_slide = true;
+static bool show_tracks_while_browsing = false;
static struct pf_slide_cache pf_sldcache;
@@ -491,9 +561,20 @@ static struct pf_index_t pf_idx;
static struct pf_track_t pf_tracks;
+static struct mp3entry id3;
+
void reset_track_list(void);
static bool thread_is_running;
+static bool wants_to_quit = false;
+
+/*
+ Prevent picture loading thread from allocating
+ buflib memory while the main thread may be
+ performing buffer-shifting operations.
+*/
+static struct mutex buf_ctx_mutex;
+static bool buf_ctx_locked = false;
static int cover_animation_keyframe;
static int extra_fade;
@@ -528,6 +609,12 @@ enum pf_states {
static int pf_state;
+#if PF_PLAYBACK_CAPABLE
+static bool insert_whole_album;
+static bool old_shuffle = false;
+static int old_playlist = -1;
+#endif
+
/** code */
static bool free_slide_prio(int prio);
bool load_new_slide(void);
@@ -536,7 +623,19 @@ static void draw_progressbar(int step, int count, char *msg);
static void draw_splashscreen(unsigned char * buf_tmp, size_t buf_tmp_size);
static void free_all_slide_prio(int prio);
-static bool check_database(bool prompt)
+static inline void buf_ctx_lock(void)
+{
+ rb->mutex_lock(&buf_ctx_mutex);
+ buf_ctx_locked = true;
+}
+
+static inline void buf_ctx_unlock(void)
+{
+ rb->mutex_unlock(&buf_ctx_mutex);
+ buf_ctx_locked = false;
+}
+
+static bool check_database(void)
{
bool needwarn = true;
int spin = 5;
@@ -554,9 +653,7 @@ static bool check_database(bool prompt)
needwarn = false;
rb->splash(0, ID2P(LANG_TAGCACHE_BUSY));
}
- else if (!prompt)
- return false;
- else if (rb->action_userabort(HZ/5))
+ else
return false;
rb->yield();
@@ -571,7 +668,7 @@ static bool confirm_quit(void)
{ (const char*[]) {"Quit?", "Progress will be lost"}, 2};
enum yesno_res response = rb->gui_syncyesno_run(&prompt, NULL, NULL);
while (rb->button_get(false) == BUTTON_NONE)
- ;;
+ {;;}
if(response == YESNO_NO)
return false;
@@ -579,9 +676,10 @@ static bool confirm_quit(void)
return true;
}
-static void config_save(int cache_version)
+static void config_save(int cache_version, bool update_albumart)
{
pf_cfg.cache_version = cache_version;
+ pf_cfg.update_albumart = update_albumart;
configfile_save(CONFIG_FILE, config, CONFIG_NUM_ITEMS, CONFIG_VERSION);
}
@@ -596,9 +694,13 @@ static void config_set_defaults(struct pf_config_t *cfg)
cfg->last_album = 0;
cfg->backlight_mode = 0;
cfg->resize = true;
- cfg->cache_version = 0;
+ cfg->cache_version = CACHE_REBUILD;
cfg->show_album_name = (LCD_HEIGHT > 100)
- ? ALBUM_NAME_TOP : ALBUM_NAME_BOTTOM;
+ ? ALBUM_AND_ARTIST_BOTTOM : ALBUM_NAME_BOTTOM;
+ cfg->sort_albums_by = SORT_BY_ARTIST_AND_NAME;
+ cfg->year_sort_order = ASCENDING;
+ cfg->show_year = false;
+ cfg->update_albumart = false;
}
static inline PFreal fmul(PFreal a, PFreal b)
@@ -868,6 +970,7 @@ const struct custom_format format_transposed = {
static const struct button_mapping* get_context_map(int context)
{
+ context &= ~CONTEXT_LOCKED;
return pf_contexts[context & ~CONTEXT_PLUGIN];
}
@@ -949,6 +1052,51 @@ static void init_reflect_table(void)
(5 * REFLECT_HEIGHT);
}
+
+static int compare_albums (const void *a_v, const void *b_v)
+{
+ uint32_t artist_a = ((struct album_data *)a_v)->artist_idx;
+ uint32_t artist_b = ((struct album_data *)b_v)->artist_idx;
+
+ uint32_t album_a = ((struct album_data *)a_v)->name_idx;
+ uint32_t album_b = ((struct album_data *)b_v)->name_idx;
+
+ int year_a = ((struct album_data *)a_v)->year;
+ int year_b = ((struct album_data *)b_v)->year;
+
+ switch (pf_cfg.sort_albums_by)
+ {
+ case SORT_BY_ARTIST_AND_NAME:
+ if (artist_a - artist_b == 0)
+ return (int)(album_a - album_b);
+ break;
+ case SORT_BY_ARTIST_AND_YEAR:
+ if (artist_a - artist_b == 0)
+ {
+ if (pf_cfg.year_sort_order == ASCENDING)
+ return year_a - year_b;
+ else
+ return year_b - year_a;
+ }
+ break;
+ case SORT_BY_YEAR:
+ if (year_a - year_b != 0)
+ {
+ if (pf_cfg.year_sort_order == ASCENDING)
+ return year_a - year_b;
+ else
+ return year_b - year_a;
+ }
+ break;
+ case SORT_BY_NAME:
+ if (album_a - album_b != 0)
+ return (int)(album_a - album_b);
+ break;
+ }
+
+ return (int)(artist_a - artist_b);
+}
+
static int compare_album_artists (const void *a_v, const void *b_v)
{
uint32_t a = ((struct album_data *)a_v)->artist_idx;
@@ -963,6 +1111,7 @@ static void write_album_index(int idx, int name_idx,
pf_idx.album_index[idx].seek = album_seek;
pf_idx.album_index[idx].artist_idx = artist_idx;
pf_idx.album_index[idx].artist_seek = artist_seek;
+ pf_idx.album_index[idx].year = 0;
}
static inline void write_album_entry(struct tagcache_search *tcs,
@@ -992,6 +1141,8 @@ static void write_artist_entry(struct tagcache_search *tcs,
static int get_tcs_search_res(int type, struct tagcache_search *tcs,
void **buf, size_t *bufsz)
{
+ char tcs_buf[TAGCACHE_BUFSZ];
+ const long tcs_bufsz = sizeof(tcs_buf);
int ret = SUCCESS;
unsigned int l, name_idx = 0;
void (*writefn)(struct tagcache_search *, int, unsigned int);
@@ -1007,7 +1158,7 @@ static int get_tcs_search_res(int type, struct tagcache_search *tcs,
data_size = sizeof(struct album_data);
}
- while (rb->tagcache_get_next(tcs))
+ while (rb->tagcache_get_next(tcs, tcs_buf, tcs_bufsz))
{
if (rb->button_get(false) > BUTTON_NONE)
{
@@ -1047,6 +1198,8 @@ static int get_tcs_search_res(int type, struct tagcache_search *tcs,
static int create_album_untagged(struct tagcache_search *tcs,
void **buf, size_t *bufsz)
{
+ static char tcs_buf[TAGCACHE_BUFSZ];
+ const long tcs_bufsz = sizeof(tcs_buf);
int ret = SUCCESS;
int album_count = pf_idx.album_ct; /* store existing count */
int total_count = pf_idx.album_ct + pf_idx.artist_ct * 2;
@@ -1061,7 +1214,7 @@ static int create_album_untagged(struct tagcache_search *tcs,
{
rb->tagcache_search_add_filter(tcs, tag_album, pf_idx.album_untagged_seek);
- while (rb->tagcache_get_next(tcs))
+ while (rb->tagcache_get_next(tcs, tcs_buf, tcs_bufsz))
{
if (rb->button_get(false) > BUTTON_NONE) {
if (confirm_quit())
@@ -1166,9 +1319,8 @@ static int build_artist_index(struct tagcache_search *tcs,
if (res < SUCCESS)
return res;
- ALIGN_BUFFER(*buf, *bufsz, 4);
-
/* finalize the artist index */
+ ALIGN_BUFFER(*buf, *bufsz, alignof(struct artist_data));
tmp_artist = (struct artist_data*)*buf;
for (i = pf_idx.artist_ct - 1; i >= 0; i--)
tmp_artist[i] = pf_idx.artist_index[-i];
@@ -1176,7 +1328,6 @@ static int build_artist_index(struct tagcache_search *tcs,
pf_idx.artist_index = tmp_artist;
/* move buf ptr to end of artist_index */
*buf = pf_idx.artist_index + pf_idx.artist_ct;
- ALIGN_BUFFER(*buf, *bufsz, 4);
if (res == SUCCESS)
{
@@ -1190,12 +1341,59 @@ static int build_artist_index(struct tagcache_search *tcs,
}
+static int assign_album_year(void)
+{
+ char tcs_buf[TAGCACHE_BUFSZ];
+ const long tcs_bufsz = sizeof(tcs_buf);
+ draw_progressbar(0, pf_idx.album_ct, "Assigning Album Year");
+ for (int album_idx = 0; album_idx < pf_idx.album_ct; album_idx++)
+ {
+ /* Prevent idle poweroff */
+ rb->reset_poweroff_timer();
+
+ if (rb->button_get(false) > BUTTON_NONE)
+ {
+ if (confirm_quit())
+ return ERROR_USER_ABORT;
+ else
+ {
+ rb->lcd_clear_display();
+ draw_progressbar(album_idx, pf_idx.album_ct, "Assigning Album Year");
+ }
+ }
+ draw_progressbar(album_idx, pf_idx.album_ct, NULL);
+ int album_year = 0;
+
+ if (rb->tagcache_search(&tcs, tag_year))
+ {
+ rb->tagcache_search_add_filter(&tcs, tag_album,
+ pf_idx.album_index[album_idx].seek);
+
+ if (pf_idx.album_index[album_idx].artist_idx >= 0)
+ rb->tagcache_search_add_filter(&tcs, tag_albumartist,
+ pf_idx.album_index[album_idx].artist_seek);
+
+ while (rb->tagcache_get_next(&tcs, tcs_buf, tcs_bufsz)) {
+ int track_year = rb->tagcache_get_numeric(&tcs, tag_year);
+ if (track_year > album_year)
+ album_year = track_year;
+ }
+ }
+ rb->tagcache_search_finish(&tcs);
+
+ pf_idx.album_index[album_idx].year = album_year;
+ }
+ return SUCCESS;
+}
+
/**
Create an index of all artists and albums from the database.
Also store the artists and album names so we can access them later.
*/
static int create_album_index(void)
{
+ static char tcs_buf[TAGCACHE_BUFSZ];
+ const long tcs_bufsz = sizeof(tcs_buf);
void *buf = pf_idx.buf;
size_t buf_size = pf_idx.buf_sz;
@@ -1204,14 +1402,14 @@ static int create_album_index(void)
int i, j, last, final, retry, res;
draw_splashscreen(buf, buf_size);
- ALIGN_BUFFER(buf, buf_size, 4);
+ ALIGN_BUFFER(buf, buf_size, sizeof(long));
-/* Artists */
+ /* Artists */
res = build_artist_index(&tcs, &buf, &buf_size);
if (res < SUCCESS)
return res;
-/* Albums */
+ /* Albums */
pf_idx.album_ct = 0;
pf_idx.album_len =0;
pf_idx.album_untagged_idx = 0;
@@ -1227,7 +1425,6 @@ static int create_album_index(void)
rb->tagcache_search_finish(&tcs);
if (res < SUCCESS)
return res;
- ALIGN_BUFFER(buf, buf_size, 4);
/* Build artist list for untagged albums */
res = create_album_untagged(&tcs, &buf, &buf_size);
@@ -1235,9 +1432,8 @@ static int create_album_index(void)
if (res < SUCCESS)
return res;
- ALIGN_BUFFER(buf, buf_size, 4);
-
/* finalize the album index */
+ ALIGN_BUFFER(buf, buf_size, alignof(struct album_data));
tmp_album = (struct album_data*)buf;
for (i = pf_idx.album_ct - 1; i >= 0; i--)
tmp_album[i] = pf_idx.album_index[-i];
@@ -1245,13 +1441,15 @@ static int create_album_index(void)
pf_idx.album_index = tmp_album;
/* move buf ptr to end of album_index */
buf = pf_idx.album_index + pf_idx.album_ct;
- ALIGN_BUFFER(buf, buf_size, 4);
-/* Assign indices */
+ /* Assign indices */
draw_splashscreen(buf, buf_size);
draw_progressbar(0, pf_idx.album_ct, "Assigning Albums");
for (j = 0; j < pf_idx.album_ct; j++)
{
+ /* Prevent idle poweroff */
+ rb->reset_poweroff_timer();
+
if (rb->button_get(false) > BUTTON_NONE)
{
if (confirm_quit())
@@ -1273,7 +1471,7 @@ static int create_album_index(void)
last = 0;
final = pf_idx.artist_ct;
retry = 0;
- if (rb->tagcache_get_next(&tcs))
+ if (rb->tagcache_get_next(&tcs, tcs_buf, tcs_bufsz))
{
retry_artist_lookup:
@@ -1301,6 +1499,14 @@ retry_artist_lookup:
}
rb->tagcache_search_finish(&tcs);
}
+
+ draw_splashscreen(buf, buf_size);
+
+ res = assign_album_year();
+
+ if (res < SUCCESS)
+ return res;
+
/* sort list order to find duplicates */
rb->qsort(pf_idx.album_index, pf_idx.album_ct,
sizeof(struct album_data), compare_album_artists);
@@ -1310,6 +1516,9 @@ retry_artist_lookup:
/* mark duplicate albums for deletion */
for (i = 0; i < pf_idx.album_ct - 1; i++) /* -1 don't check last entry */
{
+ /* Prevent idle poweroff */
+ rb->reset_poweroff_timer();
+
int idxi = pf_idx.album_index[i].artist_idx;
int seeki = pf_idx.album_index[i].seek;
@@ -1347,11 +1556,13 @@ retry_artist_lookup:
}
}
- ALIGN_BUFFER(buf, buf_size, 4);
pf_idx.buf = buf;
pf_idx.buf_sz = buf_size;
pf_idx.artist_index = 0;
+ rb->qsort(pf_idx.album_index, pf_idx.album_ct,
+ sizeof(struct album_data), compare_albums);
+
return (pf_idx.album_ct > 0) ? 0 : ERROR_NO_ALBUMS;
}
@@ -1421,7 +1632,6 @@ static int load_album_index(void){
//rb->lseek(fr, sizeof(data) + 1, SEEK_SET);
/* artist names */
- ALIGN_BUFFER(buf, buf_size, 4);
if (read2buf(fr, buf, data.artist_len) == 0)
goto failure;
@@ -1430,7 +1640,6 @@ static int load_album_index(void){
buf_size -= data.artist_len;
/* album names */
- ALIGN_BUFFER(buf, buf_size, 4);
if (read2buf(fr, buf, data.album_len) == 0)
goto failure;
@@ -1439,7 +1648,7 @@ static int load_album_index(void){
buf_size -= data.album_len;
/* index of album names */
- ALIGN_BUFFER(buf, buf_size, 4);
+ ALIGN_BUFFER(buf, buf_size, alignof(struct album_data));
if (read2buf(fr, buf, album_idx_sz) == 0)
goto failure;
@@ -1465,6 +1674,9 @@ static int load_album_index(void){
pf_idx.buf = buf;
pf_idx.buf_sz = buf_size;
+ rb->qsort(pf_idx.album_index, pf_idx.album_ct,
+ sizeof(struct album_data), compare_albums);
+
return 0;
}
}
@@ -1518,6 +1730,14 @@ static char* get_album_artist(const int slide_index)
}
+static char* get_slide_name(const int slide_index, bool artist)
+{
+ if (artist)
+ return get_album_artist(slide_index);
+
+ return get_album_name(slide_index);
+}
+
/**
Return a pointer to the track name of the active album
create_track_index has to be called first.
@@ -1537,11 +1757,101 @@ static char* get_track_filename(const int track_index)
}
#endif
-static int get_wps_current_index(void)
+
+
+static int jmp_idx_prev(void)
+{
+ if (aa_cache.inspected < pf_idx.album_ct)
+ {
+#ifdef USEGSLIB
+ grey_show(false);
+ rb->lcd_clear_display();
+ rb->lcd_update();
+#endif
+ rb->splash(HZ*2, rb->str(LANG_WAIT_FOR_CACHE));
+#ifdef USEGSLIB
+ grey_show(true);
+#endif
+ return center_index;
+ }
+
+ if (pf_cfg.sort_albums_by == SORT_BY_YEAR)
+ {
+ int current_year = pf_idx.album_index[center_index].year;
+
+ for (int i = center_index - 1; i > 0; i-- )
+ {
+ if(pf_idx.album_index[i].year != current_year)
+ current_year = pf_idx.album_index[i].year;
+ while (i > 0)
+ {
+ if (pf_idx.album_index[i-1].year != current_year)
+ break;
+ i--;
+ }
+ return i;
+ }
+ }
+ else
+ {
+ bool by_artist = pf_cfg.sort_albums_by != SORT_BY_NAME;
+ char *current_selection = get_slide_name(center_index, by_artist);
+
+ for (int i = center_index - 1; i > 0; i-- )
+ {
+ if(rb->strncmp(get_slide_name(i, by_artist), current_selection, 1))
+ current_selection = get_slide_name(i, by_artist);
+ while (i > 0)
+ {
+ if (rb->strncmp(get_slide_name(i-1, by_artist), current_selection, 1))
+ break;
+ i--;
+ }
+ return i;
+ }
+ }
+
+ return 0;
+}
+
+static int jmp_idx_next(void)
+{
+ if (aa_cache.inspected < pf_idx.album_ct)
+ {
+#ifdef USEGSLIB
+ grey_show(false);
+ rb->lcd_clear_display();
+ rb->lcd_update();
+#endif
+ rb->splash(HZ*2, rb->str(LANG_WAIT_FOR_CACHE));
+#ifdef USEGSLIB
+ grey_show(true);
+#endif
+ return center_index;
+ }
+
+ if (pf_cfg.sort_albums_by == SORT_BY_YEAR)
+ {
+ int current_year = pf_idx.album_index[center_index].year;
+ for (int i = center_index + 1; i < pf_idx.album_ct; i++ )
+ if(pf_idx.album_index[i].year != current_year)
+ return i;
+ }
+ else
+ {
+ bool by_artist = pf_cfg.sort_albums_by != SORT_BY_NAME;
+ char *current_selection = get_slide_name(center_index, by_artist);
+ for (int i = center_index + 1; i < pf_idx.album_ct; i++ )
+ if(rb->strncmp(get_slide_name(i, by_artist), current_selection, 1))
+ return i;
+ }
+ return pf_idx.album_ct - 1;
+}
+
+static int id3_get_index(struct mp3entry *id3)
{
char* current_artist = UNTAGGED;
char* current_album = UNTAGGED;
- struct mp3entry *id3 = rb->audio_current_track();
if(id3)
{
@@ -1584,12 +1894,104 @@ static int compare_tracks (const void *a_v, const void *b_v)
return (int)(a - b);
}
+
+
+static bool track_buffer_avail(size_t needed)
+{
+ size_t total_out = 0;
+ size_t out = 0;
+ if (pf_tracks.borrowed == 0 && pf_tracks.used == 0)
+ {
+ pf_tracks.names = rb->buflib_buffer_out(&buf_ctx, &out);
+ pf_tracks.borrowed = out;
+ }
+
+ if (needed <= pf_tracks.borrowed - pf_tracks.used)
+ return true;
+
+ while (needed > (pf_tracks.borrowed + total_out) - pf_tracks.used)
+ {
+ if (!free_slide_prio(0))
+ break;
+ out = 0;
+ rb->buflib_buffer_out(&buf_ctx, &out);
+ total_out += out;
+ }
+ pf_tracks.borrowed += total_out;
+
+ // have to move already stored track_data structs
+ if (pf_tracks.count)
+ {
+ struct track_data *new_tracks = (struct track_data *)(total_out + (uintptr_t)pf_tracks.index);
+ unsigned int bytes = pf_tracks.count * sizeof(struct track_data);
+ rb->memmove(new_tracks, pf_tracks.index, bytes);
+ }
+
+ if (needed > pf_tracks.borrowed - pf_tracks.used)
+ return false;
+
+ return true;
+}
+
+
+static int pf_tcs_retrieve_track_title(int string_index, int disc_num, int track_num)
+{
+ char file_name[MAX_PATH];
+ char *track_title = NULL;
+ int str_len;
+
+ if (rb->strcmp(UNTAGGED, tcs.result) == 0)
+ {
+ /* show filename instead of <untaggged> */
+ if (!rb->tagcache_retrieve(&tcs, tcs.idx_id, tag_virt_basename,
+ file_name, MAX_PATH))
+ return 0;
+ track_title = file_name;
+ }
+
+ if (!track_title)
+ track_title = tcs.result;
+
+ int max_len = rb->strlen(track_title) + 10;
+ if (!track_buffer_avail(max_len))
+ return 0;
+
+ if (track_num > 0)
+ {
+ if (disc_num > 0)
+ str_len = rb->snprintf(pf_tracks.names + string_index, max_len,
+ "%d.%02d: %s", disc_num, track_num, track_title);
+ else
+ str_len = rb->snprintf(pf_tracks.names + string_index, max_len,
+ "%d: %s", track_num, track_title);
+ }
+ else
+ str_len = rb->snprintf(pf_tracks.names + string_index, max_len,
+ "%s", track_title);
+ return str_len;
+}
+
+#if PF_PLAYBACK_CAPABLE
+static int pf_tcs_retrieve_file_name(int fn_idx)
+{
+ if (!track_buffer_avail(MAX_PATH))
+ return 0;
+
+ rb->tagcache_retrieve(&tcs, tcs.idx_id, tag_filename,
+ pf_tracks.names + fn_idx, MAX_PATH);
+
+ return rb->strlen(pf_tracks.names + fn_idx);
+}
+#endif
+
/**
Create the track index of the given slide_index.
*/
static void create_track_index(const int slide_index)
{
- char temp[MAX_PATH + 1];
+ char tcs_buf[TAGCACHE_BUFSZ];
+ const long tcs_bufsz = sizeof(tcs_buf);
+ buf_ctx_lock();
if ( slide_index == pf_tracks.cur_idx )
return;
@@ -1600,124 +2002,48 @@ static void create_track_index(const int slide_index)
pf_idx.album_index[slide_index].seek);
if (pf_idx.album_index[slide_index].artist_idx >= 0)
- {
rb->tagcache_search_add_filter(&tcs, tag_albumartist,
pf_idx.album_index[slide_index].artist_seek);
- }
-
- int string_index = 0, track_num;
- int disc_num;
-
- char* result = NULL;
- size_t out = 0;
+ int string_index = 0;
pf_tracks.count = 0;
- pf_tracks.names = rb->buflib_buffer_out(&buf_ctx, &out);
- pf_tracks.borrowed += out;
- int avail = pf_tracks.borrowed;
- pf_tracks.index = (struct track_data*)(pf_tracks.names + pf_tracks.borrowed);
- while (rb->tagcache_get_next(&tcs))
- {
- result = NULL;
- if (rb->strcmp(UNTAGGED, tcs.result) == 0)
- {
- /* show filename instead of <untaggged> */
- if (!rb->tagcache_retrieve(&tcs, tcs.idx_id, tag_filename,
- temp, sizeof(temp) - 1))
- {
- goto fail;
- }
- result = temp;
- }
-
- int len = 0, fn_idx = 0;
-
- avail -= sizeof(struct track_data);
- track_num = rb->tagcache_get_numeric(&tcs, tag_tracknumber);
- disc_num = rb->tagcache_get_numeric(&tcs, tag_discnumber);
- if (result)
- {
- /* if filename remove the '/' */
- result = rb->strrchr(result, PATH_SEPCH);
- if (result)
- result++;
- }
-
- if (!result)
- result = tcs.result;
-
- if (disc_num < 0)
- disc_num = 0;
-retry:
- if (track_num > 0)
- {
- if (disc_num)
- fn_idx = 1 + rb->snprintf(pf_tracks.names + string_index, avail,
- "%d.%02d: %s", disc_num, track_num, result);
- else
- fn_idx = 1 + rb->snprintf(pf_tracks.names + string_index, avail,
- "%d: %s", track_num, result);
- }
- else
- {
- track_num = 0;
- fn_idx = 1 + rb->snprintf(pf_tracks.names + string_index, avail,
- "%s", result);
- }
- if (fn_idx <= 0)
+ while (rb->tagcache_get_next(&tcs, tcs_buf, tcs_bufsz))
+ {
+ int disc_num = rb->tagcache_get_numeric(&tcs, tag_discnumber);
+ int track_num = rb->tagcache_get_numeric(&tcs, tag_tracknumber);
+ disc_num = disc_num > 0 ? disc_num : 0;
+ track_num = track_num > 0 ? track_num : 0;
+ int fn_idx = 1 + pf_tcs_retrieve_track_title(string_index, disc_num, track_num);
+ if (fn_idx <= 1)
goto fail;
-#if PF_PLAYBACK_CAPABLE
- int remain = avail - fn_idx;
- if (remain >= MAX_PATH)
- { /* retrieve filename for building the playlist */
- rb->tagcache_retrieve(&tcs, tcs.idx_id, tag_filename,
- pf_tracks.names + string_index + fn_idx, remain);
-
- len = fn_idx + rb->strlen(pf_tracks.names + string_index + fn_idx) + 1;
- /* make sure track name and file name are really split by a \0, else
- * get_track_name might fail */
- *(pf_tracks.names + string_index + fn_idx -1) = '\0';
+ pf_tracks.used += fn_idx;
- }
- else /* request more buffer so that track and filename fit */
- len = (avail - remain) + MAX_PATH;
-#else
- len = fn_idx;
+#if PF_PLAYBACK_CAPABLE
+ int fn_len = 1 + pf_tcs_retrieve_file_name(string_index + fn_idx);
+ if (fn_len <= 1)
+ goto fail;
+ pf_tracks.used += fn_len;
#endif
- if (len > avail)
- {
- while (len > avail)
- {
- if (!free_slide_prio(0))
- goto fail;
- out = 0;
- rb->buflib_buffer_out(&buf_ctx, &out);
- avail += out;
- pf_tracks.borrowed += out;
-
- struct track_data *new_tracks;
- new_tracks = (struct track_data *)(out + (uintptr_t)pf_tracks.index);
-
- unsigned int bytes = pf_tracks.count * sizeof(struct track_data);
- if (pf_tracks.count)
- rb->memmove(new_tracks, pf_tracks.index, bytes);
- pf_tracks.index = new_tracks;
- }
- goto retry;
- }
+ if (!track_buffer_avail(sizeof(struct track_data)))
+ goto fail;
- avail -= len;
- pf_tracks.index--;
+ pf_tracks.used += sizeof(struct track_data);
+ unsigned int arr_sz = (pf_tracks.count + 1) * sizeof(struct track_data);
+ // Arrray descends from upper end of buflib-borrowed buffer.
+ pf_tracks.index = (struct track_data*)(pf_tracks.names + pf_tracks.borrowed
+ - arr_sz );
pf_tracks.index->sort = (disc_num << 24) + (track_num << 14);
pf_tracks.index->sort += pf_tracks.count;
pf_tracks.index->name_idx = string_index;
pf_tracks.index->seek = tcs.result_seek;
#if PF_PLAYBACK_CAPABLE
pf_tracks.index->filename_idx = fn_idx + string_index;
+ string_index += (fn_idx + fn_len);
+#else
+ string_index += fn_idx;
#endif
pf_tracks.count++;
- string_index += len;
}
rb->tagcache_search_finish(&tcs);
@@ -1735,6 +2061,19 @@ fail:
}
/**
+ Re-grow the buflib buffer by returning space borrowed
+ for track list
+*/
+static inline void free_borrowed_tracks(void)
+{
+ rb->buflib_buffer_in(&buf_ctx, pf_tracks.borrowed);
+ pf_tracks.borrowed = 0;
+ pf_tracks.used = 0;
+ pf_tracks.cur_idx = -1;
+ buf_ctx_unlock();
+}
+
+/**
Determine filename of the album art for the given slide_index and
store the result in buf.
The algorithm looks for the first track of the given album uses
@@ -1743,15 +2082,12 @@ fail:
static bool get_albumart_for_index_from_db(const int slide_index, char *buf,
int buflen)
{
- if ( slide_index == -1 )
- {
- rb->strlcpy( buf, EMPTY_SLIDE, buflen );
- }
-
+ bool ret;
+ char tcs_buf[TAGCACHE_BUFSZ];
+ const long tcs_bufsz = sizeof(tcs_buf);
if (tcs.valid || !rb->tagcache_search(&tcs, tag_filename))
return false;
- bool result;
/* find the first track of the album */
rb->tagcache_search_add_filter(&tcs, tag_album,
pf_idx.album_index[slide_index].seek);
@@ -1759,36 +2095,12 @@ static bool get_albumart_for_index_from_db(const int slide_index, char *buf,
rb->tagcache_search_add_filter(&tcs, tag_albumartist,
pf_idx.album_index[slide_index].artist_seek);
- if ( rb->tagcache_get_next(&tcs) ) {
- struct mp3entry id3;
- int fd;
+ ret = rb->tagcache_get_next(&tcs, tcs_buf, tcs_bufsz) &&
+ retrieve_id3(&id3, tcs.result) &&
+ search_albumart_files(&id3, ":", buf, buflen);
-#if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE)
- if (rb->tagcache_fill_tags(&id3, tcs.result))
- {
- rb->strlcpy(id3.path, tcs.result, sizeof(id3.path));
- }
- else
-#endif
- {
- fd = rb->open(tcs.result, O_RDONLY);
- if (fd) {
- rb->get_metadata(&id3, fd, tcs.result);
- rb->close(fd);
- }
- }
-
- if ( search_albumart_files(&id3, ":", buf, buflen) )
- result = true;
- else
- result = false;
- }
- else {
- /* did not find a matching track */
- result = false;
- }
rb->tagcache_search_finish(&tcs);
- return result;
+ return ret;
}
/**
@@ -1885,6 +2197,9 @@ static unsigned int mfnv(char *str)
const unsigned int p = 16777619;
unsigned int hash = 0x811C9DC5; // 2166136261;
+ if (!str)
+ return 0;
+
while(*str)
hash = (hash ^ *str++) * p;
hash += hash << 13;
@@ -1906,13 +2221,7 @@ static bool save_pfraw(char* filename, struct bitmap *bm)
int fh = rb->open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0666);
if( fh < 0 ) return false;
rb->write( fh, &bmph, sizeof( struct pfraw_header ) );
- pix_t *data = (pix_t*)( bm->data );
- int y;
- for( y = 0; y < bm->height; y++ )
- {
- rb->write( fh, data , sizeof( pix_t ) * bm->width );
- data += bm->width;
- }
+ rb->write( fh, bm->data , sizeof( pix_t ) * bm->width * bm->height );
rb->close( fh );
return true;
}
@@ -1925,12 +2234,13 @@ static bool incremental_albumart_cache(bool verbose)
if (aa_cache.inspected >= pf_idx.album_ct)
return false;
+ /* Prevent idle poweroff */
+ rb->reset_poweroff_timer();
+
int idx, ret;
unsigned int hash_artist, hash_album;
unsigned int format = FORMAT_NATIVE;
- bool update = (pf_cfg.cache_version == CACHE_UPDATE);
-
if (pf_cfg.resize)
format |= FORMAT_RESIZE|FORMAT_KEEP_ASPECT;
@@ -1942,8 +2252,6 @@ static bool incremental_albumart_cache(bool verbose)
aa_cache.inspected++;
if (aa_cache.idx >= pf_idx.album_ct) { aa_cache.idx = 0; } /* Rollover */
- if (!get_albumart_for_index_from_db(idx, aa_cache.file, sizeof(aa_cache.file)))
- goto aa_failure; //rb->strcpy(aa_cache.file, EMPTY_SLIDE_BMP);
hash_artist = mfnv(get_album_artist(idx));
hash_album = mfnv(get_album_name(idx));
@@ -1951,13 +2259,15 @@ static bool incremental_albumart_cache(bool verbose)
rb->snprintf(aa_cache.pfraw_file, sizeof(aa_cache.pfraw_file),
CACHE_PREFIX "/%x%x.pfraw", hash_album, hash_artist);
- if(rb->file_exists(aa_cache.pfraw_file)) {
- if(update) {
- aa_cache.slides++;
- goto aa_success;
- }
+ if(pf_cfg.update_albumart && rb->file_exists(aa_cache.pfraw_file)) {
+ aa_cache.slides++;
+ goto aa_success;
}
+ if (!get_albumart_for_index_from_db(idx, aa_cache.file, sizeof(aa_cache.file)))
+ goto aa_failure; //rb->strcpy(aa_cache.file, EMPTY_SLIDE_BMP);
+
+
aa_cache.input_bmp.data = aa_cache.buf;
aa_cache.input_bmp.width = DISPLAY_WIDTH;
aa_cache.input_bmp.height = DISPLAY_HEIGHT;
@@ -1982,11 +2292,22 @@ static bool incremental_albumart_cache(bool verbose)
aa_failure:
if (verbose)
+ {
+ if (aa_cache.inspected >= pf_idx.album_ct)
+ configfile_save(CONFIG_FILE, config, CONFIG_NUM_ITEMS,
+ CONFIG_VERSION);
return false;
+ }
aa_success:
if (aa_cache.inspected >= pf_idx.album_ct)
+ {
+ configfile_save(CONFIG_FILE, config, CONFIG_NUM_ITEMS,
+ CONFIG_VERSION);
free_all_slide_prio(0);
+ if (pf_state == pf_idle)
+ rb->queue_post(&thread_q, EV_WAKEUP, 0);
+ }
if(verbose)/* direct interaction with user */
return true;
@@ -2002,6 +2323,7 @@ static bool create_albumart_cache(void)
{
draw_splashscreen(pf_idx.buf, pf_idx.buf_sz);
draw_progressbar(0, pf_idx.album_ct, "Preparing artwork");
+ aa_cache.inspected = 0;
for (int i=0; i < pf_idx.album_ct; i++)
{
incremental_albumart_cache(true);
@@ -2062,12 +2384,15 @@ static void thread(void)
/* we just woke up */
break;
}
- if(ev.id != SYS_TIMEOUT)
- while ( load_new_slide() ) {
- rb->yield();
- switch (ev.id) {
- case EV_EXIT:
- return;
+
+ if(ev.id != SYS_TIMEOUT) {
+ while ( rb->queue_empty(&thread_q) ) {
+ buf_ctx_lock();
+ bool slide_loaded = load_new_slide();
+ buf_ctx_unlock();
+ if (!slide_loaded)
+ break;
+ rb->yield();
}
}
}
@@ -2114,6 +2439,26 @@ static bool create_pf_thread(void)
}
+static void initialize_slide_cache(void)
+{
+ int i= 0;
+ for (i = 0; i < SLIDE_CACHE_SIZE; i++) {
+ pf_sldcache.cache[i].hid = 0;
+ pf_sldcache.cache[i].index = 0;
+ pf_sldcache.cache[i].next = i + 1;
+ pf_sldcache.cache[i].prev = i - 1;
+ }
+ pf_sldcache.cache[0].prev = i - 1;
+ pf_sldcache.cache[i - 1].next = 0;
+
+ pf_sldcache.free = 0;
+ pf_sldcache.used = -1;
+ pf_sldcache.left_idx = -1;
+ pf_sldcache.right_idx = -1;
+ pf_sldcache.center_idx = -1;
+}
+
+
/*
* The following functions implement the linked-list-in-array used to manage
* the LRU cache of slides, and the list of free cache slots.
@@ -2122,20 +2467,30 @@ static bool create_pf_thread(void)
#define _SEEK_RIGHT_WHILE(start, cond) \
({ \
int ind_, next_ = (start); \
+ int i_ = 0; \
do { \
ind_ = next_; \
next_ = pf_sldcache.cache[ind_].next; \
- } while (next_ != pf_sldcache.used && (cond)); \
+ i_++; \
+ } while (next_ != pf_sldcache.used && (cond) && i_ < SLIDE_CACHE_SIZE); \
+ if (i_ >= SLIDE_CACHE_SIZE) \
+ /* TODO: Not supposed to happen */ \
+ ind_ = -1; \
ind_; \
})
#define _SEEK_LEFT_WHILE(start, cond) \
({ \
int ind_, next_ = (start); \
+ int i_ = 0; \
do { \
ind_ = next_; \
next_ = pf_sldcache.cache[ind_].prev; \
- } while (ind_ != pf_sldcache.used && (cond)); \
+ i_++; \
+ } while (ind_ != pf_sldcache.used && (cond) && i_ < SLIDE_CACHE_SIZE); \
+ if (i_ >= SLIDE_CACHE_SIZE) \
+ /* TODO: Not supposed to happen */ \
+ ind_ = -1; \
ind_; \
})
@@ -2286,7 +2641,7 @@ static bool free_slide_prio(int prio)
static void free_all_slide_prio(int prio)
{
while (free_slide_prio(prio))
- ;;
+ {;;}
}
@@ -2324,12 +2679,7 @@ static int read_pfraw(char* filename, int prio)
bm->height = bmph.height;
pix_t *data = (pix_t*)(sizeof(struct dim) + (char *)bm);
- int y;
- for( y = 0; y < bm->height; y++ )
- {
- rb->read( fh, data , sizeof( pix_t ) * bm->width );
- data += bm->width;
- }
+ rb->read( fh, data , sizeof( pix_t ) * bm->width * bm->height );
rb->close( fh );
return hid;
}
@@ -2369,6 +2719,9 @@ static inline bool load_and_prepare_surface(const int slide_index,
*/
bool load_new_slide(void)
{
+ if (wants_to_quit)
+ return false;
+
int i = -1;
if (pf_sldcache.center_idx != -1)
@@ -2380,6 +2733,8 @@ bool load_new_slide(void)
{
pf_sldcache.center_idx = _SEEK_RIGHT_WHILE(pf_sldcache.center_idx,
pf_sldcache.cache[next_].index <= center_index);
+ if (pf_sldcache.center_idx == -1)
+ goto fatal_fail;
prev = pf_sldcache.center_idx;
next = pf_sldcache.cache[pf_sldcache.center_idx].next;
@@ -2388,6 +2743,8 @@ bool load_new_slide(void)
{
pf_sldcache.center_idx = _SEEK_LEFT_WHILE(pf_sldcache.center_idx,
pf_sldcache.cache[next_].index >= center_index);
+ if (pf_sldcache.center_idx == -1)
+ goto fatal_fail;
next = pf_sldcache.center_idx;
prev = pf_sldcache.cache[pf_sldcache.center_idx].prev;
@@ -2433,6 +2790,8 @@ bool load_new_slide(void)
pf_sldcache.right_idx = _SEEK_RIGHT_WHILE(pf_sldcache.right_idx,
pf_sldcache.cache[ind_].index - 1 == pf_sldcache.cache[next_].index);
+ if (pf_sldcache.right_idx == -1 || pf_sldcache.left_idx == -1)
+ goto fatal_fail;
/* update indices */
@@ -2445,7 +2804,9 @@ bool load_new_slide(void)
if ((prio_l < prio_r || right >= number_of_slides) && left > 0)
{
if (pf_sldcache.free == -1 && !free_slide_prio(prio_l))
+ {
return false;
+ }
i = lla_pop_head(&pf_sldcache.free);
if (load_and_prepare_surface(left - 1, i, prio_l))
@@ -2457,7 +2818,9 @@ bool load_new_slide(void)
} else if(right < number_of_slides - 1)
{
if (pf_sldcache.free == -1 && !free_slide_prio(prio_r))
+ {
return false;
+ }
i = lla_pop_head(&pf_sldcache.free);
if (load_and_prepare_surface(right + 1, i, prio_r))
@@ -2487,6 +2850,10 @@ fail_and_refree:
lla_insert_tail(&pf_sldcache.free, i);
}
return false;
+fatal_fail:
+ free_all_slide_prio(0);
+ initialize_slide_cache();
+ return false;
}
@@ -2518,13 +2885,21 @@ static inline struct dim *surface(const int slide_index)
int i;
if ((i = pf_sldcache.used ) != -1)
{
+ int j = 0;
do {
- if (pf_sldcache.cache[i].index == slide_index)
+ if (pf_sldcache.cache[i].index == slide_index) {
+ if (is_initial_slide && slide_index == center_index)
+ is_initial_slide = false;
return get_slide(pf_sldcache.cache[i].hid);
+ }
i = pf_sldcache.cache[i].next;
- } while (i != pf_sldcache.used);
+ j++;
+ } while (i != pf_sldcache.used && j < SLIDE_CACHE_SIZE);
}
- return get_slide(empty_slide_hid);
+ if (is_initial_slide && slide_index == center_index)
+ return NULL;
+ else
+ return get_slide(empty_slide_hid);
}
/**
@@ -2712,7 +3087,7 @@ static void render_slide(struct slide_data *slide, const int alpha)
const pix_t *ptr = &src[column * bmp->height];
-#if defined(LCD_STRIDEFORMAT) && LCD_STRIDEFORMAT == VERTICAL_STRIDE
+#if LCD_STRIDEFORMAT == VERTICAL_STRIDE
#define PIXELSTEP_Y 1
#define LCDADDR(x, y) (&buffer[BUFFER_HEIGHT*(x) + (y)])
#else
@@ -2785,14 +3160,106 @@ static inline void set_current_slide(const int slide_index)
{
int old_center_index = center_index;
step = 0;
- center_index = fbound(slide_index, 0, number_of_slides - 1);
+ center_index = fbound(0, slide_index, number_of_slides - 1);
if (old_center_index != center_index)
+ {
+ rb->queue_remove_from_head(&thread_q, EV_WAKEUP);
rb->queue_post(&thread_q, EV_WAKEUP, 0);
+ }
target = center_index;
- slide_frame = slide_index << 16;
+ slide_frame = center_index << 16;
reset_slides();
}
+
+static void skip_animation_to_idle_state(void);
+static bool sort_albums(int new_sorting, bool from_settings)
+{
+ int i, album_idx, artist_idx;
+ char* current_album_name = NULL;
+ char* current_album_artist = NULL;
+ static const char* sort_options[] = {
+ ID2P(LANG_ARTIST_PLUS_NAME),
+ ID2P(LANG_ARTIST_PLUS_YEAR),
+ ID2P(LANG_ID3_YEAR),
+ ID2P(LANG_NAME)
+ };
+
+ /* Only change sorting once artwork has been inspected */
+ if (aa_cache.inspected < pf_idx.album_ct)
+ {
+#ifdef USEGSLIB
+ if (!from_settings)
+ grey_show(false);
+#endif
+ rb->splash(HZ*2, rb->str(LANG_WAIT_FOR_CACHE));
+#ifdef USEGSLIB
+ if (!from_settings)
+ grey_show(true);
+#endif
+ return false;
+ }
+
+ /* set idle state */
+ if (pf_state == pf_show_tracks)
+ free_borrowed_tracks();
+ if (pf_state == pf_show_tracks ||
+ pf_state == pf_cover_in ||
+ pf_state == pf_cover_out)
+ skip_animation_to_idle_state();
+ else if (pf_state == pf_scrolling)
+ set_current_slide(target);
+ pf_state = pf_idle;
+
+ pf_cfg.sort_albums_by = new_sorting;
+ if (!from_settings)
+ {
+#ifdef USEGSLIB
+ grey_show(false);
+#if LCD_DEPTH > 1
+ rb->lcd_set_background(N_BRIGHT(0));
+ rb->lcd_set_foreground(N_BRIGHT(255));
+#endif
+ rb->lcd_clear_display();
+ rb->lcd_update();
+#endif
+ rb->splash(HZ, sort_options[pf_cfg.sort_albums_by]);
+#ifdef USEGSLIB
+ grey_show(true);
+#endif
+ }
+
+ current_album_artist = get_album_artist(center_index);
+ current_album_name = get_album_name(center_index);
+
+ end_pf_thread(); /* stop loading of covers */
+
+ rb->qsort(pf_idx.album_index, pf_idx.album_ct,
+ sizeof(struct album_data), compare_albums);
+
+ /* Empty cache and restart cover loading thread */
+ rb->buflib_init(&buf_ctx, (void *)pf_idx.buf, pf_idx.buf_sz);
+ empty_slide_hid = read_pfraw(EMPTY_SLIDE, 0);
+ initialize_slide_cache();
+ is_initial_slide = true;
+ create_pf_thread();
+
+ /* Go to previously selected slide */
+ for (i = 0; i < pf_idx.album_ct; i++ )
+ {
+ album_idx = pf_idx.album_index[i].name_idx;
+ artist_idx = pf_idx.album_index[i].artist_idx;
+
+ if(!rb->strcmp(pf_idx.album_names + album_idx, current_album_name) &&
+ !rb->strcmp(pf_idx.artist_names + artist_idx, current_album_artist))
+ {
+ set_current_slide(i);
+ pf_cfg.last_album = i;
+ }
+ }
+ return true;
+}
+
/**
Start the animation for changing slides
*/
@@ -2802,6 +3269,8 @@ static void start_animation(void)
pf_state = pf_scrolling;
}
+static void update_scroll_animation(void);
+
/**
Go to the previous slide
*/
@@ -2815,6 +3284,8 @@ static void show_previous_slide(void)
} else if ( step > 0 ) {
target = center_index;
step = (target <= center_slide.slide_index) ? -1 : 1;
+ if (step < 0)
+ update_scroll_animation();
} else {
target = fmax(0, center_index - 2);
}
@@ -2834,6 +3305,8 @@ static void show_next_slide(void)
} else if ( step < 0 ) {
target = center_index;
step = (target < center_slide.slide_index) ? -1 : 1;
+ if (step > 0)
+ update_scroll_animation();
} else {
target = fmin(center_index + 2, number_of_slides - 1);
}
@@ -3002,35 +3475,55 @@ static void update_scroll_animation(void)
*/
static void cleanup(void)
{
+ wants_to_quit = true;
+ if (buf_ctx_locked)
+ buf_ctx_unlock();
+
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
rb->cpu_boost(false);
#endif
end_pf_thread();
-#ifdef HAVE_BACKLIGHT
+
/* Turn on backlight timeout (revert to settings) */
backlight_use_settings();
-#endif
#ifdef USEGSLIB
grey_release();
#endif
}
+static void skip_animation_to_show_tracks(void);
+static void adjust_album_display_for_setting(int old_val, int new_val)
+{
+ if (old_val == new_val)
+ return;
+
+ reset_track_list();
+ recalc_offsets();
+ reset_slides();
+
+ if (pf_state == pf_show_tracks)
+ skip_animation_to_show_tracks();
+}
+
/**
Shows the settings menu
*/
static int settings_menu(void)
{
int selection = 0;
- bool old_val;
+ int old_val;
MENUITEM_STRINGLIST(settings_menu, "PictureFlow Settings", NULL,
+ ID2P(LANG_SHOW_ALBUM_TITLE),
+ ID2P(LANG_SHOW_YEAR_IN_ALBUM_TITLE),
+ ID2P(LANG_SORT_ALBUMS_BY),
+ ID2P(LANG_YEAR_SORT_ORDER),
ID2P(LANG_DISPLAY_FPS),
ID2P(LANG_SPACING),
ID2P(LANG_CENTRE_MARGIN),
ID2P(LANG_NUMBER_OF_SLIDES),
ID2P(LANG_ZOOM),
- ID2P(LANG_SHOW_ALBUM_TITLE),
ID2P(LANG_RESIZE_COVERS),
ID2P(LANG_REBUILD_CACHE),
ID2P(LANG_UPDATE_CACHE),
@@ -3044,6 +3537,16 @@ static int settings_menu(void)
{ STR(LANG_SHOW_ALL_AT_THE_TOP) },
{ STR(LANG_SHOW_ALL_AT_THE_BOTTOM) },
};
+ static const struct opt_items sort_options[] = {
+ { STR(LANG_ARTIST_PLUS_NAME) },
+ { STR(LANG_ARTIST_PLUS_YEAR) },
+ { STR(LANG_ID3_YEAR) },
+ { STR(LANG_NAME) }
+ };
+ static const struct opt_items year_sort_order_options[] = {
+ { STR(LANG_ASCENDING) },
+ { STR(LANG_DESCENDING) }
+ };
static const struct opt_items wps_options[] = {
{ STR(LANG_OFF) },
{ STR(LANG_DIRECT) },
@@ -3058,73 +3561,96 @@ static int settings_menu(void)
selection=rb->do_menu(&settings_menu,&selection, NULL, false);
switch(selection) {
case 0:
+ old_val = pf_cfg.show_album_name;
+ rb->set_option(rb->str(LANG_SHOW_ALBUM_TITLE),
+ &pf_cfg.show_album_name, RB_INT, album_name_options, 5, NULL);
+ adjust_album_display_for_setting(old_val, pf_cfg.show_album_name);
+ break;
+ case 1:
+ rb->set_bool(rb->str(LANG_SHOW_YEAR_IN_ALBUM_TITLE), &pf_cfg.show_year);
+ break;
+ case 2:
+ old_val = pf_cfg.sort_albums_by;
+ rb->set_option(rb->str(LANG_SORT_ALBUMS_BY),
+ &pf_cfg.sort_albums_by, RB_INT, sort_options, 4, NULL);
+ if (old_val != pf_cfg.sort_albums_by &&
+ !sort_albums(pf_cfg.sort_albums_by, true))
+ pf_cfg.sort_albums_by = old_val;
+ break;
+ case 3:
+ old_val = pf_cfg.year_sort_order;
+ rb->set_option(rb->str(LANG_YEAR_SORT_ORDER),
+ &pf_cfg.year_sort_order, RB_INT, year_sort_order_options, 2, NULL);
+ if (old_val != pf_cfg.year_sort_order &&
+ !sort_albums(pf_cfg.sort_albums_by, true))
+ pf_cfg.year_sort_order = old_val;
+ break;
+ case 4:
+ old_val = pf_cfg.show_fps;
rb->set_bool(rb->str(LANG_DISPLAY_FPS), &pf_cfg.show_fps);
- reset_track_list();
+ if (old_val != pf_cfg.show_fps)
+ reset_track_list();
break;
- case 1:
+ case 5:
+ old_val = pf_cfg.slide_spacing;
rb->set_int(rb->str(LANG_SPACING), "", 1,
&pf_cfg.slide_spacing,
NULL, 1, 0, 100, NULL );
- recalc_offsets();
- reset_slides();
+ adjust_album_display_for_setting(old_val, pf_cfg.slide_spacing);
break;
- case 2:
+ case 6:
+ old_val = pf_cfg.center_margin;
rb->set_int(rb->str(LANG_CENTRE_MARGIN), "", 1,
&pf_cfg.center_margin,
NULL, 1, 0, 80, NULL );
- recalc_offsets();
- reset_slides();
+ adjust_album_display_for_setting(old_val, pf_cfg.center_margin);
break;
- case 3:
+ case 7:
+ old_val = pf_cfg.num_slides;
rb->set_int(rb->str(LANG_NUMBER_OF_SLIDES), "", 1,
&pf_cfg.num_slides, NULL, 1, 1, MAX_SLIDES_COUNT, NULL );
- recalc_offsets();
- reset_slides();
+ adjust_album_display_for_setting(old_val, pf_cfg.num_slides);
break;
- case 4:
+ case 8:
+ old_val = pf_cfg.zoom;
rb->set_int(rb->str(LANG_ZOOM), "", 1, &pf_cfg.zoom,
NULL, 1, 10, 300, NULL );
- recalc_offsets();
- reset_slides();
- break;
- case 5:
- rb->set_option(rb->str(LANG_SHOW_ALBUM_TITLE),
- &pf_cfg.show_album_name, INT, album_name_options, 5, NULL);
- reset_track_list();
- recalc_offsets();
- reset_slides();
+ adjust_album_display_for_setting(old_val, pf_cfg.zoom);
break;
- case 6:
+
+ case 9:
old_val = pf_cfg.resize;
rb->set_bool(rb->str(LANG_RESIZE_COVERS), &pf_cfg.resize);
if (old_val == pf_cfg.resize) /* changed? */
break;
/* fallthrough if changed, since cache needs to be rebuilt */
- case 7:
+ case 10:
+ pf_cfg.update_albumart = false;
pf_cfg.cache_version = CACHE_REBUILD;
rb->remove(EMPTY_SLIDE);
configfile_save(CONFIG_FILE, config,
CONFIG_NUM_ITEMS, CONFIG_VERSION);
rb->splash(HZ, ID2P(LANG_CACHE_REBUILT_NEXT_RESTART));
break;
- case 8:
- pf_cfg.cache_version = CACHE_UPDATE;
+ case 11:
+ pf_cfg.update_albumart = true;
+ pf_cfg.cache_version = CACHE_REBUILD;
rb->remove(EMPTY_SLIDE);
configfile_save(CONFIG_FILE, config,
CONFIG_NUM_ITEMS, CONFIG_VERSION);
rb->splash(HZ, ID2P(LANG_CACHE_REBUILT_NEXT_RESTART));
break;
- case 9:
+ case 12:
rb->set_option(rb->str(LANG_WPS_INTEGRATION),
- &pf_cfg.auto_wps, INT, wps_options, 3, NULL);
+ &pf_cfg.auto_wps, RB_INT, wps_options, 3, NULL);
break;
- case 10:
+ case 13:
rb->set_option(rb->str(LANG_BACKLIGHT),
- &pf_cfg.backlight_mode, INT, backlight_options, 2, NULL);
+ &pf_cfg.backlight_mode, RB_INT, backlight_options, 2, NULL);
break;
case MENU_ATTACHED_USB:
@@ -3138,45 +3664,67 @@ static int settings_menu(void)
Show the main menu
*/
enum {
+ PF_SHOW_TRACKS_WHILE_BROWSING,
+ PF_GOTO_LAST_ALBUM,
PF_GOTO_WPS,
#if PF_PLAYBACK_CAPABLE
- PF_MENU_CLEAR_PLAYLIST,
PF_MENU_PLAYBACK_CONTROL,
#endif
PF_MENU_SETTINGS,
- PF_MENU_RETURN,
PF_MENU_QUIT,
};
static int main_menu(void)
{
int selection = 0;
- int result;
+ int result, curr_album;
#if LCD_DEPTH > 1
rb->lcd_set_foreground(N_BRIGHT(255));
#endif
MENUITEM_STRINGLIST(main_menu, "PictureFlow Main Menu", NULL,
+ ID2P(LANG_SHOW_TRACKS_WHILE_BROWSING),
+ ID2P(LANG_GOTO_LAST_ALBUM),
ID2P(LANG_GOTO_WPS),
#if PF_PLAYBACK_CAPABLE
- ID2P(LANG_CLEAR_PLAYLIST),
ID2P(LANG_PLAYBACK_CONTROL),
#endif
ID2P(LANG_SETTINGS),
- ID2P(LANG_RETURN),
ID2P(LANG_MENU_QUIT));
while (1) {
switch (rb->do_menu(&main_menu,&selection, NULL, false)) {
+ case PF_SHOW_TRACKS_WHILE_BROWSING:
+ if (pf_state != pf_show_tracks)
+ {
+ if (pf_state == pf_scrolling)
+ set_current_slide(target);
+
+ skip_animation_to_show_tracks();
+ }
+ show_tracks_while_browsing = true;
+ return 0;
+ case PF_GOTO_LAST_ALBUM:
+ if (pf_state == pf_scrolling)
+ curr_album = target;
+ else
+ curr_album = center_index;
+
+ if (pf_state == pf_show_tracks)
+ free_borrowed_tracks();
+ if (pf_state == pf_show_tracks ||
+ pf_state == pf_cover_in ||
+ pf_state == pf_cover_out)
+ skip_animation_to_idle_state();
+
+ set_current_slide(pf_cfg.last_album);
+ pf_cfg.last_album = curr_album;
+
+ pf_state = pf_idle;
+ return 0;
case PF_GOTO_WPS: /* WPS */
return -2;
#if PF_PLAYBACK_CAPABLE
- case PF_MENU_CLEAR_PLAYLIST:
- if(rb->playlist_remove_all_tracks(NULL) == 0) {
- rb->playlist_create(NULL, NULL);
- rb->splash(HZ*2, ID2P(LANG_PLAYLIST_CLEARED));
- }
- break;
case PF_MENU_PLAYBACK_CONTROL: /* Playback Control */
playback_control(NULL);
break;
@@ -3185,8 +3733,6 @@ static int main_menu(void)
result = settings_menu();
if ( result != 0 ) return result;
break;
- case PF_MENU_RETURN:
- return 0;
case PF_MENU_QUIT:
return -1;
@@ -3199,21 +3745,33 @@ static int main_menu(void)
}
}
+#define ZOOMIN_FRAME_COUNT 19
+#define ZOOMIN_FRAME_DIST -5
+#define ZOOMIN_FRAME_ANGLE 1
+#define ZOOMIN_FRAME_FADE 13
+
+#define ROTATE_FRAME_COUNT 15
+#define ROTATE_FRAME_ANGLE 16
+
+#define KEYFRAME_COUNT ZOOMIN_FRAME_COUNT + ROTATE_FRAME_COUNT
+
/**
Animation step for zooming into the current cover
*/
static void update_cover_in_animation(void)
{
cover_animation_keyframe++;
- if( cover_animation_keyframe < 20 ) {
- center_slide.distance-=5;
- center_slide.angle+=1;
- extra_fade += 13;
- }
- else if( cover_animation_keyframe < 35 ) {
- center_slide.angle+=16;
+
+ if(cover_animation_keyframe <= ZOOMIN_FRAME_COUNT)
+ {
+ center_slide.distance += ZOOMIN_FRAME_DIST;
+ center_slide.angle += ZOOMIN_FRAME_ANGLE;
+ extra_fade += ZOOMIN_FRAME_FADE;
}
- else {
+ else if(cover_animation_keyframe <= KEYFRAME_COUNT)
+ center_slide.angle += ROTATE_FRAME_ANGLE;
+ else
+ {
cover_animation_keyframe = 0;
pf_state = pf_show_tracks;
}
@@ -3225,21 +3783,57 @@ static void update_cover_in_animation(void)
static void update_cover_out_animation(void)
{
cover_animation_keyframe++;
- if( cover_animation_keyframe <= 15 ) {
- center_slide.angle-=16;
- }
- else if( cover_animation_keyframe < 35 ) {
- center_slide.distance+=5;
- center_slide.angle-=1;
- extra_fade -= 13;
+
+ if(cover_animation_keyframe <= ROTATE_FRAME_COUNT)
+ center_slide.angle -= ROTATE_FRAME_ANGLE;
+ else if(cover_animation_keyframe <= KEYFRAME_COUNT)
+ {
+ center_slide.distance -= ZOOMIN_FRAME_DIST;
+ center_slide.angle -= ZOOMIN_FRAME_ANGLE;
+ extra_fade -= ZOOMIN_FRAME_FADE;
}
- else {
+ else
+ {
cover_animation_keyframe = 0;
pf_state = pf_idle;
}
}
/**
+ Immediately show tracks and skip any animation frames
+*/
+static void skip_animation_to_show_tracks(void)
+{
+ pf_state = pf_show_tracks;
+ cover_animation_keyframe = 0;
+
+ extra_fade = ZOOMIN_FRAME_COUNT * ZOOMIN_FRAME_FADE;
+ center_slide.distance = ZOOMIN_FRAME_COUNT * ZOOMIN_FRAME_DIST;
+ center_slide.angle = (ZOOMIN_FRAME_COUNT * ZOOMIN_FRAME_ANGLE) +
+ (ROTATE_FRAME_COUNT * ROTATE_FRAME_ANGLE);
+}
+
+/**
+ Immediately transition to idle state and skip any animation frames
+*/
+static void skip_animation_to_idle_state(void)
+{
+ pf_state = pf_idle;
+ cover_animation_keyframe = 0;
+ extra_fade = 0;
+ set_current_slide(center_index);
+}
+
+/**
+ Change direction during cover in/out animation
+*/
+static void reverse_animation(void)
+{
+ pf_state = pf_state == pf_cover_out ? pf_cover_in : pf_cover_out;
+ cover_animation_keyframe = KEYFRAME_COUNT - cover_animation_keyframe;
+}
+
+/**
Draw a blue gradient at y with height h
*/
static inline void draw_gradient(int y, int h)
@@ -3267,30 +3861,32 @@ static inline void draw_gradient(int y, int h)
static void track_list_yh(int char_height)
{
+ bool needs_space = pf_cfg.show_fps || aa_cache.inspected < pf_idx.album_ct;
+
switch (pf_cfg.show_album_name)
{
case ALBUM_NAME_HIDE:
- pf_tracks.list_y = (pf_cfg.show_fps ? char_height : 0);
+ pf_tracks.list_y = (needs_space ? char_height : 0);
pf_tracks.list_h = LCD_HEIGHT - pf_tracks.list_y;
break;
case ALBUM_NAME_BOTTOM:
- pf_tracks.list_y = (pf_cfg.show_fps ? char_height : 0);
+ pf_tracks.list_y = (needs_space ? char_height : 0);
pf_tracks.list_h = LCD_HEIGHT - pf_tracks.list_y - (char_height * 3);
break;
case ALBUM_AND_ARTIST_TOP:
pf_tracks.list_y = char_height * 3;
pf_tracks.list_h = LCD_HEIGHT - pf_tracks.list_y -
- (pf_cfg.show_fps ? char_height : 0);
+ (needs_space ? char_height : 0);
break;
case ALBUM_AND_ARTIST_BOTTOM:
- pf_tracks.list_y = (pf_cfg.show_fps ? char_height : 0);
+ pf_tracks.list_y = (needs_space ? char_height : 0);
pf_tracks.list_h = LCD_HEIGHT - pf_tracks.list_y - (char_height * 3);
break;
case ALBUM_NAME_TOP:
default:
pf_tracks.list_y = char_height * 3;
pf_tracks.list_h = LCD_HEIGHT - pf_tracks.list_y -
- (pf_cfg.show_fps ? char_height : 0);
+ (needs_space ? char_height : 0);
break;
}
}
@@ -3320,6 +3916,20 @@ void reset_track_list(void)
}
}
+static void draw_album_text(void);
+static void show_track_list_loading(void)
+{
+ int x = (LCD_WIDTH - mylcd_getstringsize(rb->str(LANG_WAIT), NULL, NULL)) / 2;
+ mylcd_set_foreground(G_BRIGHT(255));
+ int char_height = rb->screens[SCREEN_MAIN]->getcharheight();
+ track_list_yh(char_height);
+ mylcd_putsxy(x, pf_tracks.list_y + (pf_tracks.list_h - char_height) / 2,
+ rb->str(LANG_WAIT));
+ draw_album_text();
+ mylcd_update();
+ mylcd_clear_display();
+}
+
/**
Display the list of tracks
*/
@@ -3327,7 +3937,17 @@ static void show_track_list(void)
{
mylcd_clear_display();
if ( center_slide.slide_index != pf_tracks.cur_idx ) {
+#ifdef HAVE_TC_RAMCACHE
+ if (!rb->tagcache_is_in_ram())
+#endif
+ show_track_list_loading();
create_track_index(center_slide.slide_index);
+ if (pf_tracks.count == 0)
+ {
+ pf_state = pf_cover_out;
+ free_borrowed_tracks();
+ return;
+ }
reset_track_list();
}
int titletxt_w, titletxt_x, color, titletxt_h;
@@ -3341,7 +3961,7 @@ static void show_track_list(void)
for (; track_i < pf_tracks.list_visible + pf_tracks.list_start; track_i++)
{
char *trackname = get_track_name(track_i);
- if ( track_i == pf_tracks.sel ) {
+ if (track_i == pf_tracks.sel && !show_tracks_while_browsing) {
if (pf_tracks.sel != pf_tracks.last_sel) {
set_scroll_line(trackname, PF_SCROLL_TRACK);
pf_tracks.last_sel = pf_tracks.sel;
@@ -3368,7 +3988,7 @@ static void select_next_track(void)
pf_tracks.sel++;
if (pf_tracks.sel==(pf_tracks.list_visible+pf_tracks.list_start))
pf_tracks.list_start++;
- } else {
+ } else if (rb->global_settings->list_wraparound) {
/* Rollover */
pf_tracks.sel = 0;
pf_tracks.list_start = 0;
@@ -3380,68 +4000,319 @@ static void select_prev_track(void)
if (pf_tracks.sel > 0 ) {
if (pf_tracks.sel==pf_tracks.list_start) pf_tracks.list_start--;
pf_tracks.sel--;
- } else {
+ } else if (rb->global_settings->list_wraparound) {
/* Rolllover */
pf_tracks.sel = pf_tracks.count - 1;
pf_tracks.list_start = pf_tracks.count - pf_tracks.list_visible;
}
}
+static void select_next_album(void)
+{
+ if (center_index < number_of_slides - 1) {
+ free_borrowed_tracks();
+ target = center_index + 1;
+ set_current_slide(target);
+ skip_animation_to_show_tracks();
+ }
+}
+
+static void select_prev_album(void)
+{
+ if (center_index > 0) {
+ free_borrowed_tracks();
+ target = center_index - 1;
+ set_current_slide(target);
+ skip_animation_to_show_tracks();
+ }
+}
+
#if PF_PLAYBACK_CAPABLE
-/*
- * Puts the current tracklist into a newly created playlist and starts playling
- */
-static void start_playback(bool append)
+static int show_id3_info(const char *selected_file)
+{
+ int i;
+ unsigned long last_tick;
+ const char *file_name;
+ bool is_multiple_tracks = insert_whole_album && pf_tracks.count > 1;
+
+ last_tick = *(rb->current_tick) + HZ/2;
+ rb->splash_progress_set_delay(HZ / 2); /* wait 1/2 sec before progress */
+ i = 0;
+ do {
+ file_name = i == 0 ? selected_file : get_track_filename(i);
+ if (rb->mp3info(&id3, file_name))
+ return 0;
+
+ if (is_multiple_tracks)
+ {
+ rb->splash_progress(i, pf_tracks.count,
+ "%s (%s)", rb->str(LANG_WAIT), rb->str(LANG_OFF_ABORT));
+ if (TIME_AFTER(*(rb->current_tick), last_tick + HZ/4))
+ {
+ if (rb->action_userabort(TIMEOUT_NOBLOCK))
+ return 0;
+ last_tick = *(rb->current_tick);
+ }
+
+ collect_id3(&id3, i == 0);
+ rb->yield();
+ }
+ } while (++i < pf_tracks.count && is_multiple_tracks);
+
+ if (is_multiple_tracks)
+ finalize_id3(&id3);
+
+ return rb->browse_id3(&id3, 0, 0, NULL, i) ? PLUGIN_USB_CONNECTED : 0;
+}
+
+
+static bool pf_current_playlist_insert(int position, bool queue, bool create_new)
{
- static int old_playlist = -1, old_shuffle = 0;
- int count = 0;
- int position = pf_tracks.sel;
- int shuffle = rb->global_settings->playlist_shuffle;
- /* reuse existing playlist if possible
- * regenerate if shuffle is on or changed, since playlist index and
- * selected track are "out of sync" */
- if (!shuffle && !append && center_slide.slide_index == old_playlist
- && (old_shuffle == shuffle))
+ if (position == PLAYLIST_REPLACE)
{
- goto play;
+ if ((!create_new && rb->playlist_remove_all_tracks(NULL) == 0) ||
+ (create_new && rb->playlist_create(NULL, NULL) == 0))
+ position = PLAYLIST_INSERT_LAST;
+ else
+ return false;
}
- /* First, replace the current playlist with a new one */
- else if (append || (rb->playlist_remove_all_tracks(NULL) == 0
- && rb->playlist_create(NULL, NULL) == 0))
+
+ if (!insert_whole_album)
+ rb->playlist_insert_track(NULL, get_track_filename(pf_tracks.sel),
+ position, queue, false);
+ else
{
+ int i = 0;
do {
rb->yield();
- if (rb->playlist_insert_track(NULL, get_track_filename(count),
- PLAYLIST_INSERT_LAST, false, true) < 0)
+ if (rb->playlist_insert_track(NULL, get_track_filename(i),
+ position, queue, false) < 0)
break;
- } while(++count < pf_tracks.count);
- rb->playlist_sync(NULL);
+ if (position == PLAYLIST_INSERT_FIRST)
+ position = PLAYLIST_INSERT;
+ } while(++i < pf_tracks.count);
}
+ rb->playlist_sync(NULL);
+ old_playlist = create_new ? center_slide.slide_index : -1;
+ return true;
+}
+
+
+static int pf_add_to_playlist(const char* playlist, bool new_playlist)
+{
+ int fd;
+ int result = 0;
+
+ if (new_playlist)
+ fd = rb->open_utf8(playlist, O_CREAT|O_WRONLY|O_TRUNC);
else
- return;
+ fd = rb->open(playlist, O_CREAT|O_WRONLY|O_APPEND, 0666);
- if (rb->global_settings->playlist_shuffle)
- position = rb->playlist_shuffle(*rb->current_tick, pf_tracks.sel);
-play:
- /* TODO: can we adjust selected_track if !play_selected ?
- * if shuffle, we can't predict the playing track easily, and for either
- * case the track list doesn't get auto scrolled*/
- if(!append)
- rb->playlist_start(position, 0, 0);
- old_playlist = center_slide.slide_index;
- old_shuffle = shuffle;
+ if(fd < 0)
+ return -1;
+
+ rb->reload_directory();
+
+ if (!insert_whole_album)
+ {
+ if (rb->fdprintf(fd, "%s\n", get_track_filename(pf_tracks.sel)) <= 0)
+ result = -1;
+ }
+ else
+ {
+ int i = 0;
+ do {
+ if (rb->fdprintf(fd, "%s\n", get_track_filename(i)) <= 0)
+ {
+ result = -1;
+ break;
+ }
+ rb->yield();
+ } while(++i < pf_tracks.count);
+ }
+ rb->close(fd);
+ return result;
}
+
+
+static bool track_list_ready(void)
+{
+ if (pf_state != pf_show_tracks)
+ {
+#ifdef HAVE_TC_RAMCACHE
+ if (!rb->tagcache_is_in_ram())
#endif
+ rb->splash(0, ID2P(LANG_WAIT));
+ create_track_index(center_slide.slide_index);
+ if (pf_tracks.count == 0)
+ {
+ free_borrowed_tracks();
+ return false;
+ }
+ reset_track_list();
+ }
+ return true;
+}
+
+
+static bool context_menu_ready(void)
+{
+#ifdef USEGSLIB
+ grey_show(false);
+ rb->lcd_clear_display();
+ rb->lcd_update();
+#endif
+ if (!track_list_ready())
+ {
+#ifdef USEGSLIB
+ grey_show(true);
+#endif
+ return false;
+ }
+#if LCD_DEPTH > 1
+#ifdef USEGSLIB
+ rb->lcd_set_foreground(N_BRIGHT(0));
+ rb->lcd_set_background(N_BRIGHT(255));
+#endif
+#endif
+ insert_whole_album = (pf_state != pf_show_tracks) || show_tracks_while_browsing;
+ FOR_NB_SCREENS(i)
+ rb->viewportmanager_theme_enable(i, true, NULL);
+
+ return true;
+}
+
+static void context_menu_cleanup(void)
+{
+ FOR_NB_SCREENS(i)
+ rb->viewportmanager_theme_undo(i, false);
+ if (pf_state != pf_show_tracks)
+ free_borrowed_tracks();
+#ifdef USEGSLIB
+ grey_show(true);
+#endif
+ mylcd_set_drawmode(DRMODE_FG);
+}
+
+
+static int context_menu(void)
+{
+ char album_name[MAX_PATH];
+ char *file_name = get_track_filename(show_tracks_while_browsing ? 0 : pf_tracks.sel);
+ int attr = FILE_ATTR_AUDIO;
+
+ enum {
+ PF_CURRENT_PLAYLIST = 0,
+ PF_CATALOG,
+ PF_ID3_INFO
+ };
+ MENUITEM_STRINGLIST(context_menu, ID2P(LANG_ONPLAY_MENU_TITLE), NULL,
+ ID2P(LANG_PLAYING_NEXT),
+ ID2P(LANG_ADD_TO_PL),
+ ID2P(LANG_MENU_SHOW_ID3_INFO));
+
+ while (1) {
+ switch (rb->do_menu(&context_menu,
+ NULL, NULL, false)) {
+
+ case PF_CURRENT_PLAYLIST:
+ if (insert_whole_album && pf_tracks.count > 1)
+ {
+ attr = ATTR_DIRECTORY;
+ file_name = NULL;
+ }
+ rb->onplay_show_playlist_menu(file_name, attr, &pf_current_playlist_insert);
+ return 0;
+ case PF_CATALOG:
+ if (insert_whole_album)
+ {
+ /* add a leading slash so that catalog_add_to_a_playlist
+ later prefills the name when creating a new playlist */
+ rb->snprintf(album_name, MAX_PATH, "/%s", get_album_name(center_index));
+ rb->fix_path_part(album_name, 1, sizeof(album_name) - 2);
+ file_name = album_name;
+ attr = ATTR_DIRECTORY;
+ }
+
+ rb->onplay_show_playlist_cat_menu(file_name, attr, &pf_add_to_playlist);
+ return 0;
+ case PF_ID3_INFO:
+ return show_id3_info(file_name);
+ case MENU_ATTACHED_USB:
+ return PLUGIN_USB_CONNECTED;
+ default:
+ return 0;
+
+ }
+ }
+}
+
+
+
+/*
+ * Puts selected album's tracks into a newly created playlist and starts playing
+ */
+static bool start_playback(bool return_to_WPS)
+{
+#ifdef USEGSLIB
+ grey_show(false);
+#if LCD_DEPTH > 1
+ rb->lcd_set_background(N_BRIGHT(0));
+ rb->lcd_set_foreground(N_BRIGHT(255));
+#endif
+ rb->lcd_clear_display();
+ rb->lcd_update();
+#else /* if !USEGSLIB */
+ (void) return_to_WPS;
+#endif
+
+ if (!rb->warn_on_pl_erase() || !track_list_ready())
+ {
+#ifdef USEGSLIB
+ grey_show(true);
+#endif
+ return false;
+ }
+
+ insert_whole_album = true;
+ int start_index = pf_tracks.sel;
+ bool shuffle = rb->global_settings->playlist_shuffle;
+ /* can't reuse playlist if it may be out of sync with our track list */
+ if (shuffle || center_slide.slide_index != old_playlist
+ || (old_shuffle != shuffle))
+ {
+ if (!pf_current_playlist_insert(PLAYLIST_REPLACE, false, true))
+ {
+#ifdef USEGSLIB
+ grey_show(true);
+#endif
+ return false;
+ }
+ if (shuffle)
+ start_index = rb->playlist_shuffle(*rb->current_tick, pf_tracks.sel);
+ }
+ rb->playlist_start(start_index, 0, 0);
+ old_shuffle = shuffle;
+#ifdef USEGSLIB
+ if (!return_to_WPS)
+ grey_show(true);
+#endif
+ return true;
+}
+#endif /* PF_PLAYBACK_CAPABLE */
/**
Draw the current album name
*/
static void draw_album_text(void)
{
+ char album_and_year[MAX_PATH];
+
if (pf_cfg.show_album_name == ALBUM_NAME_HIDE)
return;
static int prev_albumtxt_index = -1;
+ static bool prev_show_year = false;
int albumtxt_index;
int char_height;
int albumtxt_x, albumtxt_y, artisttxt_x;
@@ -3469,10 +4340,18 @@ static void draw_album_text(void)
}
albumtxt = get_album_name_idx(albumtxt_index, &album_idx);
+ if (pf_cfg.show_year && pf_idx.album_index[albumtxt_index].year > 0)
+ {
+ rb->snprintf(album_and_year, sizeof(album_and_year), "%s – %d",
+ albumtxt, pf_idx.album_index[albumtxt_index].year);
+ } else
+ rb->snprintf(album_and_year, sizeof(album_and_year), "%s", albumtxt);
+
mylcd_set_foreground(G_BRIGHT(c));
- if (albumtxt_index != prev_albumtxt_index) {
- set_scroll_line(albumtxt, PF_SCROLL_ALBUM);
+ if (albumtxt_index != prev_albumtxt_index || pf_cfg.show_year != prev_show_year) {
+ set_scroll_line(album_and_year, PF_SCROLL_ALBUM);
prev_albumtxt_index = albumtxt_index;
+ prev_show_year = pf_cfg.show_year;
}
char_height = rb->screens[SCREEN_MAIN]->getcharheight();
@@ -3497,7 +4376,7 @@ static void draw_album_text(void)
|| (pf_cfg.show_album_name == ALBUM_AND_ARTIST_BOTTOM)){
if (album_idx != (int) pf_idx.album_untagged_idx)
- mylcd_putsxy(albumtxt_x, albumtxt_y, albumtxt);
+ mylcd_putsxy(albumtxt_x, albumtxt_y, album_and_year);
artisttxt = get_album_artist(albumtxt_index);
set_scroll_line(artisttxt, PF_SCROLL_ARTIST);
@@ -3505,10 +4384,24 @@ static void draw_album_text(void)
int y_offset = char_height + char_height/2;
mylcd_putsxy(artisttxt_x, albumtxt_y + y_offset, artisttxt);
} else {
- mylcd_putsxy(albumtxt_x, albumtxt_y, albumtxt);
+ mylcd_putsxy(albumtxt_x, albumtxt_y, album_and_year);
}
}
+
+static void set_initial_slide(const char* selected_file)
+{
+ if (selected_file)
+ set_current_slide(retrieve_id3(&id3, selected_file) ?
+ id3_get_index(&id3) :
+ pf_cfg.last_album);
+ else
+ set_current_slide(rb->audio_status() ?
+ id3_get_index(rb->audio_current_track()) :
+ pf_cfg.last_album);
+
+}
+
/**
Display an error message and wait for input.
*/
@@ -3524,7 +4417,7 @@ static void error_wait(const char *message)
Main function that also contain the main plasma
algorithm.
*/
-static int pictureflow_main(void)
+static int pictureflow_main(const char* selected_file)
{
int ret = SUCCESS;
@@ -3541,20 +4434,17 @@ static int pictureflow_main(void)
config_set_defaults(&pf_cfg);
configfile_load(CONFIG_FILE, config, CONFIG_NUM_ITEMS, CONFIG_VERSION);
- if(pf_cfg.auto_wps == 0)
- draw_splashscreen(pf_idx.buf, pf_idx.buf_sz);
- if(pf_cfg.backlight_mode == 0) {
- /* Turn off backlight timeout */
+
#ifdef HAVE_BACKLIGHT
+ if(pf_cfg.backlight_mode == 0)
backlight_ignore_timeout();
#endif
- }
+
+ rb->mutex_init(&buf_ctx_mutex);
init_scroll_lines();
init_reflect_table();
- ALIGN_BUFFER(pf_idx.buf, pf_idx.buf_sz, 4);
-
/*Scan will trigger when no file is found or the option was activated*/
if ((pf_cfg.cache_version != CACHE_VERSION)||(load_album_index() < 0)){
rb->splash(HZ/2,"Creating index, please wait");
@@ -3579,32 +4469,30 @@ static int pictureflow_main(void)
return PLUGIN_ERROR;
}
- ALIGN_BUFFER(pf_idx.buf, pf_idx.buf_sz, 4);
- number_of_slides = pf_idx.album_ct;
-
- size_t aa_bufsz = ALIGN_DOWN(pf_idx.buf_sz / 4, 0x4);
+ number_of_slides = pf_idx.album_ct;
+ size_t aa_bufsz = ALIGN_DOWN(pf_idx.buf_sz / 4, sizeof(long));
if (aa_bufsz < DISPLAY_WIDTH * DISPLAY_HEIGHT * sizeof(pix_t))
{
error_wait("Not enough memory for album art cache");
return PLUGIN_ERROR;
}
- pf_idx.buf_sz -= aa_bufsz;
- ALIGN_BUFFER(pf_idx.buf, pf_idx.buf_sz, 4);
+ ALIGN_BUFFER(pf_idx.buf, pf_idx.buf_sz, sizeof(long));
aa_cache.buf = (char*) pf_idx.buf;
aa_cache.buf_sz = aa_bufsz;
+
pf_idx.buf += aa_bufsz;
- ALIGN_BUFFER(pf_idx.buf, pf_idx.buf_sz, 4);
+ pf_idx.buf_sz -= aa_bufsz;
if (!create_empty_slide(pf_cfg.cache_version != CACHE_VERSION)) {
- config_save(CACHE_REBUILD);
+ config_save(CACHE_REBUILD, false);
error_wait("Could not load the empty slide");
return PLUGIN_ERROR;
}
if ((pf_cfg.cache_version != CACHE_VERSION) && !create_albumart_cache()) {
- config_save(CACHE_REBUILD);
+ config_save(CACHE_REBUILD, false);
error_wait("Could not create album art cache");
} else if(aa_cache.inspected < pf_idx.album_ct) {
rb->splash(HZ * 2, "Updating album art cache in background");
@@ -3612,7 +4500,7 @@ static int pictureflow_main(void)
if (pf_cfg.cache_version != CACHE_VERSION)
{
- config_save(CACHE_VERSION);
+ config_save(CACHE_VERSION, pf_cfg.update_albumart);
}
rb->buflib_init(&buf_ctx, (void *)pf_idx.buf, pf_idx.buf_sz);
@@ -3628,29 +4516,16 @@ static int pictureflow_main(void)
return PLUGIN_ERROR;
}
- int i;
-
- /* initialize */
- for (i = 0; i < SLIDE_CACHE_SIZE; i++) {
- pf_sldcache.cache[i].hid = 0;
- pf_sldcache.cache[i].index = 0;
- pf_sldcache.cache[i].next = i + 1;
- pf_sldcache.cache[i].prev = i - 1;
- }
- pf_sldcache.cache[0].prev = i - 1;
- pf_sldcache.cache[i - 1].next = 0;
-
- pf_sldcache.free = 0;
- pf_sldcache.used = -1;
- pf_sldcache.left_idx = -1;
- pf_sldcache.right_idx = -1;
- pf_sldcache.center_idx = -1;
+ initialize_slide_cache();
buffer = LCD_BUF;
pf_state = pf_idle;
pf_tracks.cur_idx = -1;
+ pf_tracks.borrowed = 0;
+ pf_tracks.used = 0;
+
extra_fade = 0;
slide_frame = 0;
step = 0;
@@ -3659,7 +4534,7 @@ static int pictureflow_main(void)
recalc_offsets();
reset_slides();
- set_current_slide(get_wps_current_index());
+ set_initial_slide(selected_file);
char fpstxt[10];
int button;
@@ -3699,6 +4574,7 @@ static int pictureflow_main(void)
instant_update = true;
break;
case pf_cover_out:
+ show_tracks_while_browsing = false;
update_cover_out_animation();
render_all_slides();
instant_update = true;
@@ -3707,8 +4583,14 @@ static int pictureflow_main(void)
show_track_list();
break;
case pf_idle:
+ show_tracks_while_browsing = false;
render_all_slides();
- incremental_albumart_cache(false);
+ if (aa_cache.inspected < pf_idx.album_ct)
+ {
+ buf_ctx_lock();
+ incremental_albumart_cache(false);
+ buf_ctx_unlock();
+ }
break;
}
@@ -3718,16 +4600,24 @@ static int pictureflow_main(void)
last_update = current_update;
frames = 0;
}
- /* Draw FPS */
- if (pf_cfg.show_fps)
+ /* Draw FPS or draw percentage of already built album cache */
+ if (pf_cfg.show_fps || aa_cache.inspected < pf_idx.album_ct)
{
#ifdef USEGSLIB
mylcd_set_foreground(G_BRIGHT(255));
#else
mylcd_set_foreground(G_PIX(255,0,0));
#endif
- rb->snprintf(fpstxt, sizeof(fpstxt), "FPS: %d", fps);
- if (pf_cfg.show_album_name == ALBUM_NAME_TOP)
+ if(aa_cache.inspected >= pf_idx.album_ct)
+ rb->snprintf(fpstxt, sizeof(fpstxt), "FPS: %d", fps);
+ else
+ {
+ int progress_pct = 100 * aa_cache.inspected / pf_idx.album_ct;
+ rb->snprintf(fpstxt, sizeof(fpstxt), "%d %%", progress_pct);
+ }
+
+ if (pf_cfg.show_album_name == ALBUM_NAME_TOP ||
+ pf_cfg.show_album_name == ALBUM_AND_ARTIST_TOP)
fpstxt_y = LCD_HEIGHT -
rb->screens[SCREEN_MAIN]->getcharheight();
else
@@ -3738,7 +4628,8 @@ static int pictureflow_main(void)
/* Copy offscreen buffer to LCD and give time to other threads */
- mylcd_update();
+ if (is_initial_slide == false)
+ mylcd_update();
rb->yield();
/*/ Handle buttons */
@@ -3755,21 +4646,29 @@ static int pictureflow_main(void)
case PF_WPS:
return PLUGIN_GOTO_WPS;
case PF_BACK:
- if ( pf_state == pf_show_tracks )
+ if (show_tracks_while_browsing)
+ show_tracks_while_browsing = false;
+ else if (pf_state == pf_show_tracks)
{
- rb->buflib_buffer_in(&buf_ctx, pf_tracks.borrowed);
- pf_tracks.borrowed = 0;
- pf_tracks.cur_idx = -1;
pf_state = pf_cover_out;
+ free_borrowed_tracks();
}
- if (pf_state == pf_idle || pf_state == pf_scrolling)
+ else if (pf_state == pf_cover_in)
+ reverse_animation();
+ else if (pf_state == pf_cover_out)
+ skip_animation_to_idle_state();
+ else if (pf_state == pf_idle || pf_state == pf_scrolling)
return PLUGIN_OK;
break;
case PF_MENU:
#ifdef USEGSLIB
grey_show(false);
#endif
+ FOR_NB_SCREENS(i)
+ rb->viewportmanager_theme_enable(i, true, NULL);
ret = main_menu();
+ FOR_NB_SCREENS(i)
+ rb->viewportmanager_theme_undo(i, false);
if ( ret == -2 ) return PLUGIN_GOTO_WPS;
if ( ret == -1 ) return PLUGIN_OK;
if ( ret != 0 ) return ret;
@@ -3782,7 +4681,17 @@ static int pictureflow_main(void)
case PF_NEXT:
case PF_NEXT_REPEAT:
if ( pf_state == pf_show_tracks )
- select_next_track();
+ {
+ if (show_tracks_while_browsing)
+ select_next_album();
+ else
+ select_next_track();
+ }
+ else if (pf_state == pf_cover_in)
+ skip_animation_to_show_tracks();
+ else if (pf_state == pf_cover_out)
+ skip_animation_to_idle_state();
+
if ( pf_state == pf_idle || pf_state == pf_scrolling )
show_next_slide();
break;
@@ -3790,24 +4699,74 @@ static int pictureflow_main(void)
case PF_PREV:
case PF_PREV_REPEAT:
if ( pf_state == pf_show_tracks )
- select_prev_track();
+ {
+ if (show_tracks_while_browsing)
+ select_prev_album();
+ else
+ select_prev_track();
+ }
+ else if (pf_state == pf_cover_in)
+ skip_animation_to_show_tracks();
+ else if (pf_state == pf_cover_out)
+ skip_animation_to_idle_state();
+
if ( pf_state == pf_idle || pf_state == pf_scrolling )
show_previous_slide();
break;
+ case PF_SORTING_NEXT:
+ sort_albums((pf_cfg.sort_albums_by + 1) % SORT_VALUES_SIZE, false);
+ break;
+ case PF_SORTING_PREV:
+ sort_albums((pf_cfg.sort_albums_by + (SORT_VALUES_SIZE - 1)) % SORT_VALUES_SIZE, false);
+ break;
+ case PF_JMP:
+ if (pf_state == pf_idle || pf_state == pf_scrolling)
+ {
+ int new_idx = jmp_idx_next();
+ if (new_idx != center_index)
+ {
+ pf_state = pf_idle;
+ set_current_slide(new_idx);
+ }
+ }
+ else if ( pf_state == pf_show_tracks )
+ select_next_album();
+ break;
+ case PF_JMP_PREV:
+ if (pf_state == pf_idle || pf_state == pf_scrolling)
+ {
+ int new_idx = jmp_idx_prev();
+ if (new_idx != center_index)
+ {
+ pf_state = pf_idle;
+ set_current_slide(new_idx);
+ }
+ }
+ else if ( pf_state == pf_show_tracks )
+ select_prev_album();
+ else if (pf_state == pf_cover_in)
+ reverse_animation();
+ else if (pf_state == pf_cover_out)
+ skip_animation_to_idle_state();
+ break;
#if PF_PLAYBACK_CAPABLE
case PF_CONTEXT:
- if ( pf_cfg.auto_wps != 0 ) {
- if( pf_state == pf_idle ) {
- create_track_index(center_slide.slide_index);
- reset_track_list();
- start_playback(true);
- rb->splash(HZ*2, ID2P(LANG_ADDED_TO_PLAYLIST));
+ if (pf_state == pf_idle || pf_state == pf_scrolling ||
+ pf_state == pf_show_tracks || pf_state == pf_cover_out)
+ {
+ if ( pf_state == pf_scrolling)
+ {
+ set_current_slide(target);
+ pf_state = pf_idle;
}
- else if( pf_state == pf_show_tracks ) {
- rb->playlist_insert_track(NULL, get_track_filename(pf_tracks.sel),
- PLAYLIST_INSERT_LAST, false, true);
- rb->playlist_sync(NULL);
- rb->splash(HZ*2, ID2P(LANG_ADDED_TO_PLAYLIST));
+ else if (pf_state == pf_cover_out)
+ skip_animation_to_idle_state();
+
+ if (context_menu_ready())
+ {
+ ret = context_menu();
+ context_menu_cleanup();
+ if ( ret != 0 ) return ret;
}
}
break;
@@ -3818,26 +4777,33 @@ static int pictureflow_main(void)
break;
}
case PF_SELECT:
- if ( pf_state == pf_idle ) {
+ if ( pf_state == pf_idle || pf_state == pf_scrolling) {
+ if (pf_state == pf_scrolling)
+ set_current_slide(target);
#if PF_PLAYBACK_CAPABLE
if(pf_cfg.auto_wps == 1) {
- create_track_index(center_slide.slide_index);
- reset_track_list();
- start_playback(false);
- pf_cfg.last_album = center_index;
- return PLUGIN_GOTO_WPS;
+ if (start_playback(true))
+ return PLUGIN_GOTO_WPS;
}
else
#endif
pf_state = pf_cover_in;
}
- else if ( pf_state == pf_show_tracks ) {
+ else if (pf_state == pf_cover_out)
+ reverse_animation();
+ else if (pf_state == pf_cover_in)
+ skip_animation_to_show_tracks();
+ else if (pf_state == pf_show_tracks)
+ {
+ if (show_tracks_while_browsing)
+ show_tracks_while_browsing = false;
#if PF_PLAYBACK_CAPABLE
- start_playback(false);
- if(pf_cfg.auto_wps != 0) {
- pf_cfg.last_album = center_index;
- return PLUGIN_GOTO_WPS;
+ else if(pf_cfg.auto_wps != 0) {
+ if (start_playback(true))
+ return PLUGIN_GOTO_WPS;
}
+ else
+ start_playback(false);
#endif
}
break;
@@ -3856,20 +4822,16 @@ enum plugin_status plugin_start(const void *parameter)
lcd_fb = vp_main->buffer->fb_ptr;
int ret;
- (void) parameter;
+ const char *file = parameter;
void * buf;
size_t buf_size;
- bool prompt = (parameter && (((char *) parameter)[0] == ACTIVITY_MAINMENU));
+ bool file_id3 = (parameter && (((char *) parameter)[0] == '/'));
- if (!check_database(prompt))
+ if (!check_database())
{
- if (prompt)
- return PLUGIN_OK;
- else
- error_wait("Please enable database");
-
- return PLUGIN_ERROR;
+ error_wait("Please enable database");
+ return PLUGIN_OK;
}
atexit(cleanup);
@@ -3907,11 +4869,18 @@ enum plugin_status plugin_start(const void *parameter)
pf_idx.buf = buf;
pf_idx.buf_sz = buf_size;
- ret = pictureflow_main();
+ ret = file_id3 ? pictureflow_main(file) : pictureflow_main(NULL);
if ( ret == PLUGIN_OK || ret == PLUGIN_GOTO_WPS) {
+ if (pf_state == pf_scrolling)
+ pf_cfg.last_album = target;
+ else
+ pf_cfg.last_album = center_index;
if (configfile_save(CONFIG_FILE, config, CONFIG_NUM_ITEMS,
CONFIG_VERSION))
{
+#ifdef USEGSLIB
+ grey_show(false);
+#endif
rb->splash(HZ, ID2P(LANG_ERROR_WRITING_CONFIG));
ret = PLUGIN_ERROR;
}
diff --git a/apps/plugins/pitch_detector.c b/apps/plugins/pitch_detector.c
index 4ae43b3236..f7d1219445 100644
--- a/apps/plugins/pitch_detector.c
+++ b/apps/plugins/pitch_detector.c
@@ -503,19 +503,19 @@ static bool main_menu(void)
rb->set_option(
"Algorithm Pickiness (Lower -> more discriminating)",
&settings.yin_threshold,
- INT, yin_threshold_text,
+ RB_INT, yin_threshold_text,
sizeof(yin_threshold_text) / sizeof(yin_threshold_text[0]),
NULL);
break;
case 5:
rb->set_option("Display Accidentals As",
&settings.use_sharps,
- BOOL, accidental_text, 2, NULL);
+ RB_BOOL, accidental_text, 2, NULL);
break;
case 6:
rb->set_option("Key Transposition",
&settings.key_transposition,
- INT, transpose_text, 12, NULL);
+ RB_INT, transpose_text, 12, NULL);
break;
case 7:
rb->set_bool("Display Frequency (Hz)",
@@ -1013,6 +1013,11 @@ static void record_and_get_pitch(void)
break;
case PLA_CANCEL:
+#if (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_3G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_4G_PAD)
+ case PLA_UP:
+#endif
rb->pcm_stop_recording();
quit = main_menu();
if(!quit)
diff --git a/apps/plugins/pitch_screen.c b/apps/plugins/pitch_screen.c
new file mode 100644
index 0000000000..e24e0240a2
--- /dev/null
+++ b/apps/plugins/pitch_screen.c
@@ -0,0 +1,1279 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2002 Björn Stenberg
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#include "plugin.h"
+#include "lib/icon_helper.h"
+#include "lib/arg_helper.h"
+
+#define ICON_BORDER 12 /* icons are currently 7x8, so add ~2 pixels */
+ /* on both sides when drawing */
+
+#define PITCH_MAX (200 * PITCH_SPEED_PRECISION)
+#define PITCH_MIN (50 * PITCH_SPEED_PRECISION)
+#define PITCH_SMALL_DELTA (PITCH_SPEED_PRECISION / 10) /* .1% */
+#define PITCH_BIG_DELTA (PITCH_SPEED_PRECISION) /* 1% */
+#define PITCH_NUDGE_DELTA (2 * PITCH_SPEED_PRECISION) /* 2% */
+
+#define SPEED_SMALL_DELTA (PITCH_SPEED_PRECISION / 10) /* .1% */
+#define SPEED_BIG_DELTA (PITCH_SPEED_PRECISION) /* 1% */
+
+#define SEMITONE_SMALL_DELTA (PITCH_SPEED_PRECISION / 10) /* 10 cents */
+#define SEMITONE_BIG_DELTA PITCH_SPEED_PRECISION /* 1 semitone */
+
+#define PVAR_VERBOSE 0x01
+#define PVAR_GUI 0x02
+struct pvars
+{
+ int32_t speed;
+ int32_t pitch;
+ int32_t stretch;
+ int32_t flags;
+};
+static struct pvars pitch_vars;
+
+enum
+{
+ PITCH_TOP = 0,
+ PITCH_MID,
+ PITCH_BOTTOM,
+ PITCH_ITEM_COUNT,
+};
+
+/* This is a table of semitone percentage values of the appropriate
+ precision (based on PITCH_SPEED_PRECISION). Note that these are
+ all constant expressions, which will be evaluated at compile time,
+ so no need to worry about how complex the expressions look.
+ That's just to get the precision right.
+
+ I calculated these values, starting from 50, as
+
+ x(n) = 50 * 2^(n/12)
+
+ All that math in each entry simply converts the float constant
+ to an integer equal to PITCH_SPEED_PRECISION times the float value,
+ with as little precision loss as possible (i.e. correctly rounding
+ the last digit).
+*/
+#define TO_INT_WITH_PRECISION(x) \
+ ( (unsigned short)(((x) * PITCH_SPEED_PRECISION * 10 + 5) / 10) )
+
+static const unsigned short semitone_table[] =
+{
+ TO_INT_WITH_PRECISION(50.00000000), /* Octave lower */
+ TO_INT_WITH_PRECISION(52.97315472),
+ TO_INT_WITH_PRECISION(56.12310242),
+ TO_INT_WITH_PRECISION(59.46035575),
+ TO_INT_WITH_PRECISION(62.99605249),
+ TO_INT_WITH_PRECISION(66.74199271),
+ TO_INT_WITH_PRECISION(70.71067812),
+ TO_INT_WITH_PRECISION(74.91535384),
+ TO_INT_WITH_PRECISION(79.37005260),
+ TO_INT_WITH_PRECISION(84.08964153),
+ TO_INT_WITH_PRECISION(89.08987181),
+ TO_INT_WITH_PRECISION(94.38743127),
+ TO_INT_WITH_PRECISION(100.0000000), /* Normal sound */
+ TO_INT_WITH_PRECISION(105.9463094),
+ TO_INT_WITH_PRECISION(112.2462048),
+ TO_INT_WITH_PRECISION(118.9207115),
+ TO_INT_WITH_PRECISION(125.9921049),
+ TO_INT_WITH_PRECISION(133.4839854),
+ TO_INT_WITH_PRECISION(141.4213562),
+ TO_INT_WITH_PRECISION(149.8307077),
+ TO_INT_WITH_PRECISION(158.7401052),
+ TO_INT_WITH_PRECISION(168.1792831),
+ TO_INT_WITH_PRECISION(178.1797436),
+ TO_INT_WITH_PRECISION(188.7748625),
+ TO_INT_WITH_PRECISION(200.0000000) /* Octave higher */
+};
+
+#define NUM_SEMITONES ((int)(sizeof(semitone_table)/sizeof(semitone_table[0])))
+#define SEMITONE_END (NUM_SEMITONES/2)
+#define SEMITONE_START (-SEMITONE_END)
+
+/* A table of values for approximating the cent curve with
+ linear interpolation. Multipy the next lowest semitone
+ by this much to find the corresponding cent percentage.
+
+ These values were calculated as
+ x(n) = 100 * 2^(n * 20/1200)
+*/
+
+static const unsigned short cent_interp[] =
+{
+ TO_INT_WITH_PRECISION(100.0000000),
+ TO_INT_WITH_PRECISION(101.1619440),
+ TO_INT_WITH_PRECISION(102.3373892),
+ TO_INT_WITH_PRECISION(103.5264924),
+ TO_INT_WITH_PRECISION(104.7294123),
+ /* this one's the next semitone but we have it here for convenience */
+ TO_INT_WITH_PRECISION(105.9463094),
+};
+
+int viewport_get_nb_lines(const struct viewport *vp)
+{
+ return vp->height/rb->font_get(vp->font)->height;
+}
+#if 0 /* replaced with cbmp_get_icon(CBMP_Mono_7x8, Icon_ABCD, &w, &h) */
+enum icons_7x8 {
+ Icon_Plug,
+ Icon_USBPlug,
+ Icon_Mute,
+ Icon_Play,
+ Icon_Stop,
+ Icon_Pause,
+ Icon_FastForward,
+ Icon_FastBackward,
+ Icon_Record,
+ Icon_RecPause,
+ Icon_Radio,
+ Icon_Radio_Mute,
+ Icon_Repeat,
+ Icon_RepeatOne,
+ Icon_Shuffle,
+ Icon_DownArrow,
+ Icon_UpArrow,
+ Icon_RepeatAB,
+ Icon7x8Last
+};
+
+const unsigned char bitmap_icons_7x8[][7] =
+{
+ {0x08,0x1c,0x3e,0x3e,0x3e,0x14,0x14}, /* Power plug */
+ {0x1c,0x14,0x3e,0x2a,0x22,0x1c,0x08}, /* USB plug */
+ {0x01,0x1e,0x1c,0x3e,0x7f,0x20,0x40}, /* Speaker mute */
+ {0x00,0x7f,0x7f,0x3e,0x1c,0x08,0x00}, /* Play */
+ {0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f}, /* Stop */
+ {0x00,0x7f,0x7f,0x00,0x7f,0x7f,0x00}, /* Pause */
+ {0x7f,0x3e,0x1c,0x7f,0x3e,0x1c,0x08}, /* Fast forward */
+ {0x08,0x1c,0x3e,0x7f,0x1c,0x3e,0x7f}, /* Fast backward */
+ {0x1c,0x3e,0x7f,0x7f,0x7f,0x3e,0x1c}, /* Record */
+ {0x1c,0x3e,0x7f,0x00,0x7f,0x3e,0x1c}, /* Record pause */
+ {0x40,0xa0,0xa0,0xa0,0x7f,0x02,0x02}, /* Radio on */
+ {0x42,0xa4,0xa8,0xb0,0x7f,0x22,0x42}, /* Radio mute */
+ {0x44,0x4e,0x5f,0x44,0x44,0x44,0x38}, /* Repeat playmode */
+ {0x44,0x4e,0x5f,0x44,0x38,0x02,0x7f}, /* Repeat-one playmode */
+ {0x3e,0x41,0x51,0x41,0x45,0x41,0x3e}, /* Shuffle playmode (dice) */
+ {0x04,0x0c,0x1c,0x3c,0x1c,0x0c,0x04}, /* Down-arrow */
+ {0x20,0x30,0x38,0x3c,0x38,0x30,0x20}, /* Up-arrow */
+ {0x7f,0x04,0x4e,0x5f,0x44,0x38,0x7f} /* Repeat-AB playmode */
+};
+#endif
+
+/* Number of cents between entries in the cent_interp table */
+#define CENT_INTERP_INTERVAL 20
+#define CENT_INTERP_NUM ((int)(sizeof(cent_interp)/sizeof(cent_interp[0])))
+
+/* This stores whether the pitch and speed are at their own limits */
+/* or that of the timestretching algorithm */
+static bool at_limit = false;
+
+/*
+ *
+ * The pitchscreen is divided into 3 viewports (each row is a viewport)
+ * Then each viewport is again divided into 3 colums, each showsing some infos
+ * Additionally, on touchscreen, each cell represents a button
+ *
+ * Below a sketch describing what each cell will show (what's drawn on it)
+ * --------------------------
+ * | | | | <-- pitch up in the middle (text and button)
+ * | | | | <-- arrows for mode toggling on the sides for touchscreen
+ * |------------------------|
+ * | | | | <-- semitone/speed up/down on the sides
+ * | | | | <-- reset pitch&speed in the middle
+ * |------------------------|
+ * | | | | <-- pitch down in the middle
+ * | | | | <-- Two "OK" for exit on the sides for touchscreen
+ * |------------------------|
+ *
+ *
+ */
+
+static void speak_pitch_mode(bool enqueue)
+{
+ bool timestretch_mode = rb->global_settings->pitch_mode_timestretch && rb->dsp_timestretch_available();
+ if (timestretch_mode)
+ rb->talk_id(VOICE_PITCH_TIMESTRETCH_MODE, enqueue);
+ if (rb->global_settings->pitch_mode_semitone)
+ rb->talk_id(VOICE_PITCH_SEMITONE_MODE, timestretch_mode ? true : enqueue);
+ else
+ rb->talk_id(VOICE_PITCH_ABSOLUTE_MODE, timestretch_mode ? true : enqueue);
+ return;
+}
+
+/*
+ * Fixes the viewports so they represent the 3 rows, and adds a little margin
+ * on all sides for the icons (which are drawn outside of the grid
+ *
+ * The modified viewports need to be passed to the touchscreen handling function
+ **/
+static void pitchscreen_fix_viewports(struct viewport *parent,
+ struct viewport pitch_viewports[PITCH_ITEM_COUNT])
+{
+ int i, font_height;
+ font_height = rb->font_get(parent->font)->height;
+ for (i = 0; i < PITCH_ITEM_COUNT; i++)
+ {
+ pitch_viewports[i] = *parent;
+ pitch_viewports[i].height = parent->height / PITCH_ITEM_COUNT;
+ pitch_viewports[i].x += ICON_BORDER;
+ pitch_viewports[i].width -= 2*ICON_BORDER;
+ }
+ pitch_viewports[PITCH_TOP].y += ICON_BORDER;
+ pitch_viewports[PITCH_TOP].height -= ICON_BORDER;
+
+ if(pitch_viewports[PITCH_MID].height < font_height * 2)
+ pitch_viewports[PITCH_MID].height = font_height * 2;
+
+ pitch_viewports[PITCH_MID].y = pitch_viewports[PITCH_TOP].y
+ + pitch_viewports[PITCH_TOP].height;
+
+ pitch_viewports[PITCH_BOTTOM].y = pitch_viewports[PITCH_MID].y
+ + pitch_viewports[PITCH_MID].height;
+
+ pitch_viewports[PITCH_BOTTOM].height -= ICON_BORDER;
+}
+
+/* must be called before pitchscreen_draw, or within
+ * since it neither clears nor updates the display */
+static void pitchscreen_draw_icons(struct screen *display,
+ struct viewport *parent)
+{
+
+ display->set_viewport(parent);
+ int w, h;
+ const unsigned char* uparrow = cbmp_get_icon(CBMP_Mono_7x8, Icon_UpArrow, &w, &h);
+ if (uparrow)
+ display->mono_bitmap(uparrow, parent->width/2 - 3, 2, w, h);
+
+ const unsigned char* dnarrow = cbmp_get_icon(CBMP_Mono_7x8, Icon_DownArrow, &w, &h);
+ if (dnarrow)
+ display->mono_bitmap(dnarrow, parent->width /2 - 3, parent->height - 10, w, h);
+
+ const unsigned char* fastfwd = cbmp_get_icon(CBMP_Mono_7x8, Icon_FastForward, &w, &h);
+ if (fastfwd)
+ display->mono_bitmap(fastfwd, parent->width - 10, parent->height /2 - 4, 7, 8);
+
+ const unsigned char* fastrew = cbmp_get_icon(CBMP_Mono_7x8, Icon_FastBackward, &w, &h);
+ if (fastrew)
+ display->mono_bitmap(fastrew, 2, parent->height /2 - 4, w, h);
+
+ display->update_viewport();
+
+}
+
+static void pitchscreen_draw(struct screen *display, int max_lines,
+ struct viewport pitch_viewports[PITCH_ITEM_COUNT],
+ int32_t pitch, int32_t semitone
+ ,int32_t speed
+ )
+{
+ const char* ptr;
+ char buf[32];
+ int w, h;
+ bool show_lang_pitch;
+ struct viewport *last_vp = NULL;
+
+ /* "Pitch up/Pitch down" - hide for a small screen,
+ * the text is drawn centered automatically
+ *
+ * note: this assumes 5 lines always fit on a touchscreen (should be
+ * reasonable) */
+ if (max_lines >= 5)
+ {
+ int w, h;
+ struct viewport *vp = &pitch_viewports[PITCH_TOP];
+ last_vp = display->set_viewport(vp);
+ display->clear_viewport();
+#ifdef HAVE_TOUCHSCREEN
+ /* two arrows in the top row, left and right column */
+ char *arrows[] = { "<", ">"};
+ display->getstringsize(arrows[0], &w, &h);
+ display->putsxy(0, vp->height/2 - h/2, arrows[0]);
+ display->putsxy(vp->width - w, vp->height/2 - h/2, arrows[1]);
+#endif
+ /* UP: Pitch Up */
+ if (rb->global_settings->pitch_mode_semitone)
+ ptr = rb->str(LANG_PITCH_UP_SEMITONE);
+ else
+ ptr = rb->str(LANG_PITCH_UP);
+
+ display->getstringsize(ptr, &w, NULL);
+ /* draw text */
+ display->putsxy(vp->width/2 - w/2, 0, ptr);
+ display->update_viewport();
+
+ /* DOWN: Pitch Down */
+ vp = &pitch_viewports[PITCH_BOTTOM];
+ display->set_viewport(vp);
+ display->clear_viewport();
+
+#ifdef HAVE_TOUCHSCREEN
+ ptr = rb->str(LANG_KBD_OK);
+ display->getstringsize(ptr, &w, &h);
+ /* one OK in the middle first column of the vp (at half height) */
+ display->putsxy(vp->width/6 - w/2, vp->height/2 - h/2, ptr);
+ /* one OK in the middle of the last column of the vp (at half height) */
+ display->putsxy(5*vp->width/6 - w/2, vp->height/2 - h/2, ptr);
+#endif
+ if (rb->global_settings->pitch_mode_semitone)
+ ptr = rb->str(LANG_PITCH_DOWN_SEMITONE);
+ else
+ ptr = rb->str(LANG_PITCH_DOWN);
+ display->getstringsize(ptr, &w, &h);
+ /* draw text */
+ display->putsxy(vp->width/2 - w/2, vp->height - h, ptr);
+ display->update_viewport();
+ }
+
+ /* Middle section */
+ display->set_viewport(&pitch_viewports[PITCH_MID]);
+ display->clear_viewport();
+ int width_used = 0;
+
+ /* Middle section upper line - hide for a small screen */
+ if ((show_lang_pitch = (max_lines >= 3)))
+ {
+ if(rb->global_settings->pitch_mode_timestretch)
+ {
+ /* Pitch:XXX.X% */
+ if(rb->global_settings->pitch_mode_semitone)
+ {
+ rb->snprintf(buf, sizeof(buf), "%s: %s%d.%02d", rb->str(LANG_PITCH),
+ semitone >= 0 ? "+" : "-",
+ abs(semitone / PITCH_SPEED_PRECISION),
+ abs((semitone % PITCH_SPEED_PRECISION) /
+ (PITCH_SPEED_PRECISION / 100))
+ );
+ }
+ else
+ {
+ rb->snprintf(buf, sizeof(buf), "%s: %ld.%ld%%", rb->str(LANG_PITCH),
+ pitch / PITCH_SPEED_PRECISION,
+ (pitch % PITCH_SPEED_PRECISION) /
+ (PITCH_SPEED_PRECISION / 10));
+ }
+ }
+ else
+ {
+ /* Rate */
+ rb->snprintf(buf, sizeof(buf), "%s:", rb->str(LANG_PLAYBACK_RATE));
+ }
+ display->getstringsize(buf, &w, &h);
+ display->putsxy((pitch_viewports[PITCH_MID].width / 2) - (w / 2),
+ (pitch_viewports[PITCH_MID].height / 2) - h, buf);
+ if (w > width_used)
+ width_used = w;
+ }
+
+ /* Middle section lower line */
+ /* "Speed:XXX%" */
+ if(rb->global_settings->pitch_mode_timestretch)
+ {
+ rb->snprintf(buf, sizeof(buf), "%s: %ld.%ld%%", rb->str(LANG_SPEED),
+ speed / PITCH_SPEED_PRECISION,
+ (speed % PITCH_SPEED_PRECISION) / (PITCH_SPEED_PRECISION / 10));
+ }
+ else
+ {
+ if(rb->global_settings->pitch_mode_semitone)
+ {
+ rb->snprintf(buf, sizeof(buf), "%s%d.%02d",
+ semitone >= 0 ? "+" : "-",
+ abs(semitone / PITCH_SPEED_PRECISION),
+ abs((semitone % PITCH_SPEED_PRECISION) /
+ (PITCH_SPEED_PRECISION / 100))
+ );
+ }
+ else
+ {
+ rb->snprintf(buf, sizeof(buf), "%ld.%ld%%",
+ pitch / PITCH_SPEED_PRECISION,
+ (pitch % PITCH_SPEED_PRECISION) / (PITCH_SPEED_PRECISION / 10));
+ }
+ }
+
+ display->getstringsize(buf, &w, &h);
+ display->putsxy((pitch_viewports[PITCH_MID].width / 2) - (w / 2),
+ show_lang_pitch ? (pitch_viewports[PITCH_MID].height / 2) :
+ (pitch_viewports[PITCH_MID].height / 2) - (h / 2),
+ buf);
+ if (w > width_used)
+ width_used = w;
+
+ /* "limit" and "timestretch" labels */
+ if (max_lines >= 7)
+ {
+ if(at_limit)
+ {
+ const char * const p = rb->str(LANG_STRETCH_LIMIT);
+ display->getstringsize(p, &w, &h);
+ display->putsxy((pitch_viewports[PITCH_MID].width / 2) - (w / 2),
+ (pitch_viewports[PITCH_MID].height / 2) + h, p);
+ if (w > width_used)
+ width_used = w;
+ }
+ }
+
+ /* Middle section left/right labels */
+ const char *leftlabel = "-2%";
+ const char *rightlabel = "+2%";
+ if (rb->global_settings->pitch_mode_timestretch)
+ {
+ leftlabel = "<<";
+ rightlabel = ">>";
+ }
+
+ /* Only display if they fit */
+ display->getstringsize(leftlabel, &w, &h);
+ width_used += w;
+ display->getstringsize(rightlabel, &w, &h);
+ width_used += w;
+
+ if (width_used <= pitch_viewports[PITCH_MID].width)
+ {
+ display->putsxy(0, (pitch_viewports[PITCH_MID].height / 2) - (h / 2),
+ leftlabel);
+ display->putsxy((pitch_viewports[PITCH_MID].width - w),
+ (pitch_viewports[PITCH_MID].height / 2) - (h / 2),
+ rightlabel);
+ }
+ display->update_viewport();
+ display->set_viewport(last_vp);
+}
+
+static int32_t pitch_increase(int32_t pitch, int32_t pitch_delta, bool allow_cutoff
+ /* need this to maintain correct pitch/speed caps */
+ , int32_t speed
+ )
+{
+ int32_t new_pitch;
+ int32_t new_stretch;
+ at_limit = false;
+
+ if (pitch_delta < 0)
+ {
+ /* for large jumps, snap up to whole numbers */
+ if(allow_cutoff && pitch_delta <= -PITCH_SPEED_PRECISION &&
+ (pitch + pitch_delta) % PITCH_SPEED_PRECISION != 0)
+ {
+ pitch_delta += PITCH_SPEED_PRECISION - ((pitch + pitch_delta) % PITCH_SPEED_PRECISION);
+ }
+
+ new_pitch = pitch + pitch_delta;
+
+ if (new_pitch < PITCH_MIN)
+ {
+ if (!allow_cutoff)
+ {
+ return pitch;
+ }
+ new_pitch = PITCH_MIN;
+ at_limit = true;
+ }
+ }
+ else if (pitch_delta > 0)
+ {
+ /* for large jumps, snap down to whole numbers */
+ if(allow_cutoff && pitch_delta >= PITCH_SPEED_PRECISION &&
+ (pitch + pitch_delta) % PITCH_SPEED_PRECISION != 0)
+ {
+ pitch_delta -= (pitch + pitch_delta) % PITCH_SPEED_PRECISION;
+ }
+
+ new_pitch = pitch + pitch_delta;
+
+ if (new_pitch > PITCH_MAX)
+ {
+ if (!allow_cutoff)
+ return pitch;
+ new_pitch = PITCH_MAX;
+ at_limit = true;
+ }
+ }
+ else
+ {
+ /* pitch_delta == 0 -> no real change */
+ return pitch;
+ }
+ if (rb->dsp_timestretch_available())
+ {
+ /* increase the multiple to increase precision of this calculation */
+ new_stretch = GET_STRETCH(new_pitch, speed);
+ if(new_stretch < STRETCH_MIN)
+ {
+ /* we have to ignore allow_cutoff, because we can't have the */
+ /* stretch go higher than STRETCH_MAX */
+ new_pitch = GET_PITCH(speed, STRETCH_MIN);
+ }
+ else if(new_stretch > STRETCH_MAX)
+ {
+ /* we have to ignore allow_cutoff, because we can't have the */
+ /* stretch go higher than STRETCH_MAX */
+ new_pitch = GET_PITCH(speed, STRETCH_MAX);
+ }
+
+ if(new_stretch >= STRETCH_MAX ||
+ new_stretch <= STRETCH_MIN)
+ {
+ at_limit = true;
+ }
+ }
+
+ rb->sound_set_pitch(new_pitch);
+
+ return new_pitch;
+}
+
+static int32_t get_semitone_from_pitch(int32_t pitch)
+{
+ int semitone = 0;
+ int32_t fractional_index = 0;
+
+ while(semitone < NUM_SEMITONES - 1 &&
+ pitch >= semitone_table[semitone + 1])
+ {
+ semitone++;
+ }
+
+
+ /* now find the fractional part */
+ while(pitch > (cent_interp[fractional_index + 1] *
+ semitone_table[semitone] / PITCH_SPEED_100))
+ {
+ /* Check to make sure fractional_index isn't too big */
+ /* This should never happen. */
+ if(fractional_index >= CENT_INTERP_NUM - 1)
+ {
+ break;
+ }
+ fractional_index++;
+ }
+
+ int32_t semitone_pitch_a = cent_interp[fractional_index] *
+ semitone_table[semitone] /
+ PITCH_SPEED_100;
+ int32_t semitone_pitch_b = cent_interp[fractional_index + 1] *
+ semitone_table[semitone] /
+ PITCH_SPEED_100;
+ /* this will be the integer offset from the cent_interp entry */
+ int32_t semitone_frac_ofs = (pitch - semitone_pitch_a) * CENT_INTERP_INTERVAL /
+ (semitone_pitch_b - semitone_pitch_a);
+ semitone = (semitone + SEMITONE_START) * PITCH_SPEED_PRECISION +
+ fractional_index * CENT_INTERP_INTERVAL +
+ semitone_frac_ofs;
+
+ return semitone;
+}
+
+static int32_t get_pitch_from_semitone(int32_t semitone)
+{
+ int32_t adjusted_semitone = semitone - SEMITONE_START * PITCH_SPEED_PRECISION;
+
+ /* Find the index into the semitone table */
+ int32_t semitone_index = (adjusted_semitone / PITCH_SPEED_PRECISION);
+
+ /* set pitch to the semitone's integer part value */
+ int32_t pitch = semitone_table[semitone_index];
+ /* get the range of the cent modification for future calculation */
+ int32_t pitch_mod_a =
+ cent_interp[(adjusted_semitone % PITCH_SPEED_PRECISION) /
+ CENT_INTERP_INTERVAL];
+ int32_t pitch_mod_b =
+ cent_interp[(adjusted_semitone % PITCH_SPEED_PRECISION) /
+ CENT_INTERP_INTERVAL + 1];
+ /* figure out the cent mod amount based on the semitone fractional value */
+ int32_t pitch_mod = pitch_mod_a + (pitch_mod_b - pitch_mod_a) *
+ (adjusted_semitone % CENT_INTERP_INTERVAL) / CENT_INTERP_INTERVAL;
+
+ /* modify pitch based on the mod amount we just calculated */
+ return (pitch * pitch_mod + PITCH_SPEED_100 / 2) / PITCH_SPEED_100;
+}
+
+static int32_t pitch_increase_semitone(int32_t pitch,
+ int32_t current_semitone,
+ int32_t semitone_delta
+ , int32_t speed
+ )
+{
+ int32_t new_semitone = current_semitone;
+
+ /* snap to the delta interval */
+ if(current_semitone % semitone_delta != 0)
+ {
+ if(current_semitone > 0 && semitone_delta > 0)
+ new_semitone += semitone_delta;
+ else if(current_semitone < 0 && semitone_delta < 0)
+ new_semitone += semitone_delta;
+
+ new_semitone -= new_semitone % semitone_delta;
+ }
+ else
+ new_semitone += semitone_delta;
+
+ /* clamp the pitch so it doesn't go beyond the pitch limits */
+ if(new_semitone < (SEMITONE_START * PITCH_SPEED_PRECISION))
+ {
+ new_semitone = SEMITONE_START * PITCH_SPEED_PRECISION;
+ at_limit = true;
+ }
+ else if(new_semitone > (SEMITONE_END * PITCH_SPEED_PRECISION))
+ {
+ new_semitone = SEMITONE_END * PITCH_SPEED_PRECISION;
+ at_limit = true;
+ }
+
+ int32_t new_pitch = get_pitch_from_semitone(new_semitone);
+
+ int32_t new_stretch = GET_STRETCH(new_pitch, speed);
+
+ /* clamp the pitch so it doesn't go beyond the stretch limits */
+ if( new_stretch > STRETCH_MAX)
+ {
+ new_pitch = GET_PITCH(speed, STRETCH_MAX);
+ new_semitone = get_semitone_from_pitch(new_pitch);
+ at_limit = true;
+ }
+ else if (new_stretch < STRETCH_MIN)
+ {
+ new_pitch = GET_PITCH(speed, STRETCH_MIN);
+ new_semitone = get_semitone_from_pitch(new_pitch);
+ at_limit = true;
+ }
+
+ pitch_increase(pitch, new_pitch - pitch, false
+ , speed
+ );
+
+ return new_semitone;
+}
+
+#ifdef HAVE_TOUCHSCREEN
+/*
+ * Check for touchscreen presses as per sketch above in this file
+ *
+ * goes through each row of the, checks whether the touchscreen
+ * was pressed in it. Then it looks the columns of each row for specific actions
+ */
+static int pitchscreen_do_touchscreen(struct viewport vps[])
+{
+ short x, y;
+ struct viewport *this_vp = &vps[PITCH_TOP];
+ int ret;
+ static bool wait_for_release = false;
+ ret = rb->action_get_touchscreen_press_in_vp(&x, &y, this_vp);
+
+ /* top row */
+ if (ret > ACTION_UNKNOWN)
+ { /* press on top row, left or right column
+ * only toggle mode if released */
+ int column = this_vp->width / 3;
+ if ((x < column || x > (2*column)) && (ret == BUTTON_REL))
+ return ACTION_PS_TOGGLE_MODE;
+
+
+ else if (x >= column && x <= (2*column))
+ { /* center column pressed */
+ if (ret == BUTTON_REPEAT)
+ return ACTION_PS_INC_BIG;
+ else if (ret & BUTTON_REL)
+ return ACTION_PS_INC_SMALL;
+ }
+ return ACTION_NONE;
+ }
+
+ /* now the center row */
+ this_vp = &vps[PITCH_MID];
+ ret = rb->action_get_touchscreen_press_in_vp(&x, &y, this_vp);
+
+ if (ret > ACTION_UNKNOWN)
+ {
+ int column = this_vp->width / 3;
+
+ if (x < column)
+ { /* left column */
+ if (ret & BUTTON_REL)
+ {
+ wait_for_release = false;
+ return ACTION_PS_NUDGE_LEFTOFF;
+ }
+ else if (ret & BUTTON_REPEAT)
+ return ACTION_PS_SLOWER;
+ if (!wait_for_release)
+ {
+ wait_for_release = true;
+ return ACTION_PS_NUDGE_LEFT;
+ }
+ }
+ else if (x > (2*column))
+ { /* right column */
+ if (ret & BUTTON_REL)
+ {
+ wait_for_release = false;
+ return ACTION_PS_NUDGE_RIGHTOFF;
+ }
+ else if (ret & BUTTON_REPEAT)
+ return ACTION_PS_FASTER;
+ if (!wait_for_release)
+ {
+ wait_for_release = true;
+ return ACTION_PS_NUDGE_RIGHT;
+ }
+ }
+ else
+ /* center column was pressed */
+ return ACTION_PS_RESET;
+ }
+
+ /* now the bottom row */
+ this_vp = &vps[PITCH_BOTTOM];
+ ret = rb->action_get_touchscreen_press_in_vp(&x, &y, this_vp);
+
+ if (ret > ACTION_UNKNOWN)
+ {
+ int column = this_vp->width / 3;
+
+ /* left or right column is exit */
+ if ((x < column || x > (2*column)) && (ret == BUTTON_REL))
+ return ACTION_PS_EXIT;
+ else if (x >= column && x <= (2*column))
+ { /* center column was pressed */
+ if (ret & BUTTON_REPEAT)
+ return ACTION_PS_DEC_BIG;
+ else if (ret & BUTTON_REL)
+ return ACTION_PS_DEC_SMALL;
+ }
+ return ACTION_NONE;
+ }
+ return ACTION_NONE;
+}
+
+#endif
+/*
+ returns:
+ 0 on exit
+ 1 if USB was connected
+*/
+
+int gui_syncpitchscreen_run(void)
+{
+ int button;
+ int32_t pitch = rb->sound_get_pitch();
+ int32_t semitone;
+
+ int32_t new_pitch;
+ int32_t pitch_delta;
+ bool nudged = false;
+ int i, updated = 4, decimals = 0;
+ bool exit = false;
+ /* should maybe be passed per parameter later, not needed for now */
+ struct viewport parent[NB_SCREENS];
+ struct viewport pitch_viewports[NB_SCREENS][PITCH_ITEM_COUNT];
+ int max_lines[NB_SCREENS];
+
+ //push_current_activity(ACTIVITY_PITCHSCREEN);
+
+ int32_t new_speed = 0, new_stretch;
+
+ /* the speed variable holds the apparent speed of the playback */
+ int32_t speed;
+ if (rb->dsp_timestretch_available())
+ {
+ speed = GET_SPEED(pitch, rb->dsp_get_timestretch());
+ }
+ else
+ {
+ speed = pitch;
+ }
+
+
+
+ /* Count decimals for speaking */
+ for (i = PITCH_SPEED_PRECISION; i >= 10; i /= 10)
+ decimals++;
+
+ /* set the semitone index based on the current pitch */
+ semitone = get_semitone_from_pitch(pitch);
+
+ /* initialize pitchscreen vps */
+ FOR_NB_SCREENS(i)
+ {
+ rb->viewport_set_defaults(&parent[i], i);
+ max_lines[i] = viewport_get_nb_lines(&parent[i]);
+ pitchscreen_fix_viewports(&parent[i], pitch_viewports[i]);
+ rb->screens[i]->set_viewport(&parent[i]);
+ rb->screens[i]->clear_viewport();
+
+ /* also, draw the icons now, it's only needed once */
+ pitchscreen_draw_icons(rb->screens[i], &parent[i]);
+ }
+
+
+ while (!exit)
+ {
+ FOR_NB_SCREENS(i)
+ pitchscreen_draw(rb->screens[i], max_lines[i],
+ pitch_viewports[i], pitch, semitone
+ , speed
+ );
+ pitch_delta = 0;
+ new_speed = 0;
+
+ if (rb->global_settings->talk_menu && updated)
+ {
+ rb->talk_shutup();
+ switch (updated)
+ {
+ case 1:
+ if (rb->global_settings->pitch_mode_semitone)
+ rb->talk_value_decimal(semitone, UNIT_SIGNED, decimals, false);
+ else
+ rb->talk_value_decimal(pitch, UNIT_PERCENT, decimals, false);
+ break;
+ case 2:
+ rb->talk_value_decimal(speed, UNIT_PERCENT, decimals, false);
+ break;
+ case 3:
+ speak_pitch_mode(false);
+ break;
+ case 4:
+ if (rb->global_settings->pitch_mode_timestretch && rb->dsp_timestretch_available())
+ rb->talk_id(LANG_PITCH, false);
+ else
+ rb->talk_id(LANG_PLAYBACK_RATE, false);
+ rb->talk_value_decimal(pitch, UNIT_PERCENT, decimals, true);
+ if (rb->global_settings->pitch_mode_timestretch && rb->dsp_timestretch_available())
+ {
+ rb->talk_id(LANG_SPEED, true);
+ rb->talk_value_decimal(speed, UNIT_PERCENT, decimals, true);
+ }
+ speak_pitch_mode(true);
+ break;
+ default:
+ break;
+ }
+ }
+ updated = 0;
+
+ button = rb->get_action(CONTEXT_PITCHSCREEN, HZ);
+
+#ifdef HAVE_TOUCHSCREEN
+ if (button == ACTION_TOUCHSCREEN)
+ {
+ FOR_NB_SCREENS(i)
+ button = pitchscreen_do_touchscreen(pitch_viewports[i]);
+ }
+#endif
+ switch (button)
+ {
+ case ACTION_PS_INC_SMALL:
+ if(rb->global_settings->pitch_mode_semitone)
+ pitch_delta = SEMITONE_SMALL_DELTA;
+ else
+ pitch_delta = PITCH_SMALL_DELTA;
+ updated = 1;
+ break;
+
+ case ACTION_PS_INC_BIG:
+ if(rb->global_settings->pitch_mode_semitone)
+ pitch_delta = SEMITONE_BIG_DELTA;
+ else
+ pitch_delta = PITCH_BIG_DELTA;
+ updated = 1;
+ break;
+
+ case ACTION_PS_DEC_SMALL:
+ if(rb->global_settings->pitch_mode_semitone)
+ pitch_delta = -SEMITONE_SMALL_DELTA;
+ else
+ pitch_delta = -PITCH_SMALL_DELTA;
+ updated = 1;
+ break;
+
+ case ACTION_PS_DEC_BIG:
+ if(rb->global_settings->pitch_mode_semitone)
+ pitch_delta = -SEMITONE_BIG_DELTA;
+ else
+ pitch_delta = -PITCH_BIG_DELTA;
+ updated = 1;
+ break;
+
+ case ACTION_PS_NUDGE_RIGHT:
+ if (!rb->global_settings->pitch_mode_timestretch)
+ {
+ new_pitch = pitch_increase(pitch, PITCH_NUDGE_DELTA, false
+ , speed
+ );
+ nudged = (new_pitch != pitch);
+ pitch = new_pitch;
+ semitone = get_semitone_from_pitch(pitch);
+ speed = pitch;
+ updated = nudged ? 1 : 0;
+ break;
+ }
+ else
+ {
+ new_speed = speed + SPEED_SMALL_DELTA;
+ at_limit = false;
+ updated = 2;
+ }
+ break;
+
+ case ACTION_PS_FASTER:
+ if (rb->global_settings->pitch_mode_timestretch)
+ {
+ new_speed = speed + SPEED_BIG_DELTA;
+ /* snap to whole numbers */
+ if(new_speed % PITCH_SPEED_PRECISION != 0)
+ new_speed -= new_speed % PITCH_SPEED_PRECISION;
+ at_limit = false;
+ updated = 2;
+ }
+ break;
+
+ case ACTION_PS_NUDGE_RIGHTOFF:
+ if (nudged)
+ {
+ pitch = pitch_increase(pitch, -PITCH_NUDGE_DELTA, false
+ , speed
+ );
+ speed = pitch;
+ semitone = get_semitone_from_pitch(pitch);
+ nudged = false;
+ updated = 1;
+ }
+ break;
+
+ case ACTION_PS_NUDGE_LEFT:
+ if (!rb->global_settings->pitch_mode_timestretch)
+ {
+ new_pitch = pitch_increase(pitch, -PITCH_NUDGE_DELTA, false
+ , speed
+ );
+ nudged = (new_pitch != pitch);
+ pitch = new_pitch;
+ semitone = get_semitone_from_pitch(pitch);
+ speed = pitch;
+ updated = nudged ? 1 : 0;
+ break;
+ }
+ else
+ {
+ new_speed = speed - SPEED_SMALL_DELTA;
+ at_limit = false;
+ updated = 2;
+ }
+ break;
+
+ case ACTION_PS_SLOWER:
+ if (rb->global_settings->pitch_mode_timestretch)
+ {
+ new_speed = speed - SPEED_BIG_DELTA;
+ /* snap to whole numbers */
+ if(new_speed % PITCH_SPEED_PRECISION != 0)
+ new_speed += PITCH_SPEED_PRECISION - speed % PITCH_SPEED_PRECISION;
+ at_limit = false;
+ updated = 2;
+ }
+ break;
+
+ case ACTION_PS_NUDGE_LEFTOFF:
+ if (nudged)
+ {
+ pitch = pitch_increase(pitch, PITCH_NUDGE_DELTA, false
+ , speed
+ );
+ speed = pitch;
+ semitone = get_semitone_from_pitch(pitch);
+ nudged = false;
+ updated = 1;
+ }
+ break;
+
+ case ACTION_PS_RESET:
+ pitch = PITCH_SPEED_100;
+ rb->sound_set_pitch(pitch);
+ speed = PITCH_SPEED_100;
+ if (rb->dsp_timestretch_available())
+ {
+ rb->dsp_set_timestretch(PITCH_SPEED_100);
+ at_limit = false;
+ }
+ semitone = get_semitone_from_pitch(pitch);
+ updated = 4;
+ break;
+
+ case ACTION_PS_TOGGLE_MODE:
+ rb->global_settings->pitch_mode_semitone = !rb->global_settings->pitch_mode_semitone;
+
+ if (rb->dsp_timestretch_available() && !rb->global_settings->pitch_mode_semitone)
+ {
+ rb->global_settings->pitch_mode_timestretch = !rb->global_settings->pitch_mode_timestretch;
+ if(!rb->global_settings->pitch_mode_timestretch)
+ {
+ /* no longer in timestretch mode. Reset speed */
+ speed = pitch;
+ rb->dsp_set_timestretch(PITCH_SPEED_100);
+ }
+ }
+ rb->settings_save();
+ updated = 3;
+ break;
+
+ case ACTION_PS_EXIT:
+ exit = true;
+ break;
+
+ default:
+ if (rb->default_event_handler(button) == SYS_USB_CONNECTED)
+ return 1;
+ break;
+ }
+ if (pitch_delta)
+ {
+ if (rb->global_settings->pitch_mode_semitone)
+ {
+ semitone = pitch_increase_semitone(pitch, semitone, pitch_delta
+ , speed
+ );
+ pitch = get_pitch_from_semitone(semitone);
+ }
+ else
+ {
+ pitch = pitch_increase(pitch, pitch_delta, true
+ , speed
+ );
+ semitone = get_semitone_from_pitch(pitch);
+ }
+ if (rb->global_settings->pitch_mode_timestretch)
+ {
+ /* do this to make sure we properly obey the stretch limits */
+ new_speed = speed;
+ }
+ else
+ {
+ speed = pitch;
+ }
+ }
+
+ if(new_speed)
+ {
+ new_stretch = GET_STRETCH(pitch, new_speed);
+
+ /* limit the amount of stretch */
+ if(new_stretch > STRETCH_MAX)
+ {
+ new_stretch = STRETCH_MAX;
+ new_speed = GET_SPEED(pitch, new_stretch);
+ }
+ else if(new_stretch < STRETCH_MIN)
+ {
+ new_stretch = STRETCH_MIN;
+ new_speed = GET_SPEED(pitch, new_stretch);
+ }
+
+ new_stretch = GET_STRETCH(pitch, new_speed);
+ if(new_stretch >= STRETCH_MAX ||
+ new_stretch <= STRETCH_MIN)
+ {
+ at_limit = true;
+ }
+
+ /* set the amount of stretch */
+ rb->dsp_set_timestretch(new_stretch);
+
+ /* update the speed variable with the new speed */
+ speed = new_speed;
+
+ /* Reset new_speed so we only call dsp_set_timestretch */
+ /* when needed */
+ new_speed = 0;
+ }
+ }
+
+ //rb->pcmbuf_set_low_latency(false);
+ //pop_current_activity();
+
+ /* Clean up */
+ FOR_NB_SCREENS(i)
+ {
+ rb->screens[i]->set_viewport(NULL);
+ }
+
+ return 0;
+}
+
+static int arg_callback(char argchar, const char **parameter)
+{
+ int ret;
+ long num, dec;
+ bool bret;
+ //rb->splashf(100, "Arg: %c", argchar);
+ while (*parameter[0] > '/' && ispunct(*parameter[0])) (*parameter)++;
+ switch (tolower(argchar))
+ {
+ case 'q' :
+ pitch_vars.flags &= ~PVAR_VERBOSE;
+ break;
+ case 'g' :
+ pitch_vars.flags |= PVAR_GUI;
+ break;
+ case 'p' :
+ ret = longnum_parse(parameter, &num, &dec);
+ if (ret)
+ {
+ dec /= (ARGPARSE_FRAC_DEC_MULTIPLIER / PITCH_SPEED_PRECISION);
+ if (num < 0)
+ dec = -dec;
+ pitch_vars.pitch = (num * PITCH_SPEED_PRECISION + (dec % PITCH_SPEED_PRECISION));
+ }
+ break;
+ case 'k' :
+ ret = bool_parse(parameter, &bret);
+ if (ret)
+ {
+ if(!bret && rb->dsp_timestretch_available())
+ {
+ /* no longer in timestretch mode. Reset speed */
+ rb->dsp_set_timestretch(PITCH_SPEED_100);
+ }
+ rb->dsp_timestretch_enable(bret);
+ if ((pitch_vars.flags & PVAR_VERBOSE) == PVAR_VERBOSE)
+ rb->splashf(HZ, "Timestretch: %s", bret ? "true" : "false");
+ int n = 10; /* 1 second */
+ while (bret && n-- > 0 && !rb->dsp_timestretch_available())
+ {
+ rb->sleep(HZ / 10);
+ }
+ }
+ break;
+ case 's' :
+ ret = longnum_parse(parameter, &num, &dec);
+ if (ret && rb->dsp_timestretch_available())
+ {
+ dec /= (ARGPARSE_FRAC_DEC_MULTIPLIER / PITCH_SPEED_PRECISION);
+ if (num < 0)
+ break;
+ pitch_vars.speed = (num * PITCH_SPEED_PRECISION + (dec % PITCH_SPEED_PRECISION));
+
+ }
+ break;
+ default :
+ rb->splashf(HZ, "Unknown switch '%c'",argchar);
+ //return 0;
+ }
+
+ return 1;
+}
+
+void fill_pitchvars(struct pvars *pv)
+{
+ if (!pv)
+ return;
+ pv->pitch = rb->sound_get_pitch();
+
+ /* the speed variable holds the apparent speed of the playback */
+ if (rb->dsp_timestretch_available())
+ {
+ pv->speed = GET_SPEED(pv->pitch, rb->dsp_get_timestretch());
+ }
+ else
+ {
+ pv->speed = pv->pitch;
+ }
+
+ pv->stretch = GET_STRETCH(pv->pitch, pv->speed);
+ pv->flags |= PVAR_VERBOSE;
+
+}
+/* plugin entry point */
+enum plugin_status plugin_start(const void* parameter)
+{
+ /* pitch_screen
+ * accepts args -q, -g, -p=, -s=, -k=; (= sign is optional)
+ * -q silences output splash
+ * -g runs the gui (DEFAULT)
+ * -p100 would set pitch to 100%
+ * -s=90 sets speed to 90% if timestrech is enabled
+ * -k=true -k1 enables time stretch -k0 -kf-kn disables
+*/
+ bool gui = false;
+ rb->pcmbuf_set_low_latency(true);
+
+ /* Figure out whether to be in timestretch mode */
+ if (parameter == NULL) /* gui mode */
+ {
+ if (rb->global_settings->pitch_mode_timestretch && !rb->dsp_timestretch_available())
+ {
+ rb->global_settings->pitch_mode_timestretch = false;
+ rb->settings_save();
+ }
+ gui = true;
+ }
+ else
+ {
+ struct pvars cur;
+ fill_pitchvars(&cur);
+ fill_pitchvars(&pitch_vars);
+ argparse((const char*) parameter, -1, &arg_callback);
+ if (pitch_vars.pitch != cur.pitch)
+ {
+ rb->sound_set_pitch(pitch_vars.pitch);
+ pitch_vars.pitch = rb->sound_get_pitch();
+ if ((pitch_vars.flags & PVAR_VERBOSE) == PVAR_VERBOSE)
+ rb->splashf(HZ, "pitch: %ld.%02ld%%",
+ pitch_vars.pitch / PITCH_SPEED_PRECISION,
+ pitch_vars.pitch % PITCH_SPEED_PRECISION);
+ }
+ if (pitch_vars.speed != cur.speed)
+ {
+ pitch_vars.stretch = GET_STRETCH(pitch_vars.pitch, pitch_vars.speed);
+
+ /* limit the amount of stretch */
+ if(pitch_vars.stretch > STRETCH_MAX)
+ {
+ pitch_vars.stretch = STRETCH_MAX;
+ pitch_vars.speed = GET_SPEED(pitch_vars.pitch, pitch_vars.stretch);
+ }
+ else if(pitch_vars.stretch < STRETCH_MIN)
+ {
+ pitch_vars.stretch = STRETCH_MIN;
+ pitch_vars.speed = GET_SPEED(pitch_vars.pitch, pitch_vars.stretch);
+ }
+
+ pitch_vars.stretch = GET_STRETCH(pitch_vars.pitch, pitch_vars.speed);
+ if ((pitch_vars.flags & PVAR_VERBOSE) == PVAR_VERBOSE)
+ rb->splashf(HZ, "speed: %ld.%02ld%%",
+ pitch_vars.speed / PITCH_SPEED_PRECISION,
+ pitch_vars.speed % PITCH_SPEED_PRECISION);
+ /* set the amount of stretch */
+ rb->dsp_set_timestretch(pitch_vars.stretch);
+ }
+ gui = ((pitch_vars.flags & PVAR_GUI) == PVAR_GUI);
+ if ((pitch_vars.flags & PVAR_VERBOSE) == PVAR_VERBOSE)
+ rb->splashf(HZ, "GUI: %d", gui);
+
+ }
+
+ if (gui && gui_syncpitchscreen_run() == 1)
+ return PLUGIN_USB_CONNECTED;
+ rb->pcmbuf_set_low_latency(false);
+ return PLUGIN_OK;
+}
diff --git a/apps/plugins/plasma.c b/apps/plugins/plasma.c
index f944d3d775..cdb6e66569 100644
--- a/apps/plugins/plasma.c
+++ b/apps/plugins/plasma.c
@@ -140,10 +140,10 @@ static void cleanup(void)
#ifndef HAVE_LCD_COLOR
grey_release();
#endif
-#ifdef HAVE_BACKLIGHT
+
/* Turn on backlight timeout (revert to settings) */
backlight_use_settings();
-#endif
+
#if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_PAL256)
rb->lcd_set_mode(LCD_MODE_RGB565);
#endif
@@ -268,6 +268,11 @@ int main(void)
switch(action)
{
+#if (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_3G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_4G_PAD)
+ case PLA_UP:
+#endif
case PLA_EXIT:
case PLA_CANCEL:
return PLUGIN_OK;
@@ -277,7 +282,11 @@ int main(void)
case PLA_SCROLL_FWD:
case PLA_SCROLL_FWD_REPEAT:
#endif
+#if (CONFIG_KEYPAD != IPOD_1G2G_PAD) \
+ && (CONFIG_KEYPAD != IPOD_3G_PAD) \
+ && (CONFIG_KEYPAD != IPOD_4G_PAD)
case PLA_UP:
+#endif
case PLA_UP_REPEAT:
++plasma_frequency;
wave_table_generate();
@@ -321,10 +330,10 @@ enum plugin_status plugin_start(const void* parameter)
#if LCD_DEPTH > 1
rb->lcd_set_backdrop(NULL);
#endif
-#ifdef HAVE_BACKLIGHT
+
/* Turn off backlight timeout */
backlight_ignore_timeout();
-#endif
+
#if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_PAL256)
rb->lcd_set_mode(LCD_MODE_PAL256);
#endif
diff --git a/apps/plugins/playing_time.c b/apps/plugins/playing_time.c
new file mode 100644
index 0000000000..354c5a3e06
--- /dev/null
+++ b/apps/plugins/playing_time.c
@@ -0,0 +1,392 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "plugin.h"
+
+#define rb_talk_ids(enqueue, ids...) rb->talk_idarray(TALK_IDARRAY(ids), enqueue)
+
+/* units used with output_dyn_value */
+const unsigned char * const byte_units[] =
+{
+ ID2P(LANG_BYTE),
+ ID2P(LANG_KIBIBYTE),
+ ID2P(LANG_MEBIBYTE),
+ ID2P(LANG_GIBIBYTE)
+};
+
+const unsigned char * const * const kibyte_units = &byte_units[1];
+
+enum ePT_SECS {
+ ePT_SECS_TTL = 0,
+ ePT_SECS_BEF,
+ ePT_SECS_AFT,
+ ePT_SECS_COUNT
+};
+
+enum ePT_KBS {
+ /* Note: Order matters (voicing order of LANG_PLAYTIME_STORAGE) */
+ ePT_KBS_TTL = 0,
+ ePT_KBS_BEF,
+ ePT_KBS_AFT,
+ ePT_KBS_COUNT
+};
+
+/* playing_time screen context */
+struct playing_time_info {
+ int curr_index; /* index of currently playing track in playlist */
+ int curr_display_index; /* display index of currently playing track in playlist */
+ int nb_tracks; /* how many tracks in playlist */
+
+ /* seconds total, before, and after current position. Datatype
+ allows for values up to 68years. If I had kept it in ms
+ though, it would have overflowed at 24days, which takes
+ something like 8.5GB at 32kbps, and so we could conceivably
+ have playlists lasting longer than that. */
+ long secs[ePT_SECS_COUNT];
+ long trk_secs[ePT_SECS_COUNT];
+
+ /* kilobytes played total, before, and after current pos.
+ Kilobytes because bytes would overflow. Data type range is up
+ to 2TB. */
+ long kbs[ePT_KBS_COUNT];
+};
+
+/* list callback for playing_time screen */
+static const char * playing_time_get_or_speak_info(int selected_item, void * data,
+ char *buf, size_t buffer_len,
+ bool say_it)
+{
+ long elapsed_pct; /* percentage of duration elapsed */
+ struct playing_time_info *pti = (struct playing_time_info *)data;
+ switch(selected_item) {
+ case 0: { /* elapsed and total time */
+ char timestr1[25], timestr2[25];
+ rb->format_time_auto(timestr1, sizeof(timestr1),
+ pti->secs[ePT_SECS_BEF], UNIT_SEC, false);
+
+ rb->format_time_auto(timestr2, sizeof(timestr2),
+ pti->secs[ePT_SECS_TTL], UNIT_SEC, false);
+
+ if (pti->secs[ePT_SECS_TTL] == 0)
+ elapsed_pct = 0;
+ else if (pti->secs[ePT_SECS_TTL] <= 0xFFFFFF)
+ {
+ elapsed_pct = (pti->secs[ePT_SECS_BEF] * 100
+ / pti->secs[ePT_SECS_TTL]);
+ }
+ else /* sacrifice some precision to avoid overflow */
+ {
+ elapsed_pct = (pti->secs[ePT_SECS_BEF] >> 7) * 100
+ / (pti->secs[ePT_SECS_TTL] >> 7);
+ }
+ rb->snprintf(buf, buffer_len, rb->str(LANG_PLAYTIME_ELAPSED),
+ timestr1, timestr2, elapsed_pct);
+
+ if (say_it)
+ rb_talk_ids(false, LANG_PLAYTIME_ELAPSED,
+ TALK_ID(pti->secs[ePT_SECS_BEF], UNIT_TIME),
+ VOICE_OF,
+ TALK_ID(pti->secs[ePT_SECS_TTL], UNIT_TIME),
+ VOICE_PAUSE,
+ TALK_ID(elapsed_pct, UNIT_PERCENT));
+ break;
+ }
+ case 1: { /* playlist remaining time */
+ char timestr[25];
+ rb->format_time_auto(timestr, sizeof(timestr), pti->secs[ePT_SECS_AFT],
+ UNIT_SEC, false);
+ rb->snprintf(buf, buffer_len, rb->str(LANG_PLAYTIME_REMAINING), timestr);
+
+ if (say_it)
+ rb_talk_ids(false, LANG_PLAYTIME_REMAINING,
+ TALK_ID(pti->secs[ePT_SECS_AFT], UNIT_TIME));
+ break;
+ }
+ case 2: { /* track elapsed and duration */
+ char timestr1[25], timestr2[25];
+
+ rb->format_time_auto(timestr1, sizeof(timestr1), pti->trk_secs[ePT_SECS_BEF],
+ UNIT_SEC, false);
+ rb->format_time_auto(timestr2, sizeof(timestr2), pti->trk_secs[ePT_SECS_TTL],
+ UNIT_SEC, false);
+
+ if (pti->trk_secs[ePT_SECS_TTL] == 0)
+ elapsed_pct = 0;
+ else if (pti->trk_secs[ePT_SECS_TTL] <= 0xFFFFFF)
+ {
+ elapsed_pct = (pti->trk_secs[ePT_SECS_BEF] * 100
+ / pti->trk_secs[ePT_SECS_TTL]);
+ }
+ else /* sacrifice some precision to avoid overflow */
+ {
+ elapsed_pct = (pti->trk_secs[ePT_SECS_BEF] >> 7) * 100
+ / (pti->trk_secs[ePT_SECS_TTL] >> 7);
+ }
+ rb->snprintf(buf, buffer_len, rb->str(LANG_PLAYTIME_TRK_ELAPSED),
+ timestr1, timestr2, elapsed_pct);
+
+ if (say_it)
+ rb_talk_ids(false, LANG_PLAYTIME_TRK_ELAPSED,
+ TALK_ID(pti->trk_secs[ePT_SECS_BEF], UNIT_TIME),
+ VOICE_OF,
+ TALK_ID(pti->trk_secs[ePT_SECS_TTL], UNIT_TIME),
+ VOICE_PAUSE,
+ TALK_ID(elapsed_pct, UNIT_PERCENT));
+ break;
+ }
+ case 3: { /* track remaining time */
+ char timestr[25];
+ rb->format_time_auto(timestr, sizeof(timestr), pti->trk_secs[ePT_SECS_AFT],
+ UNIT_SEC, false);
+ rb->snprintf(buf, buffer_len, rb->str(LANG_PLAYTIME_TRK_REMAINING), timestr);
+
+ if (say_it)
+ rb_talk_ids(false, LANG_PLAYTIME_TRK_REMAINING,
+ TALK_ID(pti->trk_secs[ePT_SECS_AFT], UNIT_TIME));
+ break;
+ }
+ case 4: { /* track index */
+ int track_pct = pti->curr_display_index * 100 / pti->nb_tracks;
+
+ rb->snprintf(buf, buffer_len, rb->str(LANG_PLAYTIME_TRACK),
+ pti->curr_display_index, pti->nb_tracks, track_pct);
+
+ if (say_it)
+ rb_talk_ids(false, LANG_PLAYTIME_TRACK,
+ TALK_ID(pti->curr_display_index, UNIT_INT),
+ VOICE_OF,
+ TALK_ID(pti->nb_tracks, UNIT_INT),
+ VOICE_PAUSE,
+ TALK_ID(track_pct, UNIT_PERCENT));
+ break;
+ }
+ case 5: { /* storage size */
+ int i;
+ char kbstr[ePT_KBS_COUNT][10];
+
+ for (i = 0; i < ePT_KBS_COUNT; i++) {
+ rb->output_dyn_value(kbstr[i], sizeof(kbstr[i]),
+ pti->kbs[i], kibyte_units, 3, true);
+ }
+ rb->snprintf(buf, buffer_len, rb->str(LANG_PLAYTIME_STORAGE),
+ kbstr[ePT_KBS_TTL], kbstr[ePT_KBS_BEF],kbstr[ePT_KBS_AFT]);
+
+ if (say_it) {
+ int32_t voice_ids[ePT_KBS_COUNT];
+ voice_ids[ePT_KBS_TTL] = LANG_PLAYTIME_STORAGE;
+ voice_ids[ePT_KBS_BEF] = VOICE_PLAYTIME_DONE;
+ voice_ids[ePT_KBS_AFT] = LANG_PLAYTIME_REMAINING;
+
+ for (i = 0; i < ePT_KBS_COUNT; i++) {
+ rb_talk_ids(i > 0, VOICE_PAUSE, voice_ids[i]);
+ rb->output_dyn_value(NULL, 0, pti->kbs[i], kibyte_units, 3, true);
+ }
+ }
+ break;
+ }
+ case 6: { /* Average track file size */
+ char str[10];
+ long avg_track_size = pti->kbs[ePT_KBS_TTL] / pti->nb_tracks;
+ rb->output_dyn_value(str, sizeof(str), avg_track_size, kibyte_units, 3, true);
+ rb->snprintf(buf, buffer_len, rb->str(LANG_PLAYTIME_AVG_TRACK_SIZE), str);
+
+ if (say_it) {
+ rb->talk_id(LANG_PLAYTIME_AVG_TRACK_SIZE, false);
+ rb->output_dyn_value(NULL, 0, avg_track_size, kibyte_units, 3, true);
+ }
+ break;
+ }
+ case 7: { /* Average bitrate */
+ /* Convert power of 2 kilobytes to power of 10 kilobits */
+ long avg_bitrate = (pti->kbs[ePT_KBS_TTL] / pti->secs[ePT_SECS_TTL]
+ * 1024 * 8 / 1000);
+ rb->snprintf(buf, buffer_len, rb->str(LANG_PLAYTIME_AVG_BITRATE), avg_bitrate);
+
+ if (say_it)
+ rb_talk_ids(false, LANG_PLAYTIME_AVG_BITRATE,
+ TALK_ID(avg_bitrate, UNIT_KBIT));
+ break;
+ }
+ }
+ return buf;
+}
+
+static const char * playing_time_get_info(int selected_item, void * data,
+ char *buffer, size_t buffer_len)
+{
+ return playing_time_get_or_speak_info(selected_item, data,
+ buffer, buffer_len, false);
+}
+
+static int playing_time_speak_info(int selected_item, void * data)
+{
+ static char buffer[MAX_PATH];
+ playing_time_get_or_speak_info(selected_item, data,
+ buffer, MAX_PATH, true);
+ return 0;
+}
+
+/* playing time screen: shows total and elapsed playlist duration and
+ other stats */
+static bool playing_time(void)
+{
+ int error_count = 0;
+ unsigned long talked_tick = *rb->current_tick;
+ struct playing_time_info pti;
+ struct playlist_track_info pltrack;
+ struct mp3entry id3;
+ int i, index, fd;
+
+ pti.nb_tracks = rb->playlist_amount();
+ rb->playlist_get_resume_info(&pti.curr_index);
+ struct mp3entry *curr_id3 = rb->audio_current_track();
+ if (pti.curr_index == -1 || !curr_id3)
+ return false;
+ pti.curr_display_index = rb->playlist_get_display_index();
+
+ pti.secs[ePT_SECS_BEF] = pti.trk_secs[ePT_SECS_BEF] = curr_id3->elapsed / 1000;
+ pti.secs[ePT_SECS_AFT] = pti.trk_secs[ePT_SECS_AFT]
+ = (curr_id3->length -curr_id3->elapsed) / 1000;
+ pti.kbs[ePT_KBS_BEF] = curr_id3->offset / 1024;
+ pti.kbs[ePT_KBS_AFT] = (curr_id3->filesize -curr_id3->offset) / 1024;
+
+ rb->splash(0, ID2P(LANG_WAIT));
+ rb->splash_progress_set_delay(5 * HZ);
+ /* Go through each file in the playlist and get its stats. For
+ huge playlists this can take a while... The reference position
+ is the position at the moment this function was invoked,
+ although playback continues forward. */
+ index = rb->playlist_get_first_index(NULL);
+ for (i = 0; i < pti.nb_tracks; i++, index++) {
+
+ if (index == pti.nb_tracks)
+ index = 0;
+
+ /* Show a splash while we are loading. */
+ rb->splash_progress(i, pti.nb_tracks,
+ "%s (%s)", rb->str(LANG_WAIT), rb->str(LANG_OFF_ABORT));
+
+ /* Voice equivalent */
+ if (TIME_AFTER(*rb->current_tick, talked_tick + 5 * HZ)) {
+ talked_tick = *rb->current_tick;
+ rb_talk_ids(false, LANG_LOADING_PERCENT,
+ TALK_ID(i * 100 / pti.nb_tracks, UNIT_PERCENT));
+ }
+ if (rb->action_userabort(TIMEOUT_NOBLOCK))
+ goto exit;
+
+ if (index == pti.curr_index)
+ continue;
+
+ if (rb->playlist_get_track_info(NULL, index, &pltrack) >= 0)
+ {
+ bool ret = false;
+ if ((fd = rb->open(pltrack.filename, O_RDONLY)) >= 0)
+ {
+ ret = rb->get_metadata(&id3, fd, pltrack.filename);
+ rb->close(fd);
+ if (ret)
+ {
+ if (pltrack.display_index < pti.curr_display_index) {
+ pti.secs[ePT_SECS_BEF] += id3.length / 1000;
+ pti.kbs[ePT_KBS_BEF] += id3.filesize / 1024;
+ } else {
+ pti.secs[ePT_SECS_AFT] += id3.length / 1000;
+ pti.kbs[ePT_KBS_AFT] += id3.filesize / 1024;
+ }
+ }
+ }
+
+ if (!ret)
+ {
+ error_count++;
+ continue;
+ }
+ }
+ else
+ {
+ error_count++;
+ break;
+ }
+ }
+
+ if (error_count > 0)
+ {
+ rb->splash(HZ, ID2P(LANG_PLAYTIME_ERROR));
+ }
+
+ pti.nb_tracks -= error_count;
+ pti.secs[ePT_SECS_TTL] = pti.secs[ePT_SECS_BEF] + pti.secs[ePT_SECS_AFT];
+ pti.trk_secs[ePT_SECS_TTL] = pti.trk_secs[ePT_SECS_BEF] + pti.trk_secs[ePT_SECS_AFT];
+ pti.kbs[ePT_KBS_TTL] = pti.kbs[ePT_KBS_BEF] + pti.kbs[ePT_KBS_AFT];
+
+ struct gui_synclist pt_lists;
+ int key;
+
+ rb->gui_synclist_init(&pt_lists, &playing_time_get_info, &pti, true, 1, NULL);
+ if (rb->global_settings->talk_menu)
+ rb->gui_synclist_set_voice_callback(&pt_lists, playing_time_speak_info);
+ rb->gui_synclist_set_nb_items(&pt_lists, 8);
+ rb->gui_synclist_set_title(&pt_lists, rb->str(LANG_PLAYING_TIME), NOICON);
+ rb->gui_synclist_draw(&pt_lists);
+ rb->gui_synclist_speak_item(&pt_lists);
+ while (true) {
+ if (rb->list_do_action(CONTEXT_LIST, HZ/2, &pt_lists, &key) == 0
+ && key!=ACTION_NONE && key!=ACTION_UNKNOWN)
+ {
+ bool usb = rb->default_event_handler(key) == SYS_USB_CONNECTED;
+
+ if (!usb && IS_SYSEVENT(key))
+ continue;
+
+ rb->talk_force_shutup();
+ return usb;
+ }
+
+ }
+
+ exit:
+ return false;
+}
+
+/* this is the plugin entry point */
+enum plugin_status plugin_start(const void* parameter)
+{
+ enum plugin_status status = PLUGIN_OK;
+
+ (void)parameter;
+
+ if (!rb->audio_status())
+ {
+ rb->splash(HZ*2, "Nothing Playing");
+ return status;
+ }
+
+ FOR_NB_SCREENS(i)
+ rb->viewportmanager_theme_enable(i, true, NULL);
+
+ if (playing_time())
+ status = PLUGIN_USB_CONNECTED;
+
+ FOR_NB_SCREENS(i)
+ rb->viewportmanager_theme_undo(i, false);
+
+ return status;
+}
diff --git a/apps/plugins/plugin.lds b/apps/plugins/plugin.lds
index cc0be3abd5..fac47d8c45 100644
--- a/apps/plugins/plugin.lds
+++ b/apps/plugins/plugin.lds
@@ -36,7 +36,6 @@ OUTPUT_FORMAT(elf32-littlemips)
#elif CONFIG_CPU==DM320
-/* Give this 1 meg to allow it to align to the MMU boundary */
#ifndef LCD_NATIVE_WIDTH
#define LCD_NATIVE_WIDTH LCD_WIDTH
#endif
@@ -45,9 +44,17 @@ OUTPUT_FORMAT(elf32-littlemips)
#define LCD_NATIVE_HEIGHT LCD_HEIGHT
#endif
+#ifdef MROBE_500
+/* Give this 1 meg to allow it to align to the MMU boundary */
#define LCD_FUDGE LCD_NATIVE_WIDTH%32
-#define LCD_BUFFER_SIZE ((LCD_NATIVE_WIDTH+LCD_FUDGE)*LCD_NATIVE_HEIGHT*2)
+#define LCD_BUFFER_SIZE ((LCD_NATIVE_WIDTH+LCD_FUDGE)*LCD_NATIVE_HEIGHT*2)
#define LCD_TTB_AREA 0x100000*((LCD_BUFFER_SIZE>>19)+1)
+#else
+/* must be 16Kb (0x4000) aligned */
+#define TTB_SIZE (0x4000)
+#define LCD_BUFFER_SIZE (LCD_NATIVE_WIDTH*LCD_NATIVE_HEIGHT*2)
+#define LCD_TTB_AREA (TTB_SIZE + LCD_BUFFER_SIZE)
+#endif
#define DRAMSIZE (MEMORYSIZE * 0x100000) - STUBOFFSET - PLUGIN_BUFFER_SIZE - CODEC_SIZE - LCD_TTB_AREA
diff --git a/apps/plugins/plugin_crt0.c b/apps/plugins/plugin_crt0.c
index 680bb0723d..5564c5575f 100644
--- a/apps/plugins/plugin_crt0.c
+++ b/apps/plugins/plugin_crt0.c
@@ -141,6 +141,7 @@ void exit_on_usb(int button)
long result = rb->default_event_handler_ex(button, cleanup_wrapper, NULL);
if (result == SYS_USB_CONNECTED)
_exit(PLUGIN_USB_CONNECTED);
- else if (result == SYS_POWEROFF)
+ else if (result == SYS_POWEROFF || result == SYS_REBOOT)
+ /* note: nobody actually pays attention to this exit code */
_exit(PLUGIN_POWEROFF);
}
diff --git a/apps/plugins/plugins.make b/apps/plugins/plugins.make
index 71eaeb52c0..b430bd2792 100644
--- a/apps/plugins/plugins.make
+++ b/apps/plugins/plugins.make
@@ -101,8 +101,35 @@ else
PLUGINLIBFLAGS = $(PLUGINFLAGS) -ffunction-sections -fdata-sections
endif
+ROOT_PLUGINSLIB_DIR := $(ROOTDIR)/apps/plugins/lib
+BUILD_PLUGINSLIB_DIR := $(BUILDDIR)/apps/plugins/lib
+
+# action_helper #
+ACTION_REQ := $(addprefix $(ROOT_PLUGINSLIB_DIR)/,action_helper.pl action_helper.h) \
+ $(BUILD_PLUGINSLIB_DIR)/pluginlib_actions.o
+
+# special rule for generating and compiling action_helper
+$(BUILD_PLUGINSLIB_DIR)/action_helper.o: $(ACTION_REQ)
+ $(SILENT)mkdir -p $(dir $@)
+ $(call PRINTS,GEN $(@F))$(CC) $(PLUGINFLAGS) $(INCLUDES) -E -P \
+ $(ROOT_PLUGINSLIB_DIR)/pluginlib_actions.h - < /dev/null | $< > $(basename $@).c
+ $(call PRINTS,CC $(subst $(ROOTDIR)/,,$<))$(CC) -I$(ROOT_PLUGINSLIB_DIR) \
+ $(PLUGINLIBFLAGS) -c $(basename $@).c -o $@
+
+# button_helper #
+BUTTON_REQ := $(addprefix $(ROOT_PLUGINSLIB_DIR)/,button_helper.pl button_helper.h) \
+ $(BUILD_PLUGINSLIB_DIR)/action_helper.o
+
+# special rule for generating and compiling button_helper
+$(BUILD_PLUGINSLIB_DIR)/button_helper.o: $(BUTTON_REQ) $(ROOTDIR)/firmware/export/button.h
+ $(SILENT)mkdir -p $(dir $@)
+ $(call PRINTS,GEN $(@F))$(CC) $(PLUGINFLAGS) $(INCLUDES) -dM -E -P \
+ $(addprefix -include ,button-target.h button.h) - < /dev/null | $< > $(basename $@).c
+ $(call PRINTS,CC $(subst $(ROOTDIR)/,,$<))$(CC) -I$(ROOT_PLUGINSLIB_DIR) \
+ $(PLUGINLIBFLAGS) -c $(basename $@).c -o $@
+
# special pattern rule for compiling plugin lib (with function and data sections)
-$(BUILDDIR)/apps/plugins/lib/%.o: $(ROOTDIR)/apps/plugins/lib/%.c
+$(BUILD_PLUGINSLIB_DIR)/%.o: $(ROOT_PLUGINSLIB_DIR)/%.c
$(SILENT)mkdir -p $(dir $@)
$(call PRINTS,CC $(subst $(ROOTDIR)/,,$<))$(CC) -I$(dir $<) $(PLUGINLIBFLAGS) -c $< -o $@
@@ -112,7 +139,7 @@ $(BUILDDIR)/apps/plugins/%.o: $(ROOTDIR)/apps/plugins/%.c
$(call PRINTS,CC $(subst $(ROOTDIR)/,,$<))$(CC) -I$(dir $<) $(PLUGINFLAGS) -c $< -o $@
ifdef APP_TYPE
- PLUGINLDFLAGS = $(SHARED_LDFLAG) -Wl,-Map,$*.map
+ PLUGINLDFLAGS = $(SHARED_LDFLAGS) -Wl,-Map,$*.map
PLUGINFLAGS += $(SHARED_CFLAGS) # <-- from Makefile
else
PLUGINLDFLAGS = -T$(PLUGINLINK_LDS) -Wl,--gc-sections -Wl,-Map,$*.map
diff --git a/apps/plugins/pong.c b/apps/plugins/pong.c
index 22484d0bc9..17e6e6ed73 100644
--- a/apps/plugins/pong.c
+++ b/apps/plugins/pong.c
@@ -325,6 +325,9 @@ CONFIG_KEYPAD == MROBE500_PAD
#define PONG_RIGHT_UP BUTTON_BACK
#define PONG_RIGHT_DOWN BUTTON_RIGHT
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+/* use touchscreen */
+
#else
#error No keymap defined!
#endif
@@ -532,7 +535,7 @@ static void ball(struct pong *p)
p->ball.x += p->ball.speedx;
p->ball.y += p->ball.speedy;
- newx = p->ball.x/RES;
+ /*newx = p->ball.x/RES;*/
newy = p->ball.y/RES;
/* detect if ball hits a wall */
@@ -747,10 +750,9 @@ enum plugin_status plugin_start(const void* parameter)
this to avoid the compiler warning about it */
(void)parameter;
-#ifdef HAVE_BACKLIGHT
/* Turn off backlight timeout */
backlight_ignore_timeout();
-#endif
+
/* Clear screen */
rb->lcd_clear_display();
@@ -789,9 +791,9 @@ enum plugin_status plugin_start(const void* parameter)
rb->lcd_clear_display();
}
}
-#ifdef HAVE_BACKLIGHT
+
/* Turn on backlight timeout (revert to settings) */
backlight_use_settings();
-#endif
+
return (game == 0) ? PLUGIN_OK : PLUGIN_USB_CONNECTED;
}
diff --git a/apps/plugins/properties.c b/apps/plugins/properties.c
index 9db9572d43..ce3c03694c 100644
--- a/apps/plugins/properties.c
+++ b/apps/plugins/properties.c
@@ -20,30 +20,50 @@
****************************************************************************/
#include "plugin.h"
+#ifdef HAVE_TAGCACHE
+#include "lib/mul_id3.h"
+#endif
+#if !defined(ARRAY_SIZE)
+ #define ARRAY_SIZE(x) (sizeof((x)) / sizeof((x)[0]))
+#endif
-bool its_a_dir = false;
+struct dir_stats {
+ char dirname[MAX_PATH];
+ int len;
+ unsigned int dir_count;
+ unsigned int file_count;
+ unsigned long long byte_count;
+};
+
+enum props_types {
+ PROPS_FILE = 0,
+ PROPS_ID3,
+ PROPS_MUL_ID3,
+ PROPS_DIR
+};
-char str_filename[MAX_PATH];
-char str_dirname[MAX_PATH];
-char str_size[64];
-char str_dircount[64];
-char str_filecount[64];
-char str_date[64];
-char str_time[64];
+static int props_type = PROPS_FILE;
-char str_title[MAX_PATH];
-char str_artist[MAX_PATH];
-char str_album[MAX_PATH];
-char str_duration[32];
+static struct mp3entry id3;
+#ifdef HAVE_TAGCACHE
+static int mul_id3_count;
+static int skipped_count;
+#endif
-unsigned nseconds;
-unsigned long nsize;
-int32_t size_unit;
-struct tm tm;
+static char str_filename[MAX_PATH];
+static char str_dirname[MAX_PATH];
+static char str_size[64];
+static char str_dircount[64];
+static char str_filecount[64];
+static char str_date[64];
+static char str_time[64];
-int num_properties;
+static unsigned long nsize;
+static int32_t size_unit;
+static struct tm tm;
+#define NUM_FILE_PROPERTIES 5
static const unsigned char* const props_file[] =
{
ID2P(LANG_PROPERTIES_PATH), str_dirname,
@@ -51,11 +71,9 @@ static const unsigned char* const props_file[] =
ID2P(LANG_PROPERTIES_SIZE), str_size,
ID2P(LANG_PROPERTIES_DATE), str_date,
ID2P(LANG_PROPERTIES_TIME), str_time,
- ID2P(LANG_PROPERTIES_ARTIST), str_artist,
- ID2P(LANG_PROPERTIES_TITLE), str_title,
- ID2P(LANG_PROPERTIES_ALBUM), str_album,
- ID2P(LANG_PROPERTIES_DURATION), str_duration,
};
+
+#define NUM_DIR_PROPERTIES 4
static const unsigned char* const props_dir[] =
{
ID2P(LANG_PROPERTIES_PATH), str_dirname,
@@ -89,7 +107,6 @@ static bool file_properties(const char* selected_file)
bool found = false;
DIR* dir;
struct dirent* entry;
- static struct mp3entry id3;
dir = rb->opendir(str_dirname);
if (dir)
@@ -110,38 +127,8 @@ static bool file_properties(const char* selected_file)
rb->snprintf(str_time, sizeof str_time, "%02d:%02d:%02d",
tm.tm_hour, tm.tm_min, tm.tm_sec);
- num_properties = 5;
-
- int fd = rb->open(selected_file, O_RDONLY);
- if (fd >= 0 &&
- rb->get_metadata(&id3, fd, selected_file))
- {
- long dur = id3.length / 1000; /* seconds */
- rb->snprintf(str_artist, sizeof str_artist,
- "%s", id3.artist ? id3.artist : "");
- rb->snprintf(str_title, sizeof str_title,
- "%s", id3.title ? id3.title : "");
- rb->snprintf(str_album, sizeof str_album,
- "%s", id3.album ? id3.album : "");
- num_properties += 3;
-
- if (dur > 0)
- {
- nseconds = dur;
- if (dur < 3600)
- rb->snprintf(str_duration, sizeof str_duration,
- "%d:%02d", (int)(dur / 60),
- (int)(dur % 60));
- else
- rb->snprintf(str_duration, sizeof str_duration,
- "%d:%02d:%02d",
- (int)(dur / 3600),
- (int)(dur % 3600 / 60),
- (int)(dur % 60));
- num_properties++;
- }
- }
- rb->close(fd);
+ if (!rb->mp3info(&id3, selected_file))
+ props_type = PROPS_ID3;
found = true;
break;
}
@@ -151,15 +138,7 @@ static bool file_properties(const char* selected_file)
return found;
}
-typedef struct {
- char dirname[MAX_PATH];
- int len;
- unsigned int dc;
- unsigned int fc;
- unsigned long long bc;
-} DPS;
-
-static bool _dir_properties(DPS *dps)
+static bool _dir_properties(struct dir_stats *stats)
{
/* recursively scan directories in search of files
and informs the user of the progress */
@@ -170,11 +149,11 @@ static bool _dir_properties(DPS *dps)
struct dirent* entry;
result = true;
- dirlen = rb->strlen(dps->dirname);
- dir = rb->opendir(dps->dirname);
+ dirlen = rb->strlen(stats->dirname);
+ dir = rb->opendir(stats->dirname);
if (!dir)
{
- rb->splashf(HZ*2, "%s", dps->dirname);
+ rb->splashf(HZ*2, "%s", stats->dirname);
return false; /* open error */
}
@@ -183,7 +162,7 @@ static bool _dir_properties(DPS *dps)
{
struct dirinfo info = rb->dir_get_info(dir, entry);
/* append name to current directory */
- rb->snprintf(dps->dirname+dirlen, dps->len-dirlen, "/%s",
+ rb->snprintf(stats->dirname+dirlen, stats->len-dirlen, "/%s",
entry->d_name);
if (info.attribute & ATTR_DIRECTORY)
@@ -192,29 +171,30 @@ static bool _dir_properties(DPS *dps)
!rb->strcmp((char *)entry->d_name, ".."))
continue; /* skip these */
- dps->dc++; /* new directory */
+ stats->dir_count++; /* new directory */
if (*rb->current_tick - lasttick > (HZ/8))
{
- unsigned log = human_size_log(dps->bc);
+ unsigned log;
lasttick = *rb->current_tick;
rb->lcd_clear_display();
rb->lcd_puts(0,0,"SCANNING...");
- rb->lcd_puts(0,1,dps->dirname);
- rb->lcd_putsf(0,2,"Directories: %d", dps->dc);
- rb->lcd_putsf(0,3,"Files: %d", dps->fc);
- log = human_size_log(dps->bc);
- rb->lcd_putsf(0,4,"Size: %lu %cB", (unsigned long)(dps->bc >> (10*log)),
- rb->str(units[log]));
+ rb->lcd_puts(0,1,stats->dirname);
+ rb->lcd_putsf(0,2,"Directories: %d", stats->dir_count);
+ rb->lcd_putsf(0,3,"Files: %d", stats->file_count);
+ log = human_size_log(stats->byte_count);
+ rb->lcd_putsf(0,4,"Size: %lu %s",
+ (unsigned long)(stats->byte_count >> (10*log)),
+ rb->str(units[log]));
rb->lcd_update();
}
/* recursion */
- result = _dir_properties(dps);
+ result = _dir_properties(stats);
}
else
{
- dps->fc++; /* new file */
- dps->bc += info.size;
+ stats->file_count++; /* new file */
+ stats->byte_count += info.size;
}
if(ACTION_STD_CANCEL == rb->get_action(CONTEXT_STD,TIMEOUT_NOBLOCK))
result = false;
@@ -224,17 +204,17 @@ static bool _dir_properties(DPS *dps)
return result;
}
-static bool dir_properties(const char* selected_file, DPS *dps)
+static bool dir_properties(const char* selected_file, struct dir_stats *stats)
{
unsigned log;
- rb->strlcpy(dps->dirname, selected_file, MAX_PATH);
+ rb->strlcpy(stats->dirname, selected_file, MAX_PATH);
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
rb->cpu_boost(true);
#endif
- if (!_dir_properties(dps))
+ if (!_dir_properties(stats))
{
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
rb->cpu_boost(false);
@@ -247,13 +227,12 @@ static bool dir_properties(const char* selected_file, DPS *dps)
#endif
rb->strlcpy(str_dirname, selected_file, MAX_PATH);
- rb->snprintf(str_dircount, sizeof str_dircount, "%d", dps->dc);
- rb->snprintf(str_filecount, sizeof str_filecount, "%d", dps->fc);
- log = human_size_log(dps->bc);
- nsize = (long) (dps->bc >> (log*10));
+ rb->snprintf(str_dircount, sizeof str_dircount, "%d", stats->dir_count);
+ rb->snprintf(str_filecount, sizeof str_filecount, "%d", stats->file_count);
+ log = human_size_log(stats->byte_count);
+ nsize = (long) (stats->byte_count >> (log*10));
size_unit = units[log];
rb->snprintf(str_size, sizeof str_size, "%ld %s", nsize, rb->str(size_unit));
- num_properties = 4;
return true;
}
@@ -267,35 +246,20 @@ static const char * get_props(int selected_item, void* data,
char *buffer, size_t buffer_len)
{
(void)data;
- if(its_a_dir)
- {
- if(selected_item >= (int)(sizeof(props_dir) / sizeof(props_dir[0])))
- {
- rb->strlcpy(buffer, "ERROR", buffer_len);
- }
- else
- {
- rb->strlcpy(buffer, p2str(props_dir[selected_item]), buffer_len);
- }
- }
- else
- {
- if(selected_item >= (int)(sizeof(props_file) / sizeof(props_file[0])))
- {
- rb->strlcpy(buffer, "ERROR", buffer_len);
- }
- else
- {
- rb->strlcpy(buffer, p2str(props_file[selected_item]), buffer_len);
- }
- }
+ if (PROPS_DIR == props_type)
+ rb->strlcpy(buffer, selected_item >= (int)(ARRAY_SIZE(props_dir)) ? "ERROR" :
+ (char *) p2str(props_dir[selected_item]), buffer_len);
+ else if (PROPS_FILE == props_type)
+ rb->strlcpy(buffer, selected_item >= (int)(ARRAY_SIZE(props_file)) ? "ERROR" :
+ (char *) p2str(props_file[selected_item]), buffer_len);
+
return buffer;
}
static int speak_property_selection(int selected_item, void *data)
{
- DPS *dps = data;
- int32_t id = P2ID((its_a_dir ? props_dir : props_file)[selected_item]);
+ struct dir_stats *stats = data;
+ int32_t id = P2ID((props_type == PROPS_DIR ? props_dir : props_file)[selected_item]);
rb->talk_id(id, false);
switch (id)
{
@@ -333,14 +297,11 @@ static int speak_property_selection(int selected_item, void *data)
case LANG_PROPERTIES_TIME:
rb->talk_time(&tm, true);
break;
- case LANG_PROPERTIES_DURATION:
- rb->talk_value(nseconds, UNIT_TIME, true);
- break;
case LANG_PROPERTIES_SUBDIRS:
- rb->talk_number(dps->dc, true);
+ rb->talk_number(stats->dir_count, true);
break;
case LANG_PROPERTIES_FILES:
- rb->talk_number(dps->fc, true);
+ rb->talk_number(stats->file_count, true);
break;
default:
rb->talk_spell(props_file[selected_item + 1], true);
@@ -349,33 +310,49 @@ static int speak_property_selection(int selected_item, void *data)
return 0;
}
-enum plugin_status plugin_start(const void* parameter)
+static int browse_file_or_dir(struct dir_stats *stats)
{
struct gui_synclist properties_lists;
int button;
- bool quit = false, usb = false;
- const char *file = parameter;
- if(!parameter) return PLUGIN_ERROR;
-#ifdef HAVE_TOUCHSCREEN
- rb->touchscreen_set_mode(rb->global_settings->touch_mode);
-#endif
- static DPS dps = {
- .len = MAX_PATH,
- .dc = 0,
- .fc = 0,
- .bc = 0,
- };
+ rb->gui_synclist_init(&properties_lists, &get_props, stats, false, 2, NULL);
+ rb->gui_synclist_set_title(&properties_lists,
+ rb->str(props_type == PROPS_DIR ?
+ LANG_PROPERTIES_DIRECTORY_PROPERTIES :
+ LANG_PROPERTIES_FILE_PROPERTIES),
+ NOICON);
+ rb->gui_synclist_set_icon_callback(&properties_lists, NULL);
+ if (rb->global_settings->talk_menu)
+ rb->gui_synclist_set_voice_callback(&properties_lists, speak_property_selection);
+ rb->gui_synclist_set_nb_items(&properties_lists,
+ 2 * (props_type == PROPS_FILE ? NUM_FILE_PROPERTIES :
+ NUM_DIR_PROPERTIES));
+ rb->gui_synclist_select_item(&properties_lists, 0);
+ rb->gui_synclist_draw(&properties_lists);
+ rb->gui_synclist_speak_item(&properties_lists);
- /* determine if it's a file or a directory */
- bool found = false;
+ while(true)
+ {
+ button = rb->get_action(CONTEXT_LIST, HZ);
+ /* HZ so the status bar redraws corectly */
+ if (rb->gui_synclist_do_button(&properties_lists,&button))
+ continue;
+ switch(button)
+ {
+ case ACTION_STD_CANCEL:
+ return false;
+ default:
+ if (rb->default_event_handler(button) == SYS_USB_CONNECTED)
+ return true;
+ break;
+ }
+ }
+}
+
+static bool determine_file_or_dir(void)
+{
DIR* dir;
struct dirent* entry;
- char* ptr = rb->strrchr(file, '/') + 1;
- int dirlen = (ptr - file);
-
- rb->strlcpy(str_dirname, file, dirlen + 1);
- rb->snprintf(str_filename, sizeof str_filename, "%s", file+dirlen);
dir = rb->opendir(str_dirname);
if (dir)
@@ -385,69 +362,104 @@ enum plugin_status plugin_start(const void* parameter)
if(!rb->strcmp(entry->d_name, str_filename))
{
struct dirinfo info = rb->dir_get_info(dir, entry);
- its_a_dir = info.attribute & ATTR_DIRECTORY ? true : false;
- found = true;
- break;
+ props_type = info.attribute & ATTR_DIRECTORY ? PROPS_DIR : PROPS_FILE;
+ rb->closedir(dir);
+ return true;
}
}
rb->closedir(dir);
}
- /* now we know if it's a file or a dir or maybe something failed */
+ return false;
+}
- if(!found)
+#ifdef HAVE_TAGCACHE
+bool mul_id3_add(const char *file_name)
+{
+ if (!file_name || rb->mp3info(&id3, file_name))
+ skipped_count++;
+ else
{
- /* weird: we couldn't find the entry. This Should Never Happen (TM) */
- rb->splashf(0, "File/Dir not found: %s", file);
- rb->action_userabort(TIMEOUT_BLOCK);
- return PLUGIN_OK;
+ collect_id3(&id3, mul_id3_count == 0);
+ mul_id3_count++;
}
- /* get the info depending on its_a_dir */
- if(!(its_a_dir ? dir_properties(file, &dps) : file_properties(file)))
+ return true;
+}
+#endif
+
+enum plugin_status plugin_start(const void* parameter)
+{
+ static struct dir_stats stats =
{
- /* something went wrong (to do: tell user what it was (nesting,...) */
- rb->splash(0, ID2P(LANG_PROPERTIES_FAIL));
- rb->action_userabort(TIMEOUT_BLOCK);
- return PLUGIN_OK;
- }
+ .len = MAX_PATH,
+ .dir_count = 0,
+ .file_count = 0,
+ .byte_count = 0,
+ };
- FOR_NB_SCREENS(i)
- rb->viewportmanager_theme_enable(i, true, NULL);
+ const char *file = parameter;
+ if(!parameter)
+ return PLUGIN_ERROR;
- rb->gui_synclist_init(&properties_lists, &get_props, &dps, false, 2, NULL);
- rb->gui_synclist_set_title(&properties_lists, rb->str(its_a_dir ? LANG_PROPERTIES_DIRECTORY_PROPERTIES : LANG_PROPERTIES_FILE_PROPERTIES), NOICON);
- rb->gui_synclist_set_icon_callback(&properties_lists, NULL);
- if (rb->global_settings->talk_menu)
- rb->gui_synclist_set_voice_callback(&properties_lists, speak_property_selection);
- rb->gui_synclist_set_nb_items(&properties_lists, num_properties * 2);
- rb->gui_synclist_limit_scroll(&properties_lists, true);
- rb->gui_synclist_select_item(&properties_lists, 0);
- rb->gui_synclist_draw(&properties_lists);
- rb->gui_synclist_speak_item(&properties_lists);
+#ifdef HAVE_TOUCHSCREEN
+ rb->touchscreen_set_mode(rb->global_settings->touch_mode);
+#endif
- while(!quit)
+#ifdef HAVE_TAGCACHE
+ if (!rb->strcmp(file, MAKE_ACT_STR(ACTIVITY_DATABASEBROWSER))) /* db table selected */
{
- button = rb->get_action(CONTEXT_LIST, HZ);
- /* HZ so the status bar redraws corectly */
- if (rb->gui_synclist_do_button(&properties_lists,&button,LIST_WRAP_ON))
- continue;
- switch(button)
+ props_type = PROPS_MUL_ID3;
+ mul_id3_count = skipped_count = 0;
+
+ if (!rb->tagtree_subentries_do_action(&mul_id3_add) || mul_id3_count == 0)
+ return PLUGIN_ERROR;
+ else if (mul_id3_count > 1) /* otherwise, the retrieved id3 can be used as-is */
+ finalize_id3(&id3);
+
+ if (skipped_count > 0)
+ rb->splashf(HZ*2, "Skipped %d", skipped_count);
+ }
+ else
+#endif
+ if (file[0] == '/') /* single track selected */
+ {
+ const char* file_name = rb->strrchr(file, '/') + 1;
+ int dirlen = (file_name - file);
+
+ rb->strlcpy(str_dirname, file, dirlen + 1);
+ rb->snprintf(str_filename, sizeof str_filename, "%s", file+dirlen);
+
+ if(!determine_file_or_dir())
{
- case ACTION_STD_CANCEL:
- quit = true;
- break;
- default:
- if (rb->default_event_handler(button) == SYS_USB_CONNECTED)
- {
- quit = true;
- usb = true;
- }
- break;
+ /* weird: we couldn't find the entry. This Should Never Happen (TM) */
+ rb->splashf(0, "File/Dir not found: %s", file);
+ rb->action_userabort(TIMEOUT_BLOCK);
+ return PLUGIN_OK;
+ }
+
+ /* get the info depending on its_a_dir */
+ if(!(props_type == PROPS_DIR ? dir_properties(file, &stats) : file_properties(file)))
+ {
+ /* something went wrong (to do: tell user what it was (nesting,...) */
+ rb->splash(0, ID2P(LANG_PROPERTIES_FAIL));
+ rb->action_userabort(TIMEOUT_BLOCK);
+ return PLUGIN_OK;
}
}
+ else
+ return PLUGIN_ERROR;
+
+ FOR_NB_SCREENS(i)
+ rb->viewportmanager_theme_enable(i, true, NULL);
+
+ bool usb = props_type == PROPS_ID3 ? rb->browse_id3(&id3, 0, 0, &tm, 1) :
+#ifdef HAVE_TAGCACHE
+ props_type == PROPS_MUL_ID3 ? rb->browse_id3(&id3, 0, 0, NULL, mul_id3_count) :
+#endif
+ browse_file_or_dir(&stats);
FOR_NB_SCREENS(i)
rb->viewportmanager_theme_undo(i, false);
- return usb? PLUGIN_USB_CONNECTED: PLUGIN_OK;
+ return usb ? PLUGIN_USB_CONNECTED : PLUGIN_OK;
}
diff --git a/apps/plugins/puzzles/rockbox.c b/apps/plugins/puzzles/rockbox.c
index 73f9857b4a..e175429075 100644
--- a/apps/plugins/puzzles/rockbox.c
+++ b/apps/plugins/puzzles/rockbox.c
@@ -1465,7 +1465,7 @@ static void rb_blitter_free(void *handle, blitter *bl)
static void rb_blitter_save(void *handle, blitter *bl, int x, int y)
{
/* no viewport offset */
-#if defined(LCD_STRIDEFORMAT) && (LCD_STRIDEFORMAT == VERTICAL_STRIDE)
+#if LCD_STRIDEFORMAT == VERTICAL_STRIDE
#error no vertical stride
#else
if(bl && bl->bmp.data)
@@ -2450,7 +2450,6 @@ static int list_choose(const char *list_str, const char *title, int sel)
rb->gui_synclist_init(&list, &config_choices_formatter, (void*)list_str, false, 1, NULL);
rb->gui_synclist_set_icon_callback(&list, NULL);
rb->gui_synclist_set_nb_items(&list, n);
- rb->gui_synclist_limit_scroll(&list, false);
rb->gui_synclist_select_item(&list, sel);
@@ -2459,7 +2458,7 @@ static int list_choose(const char *list_str, const char *title, int sel)
{
rb->gui_synclist_draw(&list);
int button = rb->get_action(CONTEXT_LIST, TIMEOUT_BLOCK);
- if(rb->gui_synclist_do_button(&list, &button, LIST_WRAP_ON))
+ if(rb->gui_synclist_do_button(&list, &button))
continue;
switch(button)
{
@@ -2664,7 +2663,6 @@ static bool config_menu(void)
rb->gui_synclist_init(&list, &config_formatter, config, false, 1, NULL);
rb->gui_synclist_set_icon_callback(&list, NULL);
rb->gui_synclist_set_nb_items(&list, n);
- rb->gui_synclist_limit_scroll(&list, false);
rb->gui_synclist_select_item(&list, 0);
@@ -2674,7 +2672,7 @@ static bool config_menu(void)
{
rb->gui_synclist_draw(&list);
int button = rb->get_action(CONTEXT_LIST, TIMEOUT_BLOCK);
- if(rb->gui_synclist_do_button(&list, &button, LIST_WRAP_ON))
+ if(rb->gui_synclist_do_button(&list, &button))
continue;
switch(button)
{
@@ -2750,7 +2748,6 @@ static int do_preset_menu(struct preset_menu *menu, char *title, int selected)
rb->gui_synclist_init(&list, &preset_formatter, menu, false, 1, NULL);
rb->gui_synclist_set_icon_callback(&list, NULL);
rb->gui_synclist_set_nb_items(&list, menu->n_entries);
- rb->gui_synclist_limit_scroll(&list, false);
rb->gui_synclist_select_item(&list, selected);
@@ -2760,7 +2757,7 @@ static int do_preset_menu(struct preset_menu *menu, char *title, int selected)
{
rb->gui_synclist_draw(&list);
int button = rb->get_action(CONTEXT_LIST, TIMEOUT_BLOCK);
- if(rb->gui_synclist_do_button(&list, &button, LIST_WRAP_ON))
+ if(rb->gui_synclist_do_button(&list, &button))
continue;
switch(button)
{
@@ -3393,9 +3390,7 @@ static void shutdown_tlsf(void)
static void exit_handler(void)
{
-#ifdef HAVE_SW_POWEROFF
sw_poweroff_restore();
-#endif
unload_fonts();
shutdown_tlsf();
@@ -3655,9 +3650,7 @@ static void puzzles_main(void)
{
rb_atexit(exit_handler);
-#ifdef HAVE_SW_POWEROFF
sw_poweroff_disable();
-#endif
init_default_settings();
init_fonttab();
diff --git a/apps/plugins/random_folder_advance_config.c b/apps/plugins/random_folder_advance_config.c
index c9dce0223b..5688ff93d3 100644
--- a/apps/plugins/random_folder_advance_config.c
+++ b/apps/plugins/random_folder_advance_config.c
@@ -211,7 +211,7 @@ static bool custom_dir(void)
rb->close(fd2);
if(errors)
/* Press button to continue */
- rb->get_action(CONTEXT_STD, TIMEOUT_BLOCK);
+ rb->get_action(CONTEXT_STD, TIMEOUT_BLOCK);
}
else
return false;
@@ -259,11 +259,11 @@ static int load_list(void)
{
return -2;
}
-
+
rb->read(myfd,buffer,buffer_size);
rb->close(myfd);
list = (struct file_format *)buffer;
-
+
return 0;
}
@@ -288,7 +288,7 @@ static int save_list(void)
rb->lseek(myfd,0,SEEK_SET);
rb->write(myfd,&dirs_count,sizeof(int));
rb->close(myfd);
-
+
return 1;
}
@@ -298,27 +298,26 @@ static int edit_list(void)
bool exit = false;
int button,i;
int selection, ret = 0;
-
+
/* load the dat file if not already done */
if ((list == NULL || list->count == 0) && (i = load_list()) != 0)
{
rb->splashf(HZ*2, "Could not load %s, rv = %d", RFA_FILE, i);
return -1;
}
-
+
dirs_count = list->count;
-
+
rb->gui_synclist_init(&lists,list_get_name_cb,0, false, 1, NULL);
rb->gui_synclist_set_icon_callback(&lists,NULL);
rb->gui_synclist_set_nb_items(&lists,list->count);
- rb->gui_synclist_limit_scroll(&lists,true);
rb->gui_synclist_select_item(&lists, 0);
-
+
while (!exit)
{
rb->gui_synclist_draw(&lists);
button = rb->get_action(CONTEXT_LIST,TIMEOUT_BLOCK);
- if (rb->gui_synclist_do_button(&lists,&button,LIST_WRAP_UNLESS_HELD))
+ if (rb->gui_synclist_do_button(&lists, &button))
continue;
selection = rb->gui_synclist_get_sel_pos(&lists);
switch (button)
@@ -366,6 +365,7 @@ static int edit_list(void)
{
case 0:
save_list();
+ /* fallthrough */
case 1:
exit = true;
ret = -2;
@@ -386,22 +386,22 @@ static int export_list_to_file_text(void)
rb->splashf(HZ*2, "Could not load %s, rv = %d", RFA_FILE, i);
return 0;
}
-
+
if (list->count <= 0)
{
rb->splashf(HZ*2, "no dirs in list file: %s", RFA_FILE);
return 0;
}
-
+
/* create and open the file */
int myfd = rb->creat(RFA_FILE_TEXT, 0666);
if (myfd < 0)
{
- rb->splashf(HZ*4, "failed to open: fd = %d, file = %s",
+ rb->splashf(HZ*4, "failed to open: fd = %d, file = %s",
myfd, RFA_FILE_TEXT);
return -1;
}
-
+
/* write each directory to file */
for (i = 0; i < list->count; i++)
{
@@ -410,7 +410,7 @@ static int export_list_to_file_text(void)
rb->fdprintf(myfd, "%s\n", list->folder[i]);
}
}
-
+
rb->close(myfd);
rb->splash(HZ, "Done");
return 1;
@@ -419,7 +419,7 @@ static int export_list_to_file_text(void)
static int import_list_from_file_text(void)
{
char line[MAX_PATH];
-
+
buffer = rb->plugin_get_audio_buffer(&buffer_size);
if (buffer == NULL)
{
@@ -433,11 +433,11 @@ static int import_list_from_file_text(void)
rb->splashf(HZ*2, "failed to open: %s", RFA_FILE_TEXT);
return -1;
}
-
+
/* set the list structure, and initialize count */
list = (struct file_format *)buffer;
list->count = 0;
-
+
while ((rb->read_line(myfd, line, MAX_PATH - 1)) > 0)
{
/* copy the dir name, and skip the newline */
@@ -447,16 +447,16 @@ static int import_list_from_file_text(void)
{
if (line[len-1] == 0x0A || line[len-1] == 0x0D)
line[len-1] = 0x00;
- if (len > 1 &&
+ if (len > 1 &&
(line[len-2] == 0x0A || line[len-2] == 0x0D))
line[len-2] = 0x00;
}
-
+
rb->strcpy(list->folder[list->count++], line);
}
-
+
rb->close(myfd);
-
+
if (list->count == 0)
{
load_list();
@@ -483,14 +483,14 @@ static int start_shuffled_play(void)
rb->splashf(HZ*2, "Not enough memory for shuffling");
return 0;
}
-
+
/* load the dat file if not already done */
if ((list == NULL || list->count == 0) && (i = load_list()) != 0)
{
rb->splashf(HZ*2, "Could not load %s, rv = %d", RFA_FILE, i);
return 0;
}
-
+
if (list->count <= 0)
{
rb->splashf(HZ*2, "no dirs in list file: %s", RFA_FILE);
@@ -506,7 +506,7 @@ static int start_shuffled_play(void)
}
for(i=0;i<list->count;i++)
order[i]=i;
-
+
for(i = list->count - 1; i >= 0; i--)
{
/* the rand is from 0 to RAND_MAX, so adjust to our value range */
@@ -517,7 +517,7 @@ static int start_shuffled_play(void)
order[candidate] = order[i];
order[i] = store;
}
-
+
/* We don't want whatever is playing */
if (!(rb->playlist_remove_all_tracks(NULL) == 0
&& rb->playlist_create(NULL, NULL) == 0))
@@ -642,6 +642,6 @@ enum plugin_status plugin_start(const void* parameter)
#endif
cancel = false;
-
+
return main_menu();
}
diff --git a/apps/plugins/rb_info.c b/apps/plugins/rb_info.c
new file mode 100644
index 0000000000..a89cc658cc
--- /dev/null
+++ b/apps/plugins/rb_info.c
@@ -0,0 +1,574 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// __ \_/ ___\| |/ /| __ \ / __ \ \/ /
+ * Jukebox | | ( (__) ) \___| ( | \_\ ( (__) ) (
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2020 William Wilgus
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+/* WIP rb_info common info that you wonder about when rockboxing?
+ */
+
+#include "plugin.h"
+#include "lang_enum.h"
+#include "../open_plugin.h"
+#include "logf.h"
+#include "lib/action_helper.h"
+#include "lib/button_helper.h"
+#include "lib/pluginlib_actions.h"
+#include "lib/printcell_helper.h"
+
+#define MENU_ID(x) (((void*)&"RPBUTACNGSX\0" + x))
+enum {
+ M_ROOT = 0,
+ M_PATHS,
+ M_BUFFERS,
+ M_BUTTONS,
+ M_BTNTEST,
+ M_ACTIONS,
+ M_CONTEXTS,
+ M_ACTTEST,
+ M_PLUGINS,
+ M_TESTPUT,
+ M_EXIT,
+ M_LAST_ITEM //ITEM COUNT
+};
+
+#define MENU_ID_PLUGINS_ITEMS 5
+
+/*Action test and Button test*/
+static struct menu_test_t {
+ int count;
+ int context;
+ int last_btn_or_act;
+} m_test;
+
+struct menu_buffer_t { const char *name; size_t size;};
+static const struct menu_buffer_t m_buffer[] =
+{
+#ifndef MAX_LOGF_SIZE
+#define MAX_LOGF_SIZE (0)
+#endif
+#ifndef CACHE_SIZE
+#define CACHE_SIZE (0)
+#endif
+ {"thread stack", DEFAULT_STACK_SIZE},
+ {"plugin buffer", PLUGIN_BUFFER_SIZE},
+ {"frame_buffer", FRAMEBUFFER_SIZE},
+ {"codec_buffer", CODEC_SIZE},
+ {"logf_buffer", MAX_LOGF_SIZE},
+ {"cache", CACHE_SIZE},
+};
+
+/* stringify the macro value */
+#define MACROVAL(x) MACROSTR(x)
+#define MACROSTR(x) #x
+static int main_last_sel = 0;
+static struct gui_synclist lists;
+static void synclist_set(char*, int, int, int);
+
+struct paths { const char *name; const char *path; };
+static const struct paths paths[] = {
+ {"Home", ""HOME_DIR},
+ {"Rockbox", ""ROCKBOX_DIR},
+ {"Plugins", ""PLUGIN_DIR},
+ {"Codecs", ""CODECS_DIR},
+ {"WPS", ""WPS_DIR},
+ {"SBS", ""SBS_DIR},
+ {"Theme", ""THEME_DIR},
+ {"Font", ""FONT_DIR},
+ {"Icon", ""ICON_DIR},
+ {"Backdrop", ""BACKDROP_DIR},
+ {"Eq", ""EQS_DIR},
+ {"Rec Presets", ""RECPRESETS_DIR},
+ {"Recordings", ""REC_BASE_DIR,},
+ {"Fm Presets", ""FMPRESET_PATH},
+ {"MAX_PATH", ""MACROVAL(MAX_PATH)" bytes"},
+};
+#define TESTPUT_HEADER "$*64$col1$col2$*128$col3$col4$col5$col6$*64$col7$col8"
+static int testput_cols = 0;
+struct mainmenu { const char *name; void *menuid; int items;};
+static struct mainmenu mainmenu[M_LAST_ITEM] = {
+#define MENU_ITEM(ID, NAME, COUNT) [ID]{NAME, MENU_ID(ID), (int)COUNT}
+MENU_ITEM(M_ROOT, "Rockbox Info Plugin", M_LAST_ITEM),
+MENU_ITEM(M_PATHS, ID2P(LANG_SHOW_PATH), ARRAYLEN(paths)),
+MENU_ITEM(M_BUFFERS, ID2P(LANG_BUFFER_STAT), ARRAYLEN(m_buffer)),
+MENU_ITEM(M_BUTTONS, "Buttons", -1), /* Set at runtime in plugin_start: */
+MENU_ITEM(M_BTNTEST, "Button test", 2),
+MENU_ITEM(M_ACTIONS, "Actions", LAST_ACTION_PLACEHOLDER),
+MENU_ITEM(M_CONTEXTS, "Contexts", LAST_CONTEXT_PLACEHOLDER ),
+MENU_ITEM(M_ACTTEST, "Action test", 3),
+MENU_ITEM(M_PLUGINS, ID2P(LANG_PLUGINS), MENU_ID_PLUGINS_ITEMS),
+MENU_ITEM(M_TESTPUT, "Printcell test", 36),
+MENU_ITEM(M_EXIT, ID2P(LANG_MENU_QUIT), 0),
+#undef MENU_ITEM
+};
+
+static const struct mainmenu *mainitem(int selected_item)
+{
+ static const struct mainmenu empty = {0};
+ if (selected_item >= 0 && selected_item < (int) ARRAYLEN(mainmenu))
+ return &mainmenu[selected_item];
+ else
+ return &empty;
+}
+
+static void cleanup(void *parameter)
+{
+ (void)parameter;
+}
+
+#if 0
+static enum themable_icons menu_icon_cb(int selected_item, void * data)
+{
+ (void)data;
+ (void)selected_item;
+ return Icon_NOICON;
+}
+#endif
+
+static const char *menu_plugin_name_cb(int selected_item, void* data,
+ char* buf, size_t buf_len)
+{
+ (void)data;
+ buf[0] = '\0';
+ switch(selected_item)
+ {
+ case 0:
+ rb->snprintf(buf, buf_len, "%s: [%d bytes] ", "plugin_api", (int)sizeof(struct plugin_api));
+ break;
+ case 1:
+ rb->snprintf(buf, buf_len, "%s: [%d bytes] ", "plugin buffer", PLUGIN_BUFFER_SIZE);
+ break;
+ case 2:
+ rb->snprintf(buf, buf_len, "%s: [%d bytes] ", "frame_buffer", (int)FRAMEBUFFER_SIZE);
+ break;
+ case 3:
+ rb->snprintf(buf, buf_len, "%s: [W: %d H:%d] ", "LCD", LCD_WIDTH, LCD_HEIGHT);
+ break;
+ case 4:
+ rb->snprintf(buf, buf_len, "%s: [%d bits] ", "fb_data", (int)(sizeof(fb_data) * CHAR_BIT));
+ break;
+ case 5:
+ break;
+ }
+ return buf;
+}
+
+static const char *menu_button_test_name_cb(int selected_item, void* data,
+ char* buf, size_t buf_len)
+{
+ (void)data;
+ int curbtn = BUTTON_NONE;
+ buf[0] = '\0';
+ switch(selected_item)
+ {
+ case 0:
+ rb->snprintf(buf, buf_len, "%s: [%s] ", "Button test",
+ m_test.count > 0 ? "true":"false");
+ break;
+ case 1:
+ if (m_test.count > 0)
+ {
+ if (m_test.count <= 2)
+ curbtn = rb->button_get_w_tmo(HZ * 2);
+ else
+ m_test.last_btn_or_act = BUTTON_NONE;
+ if (curbtn == BUTTON_NONE)
+ {
+ m_test.count--;
+ }
+ else
+ m_test.last_btn_or_act = curbtn;
+ }
+ get_button_names(buf, buf_len, m_test.last_btn_or_act);
+
+ break;
+ }
+ return buf;
+}
+
+static const char *menu_action_test_name_cb(int selected_item, void* data,
+ char* buf, size_t buf_len)
+{
+ (void)data;
+ const char *fmtstr;
+ int curact = ACTION_NONE;
+ buf[0] = '\0';
+ switch(selected_item)
+ {
+ case 0:
+ rb->snprintf(buf, buf_len, "%s: [%s] ", "Action test",
+ m_test.count > 0 ? "true":"false");
+ break;
+ case 1:
+ if (m_test.count <= 0)
+ {
+ if (m_test.context <= 0)
+ fmtstr = "%s > ";
+ else if (m_test.context >= LAST_CONTEXT_PLACEHOLDER - 1)
+ fmtstr = "< %s ";
+ else
+ fmtstr = "< %s > ";
+ }
+ else
+ fmtstr = "%s";
+
+ rb->snprintf(buf, buf_len, fmtstr, context_name(m_test.context));
+ break;
+ case 2:
+ if (m_test.count > 0)
+ {
+ if (m_test.count <= 2)
+ curact = rb->get_action(m_test.context, HZ * 2);
+ else
+ m_test.last_btn_or_act = ACTION_NONE;
+ if (curact == ACTION_NONE && rb->button_get(false) == BUTTON_NONE)
+ {
+ m_test.count--;
+ }
+ else
+ {
+ m_test.last_btn_or_act = curact;
+ m_test.count = 2;
+ }
+ }
+ return action_name(m_test.last_btn_or_act);
+
+ break;
+ }
+ return buf;
+}
+
+static const char* list_get_name_cb(int selected_item, void* data,
+ char* buf, size_t buf_len)
+{
+ buf[0] = '\0';
+ if (data == MENU_ID(M_ROOT))
+ return mainitem(selected_item)->name;
+ else if (selected_item == 0 && data != MENU_ID(M_TESTPUT)) /*header text*/
+ return mainitem(main_last_sel)->name;
+ else if (selected_item >= mainitem(main_last_sel)->items - 1)
+ return ID2P(LANG_BACK);
+
+ if (data == MENU_ID(M_PATHS))
+ {
+ selected_item--;
+ if (selected_item >= 0 && selected_item < mainitem(M_PATHS)->items)
+ {
+ const struct paths *cur = &paths[selected_item];
+ rb->snprintf(buf, buf_len, "%s: [%s] ", cur->name, cur->path);
+ return buf;
+ }
+ }
+ else if (data == MENU_ID(M_BUTTONS))
+ {
+ const struct available_button *btn = &available_buttons[selected_item - 1];
+ rb->snprintf(buf, buf_len, "%s: [0x%X] ", btn->name, (unsigned int) btn->value);
+ return buf;
+ }
+ else if (data == MENU_ID(M_BTNTEST))
+ return menu_button_test_name_cb(selected_item - 1, data, buf, buf_len);
+ else if (data == MENU_ID(M_ACTIONS))
+ return action_name(selected_item - 1);
+ else if (data == MENU_ID(M_CONTEXTS))
+ return context_name(selected_item - 1);
+ else if (data == MENU_ID(M_ACTTEST))
+ return menu_action_test_name_cb(selected_item - 1, data, buf, buf_len);
+ else if (data == MENU_ID(M_BUFFERS))
+ {
+ const struct menu_buffer_t *bufm = &m_buffer[selected_item - 1];
+ rb->snprintf(buf, buf_len, "%s: [%ld bytes] ", bufm->name, (long)bufm->size);
+ return buf;
+ }
+ else if (data == MENU_ID(M_PLUGINS))
+ {
+ return menu_plugin_name_cb(selected_item - 1, data, buf, buf_len);
+ }
+ else if (data == MENU_ID(M_TESTPUT))
+ {
+ rb->snprintf(buf, buf_len, "put_line item: [ %d ]$Text %d$Text LONGER TEST text %d $4$5$6$7$8$9", selected_item, 1, 2);
+ return buf;
+ }
+ return buf;
+}
+
+static int list_voice_cb(int list_index, void* data)
+{
+ if (!rb->global_settings->talk_menu)
+ return -1;
+
+ if (data == MENU_ID(M_ROOT))
+ {
+ const char * name = mainitem(list_index)->name;
+ long id = P2ID((const unsigned char *)name);
+ if(id>=0)
+ rb->talk_id(id, true);
+ else
+ rb->talk_spell(name, true);
+ }
+ else if (data == MENU_ID(M_BUFFERS) || data == MENU_ID(M_PLUGINS))
+ {
+ char buf[64];
+ const char* name = list_get_name_cb(list_index, data, buf, sizeof(buf));
+ long id = P2ID((const unsigned char *)name);
+ if(id>=0)
+ rb->talk_id(id, true);
+ else
+ {
+ char* bytstr = rb->strcasestr(name, "bytes");
+ if (bytstr != NULL)
+ *bytstr = '\0';
+ rb->talk_spell(name, true);
+ }
+ }
+ else if (data == MENU_ID(M_TESTPUT))
+ {
+ char buf[64];
+ const char* name = printcell_get_column_text(printcell_get_column_selected(),
+ buf, sizeof(buf));
+ long id = P2ID((const unsigned char *)name);
+ if(id>=0)
+ rb->talk_id(id, true);
+ else
+ rb->talk_spell(name, true);
+ }
+ else
+ {
+ char buf[64];
+ const char* name = list_get_name_cb(list_index, data, buf, sizeof(buf));
+ long id = P2ID((const unsigned char *)name);
+ if(id>=0)
+ rb->talk_id(id, true);
+ else
+ rb->talk_spell(name, true);
+ }
+ return 0;
+}
+
+int menu_action_cb(int *action, int selected_item, bool* exit, struct gui_synclist *lists)
+{
+ if (lists->data == MENU_ID(M_TESTPUT) && (selected_item < (mainitem(M_TESTPUT)->items) - 1)/*back*/)
+ {
+ if (*action == ACTION_STD_OK)
+ {
+ printcell_increment_column(1, true);
+ *action = ACTION_NONE;
+ }
+ else if (*action == ACTION_STD_CANCEL)
+ {
+ if (printcell_increment_column(-1, true) != testput_cols - 1)
+ {
+ *action = ACTION_NONE;
+ }
+ }
+ else if (*action == ACTION_STD_CONTEXT)
+ {
+ char buf[PRINTCELL_MAXLINELEN];
+ char* bufp = buf;
+ int selcol = printcell_get_column_selected();
+ bufp = printcell_get_column_text(selcol, bufp, PRINTCELL_MAXLINELEN);
+ rb->splashf(HZ * 2, "Item: %s", bufp);
+ }
+ }
+ else if (lists->data == MENU_ID(M_ACTTEST))
+ {
+ if (selected_item == 2) /* context */
+ {
+ int ctx = m_test.context;
+ if (*action == ACTION_STD_OK)
+ m_test.context++;
+ else if (*action == ACTION_STD_CANCEL)
+ m_test.context--;
+
+ if (m_test.context < 0)
+ m_test.context = 0;
+ else if (m_test.context >= LAST_CONTEXT_PLACEHOLDER)
+ m_test.context = LAST_CONTEXT_PLACEHOLDER - 1;
+
+ if (ctx != m_test.context)
+ rb->gui_synclist_speak_item(lists);
+
+ goto default_handler;
+ }
+ if (*action == ACTION_STD_OK)
+ {
+ if (selected_item == 1 || selected_item == 3)
+ {
+ m_test.count = 3;
+ rb->gui_synclist_select_item(lists, 3);
+ }
+ }
+ }
+ else if (lists->data == MENU_ID(M_BTNTEST))
+ {
+ if (*action == ACTION_STD_OK)
+ {
+ if (selected_item == 1 || selected_item == 2)
+ {
+ m_test.count = 3;
+ rb->gui_synclist_select_item(lists, 2);
+ }
+ }
+ }
+/* common */
+ if (*action == ACTION_STD_OK)
+ {
+ if (lists->data == MENU_ID(M_ROOT))
+ {
+ rb->memset(&m_test, 0, sizeof(struct menu_test_t));
+ const struct mainmenu *cur = mainitem(selected_item);
+
+ if (cur->menuid == NULL || cur->menuid == MENU_ID(M_EXIT))
+ *exit = true;
+ else
+ {
+ main_last_sel = selected_item;
+
+ if (cur->menuid == MENU_ID(M_TESTPUT))
+ {
+ synclist_set(cur->menuid, 0, cur->items, 1);
+ printcell_enable(true);
+ //lists->callback_draw_item = test_listdraw_fn;
+ }
+ else
+ {
+ printcell_enable(false);
+ synclist_set(cur->menuid, 1, cur->items, 1);
+ }
+ rb->gui_synclist_draw(lists);
+ }
+ }
+ else if (selected_item <= 0) /* title */
+ {
+ rb->gui_synclist_select_item(lists, 1);
+ }
+ else if (selected_item >= (mainitem(main_last_sel)->items) - 1)/*back*/
+ {
+ *action = ACTION_STD_CANCEL;
+ }
+ else if (lists->data == MENU_ID(M_TESTPUT))
+ {
+
+ }
+ else if (lists->data == MENU_ID(M_ACTIONS) ||
+ lists->data == MENU_ID(M_CONTEXTS))
+ {
+ char buf[MAX_PATH];
+ const char *name = list_get_name_cb(selected_item, lists->data, buf, sizeof(buf));
+ /* splash long enough to get fingers off button then wait for new button press */
+ rb->splashf(HZ / 2, "%s %d (0x%X)", name, selected_item -1, selected_item -1);
+ rb->button_get(true);
+ }
+ }
+ if (*action == ACTION_STD_CANCEL)
+ {
+ if (lists->data == MENU_ID(M_TESTPUT))
+ {
+ //lists->callback_draw_item = NULL;
+ printcell_enable(false);
+ }
+ if (lists->data != MENU_ID(M_ROOT))
+ {
+ const struct mainmenu *mainm = &mainmenu[0];
+ synclist_set(mainm->menuid, main_last_sel, mainm->items, 1);
+ rb->gui_synclist_draw(lists);
+ }
+ else
+ *exit = true;
+ }
+default_handler:
+ if (rb->default_event_handler_ex(*action, cleanup, NULL) == SYS_USB_CONNECTED)
+ {
+ *exit = true;
+ return PLUGIN_USB_CONNECTED;
+ }
+ return PLUGIN_OK;
+}
+
+static void synclist_set(char* menu_id, int selected_item, int items, int sel_size)
+{
+ if (items <= 0)
+ return;
+ if (selected_item < 0)
+ selected_item = 0;
+
+ list_voice_cb(0, menu_id);
+ rb->gui_synclist_init(&lists,list_get_name_cb,
+ menu_id, false, sel_size, NULL);
+ if (menu_id == MENU_ID(M_TESTPUT))
+ {
+ testput_cols = printcell_set_columns(&lists, NULL,
+ TESTPUT_HEADER, Icon_Rockbox);
+ }
+ else
+ {
+ rb->gui_synclist_set_title(&lists, NULL,-1);
+ }
+ rb->gui_synclist_set_icon_callback(&lists,NULL);
+ rb->gui_synclist_set_voice_callback(&lists, list_voice_cb);
+ rb->gui_synclist_set_nb_items(&lists,items);
+ rb->gui_synclist_select_item(&lists, selected_item);
+
+}
+
+enum plugin_status plugin_start(const void* parameter)
+{
+ int ret = PLUGIN_OK;
+ int selected_item = -1;
+ int action;
+ bool redraw = true;
+ bool exit = false;
+ if (parameter)
+ {
+ //
+ }
+ mainmenu[M_BUTTONS].items = available_button_count;
+ /* add header and back item to each submenu */
+ for (int i = 1; i < M_LAST_ITEM; i++)
+ mainmenu[i].items += 2;
+ mainmenu[M_TESTPUT].items -= 1;
+ if (!exit)
+ {
+ const struct mainmenu *mainm = &mainmenu[0];
+ synclist_set(mainm->menuid, main_last_sel, mainm->items, 1);
+ rb->gui_synclist_draw(&lists);
+
+ while (!exit)
+ {
+ action = rb->get_action(CONTEXT_LIST, HZ / 10);
+ if (m_test.count > 0)
+ action = ACTION_REDRAW;
+
+ if (action == ACTION_NONE)
+ {
+ if (redraw)
+ {
+ action = ACTION_REDRAW;
+ redraw = false;
+ }
+ }
+ else
+ redraw = true;
+ ret = menu_action_cb(&action, selected_item, &exit, &lists);
+ if (rb->gui_synclist_do_button(&lists, &action))
+ continue;
+ selected_item = rb->gui_synclist_get_sel_pos(&lists);
+ }
+ }
+ printcell_enable(false);
+ return ret;
+}
diff --git a/apps/plugins/resistor.c b/apps/plugins/resistor.c
index 2e204010a9..4461dc0dea 100644
--- a/apps/plugins/resistor.c
+++ b/apps/plugins/resistor.c
@@ -574,9 +574,8 @@ static void display_helpfile(void)
static void led_resistance_calc(void)
{
-#ifdef HAVE_BACKLIGHT
backlight_ignore_timeout();
-#endif
+
int voltage_menu_selection, button_press, j, k, l, foreward_current = 0;
int fwd_current_selection = 0;
bool quit = false;
@@ -769,9 +768,8 @@ static void led_resistance_calc(void)
default:
quit = true;
-#ifdef HAVE_BACKLIGHT
backlight_use_settings();
-#endif
+
break;
}
}
@@ -784,9 +782,8 @@ static void led_resistance_calc(void)
static void resistance_to_color(void)
{
-#ifdef HAVE_BACKLIGHT
backlight_ignore_timeout();
-#endif
+
int menu_selection;
int menu_selection_tol;
int button_press;
@@ -910,9 +907,9 @@ static void resistance_to_color(void)
break;
default:
quit = true;
-#ifdef HAVE_BACKLIGHT
+
backlight_use_settings();
-#endif
+
break;
}
}
@@ -924,9 +921,8 @@ static void resistance_to_color(void)
static void color_to_resistance(void)
{
-#ifdef HAVE_BACKLIGHT
backlight_ignore_timeout();
-#endif
+
bool quit = false;
int button_input = 0;
@@ -953,9 +949,9 @@ static void color_to_resistance(void)
if(third_band==RES_INVALID) break;
fourth_band = do_fourth_band_menu();
- if(third_band==RES_INVALID) break;
-
- total_resistance_centiunits = calculate_resistance(first_band,
+ if(fourth_band==RES_INVALID) break;
+
+ total_resistance_centiunits = calculate_resistance(first_band,
second_band,
third_band);
@@ -995,9 +991,7 @@ static void color_to_resistance(void)
case PLA_SELECT:
default:
quit = true;
-#ifdef HAVE_BACKLIGHT
backlight_use_settings();
-#endif
break;
}
}
diff --git a/apps/plugins/reversi/reversi-gui.c b/apps/plugins/reversi/reversi-gui.c
index 74dd98b676..e4bb232a26 100644
--- a/apps/plugins/reversi/reversi-gui.c
+++ b/apps/plugins/reversi/reversi-gui.c
@@ -400,7 +400,7 @@ static bool reversi_gui_choose_strategy(
}
result =
- rb->set_option(prompt, &index, INT, strategy_settings, num_items, NULL);
+ rb->set_option(prompt, &index, RB_INT, strategy_settings, num_items, NULL);
(*player) = strategy_values[index];
@@ -450,7 +450,7 @@ static bool reversi_gui_menu(void) {
break;
}
}
- rb->set_option(MENU_TEXT_WRAP_MODE, &index, INT,
+ rb->set_option(MENU_TEXT_WRAP_MODE, &index, RB_INT,
cursor_wrap_mode_settings, 3, NULL);
cursor_wrap_mode = cursor_wrap_mode_values[index];
break;
diff --git a/apps/plugins/reversi/reversi-gui.h b/apps/plugins/reversi/reversi-gui.h
index 7e031e6103..926a71e1a2 100644
--- a/apps/plugins/reversi/reversi-gui.h
+++ b/apps/plugins/reversi/reversi-gui.h
@@ -361,6 +361,9 @@
#define REVERSI_BUTTON_MAKE_MOVE BUTTON_SELECT
#define REVERSI_BUTTON_MENU BUTTON_MENU
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+/* use touchscreen */
+
#else
#error No keymap defined!
#endif
diff --git a/apps/plugins/robotfindskitten.c b/apps/plugins/robotfindskitten.c
index 4f228423b6..31c419e33d 100644
--- a/apps/plugins/robotfindskitten.c
+++ b/apps/plugins/robotfindskitten.c
@@ -469,7 +469,13 @@ static char* messages[] =
#define RFK_VERSION "v1.4142135.406"
+#if (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_3G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_4G_PAD)
+# define RFK_QUIT PLA_SELECT_REPEAT
+#else
# define RFK_QUIT PLA_CANCEL
+#endif
# define RFK_RIGHT PLA_RIGHT
# define RFK_LEFT PLA_LEFT
# define RFK_UP PLA_UP
diff --git a/apps/plugins/rockblox.c b/apps/plugins/rockblox.c
index 07a15bfb62..a0105a1ffb 100644
--- a/apps/plugins/rockblox.c
+++ b/apps/plugins/rockblox.c
@@ -33,7 +33,7 @@
(CONFIG_KEYPAD == IPOD_3G_PAD) || \
(CONFIG_KEYPAD == IPOD_1G2G_PAD)
-#define ROCKBLOX_OFF (BUTTON_MENU | BUTTON_SELECT)
+#define ROCKBLOX_OFF (BUTTON_SELECT | BUTTON_REPEAT)
#define ROCKBLOX_ROTATE_CCW BUTTON_SCROLL_BACK
#define ROCKBLOX_ROTATE_CCW2 (BUTTON_MENU | BUTTON_REL)
#define ROCKBLOX_ROTATE_CW BUTTON_SCROLL_FWD
@@ -41,6 +41,7 @@
#define ROCKBLOX_RIGHT BUTTON_RIGHT
#define ROCKBLOX_DOWN BUTTON_PLAY
#define ROCKBLOX_RESTART (BUTTON_SELECT | BUTTON_PLAY)
+#define ROCKBLOX_DROP_PRE BUTTON_SELECT
#define ROCKBLOX_DROP (BUTTON_SELECT | BUTTON_REL)
#elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
@@ -465,6 +466,9 @@
#define ROCKBLOX_DROP BUTTON_PLAY
#define ROCKBLOX_RESTART BUTTON_BACK
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+/* use touchscreen */
+
#else
#error No keymap defined!
#endif
@@ -534,6 +538,22 @@
#define LEVEL_Y 142
#define LINES_Y 218
+#elif (LCD_WIDTH == 360) && (LCD_HEIGHT == 400)
+
+#define BLOCK_WIDTH 19
+#define BLOCK_HEIGHT 19
+#define BOARD_X 27
+#define BOARD_Y 0
+#define LABEL_X 258
+#define SCORE_Y 40
+#define LEVEL_Y 92
+#define LINES_Y 140
+#define HIGH_LABEL_X 258
+#define HIGH_SCORE_Y 200
+#define HIGH_LEVEL_Y 258
+#define PREVIEW_X 258
+#define PREVIEW_Y 300
+
#elif (LCD_WIDTH == 320) && (LCD_HEIGHT == 240)
#define BLOCK_WIDTH 12
@@ -1479,16 +1499,13 @@ static int rockblox_loop (void)
#ifdef HAS_BUTTON_HOLD
if (rb->button_hold ()) {
/* Turn on backlight timeout (revert to settings) */
-#ifdef HAVE_BACKLIGHT
backlight_use_settings();
-#endif
rb->splash(0, "Paused");
while (rb->button_hold ())
rb->sleep(HZ/10);
-#ifdef HAVE_BACKLIGHT
/* Turn off backlight timeout */
backlight_ignore_timeout();
-#endif
+
/* get rid of the splash text */
rb->lcd_bitmap (rockblox_background, 0, 0, LCD_WIDTH, LCD_HEIGHT);
show_details ();
@@ -1658,10 +1675,9 @@ enum plugin_status plugin_start (const void *parameter)
rb->lcd_setfont (FONT_SYSFIXED);
-#ifdef HAVE_BACKLIGHT
/* Turn off backlight timeout */
backlight_ignore_timeout();
-#endif
+
load_game();
resume_file = resume;
@@ -1709,9 +1725,8 @@ enum plugin_status plugin_start (const void *parameter)
/* Save user's HighScore */
highscore_save(SCORE_FILE, highscores, NUM_SCORES);
-#ifdef HAVE_BACKLIGNT
+
backlight_use_settings();
-#endif
return PLUGIN_OK;
}
diff --git a/apps/plugins/rockblox1d.c b/apps/plugins/rockblox1d.c
index 6a2b013c44..6d535bbcd7 100644
--- a/apps/plugins/rockblox1d.c
+++ b/apps/plugins/rockblox1d.c
@@ -28,7 +28,14 @@ static const struct button_mapping *plugin_contexts[] = { pla_main_ctx };
#define ONEDROCKBLOX_DOWN PLA_DOWN
#define ONEDROCKBLOX_DOWN_REPEAT PLA_DOWN_REPEAT
#define ONEDROCKBLOX_QUIT PLA_EXIT
+
+#if (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_3G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_4G_PAD)
+#define ONEDROCKBLOX_QUIT2 PLA_UP
+#else
#define ONEDROCKBLOX_QUIT2 PLA_CANCEL
+#endif
#define mrand(max) (short)(rb->rand()%max)
diff --git a/apps/plugins/rockboy/menu.c b/apps/plugins/rockboy/menu.c
index 6fafdc11a6..870ea389fb 100644
--- a/apps/plugins/rockboy/menu.c
+++ b/apps/plugins/rockboy/menu.c
@@ -83,9 +83,8 @@ int do_user_menu(void) {
rb->lcd_set_mode(LCD_MODE_RGB565);
#endif
-#ifdef HAVE_BACKLIGHT
backlight_use_settings();
-#endif
+
/* Clean out the button Queue */
while (rb->button_get(false) != BUTTON_NONE)
rb->yield();
@@ -139,10 +138,9 @@ int do_user_menu(void) {
rb->lcd_set_mode(LCD_MODE_PAL256);
#endif
-#ifdef HAVE_BACKLIGHT
/* ignore backlight time out */
backlight_ignore_timeout();
-#endif
+
return ret;
}
@@ -430,7 +428,7 @@ static void do_opt_menu(void)
options.dirty=1; /* Assume that the settings have been changed */
struct viewport *parentvp = NULL;
- const struct settings_list* vol = rb->find_setting(&rb->global_settings->volume, NULL);
+ const struct settings_list* vol = rb->find_setting(&rb->global_settings->volume);
while(!done)
{
@@ -439,39 +437,39 @@ static void do_opt_menu(void)
switch (result)
{
case 0: /* Frameskip */
- rb->set_option("Max Frameskip", &options.maxskip, INT, frameskip,
+ rb->set_option("Max Frameskip", &options.maxskip, RB_INT, frameskip,
sizeof(frameskip)/sizeof(*frameskip), NULL );
break;
case 1: /* Autosave */
- rb->set_option("Autosave", &options.autosave, INT, onoff, 2, NULL );
+ rb->set_option("Autosave", &options.autosave, RB_INT, onoff, 2, NULL );
break;
case 2: /* Sound */
if(options.sound>1) options.sound=1;
- rb->set_option("Sound", &options.sound, INT, onoff, 2, NULL );
+ rb->set_option("Sound", &options.sound, RB_INT, onoff, 2, NULL );
if(options.sound) sound_dirty();
break;
case 3: /* Volume */
rb->option_screen((struct settings_list*)vol, parentvp, false, "Volume");
break;
case 4: /* Stats */
- rb->set_option("Stats", &options.showstats, INT, stats, 3, NULL );
+ rb->set_option("Stats", &options.showstats, RB_INT, stats, 3, NULL );
break;
case 5: /* Keys */
setupkeys();
break;
#ifdef HAVE_LCD_COLOR
case 6: /* Screen Size */
- rb->set_option("Screen Size", &options.scaling, INT, scaling,
+ rb->set_option("Screen Size", &options.scaling, RB_INT, scaling,
sizeof(scaling)/sizeof(*scaling), NULL );
setvidmode();
break;
case 7: /* Screen rotate */
- rb->set_option("Screen Rotate", &options.rotate, INT, rotate,
+ rb->set_option("Screen Rotate", &options.rotate, RB_INT, rotate,
sizeof(rotate)/sizeof(*rotate), NULL );
setvidmode();
break;
case 8: /* Palette */
- rb->set_option("Set Palette", &options.pal, INT, palette, 17, NULL );
+ rb->set_option("Set Palette", &options.pal, RB_INT, palette, 17, NULL );
set_pal();
break;
#endif
diff --git a/apps/plugins/rockboy/rockboy.c b/apps/plugins/rockboy/rockboy.c
index d2c56ff6cc..2d0c349507 100644
--- a/apps/plugins/rockboy/rockboy.c
+++ b/apps/plugins/rockboy/rockboy.c
@@ -468,6 +468,9 @@ static void setoptions (void)
options.SELECT = BUTTON_VOL_UP;
options.MENU = BUTTON_POWER;
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+ /* use touchscreen */
+
#else
#error No Keymap Defined!
#endif
@@ -599,10 +602,9 @@ enum plugin_status plugin_start(const void* parameter)
rb->lcd_set_mode(LCD_MODE_PAL256);
#endif
-#ifdef HAVE_BACKLIGHT
/* ignore backlight time out */
backlight_ignore_timeout();
-#endif
+
gnuboy_main(parameter);
#ifdef HAVE_WHEEL_POSITION
@@ -613,9 +615,7 @@ enum plugin_status plugin_start(const void* parameter)
rb->lcd_set_mode(LCD_MODE_RGB565);
#endif
-#ifdef HAVE_BACKLIGHT
backlight_use_settings();
-#endif
if(!rb->audio_status())
rockboy_pcm_close();
diff --git a/apps/plugins/rocklife.c b/apps/plugins/rocklife.c
index 0be48dcce8..c4c7842fe0 100644
--- a/apps/plugins/rocklife.c
+++ b/apps/plugins/rocklife.c
@@ -70,7 +70,13 @@
#define ROCKLIFE_INIT PLA_DOWN
#define ROCKLIFE_NEXT PLA_RIGHT
#define ROCKLIFE_NEXT_REP PLA_RIGHT_REPEAT
+#if (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_3G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_4G_PAD)
+#define ROCKLIFE_QUIT PLA_UP
+#else
#define ROCKLIFE_QUIT PLA_CANCEL
+#endif
#define ROCKLIFE_STATUS PLA_LEFT
#define PATTERN_RANDOM 0
@@ -150,6 +156,7 @@ static bool load_cellfile(const char *file, char *pgrid){
switch(c) {
case '!':
comment = true;
+ break;
case '.':
if (!comment)
x++;
@@ -473,9 +480,8 @@ enum plugin_status plugin_start(const void* parameter)
char *ptemp;
(void)(parameter);
-#ifdef HAVE_BACKLIGHT
backlight_ignore_timeout();
-#endif
+
#if LCD_DEPTH > 1
rb->lcd_set_backdrop(NULL);
#ifdef HAVE_LCD_COLOR
@@ -579,8 +585,7 @@ enum plugin_status plugin_start(const void* parameter)
rb->yield();
}
-#ifdef HAVE_BACKLIGHT
backlight_use_settings();
-#endif
+
return usb? PLUGIN_USB_CONNECTED: PLUGIN_OK;
}
diff --git a/apps/plugins/rockpaint.c b/apps/plugins/rockpaint.c
index 0d3211d4d2..52281edfb0 100644
--- a/apps/plugins/rockpaint.c
+++ b/apps/plugins/rockpaint.c
@@ -404,6 +404,20 @@
#define ROCKPAINT_LEFT BUTTON_LEFT
#define ROCKPAINT_RIGHT BUTTON_RIGHT
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+/* use touchscreen */
+
+#elif CONFIG_KEYPAD == EROSQ_PAD
+#define ROCKPAINT_QUIT BUTTON_POWER
+#define ROCKPAINT_DRAW BUTTON_PLAY
+#define ROCKPAINT_MENU BUTTON_MENU
+#define ROCKPAINT_TOOLBAR BUTTON_VOL_UP
+#define ROCKPAINT_TOOLBAR2 BUTTON_VOL_DOWN
+#define ROCKPAINT_UP BUTTON_PREV
+#define ROCKPAINT_DOWN BUTTON_NEXT
+#define ROCKPAINT_LEFT BUTTON_SCROLL_BACK
+#define ROCKPAINT_RIGHT BUTTON_SCROLL_FWD
+
#else
#error "Please define keys for this keypad"
#endif
@@ -1070,15 +1084,15 @@ static bool callback_show_item(char *name, int attr, struct tree_context *tc)
static bool browse( char *dst, int dst_size, const char *start )
{
- struct browse_context browse;
-
- rb->browse_context_init(&browse, SHOW_ALL,
- BROWSE_SELECTONLY|BROWSE_NO_CONTEXT_MENU,
- NULL, NOICON, start, NULL);
-
- browse.callback_show_item = callback_show_item;
- browse.buf = dst;
- browse.bufsize = dst_size;
+ struct browse_context browse = {
+ .dirfilter = SHOW_ALL,
+ .flags = BROWSE_SELECTONLY | BROWSE_NO_CONTEXT_MENU,
+ .icon = Icon_NOICON,
+ .root = start,
+ .buf = dst,
+ .bufsize = dst_size,
+ .callback_show_item = callback_show_item,
+ };
rb->rockbox_browse(&browse);
@@ -1893,6 +1907,7 @@ static void draw_text( int x, int y )
rb->lcd_set_foreground( rp_colors[ drawcolor ] );
buffer_putsxyofs( save_buffer, COLS, ROWS, x, y, 0,
buffer->text.text );
+ /* fallthrough */
case TEXT_MENU_CANCEL:
default:
restore_screen();
@@ -2854,7 +2869,7 @@ static void goto_menu(void)
case MAIN_MENU_BRUSH_SIZE:
for(multi = 0; multi<4; multi++)
if(bsize == times_list[multi]) break;
- rb->set_option( "Brush Size", &multi, INT, times_options, 4, NULL );
+ rb->set_option( "Brush Size", &multi, RB_INT, times_options, 4, NULL );
if( multi >= 0 )
bsize = times_list[multi];
break;
@@ -2862,7 +2877,7 @@ static void goto_menu(void)
case MAIN_MENU_BRUSH_SPEED:
for(multi = 0; multi<3; multi++)
if(bspeed == times_list[multi]) break;
- rb->set_option( "Brush Speed", &multi, INT, times_options, 3, NULL );
+ rb->set_option( "Brush Speed", &multi, RB_INT, times_options, 3, NULL );
if( multi >= 0 ) {
bspeed = times_list[multi];
incdec_x.step[0] = bspeed;
@@ -2879,7 +2894,7 @@ static void goto_menu(void)
case MAIN_MENU_GRID_SIZE:
for(multi = 0; multi<4; multi++)
if(gridsize == gridsize_list[multi]) break;
- rb->set_option( "Grid Size", &multi, INT, gridsize_options, 4, NULL );
+ rb->set_option( "Grid Size", &multi, RB_INT, gridsize_options, 4, NULL );
if( multi >= 0 )
gridsize = gridsize_list[multi];
break;
diff --git a/apps/plugins/sdl/SDL_mixer/timidity/playmidi.c b/apps/plugins/sdl/SDL_mixer/timidity/playmidi.c
index 1638732dc5..38f7109b13 100644
--- a/apps/plugins/sdl/SDL_mixer/timidity/playmidi.c
+++ b/apps/plugins/sdl/SDL_mixer/timidity/playmidi.c
@@ -21,6 +21,8 @@
#include "tables.h"
+/* ROCKBOX HACK: avoid a conflict with adjust_volume() in misc.h */
+#define adjust_volume adjust_midi_volume
static int opt_expression_curve = 2;
static int opt_volume_curve = 2;
diff --git a/apps/plugins/sdl/main.c b/apps/plugins/sdl/main.c
index 6efb072faf..7220c7cfd9 100644
--- a/apps/plugins/sdl/main.c
+++ b/apps/plugins/sdl/main.c
@@ -64,9 +64,8 @@ void cleanup(void)
if(audiobuf)
memset(audiobuf, 0, 4); /* clear */
-#ifdef HAVE_BACKLIGHT
backlight_use_settings();
-#endif
+
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
rb->cpu_boost(false);
#endif
@@ -219,9 +218,9 @@ enum plugin_status plugin_start(const void *param)
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
rb->cpu_boost(true);
#endif
-#ifdef HAVE_BACKLIGHT
+
backlight_ignore_timeout();
-#endif
+
/* set the real exit handler */
#undef rb_atexit
rb_atexit(cleanup);
diff --git a/apps/plugins/sdl/src/audio/rockbox/SDL_rockboxaudio.c b/apps/plugins/sdl/src/audio/rockbox/SDL_rockboxaudio.c
index cb72687d48..ac268b4a19 100644
--- a/apps/plugins/sdl/src/audio/rockbox/SDL_rockboxaudio.c
+++ b/apps/plugins/sdl/src/audio/rockbox/SDL_rockboxaudio.c
@@ -132,38 +132,42 @@ static void ROCKBOXAUD_WaitAudio(_THIS)
}
/* when this is called, SDL wants us to play the samples in mixbuf */
-static void ROCKBOXAUD_PlayAudio(_THIS)
+static void
+ROCKBOXAUD_PlayAudio(_THIS)
{
/* There are two cases in which we should be called:
* - There is an empty buffer (marked with status = 0)
* - There are more than two buffers marked as playing, meaning at least one is stale.
*/
- int idx = -1;
/* Find the next empty or stale buffer and fill. */
- for(int i = 1; i < this->hidden->n_buffers; ++i)
+ for(int i = 1; i <= this->hidden->n_buffers; ++i)
{
- idx = (this->hidden->current_playing + i) % this->hidden->n_buffers;
+ int idx = (this->hidden->current_playing + i) % this->hidden->n_buffers;
/* Empty or stale. */
if(this->hidden->status[idx] == 0 ||
- this->hidden->status[idx] == 2)
- break;
- }
- if(idx < 0)
- return;
+ this->hidden->status[idx] == 2) {
+
+ LOGF("found empty buffer: %d (status: %d)", idx, this->hidden->status[idx]);
+
+ /* probably premature optimization here */
+ char *dst = (char*)this->hidden->rb_buf[idx], *src = this->hidden->mixbuf;
+ int size = this->spec.size / 2;
+ memcpy(dst, src, size);
- /* probably premature optimization here */
- char *dst = (char*)this->hidden->rb_buf[idx], *src = this->hidden->mixbuf;
- int size = this->hidden->mixlen / 2;
- memcpy(dst, src, size);
+ this->hidden->status[idx] = 1;
+ rb->yield();
- this->hidden->status[idx] = 1;
- rb->yield();
+ memcpy(dst + size, src + size, this->spec.size - size);
- memcpy(dst + size, src + size, this->hidden->mixlen - size);
+ LOGF("filled buffer %d (status %d %d %d %d)", idx, this->hidden->status[0], this->hidden->status[1], this->hidden->status[2], this->hidden->status[3]);
+
+ return;
+ }
+ }
- //LOGF("filled buffer %d (status %d %d %d)", idx, this->hidden->status[0], this->hidden->status[1], this->hidden->status[2]);
+ LOGF("WARNING: PlayDevice could not find buffer to fill; DROPPING SAMPLES!");
}
static SDL_AudioDevice *ROCKBOXAUD_CreateDevice(int devindex)
diff --git a/apps/plugins/sdl/src/thread/rockbox/SDL_systhread.c b/apps/plugins/sdl/src/thread/rockbox/SDL_systhread.c
index b9bd8735d3..d393e50e14 100644
--- a/apps/plugins/sdl/src/thread/rockbox/SDL_systhread.c
+++ b/apps/plugins/sdl/src/thread/rockbox/SDL_systhread.c
@@ -42,20 +42,25 @@ static void rbsdl_runthread(void)
SDL_RunThread(args);
}
+#define RBSDL_THREAD_STACK_SIZE (DEFAULT_STACK_SIZE * 4)
#define MAX_THREAD 4
static char names[MAX_THREAD][16];
-static long stacks[MAX_THREAD][DEFAULT_STACK_SIZE / sizeof(long)];
+static long stacks[MAX_THREAD][RBSDL_THREAD_STACK_SIZE / sizeof(long)];
int SDL_SYS_CreateThread(SDL_Thread *thread, void *args)
{
static int threadnum = 0;
+
+ if(threadnum >= MAX_THREAD)
+ return -1;
+
snprintf(names[threadnum], 16, "sdl_%d", threadnum);
while(global_args) rb->yield(); /* busy wait, pray that this works */
global_args = args;
- thread->handle = rb->create_thread(rbsdl_runthread, stacks[threadnum], DEFAULT_STACK_SIZE,
+ thread->handle = rb->create_thread(rbsdl_runthread, stacks[threadnum], RBSDL_THREAD_STACK_SIZE,
0, names[threadnum] /* collisions allowed? */
IF_PRIO(, PRIORITY_BUFFERING) // this is used for sound mixing
IF_COP(, CPU));
diff --git a/apps/plugins/shopper.c b/apps/plugins/shopper.c
index 7129291c10..25a484a31e 100644
--- a/apps/plugins/shopper.c
+++ b/apps/plugins/shopper.c
@@ -304,7 +304,6 @@ enum plugin_status plugin_start(const void* parameter)
/* now dump it in the list */
rb->gui_synclist_init(&lists,list_get_name_cb,0, false, 1, NULL);
rb->gui_synclist_set_icon_callback(&lists, list_get_icon_cb);
- rb->gui_synclist_limit_scroll(&lists,true);
create_view(&lists);
rb->gui_synclist_set_nb_items(&lists,view_item_count);
rb->gui_synclist_select_item(&lists, 0);
@@ -316,7 +315,7 @@ enum plugin_status plugin_start(const void* parameter)
rb->gui_synclist_draw(&lists);
cur_sel = rb->gui_synclist_get_sel_pos(&lists);
button = rb->get_action(CONTEXT_LIST,TIMEOUT_BLOCK);
- if (rb->gui_synclist_do_button(&lists,&button,LIST_WRAP_UNLESS_HELD))
+ if (rb->gui_synclist_do_button(&lists, &button))
continue;
switch (button)
{
diff --git a/apps/plugins/shortcuts/shortcuts_view.c b/apps/plugins/shortcuts/shortcuts_view.c
index f4c4b58bc1..e0146f3174 100644
--- a/apps/plugins/shortcuts/shortcuts_view.c
+++ b/apps/plugins/shortcuts/shortcuts_view.c
@@ -32,7 +32,7 @@ enum sc_list_action_type
SCLA_USB,
};
-
+static size_t root_len;
static char *link_filename;
static bool user_file;
@@ -59,13 +59,8 @@ enum sc_list_action_type draw_sc_list(struct gui_synclist *gui_sc)
/* user input */
button = rb->get_action(CONTEXT_LIST, HZ);
/* HZ so the status bar redraws corectly */
- if (rb->gui_synclist_do_button(gui_sc, &button,
- LIST_WRAP_UNLESS_HELD)) {
- /* automatic handling of user input.
- * _UNLESS_HELD can be _ON or _OFF also
- * selection changed, so redraw */
+ if (rb->gui_synclist_do_button(gui_sc, &button))
continue;
- }
switch (button) { /* process the user input */
case ACTION_STD_OK:
return SCLA_SELECT;
@@ -115,7 +110,6 @@ int list_sc(void)
rb->gui_synclist_set_title(&gui_sc,
(user_file?"Shortcuts (sealed)":"Shortcuts (editable)"), NOICON);
rb->gui_synclist_set_nb_items(&gui_sc, sc_file.entry_cnt);
- rb->gui_synclist_limit_scroll(&gui_sc, false);
rb->gui_synclist_select_item(&gui_sc, 0);
/* Draw the prepared widget to the LCD now */
@@ -181,6 +175,42 @@ bool goto_entry(char *file_or_dir)
}
#endif
+static bool callback_show_item(char *name, int attr, struct tree_context *tc)
+{
+ (void)name;
+ if(attr & ATTR_DIRECTORY)
+ {
+ if ((tc->browse->flags & BROWSE_SELECTED) == 0 &&
+ rb->strlen(tc->currdir) < root_len)
+ {
+ tc->is_browsing = false; /* exit immediately */
+ }
+ }
+
+ return true;
+}
+
+bool open_browse(char *path, char *buf, size_t bufsz)
+{
+ struct browse_context browse = {
+ .dirfilter = rb->global_settings->dirfilter,
+ .flags = BROWSE_DIRFILTER| BROWSE_SELECTONLY | BROWSE_NO_CONTEXT_MENU,
+ .title = path,
+ .icon = Icon_Plugin,
+ .root = path,
+ .buf = buf,
+ .bufsize = bufsz,
+ .callback_show_item = callback_show_item,
+ };
+ root_len = 0;
+ char *name = rb->strrchr(path, '/');
+ if (name)
+ root_len = name - path;
+ rb->rockbox_browse(&browse);
+
+ return (browse.flags & BROWSE_SELECTED);
+}
+
int goto_entry(char *file_or_dir)
{
DEBUGF("Trying to go to '%s'...\n", file_or_dir);
@@ -208,14 +238,19 @@ int goto_entry(char *file_or_dir)
}
else
{
- /* Set the browsers dirfilter to the global setting
- * This is required in case the plugin was launched
- * from the plugins browser, in which case the
- * dirfilter is set to only display .rock files */
- rb->set_dirfilter(rb->global_settings->dirfilter);
-
- /* Change directory to the entry selected by the user */
- rb->set_current_file(file_or_dir);
+ if (!is_dir)
+ {
+ rb->set_current_file(file_or_dir);
+ return LOOP_EXIT;
+ }
+ char tmp_buf[MAX_PATH];
+ if (open_browse(file_or_dir, tmp_buf, sizeof(tmp_buf)))
+ {
+ DEBUGF("Trying to load '%s'...\n", tmp_buf);
+ rb->set_dirfilter(rb->global_settings->dirfilter);
+ rb->set_current_file(tmp_buf);
+ return LOOP_EXIT;
+ }
}
return PLUGIN_OK;
}
diff --git a/apps/plugins/sliding_puzzle.c b/apps/plugins/sliding_puzzle.c
index d1820b2f50..33d2bb68f6 100644
--- a/apps/plugins/sliding_puzzle.c
+++ b/apps/plugins/sliding_puzzle.c
@@ -36,7 +36,7 @@
#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
(CONFIG_KEYPAD == IPOD_3G_PAD) || \
(CONFIG_KEYPAD == IPOD_1G2G_PAD)
-#define PUZZLE_QUIT (BUTTON_SELECT | BUTTON_MENU)
+#define PUZZLE_QUIT (BUTTON_SELECT | BUTTON_REPEAT)
#define PUZZLE_LEFT BUTTON_LEFT
#define PUZZLE_RIGHT BUTTON_RIGHT
#define PUZZLE_UP BUTTON_MENU
@@ -357,6 +357,9 @@ CONFIG_KEYPAD == MROBE500_PAD
#define PUZZLE_SHUFFLE BUTTON_BACK
#define PUZZLE_PICTURE BUTTON_PLAY
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+/* use touchscreen */
+
#else
#error No keymap defined!
#endif
@@ -465,8 +468,8 @@ static const char * initial_bmp_path=NULL;
static const char * get_albumart_bmp_path(void)
{
struct mp3entry* track = rb->audio_current_track();
-
- if (!track || !track->path || track->path[0] == '\0')
+ /* Note rb->audio_current_track->path should never be null */
+ if (!track || track->path[0] == '\0')
return NULL;
if (!rb->search_albumart_files(track, "", albumart_path, MAX_PATH ) )
@@ -845,7 +848,7 @@ enum plugin_status plugin_start(
#if (CONFIG_KEYPAD == IPOD_4G_PAD) || \
(CONFIG_KEYPAD == IPOD_3G_PAD) || \
(CONFIG_KEYPAD == IPOD_1G2G_PAD)
- rb->lcd_putsxy(0, 18, "[S-MENU] to stop");
+ rb->lcd_putsxy(0, 18, "Long [SELECT] to stop");
rb->lcd_putsxy(0, 28, "[S-LEFT] shuffle");
rb->lcd_putsxy(0, 38, "[S-RIGHT] change pic");
#elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
diff --git a/apps/plugins/snake.c b/apps/plugins/snake.c
index 459d345fa2..359077c9fa 100644
--- a/apps/plugins/snake.c
+++ b/apps/plugins/snake.c
@@ -55,12 +55,12 @@ dir is the current direction of the snake - 0=up, 1=right, 2=down, 3=left;
#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
(CONFIG_KEYPAD == IPOD_3G_PAD) || \
(CONFIG_KEYPAD == IPOD_1G2G_PAD)
-#define SNAKE_QUIT (BUTTON_SELECT|BUTTON_MENU)
+#define SNAKE_QUIT (BUTTON_SELECT|BUTTON_REPEAT)
#define SNAKE_LEFT BUTTON_LEFT
#define SNAKE_RIGHT BUTTON_RIGHT
#define SNAKE_UP BUTTON_MENU
#define SNAKE_DOWN BUTTON_PLAY
-#define SNAKE_PLAYPAUSE BUTTON_SELECT
+#define SNAKE_PLAYPAUSE (BUTTON_SELECT|BUTTON_REL)
#elif (CONFIG_KEYPAD == IAUDIO_X5M5_PAD)
#define SNAKE_QUIT BUTTON_POWER
@@ -313,6 +313,9 @@ dir is the current direction of the snake - 0=up, 1=right, 2=down, 3=left;
#define SNAKE_DOWN BUTTON_DOWN
#define SNAKE_PLAYPAUSE BUTTON_PLAY
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+/* use touchscreen */
+
#else
#error No keymap defined!
#endif
diff --git a/apps/plugins/snake2.c b/apps/plugins/snake2.c
index 1536840daf..c71fa7f247 100644
--- a/apps/plugins/snake2.c
+++ b/apps/plugins/snake2.c
@@ -62,6 +62,18 @@ Head and Tail are stored
#define TOP_X4 548 /* x-coord of the lowerright item (hi-score) */
#define TOP_Y1 8 /* y-coord of the top row of items */
#define TOP_Y2 50 /* y-coord of the bottom row of items */
+#elif (LCD_WIDTH >= 360) && (LCD_HEIGHT >= 400)
+ #define MULTIPLIER 12 /*Modifier for porting on other screens*/
+ #define MODIFIER_1 12
+ #define MODIFIER_2 10
+ #define CENTER_X 12
+ #define CENTER_Y 40
+ #define TOP_X1 34 /* x-coord of the upperleft item (game type) */
+ #define TOP_X2 320 /* x-coord of the upperright item (maze type) */
+ #define TOP_X3 42 /* x-coord of the lowerleft item (speed) */
+ #define TOP_X4 314 /* x-coord of the lowerright item (hi-score) */
+ #define TOP_Y1 4 /* y-coord of the top row of items */
+ #define TOP_Y2 25 /* y-coord of the bottom row of items */
#elif (LCD_WIDTH >= 320) && (LCD_HEIGHT >= 240)
#define MULTIPLIER 10 /*Modifier for porting on other screens*/
#define MODIFIER_1 10
@@ -169,8 +181,8 @@ Head and Tail are stored
#define SNAKE2_RIGHT BUTTON_RIGHT
#define SNAKE2_UP BUTTON_MENU
#define SNAKE2_DOWN BUTTON_PLAY
-#define SNAKE2_QUIT (BUTTON_SELECT | BUTTON_MENU)
-#define SNAKE2_PLAYPAUSE BUTTON_SELECT
+#define SNAKE2_QUIT (BUTTON_SELECT | BUTTON_REPEAT)
+#define SNAKE2_PLAYPAUSE (BUTTON_SELECT | BUTTON_REL)
#define SNAKE2_PLAYPAUSE_TEXT "Select"
#elif (CONFIG_KEYPAD == IAUDIO_X5M5_PAD)
@@ -446,6 +458,9 @@ CONFIG_KEYPAD == MROBE500_PAD
#define SNAKE2_PLAYPAUSE BUTTON_PLAY
#define SNAKE2_PLAYPAUSE_TEXT "PLAY"
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+/* use touchscreen */
+
#else
#error No keymap defined!
#endif
@@ -1584,7 +1599,7 @@ static void game_init(void)
speed = level*20;
return;
case 1:
- rb->set_option("Game Type", &game_type, INT,
+ rb->set_option("Game Type", &game_type, RB_INT,
type_options, 2, NULL);
break;
case 2:
diff --git a/apps/plugins/snow.c b/apps/plugins/snow.c
index 10b41c972b..c7d7ad31d8 100644
--- a/apps/plugins/snow.c
+++ b/apps/plugins/snow.c
@@ -30,8 +30,14 @@ static const struct button_mapping *plugin_contexts[] = { pla_main_ctx };
/* PLA definitions */
#define SNOW_QUIT PLA_EXIT
-#define SNOW_QUIT2 PLA_CANCEL
+#if (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_3G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_4G_PAD)
+#define SNOW_QUIT2 PLA_UP
+#else
+#define SNOW_QUIT2 PLA_CANCEL
+#endif
static short particles[NUM_PARTICLES][2];
#if LCD_WIDTH >= 160
diff --git a/apps/plugins/sokoban.c b/apps/plugins/sokoban.c
index 41c671a38c..bf61db7d88 100644
--- a/apps/plugins/sokoban.c
+++ b/apps/plugins/sokoban.c
@@ -125,7 +125,7 @@
#define SOKOBAN_RIGHT BUTTON_RIGHT
#define SOKOBAN_UP BUTTON_MENU
#define SOKOBAN_DOWN BUTTON_PLAY
-#define SOKOBAN_MENU (BUTTON_SELECT | BUTTON_MENU)
+#define SOKOBAN_MENU (BUTTON_SELECT | BUTTON_REPEAT)
#define SOKOBAN_UNDO_PRE BUTTON_SELECT
#define SOKOBAN_UNDO (BUTTON_SELECT | BUTTON_REL)
#define SOKOBAN_REDO (BUTTON_SELECT | BUTTON_PLAY)
@@ -696,6 +696,9 @@
#define BUTTON_SAVE BUTTON_BACK
#define BUTTON_SAVE_NAME "BACK"
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+/* use touchscreen */
+
#else
#error No keymap defined!
#endif
diff --git a/apps/plugins/solitaire.c b/apps/plugins/solitaire.c
index 2d737df678..fde3d04a0b 100644
--- a/apps/plugins/solitaire.c
+++ b/apps/plugins/solitaire.c
@@ -56,7 +56,7 @@
#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || (CONFIG_KEYPAD == IPOD_3G_PAD) || \
(CONFIG_KEYPAD == IPOD_1G2G_PAD)
-# define SOL_QUIT (BUTTON_SELECT | BUTTON_MENU)
+# define SOL_QUIT (BUTTON_SELECT | BUTTON_REPEAT)
# define SOL_UP BUTTON_SCROLL_BACK
# define SOL_DOWN BUTTON_SCROLL_FWD
# define SOL_LEFT_PRE BUTTON_LEFT
@@ -741,6 +741,9 @@ CONFIG_KEYPAD == MROBE500_PAD
# define HK_CUR2STACK "HOLD SELECT"
# define HK_REM2STACK "VOL+"
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+# define SOL_QUIT BUTTON_POWER
+
#else
#error No keymap defined!
#endif
@@ -1085,7 +1088,7 @@ static int solitaire_menu(bool in_game)
case 2:
if (rb->set_option("Draw Cards Option", &sol.draw_type,
- INT, drawcards, 2, NULL))
+ RB_INT, drawcards, 2, NULL))
result = MENU_USB;
break;
@@ -1675,7 +1678,7 @@ static int solitaire( int skipmenu )
{
switch( solitaire_menu(false) )
{
- case MENU_QUIT:
+ case MENU_SAVE_AND_QUIT:
return SOLITAIRE_QUIT;
case MENU_USB:
@@ -2146,6 +2149,7 @@ static int solitaire( int skipmenu )
break;
case SYS_POWEROFF:
+ case SYS_REBOOT:
return SOLITAIRE_SAVE_AND_QUIT;
default:
diff --git a/apps/plugins/spacerocks.c b/apps/plugins/spacerocks.c
index 2d39c26b3d..36729f8453 100644
--- a/apps/plugins/spacerocks.c
+++ b/apps/plugins/spacerocks.c
@@ -51,10 +51,10 @@
#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || (CONFIG_KEYPAD == IPOD_3G_PAD) || \
(CONFIG_KEYPAD == IPOD_1G2G_PAD)
-#define AST_PAUSE (BUTTON_SELECT | BUTTON_PLAY)
-#define AST_QUIT (BUTTON_SELECT | BUTTON_MENU)
-#define AST_THRUST BUTTON_MENU
-#define AST_HYPERSPACE BUTTON_PLAY
+#define AST_PAUSE BUTTON_PLAY
+#define AST_QUIT BUTTON_MENU
+#define AST_THRUST BUTTON_RIGHT
+#define AST_HYPERSPACE BUTTON_LEFT
#define AST_LEFT BUTTON_SCROLL_BACK
#define AST_RIGHT BUTTON_SCROLL_FWD
#define AST_FIRE BUTTON_SELECT
@@ -259,8 +259,8 @@
#define AST_QUIT BUTTON_POWER
#define AST_THRUST BUTTON_UP
#define AST_HYPERSPACE BUTTON_DOWN
-#define AST_LEFT BUTTON_LEFT
-#define AST_RIGHT BUTTON_RIGHT
+#define AST_LEFT BUTTON_SCROLL_BACK
+#define AST_RIGHT BUTTON_SCROLL_FWD
#define AST_FIRE BUTTON_SELECT
#elif (CONFIG_KEYPAD == SAMSUNG_YPR0_PAD)
@@ -372,6 +372,9 @@
#define AST_RIGHT BUTTON_RIGHT
#define AST_FIRE BUTTON_PLAY
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+/* use touchscreen */
+
#else
#error No keymap defined!
#endif
@@ -2125,10 +2128,10 @@ enum plugin_status plugin_start(const void* parameter)
#endif
/* universal font */
rb->lcd_setfont(FONT_SYSFIXED);
-#ifdef HAVE_BACKLIGHT
+
/* Turn off backlight timeout */
backlight_ignore_timeout();
-#endif
+
highscore_load(SCORE_FILE, highscores, NUM_SCORES);
rb->srand(*rb->current_tick);
@@ -2140,10 +2143,9 @@ enum plugin_status plugin_start(const void* parameter)
rb->lcd_setfont(FONT_UI);
highscore_save(SCORE_FILE, highscores, NUM_SCORES);
-#ifdef HAVE_BACKLIGHT
+
/* Turn on backlight timeout (revert to settings) */
backlight_use_settings();
-#endif
return ret;
}
diff --git a/apps/plugins/speedread.c b/apps/plugins/speedread.c
index 42634fb536..7a9ab61e7c 100644
--- a/apps/plugins/speedread.c
+++ b/apps/plugins/speedread.c
@@ -141,9 +141,9 @@ static void cleanup(void)
{
if(custom_font != FONT_UI)
rb->font_unload(custom_font);
-#ifdef HAVE_BACKLIGHT
+
backlight_use_settings();
-#endif
+
}
/* returns height of drawn area */
@@ -302,9 +302,8 @@ static void begin_anim(void)
static void init_drawing(void)
{
-#ifdef HAVE_BACKLIGHT
backlight_ignore_timeout();
-#endif
+
atexit(cleanup);
rb->lcd_set_background(OUTSIDE_COLOR);
@@ -483,16 +482,19 @@ static void load_font(void)
static void font_menu(void)
{
/* taken from text_viewer */
- struct browse_context browse;
char font[MAX_PATH], name[MAX_FILENAME+10];
-
rb->snprintf(name, sizeof(name), "%s.fnt", rb->global_settings->font_file);
- rb->browse_context_init(&browse, SHOW_FONT,
- BROWSE_SELECTONLY|BROWSE_NO_CONTEXT_MENU,
- "Font", Icon_Menu_setting, FONT_DIR, name);
- browse.buf = font;
- browse.bufsize = sizeof(font);
+ struct browse_context browse = {
+ .dirfilter = SHOW_FONT,
+ .flags = BROWSE_SELECTONLY | BROWSE_NO_CONTEXT_MENU,
+ .title = rb->str(LANG_CUSTOM_FONT),
+ .icon = Icon_Menu_setting,
+ .root = FONT_DIR,
+ .selected = name,
+ .buf = font,
+ .bufsize = sizeof(font),
+ };
rb->rockbox_browse(&browse);
diff --git a/apps/plugins/star.c b/apps/plugins/star.c
index c186474ae3..59cefa2c15 100644
--- a/apps/plugins/star.c
+++ b/apps/plugins/star.c
@@ -80,7 +80,7 @@
(CONFIG_KEYPAD == IPOD_3G_PAD) || \
(CONFIG_KEYPAD == IPOD_1G2G_PAD)
-#define STAR_QUIT (BUTTON_SELECT | BUTTON_MENU)
+#define STAR_QUIT (BUTTON_SELECT | BUTTON_REPEAT)
#define STAR_LEFT BUTTON_LEFT
#define STAR_RIGHT BUTTON_RIGHT
#define STAR_UP BUTTON_MENU
@@ -91,7 +91,7 @@
#define STAR_LEVEL_DOWN (BUTTON_SELECT | BUTTON_LEFT)
#define STAR_LEVEL_REPEAT (BUTTON_SELECT | BUTTON_PLAY)
#define STAR_TOGGLE_CONTROL_NAME "SELECT"
-#define STAR_QUIT_NAME "S + MENU"
+#define STAR_QUIT_NAME "Long SELECT"
#define STAR_LEVEL_UP_NAME "S >"
#define STAR_LEVEL_DOWN_NAME "S <"
#define STAR_LEVEL_REPEAT_NAME "S + PLAY"
@@ -668,6 +668,10 @@
#define STAR_LEVEL_DOWN_NAME "VOL-"
#define STAR_LEVEL_REPEAT_NAME "BACK"
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+#define STAR_QUIT BUTTON_POWER
+#define STAR_QUIT_NAME "POWER"
+
#else
#error No keymap defined!
#endif
diff --git a/apps/plugins/starfield.c b/apps/plugins/starfield.c
index 7fc400d0ee..239b7c1396 100644
--- a/apps/plugins/starfield.c
+++ b/apps/plugins/starfield.c
@@ -27,11 +27,21 @@ static const struct button_mapping *plugin_contexts[] = { pla_main_ctx };
/* Key assignement */
#define STARFIELD_QUIT PLA_EXIT
+#if (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_3G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_4G_PAD)
+#define STARFIELD_QUIT2 PLA_UP
+#define STARFIELD_INCREASE_ZMOVE PLA_SCROLL_FWD
+#define STARFIELD_INCREASE_ZMOVE_REPEAT PLA_SCROLL_FWD_REPEAT
+#define STARFIELD_DECREASE_ZMOVE PLA_SCROLL_BACK
+#define STARFIELD_DECREASE_ZMOVE_REPEAT PLA_SCROLL_BACK_REPEAT
+#else
#define STARFIELD_QUIT2 PLA_CANCEL
#define STARFIELD_INCREASE_ZMOVE PLA_UP
#define STARFIELD_INCREASE_ZMOVE_REPEAT PLA_UP_REPEAT
#define STARFIELD_DECREASE_ZMOVE PLA_DOWN
#define STARFIELD_DECREASE_ZMOVE_REPEAT PLA_DOWN_REPEAT
+#endif
#define STARFIELD_INCREASE_NB_STARS PLA_RIGHT
#define STARFIELD_INCREASE_NB_STARS_REPEAT PLA_RIGHT_REPEAT
#define STARFIELD_DECREASE_NB_STARS PLA_LEFT
@@ -324,14 +334,14 @@ enum plugin_status plugin_start(const void* parameter)
int ret;
(void)parameter;
-#ifdef HAVE_BACKLIGHT
+
/* Turn off backlight timeout */
backlight_ignore_timeout();
-#endif
+
ret = plugin_main();
-#ifdef HAVE_BACKLIGHT
+
/* Turn on backlight timeout (revert to settings) */
backlight_use_settings();
-#endif
+
return ret;
}
diff --git a/apps/plugins/stats.c b/apps/plugins/stats.c
index 19ccd9f452..b48259a0e4 100644
--- a/apps/plugins/stats.c
+++ b/apps/plugins/stats.c
@@ -29,7 +29,15 @@ static bool cancel;
/* we use PLA */
#define STATS_STOP PLA_EXIT
+
+#if (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_3G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_4G_PAD)
+#define STATS_STOP2 PLA_UP
+#else
#define STATS_STOP2 PLA_CANCEL
+#endif
+
/* this set the context to use with PLA */
static const struct button_mapping *plugin_contexts[] = { pla_main_ctx };
@@ -149,7 +157,7 @@ static void traversedir(char* location, char* name)
lasttick = *rb->current_tick;
button = pluginlib_getaction(TIMEOUT_NOBLOCK, plugin_contexts,
ARRAYLEN(plugin_contexts));
- if (button == STATS_STOP) {
+ if (button == STATS_STOP || button == STATS_STOP2) {
cancel = true;
break;
}
diff --git a/apps/plugins/stopwatch.lua b/apps/plugins/stopwatch.lua
index 578ba7f42c..34a3c57d72 100644
--- a/apps/plugins/stopwatch.lua
+++ b/apps/plugins/stopwatch.lua
@@ -280,7 +280,7 @@ function arrangeButtons(btns)
end
end
-rb.touchscreen_set_mode(rb.TOUCHSCREEN_POINT)
+rb.touchscreen_mode(rb.TOUCHSCREEN_POINT)
LapsView:init()
diff --git a/apps/plugins/sudoku/sudoku.c b/apps/plugins/sudoku/sudoku.c
index 34a1f6dd07..3ede4e8af6 100644
--- a/apps/plugins/sudoku/sudoku.c
+++ b/apps/plugins/sudoku/sudoku.c
@@ -867,7 +867,7 @@ static bool numdisplay_setting(void)
{"Coloured", -1},
};
- return rb->set_option("Number Display", &sudcfg.number_display, INT, names,
+ return rb->set_option("Number Display", &sudcfg.number_display, RB_INT, names,
sizeof(names) / sizeof(names[0]), NULL);
}
#endif
@@ -880,7 +880,7 @@ static bool showmarkings_setting(void)
{"Show", -1},
};
- return rb->set_option("Show Markings", &sudcfg.show_markings, INT, names,
+ return rb->set_option("Show Markings", &sudcfg.show_markings, RB_INT, names,
sizeof(names) / sizeof(names[0]), NULL);
}
#endif
diff --git a/apps/plugins/sudoku/sudoku.h b/apps/plugins/sudoku/sudoku.h
index 1332a9a80f..e06581fdc9 100644
--- a/apps/plugins/sudoku/sudoku.h
+++ b/apps/plugins/sudoku/sudoku.h
@@ -460,6 +460,9 @@
#define SUDOKU_BUTTON_MENU BUTTON_MENU
#define SUDOKU_BUTTON_POSSIBLE BUTTON_BACK
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+/* use touchscreen */
+
#else
#error No keymap defined!
#endif
diff --git a/apps/plugins/superdom.c b/apps/plugins/superdom.c
index 50027a30c6..79a6d1a8f2 100644
--- a/apps/plugins/superdom.c
+++ b/apps/plugins/superdom.c
@@ -583,7 +583,7 @@ static int settings_menu(void)
};
static int sel=1;
rb->set_option("Computer difficulty", &sel,
- INT, difficulty_options, 3, NULL);
+ RB_INT, difficulty_options, 3, NULL);
superdom_settings.compdiff=sel+1;
break;
}
@@ -1450,7 +1450,6 @@ static int show_inventory(void)
{
struct simplelist_info info;
rb->simplelist_info_init(&info, "Inventory", 9, NULL);
- info.hide_selection = true;
info.get_name = inventory_data;
if(rb->simplelist_show_list(&info))
{
diff --git a/apps/plugins/tagcache/SOURCES b/apps/plugins/tagcache/SOURCES
new file mode 100644
index 0000000000..2541f3e87c
--- /dev/null
+++ b/apps/plugins/tagcache/SOURCES
@@ -0,0 +1,2 @@
+tagcache.c
+
diff --git a/apps/plugins/tagcache/tagcache.c b/apps/plugins/tagcache/tagcache.c
new file mode 100644
index 0000000000..cce9efbed9
--- /dev/null
+++ b/apps/plugins/tagcache/tagcache.c
@@ -0,0 +1,1021 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2023 by William Wilgus
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+/*Plugin Includes*/
+
+#include "plugin.h"
+#include "errno.h"
+
+/* Redefinitons of ANSI C functions. */
+#include "lib/wrappers.h"
+#include "lib/helper.h"
+
+static void thread_create(void);
+static void thread(void); /* the thread running commit*/
+static void allocate_tempbuf(void);
+static void free_tempbuf(void);
+static bool do_timed_yield(void);
+static void _log(const char *fmt, ...);
+static bool logdump(bool append);
+/*Aliases*/
+#if 0
+#ifdef ROCKBOX_HAS_LOGF
+ #define logf rb->logf
+#else
+ #define logf(...) {}
+#endif
+#endif
+
+#define logf _log
+#define sleep rb->sleep
+#define qsort rb->qsort
+
+#define write(x,y,z) rb->write(x,y,z)
+#define ftruncate rb->ftruncate
+#define remove rb->remove
+#define rename rb->rename
+
+#define vsnprintf rb->vsnprintf
+#define mkdir rb->mkdir
+#define filesize rb->filesize
+
+#define strtok_r rb->strtok_r
+#define strncasecmp rb->strncasecmp
+#define strcasecmp rb->strcasecmp
+
+#define current_tick (*rb->current_tick)
+#define crc_32(x,y,z) rb->crc_32(x,y,z)
+#define plugin_get_buffer rb->plugin_get_buffer
+
+#define MAX_LOG_SIZE 16384
+
+#define EV_EXIT MAKE_SYS_EVENT(SYS_EVENT_CLS_PRIVATE, 0xFF)
+#define EV_STARTUP MAKE_SYS_EVENT(SYS_EVENT_CLS_PRIVATE, 0x01)
+
+#define BACKUP_DIRECTORY PLUGIN_APPS_DATA_DIR "/db_commit"
+
+#define RC_SUCCESS 0
+#define RC_USER_CANCEL 2
+
+enum fcpy_op_flags
+{
+ FCPY_MOVE = 0x00, /* Is a move operation (default) */
+ FCPY_COPY = 0x01, /* Is a copy operation */
+ FCPY_OVERWRITE = 0x02, /* Overwrite destination */
+ FCPY_EXDEV = 0x04, /* Actually copy/move across volumes */
+};
+
+/* communication to the worker thread */
+static struct
+{
+ int user_index;
+ bool exiting; /* signal to the thread that we want to exit */
+ bool resume;
+ unsigned int id; /* worker thread id */
+ struct event_queue queue; /* thread event queue */
+ long last_useraction_tick;
+} gThread;
+
+/* status of db and commit */
+static struct
+{
+ long last_check;
+ bool do_commit;
+ bool auto_commit;
+ bool commit_ready;
+ bool db_exists;
+ bool have_backup;
+ bool bu_exists;
+} gStatus;
+
+static unsigned char logbuffer[MAX_LOG_SIZE + 1];
+static int log_font_h = -1;
+static int logindex;
+static bool logwrap;
+static bool logenabled = true;
+
+/*Support Fns*/
+/* open but with a builtin printf for assembling the path */
+int open_pathfmt(char *buf, size_t size, int oflag, const char *pathfmt, ...)
+{
+ va_list ap;
+ va_start(ap, pathfmt);
+ vsnprintf(buf, size, pathfmt, ap);
+ va_end(ap);
+ if ((oflag & O_PATH) == O_PATH)
+ return -1;
+ int handle = open(buf, oflag, 0666);
+ //logf("Open: %s %d flag: %x", buf, handle, oflag);
+ return handle;
+}
+
+static void sleep_yield(void)
+{
+ sleep(1);
+ #undef yield
+ rb->yield();
+ #define yield sleep_yield
+}
+
+/* make sure tag can be displayed by font pf*/
+static bool text_is_displayable(struct font *pf, unsigned char *src)
+{
+ unsigned short code;
+ const unsigned char *ptr = src;
+ while(*ptr)
+ {
+ ptr = rb->utf8decode(ptr, &code);
+
+ if(!rb->font_get_bits(pf, code))
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+/* callback for each tag if false returned tag will not be added */
+bool user_check_tag(int index_type, char* build_idx_buf)
+{
+ static struct font *pf = NULL;
+ if (!pf)
+ pf = rb->font_get(FONT_UI);
+
+ if (index_type == tag_artist || index_type == tag_album ||
+ index_type == tag_genre || index_type == tag_title ||
+ index_type == tag_composer || index_type == tag_comment ||
+ index_type == tag_albumartist || index_type == tag_virt_canonicalartist)
+ {
+ /* this could be expanded with more rules / transformations */
+ if (rb->utf8length(build_idx_buf) != strlen(build_idx_buf))
+ {
+ if (!text_is_displayable(pf, build_idx_buf))
+ {
+ logf("Can't display (%d) %s", index_type, build_idx_buf);
+ }
+ }
+ }
+ return true;
+}
+
+/* undef features we don't need */
+#undef HAVE_DIRCACHE
+#undef HAVE_TC_RAMCACHE
+#undef HAVE_EEPROM_SETTINGS
+/* paste the whole tagcache.c file */
+#include "../tagcache.c"
+
+static void check_logindex(void)
+{
+ if(logindex >= MAX_LOG_SIZE)
+ {
+ logdump(true);
+ //logwrap = true;
+ logindex = 0;
+ }
+}
+
+static int log_push(void *userp, int c)
+{
+ (void)userp;
+
+ logbuffer[logindex++] = c;
+ check_logindex();
+
+ return 1;
+}
+
+/* our logf function */
+static void _log(const char *fmt, ...)
+{
+ if (!logenabled)
+ {
+ rb->splash(10, "log not enabled");
+ return;
+ }
+
+ #ifdef USB_ENABLE_SERIAL
+ int old_logindex = logindex;
+ #endif
+ va_list ap;
+
+ va_start(ap, fmt);
+
+#if (CONFIG_PLATFORM & PLATFORM_HOSTED)
+ char buf[1024];
+ vsnprintf(buf, sizeof buf, fmt, ap);
+ DEBUGF("%s\n", buf);
+ /* restart va_list otherwise the result if undefined when vuprintf is called */
+ va_end(ap);
+ va_start(ap, fmt);
+#endif
+
+ rb->vuprintf(log_push, NULL, fmt, ap);
+ va_end(ap);
+
+ /* add trailing zero */
+ log_push(NULL, '\0');
+}
+
+
+int compute_nb_lines(int w, struct font* font)
+{
+ int i, nb_lines;
+ int cur_x, delta_x;
+
+ if(logindex>= MAX_LOG_SIZE || (logindex == 0 && !logwrap))
+ return 0;
+
+ if(logwrap)
+ i = logindex;
+ else
+ i = 0;
+
+ cur_x = 0;
+ nb_lines = 0;
+
+ do {
+ if(logbuffer[i] == '\0')
+ {
+ cur_x = 0;
+ nb_lines++;
+ }
+ else
+ {
+ /* does character fit on this line ? */
+ delta_x = rb->font_get_width(font, logbuffer[i]);
+
+ if(cur_x + delta_x > w)
+ {
+ cur_x = 0;
+ nb_lines++;
+ }
+
+ /* update pointer */
+ cur_x += delta_x;
+ }
+
+ i++;
+ if(i >= MAX_LOG_SIZE)
+ i = 0;
+ } while(i != logindex);
+
+ return nb_lines;
+}
+
+static bool logdisplay(void)
+{
+
+ int w, h, i, index;
+ int fontnr;
+ int cur_x, cur_y, delta_x;
+ struct font* font;
+
+ char buf[2];
+
+ fontnr = FONT_FIRSTUSERFONT;
+ font = rb->font_get(fontnr);
+ buf[1] = '\0';
+ w = LCD_WIDTH;
+ h = LCD_HEIGHT;
+
+ if (log_font_h < 0) /* init, get the horizontal size of each line */
+ {
+ rb->font_getstringsize("A", NULL, &log_font_h, fontnr);
+ /* start at the end of the log */
+ gThread.user_index = compute_nb_lines(w, font) - h/log_font_h -1;
+ /* user_index will be number of the first line to display (warning: line!=log entry) */
+ /* if negative, will be set 0 to zero later */
+ }
+
+ rb->lcd_clear_display();
+
+ if(gThread.user_index < 0 || gThread.user_index >= MAX_LOG_SIZE)
+ gThread.user_index = 0;
+
+ if(logwrap)
+ i = logindex;
+ else
+ i = 0;
+
+ index = 0;
+ cur_x = 0;
+ cur_y = 0;
+
+ /* nothing to print ? */
+ if(logindex == 0 && !logwrap)
+ goto end_print;
+
+ do {
+ if(logbuffer[i] == '\0')
+ {
+ /* should be display a newline ? */
+ if(index >= gThread.user_index)
+ cur_y += log_font_h;
+ cur_x = 0;
+ index++;
+ }
+ else
+ {
+ /* does character fit on this line ? */
+ delta_x = rb->font_get_width(font, logbuffer[i]);
+
+ if(cur_x + delta_x > w)
+ {
+ /* should be display a newline ? */
+ if(index >= gThread.user_index)
+ cur_y += log_font_h;
+ cur_x = 0;
+ index++;
+ }
+
+ /* should we print character ? */
+ if(index >= gThread.user_index)
+ {
+ buf[0] = logbuffer[i];
+ rb->lcd_putsxy(cur_x, cur_y, buf);
+ }
+
+ /* update pointer */
+ cur_x += delta_x;
+ }
+ i++;
+ /* did we fill the screen ? */
+ if(cur_y > h - log_font_h)
+ {
+ if (TIME_AFTER(current_tick, gThread.last_useraction_tick + HZ))
+ gThread.user_index++;
+ break;
+ }
+
+ if(i >= MAX_LOG_SIZE)
+ i = 0;
+ } while(i != logindex);
+
+ end_print:
+ rb->lcd_update();
+
+ return false;
+}
+
+static bool logdump(bool append)
+{
+ int fd;
+ int flags = O_CREAT|O_WRONLY|O_TRUNC;
+ /* nothing to print ? */
+ if(!logenabled || (logindex == 0 && !logwrap))
+ {
+ /* nothing is logged just yet */
+ return false;
+ }
+ if (append)
+ {
+ flags = O_CREAT|O_WRONLY|O_APPEND;
+ }
+
+ fd = open(ROCKBOX_DIR "/db_commit_log.txt", flags, 0666);
+ logenabled = false;
+ if(-1 != fd) {
+ int i;
+
+ if(logwrap)
+ i = logindex;
+ else
+ i = 0;
+
+ do {
+ if(logbuffer[i]=='\0')
+ rb->fdprintf(fd, "\n");
+ else
+ rb->fdprintf(fd, "%c", logbuffer[i]);
+
+ i++;
+ if(i >= MAX_LOG_SIZE)
+ i = 0;
+ } while(i != logindex);
+
+ close(fd);
+ }
+
+ logenabled = true;
+
+ return false;
+}
+
+static void allocate_tempbuf(void)
+{
+ tempbuf_size = 0;
+ tempbuf = rb->plugin_get_audio_buffer(&tempbuf_size);
+ tempbuf_size &= ~0x03;
+
+}
+
+static void free_tempbuf(void)
+{
+ if (tempbuf_size == 0)
+ return ;
+
+ rb->plugin_release_audio_buffer();
+ tempbuf = NULL;
+ tempbuf_size = 0;
+}
+
+static bool do_timed_yield(void)
+{
+ /* Sorting can lock up for quite a while, so yield occasionally */
+ static long wakeup_tick = 0;
+ if (TIME_AFTER(current_tick, wakeup_tick))
+ {
+ yield();
+
+ wakeup_tick = current_tick + (HZ/5);
+ return true;
+ }
+ return false;
+}
+
+/* copy/move a file */
+static int fcpy(const char *src, const char *target,
+ unsigned int flags, bool (*poll_cancel)(const char *))
+{
+ int rc = -1;
+
+ while (!(flags & (FCPY_COPY | FCPY_EXDEV))) {
+ if ((flags & FCPY_OVERWRITE) || !file_exists(target)) {
+ /* Rename and possibly overwrite the file */
+ if (poll_cancel && poll_cancel(src)) {
+ rc = RC_USER_CANCEL;
+ } else {
+ rc = rename(src, target);
+ }
+
+ #ifdef HAVE_MULTIVOLUME
+ if (rc < 0 && errno == EXDEV) {
+ /* Failed because cross volume rename doesn't work; force
+ a move instead */
+ flags |= FCPY_EXDEV;
+ break;
+ }
+ #endif /* HAVE_MULTIVOLUME */
+ }
+
+ return rc;
+ }
+
+ /* See if we can get the plugin buffer for the file copy buffer */
+ size_t buffersize;
+ char *buffer = (char *) plugin_get_buffer(&buffersize);
+ if (buffer == NULL || buffersize < 512) {
+ /* Not large enough, try for a disk sector worth of stack
+ instead */
+ buffersize = 512;
+ buffer = (char *)alloca(buffersize);
+ }
+
+ if (buffer == NULL) {
+ return -1;
+ }
+
+ buffersize &= ~0x1ff; /* Round buffer size to multiple of sector
+ size */
+
+ int src_fd = open(src, O_RDONLY);
+ if (src_fd >= 0) {
+ int oflag = O_WRONLY|O_CREAT;
+
+ if (!(flags & FCPY_OVERWRITE)) {
+ oflag |= O_EXCL;
+ }
+
+ int target_fd = open(target, oflag, 0666);
+ if (target_fd >= 0) {
+ off_t total_size = 0;
+ off_t next_cancel_test = 0; /* No excessive button polling */
+
+ rc = RC_SUCCESS;
+
+ while (rc == RC_SUCCESS) {
+ if (total_size >= next_cancel_test) {
+ next_cancel_test = total_size + 0x10000;
+ if (poll_cancel && poll_cancel(src)) {
+ rc = RC_USER_CANCEL;
+ break;
+ }
+ }
+
+ ssize_t bytesread = read(src_fd, buffer, buffersize);
+ if (bytesread <= 0) {
+ if (bytesread < 0) {
+ rc = -1;
+ }
+ /* else eof on buffer boundary; nothing to write */
+ break;
+ }
+
+ ssize_t byteswritten = write(target_fd, buffer, bytesread);
+ if (byteswritten < bytesread) {
+ /* Some I/O error */
+ rc = -1;
+ break;
+ }
+
+ total_size += byteswritten;
+
+ if (bytesread < (ssize_t)buffersize) {
+ /* EOF with trailing bytes */
+ break;
+ }
+ }
+
+ if (rc == RC_SUCCESS) {
+ /* If overwriting, set the correct length if original was
+ longer */
+ rc = ftruncate(target_fd, total_size);
+ }
+
+ close(target_fd);
+
+ if (rc != RC_SUCCESS) {
+ /* Copy failed. Cleanup. */
+ remove(target);
+ }
+ }
+
+ close(src_fd);
+ }
+
+ if (rc == RC_SUCCESS && !(flags & FCPY_COPY)) {
+ /* Remove the source file */
+ rc = remove(src);
+ }
+
+ return rc;
+}
+
+static bool backup_restore_tagcache(bool backup)
+{
+ struct master_header tcmh;
+ char path[MAX_PATH];
+ char bu_path[MAX_PATH];
+ int fd;
+ int rc;
+
+ if (backup)
+ {
+ if (!rb->dir_exists(BACKUP_DIRECTORY))
+ mkdir(BACKUP_DIRECTORY);
+ snprintf(path, sizeof(path), "%s/"TAGCACHE_FILE_MASTER, tc_stat.db_path);
+ snprintf(bu_path, sizeof(bu_path), "%s/"TAGCACHE_FILE_MASTER, BACKUP_DIRECTORY);
+ }
+ else
+ {
+ if (!rb->dir_exists(BACKUP_DIRECTORY))
+ return false;
+ snprintf(path, sizeof(path), "%s/"TAGCACHE_FILE_MASTER, BACKUP_DIRECTORY);
+ snprintf(bu_path, sizeof(bu_path), "%s/"TAGCACHE_FILE_MASTER, tc_stat.db_path);
+ }
+
+ fd = open(path, O_RDONLY, 0666);
+
+ if (fd >= 0)
+ {
+ rc = read(fd, &tcmh, sizeof(struct master_header));
+ close(fd);
+ if (rc != sizeof(struct master_header))
+ {
+ logf("master file read failed");
+ return false;
+ }
+ int entries = tcmh.tch.entry_count;
+
+ logf("master file %d entries", entries);
+ if (backup)
+ logf("backup db to %s", BACKUP_DIRECTORY);
+ else
+ logf("restore db to %s", tc_stat.db_path);
+
+ if (entries > 0)
+ {
+ logf("%s", path);
+ fcpy(path, bu_path, FCPY_COPY|FCPY_OVERWRITE, NULL);
+
+ for (int i = 0; i < TAG_COUNT; i++)
+ {
+ if (TAGCACHE_IS_NUMERIC(i))
+ continue;
+ snprintf(path, sizeof(path),
+ "%s/"TAGCACHE_FILE_INDEX, tc_stat.db_path, i);
+
+ snprintf(bu_path, sizeof(bu_path),
+ "%s/"TAGCACHE_FILE_INDEX, BACKUP_DIRECTORY, i);
+ /* Note: above we swapped paths in the snprintf call here we swap variables */
+ if (backup)
+ {
+ logf("%s", path);
+ if (fcpy(path, bu_path, FCPY_COPY|FCPY_OVERWRITE, NULL) < 0)
+ goto failed;
+ gStatus.have_backup = true;
+ }
+ else
+ {
+ logf("%s", bu_path);
+ if (fcpy(bu_path, path, FCPY_COPY|FCPY_OVERWRITE, NULL) < 0)
+ goto failed;
+ }
+ }
+ }
+ return true;
+ }
+failed:
+ if (backup)
+ {
+ logf("failed backup");
+ }
+
+ return false;
+}
+
+/* asks the user if they wish to quit */
+static bool confirm_quit(void)
+{
+ const struct text_message prompt =
+ { (const char*[]) {"Are you sure?", "This will abort commit."}, 2};
+ enum yesno_res response = rb->gui_syncyesno_run(&prompt, NULL, NULL);
+ return (response == YESNO_YES);
+}
+
+/* asks the user if they wish to backup/restore */
+static bool prompt_backup_restore(bool backup)
+{
+ const struct text_message bu_prompt = { (const char*[]) {"Backup database?"}, 1};
+ const struct text_message re_prompt =
+ { (const char*[]) {"Error committing,", "Restore database?"}, 2};
+ enum yesno_res response =
+ rb->gui_syncyesno_run(backup ? &bu_prompt:&re_prompt, NULL, NULL);
+ if(response == YESNO_YES)
+ return backup_restore_tagcache(backup);
+ return true;
+}
+
+static const char* list_get_name_cb(int selected_item, void* data,
+ char* buf, size_t buf_len)
+{
+ (void) data;
+ (void) buf;
+ (void) buf_len;
+
+ /* buf supplied isn't used so lets use it for a filename buffer */
+ if (TIME_AFTER(current_tick, gStatus.last_check))
+ {
+ snprintf(buf, buf_len, "%s/"TAGCACHE_FILE_NOCOMMIT, tc_stat.db_path);
+ gStatus.auto_commit = !file_exists(buf);
+ snprintf(buf, buf_len, "%s/"TAGCACHE_FILE_TEMP, tc_stat.db_path);
+ gStatus.commit_ready = file_exists(buf);
+ snprintf(buf, buf_len, "%s/"TAGCACHE_FILE_MASTER, tc_stat.db_path);
+ gStatus.db_exists = file_exists(buf);
+ snprintf(buf, buf_len, "%s/"TAGCACHE_FILE_MASTER, BACKUP_DIRECTORY);
+ gStatus.bu_exists = file_exists(buf);
+ gStatus.last_check = current_tick + HZ;
+ buf[0] = '\0';
+ }
+
+ switch(selected_item)
+ {
+
+ case 0: /* exit */
+ return ID2P(LANG_MENU_QUIT);
+ case 1: /*sep*/
+ return ID2P(VOICE_BLANK);
+ case 2: /*backup*/
+ if (!gStatus.db_exists)
+ return ID2P(VOICE_BLANK);
+ return "Backup";
+ case 3: /*restore*/
+ if (!gStatus.bu_exists)
+ return ID2P(VOICE_BLANK);
+ return "Restore";
+ case 4: /*sep*/
+ return ID2P(VOICE_BLANK);
+ case 5: /*auto commit*/
+ if (gStatus.auto_commit)
+ return "Disable auto commit";
+ else
+ return "Enable auto commit";
+ case 6: /*destroy*/
+ if (gStatus.db_exists)
+ return "Delete database";
+ /*fall through*/
+ case 7: /*sep*/
+ return ID2P(VOICE_BLANK);
+ case 8: /*commit*/
+ if (gStatus.commit_ready)
+ return "Commit";
+ else
+ return "Nothing to commit";
+ default:
+ return "?";
+ }
+}
+
+static int list_voice_cb(int list_index, void* data)
+{
+ #define MAX_MENU_NAME 32
+ if (!rb->global_settings->talk_menu)
+ return -1;
+ else
+ {
+ char buf[MAX_MENU_NAME];
+ const char* name = list_get_name_cb(list_index, data, buf, sizeof(buf));
+ long id = P2ID((const unsigned char *)name);
+ if(id>=0)
+ rb->talk_id(id, true);
+ else
+ rb->talk_spell(name, true);
+ }
+ return 0;
+}
+
+static int commit_menu(void)
+{
+ struct gui_synclist lists;
+ bool exit = false;
+ int button,i;
+ int selection, ret = 0;
+
+ rb->gui_synclist_init(&lists,list_get_name_cb,0, false, 1, NULL);
+ rb->gui_synclist_set_icon_callback(&lists, NULL);
+ rb->gui_synclist_set_nb_items(&lists, 9);
+ rb->gui_synclist_select_item(&lists, 0);
+
+ while (!exit)
+ {
+ rb->gui_synclist_draw(&lists);
+ button = rb->get_action(CONTEXT_LIST,TIMEOUT_BLOCK);
+ if (rb->gui_synclist_do_button(&lists, &button))
+ continue;
+ selection = rb->gui_synclist_get_sel_pos(&lists);
+
+ if (button == ACTION_STD_CANCEL)
+ return 0;
+ else if (button == ACTION_STD_OK)
+ {
+ switch(selection)
+ {
+ case 0: /* exit */
+ exit = true;
+ break;
+ case 1: /*sep*/
+ continue;
+ case 2: /*backup*/
+ if (!gStatus.db_exists)
+ break;
+ if (!backup_restore_tagcache(true))
+ rb->splash(HZ, "Backup failed!");
+ else
+ {
+ rb->splash(HZ, "Backup success!");
+ gStatus.bu_exists = true;
+ }
+ break;
+ case 3: /*restore*/
+ if (!gStatus.bu_exists)
+ break;
+ if (!backup_restore_tagcache(false))
+ rb->splash(HZ, "Restore failed!");
+ else
+ rb->splash(HZ, "Restore success!");
+ break;
+ case 4: /*sep*/
+ continue;
+ case 5: /*auto commit*/
+ {
+ /* build_idx_buf supplied by tagcache.c isn't being used
+ * so lets use it for a filename buffer */
+ snprintf(build_idx_buf, build_idx_bufsz,
+ "%s/" TAGCACHE_FILE_NOCOMMIT, tc_stat.db_path);
+ if(gStatus.auto_commit)
+ close(open(build_idx_buf, O_WRONLY | O_CREAT | O_TRUNC, 0666));
+ else
+ remove(build_idx_buf);
+ gStatus.auto_commit = !file_exists(build_idx_buf);
+ break;
+ }
+ case 6: /*destroy*/
+ {
+ if (!gStatus.db_exists)
+ break;
+ const struct text_message prompt =
+ { (const char*[]) {"Are you sure?", "This will destroy database."}, 2};
+ if (rb->gui_syncyesno_run(&prompt, NULL, NULL) == YESNO_YES)
+ remove_files();
+ break;
+ }
+ case 7: /*sep*/
+ continue;
+ case 8: /*commit*/
+ if (gStatus.commit_ready)
+ {
+ gStatus.do_commit = true;
+ exit = true;
+ }
+ break;
+
+ case MENU_ATTACHED_USB:
+ return PLUGIN_USB_CONNECTED;
+ default:
+ return 0;
+ }
+ }
+ } /*while*/
+ return ret;
+}
+
+/*-----------------------------------------------------------------------------*/
+/******* plugin_start ******* */
+/*-----------------------------------------------------------------------------*/
+
+enum plugin_status plugin_start(const void* parameter)
+{
+ (void) parameter;
+
+ /* Turn off backlight timeout */
+ backlight_ignore_timeout();
+
+ memset(&gThread, 0, sizeof(gThread));
+ memset(&gStatus, 0, sizeof(gStatus));
+ memset(&tc_stat, 0, sizeof(struct tagcache_stat));
+ memset(&current_tcmh, 0, sizeof(struct master_header));
+ filenametag_fd = -1;
+
+ strlcpy(tc_stat.db_path, rb->global_settings->tagcache_db_path, sizeof(tc_stat.db_path));
+ if (!rb->dir_exists(tc_stat.db_path)) /* on fail use default DB path */
+ strlcpy(tc_stat.db_path, ROCKBOX_DIR, sizeof(tc_stat.db_path));
+ tc_stat.initialized = true;
+ tc_stat.commit_step = -1;
+
+ logf("started");
+
+ int result = commit_menu();
+
+ if (!gStatus.do_commit)
+ {
+ /* Turn on backlight timeout (revert to settings) */
+ backlight_use_settings();
+ return PLUGIN_OK;
+ }
+
+ logdump(false);
+ allocate_tempbuf();
+ if (gStatus.db_exists && !gStatus.have_backup && !prompt_backup_restore(true))
+ rb->splash(HZ, "Backup failed!");
+ thread_create();
+ gThread.user_index = 0;
+ logdisplay(); /* get something on the screen while user waits */
+
+ while (!gThread.exiting)
+ {
+ logdisplay();
+
+ int action = rb->get_action(CONTEXT_STD, HZ/20);
+
+ switch( action )
+ {
+ case ACTION_NONE:
+ break;
+ case ACTION_STD_NEXT:
+ case ACTION_STD_NEXTREPEAT:
+ gThread.last_useraction_tick = current_tick;
+ gThread.user_index++;
+ break;
+ case ACTION_STD_PREV:
+ case ACTION_STD_PREVREPEAT:
+ gThread.last_useraction_tick = current_tick;
+ gThread.user_index--;
+ break;
+ case ACTION_STD_OK:
+ gThread.last_useraction_tick = current_tick;
+ gThread.user_index = 0;
+ break;
+ case SYS_POWEROFF:
+ case ACTION_STD_CANCEL:
+ if (tc_stat.commit_step >= 0 && !tc_stat.ready)
+ {
+ if (!confirm_quit())
+ break;
+ tc_stat.commit_delayed = true; /* Cancel the commit */
+ }
+ rb->queue_remove_from_head(&gThread.queue, EV_EXIT);
+ rb->queue_post(&gThread.queue, EV_EXIT, 0);
+ break;
+#ifdef HAVE_TOUCHSCREEN
+ case ACTION_TOUCHSCREEN:
+ {
+ gThread.last_useraction_tick = current_tick;
+ short x, y;
+ static int prev_y;
+
+ action = rb->action_get_touchscreen_press(&x, &y);
+
+ if(action & BUTTON_REL)
+ prev_y = 0;
+ else
+ {
+ if(prev_y != 0)
+ gThread.user_index += (prev_y - y) / log_font_h;
+
+ prev_y = y;
+ }
+ }
+#endif
+ default:
+ break;
+ }
+ yield();
+ }
+
+ rb->thread_wait(gThread.id);
+ rb->queue_delete(&gThread.queue);
+ free_tempbuf();
+
+ if (tc_stat.commit_delayed || !tc_stat.ready)
+ {
+ remove_files();
+ if (gStatus.bu_exists && !prompt_backup_restore(false))
+ rb->splash(HZ, "Restore failed!");
+ }
+
+ if (tc_stat.ready)
+ rb->tagcache_commit_finalize();
+
+ /* Turn on backlight timeout (revert to settings) */
+ backlight_use_settings();
+ return PLUGIN_OK;
+}
+
+/****************** main thread + helper ******************/
+static void thread(void)
+{
+ struct queue_event ev;
+ while (!gThread.exiting)
+ {
+ rb->queue_wait_w_tmo(&gThread.queue, &ev, 1);
+ switch (ev.id)
+ {
+ case SYS_USB_CONNECTED:
+ rb->usb_acknowledge(SYS_USB_CONNECTED_ACK);
+ logenabled = false;
+ break;
+ case SYS_USB_DISCONNECTED:
+ logenabled = true;
+ /*fall through*/
+ case EV_STARTUP:
+ logf("Thread Started");
+ cpu_boost(true);
+ if (commit())
+ tc_stat.ready = true;
+ cpu_boost(false);
+ logdump(true);
+ gThread.user_index++;
+ logdisplay();
+ break;
+ case EV_EXIT:
+ gThread.exiting = true;
+ return;
+ default:
+ break;
+ }
+ yield();
+ }
+}
+
+static void thread_create(void)
+{
+ /* put the thread's queue in the bcast list */
+ rb->queue_init(&gThread.queue, true);
+ /*Note: tagcache_stack is defined in apps/tagcache.c */
+ gThread.last_useraction_tick = current_tick;
+ gThread.id = rb->create_thread(thread, tagcache_stack, sizeof(tagcache_stack),
+ 0, "db_commit"
+ IF_PRIO(, PRIORITY_USER_INTERFACE)
+ IF_COP(, CPU));
+ rb->queue_post(&gThread.queue, EV_STARTUP, 0);
+ yield();
+}
diff --git a/apps/plugins/tagcache/tagcache.h b/apps/plugins/tagcache/tagcache.h
new file mode 100644
index 0000000000..e416289741
--- /dev/null
+++ b/apps/plugins/tagcache/tagcache.h
@@ -0,0 +1,25 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#ifndef _TC_PLUGIN_
+#define _TC_PLUGIN_
+#endif /*_TC_PLUGIN_ */
+
+
diff --git a/apps/plugins/tagcache/tagcache.make b/apps/plugins/tagcache/tagcache.make
new file mode 100644
index 0000000000..5d6d65cb0e
--- /dev/null
+++ b/apps/plugins/tagcache/tagcache.make
@@ -0,0 +1,30 @@
+# __________ __ ___.
+# Open \______ \ ____ ____ | | _\_ |__ _______ ___
+# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+# \/ \/ \/ \/ \/
+# $Id$
+#
+
+TCPLUG_SRCDIR := $(APPSDIR)/plugins/tagcache
+TCPLUG_BUILDDIR := $(BUILDDIR)/apps/plugins/tagcache
+
+ROCKS += $(TCPLUG_BUILDDIR)/db_commit.rock
+
+TCPLUG_FLAGS = $(PLUGINFLAGS) -fno-strict-aliasing -Wno-unused \
+ -I$(TCPLUG_SRCDIR) -ffunction-sections \
+ -fdata-sections -Wl,--gc-sections
+TCPLUG_SRC := $(call preprocess, $(TCPLUG_SRCDIR)/SOURCES)
+TCPLUG_OBJ := $(call c2obj, $(TCPLUG_SRC))
+
+# add source files to OTHER_SRC to get automatic dependencies
+OTHER_SRC += $(APPSDIR)/tagcache.c $(TCPLUG_SRC)
+
+$(TCPLUG_BUILDDIR)/db_commit.rock: $(TCPLUG_OBJ)
+
+# special pattern rule for compiling with extra flags
+$(TCPLUG_BUILDDIR)/%.o: $(TCPLUG_SRCDIR)/%.c $(TCPLUG_SRCDIR)/tagcache.make
+ $(SILENT)mkdir -p $(dir $@)
+ $(call PRINTS,CC $(subst $(ROOTDIR)/,,$<))$(CC) -I$(dir $<) $(TCPLUG_FLAGS) -c $< -o $@
+
diff --git a/apps/plugins/test_codec.c b/apps/plugins/test_codec.c
index ac0dbf1633..91599bfc5d 100644
--- a/apps/plugins/test_codec.c
+++ b/apps/plugins/test_codec.c
@@ -915,7 +915,7 @@ menu:
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
if (result == BOOST)
{
- rb->set_option("Boosting", &boost, INT,
+ rb->set_option("Boosting", &boost, RB_INT,
boost_settings, 2, NULL);
goto menu;
}
diff --git a/apps/plugins/test_disk.c b/apps/plugins/test_disk.c
index b689ba672e..fee6c4d0b0 100644
--- a/apps/plugins/test_disk.c
+++ b/apps/plugins/test_disk.c
@@ -156,7 +156,7 @@ static bool test_fs(void)
fd = rb->open(TEST_FILE, O_RDONLY);
if (fd < 0)
{
- rb->splashf(0, "open() failed: %d", ret);
+ rb->splashf(0, "open() failed: %d", fd);
goto error;
}
@@ -467,10 +467,9 @@ enum plugin_status plugin_start(const void* parameter)
rb->srand(*rb->current_tick);
-#ifdef HAVE_BACKLIGHT
/* Turn off backlight timeout */
backlight_ignore_timeout();
-#endif
+
while(!quit)
{
@@ -489,9 +488,8 @@ enum plugin_status plugin_start(const void* parameter)
}
/* Turn on backlight timeout (revert to settings) */
-#ifdef HAVE_BACKLIGHT
backlight_use_settings();
-#endif
+
rb->rmdir(testbasedir);
return PLUGIN_OK;
diff --git a/apps/plugins/test_fps.c b/apps/plugins/test_fps.c
index ddf938ac25..2f4e9bb13e 100644
--- a/apps/plugins/test_fps.c
+++ b/apps/plugins/test_fps.c
@@ -401,9 +401,9 @@ enum plugin_status plugin_start(const void* parameter)
#if (CONFIG_PLATFORM & PLATFORM_NATIVE)
cpu_freq = *rb->cpu_frequency; /* remember CPU frequency */
#endif
-#ifdef HAVE_BACKLIGHT
+
backlight_ignore_timeout();
-#endif
+
time_main_update();
rb->sleep(HZ);
#if defined(HAVE_LCD_COLOR) && (MEMORYSIZE > 2)
@@ -424,9 +424,9 @@ enum plugin_status plugin_start(const void* parameter)
(cpu_freq + 500000) / 1000000);
log_text(str);
#endif
-#ifdef HAVE_BACKLIGHT
+
backlight_use_settings();
-#endif
+
/* wait until user closes plugin */
plugin_quit();
diff --git a/apps/plugins/test_gfx.c b/apps/plugins/test_gfx.c
index 7734179159..60e2836463 100644
--- a/apps/plugins/test_gfx.c
+++ b/apps/plugins/test_gfx.c
@@ -495,9 +495,9 @@ enum plugin_status plugin_start(const void* parameter)
rb->lcd_set_backdrop(NULL);
rb->lcd_clear_display();
#endif
-#ifdef HAVE_BACKLIGHT
+
backlight_ignore_timeout();
-#endif
+
rb->splashf(0, "LCD driver performance test, please wait %d sec",
7*4*DURATION/HZ);
init_rand_table();
@@ -522,9 +522,9 @@ enum plugin_status plugin_start(const void* parameter)
(cpu_freq + 500000) / 1000000);
#endif
rb->close(log_fd);
-#ifdef HAVE_BACKLIGHT
+
backlight_use_settings();
-#endif
+
#ifdef TEST_GREYLIB
grey_release();
#endif
diff --git a/apps/plugins/test_grey.c b/apps/plugins/test_grey.c
index 121cbad051..07fda1b9c6 100644
--- a/apps/plugins/test_grey.c
+++ b/apps/plugins/test_grey.c
@@ -123,9 +123,9 @@ enum plugin_status plugin_start(const void* parameter)
}
for (i = 0; i <= STEPS; i++)
input_levels[i] = lcd_levels[i] = (255 * i + (STEPS/2)) / STEPS;
-#ifdef HAVE_BACKLIGHT
+
backlight_ignore_timeout();
-#endif
+
grey_set_background(0); /* set background to black */
grey_clear_display();
grey_show(true);
@@ -221,8 +221,8 @@ enum plugin_status plugin_start(const void* parameter)
}
grey_release();
-#ifdef HAVE_BACKLIGHT
+
backlight_use_settings();
-#endif
+
return PLUGIN_OK;
}
diff --git a/apps/plugins/test_kbd.c b/apps/plugins/test_kbd.c
new file mode 100644
index 0000000000..a40aa4c76a
--- /dev/null
+++ b/apps/plugins/test_kbd.c
@@ -0,0 +1,46 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2002 Björn Stenberg
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+/* welcome to the example rockbox plugin */
+
+/* mandatory include for all plugins */
+#include "plugin.h"
+
+/* this is the plugin entry point */
+enum plugin_status plugin_start(const void* parameter)
+{
+ /* if you don't use the parameter, you can do like
+ this to avoid the compiler warning about it */
+ (void)parameter;
+
+ /* "rb->" marks a plugin api call. Rockbox offers many of its built-in
+ * functions to plugins */
+ /* now go ahead and have fun! */
+ char buffer[MAX_PATH];
+ rb->snprintf(buffer, sizeof(buffer), "Keyboard test; Current plugin filename: '%s'",
+ rb->plugin_get_current_filename());
+
+ if (rb->kbd_input(buffer, sizeof(buffer), NULL) == 0)
+ rb->splash(HZ*2, buffer);
+
+ /* tell Rockbox that we have completed successfully */
+ return PLUGIN_OK;
+}
diff --git a/apps/plugins/test_sampr.c b/apps/plugins/test_sampr.c
index c13392f069..3006faeea5 100644
--- a/apps/plugins/test_sampr.c
+++ b/apps/plugins/test_sampr.c
@@ -252,7 +252,7 @@ static void play_tone(bool volume_set)
else
#endif /* HAVE_VOLUME_IN_LIST */
{
- rb->set_option("Sample Rate", &freq, INT, names,
+ rb->set_option("Sample Rate", &freq, RB_INT, names,
HW_NUM_FREQ, set_frequency);
(void)volume_set;
}
diff --git a/apps/plugins/test_touchscreen.c b/apps/plugins/test_touchscreen.c
index 120ca8ac34..0d8e91f6a9 100644
--- a/apps/plugins/test_touchscreen.c
+++ b/apps/plugins/test_touchscreen.c
@@ -37,12 +37,17 @@
#elif CONFIG_KEYPAD == CREATIVE_ZENXFI2_PAD
#define TOUCHSCREEN_QUIT BUTTON_POWER
#define TOUCHSCREEN_TOGGLE BUTTON_MENU
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+#define TOUCHSCREEN_QUIT BUTTON_POWER
+#define TOUCHSCREEN_TOGGLE BUTTON_PLAY
#elif (CONFIG_KEYPAD == ANDROID_PAD)
#define TOUCHSCREEN_QUIT BUTTON_BACK
#define TOUCHSCREEN_TOGGLE BUTTON_MENU
#elif (CONFIG_KEYPAD == SDL_PAD)
#define TOUCHSCREEN_QUIT BUTTON_MIDLEFT
#define TOUCHSCREEN_TOGGLE BUTTON_CENTER
+#else
+# error "No keymap defined!"
#endif
/* plugin entry point */
diff --git a/apps/plugins/test_usb.c b/apps/plugins/test_usb.c
new file mode 100644
index 0000000000..28ef8f7e5f
--- /dev/null
+++ b/apps/plugins/test_usb.c
@@ -0,0 +1,137 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2022 Aidan MacDonald
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "plugin.h"
+#include "logf.h"
+
+#undef DEBUGF
+#define DEBUGF(...)
+//#define DEBUGF printf
+
+#define EV_EXIT MAKE_SYS_EVENT(SYS_EVENT_CLS_PRIVATE, 0xFF)
+
+unsigned char stack[DEFAULT_STACK_SIZE];
+struct event_queue queue;
+int thread_id;
+const char* state = "none";
+const char* prev_state = "none";
+
+static void main_loop(void)
+{
+ bool exiting = false;
+ struct queue_event ev;
+
+ while(true) {
+ rb->queue_wait(&queue, &ev);
+
+ /* events that are handled whether exiting or not */
+ switch(ev.id) {
+ case EV_EXIT:
+ return;
+ }
+
+ if(exiting)
+ continue;
+
+ /* events handled only when not exiting */
+ switch(ev.id) {
+ case SYS_USB_CONNECTED:
+ prev_state = state;
+ state = "connected";
+ logf("test_usb: connect ack %ld", *rb->current_tick);
+ DEBUGF("test_usb: connect ack %ld\n", *rb->current_tick);
+ rb->usb_acknowledge(SYS_USB_CONNECTED_ACK);
+ break;
+
+ case SYS_USB_DISCONNECTED:
+ prev_state = state;
+ state = "disconnected";
+ logf("test_usb: disconnect %ld", *rb->current_tick);
+ DEBUGF("test_usb: disconnect %ld\n", *rb->current_tick);
+ break;
+
+ case SYS_POWEROFF:
+ case SYS_REBOOT:
+ prev_state = state;
+ state = "exiting";
+ exiting = true;
+ break;
+ }
+ }
+}
+
+static void kill_tsr(void)
+{
+ rb->queue_post(&queue, EV_EXIT, 0);
+ rb->thread_wait(thread_id);
+ rb->queue_delete(&queue);
+}
+
+static int exit_tsr(bool reenter)
+{
+ MENUITEM_STRINGLIST(menu, "USB test menu", NULL,
+ "Status", "Stop plugin", "Back");
+
+ while(true) {
+ int result = reenter ? rb->do_menu(&menu, NULL, NULL, false) : 1;
+ switch(result) {
+ case 0:
+ rb->splashf(HZ, "State: %s", state);
+ rb->splashf(HZ, "Prev: %s", prev_state);
+ break;
+ case 1:
+ rb->splashf(HZ, "Stopping USB test thread");
+ kill_tsr();
+ return (reenter ? PLUGIN_TSR_TERMINATE : PLUGIN_TSR_SUSPEND);
+ case 2:
+ return PLUGIN_TSR_CONTINUE;
+ }
+ }
+}
+
+static void run_tsr(void)
+{
+ rb->queue_init(&queue, true);
+ thread_id = rb->create_thread(main_loop, stack, sizeof(stack),
+ 0, "test_usb TSR"
+ IF_PRIO(, PRIORITY_BACKGROUND)
+ IF_COP(, CPU));
+ rb->plugin_tsr(exit_tsr);
+}
+
+enum plugin_status plugin_start(const void* parameter)
+{
+ bool resume = (parameter == rb->plugin_tsr);
+
+ MENUITEM_STRINGLIST(menu, "USB test menu", NULL,
+ "Start", "Quit");
+
+ switch(!resume ? rb->do_menu(&menu, NULL, NULL, false) : 0) {
+ case 0:
+ run_tsr();
+ rb->splashf(HZ, "USB test thread started");
+ return PLUGIN_OK;
+ case 1:
+ return PLUGIN_OK;
+ default:
+ return PLUGIN_ERROR;
+ }
+}
diff --git a/apps/plugins/test_viewports.c b/apps/plugins/test_viewports.c
index 669766a207..6eff0249a2 100644
--- a/apps/plugins/test_viewports.c
+++ b/apps/plugins/test_viewports.c
@@ -130,7 +130,7 @@ static void *test_address_fn(int x, int y)
struct frame_buffer_t *fb = vp0.buffer;
/* LCD_STRIDEFORMAT & LCD_NATIVE_STRIDE macros allow Horiz screens to work with RB */
-#if defined(LCD_STRIDEFORMAT) && LCD_STRIDEFORMAT == VERTICAL_STRIDE
+#if LCD_STRIDEFORMAT == VERTICAL_STRIDE
size_t element = (x * LCD_NATIVE_STRIDE(fb->stride)) + y;
#else
size_t element = (y * LCD_NATIVE_STRIDE(fb->stride)) + x;
@@ -245,13 +245,15 @@ enum plugin_status plugin_start(const void* parameter)
#endif
rb->button_clear_queue();
while(rb->button_get(true) <= BUTTON_NONE)
- ;;
+ {;;}
rb->button_get(true);
+ rb->screens[SCREEN_MAIN]->scroll_stop();
/* Restore the default viewport */
rb->screens[SCREEN_MAIN]->set_viewport(NULL);
#ifdef HAVE_REMOTE_LCD
+ rb->screens[SCREEN_REMOTE]->scroll_stop();
rb->screens[SCREEN_REMOTE]->set_viewport(NULL);
#endif
diff --git a/apps/plugins/text_editor.c b/apps/plugins/text_editor.c
index 0cbb61c774..8740606c58 100644
--- a/apps/plugins/text_editor.c
+++ b/apps/plugins/text_editor.c
@@ -214,7 +214,6 @@ static void setup_lists(struct gui_synclist *lists, int sel)
rb->gui_synclist_init(lists,list_get_name_cb,0, false, 1, NULL);
rb->gui_synclist_set_icon_callback(lists,NULL);
rb->gui_synclist_set_nb_items(lists,line_count);
- rb->gui_synclist_limit_scroll(lists,true);
rb->gui_synclist_select_item(lists, sel);
rb->gui_synclist_draw(lists);
}
@@ -395,7 +394,7 @@ enum plugin_status plugin_start(const void* parameter)
rb->gui_synclist_draw(&lists);
cur_sel = rb->gui_synclist_get_sel_pos(&lists);
button = rb->get_action(CONTEXT_LIST,TIMEOUT_BLOCK);
- if (rb->gui_synclist_do_button(&lists,&button,LIST_WRAP_UNLESS_HELD))
+ if (rb->gui_synclist_do_button(&lists, &button))
continue;
switch (button)
{
diff --git a/apps/plugins/text_viewer/tv_button.h b/apps/plugins/text_viewer/tv_button.h
index d9a57d114f..4d45fbba03 100644
--- a/apps/plugins/text_viewer/tv_button.h
+++ b/apps/plugins/text_viewer/tv_button.h
@@ -572,6 +572,9 @@
#define TV_LINE_DOWN BUTTON_SCROLL_FWD
#define TV_BOOKMARK BUTTON_PLAY
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+#define TV_BOOKMARK BUTTON_PLAY
+
#else
#error No keymap defined!
#endif
diff --git a/apps/plugins/text_viewer/tv_menu.c b/apps/plugins/text_viewer/tv_menu.c
index 53e0adaf67..74a964ba98 100644
--- a/apps/plugins/text_viewer/tv_menu.c
+++ b/apps/plugins/text_viewer/tv_menu.c
@@ -46,15 +46,14 @@ static bool tv_horizontal_scroll_mode_setting(void)
{"Scroll by Column", -1},
};
- return rb->set_option("Scroll Mode", &new_prefs.horizontal_scroll_mode, INT,
+ return rb->set_option("Scroll Mode", &new_prefs.horizontal_scroll_mode, RB_INT,
names, 2, NULL);
}
MENUITEM_FUNCTION(horizontal_scrollbar_item, 0, "Scrollbar",
- tv_horizontal_scrollbar_setting,
- NULL, NULL, Icon_NOICON);
+ tv_horizontal_scrollbar_setting, NULL, Icon_NOICON);
MENUITEM_FUNCTION(horizontal_scroll_mode_item, 0, "Scroll Mode",
- tv_horizontal_scroll_mode_setting, NULL, NULL, Icon_NOICON);
+ tv_horizontal_scroll_mode_setting, NULL, Icon_NOICON);
MAKE_MENU(horizontal_scroll_menu, "Horizontal", NULL, Icon_NOICON,
&horizontal_scrollbar_item,
@@ -76,7 +75,7 @@ static bool tv_vertical_scroll_mode_setting(void)
{"Scroll by Line", -1},
};
- return rb->set_option("Scroll Mode", &new_prefs.vertical_scroll_mode, INT,
+ return rb->set_option("Scroll Mode", &new_prefs.vertical_scroll_mode, RB_INT,
names, 2, NULL);
}
@@ -98,21 +97,19 @@ static bool tv_narrow_mode_setting(void)
{"Top/Bottom Page", -1},
};
- return rb->set_option("Left/Right Key", &new_prefs.narrow_mode, INT,
+ return rb->set_option("Left/Right Key", &new_prefs.narrow_mode, RB_INT,
names, 2, NULL);
}
MENUITEM_FUNCTION(vertical_scrollbar_item, 0, "Scrollbar",
- tv_vertical_scrollbar_setting,
- NULL, NULL, Icon_NOICON);
+ tv_vertical_scrollbar_setting, NULL, Icon_NOICON);
MENUITEM_FUNCTION(vertical_scroll_mode_item, 0, "Scroll Mode",
- tv_vertical_scroll_mode_setting, NULL, NULL, Icon_NOICON);
-MENUITEM_FUNCTION(overlap_page_mode_item, 0, "Overlap Pages", tv_overlap_page_mode_setting,
- NULL, NULL, Icon_NOICON);
+ tv_vertical_scroll_mode_setting, NULL, Icon_NOICON);
+MENUITEM_FUNCTION(overlap_page_mode_item, 0, "Overlap Pages", tv_overlap_page_mode_setting, NULL, Icon_NOICON);
MENUITEM_FUNCTION(autoscroll_speed_item, 0, "Auto-Scroll Speed",
- tv_autoscroll_speed_setting, NULL, NULL, Icon_NOICON);
+ tv_autoscroll_speed_setting, NULL, Icon_NOICON);
MENUITEM_FUNCTION(narrow_mode_item, 0, "Left/Right Key (Narrow mode)",
- tv_narrow_mode_setting, NULL, NULL, Icon_NOICON);
+ tv_narrow_mode_setting, NULL, Icon_NOICON);
MAKE_MENU(vertical_scroll_menu, "Vertical", NULL, Icon_NOICON,
&vertical_scrollbar_item,
@@ -141,7 +138,7 @@ static bool tv_encoding_setting(void)
names[idx].voice_id = -1;
}
- return rb->set_option("Encoding", &new_prefs.encoding, INT, names,
+ return rb->set_option("Encoding", &new_prefs.encoding, RB_INT, names,
sizeof(names) / sizeof(names[0]), NULL);
}
@@ -152,7 +149,7 @@ static bool tv_word_wrap_setting(void)
{"Off (Chop Words)", -1},
};
- return rb->set_option("Word Wrap", &new_prefs.word_mode, INT,
+ return rb->set_option("Word Wrap", &new_prefs.word_mode, RB_INT,
names, 2, NULL);
}
@@ -165,7 +162,7 @@ static bool tv_line_mode_setting(void)
{"Reflow Lines", -1},
};
- return rb->set_option("Line Mode", &new_prefs.line_mode, INT, names,
+ return rb->set_option("Line Mode", &new_prefs.line_mode, RB_INT, names,
sizeof(names) / sizeof(names[0]), NULL);
}
@@ -182,7 +179,7 @@ static bool tv_alignment_setting(void)
{"Right", -1},
};
- return rb->set_option("Alignment", &new_prefs.alignment, INT,
+ return rb->set_option("Alignment", &new_prefs.alignment, RB_INT,
names , 2, NULL);
}
@@ -203,16 +200,19 @@ static bool tv_statusbar_setting(void)
static bool tv_font_setting(void)
{
- struct browse_context browse;
char font[MAX_PATH], name[MAX_FILENAME+10];
-
rb->snprintf(name, sizeof(name), "%s.fnt", new_prefs.font_name);
- rb->browse_context_init(&browse, SHOW_FONT,
- BROWSE_SELECTONLY|BROWSE_NO_CONTEXT_MENU,
- "Font", Icon_Menu_setting, FONT_DIR, name);
- browse.buf = font;
- browse.bufsize = sizeof(font);
+ struct browse_context browse = {
+ .dirfilter = SHOW_FONT,
+ .flags = BROWSE_SELECTONLY | BROWSE_NO_CONTEXT_MENU,
+ .title = "Font", /* XXX: Translate? */
+ .icon = Icon_Menu_setting,
+ .root = FONT_DIR,
+ .selected = name,
+ .buf = font,
+ .bufsize = sizeof(font),
+ };
rb->rockbox_browse(&browse);
@@ -240,29 +240,29 @@ static bool tv_night_mode_setting(void)
}
#endif
-MENUITEM_FUNCTION(encoding_item, 0, "Encoding", tv_encoding_setting,
- NULL, NULL, Icon_NOICON);
-MENUITEM_FUNCTION(word_wrap_item, 0, "Word Wrap", tv_word_wrap_setting,
- NULL, NULL, Icon_NOICON);
-MENUITEM_FUNCTION(line_mode_item, 0, "Line Mode", tv_line_mode_setting,
- NULL, NULL, Icon_NOICON);
-MENUITEM_FUNCTION(windows_item, 0, "Screens Per Page", tv_windows_setting,
- NULL, NULL, Icon_NOICON);
-MENUITEM_FUNCTION(alignment_item, 0, "Alignment", tv_alignment_setting,
- NULL, NULL, Icon_NOICON);
-MENUITEM_FUNCTION(header_item, 0, "Show Header", tv_header_setting,
- NULL, NULL, Icon_NOICON);
-MENUITEM_FUNCTION(footer_item, 0, "Show Footer", tv_footer_setting,
- NULL, NULL, Icon_NOICON);
-MENUITEM_FUNCTION(statusbar_item, 0, "Show Statusbar", tv_statusbar_setting,
- NULL, NULL, Icon_NOICON);
-MENUITEM_FUNCTION(font_item, 0, "Font", tv_font_setting,
- NULL, NULL, Icon_NOICON);
-MENUITEM_FUNCTION(indent_spaces_item, 0, "Indent Spaces", tv_indent_spaces_setting,
- NULL, NULL, Icon_NOICON);
+MENUITEM_FUNCTION(encoding_item, 0, "Encoding",
+ tv_encoding_setting, NULL, Icon_NOICON);
+MENUITEM_FUNCTION(word_wrap_item, 0, "Word Wrap",
+ tv_word_wrap_setting, NULL, Icon_NOICON);
+MENUITEM_FUNCTION(line_mode_item, 0, "Line Mode",
+ tv_line_mode_setting, NULL, Icon_NOICON);
+MENUITEM_FUNCTION(windows_item, 0, "Screens Per Page",
+ tv_windows_setting, NULL, Icon_NOICON);
+MENUITEM_FUNCTION(alignment_item, 0, "Alignment",
+ tv_alignment_setting, NULL, Icon_NOICON);
+MENUITEM_FUNCTION(header_item, 0, "Show Header",
+ tv_header_setting, NULL, Icon_NOICON);
+MENUITEM_FUNCTION(footer_item, 0, "Show Footer",
+ tv_footer_setting, NULL, Icon_NOICON);
+MENUITEM_FUNCTION(statusbar_item, 0, "Show Statusbar",
+ tv_statusbar_setting, NULL, Icon_NOICON);
+MENUITEM_FUNCTION(font_item, 0, "Font",
+ tv_font_setting, NULL, Icon_NOICON);
+MENUITEM_FUNCTION(indent_spaces_item, 0, "Indent Spaces",
+ tv_indent_spaces_setting, NULL, Icon_NOICON);
#ifdef HAVE_LCD_COLOR
-MENUITEM_FUNCTION(night_mode_item, 0, "Night Mode", tv_night_mode_setting,
- NULL, NULL, Icon_NOICON);
+MENUITEM_FUNCTION(night_mode_item, 0, "Night Mode",
+ tv_night_mode_setting, NULL, Icon_NOICON);
#endif
MAKE_MENU(option_menu, "Viewer Options", NULL, Icon_NOICON,
diff --git a/apps/plugins/theme_remove.c b/apps/plugins/theme_remove.c
index f39d224868..6f5353f3f6 100644
--- a/apps/plugins/theme_remove.c
+++ b/apps/plugins/theme_remove.c
@@ -589,7 +589,7 @@ static bool option_menu(void)
{
struct remove_setting *setting = &remove_list[result];
int prev_option = setting->option;
- if (rb->set_option(option_menu_[result], &setting->option, INT,
+ if (rb->set_option(option_menu_[result], &setting->option, RB_INT,
remove_names, NUM_REMOVE_OPTION, NULL))
return true;
if (prev_option != setting->option)
diff --git a/apps/plugins/vbrfix.c b/apps/plugins/vbrfix.c
index 768ec9d99f..79fa134499 100644
--- a/apps/plugins/vbrfix.c
+++ b/apps/plugins/vbrfix.c
@@ -37,7 +37,7 @@ static void xingupdate(int percent)
long now = *(rb->current_tick) / HZ;
if (now - last_talk >= 5)
{
- rb->talk_value(percent, UNIT_PERCENT, false);
+ rb->talk_value_decimal(percent, UNIT_PERCENT, 0, false);
last_talk = now;
}
}
@@ -150,7 +150,7 @@ static bool vbr_fix(const char *selected_file)
xingupdate(0);
rc = rb->mp3info(&entry, selected_file);
- if(rc < 0) {
+ if(rc) {
fileerror(rc);
return true;
}
@@ -258,6 +258,8 @@ static bool vbr_fix(const char *selected_file)
}
else
{
+ rb->close(fd);
+
/* Not a VBR file */
DEBUGF("Not a VBR file\n");
rb->splash(HZ*2, ID2P(LANG_NOT_A_VBR_FILE));
diff --git a/apps/plugins/viewers.config b/apps/plugins/viewers.config
index 2bf052bb20..45bbfeef0b 100644
--- a/apps/plugins/viewers.config
+++ b/apps/plugins/viewers.config
@@ -3,6 +3,10 @@ txt,viewers/text_viewer,1
txt,apps/text_editor,2
txt,viewers/sort,-
txt,viewers/speedread,1
+md,viewers/text_viewer,1
+md,apps/text_editor,2
+md,viewers/sort,-
+md,viewers/speedread,1
nfo,viewers/text_viewer,1
bmp,viewers/imageviewer,2
bmp,apps/rockpaint,11
@@ -103,3 +107,7 @@ z7,viewers/frotz,-
z8,viewers/frotz,-
shopper,viewers/shopper,1
lnk,viewers/windows_lnk,-
+#ifdef HAVE_TAGCACHE
+*,demos/pictureflow,-
+log,viewers/lastfm_scrobbler_viewer,4
+#endif
diff --git a/apps/plugins/vu_meter.c b/apps/plugins/vu_meter.c
index e24ad8dcdc..0dafd05845 100644
--- a/apps/plugins/vu_meter.c
+++ b/apps/plugins/vu_meter.c
@@ -20,6 +20,8 @@
#include "plugin.h"
#include "fixedpoint.h"
#include "lib/playback_control.h"
+#include "lib/helper.h"
+#include "lib/pluginlib_exit.h"
@@ -452,6 +454,9 @@
#define LABEL_MENU "MENU"
#define LABEL_VOLUME "VOL+/VOL-"
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+/* use touchscreen */
+
#else
#error No keymap defined!
#endif
@@ -663,7 +668,7 @@ static bool vu_meter_menu(void)
switch(rb->do_menu(&menu, &selection, NULL, false))
{
case 0:
- rb->set_option("Meter Type", &vumeter_settings.meter_type, INT,
+ rb->set_option("Meter Type", &vumeter_settings.meter_type, RB_INT,
meter_type_option, 2, NULL);
break;
@@ -696,12 +701,12 @@ static bool vu_meter_menu(void)
case 3:
if(vumeter_settings.meter_type==ANALOG)
{
- rb->set_option("Decay Speed", &vumeter_settings.analog_decay, INT,
+ rb->set_option("Decay Speed", &vumeter_settings.analog_decay, RB_INT,
decay_speed_option, 7, NULL);
}
else
{
- rb->set_option("Decay Speed", &vumeter_settings.digital_decay, INT,
+ rb->set_option("Decay Speed", &vumeter_settings.digital_decay, RB_INT,
decay_speed_option, 7, NULL);
}
break;
@@ -907,6 +912,12 @@ static void digital_meter(void) {
rb->lcd_hline(0,LCD_WIDTH-1,half_height+3);
}
+static void vu_meter_cleanup(void)
+{
+ /* Turn on backlight timeout (revert to settings) */
+ backlight_use_settings();
+}
+
enum plugin_status plugin_start(const void* parameter) {
int button;
#if defined(VUMETER_HELP_PRE) || defined(VUMETER_MENU_PRE)
@@ -917,12 +928,17 @@ enum plugin_status plugin_start(const void* parameter) {
calc_scales();
+ atexit(vu_meter_cleanup);
+
load_settings();
rb->lcd_setfont(FONT_SYSFIXED);
#ifdef HAVE_LCD_COLOR
screen_foreground = rb->lcd_get_foreground();
#endif
+ /* Turn off backlight timeout */
+ backlight_ignore_timeout();
+
while (1)
{
rb->lcd_clear_display();
diff --git a/apps/plugins/wormlet.c b/apps/plugins/wormlet.c
index d76f6a7d5a..7dd814bc08 100644
--- a/apps/plugins/wormlet.c
+++ b/apps/plugins/wormlet.c
@@ -54,7 +54,7 @@ static long max_cycle;
#define BTN_DIR_LEFT BUTTON_LEFT
#define BTN_DIR_RIGHT BUTTON_RIGHT
#define BTN_STARTPAUSE (BUTTON_SELECT|BUTTON_REL)
-#define BTN_QUIT (BUTTON_SELECT|BUTTON_MENU)
+#define BTN_QUIT (BUTTON_SELECT|BUTTON_REPEAT)
#define BTN_STOPRESET (BUTTON_SELECT|BUTTON_PLAY)
#elif (CONFIG_KEYPAD == IRIVER_H300_PAD) || (CONFIG_KEYPAD == IRIVER_H100_PAD)
@@ -407,6 +407,9 @@ CONFIG_KEYPAD == MROBE500_PAD
#define BTN_QUIT BUTTON_POWER
#define BTN_STOPRESET BUTTON_BACK
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+/* use touchscreen */
+
#else
#error No keymap defined!
#endif
@@ -492,7 +495,8 @@ CONFIG_KEYPAD == MROBE500_PAD
#define SPEED 4
#define MAX_WORM_SEGMENTS 512
#elif ((LCD_WIDTH == 320) && (LCD_HEIGHT == 240)) || \
- ((LCD_WIDTH == 240) && ((LCD_HEIGHT == 320) || (LCD_HEIGHT == 400)))
+ ((LCD_WIDTH == 240) && ((LCD_HEIGHT == 320) || (LCD_HEIGHT == 400))) || \
+ ((LCD_WIDTH == 360) && (LCD_HEIGHT == 400))
#define FOOD_SIZE 7
#define ARGH_SIZE 8
#define SPEED 4
@@ -2424,10 +2428,9 @@ static bool launch_wormlet(void)
rb->lcd_clear_display();
-#ifdef HAVE_BACKLIGHT
/* Turn off backlight timeout */
backlight_ignore_timeout();
-#endif
+
/* start the game */
while (game_result == 1)
game_result = run();
@@ -2435,10 +2438,10 @@ static bool launch_wormlet(void)
switch (game_result)
{
case 2:
-#ifdef HAVE_BACKLIGHT
+
/* Turn on backlight timeout (revert to settings) */
backlight_use_settings();
-#endif
+
return false;
break;
}
@@ -2559,24 +2562,24 @@ enum plugin_status plugin_start(const void* parameter)
case 3:
switch(players) {
case 0:
- rb->set_option(rb->str(LANG_CONTROL_STYLE),&use_remote,INT,
+ rb->set_option(rb->str(LANG_CONTROL_STYLE),&use_remote, RB_INT,
nokey_option, 1, NULL);
break;
case 1:
- rb->set_option(rb->str(LANG_CONTROL_STYLE),&use_remote,INT,
+ rb->set_option(rb->str(LANG_CONTROL_STYLE),&use_remote, RB_INT,
key24_option, 2, NULL);
break;
case 2:
#ifdef REMOTE
- rb->set_option(rb->str(LANG_CONTROL_STYLE),&use_remote,INT,
+ rb->set_option(rb->str(LANG_CONTROL_STYLE),&use_remote, RB_INT,
remote_option, 2, NULL);
#else
- rb->set_option(rb->str(LANG_CONTROL_STYLE),&use_remote,INT,
+ rb->set_option(rb->str(LANG_CONTROL_STYLE),&use_remote, RB_INT,
key2_option, 1, NULL);
#endif
break;
case 3:
- rb->set_option(rb->str(LANG_CONTROL_STYLE),&use_remote,INT,
+ rb->set_option(rb->str(LANG_CONTROL_STYLE),&use_remote, RB_INT,
remoteonly_option, 1, NULL);
break;
}
@@ -2605,7 +2608,7 @@ enum plugin_status plugin_start(const void* parameter)
break;
case 9:
new_setting = 0;
- rb->set_option(rb->str(LANG_RESET), &new_setting, INT, noyes , 2, NULL);
+ rb->set_option(rb->str(LANG_RESET), &new_setting, RB_INT, noyes , 2, NULL);
if (new_setting == 1)
default_settings();
break;
diff --git a/apps/plugins/xobox.c b/apps/plugins/xobox.c
index cf959ad7f5..336d92bd4e 100644
--- a/apps/plugins/xobox.c
+++ b/apps/plugins/xobox.c
@@ -42,10 +42,10 @@
(CONFIG_KEYPAD == IPOD_3G_PAD) || \
(CONFIG_KEYPAD == IPOD_1G2G_PAD)
-#define QUIT (BUTTON_SELECT | BUTTON_MENU)
+#define QUIT (BUTTON_SELECT | BUTTON_REPEAT)
#define LEFT BUTTON_LEFT
#define RIGHT BUTTON_RIGHT
-#define PAUSE BUTTON_SELECT
+#define PAUSE (BUTTON_SELECT | BUTTON_REL)
#define MENU_UP BUTTON_SCROLL_FWD
#define MENU_DOWN BUTTON_SCROLL_BACK
#define UP BUTTON_MENU
@@ -351,6 +351,9 @@ CONFIG_KEYPAD == MROBE500_PAD
#define DOWN BUTTON_DOWN
#define PAUSE BUTTON_PLAY
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+/* use touchscreen */
+
#else
#error "No keymap defined!"
#endif
@@ -1294,10 +1297,9 @@ enum plugin_status plugin_start (const void *parameter)
rb->lcd_set_backdrop(NULL);
#endif
-#ifdef HAVE_BACKLIGHT
/* Turn off backlight timeout */
backlight_ignore_timeout();
-#endif
+
highscore_load(SCORE_FILE, highscores, NUM_SCORES);
if (!load_game()) {
@@ -1307,10 +1309,10 @@ enum plugin_status plugin_start (const void *parameter)
randomize ();
ret = xobox_loop ();
-#ifdef HAVE_BACKLIGHT
+
/* Turn on backlight timeout (revert to settings) */
backlight_use_settings();
-#endif
+
rb->lcd_setfont (FONT_UI);
highscore_save(SCORE_FILE, highscores, NUM_SCORES);
diff --git a/apps/plugins/xworld/engine.c b/apps/plugins/xworld/engine.c
index b09e320078..d99d9df6c6 100644
--- a/apps/plugins/xworld/engine.c
+++ b/apps/plugins/xworld/engine.c
@@ -293,7 +293,7 @@ void engine_processInput(struct Engine* e) {
e->sys->input.save = false;
}
if (e->sys->input.fastMode) {
- e->vm._fastMode = !&e->vm._fastMode;
+ e->vm._fastMode = !e->vm._fastMode;
e->sys->input.fastMode = false;
}
if (e->sys->input.stateSlot != 0) {
diff --git a/apps/plugins/xworld/sys.c b/apps/plugins/xworld/sys.c
index c57da9456b..4a80152b70 100644
--- a/apps/plugins/xworld/sys.c
+++ b/apps/plugins/xworld/sys.c
@@ -122,9 +122,8 @@ void exit_handler(void)
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
rb->cpu_boost(false);
#endif
-#ifdef HAVE_BACKLIGHT
+
backlight_use_settings();
-#endif
}
static bool sys_do_help(void)
@@ -207,7 +206,7 @@ static void do_video_settings(struct System* sys)
#else
case 2:
#endif
- rb->set_option("Scaling", &sys->settings.scaling_quality, INT, scaling_settings,
+ rb->set_option("Scaling", &sys->settings.scaling_quality, RB_INT, scaling_settings,
#ifdef HAVE_LCD_COLOR
3
#else
@@ -226,7 +225,7 @@ static void do_video_settings(struct System* sys)
#else
case 3:
#endif
- rb->set_option("Rotation", &sys->settings.rotation_option, INT, rotation_settings, 3, NULL);
+ rb->set_option("Rotation", &sys->settings.rotation_option, RB_INT, rotation_settings, 3, NULL);
if(sys->settings.rotation_option &&
sys->settings.zoom)
{
@@ -287,7 +286,7 @@ static void do_sound_settings(struct System* sys)
case 2:
{
const struct settings_list* vol =
- rb->find_setting(&rb->global_settings->volume, NULL);
+ rb->find_setting(&rb->global_settings->volume);
rb->option_screen((struct settings_list*)vol, NULL, false, "Volume");
break;
}
@@ -428,9 +427,8 @@ void sys_menu(struct System* sys)
void sys_init(struct System* sys, const char* title)
{
(void) title;
-#ifdef HAVE_BACKLIGHT
+
backlight_ignore_timeout();
-#endif
rb_atexit(exit_handler);
save_sys = sys;
rb->memset(&sys->input, 0, sizeof(sys->input));
@@ -496,7 +494,7 @@ void sys_copyRect(struct System* sys, uint16_t x, uint16_t y, uint16_t w, uint16
for (int i = 0; i < w / 2; ++i) {
uint8_t pix1 = *(buf + i) >> 4;
uint8_t pix2 = *(buf + i) & 0xF;
-#if defined(LCD_STRIDEFORMAT) && (LCD_STRIDEFORMAT == VERTICAL_STRIDE)
+#if LCD_STRIDEFORMAT == VERTICAL_STRIDE
framebuffer[( (h * 2) ) * 320 + i] = sys->palette[pix1];
framebuffer[( (h * 2) + 1) * 320 + i] = sys->palette[pix2];
#else
@@ -515,7 +513,7 @@ void sys_copyRect(struct System* sys, uint16_t x, uint16_t y, uint16_t w, uint16
for (int i = 0; i < w / 2; ++i) {
uint8_t pix1 = *(buf + i) >> 4;
uint8_t pix2 = *(buf + i) & 0xF;
-#if defined(LCD_STRIDEFORMAT) && (LCD_STRIDEFORMAT == VERTICAL_STRIDE)
+#if LCD_STRIDEFORMAT == VERTICAL_STRIDE
framebuffer[(200 - h * 2 ) * 320 + ( 320 - i )] = sys->palette[pix1];
framebuffer[(200 - h * 2 - 1) * 320 + ( 320 - i )] = sys->palette[pix2];
#else
@@ -531,7 +529,7 @@ void sys_copyRect(struct System* sys, uint16_t x, uint16_t y, uint16_t w, uint16
else
{
int next = 0;
-#if defined(LCD_STRIDEFORMAT) && (LCD_STRIDEFORMAT == VERTICAL_STRIDE)
+#if LCD_STRIDEFORMAT == VERTICAL_STRIDE
for(int x = 0; x < w / 2; ++x)
{
for(int y = 0; y < h; ++y)
@@ -565,7 +563,7 @@ void sys_copyRect(struct System* sys, uint16_t x, uint16_t y, uint16_t w, uint16
struct bitmap in_bmp;
if(sys->settings.rotation_option)
{
-#if defined(LCD_STRIDEFORMAT) && (LCD_STRIDEFORMAT == VERTICAL_STRIDE)
+#if LCD_STRIDEFORMAT == VERTICAL_STRIDE
in_bmp.width = 320;
in_bmp.height = 200;
#else
@@ -575,7 +573,7 @@ void sys_copyRect(struct System* sys, uint16_t x, uint16_t y, uint16_t w, uint16
}
else
{
-#if defined(LCD_STRIDEFORMAT) && (LCD_STRIDEFORMAT == VERTICAL_STRIDE)
+#if LCD_STRIDEFORMAT == VERTICAL_STRIDE
in_bmp.width = 200;
in_bmp.height = 320;
#else
@@ -600,7 +598,7 @@ void sys_copyRect(struct System* sys, uint16_t x, uint16_t y, uint16_t w, uint16
}
else
{
-#if defined(LCD_STRIDEFORMAT) && (LCD_STRIDEFORMAT == VERTICAL_STRIDE)
+#if LCD_STRIDEFORMAT == VERTICAL_STRIDE
for(int x = 0; x < 320; ++x)
{
for(int y = 0; y < 200; ++y)
diff --git a/apps/plugins/zxbox/keymaps.h b/apps/plugins/zxbox/keymaps.h
index e9316a301b..e95a1d8c94 100644
--- a/apps/plugins/zxbox/keymaps.h
+++ b/apps/plugins/zxbox/keymaps.h
@@ -290,6 +290,9 @@
#define ZX_UP BUTTON_UP
#define ZX_DOWN BUTTON_DOWN
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+/* use touchscreen */
+
#else
#error Keymap not defined!
diff --git a/apps/plugins/zxbox/spmain.c b/apps/plugins/zxbox/spmain.c
index 6c6b59c3fb..38cc175a85 100644
--- a/apps/plugins/zxbox/spmain.c
+++ b/apps/plugins/zxbox/spmain.c
@@ -257,21 +257,21 @@ static void options_menu(void){
{
case 0:
new_setting=settings.kempston;
- rb->set_option("Map Keys to kempston",&new_setting,INT,
+ rb->set_option("Map Keys to kempston",&new_setting, RB_INT,
no_yes, 2, NULL);
if (new_setting != settings.kempston )
settings.kempston=new_setting;
break;
case 1:
new_setting = settings.showfps;
- rb->set_option("Display Speed",&new_setting,INT,
+ rb->set_option("Display Speed",&new_setting, RB_INT,
no_yes, 2, NULL);
if (new_setting != settings.showfps )
settings.showfps=new_setting;
break;
case 2:
new_setting = settings.invert_colors;
- rb->set_option("Invert Colors",&new_setting,INT,
+ rb->set_option("Invert Colors",&new_setting, RB_INT,
no_yes, 2, NULL);
if (new_setting != settings.invert_colors )
settings.invert_colors=new_setting;
@@ -279,14 +279,14 @@ static void options_menu(void){
break;
case 3:
new_setting = settings.frameskip;
- rb->set_option("Frameskip",&new_setting,INT,
+ rb->set_option("Frameskip",&new_setting, RB_INT,
frameskip_items, 10, NULL);
if (new_setting != settings.frameskip )
settings.frameskip=new_setting;
break;
case 4:
new_setting = settings.sound;
- rb->set_option("Sound",&new_setting,INT,
+ rb->set_option("Sound",&new_setting, RB_INT,
no_yes, 2, NULL);
if (new_setting != settings.sound )
settings.sound=new_setting;
@@ -296,7 +296,7 @@ static void options_menu(void){
break;
case 5:
new_setting = 9 - settings.volume;
- rb->set_option("Volume",&new_setting,INT,
+ rb->set_option("Volume",&new_setting, RB_INT,
frameskip_items, 10, NULL);
new_setting = 9 - new_setting;
if (new_setting != settings.volume )
diff --git a/apps/radio/presets.c b/apps/radio/presets.c
index 51bdabdfce..046525c3b6 100644
--- a/apps/radio/presets.c
+++ b/apps/radio/presets.c
@@ -225,7 +225,7 @@ void radio_load_presets(char *filename)
}
/* Temporary preset, loaded until player shuts down. */
else if(filename[0] == '/')
- strlcpy(filepreset, filename, sizeof(filepreset));
+ strmemccpy(filepreset, filename, sizeof(filepreset));
/* Preset from default directory. */
else
snprintf(filepreset, sizeof(filepreset), "%s/%s.fmr",
@@ -246,7 +246,7 @@ void radio_load_presets(char *filename)
{
struct fmstation * const fms = &presets[num_presets];
fms->frequency = f;
- strlcpy(fms->name, name, MAX_FMPRESET_LEN+1);
+ strmemccpy(fms->name, name, MAX_FMPRESET_LEN+1);
num_presets++;
}
}
@@ -351,11 +351,16 @@ static int radio_delete_preset(void)
int preset_list_load(void)
{
char selected[MAX_PATH];
- struct browse_context browse;
snprintf(selected, sizeof(selected), "%s.%s", global_settings.fmr_file, "fmr");
- browse_context_init(&browse, SHOW_FMR, 0,
- str(LANG_FM_PRESET_LOAD), NOICON,
- FMPRESET_PATH, selected);
+
+ struct browse_context browse = {
+ .dirfilter = SHOW_FMR,
+ .title = str(LANG_FM_PRESET_LOAD),
+ .icon = Icon_NOICON,
+ .root = FMPRESET_PATH,
+ .selected = selected,
+ };
+
return !rockbox_browse(&browse);
}
@@ -428,11 +433,11 @@ int preset_list_clear(void)
}
MENUITEM_FUNCTION(radio_edit_preset_item, MENU_FUNC_CHECK_RETVAL,
- ID2P(LANG_FM_EDIT_PRESET),
- radio_edit_preset, NULL, NULL, Icon_NOICON);
+ ID2P(LANG_FM_EDIT_PRESET),
+ radio_edit_preset, NULL, Icon_NOICON);
MENUITEM_FUNCTION(radio_delete_preset_item, MENU_FUNC_CHECK_RETVAL,
- ID2P(LANG_FM_DELETE_PRESET),
- radio_delete_preset, NULL, NULL, Icon_NOICON);
+ ID2P(LANG_FM_DELETE_PRESET),
+ radio_delete_preset, NULL, Icon_NOICON);
static int radio_preset_callback(int action,
const struct menu_item_ex *this_item,
struct gui_synclist *this_list)
@@ -490,8 +495,7 @@ int handle_radio_presets(void)
while (result == 0)
{
gui_synclist_draw(&lists);
- list_do_action(CONTEXT_STD, TIMEOUT_BLOCK,
- &lists, &action, LIST_WRAP_UNLESS_HELD);
+ list_do_action(CONTEXT_STD, TIMEOUT_BLOCK, &lists, &action);
switch (action)
{
case ACTION_STD_MENU:
diff --git a/apps/radio/radio.c b/apps/radio/radio.c
index b74371413e..bf8ad865dd 100644
--- a/apps/radio/radio.c
+++ b/apps/radio/radio.c
@@ -56,6 +56,7 @@
#include "statusbar-skinned.h"
#include "playback.h"
#include "presets.h"
+#include "wps.h" /* for wps_state... */
#if CONFIG_TUNER
@@ -363,7 +364,9 @@ void radio_screen(void)
{
radio_load_presets(global_settings.fmr_file);
}
- skin_get_global_state()->id3 = NULL;
+ /* TODO: Can this be moved somewhere else? */
+ get_wps_state()->id3 = NULL;
+ get_wps_state()->nid3 = NULL;
#ifdef HAVE_ALBUMART
radioart_init(true);
#endif
diff --git a/apps/radio/radio_skin.c b/apps/radio/radio_skin.c
index 4d90c4e241..dc38faa31a 100644
--- a/apps/radio/radio_skin.c
+++ b/apps/radio/radio_skin.c
@@ -36,6 +36,7 @@
#include "sound.h"
#include "misc.h"
#endif
+#include "skin_engine/wps_internals.h"
char* default_radio_skin(enum screen_type screen)
@@ -62,12 +63,12 @@ void fms_fix_displays(enum fms_exiting toggle_state)
{
FOR_NB_SCREENS(i)
{
- struct wps_data *data = skin_get_gwps(FM_SCREEN, i)->data;
+ struct gui_wps *gwps = skin_get_gwps(FM_SCREEN, i);
if (toggle_state == FMS_ENTER)
{
- viewportmanager_theme_enable(i, skin_has_sbs(i, data), NULL);
+ viewportmanager_theme_enable(i, skin_has_sbs(gwps), NULL);
#ifdef HAVE_BACKDROP_IMAGE
- skin_backdrop_show(data->backdrop_id);
+ skin_backdrop_show(gwps->data->backdrop_id);
#endif
screens[i].clear_display();
/* force statusbar/skin update since we just cleared the whole screen */
@@ -79,10 +80,10 @@ void fms_fix_displays(enum fms_exiting toggle_state)
#ifdef HAVE_BACKDROP_IMAGE
skin_backdrop_show(sb_get_backdrop(i));
#endif
- viewportmanager_theme_undo(i, skin_has_sbs(i, data));
+ viewportmanager_theme_undo(i, skin_has_sbs(gwps));
}
#ifdef HAVE_TOUCHSCREEN
- if (i==SCREEN_MAIN && !data->touchregions)
+ if (i==SCREEN_MAIN && !gwps->data->touchregions)
touchscreen_set_mode(toggle_state == FMS_ENTER ?
TOUCHSCREEN_BUTTON : global_settings.touch_mode);
#endif
@@ -95,11 +96,10 @@ int fms_do_button_loop(bool update_screen)
int button = skin_wait_for_action(FM_SCREEN, CONTEXT_FM|ALLOW_SOFTLOCK,
update_screen ? TIMEOUT_NOBLOCK : HZ/5);
#ifdef HAVE_TOUCHSCREEN
- struct touchregion *region;
+ struct gui_wps *gwps = skin_get_gwps(FM_SCREEN, SCREEN_MAIN);
int offset;
if (button == ACTION_TOUCHSCREEN)
- button = skin_get_touchaction(skin_get_gwps(FM_SCREEN, SCREEN_MAIN)->data,
- &offset, &region);
+ button = skin_get_touchaction(gwps, &offset);
switch (button)
{
case ACTION_WPS_STOP:
diff --git a/apps/radio/radioart.c b/apps/radio/radioart.c
index 34efdea0da..ce774ac4b1 100644
--- a/apps/radio/radioart.c
+++ b/apps/radio/radioart.c
@@ -82,7 +82,7 @@ static int load_radioart_image(struct radioart *ra, const char* preset_name,
#endif
return -1;
}
- strlcpy(ra->name, preset_name, MAX_FMPRESET_LEN+1);
+ strmemccpy(ra->name, preset_name, MAX_FMPRESET_LEN+1);
ra->dim.height = dim->height;
ra->dim.width = dim->width;
ra->last_tick = current_tick;
@@ -202,7 +202,7 @@ void radioart_init(bool entering_screen)
{
/* grab control over buffering */
size_t bufsize;
- int handle = core_alloc_maximum("radioart", &bufsize, &radioart_ops);
+ int handle = core_alloc_maximum(&bufsize, &radioart_ops);
if (handle <0)
{
splash(HZ, "Radioart Failed - OOM");
diff --git a/apps/rbcodec_helpers.c b/apps/rbcodec_helpers.c
index b412bb3aa4..197c0d4595 100644
--- a/apps/rbcodec_helpers.c
+++ b/apps/rbcodec_helpers.c
@@ -59,18 +59,12 @@ static struct buflib_callbacks ops =
/* Allocate timestretch buffers */
bool tdspeed_alloc_buffers(int32_t **buffers, const int *buf_s, int nbuf)
{
- static const char *buffer_names[4] = {
- "tdspeed ovl L",
- "tdspeed ovl R",
- "tdspeed out L",
- "tdspeed out R"
- };
-
+ /* #Buffer index - 0 ovl L, 1 ovl R, 2 out L, 3 out R */
for (int i = 0; i < nbuf; i++)
{
if (handles[i] <= 0)
{
- handles[i] = core_alloc_ex(buffer_names[i], buf_s[i], &ops);
+ handles[i] = core_alloc_ex(buf_s[i], &ops);
if (handles[i] <= 0)
return false;
@@ -93,10 +87,7 @@ void tdspeed_free_buffers(int32_t **buffers, int nbuf)
{
for (int i = 0; i < nbuf; i++)
{
- if (handles[i] > 0)
- core_free(handles[i]);
-
- handles[i] = 0;
+ handles[i] = core_free(handles[i]);
buffers[i] = NULL;
}
}
diff --git a/apps/recorder/albumart.c b/apps/recorder/albumart.c
index 5b3658a1cb..50794c06c8 100644
--- a/apps/recorder/albumart.c
+++ b/apps/recorder/albumart.c
@@ -39,6 +39,12 @@
#define USE_JPEG_COVER
#endif
+#ifdef PLUGIN
+ #define strmemccpy strlcpy
+ /* Note we don't use the return value so this works */
+ /* FIXME if strmemccpy gets added to the rb->plugin struct */
+#endif
+
/* Strip filename from a full path
*
* buf - buffer to extract directory to.
@@ -67,38 +73,12 @@ static char* strip_filename(char* buf, int buf_size, const char* fullpath)
}
len = MIN(sep - fullpath + 1, buf_size - 1);
- strlcpy(buf, fullpath, len + 1);
+ strmemccpy(buf, fullpath, len + 1);
return (sep + 1);
}
-/* Make sure part of path only contain chars valid for a FAT32 long name.
- * Double quotes are replaced with single quotes, other unsupported chars
- * are replaced with an underscore.
- *
- * path - path to modify.
- * offset - where in path to start checking.
- * count - number of chars to check.
- */
-static void fix_path_part(char* path, int offset, int count)
-{
- static const char invalid_chars[] = "*/:<>?\\|";
- int i;
-
- path += offset;
-
- for (i = 0; i <= count; i++, path++)
- {
- if (*path == 0)
- return;
- if (*path == '"')
- *path = '\'';
- else if (strchr(invalid_chars, *path))
- *path = '_';
- }
-}
-
#ifdef USE_JPEG_COVER
-static const char * extensions[] = { "jpeg", "jpg", "bmp" };
+static const char * const extensions[] = { "jpeg", "jpg", "bmp" };
static const unsigned char extension_lens[] = { 4, 3, 3 };
/* Try checking for several file extensions, return true if a file is found and
* leaving the path modified to include the matching extension.
@@ -266,7 +246,7 @@ bool search_albumart_files(const struct mp3entry *id3, const char *size_string,
if (!found)
return false;
- strlcpy(buf, path, buflen);
+ strmemccpy(buf, path, buflen);
logf("Album art found: %s", path);
return true;
}
@@ -297,74 +277,4 @@ bool find_albumart(const struct mp3entry *id3, char *buf, int buflen,
return search_albumart_files(id3, size_string, buf, buflen);
}
-/* Draw the album art bitmap from the given handle ID onto the given WPS.
- Call with clear = true to clear the bitmap instead of drawing it. */
-void draw_album_art(struct gui_wps *gwps, int handle_id, bool clear)
-{
- if (!gwps || !gwps->data || !gwps->display || handle_id < 0)
- return;
-
- struct wps_data *data = gwps->data;
- struct skin_albumart *aa = SKINOFFSETTOPTR(get_skin_buffer(data), data->albumart);
-
- if (!aa)
- return;
-
- struct bitmap *bmp;
- if (bufgetdata(handle_id, 0, (void *)&bmp) <= 0)
- return;
-
- short x = aa->x;
- short y = aa->y;
- short width = bmp->width;
- short height = bmp->height;
-
- if (aa->width > 0)
- {
- /* Crop if the bitmap is too wide */
- width = MIN(bmp->width, aa->width);
-
- /* Align */
- if (aa->xalign & WPS_ALBUMART_ALIGN_RIGHT)
- x += aa->width - width;
- else if (aa->xalign & WPS_ALBUMART_ALIGN_CENTER)
- x += (aa->width - width) / 2;
- }
-
- if (aa->height > 0)
- {
- /* Crop if the bitmap is too high */
- height = MIN(bmp->height, aa->height);
-
- /* Align */
- if (aa->yalign & WPS_ALBUMART_ALIGN_BOTTOM)
- y += aa->height - height;
- else if (aa->yalign & WPS_ALBUMART_ALIGN_CENTER)
- y += (aa->height - height) / 2;
- }
-
- if (!clear)
- {
- /* Draw the bitmap */
- gwps->display->bitmap_part((fb_data*)bmp->data, 0, 0,
- STRIDE(gwps->display->screen_type,
- bmp->width, bmp->height),
- x, y, width, height);
-#ifdef HAVE_LCD_INVERT
- if (global_settings.invert) {
- gwps->display->set_drawmode(DRMODE_COMPLEMENT);
- gwps->display->fillrect(x, y, width, height);
- gwps->display->set_drawmode(DRMODE_SOLID);
- }
-#endif
- }
- else
- {
- /* Clear the bitmap */
- gwps->display->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
- gwps->display->fillrect(x, y, width, height);
- gwps->display->set_drawmode(DRMODE_SOLID);
- }
-}
-
#endif /* PLUGIN */
diff --git a/apps/recorder/albumart.h b/apps/recorder/albumart.h
index 80cacaf5f0..0d663d3d92 100644
--- a/apps/recorder/albumart.h
+++ b/apps/recorder/albumart.h
@@ -35,12 +35,6 @@
bool find_albumart(const struct mp3entry *id3, char *buf, int buflen,
const struct dim *dim);
-#ifndef PLUGIN
-/* Draw the album art bitmap from the given handle ID onto the given Skin.
- Call with clear = true to clear the bitmap instead of drawing it. */
-void draw_album_art(struct gui_wps *gwps, int handle_id, bool clear);
-#endif
-
bool search_albumart_files(const struct mp3entry *id3, const char *size_string,
char *buf, int buflen);
diff --git a/apps/recorder/icons.c b/apps/recorder/icons.c
index cc53716674..de623d28b7 100644
--- a/apps/recorder/icons.c
+++ b/apps/recorder/icons.c
@@ -122,3 +122,11 @@ const unsigned char bitmap_icon_disk[12] =
{0x00,0x00,0x00,0x1c,0x2e,0x4f,0x77,0x79,0x3a,0x1c,0x00,0x00};
#endif
+const struct cbmp_bitmap_info_entry core_bitmaps[CBMP_BitmapFormatLast] = /* */
+{
+/* index, pointer, w, h, count */
+[CBMP_Mono_5x8] = {bitmap_icons_5x8[0], 5,8, Icon5x8Last},
+[CBMP_Mono_7x8] = {bitmap_icons_7x8[0], 7, 8, Icon7x8Last},
+[CBMP_Mono_12x8] = {bitmap_icon_disk, 12, 8, 1},
+};
+
diff --git a/apps/recorder/icons.h b/apps/recorder/icons.h
index 249453a943..944f319415 100644
--- a/apps/recorder/icons.h
+++ b/apps/recorder/icons.h
@@ -32,7 +32,25 @@
#ifdef HAVE_REMOTE_LCD
#include "bitmaps/remote_rockboxlogo.h"
#endif
+#endif /* PLUGIN */
+
+struct cbmp_bitmap_info_entry /* */
+{
+ const unsigned char* pbmp;
+ unsigned char width;
+ unsigned char height; /* !ASSUMES MULTIPLES OF 8! */
+ unsigned char count;
+};
+
+enum cbmp_bitmap_format
+{
+ CBMP_Mono_5x8 = 0,
+ CBMP_Mono_7x8,
+ CBMP_Mono_12x8,
+ CBMP_BitmapFormatLast
+};
+extern const struct cbmp_bitmap_info_entry core_bitmaps[CBMP_BitmapFormatLast];
/* Symbolic names for icons */
enum icons_5x8 {
@@ -65,6 +83,12 @@ enum icons_7x8 {
Icon7x8Last
};
+enum icons_12x8 {
+ Icon_Disk,
+ Icon12x8Last
+};
+
+#ifndef PLUGIN
#if defined (HAVE_RECORDING)
#define BM_GLYPH_WIDTH 4
enum Glyphs_4x8 {
diff --git a/apps/recorder/jpeg_common.h b/apps/recorder/jpeg_common.h
index c2abce8f49..de998f1ddd 100644
--- a/apps/recorder/jpeg_common.h
+++ b/apps/recorder/jpeg_common.h
@@ -83,8 +83,8 @@ union uint8_rgbyuv {
static inline int clamp_component(int x)
{
- if ((unsigned)x > 255)
- x = x < 0 ? 0 : 255;
+ if(x > 255) return 255;
+ if(x < 0) return 0;
return x;
}
#include <debug.h>
diff --git a/apps/recorder/jpeg_idct_arm.S b/apps/recorder/jpeg_idct_arm.S
index e7eb4b87f1..1f2603da1b 100644
--- a/apps/recorder/jpeg_idct_arm.S
+++ b/apps/recorder/jpeg_idct_arm.S
@@ -410,7 +410,7 @@ jpeg_idct8v:
#if ARM_ARCH < 5
mov r8, r4, lsl #16
orrs r9, r6, r7
- orreqs r9, r5, r4, lsr #16
+ orrseq r9, r5, r4, lsr #16
bne 2f
mov r8, r8, asr #14
strh r8, [r2]
@@ -505,7 +505,7 @@ jpeg_idct8v:
#else /* ARMv5+ */
mov r12, r4, lsl #16
orrs r9, r6, r7
- orreqs r9, r5, r4, lsr #16
+ orrseq r9, r5, r4, lsr #16
bne 2f
mov r12, r12, asr #14
strh r12, [r2]
@@ -615,7 +615,7 @@ jpeg_idct8h:
#if ARM_ARCH < 5
add r8, r14, r4, lsl #16
orrs r9, r6, r7
- orreqs r9, r5, r4, lsr #16
+ orrseq r9, r5, r4, lsr #16
bne 2f
mov r8, r8, asr #21
cmp r8, #255
@@ -727,7 +727,7 @@ jpeg_idct8h:
#else /* ARMv5+ */
add r12, r14, r4, lsl #16
orrs r9, r6, r7
- orreqs r9, r5, r4, lsr #16
+ orrseq r9, r5, r4, lsr #16
bne 2f
mov r12, r12, asr #21
cmp r12, #255
@@ -835,7 +835,7 @@ jpeg_idct8v:
1:
ldmia r0!, { r4-r7 }
orrs r9, r6, r7
- orreqs r9, r5, r4, lsr #16
+ orrseq r9, r5, r4, lsr #16
bne 2f
mov r4, r4, lsl #2
strh r4, [r2]
@@ -939,7 +939,7 @@ jpeg_idct8h:
ldmia r0!, { r4-r7 }
sadd16 r4, r4, r14
orrs r9, r6, r7
- orreqs r9, r5, r4, lsr #16
+ orrseq r9, r5, r4, lsr #16
bne 2f
sxth r4, r4
usat r4, #8, r4, asr #5
diff --git a/apps/recorder/jpeg_load.c b/apps/recorder/jpeg_load.c
index 9ab42b7a9f..eccd781f9b 100644
--- a/apps/recorder/jpeg_load.c
+++ b/apps/recorder/jpeg_load.c
@@ -2024,7 +2024,7 @@ int clip_jpeg_fd(int fd,
#else
struct jpeg *p_jpeg = (struct jpeg*)bm->data;
int tmp_size = maxsize;
- ALIGN_BUFFER(p_jpeg, tmp_size, sizeof(int));
+ ALIGN_BUFFER(p_jpeg, tmp_size, sizeof(long));
/* not enough memory for our struct jpeg */
if ((size_t)tmp_size < sizeof(struct jpeg))
return -1;
@@ -2133,7 +2133,7 @@ int clip_jpeg_fd(int fd,
char *buf_end = (char *)bm->data + maxsize;
maxsize = buf_end - buf_start;
#ifndef JPEG_FROM_MEM
- ALIGN_BUFFER(buf_start, maxsize, sizeof(uint32_t));
+ ALIGN_BUFFER(buf_start, maxsize, sizeof(long));
if (maxsize < (int)sizeof(struct jpeg))
return -1;
memmove(buf_start, p_jpeg, sizeof(struct jpeg));
@@ -2228,4 +2228,15 @@ int read_jpeg_fd(int fd,
}
#endif
+const size_t JPEG_DECODE_OVERHEAD =
+ /* Reserve an arbitrary amount for the decode buffer
+ * FIXME: Somebody who knows what they're doing should look at this */
+ (38 * 1024)
+#ifndef JPEG_FROM_MEM
+ /* Unless the struct jpeg is defined statically, we need to allocate
+ * it in the bitmap buffer as well */
+ + sizeof(struct jpeg)
+#endif
+ ;
+
/**************** end JPEG code ********************/
diff --git a/apps/recorder/jpeg_load.h b/apps/recorder/jpeg_load.h
index 6ff96dabad..129b0fbf19 100644
--- a/apps/recorder/jpeg_load.h
+++ b/apps/recorder/jpeg_load.h
@@ -32,6 +32,13 @@
#ifndef _JPEG_LOAD_H
#define _JPEG_LOAD_H
+/* Approximate memory overhead required for JPEG decoding. This memory is
+ * taken from the bitmap buffer so you must ensure the buffer is big enough
+ * to contain all decoded pixel data plus decoder overhead, otherwise the
+ * image cannot be loaded. After the image is loaded this extra memory can
+ * be freed. */
+extern const size_t JPEG_DECODE_OVERHEAD;
+
int read_jpeg_file(const char* filename,
struct bitmap *bm,
int maxsize,
diff --git a/apps/recorder/keyboard.c b/apps/recorder/keyboard.c
index b211fad331..736e9738c8 100644
--- a/apps/recorder/keyboard.c
+++ b/apps/recorder/keyboard.c
@@ -37,6 +37,7 @@
#include "viewport.h"
#include "file.h"
#include "splash.h"
+#include "core_alloc.h"
#ifndef O_BINARY
#define O_BINARY 0
@@ -78,8 +79,17 @@
#define CHANGED_CURSOR 2
#define CHANGED_TEXT 3
+enum ekbd_viewports
+{
+ eKBD_VP_TEXT = 0,
+ eKBD_VP_PICKER,
+ eKBD_VP_MENU,
+ eKBD_COUNT_VP_COUNT
+};
+
struct keyboard_parameters
{
+ struct viewport *kbd_viewports;
unsigned short kbd_buf[KBD_BUF_SIZE];
unsigned short *kbd_buf_ptr;
unsigned short max_line_len;
@@ -142,6 +152,71 @@ static const unsigned char morse_codes[] = {
0x73,0x55,0x4c,0x61,0x5a,0x80 };
#endif
+static void keyboard_layout(struct viewport *kbd_vp,
+ struct keyboard_parameters *pm,
+ struct screen *sc)
+{
+ /*Note: viewports are initialized to vp_default by kbd_create_viewports */
+
+ int sc_w = sc->getwidth();
+ int sc_h = sc->getheight();
+
+ /* TEXT */
+ struct viewport *vp = &kbd_vp[eKBD_VP_TEXT];
+ /* make sure height is even for the text box */
+ int text_height = (MAX(pm->font_h, get_icon_height(sc->screen_type)) & ~1) + 2;
+ vp->x = 0; /* LEFT */
+ vp->y = 0; /* TOP */
+ vp->width = sc_w;
+ vp->height = text_height;
+ vp->font = pm->curfont;
+ text_height += vp->x + 3;
+
+ /* MENU */
+ vp = &kbd_vp[eKBD_VP_MENU];
+ int menu_w = 0;//pm->font_w * MENU_CHARS; /* NOT IMPLEMENTED */
+ vp->x = 0; /* LEFT */
+#ifdef HAVE_LCD_SPLIT
+ vp->y = MAX(LCD_SPLIT_POS, text_height); /* Sansa Clip/Clip+ */
+#else
+ vp->y = text_height; /* TOP */
+#endif
+ vp->width = menu_w;
+ vp->height = 0;
+ vp->font = pm->curfont;
+ menu_w += vp->x;
+
+ /* PICKER */
+ vp = &kbd_vp[eKBD_VP_PICKER];
+ vp->x = menu_w; /* LEFT */
+#ifdef HAVE_LCD_SPLIT
+ vp->y = MAX(LCD_SPLIT_POS, text_height - 2); /* Sansa Clip/Clip+ */
+#else
+ vp->y = text_height - 2; /* TOP */
+#endif
+ vp->width = sc_w - menu_w;
+ vp->height = sc_h - vp->y; /* (MAX SIZE) - OVERWRITTEN */
+ vp->font = pm->curfont;
+}
+
+static int kbd_create_viewports(struct keyboard_parameters * kbd_param)
+{
+ static struct viewport viewports[NB_SCREENS][eKBD_COUNT_VP_COUNT];
+ int i;
+ FOR_NB_SCREENS(l)
+ {
+ kbd_param[l].kbd_viewports = viewports[l];
+ for (i = 0; i < eKBD_COUNT_VP_COUNT; i++)
+ {
+ struct viewport *vp = &kbd_param[l].kbd_viewports[i];
+ viewport_set_defaults(vp, l);
+ vp->font = FONT_UI;
+ }
+ }
+
+ return sizeof(viewports);
+}
+
/* Loads a custom keyboard into memory
call with NULL to reset keyboard */
int load_kbd(unsigned char* filename)
@@ -309,8 +384,9 @@ static unsigned short get_kbd_ch(struct keyboard_parameters *pm, int x, int y)
k = k * pm->max_chars + x;
return (*pbuf != 0xFEFF && k < *pbuf)? pbuf[k+1]: ' ';
}
-
-static void kbd_calc_params(struct keyboard_parameters *pm,
+static void kbd_calc_pm_params(struct keyboard_parameters *pm,
+ struct screen *sc, struct edit_state *state);
+static void kbd_calc_vp_params(struct keyboard_parameters *pm,
struct screen *sc, struct edit_state *state);
static void kbd_draw_picker(struct keyboard_parameters *pm,
struct screen *sc, struct edit_state *state);
@@ -342,6 +418,18 @@ int kbd_input(char* text, int buflen, unsigned short *kbd)
viewportmanager_theme_enable(l, false, NULL);
}
+ if (kbd_create_viewports(param) <= 0)
+ {
+ splash(HZ * 2,"Error: No Viewport Allocated, OOM?");
+ goto cleanup;
+ }
+
+#ifdef HAVE_TOUCHSCREEN
+ /* keyboard is unusuable in pointing mode so force 3x3 for now.
+ * TODO - fix properly by using a bigger font and changing the layout */
+ enum touchscreen_mode old_mode = touchscreen_get_mode();
+ touchscreen_set_mode(TOUCHSCREEN_BUTTON);
+#endif
/* initialize state */
state.text = text;
state.buflen = buflen;
@@ -436,7 +524,15 @@ int kbd_input(char* text, int buflen, unsigned short *kbd)
pm->kbd_buf_ptr = pm->kbd_buf; /* internal layout buffer */
struct screen *sc = &screens[l];
- kbd_calc_params(pm, sc, &state);
+
+ kbd_calc_pm_params(pm, sc, &state);
+
+ keyboard_layout(pm->kbd_viewports, pm, sc);
+ /* have all the params we need lets set up our viewports */
+ kbd_calc_vp_params(pm, sc, &state);
+ /* We want these the same height */
+ pm->kbd_viewports[eKBD_VP_MENU].height = pm->main_y;
+ pm->kbd_viewports[eKBD_VP_PICKER].height = pm->main_y;
}
if (global_settings.talk_menu) /* voice UI? */
@@ -461,8 +557,8 @@ int kbd_input(char* text, int buflen, unsigned short *kbd)
{
/* declare scoped pointers inside screen loops - hide the
declarations from previous block level */
- struct keyboard_parameters *pm = &param[l];
struct screen *sc = &screens[l];
+ pm = &param[l];
sc->clear_display();
kbd_draw_picker(pm, sc, &state);
kbd_draw_edit_line(pm, sc, &state);
@@ -680,6 +776,10 @@ int kbd_input(char* text, int buflen, unsigned short *kbd)
if (ret < 0)
splash(HZ/2, ID2P(LANG_CANCEL));
+#ifdef HAVE_TOUCHSCREEN
+ touchscreen_set_mode(old_mode);
+#endif
+
#if defined(HAVE_MORSE_INPUT) && defined(KBD_TOGGLE_INPUT)
if (global_settings.morse_input != state.morse_mode)
{
@@ -687,7 +787,7 @@ int kbd_input(char* text, int buflen, unsigned short *kbd)
settings_save();
}
#endif /* HAVE_MORSE_INPUT && KBD_TOGGLE_INPUT */
-
+cleanup:
FOR_NB_SCREENS(l)
{
screens[l].setfont(FONT_UI);
@@ -695,18 +795,14 @@ int kbd_input(char* text, int buflen, unsigned short *kbd)
}
return ret;
}
-
-static void kbd_calc_params(struct keyboard_parameters *pm,
+static void kbd_calc_pm_params(struct keyboard_parameters *pm,
struct screen *sc, struct edit_state *state)
{
struct font* font;
const unsigned char *p;
unsigned short ch, *pbuf;
- int icon_w, sc_w, sc_h, w;
- int i, total_lines;
+ int i, w;
#ifdef HAVE_TOUCHSCREEN
- int button_h = 0;
- bool flippage_button = false;
pm->show_buttons = (sc->screen_type == SCREEN_MAIN &&
(touchscreen_get_mode() == TOUCHSCREEN_POINT));
#endif
@@ -754,9 +850,21 @@ static void kbd_calc_params(struct keyboard_parameters *pm,
#ifdef HAVE_TOUCHSCREEN
pm->font_w = GRID_SIZE(sc->screen_type, pm->font_w);
#endif
+
+}
+
+static void kbd_calc_vp_params(struct keyboard_parameters *pm,
+ struct screen *sc, struct edit_state *state)
+{
+ (void) state;
+ struct viewport *vp = &pm->kbd_viewports[eKBD_VP_PICKER];
+ int icon_w, sc_w, sc_h;
+ int i, total_lines;
+ unsigned short *pbuf;
/* calculate how many characters to put in a row. */
icon_w = get_icon_width(sc->screen_type);
- sc_w = sc->getwidth();
+
+ sc_w = vp->width; /**sc->getwidth();**/
if (pm->font_w < sc_w / pm->max_line_len)
pm->font_w = sc_w / pm->max_line_len;
pm->max_chars = sc_w / pm->font_w;
@@ -765,8 +873,10 @@ static void kbd_calc_params(struct keyboard_parameters *pm,
pm->max_chars_text = sc_w / pm->text_w - 2;
/* calculate pm->pages and pm->lines */
- sc_h = sc->getheight();
+ sc_h = vp->height;/**sc->getheight()**/;
#ifdef HAVE_TOUCHSCREEN
+ int button_h = 0;
+ bool flippage_button = false;
/* add space for buttons */
if (pm->show_buttons)
{
@@ -813,7 +923,7 @@ recalc_param:
pm->main_y = pm->font_h*pm->lines + pm->keyboard_margin;
pm->keyboard_margin -= pm->keyboard_margin/2;
#ifdef HAVE_TOUCHSCREEN
- /* flip page button is put between piker and edit line */
+ /* flip page button is put between picker and edit line */
if (flippage_button)
pm->main_y += button_h;
#endif
@@ -832,13 +942,18 @@ recalc_param:
static void kbd_draw_picker(struct keyboard_parameters *pm,
struct screen *sc, struct edit_state *state)
{
+ struct viewport *last;
+ struct viewport *vp = &pm->kbd_viewports[eKBD_VP_PICKER];
+ last = sc->set_viewport(vp);
+ sc->clear_viewport();
+
char outline[8];
#ifdef HAVE_MORSE_INPUT
if (state->morse_mode)
{
const int w = 6, h = 8; /* sysfixed font width, height */
int i, j, x, y;
- int sc_w = sc->getwidth(), sc_h = pm->main_y - pm->keyboard_margin - 1;
+ int sc_w = vp->width, sc_h = vp->height;//pm->main_y - pm->keyboard_margin - 1;
/* Draw morse code screen with sysfont */
sc->setfont(FONT_SYSFIXED);
@@ -915,6 +1030,7 @@ static void kbd_draw_picker(struct keyboard_parameters *pm,
sc->set_drawmode(DRMODE_SOLID);
}
}
+ sc->set_viewport(last);
}
static void kbd_draw_edit_line(struct keyboard_parameters *pm,
@@ -922,37 +1038,49 @@ static void kbd_draw_edit_line(struct keyboard_parameters *pm,
{
char outline[8];
unsigned char *utf8;
- int i = 0, j = 0, icon_w, w;
- int sc_w = sc->getwidth();
- int y = pm->main_y - pm->keyboard_margin;
+ int i = 0, j = 0, w;
+ int icon_w, icon_y;
+ struct viewport *last;
+ struct viewport *vp = &pm->kbd_viewports[eKBD_VP_TEXT];
+ last = sc->set_viewport(vp);
+ sc->clear_viewport();
+ sc->hline(1, vp->width - 1, vp->height - 1);
+
+ int sc_w = vp->width;
+ int y = (vp->height - pm->font_h) / 2;
+
+
int text_margin = (sc_w - pm->text_w * pm->max_chars_text) / 2;
+#if 0
/* Clear text area one pixel above separator line so any overdraw
doesn't collide */
screen_clear_area(sc, 0, y - 1, sc_w, pm->font_h + 6);
sc->hline(0, sc_w - 1, y);
-
+#endif
/* write out the text */
sc->setfont(pm->curfont);
pm->leftpos = MAX(0, MIN(state->len_utf8, state->editpos + 2)
- pm->max_chars_text);
+
pm->curpos = state->editpos - pm->leftpos;
utf8 = state->text + utf8seek(state->text, pm->leftpos);
while (*utf8 && i < pm->max_chars_text)
{
j = utf8seek(utf8, 1);
- strlcpy(outline, utf8, j+1);
+ strmemccpy(outline, utf8, j+1);
sc->getstringsize(outline, &w, NULL);
sc->putsxy(text_margin + i*pm->text_w + (pm->text_w-w)/2,
- pm->main_y, outline);
+ y, outline);
utf8 += j;
i++;
}
icon_w = get_icon_width(sc->screen_type);
+ icon_y = (vp->height - get_icon_height(sc->screen_type)) / 2;
if (pm->leftpos > 0)
{
/* Draw nicer bitmap arrow if room, else settle for "<". */
@@ -960,12 +1088,12 @@ static void kbd_draw_edit_line(struct keyboard_parameters *pm,
{
screen_put_icon_with_offset(sc, 0, 0,
(text_margin - icon_w) / 2,
- pm->main_y, Icon_Reverse_Cursor);
+ icon_y, Icon_Reverse_Cursor);
}
else
{
sc->getstringsize("<", &w, NULL);
- sc->putsxy(text_margin - w, pm->main_y, "<");
+ sc->putsxy(text_margin - w, y, "<");
}
}
@@ -976,11 +1104,11 @@ static void kbd_draw_edit_line(struct keyboard_parameters *pm,
{
screen_put_icon_with_offset(sc, 0, 0,
sc_w - (text_margin + icon_w) / 2,
- pm->main_y, Icon_Cursor);
+ icon_y, Icon_Cursor);
}
else
{
- sc->putsxy(sc_w - text_margin, pm->main_y, ">");
+ sc->putsxy(sc_w - text_margin, y, ">");
}
}
@@ -988,17 +1116,19 @@ static void kbd_draw_edit_line(struct keyboard_parameters *pm,
i = text_margin + pm->curpos * pm->text_w;
if (state->cur_blink)
- sc->vline(i, pm->main_y, pm->main_y + pm->font_h - 1);
+ sc->vline(i, y, y + pm->font_h - 1);
if (state->hangul) /* draw underbar */
- sc->hline(i - pm->text_w, i, pm->main_y + pm->font_h - 1);
+ sc->hline(i - pm->text_w, i, y + pm->font_h - 1);
if (pm->line_edit)
{
sc->set_drawmode(DRMODE_COMPLEMENT);
- sc->fillrect(0, y + 2, sc_w, pm->font_h + 2);
+ sc->fillrect(0, y - 1, sc_w, pm->font_h + 2);
sc->set_drawmode(DRMODE_SOLID);
}
+
+ sc->set_viewport(last);
}
#ifdef HAVE_TOUCHSCREEN
@@ -1215,16 +1345,18 @@ static void kbd_move_cursor(struct edit_state *state, int dir)
{
state->changed = CHANGED_CURSOR;
}
- else if (state->editpos > state->len_utf8)
+ else if (global_settings.list_wraparound && state->editpos > state->len_utf8)
{
state->editpos = 0;
if (global_settings.talk_menu) beep_play(1000, 150, 1500);
}
- else if (state->editpos < 0)
+ else if (global_settings.list_wraparound && state->editpos < 0)
{
state->editpos = state->len_utf8;
if (global_settings.talk_menu) beep_play(1000, 150, 1500);
}
+ else if (!global_settings.list_wraparound)
+ state->editpos -= dir;
}
static void kbd_move_picker_horizontal(struct keyboard_parameters *pm,
@@ -1235,12 +1367,22 @@ static void kbd_move_picker_horizontal(struct keyboard_parameters *pm,
pm->x += dir;
if (pm->x < 0)
{
+ if (!global_settings.list_wraparound && pm->page == 0)
+ {
+ pm->x = 0;
+ return;
+ }
if (--pm->page < 0)
pm->page = pm->pages - 1;
pm->x = pm->max_chars - 1;
}
else if (pm->x >= pm->max_chars)
{
+ if (!global_settings.list_wraparound && pm->page == pm->pages - 1)
+ {
+ pm->x = pm->max_chars - 1;
+ return;
+ }
if (++pm->page >= pm->pages)
pm->page = 0;
pm->x = 0;
@@ -1261,6 +1403,38 @@ static void kbd_move_picker_vertical(struct keyboard_parameters *pm,
#endif /* HAVE_MORSE_INPUT */
pm->y += dir;
+
+ if (!global_settings.list_wraparound)
+ {
+#if 0 /* edit line below picker */
+ if (pm->y >= pm->lines)
+ {
+ pm->y = pm->lines;
+ pm->line_edit = true;
+ }
+ else if (pm->y < 0)
+ pm->y = 0;
+ else if (pm->line_edit)
+ pm->line_edit = false;
+#else /* edit line above picker */
+ if (pm->y >= pm->lines)
+ {
+ pm->y = pm->lines;
+ }
+ else if (pm->y < 0)
+ {
+ pm->line_edit = true;
+ pm->y = 0;
+ }
+ else if (pm->line_edit)
+ {
+ pm->line_edit = false;
+ pm->y = 0;
+ }
+#endif
+ return;
+ }
+
if (pm->line_edit)
{
pm->y = (dir > 0 ? 0 : pm->lines - 1);
diff --git a/apps/recorder/pcm_record.c b/apps/recorder/pcm_record.c
index b5de670c0d..01f0e604dd 100644
--- a/apps/recorder/pcm_record.c
+++ b/apps/recorder/pcm_record.c
@@ -873,45 +873,41 @@ static bool open_rec_file(bool create)
static void * ICODE_ATTR
copy_buffer_mono_lr(void *dst, const void *src, size_t src_size)
{
- int16_t *d = dst;
- int16_t const *s = src;
-
- /* mono = (L + R) / 2 */
- do
- *d++ = ((int32_t){ *s++ } + *s++ + 1) >> 1;
- while (src_size -= PCM_SAMP_SIZE);
+ int16_t *d = (int16_t*) dst;
+ int16_t const *s = (int16_t const*) src;
+ ssize_t copy_size = src_size;
+
+ /* mono = (L + R) / 2 */
+ while(copy_size > 0) {
+ *d++ = ((int32_t)s[0] + (int32_t)s[1] + 1) >> 1;
+ s += 2;
+ copy_size -= PCM_SAMP_SIZE;
+ }
return dst;
}
-/* Copy with mono conversion - output 1/2 size of input */
static void * ICODE_ATTR
copy_buffer_mono_l(void *dst, const void *src, size_t src_size)
{
- int16_t *d = dst;
- int16_t const *s = (int16_t *)src - 2;
+ int16_t *d = (int16_t*) dst;
+ int16_t const *s = (int16_t const*) src;
+ ssize_t copy_size = src_size;
/* mono = L */
- do
- *d++ = *(s += 2);
- while (src_size -= PCM_SAMP_SIZE);
+ while(copy_size > 0) {
+ *d++ = *s;
+ s += 2;
+ copy_size -= PCM_SAMP_SIZE;
+ }
return dst;
}
-/* Copy with mono conversion - output 1/2 size of input */
static void * ICODE_ATTR
copy_buffer_mono_r(void *dst, const void *src, size_t src_size)
{
- int16_t *d = dst;
- int16_t const *s = (int16_t *)src - 1;
-
- /* mono = R */
- do
- *d++ = *(s += 2);
- while (src_size -= PCM_SAMP_SIZE);
-
- return dst;
+ return copy_buffer_mono_l(dst, src + 2, src_size);
}
@@ -983,10 +979,6 @@ void audio_record(const char *filename)
audio_queue_send(Q_AUDIO_RECORD, (intptr_t)filename);
}
-/* audio_record alias for API compatibility with HW codec */
-void audio_new_file(const char *filename)
- __attribute__((alias("audio_record")));
-
/* Stop current recording if recording */
void audio_stop_recording(void)
{
@@ -1356,7 +1348,7 @@ static void mark_stream(const char *path, enum mark_stream_action action)
file->hdr.type = CHUNK_T_STREAM_START;
file->hdr.size = count;
- strlcpy(file->path, path, MAX_PATH);
+ strmemccpy(file->path, path, MAX_PATH);
}
}
@@ -1410,12 +1402,9 @@ static int pcmrec_handle;
static void on_init_recording(void)
{
send_event(RECORDING_EVENT_START, NULL);
- /* dummy ops with no callbacks, needed because by
- * default buflib buffers can be moved around which must be avoided
- * FIXME: This buffer should play nicer and be shrinkable/movable */
- static struct buflib_callbacks dummy_ops;
+ /* FIXME: This buffer should play nicer and be shrinkable/movable */
talk_buffer_set_policy(TALK_BUFFER_LOOSE);
- pcmrec_handle = core_alloc_maximum("pcmrec", &rec_buffer_size, &dummy_ops);
+ pcmrec_handle = core_alloc_maximum(&rec_buffer_size, &buflib_ops_locked);
if (pcmrec_handle <= 0)
/* someone is abusing core_alloc_maximum(). Fix this evil guy instead of
* trying to handle OOM without hope */
@@ -1444,8 +1433,7 @@ static void on_close_recording(void)
audio_set_output_source(AUDIO_SRC_PLAYBACK);
pcm_apply_settings();
- if (pcmrec_handle > 0)
- pcmrec_handle = core_free(pcmrec_handle);
+ pcmrec_handle = core_free(pcmrec_handle);
talk_buffer_set_policy(TALK_BUFFER_DEFAULT);
send_event(RECORDING_EVENT_STOP, NULL);
@@ -1590,7 +1578,7 @@ static void on_record(const char *filename)
/* Copy path and let caller go */
char path[MAX_PATH];
- strlcpy(path, filename, MAX_PATH);
+ strmemccpy(path, filename, MAX_PATH);
queue_reply(&audio_queue, 0);
diff --git a/apps/recorder/peakmeter.c b/apps/recorder/peakmeter.c
index e8e5db551a..c9e92b5762 100644
--- a/apps/recorder/peakmeter.c
+++ b/apps/recorder/peakmeter.c
@@ -46,9 +46,7 @@
#include "pcm_record.h"
#endif
-#if !(CONFIG_PLATFORM & PLATFORM_HOSTED)
static bool pm_playback = true; /* selects between playback and recording peaks */
-#endif
static struct meter_scales scales[NB_SCREENS];
@@ -60,6 +58,8 @@ static int pm_max_right;
#if defined(HAVE_AGC) || defined(HAVE_HISTOGRAM)
static int pm_peakhold_left; /* max. peak values between peakhold calls */
static int pm_peakhold_right; /* used for AGC and histogram display */
+#endif
+#ifdef HAVE_HISTOGRAM
static long next_histogram_update;
#endif
@@ -543,11 +543,7 @@ void pm_reset_clipcount(void)
*/
void peak_meter_playback(bool playback)
{
-#if (CONFIG_PLATFORM & PLATFORM_HOSTED)
- (void)playback;
-#else
pm_playback = playback;
-#endif
/* reset the scales just in case recording and playback
use different viewport sizes. Normally we should be checking viewport
sizes every time but this will do for now */
@@ -582,10 +578,6 @@ void peak_meter_peek(void)
bool was_clipping = pm_clip_left || pm_clip_right;
#endif
/* read current values */
-#if (CONFIG_PLATFORM & PLATFORM_HOSTED)
- pm_cur_left = left = 8000;
- pm_cur_right = right = 9000;
-#else
if (pm_playback)
{
static struct pcm_peaks chan_peaks; /* *MUST* be static */
@@ -600,7 +592,6 @@ void peak_meter_peek(void)
#endif
left = pm_cur_left;
right = pm_cur_right;
-#endif
/* check for clips
An clip is assumed when two consecutive readouts
@@ -1078,9 +1069,7 @@ static void peak_meter_draw(struct screen *display, struct meter_scales *scales,
/* cliplight */
if ((pm_clip_left || pm_clip_right) &&
global_settings.cliplight
-#if !(CONFIG_PLATFORM & PLATFORM_HOSTED)
&& !pm_playback
-#endif
)
{
/* if clipping, cliplight setting on and in recording screen */
diff --git a/apps/recorder/recording.c b/apps/recorder/recording.c
index 36331a72f3..dec7b7bd8c 100644
--- a/apps/recorder/recording.c
+++ b/apps/recorder/recording.c
@@ -68,7 +68,9 @@
#include "splash.h"
#include "screen_access.h"
#include "action.h"
-#include "radio.h"
+#ifdef HAVE_FMRADIO_REC
+# include "radio.h"
+#endif
#include "viewport.h"
#include "list.h"
#include "general.h"
@@ -187,6 +189,7 @@ static short balance_mem[BAL_MEM_SIZE];
#define AGC_MODE_SIZE 5
#define AGC_SAFETY_MODE 0
+/* Note iriver devices overwrite these strings */
static const char* agc_preset_str[] =
{ "Off", "S", "L", "D", "M", "V" };
/* "Off",
@@ -233,9 +236,9 @@ static long hist_time = 0;
static void set_gain(void)
{
+ switch(global_settings.rec_source) {
#ifdef HAVE_MIC_REC
- if(global_settings.rec_source == AUDIO_SRC_MIC)
- {
+ case AUDIO_SRC_MIC:
if (global_settings.rec_mic_gain > sound_max(SOUND_MIC_GAIN))
global_settings.rec_mic_gain = sound_max(SOUND_MIC_GAIN);
@@ -244,10 +247,11 @@ static void set_gain(void)
audio_set_recording_gain(global_settings.rec_mic_gain,
0, AUDIO_GAIN_MIC);
- }
- else
+ break;
#endif /* MIC */
- {
+#if defined(HAVE_LINE_REC) || defined(HAVE_FMRADIO_REC)
+ HAVE_LINE_REC_(case AUDIO_SRC_LINEIN:)
+ HAVE_FMRADIO_REC_(case AUDIO_SRC_FMRADIO:)
if (global_settings.rec_left_gain > sound_max(SOUND_LEFT_GAIN))
global_settings.rec_left_gain = sound_max(SOUND_LEFT_GAIN);
@@ -260,11 +264,13 @@ static void set_gain(void)
if (global_settings.rec_right_gain < sound_min(SOUND_RIGHT_GAIN))
global_settings.rec_right_gain = sound_min(SOUND_RIGHT_GAIN);
- /* AUDIO_SRC_LINEIN, AUDIO_SRC_FMRADIO, AUDIO_SRC_SPDIF */
audio_set_recording_gain(global_settings.rec_left_gain,
global_settings.rec_right_gain,
AUDIO_GAIN_LINEIN);
+ break;
+#endif
}
+
/* reset the clipping indicators */
peak_meter_set_clip_hold(global_settings.peak_meter_clip_hold);
update_list = true;
@@ -332,6 +338,11 @@ static bool agc_gain_is_max(bool left, bool right)
static void change_recording_gain(bool increment, bool left, bool right)
{
+#if !defined(HAVE_LINE_REC) || !defined(HAVE_FMRADIO_REC)
+ (void)left;
+ (void)right;
+#endif
+
int factor = (increment ? 1 : -1);
switch (global_settings.rec_source)
@@ -632,16 +643,11 @@ void rec_command(enum recording_command cmd)
audio_stop_recording();
break;
case RECORDING_CMD_START:
- /* steal mp3 buffer, create unique filename and start recording */
- pm_reset_clipcount();
- pm_activate_clipcount(true);
- audio_record(rec_create_filename(path_buffer));
- break;
case RECORDING_CMD_START_NEWFILE:
/* create unique filename and start recording*/
pm_reset_clipcount();
- pm_activate_clipcount(true); /* just to be sure */
- audio_new_file(rec_create_filename(path_buffer));
+ pm_activate_clipcount(true);
+ audio_record(rec_create_filename(path_buffer));
break;
case RECORDING_CMD_PAUSE:
pm_activate_clipcount(false);
@@ -729,43 +735,24 @@ static void trigger_listener(int trigger_status)
/* Stuff for drawing the screen */
enum rec_list_items_stereo {
- ITEM_VOLUME = 0,
- ITEM_GAIN = 1,
- ITEM_GAIN_L = 2,
- ITEM_GAIN_R = 3,
-#ifdef HAVE_AGC
- ITEM_AGC_MODE = 4,
- ITEM_AGC_MAXDB = 5,
- ITEM_FILENAME = 7,
- ITEM_COUNT = 7,
-#else
- ITEM_FILENAME = 7,
- ITEM_COUNT = 5,
+#ifndef HAVE_RECORDING_WITHOUT_MONITORING
+ ITEM_VOLUME,
+#endif
+ ITEM_GAIN,
+#if defined(HAVE_LINE_REC) || defined(HAVE_FMRADIO_REC)
+ ITEM_GAIN_L,
+ ITEM_GAIN_R,
#endif
-};
-
-enum rec_list_items_mono {
- ITEM_VOLUME_M = 0,
- ITEM_GAIN_M = 1,
#ifdef HAVE_AGC
- ITEM_AGC_MODE_M = 4,
- ITEM_AGC_MAXDB_M = 5,
- ITEM_FILENAME_M = 7,
- ITEM_COUNT_M = 5,
-#else
- ITEM_FILENAME_M = 7,
- ITEM_COUNT_M = 3,
+ ITEM_AGC_MODE,
+ ITEM_AGC_MAXDB,
#endif
-};
-
#ifdef HAVE_SPDIF_REC
-enum rec_list_items_spdif {
- ITEM_VOLUME_D = 0,
- ITEM_SAMPLERATE_D = 6,
- ITEM_FILENAME_D = 7,
- ITEM_COUNT_D = 3,
-};
+ ITEM_SAMPLERATE,
#endif
+ ITEM_FILENAME,
+ ITEM_COUNT,
+};
static int listid_to_enum[ITEM_COUNT];
@@ -782,33 +769,39 @@ static const char* reclist_get_name(int selected_item, void * data,
switch (listid_to_enum[selected_item])
{
+#ifndef HAVE_RECORDING_WITHOUT_MONITORING
case ITEM_VOLUME:
snprintf(buffer, buffer_len, "%s: %s", str(LANG_VOLUME),
fmt_gain(SOUND_VOLUME,
global_settings.volume,
buf2, sizeof(buf2)));
break;
+#endif
case ITEM_GAIN:
+ switch(global_settings.rec_source) {
#ifdef HAVE_MIC_REC
- if(global_settings.rec_source == AUDIO_SRC_MIC)
- {
+ case AUDIO_SRC_MIC:
/* Draw MIC recording gain */
snprintf(buffer, buffer_len, "%s: %s", str(LANG_GAIN),
fmt_gain(SOUND_MIC_GAIN,
global_settings.rec_mic_gain,
buf2, sizeof(buf2)));
- }
- else
+ break;
#endif /* MIC */
- {
+#if defined(HAVE_LINE_REC) || defined(HAVE_FMRADIO_REC)
+ HAVE_LINE_REC_(case AUDIO_SRC_LINEIN:)
+ HAVE_FMRADIO_REC_(case AUDIO_SRC_FMRADIO:) {
int avg_gain = (global_settings.rec_left_gain +
global_settings.rec_right_gain) / 2;
snprintf(buffer, buffer_len, "%s: %s", str(LANG_GAIN),
fmt_gain(SOUND_LEFT_GAIN,
avg_gain,
buf2, sizeof(buf2)));
+ } break;
+#endif
}
break;
+#if defined(HAVE_LINE_REC) || defined(HAVE_FMRADIO_REC)
case ITEM_GAIN_L:
snprintf(buffer, buffer_len, "%s: %s",
str(LANG_GAIN_LEFT),
@@ -823,42 +816,46 @@ static const char* reclist_get_name(int selected_item, void * data,
global_settings.rec_right_gain,
buf2, sizeof(buf2)));
break;
+#endif
#ifdef HAVE_AGC
case ITEM_AGC_MODE:
snprintf(buffer, buffer_len, "%s: %s",
str(LANG_RECORDING_AGC_PRESET),
agc_preset_str[agc_preset]);
break;
- case ITEM_AGC_MAXDB:
- if (agc_preset == 0)
+ case ITEM_AGC_MAXDB: {
+ int bias, which;
+ switch(global_settings.rec_source) {
+ default:
+#if defined(HAVE_LINE_REC) || defined(HAVE_FMRADIO_REC)
+ HAVE_LINE_IN_(case AUDIO_SRC_LINEIN:)
+ HAVE_FMRADIO_IN_(case AUDIO_SRC_FMRADIO:)
+ which = SOUND_LEFT_GAIN;
+ bias = (global_settings.rec_left_gain + global_settings.rec_right_gain) / 2;
+ break;
+#endif
+#if defined(HAVE_MIC_REC)
+ case AUDIO_SRC_MIC:
+ which = SOUND_MIC_GAIN;
+ bias = global_settings.rec_mic_gain;
+ break;
+#endif /* MIC*/
+ }
+
+ if(agc_preset == 0) {
snprintf(buffer, buffer_len, "%s: %s",
str(LANG_RECORDING_AGC_MAXGAIN),
- fmt_gain(SOUND_LEFT_GAIN,
- agc_maxgain, buf2, sizeof(buf2)));
-#ifdef HAVE_MIC_REC
- else if (global_settings.rec_source == AUDIO_SRC_MIC)
+ fmt_gain(which, agc_maxgain, buf2, sizeof(buf2)));
+ } else {
snprintf(buffer, buffer_len, "%s: %s (%s)",
str(LANG_RECORDING_AGC_MAXGAIN),
- fmt_gain(SOUND_MIC_GAIN,
- agc_maxgain, buf2, sizeof(buf2)),
- fmt_gain(SOUND_MIC_GAIN,
- agc_maxgain - global_settings.rec_mic_gain,
- buf3, sizeof(buf3)));
- else
-#endif /* MIC */
- snprintf(buffer, buffer_len, "%s: %s (%s)",
- str(LANG_RECORDING_AGC_MAXGAIN),
- fmt_gain(SOUND_LEFT_GAIN,
- agc_maxgain, buf2, sizeof(buf2)),
- fmt_gain(SOUND_LEFT_GAIN,
- agc_maxgain -
- (global_settings.rec_left_gain +
- global_settings.rec_right_gain)/2,
- buf3, sizeof(buf3)));
- break;
+ fmt_gain(which, agc_maxgain, buf2, sizeof(buf2)),
+ fmt_gain(which, agc_maxgain - bias, buf3, sizeof(buf3)));
+ }
+ } break;
#endif
#ifdef HAVE_SPDIF_REC
- case ITEM_SAMPLERATE_D:
+ case ITEM_SAMPLERATE:
snprintf(buffer, buffer_len, "%s: %lu",
str(LANG_FREQUENCY), pcm_rec_sample_rate());
break;
@@ -902,12 +899,14 @@ static void recording_step_levels(int setting_id, int steps)
global_settings.volume += steps;
setvol();
break;
+#if defined(HAVE_LINE_REC) || defined(HAVE_FMRADIO_REC)
case SOUND_LEFT_GAIN:
global_settings.rec_left_gain += steps;
break;
case SOUND_RIGHT_GAIN:
global_settings.rec_right_gain += steps;
break;
+#endif
#ifdef HAVE_MIC_REC
case SOUND_MIC_GAIN:
global_settings.rec_mic_gain += steps;
@@ -1084,7 +1083,7 @@ bool recording_screen(bool no_source)
pm_y[i] = font_get(vp_top[i].font)->height * 2;
trig_ypos[i] = font_get(vp_top[i].font)->height * 3;
if(compact_view[i])
- trig_ypos[i] -= (font_get(vp_top[i].font)->height)/2;
+ trig_ypos[i] -= (font_get(vp_top[i].font)->height)/2;
}
/* init the bottom list */
@@ -1150,46 +1149,41 @@ bool recording_screen(bool no_source)
set_gain();
update_countdown = 0; /* Update immediately */
+ int listi = 0;
+
/* populate translation table for list id -> enum */
#ifdef HAVE_SPDIF_REC
if(global_settings.rec_source == AUDIO_SRC_SPDIF)
{
- listid_to_enum[0] = ITEM_VOLUME_D;
- listid_to_enum[1] = ITEM_SAMPLERATE_D;
- listid_to_enum[2] = ITEM_FILENAME_D;
+#ifndef HAVE_RECORDING_WITHOUT_MONITORING
+ listid_to_enum[listi++] = ITEM_VOLUME;
+#endif
+ listid_to_enum[listi++] = ITEM_SAMPLERATE;
+ listid_to_enum[listi++] = ITEM_FILENAME;
- gui_synclist_set_nb_items(&lists, ITEM_COUNT_D); /* spdif */
+ gui_synclist_set_nb_items(&lists, listi); /* spdif */
}
else
#endif
- if(HAVE_MIC_REC_((global_settings.rec_source == AUDIO_SRC_MIC) || )
- (global_settings.rec_channels == 1))
{
- listid_to_enum[0] = ITEM_VOLUME_M;
- listid_to_enum[1] = ITEM_GAIN_M;
-#ifdef HAVE_AGC
- listid_to_enum[2] = ITEM_AGC_MODE_M;
- listid_to_enum[3] = ITEM_AGC_MAXDB_M;
- listid_to_enum[4] = ITEM_FILENAME_M;
-#else
- listid_to_enum[2] = ITEM_FILENAME_M;
+#ifndef HAVE_RECORDING_WITHOUT_MONITORING
+ listid_to_enum[listi++] = ITEM_VOLUME;
+#endif
+ listid_to_enum[listi++] = ITEM_GAIN;
+#if defined(HAVE_LINE_REC) || defined(HAVE_FMRADIO_REC)
+ if(HAVE_MIC_REC_((global_settings.rec_source != AUDIO_SRC_MIC) || )
+ (global_settings.rec_channels != 1)) {
+ listid_to_enum[listi++] = ITEM_GAIN_L;
+ listid_to_enum[listi++] = ITEM_GAIN_R;
+ }
#endif
- gui_synclist_set_nb_items(&lists, ITEM_COUNT_M); /* mono */
- }
- else
- {
- listid_to_enum[0] = ITEM_VOLUME;
- listid_to_enum[1] = ITEM_GAIN;
- listid_to_enum[2] = ITEM_GAIN_L;
- listid_to_enum[3] = ITEM_GAIN_R;
#ifdef HAVE_AGC
- listid_to_enum[4] = ITEM_AGC_MODE;
- listid_to_enum[5] = ITEM_AGC_MAXDB;
- listid_to_enum[6] = ITEM_FILENAME;
-#else
- listid_to_enum[4] = ITEM_FILENAME;
+ listid_to_enum[listi++] = ITEM_AGC_MODE;
+ listid_to_enum[listi++] = ITEM_AGC_MAXDB;
#endif
- gui_synclist_set_nb_items(&lists, ITEM_COUNT); /* stereo */
+ listid_to_enum[listi++] = ITEM_FILENAME;
+
+ gui_synclist_set_nb_items(&lists, listi); /* stereo */
}
gui_synclist_draw(&lists);
@@ -1266,7 +1260,7 @@ bool recording_screen(bool no_source)
}
/* let list handle the button */
- gui_synclist_do_button(&lists, &button, LIST_WRAP_UNLESS_HELD);
+ gui_synclist_do_button(&lists, &button);
switch(button)
@@ -1275,26 +1269,35 @@ bool recording_screen(bool no_source)
case ACTION_SETTINGS_INCREPEAT:
switch (listid_to_enum[gui_synclist_get_sel_pos(&lists)])
{
+#ifndef HAVE_RECORDING_WITHOUT_MONITORING
case ITEM_VOLUME:
recording_step_levels(SOUND_VOLUME, 1);
break;
+#endif
case ITEM_GAIN:
+ switch(global_settings.rec_source) {
#ifdef HAVE_MIC_REC
- if(global_settings.rec_source == AUDIO_SRC_MIC)
+ case AUDIO_SRC_MIC:
recording_step_levels(SOUND_MIC_GAIN, 1);
- else
+ break;
#endif /* MIC */
- {
+#if defined(HAVE_LINE_REC) || defined(HAVE_FMRADIO_REC)
+ HAVE_LINE_REC_(case AUDIO_SRC_LINEIN:)
+ HAVE_FMRADIO_REC_(case AUDIO_SRC_FMRADIO:)
recording_step_levels(SOUND_LEFT_GAIN, 1);
recording_step_levels(SOUND_RIGHT_GAIN, 1);
+ break;
+#endif
}
break;
+#if defined(HAVE_LINE_REC) || defined(HAVE_FMRADIO_REC)
case ITEM_GAIN_L:
recording_step_levels(SOUND_LEFT_GAIN, 1);
break;
case ITEM_GAIN_R:
recording_step_levels(SOUND_RIGHT_GAIN, 1);
break;
+#endif
#ifdef HAVE_AGC
case ITEM_AGC_MODE:
agc_preset = MIN(agc_preset + 1, AGC_MODE_SIZE);
@@ -1311,19 +1314,22 @@ bool recording_screen(bool no_source)
}
break;
case ITEM_AGC_MAXDB:
+ switch(global_settings.rec_source) {
+#if defined(HAVE_LINE_IN) || defined(HAVE_FMRADIO_IN)
+ HAVE_LINE_IN_(case AUDIO_SRC_LINEIN:)
+ HAVE_FMRADIO_IN_(case AUDIO_SRC_FMRADIO:)
+ agc_maxgain = MIN(agc_maxgain + 1,
+ sound_max(SOUND_LEFT_GAIN));
+ global_settings.rec_agc_maxgain_line = agc_maxgain;
+ break;
+#endif
#ifdef HAVE_MIC_REC
- if (global_settings.rec_source == AUDIO_SRC_MIC)
- {
+ case AUDIO_SRC_MIC:
agc_maxgain = MIN(agc_maxgain + 1,
sound_max(SOUND_MIC_GAIN));
global_settings.rec_agc_maxgain_mic = agc_maxgain;
- }
- else
+ break;
#endif /* MIC */
- {
- agc_maxgain = MIN(agc_maxgain + 1,
- sound_max(SOUND_LEFT_GAIN));
- global_settings.rec_agc_maxgain_line = agc_maxgain;
}
break;
#endif /* HAVE_AGC */
@@ -1335,26 +1341,35 @@ bool recording_screen(bool no_source)
case ACTION_SETTINGS_DECREPEAT:
switch (listid_to_enum[gui_synclist_get_sel_pos(&lists)])
{
+#ifndef HAVE_RECORDING_WITHOUT_MONITORING
case ITEM_VOLUME:
recording_step_levels(SOUND_VOLUME, -1);
break;
+#endif
case ITEM_GAIN:
+ switch(global_settings.rec_source) {
#ifdef HAVE_MIC_REC
- if(global_settings.rec_source == AUDIO_SRC_MIC)
+ case AUDIO_SRC_MIC:
recording_step_levels(SOUND_MIC_GAIN, -1);
- else
+ break;
#endif /* MIC */
- {
+#if defined(HAVE_LINE_REC) || defined(HAVE_FMRADIO_REC)
+ HAVE_LINE_REC_(case AUDIO_SRC_LINEIN:)
+ HAVE_FMRADIO_REC_(case AUDIO_SRC_FMRADIO:)
recording_step_levels(SOUND_LEFT_GAIN, -1);
recording_step_levels(SOUND_RIGHT_GAIN, -1);
+ break;
+#endif
}
break;
+#if defined(HAVE_LINE_REC) || defined(HAVE_FMRADIO_REC)
case ITEM_GAIN_L:
recording_step_levels(SOUND_LEFT_GAIN, -1);
break;
case ITEM_GAIN_R:
recording_step_levels(SOUND_RIGHT_GAIN, -1);
break;
+#endif
#ifdef HAVE_AGC
case ITEM_AGC_MODE:
agc_preset = MAX(agc_preset - 1, 0);
@@ -1371,18 +1386,22 @@ bool recording_screen(bool no_source)
}
break;
case ITEM_AGC_MAXDB:
+ switch(global_settings.rec_source) {
+#if defined(HAVE_LINE_IN) || defined(HAVE_FMRADIO_IN)
+ HAVE_LINE_IN_(case AUDIO_SRC_LINEIN:)
+ HAVE_FMRADIO_IN_(case AUDIO_SRC_FMRADIO:)
+ agc_maxgain = MAX(agc_maxgain - 1,
+ sound_min(SOUND_LEFT_GAIN));
+ global_settings.rec_agc_maxgain_line = agc_maxgain;
+ break;
+#endif
#ifdef HAVE_MIC_REC
- if (global_settings.rec_source == AUDIO_SRC_MIC)
- {
+ case AUDIO_SRC_MIC:
agc_maxgain = MAX(agc_maxgain - 1,
sound_min(SOUND_MIC_GAIN));
global_settings.rec_agc_maxgain_mic = agc_maxgain;
- } else
+ break;
#endif /* MIC */
- {
- agc_maxgain = MAX(agc_maxgain - 1,
- sound_min(SOUND_LEFT_GAIN));
- global_settings.rec_agc_maxgain_line = agc_maxgain;
}
break;
#endif /* HAVE_AGC */
@@ -1511,7 +1530,8 @@ bool recording_screen(bool no_source)
break;
case SYS_POWEROFF:
- default_event_handler(SYS_POWEROFF);
+ case SYS_REBOOT:
+ default_event_handler(button);
done = true;
break;
@@ -1803,8 +1823,10 @@ rec_abort:
rec_status &= ~RCSTAT_IN_RECSCREEN;
sound_settings_apply();
- FOR_NB_SCREENS(i)
+ FOR_NB_SCREENS(i) {
+ screens[i].set_viewport(NULL);
screens[i].setfont(FONT_UI);
+ }
/* if the directory was created or recording happened, make sure the
browser is updated */
diff --git a/apps/recorder/resize.c b/apps/recorder/resize.c
index 6f561039c3..c16cec9dc9 100644
--- a/apps/recorder/resize.c
+++ b/apps/recorder/resize.c
@@ -593,7 +593,7 @@ static inline bool scale_v_linear(struct rowset *rset,
static void output_row_32_native_fromyuv(uint32_t row, void * row_in,
struct scaler_context *ctx)
{
-#if defined(LCD_STRIDEFORMAT) && LCD_STRIDEFORMAT == VERTICAL_STRIDE
+#if LCD_STRIDEFORMAT == VERTICAL_STRIDE
#define DEST_STEP (ctx->bm->height)
#define Y_STEP (1)
#else
diff --git a/apps/root_menu.c b/apps/root_menu.c
index 62014b619d..e606d5cd68 100644
--- a/apps/root_menu.c
+++ b/apps/root_menu.c
@@ -107,7 +107,7 @@ static void rootmenu_track_changed_callback(unsigned short id, void* param)
{
(void)id;
struct mp3entry *id3 = ((struct track_event *)param)->id3;
- strlcpy(current_track_path, id3->path, MAX_PATH);
+ strmemccpy(current_track_path, id3->path, MAX_PATH);
}
static int browser(void* param)
{
@@ -115,14 +115,13 @@ static int browser(void* param)
#ifdef HAVE_TAGCACHE
struct tree_context* tc = tree_get_context();
#endif
- struct browse_context browse;
int filter = SHOW_SUPPORTED;
char folder[MAX_PATH] = "/";
/* stuff needed to remember position in file browser */
static char last_folder[MAX_PATH] = "/";
/* and stuff for the database browser */
#ifdef HAVE_TAGCACHE
- static int last_db_dirlevel = 0, last_db_selection = 0;
+ static int last_db_dirlevel = 0, last_db_selection = 0, last_ft_dirlevel = 0;
#endif
switch ((intptr_t)param)
@@ -166,7 +165,7 @@ static int browser(void* param)
}
}
if (!in_hotswap)
-#endif
+#endif /*HAVE_HOTSWAP*/
strcpy(folder, last_folder);
}
push_current_activity(ACTIVITY_FILEBROWSER);
@@ -241,15 +240,19 @@ static int browser(void* param)
{
if (lang_is_rtl())
{
- splashf(0, "[%d/%d] %s", stat->commit_step,
- tagcache_get_max_commit_step(),
- str(LANG_TAGCACHE_INIT));
+ splash_progress(stat->commit_step,
+ tagcache_get_max_commit_step(),
+ "[%d/%d] %s", stat->commit_step,
+ tagcache_get_max_commit_step(),
+ str(LANG_TAGCACHE_INIT));
}
else
{
- splashf(0, "%s [%d/%d]", str(LANG_TAGCACHE_INIT),
- stat->commit_step,
- tagcache_get_max_commit_step());
+ splash_progress(stat->commit_step,
+ tagcache_get_max_commit_step(),
+ "%s [%d/%d]", str(LANG_TAGCACHE_INIT),
+ stat->commit_step,
+ tagcache_get_max_commit_step());
}
}
else
@@ -262,16 +265,29 @@ static int browser(void* param)
if (!tagcache_is_usable())
return GO_TO_PREVIOUS;
filter = SHOW_ID3DB;
+ last_ft_dirlevel = tc->dirlevel;
tc->dirlevel = last_db_dirlevel;
tc->selected_item = last_db_selection;
push_current_activity(ACTIVITY_DATABASEBROWSER);
break;
-#endif
+#endif /*HAVE_TAGCACHE*/
}
- browse_context_init(&browse, filter, 0, NULL, NOICON, folder, NULL);
+ struct browse_context browse = {
+ .dirfilter = filter,
+ .icon = Icon_NOICON,
+ .root = folder,
+ };
+
ret_val = rockbox_browse(&browse);
- pop_current_activity();
+
+ if (ret_val == GO_TO_WPS
+ || ret_val == GO_TO_PREVIOUS_MUSIC
+ || ret_val == GO_TO_PLUGIN)
+ pop_current_activity_without_refresh();
+ else
+ pop_current_activity();
+
switch ((intptr_t)param)
{
case GO_TO_FILEBROWSER:
@@ -287,6 +303,7 @@ static int browser(void* param)
case GO_TO_DBBROWSER:
last_db_dirlevel = tc->dirlevel;
last_db_selection = tc->selected_item;
+ tc->dirlevel = last_ft_dirlevel;
break;
#endif
}
@@ -313,7 +330,7 @@ static int wpsscrn(void* param)
}
else if ( global_status.resume_index != -1 )
{
- DEBUGF("Resume index %X crc32 %lX offset %lX\n",
+ DEBUGF("Resume index %d crc32 %lX offset %lX\n",
global_status.resume_index,
(unsigned long)global_status.resume_crc32,
(unsigned long)global_status.resume_offset);
@@ -330,7 +347,23 @@ static int wpsscrn(void* param)
{
splash(HZ*2, ID2P(LANG_NOTHING_TO_RESUME));
}
- pop_current_activity();
+
+ if (ret_val == GO_TO_PLAYLIST_VIEWER
+ || ret_val == GO_TO_PLUGIN
+ || ret_val == GO_TO_WPS
+ || ret_val == GO_TO_PREVIOUS_MUSIC
+ || ret_val == GO_TO_PREVIOUS_BROWSER
+ || (ret_val == GO_TO_PREVIOUS
+ && (last_screen == GO_TO_MAINMENU /* Settings */
+ || last_screen == GO_TO_BROWSEPLUGINS
+ || last_screen == GO_TO_SYSTEM_SCREEN
+ || last_screen == GO_TO_PLAYLISTS_SCREEN)))
+ {
+ pop_current_activity_without_refresh();
+ }
+ else
+ pop_current_activity();
+
return ret_val;
}
#if CONFIG_TUNER
@@ -351,6 +384,7 @@ static int miscscrn(void * param)
case GO_TO_PLUGIN:
case GO_TO_PLAYLIST_VIEWER:
case GO_TO_WPS:
+ case GO_TO_PREVIOUS_MUSIC:
return result;
default:
return GO_TO_ROOT;
@@ -360,15 +394,17 @@ static int miscscrn(void * param)
static int playlist_view_catalog(void * param)
{
- /* kludge untill catalog_view_playlists() returns something useful */
- int old_playstatus = audio_status();
(void)param;
push_current_activity(ACTIVITY_PLAYLISTBROWSER);
- catalog_view_playlists();
- pop_current_activity();
- if (!old_playstatus && audio_status())
+ bool item_was_selected = catalog_view_playlists();
+
+ if (item_was_selected)
+ {
+ pop_current_activity_without_refresh();
return GO_TO_WPS;
- return GO_TO_PREVIOUS;
+ }
+ pop_current_activity();
+ return GO_TO_ROOT;
}
static int playlist_view(void * param)
@@ -376,9 +412,7 @@ static int playlist_view(void * param)
(void)param;
int val;
- push_current_activity(ACTIVITY_PLAYLISTVIEWER);
val = playlist_viewer();
- pop_current_activity();
switch (val)
{
case PLAYLIST_VIEWER_MAINMENU:
@@ -634,6 +668,7 @@ static int item_callback(int action,
}
return action;
}
+
static int get_selection(int last_screen)
{
int i;
@@ -676,13 +711,25 @@ static inline int load_screen(int screen)
ret_val = items[screen].function(items[screen].param);
if (activity != ACTIVITY_UNKNOWN)
- pop_current_activity();
+ {
+ if (ret_val == GO_TO_PLUGIN
+ || ret_val == GO_TO_WPS
+ || ret_val == GO_TO_PREVIOUS_MUSIC
+ || ret_val == GO_TO_PREVIOUS_BROWSER
+ || ret_val == GO_TO_FILEBROWSER)
+ {
+ pop_current_activity_without_refresh();
+ }
+ else
+ pop_current_activity();
+ }
last_screen = screen;
if (ret_val == GO_TO_PREVIOUS)
last_screen = old_previous;
return ret_val;
}
+
static int load_context_screen(int selection)
{
const struct menu_item_ex *context_menu = NULL;
@@ -705,83 +752,162 @@ static int load_context_screen(int selection)
return retval;
}
-static int load_plugin_screen(char *plug_path, void* plug_param)
+static int load_plugin_screen(char *key)
{
- int ret_val;
+ int ret_val = PLUGIN_ERROR;
+ int loops = 100;
int old_previous = last_screen;
+ int old_global = global_status.last_screen;
last_screen = next_screen;
global_status.last_screen = (char)next_screen;
- status_save();
+ /*status_save(); //only needed if we crash */
- switch (plugin_load(plug_path, plug_param))
+ while(loops-- > 0) /* just to keep things from getting out of hand */
{
- case PLUGIN_GOTO_WPS:
- ret_val = GO_TO_WPS;
- break;
- case PLUGIN_GOTO_PLUGIN:
- ret_val = GO_TO_PLUGIN;
- break;
- case PLUGIN_OK:
- ret_val = audio_status() ? GO_TO_PREVIOUS : GO_TO_ROOT;
- break;
- default:
- ret_val = GO_TO_PREVIOUS;
- break;
- }
+ int opret = open_plugin_load_entry(key);
+ struct open_plugin_entry_t *op_entry = open_plugin_get_entry();
+ char *path = op_entry->path;
+ char *param = op_entry->param;
+ if (param[0] == '\0')
+ param = NULL;
+ if (path[0] == '\0' && key)
+ path = P2STR((unsigned char *)key);
+ int ret = plugin_load(path, param);
+
+ if (ret == PLUGIN_USB_CONNECTED || ret == PLUGIN_ERROR)
+ ret_val = GO_TO_ROOT;
+ else if (ret == PLUGIN_GOTO_WPS)
+ ret_val = GO_TO_WPS;
+ else if (ret == PLUGIN_GOTO_PLUGIN)
+ {
+ if (key == (char*)LANG_SHORTCUTS && op_entry->lang_id == LANG_OPEN_PLUGIN)
+ {
+ op_entry->lang_id = LANG_SHORTCUTS;
+ }
+ continue;
+ }
+ else
+ {
+ ret_val = GO_TO_PREVIOUS;
+ /* Prevents infinite loop with WPS, Plugins, Previous Screen*/
+ if (ret == PLUGIN_OK && old_global == GO_TO_WPS && !audio_status())
+ ret_val = GO_TO_ROOT;
+ last_screen = (old_previous == next_screen || old_global == GO_TO_ROOT)
+ ? GO_TO_ROOT : old_previous;
+ if (last_screen == GO_TO_ROOT)
+ global_status.last_screen = GO_TO_ROOT;
+ }
+ /* ret_val != GO_TO_PLUGIN */
- if (ret_val == GO_TO_PREVIOUS)
- last_screen = (old_previous == next_screen) ? GO_TO_ROOT : old_previous;
+ if (opret != OPEN_PLUGIN_NEEDS_FLUSHED || last_screen != GO_TO_WPS)
+ {
+ /* Keep the entry in case of GO_TO_PREVIOUS */
+ op_entry->hash = 0; /*remove hash -- prevents flush to disk */
+ op_entry->lang_id = LANG_PREVIOUS_SCREEN;
+ /*open_plugin_add_path(NULL, NULL, NULL);// clear entry */
+ }
+ break;
+ } /*while */
return ret_val;
}
-void root_menu(void)
+static void ignore_back_button_stub(bool ignore)
{
- int previous_browser = GO_TO_FILEBROWSER;
- int selected = 0;
- int shortcut_origin = GO_TO_ROOT;
-
- push_current_activity(ACTIVITY_MAINMENU);
+#if (CONFIG_PLATFORM&PLATFORM_ANDROID)
+ /* BACK button to be handled by Android instead of rockbox */
+ android_ignore_back_button(ignore);
+#else
+ (void) ignore;
+#endif
+}
+static int root_menu_setup_screens(void)
+{
+ int new_screen = next_screen;
if (global_settings.start_in_screen == 0)
- next_screen = (int)global_status.last_screen;
- else next_screen = global_settings.start_in_screen - 2;
+ new_screen = (int)global_status.last_screen;
+ else new_screen = global_settings.start_in_screen - 2;
+ if (new_screen == GO_TO_PLUGIN)
+ {
+ if (global_status.last_screen == GO_TO_SHORTCUTMENU)
+ {
+ /* Can make this any value other than GO_TO_SHORTCUTMENU
+ otherwise it takes over on startup when the user wanted
+ the plugin at key - LANG_START_SCREEN */
+ global_status.last_screen = GO_TO_PLUGIN;
+ }
+ if(global_status.last_screen == GO_TO_SHORTCUTMENU ||
+ global_status.last_screen == GO_TO_PLUGIN)
+ {
+ if (global_settings.start_in_screen == 0)
+ { /* Start in: Previous Screen */
+ last_screen = GO_TO_PREVIOUS;
+ global_status.last_screen = GO_TO_ROOT;
+ /* since the plugin has GO_TO_PLUGIN as origin it
+ will just return GO_TO_PREVIOUS <=> GO_TO_PLUGIN in a loop
+ To allow exit after restart we check for GO_TO_ROOT
+ if so exit to ROOT after the plugin exits */
+ }
+ }
+ }
#if CONFIG_TUNER
add_event(PLAYBACK_EVENT_START_PLAYBACK, rootmenu_start_playback_callback);
#endif
add_event(PLAYBACK_EVENT_TRACK_CHANGE, rootmenu_track_changed_callback);
#ifdef HAVE_RTC_ALARM
+ int alarm_wake_up_screen = 0;
if ( rtc_check_alarm_started(true) )
{
rtc_enable_alarm(false);
- next_screen = GO_TO_WPS;
+
+#if (defined(HAVE_RECORDING) || CONFIG_TUNER)
+ alarm_wake_up_screen = global_settings.alarm_wake_up_screen;
+#endif
+ switch (alarm_wake_up_screen)
+ {
#if CONFIG_TUNER
- if (global_settings.alarm_wake_up_screen == ALARM_START_FM)
- next_screen = GO_TO_FM;
+ case ALARM_START_FM:
+ new_screen = GO_TO_FM;
+ break;
#endif
#ifdef HAVE_RECORDING
- if (global_settings.alarm_wake_up_screen == ALARM_START_REC)
- {
- recording_start_automatic = true;
- next_screen = GO_TO_RECSCREEN;
- }
+ case ALARM_START_REC:
+ recording_start_automatic = true;
+ new_screen = GO_TO_RECSCREEN;
+ break;
#endif
+ default:
+ new_screen = GO_TO_WPS;
+ break;
+ } /* switch() */
}
#endif /* HAVE_RTC_ALARM */
#if defined(HAVE_HEADPHONE_DETECTION) || defined(HAVE_LINEOUT_DETECTION)
- if (next_screen == GO_TO_WPS && global_settings.unplug_autoresume)
+ if (new_screen == GO_TO_WPS && global_settings.unplug_autoresume)
{
- next_screen = GO_TO_ROOT;
+ new_screen = GO_TO_ROOT;
#ifdef HAVE_HEADPHONE_DETECTION
if (headphones_inserted())
- next_screen = GO_TO_WPS;
+ new_screen = GO_TO_WPS;
#endif
#ifdef HAVE_LINEOUT_DETECTION
if (lineout_inserted())
- next_screen = GO_TO_WPS;
+ new_screen = GO_TO_WPS;
#endif
}
#endif /*(HAVE_HEADPHONE_DETECTION) || (HAVE_LINEOUT_DETECTION)*/
+ return new_screen;
+}
+
+void root_menu(void)
+{
+ int previous_browser = GO_TO_FILEBROWSER;
+ int selected = 0;
+ int shortcut_origin = GO_TO_ROOT;
+
+ push_current_activity(ACTIVITY_MAINMENU);
+ next_screen = root_menu_setup_screens();
while (true)
{
@@ -793,22 +919,42 @@ void root_menu(void)
case GO_TO_ROOT:
if (last_screen != GO_TO_ROOT)
selected = get_selection(last_screen);
-#if (CONFIG_PLATFORM&PLATFORM_ANDROID)
+ global_status.last_screen = GO_TO_ROOT; /* We've returned to ROOT */
/* When we are in the main menu we want the hardware BACK
- * button to be handled by Android instead of rockbox */
- android_ignore_back_button(true);
-#endif
+ * button to be handled by HOST instead of rockbox */
+ ignore_back_button_stub(true);
+
next_screen = do_menu(&root_menu_, &selected, NULL, false);
-#if (CONFIG_PLATFORM&PLATFORM_ANDROID)
- android_ignore_back_button(false);
-#endif
+
+ ignore_back_button_stub(false);
+
if (next_screen != GO_TO_PREVIOUS)
last_screen = GO_TO_ROOT;
break;
+#ifdef HAVE_TAGCACHE
+ case GO_TO_FILEBROWSER:
+ case GO_TO_DBBROWSER:
+ previous_browser = next_screen;
+ goto load_next_screen;
+ break;
+#endif /* With !HAVE_TAGCACHE previous_browser is always GO_TO_FILEBROWSER */
+#if CONFIG_TUNER
+ case GO_TO_WPS:
+ case GO_TO_FM:
+ previous_music = next_screen;
+ goto load_next_screen;
+ break;
+#endif /* With !CONFIG_TUNER previous_music is always GO_TO_WPS */
case GO_TO_PREVIOUS:
+ {
next_screen = last_screen;
+ if (last_screen == GO_TO_PLUGIN)/* for WPS */
+ last_screen = GO_TO_PREVIOUS;
+ else if (last_screen == GO_TO_PREVIOUS)
+ next_screen = GO_TO_ROOT;
break;
+ }
case GO_TO_PREVIOUS_BROWSER:
next_screen = previous_browser;
@@ -822,11 +968,14 @@ void root_menu(void)
break;
case GO_TO_PLUGIN:
{
+
char *key;
if (global_status.last_screen == GO_TO_SHORTCUTMENU)
{
+ struct open_plugin_entry_t *op_entry = open_plugin_get_entry();
+ if (op_entry->lang_id == LANG_OPEN_PLUGIN)
+ op_entry->lang_id = LANG_SHORTCUTS;
shortcut_origin = last_screen;
- global_status.last_screen = last_screen;
key = ID2P(LANG_SHORTCUTS);
}
else
@@ -842,48 +991,55 @@ void root_menu(void)
case GO_TO_SHORTCUTMENU:
key = ID2P(LANG_SHORTCUTS);
break;
+ case GO_TO_PREVIOUS:
+ key = ID2P(LANG_PREVIOUS_SCREEN);
+ break;
default:
key = ID2P(LANG_OPEN_PLUGIN);
break;
}
}
- open_plugin_get_entry(key, &open_plugin_entry);
- char *path = open_plugin_entry.path;
- char *param = open_plugin_entry.param;
- if (param[0] == '\0')
- param = NULL;
- next_screen = load_plugin_screen(path, param);
+ push_activity_without_refresh(ACTIVITY_UNKNOWN); /* prevent plugin_load */
+ next_screen = load_plugin_screen(key); /* from flashing root */
+ pop_current_activity_without_refresh(); /* menu activity */
- if (next_screen != GO_TO_PLUGIN)
- open_plugin_add_path(NULL, NULL, NULL);
-
- /* shortcuts may take several trips through the GO_TO_PLUGIN case
- make sure we preserve and restore the origin */
- if (next_screen == GO_TO_PREVIOUS && shortcut_origin != GO_TO_ROOT)
+ if (next_screen == GO_TO_PREVIOUS)
{
- if (shortcut_origin != GO_TO_WPS)
- next_screen = shortcut_origin;
- shortcut_origin = GO_TO_ROOT;
+ /* shortcuts may take several trips through the GO_TO_PLUGIN
+ case make sure we preserve and restore the origin */
+ if(tree_get_context()->out_of_tree > 0) /* a shortcut has been selected */
+ {
+ next_screen = GO_TO_FILEBROWSER;
+ shortcut_origin = GO_TO_ROOT;
+ /* note in some cases there is a screen to return to
+ but the history is rewritten as if you browsed here
+ from the root so return there when finished */
+ }
+ else if (shortcut_origin != GO_TO_ROOT)
+ {
+ if (shortcut_origin != GO_TO_WPS)
+ next_screen = shortcut_origin;
+ shortcut_origin = GO_TO_ROOT;
+ }
+ /* skip GO_TO_PREVIOUS */
+ if (last_screen == GO_TO_BROWSEPLUGINS)
+ {
+ next_screen = last_screen;
+ last_screen = GO_TO_PLUGIN;
+ }
}
-
previous_browser = (next_screen != GO_TO_WPS) ? GO_TO_FILEBROWSER : GO_TO_PLUGIN;
break;
}
default:
-#ifdef HAVE_TAGCACHE
-/* With !HAVE_TAGCACHE previous_browser is always GO_TO_FILEBROWSER */
- if (next_screen == GO_TO_FILEBROWSER || next_screen == GO_TO_DBBROWSER)
- previous_browser = next_screen;
-#endif
-#if CONFIG_TUNER
-/* With !CONFIG_TUNER previous_music is always GO_TO_WPS */
- if (next_screen == GO_TO_WPS || next_screen == GO_TO_FM)
- previous_music = next_screen;
-#endif
- next_screen = load_screen(next_screen);
+ goto load_next_screen;
break;
} /* switch() */
+ continue;
+load_next_screen: /* load_screen is inlined */
+ next_screen = load_screen(next_screen);
}
+
}
diff --git a/apps/screen_access.c b/apps/screen_access.c
index a7b902918c..1909ef277c 100644
--- a/apps/screen_access.c
+++ b/apps/screen_access.c
@@ -217,6 +217,7 @@ struct screen screens[NB_SCREENS] =
.putsxy=&lcd_putsxy,
.puts=&lcd_puts,
.putsf=&lcd_putsf,
+ .putsxyf=&lcd_putsxyf,
.puts_scroll=&lcd_puts_scroll,
.putsxy_scroll_func=&lcd_putsxy_scroll_func,
.scroll_speed=&lcd_scroll_speed,
@@ -305,6 +306,7 @@ struct screen screens[NB_SCREENS] =
.putsxy=&lcd_remote_putsxy,
.puts=&lcd_remote_puts,
.putsf=&lcd_remote_putsf,
+ .putsxyf=&lcd_remote_putsxyf,
.puts_scroll=&lcd_remote_puts_scroll,
.putsxy_scroll_func=&lcd_remote_putsxy_scroll_func,
.scroll_speed=&lcd_remote_scroll_speed,
diff --git a/apps/screen_access.h b/apps/screen_access.h
index 94c0a19670..3e24306636 100644
--- a/apps/screen_access.h
+++ b/apps/screen_access.h
@@ -109,6 +109,7 @@ struct screen
void (*putsxy)(int x, int y, const unsigned char *str);
void (*puts)(int x, int y, const unsigned char *str);
void (*putsf)(int x, int y, const unsigned char *str, ...);
+ void (*putsxyf)(int x, int y, const unsigned char *fmt, ...);
bool (*puts_scroll)(int x, int y, const unsigned char *string);
bool (*putsxy_scroll_func)(int x, int y, const unsigned char *string,
void (*scroll_func)(struct scrollinfo *),
diff --git a/apps/screens.c b/apps/screens.c
index fa2f9f9927..c11e052bce 100644
--- a/apps/screens.c
+++ b/apps/screens.c
@@ -20,7 +20,7 @@
****************************************************************************/
#include <stdbool.h>
-#include <string.h>
+#include <string-extra.h>
#include <stdio.h>
#include <stdlib.h>
#include "backlight.h"
@@ -40,7 +40,6 @@
#include "powermgmt.h"
#include "talk.h"
#include "misc.h"
-#include "metadata.h"
#include "screens.h"
#include "debug.h"
#include "led.h"
@@ -124,7 +123,7 @@ static void say_time(int cursorpos, const struct tm *tm)
#define OFF_YEAR 9
#define OFF_DAY 14
-bool set_time_screen(const char* title, struct tm *tm)
+bool set_time_screen(const char* title, struct tm *tm, bool set_date)
{
struct viewport viewports[NB_SCREENS];
bool done = false, usb = false;
@@ -140,6 +139,10 @@ bool set_time_screen(const char* title, struct tm *tm)
offsets_ptr[IDX_DAY] = OFF_YEAR;
}
+ int last_item = IDX_DAY; /*time & date*/
+ if (!set_date)
+ last_item = IDX_SECONDS; /*time*/
+
/* speak selection when screen is entered */
say_time(cursorpos, tm);
@@ -162,6 +165,7 @@ bool set_time_screen(const char* title, struct tm *tm)
unsigned char buffer[20];
#endif
int *valptr = NULL;
+
static unsigned char daysinmonth[] =
{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
@@ -229,12 +233,10 @@ bool set_time_screen(const char* title, struct tm *tm)
else
prev_line_height = 0;
- screen->getstringsize(SEPARATOR, &separator_width, NULL);
-
/* weekday */
- screen->getstringsize(str(LANG_WEEKDAY_SUNDAY + tm->tm_wday),
- &weekday_width, NULL);
- screen->getstringsize(" ", &separator_width, NULL);
+ weekday_width = screen->getstringsize(str(LANG_WEEKDAY_SUNDAY + tm->tm_wday),
+ NULL, NULL);
+ separator_width = screen->getstringsize(SEPARATOR, NULL, NULL);
for(i=0, j=0; i < 6; i++)
{
@@ -243,7 +245,7 @@ bool set_time_screen(const char* title, struct tm *tm)
j = weekday_width + separator_width;
prev_line_height *= 2;
}
- screen->getstringsize(ptr[i], &width, NULL);
+ width = screen->getstringsize(ptr[i], NULL, NULL);
cursor[i][INDEX_Y] = prev_line_height;
cursor[i][INDEX_X] = j;
j += width + separator_width;
@@ -321,11 +323,11 @@ bool set_time_screen(const char* title, struct tm *tm)
button = get_action(CONTEXT_SETTINGS_TIME, TIMEOUT_BLOCK);
switch ( button ) {
case ACTION_STD_PREV:
- cursorpos = clamp_value_wrap(--cursorpos, 5, 0);
+ cursorpos = clamp_value_wrap(--cursorpos, last_item, 0);
say_time(cursorpos, tm);
break;
case ACTION_STD_NEXT:
- cursorpos = clamp_value_wrap(++cursorpos, 5, 0);
+ cursorpos = clamp_value_wrap(++cursorpos, last_item, 0);
say_time(cursorpos, tm);
break;
case ACTION_SETTINGS_INC:
@@ -365,6 +367,7 @@ bool set_time_screen(const char* title, struct tm *tm)
static const int id3_headers[]=
{
+ LANG_TAGNAVI_ALL_TRACKS,
LANG_ID3_TITLE,
LANG_ID3_ARTIST,
LANG_ID3_COMPOSER,
@@ -378,17 +381,25 @@ static const int id3_headers[]=
LANG_ID3_YEAR,
LANG_ID3_LENGTH,
LANG_ID3_PLAYLIST,
+ LANG_FORMAT,
LANG_ID3_BITRATE,
LANG_ID3_FREQUENCY,
LANG_ID3_TRACK_GAIN,
LANG_ID3_ALBUM_GAIN,
LANG_FILESIZE,
LANG_ID3_PATH,
+ LANG_DATE,
+ LANG_TIME,
};
struct id3view_info {
struct mp3entry* id3;
+ struct tm *modified;
+ int track_ct;
int count;
+ struct playlist_info *playlist;
+ int playlist_display_index;
+ int playlist_amount;
int info_id[ARRAYLEN(id3_headers)];
};
@@ -483,13 +494,19 @@ static const char * id3_get_or_speak_info(int selected_item, void* data,
{
struct id3view_info *info = (struct id3view_info*)data;
struct mp3entry* id3 =info->id3;
+ const unsigned char * const *unit;
+ unsigned int unit_ct;
+ unsigned long length;
+ bool pl_modified;
+ struct tm *tm = info->modified;
int info_no=selected_item/2;
if(!(selected_item%2))
{/* header */
if(say_it)
talk_id(id3_headers[info->info_id[info_no]], false);
snprintf(buffer, buffer_len,
- "[%s]", str(id3_headers[info->info_id[info_no]]));
+ info->info_id[info_no] > 0 ? "[%s]" : "%s",
+ str(id3_headers[info->info_id[info_no]]));
return buffer;
}
else
@@ -498,6 +515,14 @@ static const char * id3_get_or_speak_info(int selected_item, void* data,
char * val=NULL;
switch(id3_headers[info->info_id[info_no]])
{
+ case LANG_TAGNAVI_ALL_TRACKS:
+ if (info->track_ct <= 1)
+ return NULL;
+ snprintf(buffer, buffer_len, "%d", info->track_ct);
+ val = buffer;
+ if(say_it)
+ talk_number(info->track_ct, true);
+ break;
case LANG_ID3_TITLE:
val=id3->title;
if(say_it && val)
@@ -556,7 +581,9 @@ static const char * id3_get_or_speak_info(int selected_item, void* data,
case LANG_ID3_COMMENT:
if (!id3->comment)
return NULL;
- snprintf(buffer, buffer_len, "%s", id3->comment);
+
+ strmemccpy(buffer, id3->comment, buffer_len);
+
val=buffer;
if(say_it && val)
talk_spell(val, true);
@@ -582,23 +609,65 @@ static const char * id3_get_or_speak_info(int selected_item, void* data,
}
break;
case LANG_ID3_LENGTH:
- format_time(buffer, buffer_len, id3->length);
+ length = info->track_ct > 1 ? id3->length : id3->length / 1000;
+
+ format_time_auto(buffer, buffer_len,
+ length, UNIT_SEC | UNIT_TRIM_ZERO, true);
val=buffer;
if(say_it)
- talk_value(id3->length /1000, UNIT_TIME, true);
+ talk_value(length, UNIT_TIME, true);
break;
case LANG_ID3_PLAYLIST:
- snprintf(buffer, buffer_len, "%d/%d",
- playlist_get_display_index(), playlist_amount());
- val=buffer;
+ if (info->playlist_display_index == 0 || info->playlist_amount == 0 )
+ return NULL;
+
+ pl_modified = playlist_modified(info->playlist);
+
+ snprintf(buffer, buffer_len, "%d/%d%s",
+ info->playlist_display_index, info->playlist_amount,
+ pl_modified ? "* " :" ");
+ val = buffer;
+ size_t prefix_len = strlen(buffer);
+ buffer += prefix_len;
+ buffer_len -= prefix_len;
+
+ if (info->playlist)
+ playlist_name(info->playlist, buffer, buffer_len);
+ else
+ {
+ if (playlist_allow_dirplay(NULL))
+ strmemccpy(buffer, "(Folder)", buffer_len);
+ else if (playlist_dynamic_only())
+ strmemccpy(buffer, "(Dynamic)", buffer_len);
+ else
+ playlist_name(NULL, buffer, buffer_len);
+ }
+
if(say_it)
{
- talk_number(playlist_get_display_index(), true);
+ talk_number(info->playlist_display_index, true);
talk_id(VOICE_OF, true);
- talk_number(playlist_amount(), true);
+ talk_number(info->playlist_amount, true);
+
+ if (pl_modified)
+ talk_spell("Modified", true);
+ if (buffer) /* playlist name */
+ talk_spell(buffer, true);
}
break;
+ case LANG_FORMAT:
+ if (id3->codectype >= AFMT_NUM_CODECS)
+ return NULL;
+
+ strmemccpy(buffer, audio_formats[id3->codectype].label, buffer_len);
+
+ val=buffer;
+ if(say_it)
+ talk_spell(val, true);
+ break;
case LANG_ID3_BITRATE:
+ if (!id3->bitrate)
+ return NULL;
snprintf(buffer, buffer_len, "%d kbps%s", id3->bitrate,
id3->vbr ? str(LANG_ID3_VBR) : (const unsigned char*) "");
val=buffer;
@@ -610,6 +679,8 @@ static const char * id3_get_or_speak_info(int selected_item, void* data,
}
break;
case LANG_ID3_FREQUENCY:
+ if (!id3->frequency)
+ return NULL;
snprintf(buffer, buffer_len, "%ld Hz", id3->frequency);
val=buffer;
if(say_it)
@@ -638,10 +709,44 @@ static const char * id3_get_or_speak_info(int selected_item, void* data,
talk_spell(val, true);
break;
case LANG_FILESIZE: /* not LANG_ID3_FILESIZE because the string is shared */
- output_dyn_value(buffer, buffer_len, id3->filesize, byte_units, 4, true);
+ if (!id3->filesize)
+ return NULL;
+ if (info->track_ct > 1)
+ {
+ unit = kibyte_units;
+ unit_ct = 3;
+ }
+ else
+ {
+ unit = byte_units;
+ unit_ct = 4;
+ }
+ output_dyn_value(buffer, buffer_len, id3->filesize, unit, unit_ct, true);
val=buffer;
if(say_it && val)
- output_dyn_value(NULL, 0, id3->filesize, byte_units, 4, true);
+ output_dyn_value(NULL, 0, id3->filesize, unit, unit_ct, true);
+ break;
+ case LANG_DATE:
+ if (!tm)
+ return NULL;
+
+ snprintf(buffer, buffer_len, "%04d/%02d/%02d",
+ tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday);
+
+ val = buffer;
+ if (say_it)
+ talk_date(tm, true);
+ break;
+ case LANG_TIME:
+ if (!tm)
+ return NULL;
+
+ snprintf(buffer, buffer_len, "%02d:%02d:%02d",
+ tm->tm_hour, tm->tm_min, tm->tm_sec);
+
+ val = buffer;
+ if (say_it)
+ talk_time(tm, true);
break;
}
if((!val || !*val) && say_it)
@@ -651,11 +756,11 @@ static const char * id3_get_or_speak_info(int selected_item, void* data,
}
/* gui_synclist callback */
-static const char* id3_get_info(int selected_item, void* data,
- char *buffer, size_t buffer_len)
+static const char* id3_get_name_cb(int selected_item, void* data,
+ char *buffer, size_t buffer_len)
{
return id3_get_or_speak_info(selected_item, data, buffer,
- buffer_len, false);
+ buffer_len, false) ? : "";
}
static int id3_speak_item(int selected_item, void* data)
@@ -669,34 +774,48 @@ static int id3_speak_item(int selected_item, void* data)
return 0;
}
-bool browse_id3(void)
+/* Note: If track_ct > 1, filesize value will be treated as
+ * KiB (instead of Bytes), and length as s instead of ms.
+ */
+bool browse_id3_ex(struct mp3entry *id3, struct playlist_info *playlist,
+ int playlist_display_index, int playlist_amount,
+ struct tm *modified, int track_ct)
{
struct gui_synclist id3_lists;
- struct mp3entry* id3 = audio_current_track();
int key;
unsigned int i;
struct id3view_info info;
- info.count = 0;
info.id3 = id3;
+ info.modified = modified;
+ info.track_ct = track_ct;
+ info.playlist = playlist;
+ info.playlist_amount = playlist_amount;
bool ret = false;
- push_current_activity(ACTIVITY_ID3SCREEN);
+ int curr_activity = get_current_activity();
+ bool is_curr_track_info = curr_activity != ACTIVITY_PLUGIN &&
+ curr_activity != ACTIVITY_PLAYLISTVIEWER;
+ if (is_curr_track_info)
+ push_current_activity(ACTIVITY_ID3SCREEN);
+refresh_info:
+ info.count = 0;
+ info.playlist_display_index = playlist_display_index;
for (i = 0; i < ARRAYLEN(id3_headers); i++)
{
char temp[8];
info.info_id[i] = i;
- if (id3_get_info((i*2)+1, &info, temp, 8) != NULL)
+ if (id3_get_or_speak_info((i*2)+1, &info, temp, 8, false) != NULL)
info.info_id[info.count++] = i;
}
- gui_synclist_init(&id3_lists, &id3_get_info, &info, true, 2, NULL);
+ gui_synclist_init(&id3_lists, &id3_get_name_cb, &info, true, 2, NULL);
if(global_settings.talk_menu)
gui_synclist_set_voice_callback(&id3_lists, id3_speak_item);
gui_synclist_set_nb_items(&id3_lists, info.count*2);
+ gui_synclist_set_title(&id3_lists, str(LANG_TRACK_INFO), NOICON);
gui_synclist_draw(&id3_lists);
gui_synclist_speak_item(&id3_lists);
while (true) {
- if(!list_do_action(CONTEXT_LIST,HZ/2,
- &id3_lists, &key,LIST_WRAP_UNLESS_HELD)
+ if(!list_do_action(CONTEXT_LIST,HZ/2, &id3_lists, &key)
&& key!=ACTION_NONE && key!=ACTION_UNKNOWN)
{
if (key == ACTION_STD_OK || key == ACTION_STD_CANCEL)
@@ -710,13 +829,34 @@ bool browse_id3(void)
ret = true;
break;
}
+ }
+ else if (is_curr_track_info)
+ {
+ if (!audio_status())
+ {
+ ret = false;
+ break;
+ }
+ else
+ {
+ playlist_display_index = playlist_get_display_index();
+ if (playlist_display_index != info.playlist_display_index)
+ goto refresh_info;
+ }
}
}
-
- pop_current_activity();
+ if (is_curr_track_info)
+ pop_current_activity();
return ret;
}
+bool browse_id3(struct mp3entry *id3, int playlist_display_index, int playlist_amount,
+ struct tm *modified, int track_ct)
+{
+ return browse_id3_ex(id3, NULL, playlist_display_index, playlist_amount,
+ modified, track_ct);
+}
+
static const char* runtime_get_data(int selected_item, void* data,
char* buffer, size_t buffer_len)
{
@@ -774,8 +914,7 @@ int view_runtime(void)
say_runtime = false;
}
gui_synclist_draw(&lists);
- list_do_action(CONTEXT_STD, HZ,
- &lists, &action, LIST_WRAP_UNLESS_HELD);
+ list_do_action(CONTEXT_STD, HZ, &lists, &action);
if(action == ACTION_STD_CANCEL)
break;
if(action == ACTION_STD_OK) {
diff --git a/apps/screens.h b/apps/screens.h
index 2201f679bd..c3925f8ebc 100644
--- a/apps/screens.h
+++ b/apps/screens.h
@@ -23,6 +23,10 @@
#include "config.h"
#include "timefuncs.h"
+#include "metadata.h"
+#ifndef WARBLE
+#include "playlist.h"
+#endif
struct screen;
@@ -35,11 +39,16 @@ int mmc_remove_request(void);
#endif
#if CONFIG_RTC
-bool set_time_screen(const char* title, struct tm *tm);
+bool set_time_screen(const char* title, struct tm *tm, bool set_date);
#endif
-bool shutdown_screen(void);
-bool browse_id3(void);
+#ifndef WARBLE
+bool browse_id3_ex(struct mp3entry *id3, struct playlist_info *playlist,
+ int playlist_display_index, int playlist_amount,
+ struct tm *modified, int track_ct);
+#endif
+bool browse_id3(struct mp3entry *id3, int playlist_display_index, int playlist_amount,
+ struct tm *modified, int track_ct);
int view_runtime(void);
#ifdef HAVE_TOUCHSCREEN
diff --git a/apps/scrobbler.c b/apps/scrobbler.c
deleted file mode 100644
index f5ccf4a61c..0000000000
--- a/apps/scrobbler.c
+++ /dev/null
@@ -1,287 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id$
- *
- * Copyright (C) 2006-2008 Robert Keevil
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ****************************************************************************/
-/*
-Audioscrobbler spec at:
-http://www.audioscrobbler.net/wiki/Portable_Player_Logging
-*/
-
-#include <stdio.h>
-#include <config.h>
-#include "file.h"
-#include "logf.h"
-#include "metadata.h"
-#include "kernel.h"
-#include "audio.h"
-#include "core_alloc.h"
-#include "rbpaths.h"
-#include "ata_idle_notify.h"
-#include "pathfuncs.h"
-#include "appevents.h"
-#include "string-extra.h"
-#if CONFIG_RTC
-#include "time.h"
-#include "timefuncs.h"
-#endif
-
-#include "scrobbler.h"
-
-#define SCROBBLER_VERSION "1.1"
-
-/* increment this on any code change that effects output */
-#define SCROBBLER_REVISION " $Revision$"
-
-#define SCROBBLER_MAX_CACHE 32
-/* longest entry I've had is 323, add a safety margin */
-#define SCROBBLER_CACHE_LEN 512
-
-static bool scrobbler_initialised = false;
-static int scrobbler_cache = 0;
-static int cache_pos = 0;
-static bool pending = false;
-#if CONFIG_RTC
-static time_t timestamp;
-#define BASE_FILENAME HOME_DIR "/.scrobbler.log"
-#define HDR_STR_TIMELESS
-#define get_timestamp() ((long)timestamp)
-#define record_timestamp() ((void)(timestamp = mktime(get_time())))
-#else /* !CONFIG_RTC */
-#define HDR_STR_TIMELESS " Timeless"
-#define BASE_FILENAME ".scrobbler-timeless.log"
-#define get_timestamp() (0l)
-#define record_timestamp() ({})
-#endif /* CONFIG_RTC */
-
-static void get_scrobbler_filename(char *path, size_t size)
-{
- int used;
-
- used = snprintf(path, size, "/%s", BASE_FILENAME);
-
- if (used >= (int)size)
- {
- logf("SCROBBLER: not enough buffer space for log file");
- memset(path, 0, size);
- }
-}
-
-static void write_cache(void)
-{
- int i;
- int fd;
-
- char scrobbler_file[MAX_PATH];
- get_scrobbler_filename(scrobbler_file, MAX_PATH);
-
- /* If the file doesn't exist, create it.
- Check at each write since file may be deleted at any time */
- if(!file_exists(scrobbler_file))
- {
- fd = open(scrobbler_file, O_RDWR | O_CREAT, 0666);
- if(fd >= 0)
- {
- fdprintf(fd, "#AUDIOSCROBBLER/" SCROBBLER_VERSION "\n"
- "#TZ/UNKNOWN\n" "#CLIENT/Rockbox "
- TARGET_NAME SCROBBLER_REVISION
- HDR_STR_TIMELESS "\n");
-
- close(fd);
- }
- else
- {
- logf("SCROBBLER: cannot create log file (%s)", scrobbler_file);
- }
- }
-
- /* write the cache entries */
- fd = open(scrobbler_file, O_WRONLY | O_APPEND);
- if(fd >= 0)
- {
- logf("SCROBBLER: writing %d entries", cache_pos);
- /* copy data to temporary storage in case data moves during I/O */
- char temp_buf[SCROBBLER_CACHE_LEN];
- for ( i=0; i < cache_pos; i++ )
- {
- logf("SCROBBLER: write %d", i);
- char* scrobbler_buf = core_get_data(scrobbler_cache);
- ssize_t len = strlcpy(temp_buf, scrobbler_buf+(SCROBBLER_CACHE_LEN*i),
- sizeof(temp_buf));
- if (write(fd, temp_buf, len) != len)
- break;
- }
- close(fd);
- }
- else
- {
- logf("SCROBBLER: error writing file");
- }
-
- /* clear even if unsuccessful - don't want to overflow the buffer */
- cache_pos = 0;
-}
-
-static void scrobbler_flush_callback(void)
-{
- if (scrobbler_initialised && cache_pos)
- write_cache();
-}
-
-static void add_to_cache(const struct mp3entry *id)
-{
- if ( cache_pos >= SCROBBLER_MAX_CACHE )
- write_cache();
-
- char rating = 'S'; /* Skipped */
- char* scrobbler_buf = core_get_data(scrobbler_cache);
-
- logf("SCROBBLER: add_to_cache[%d]", cache_pos);
-
- if (id->elapsed > id->length / 2)
- rating = 'L'; /* Listened */
-
- char tracknum[11] = { "" };
-
- if (id->tracknum > 0)
- snprintf(tracknum, sizeof (tracknum), "%d", id->tracknum);
-
- int ret = snprintf(scrobbler_buf+(SCROBBLER_CACHE_LEN*cache_pos),
- SCROBBLER_CACHE_LEN,
- "%s\t%s\t%s\t%s\t%d\t%c\t%ld\t%s\n",
- id->artist,
- id->album ?: "",
- id->title,
- tracknum,
- (int)(id->length / 1000),
- rating,
- get_timestamp(),
- id->mb_track_id ?: "");
-
- if ( ret >= SCROBBLER_CACHE_LEN )
- {
- logf("SCROBBLER: entry too long:");
- logf("SCROBBLER: %s", id->path);
- }
- else
- {
- cache_pos++;
- register_storage_idle_func(scrobbler_flush_callback);
- }
-
-}
-
-static void scrobbler_change_event(unsigned short id, void *ev_data)
-{
- (void)id;
- struct mp3entry *id3 = ((struct track_event *)ev_data)->id3;
-
- /* check if track was resumed > %50 played
- check for blank artist or track name */
- if (id3->elapsed > id3->length / 2 || !id3->artist || !id3->title)
- {
- pending = false;
- logf("SCROBBLER: skipping file %s", id3->path);
- }
- else
- {
- logf("SCROBBLER: add pending");
- record_timestamp();
- pending = true;
- }
-}
-
-static void scrobbler_finish_event(unsigned short id, void *data)
-{
- (void)id;
- struct track_event *te = (struct track_event *)data;
-
- /* add entry using the currently ending track */
- if (pending && (te->flags & TEF_CURRENT)
- && !(te->flags & TEF_REWIND)
- )
- {
- pending = false;
- add_to_cache(te->id3);
- }
-}
-
-int scrobbler_init(void)
-{
- if (scrobbler_initialised)
- return 1;
-
- scrobbler_cache = core_alloc("scrobbler",
- SCROBBLER_MAX_CACHE*SCROBBLER_CACHE_LEN);
-
- if (scrobbler_cache <= 0)
- {
- logf("SCROOBLER: OOM");
- return -1;
- }
-
- cache_pos = 0;
- pending = false;
-
- scrobbler_initialised = true;
-
- add_event(PLAYBACK_EVENT_TRACK_CHANGE, scrobbler_change_event);
- add_event(PLAYBACK_EVENT_TRACK_FINISH, scrobbler_finish_event);
-
- return 1;
-}
-
-static void scrobbler_flush_cache(void)
-{
- /* Add any pending entries to the cache */
- if (pending)
- {
- pending = false;
- if (audio_status())
- add_to_cache(audio_current_track());
- }
-
- /* Write the cache to disk if needed */
- if (cache_pos)
- write_cache();
-}
-
-void scrobbler_shutdown(bool poweroff)
-{
- if (!scrobbler_initialised)
- return;
-
- remove_event(PLAYBACK_EVENT_TRACK_CHANGE, scrobbler_change_event);
- remove_event(PLAYBACK_EVENT_TRACK_FINISH, scrobbler_finish_event);
-
- scrobbler_flush_cache();
-
- if (!poweroff)
- {
- /* get rid of the buffer */
- core_free(scrobbler_cache);
- scrobbler_cache = 0;
- }
-
- scrobbler_initialised = false;
-}
-
-bool scrobbler_is_enabled(void)
-{
- return scrobbler_initialised;
-}
diff --git a/apps/settings.c b/apps/settings.c
index f89fc581ca..b281646686 100644
--- a/apps/settings.c
+++ b/apps/settings.c
@@ -19,6 +19,12 @@
* KIND, either express or implied.
*
****************************************************************************/
+/* Define LOGF_ENABLE to enable logf output in this file */
+/*#define LOGF_ENABLE*/
+/*Define DEBUG_AVAIL_SETTINGS to get a list of all available settings and flags */
+/*#define DEBUG_AVAIL_SETTINGS*/ /* Needs (LOGF_ENABLE) */
+#include "logf.h"
+
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
@@ -35,8 +41,7 @@
#include "backlight.h"
#include "audio.h"
#include "talk.h"
-#include "strlcpy.h"
-#include "strcasestr.h"
+#include "string-extra.h"
#include "rtc.h"
#include "power.h"
#include "ata_idle_notify.h"
@@ -83,11 +88,7 @@ struct system_status global_status;
#include "pcm_sampr.h"
#define NVRAM_DATA_START 8
-#ifdef HAVE_RTC_RAM
-#define NVRAM_BLOCK_SIZE 44
-#else
#define NVRAM_BLOCK_SIZE (sizeof(struct system_status) + NVRAM_DATA_START)
-#endif
#define MAX_LINES 10
@@ -100,121 +101,137 @@ struct system_status global_status;
#include "usb-ibasso.h"
#endif
+#ifdef ROCKBOX_NO_TEMP_SETTINGS_FILE /* Overwrites same file each time */
+#define CONFIGFILE_TEMP CONFIGFILE
+#define NVRAM_FILE_TEMP NVRAM_FILE
+#define rename_temp_file(a,b,c)
+#else /* creates temp files on save, renames next load, saves old file if desired */
+#define CONFIGFILE_TEMP CONFIGFILE".new"
+#define NVRAM_FILE_TEMP NVRAM_FILE".new"
+
+#ifdef LOGF_ENABLE
+static char *debug_get_flags(uint32_t flags);
+#endif
+static void debug_available_settings(void);
+
+static void rename_temp_file(const char *tempfile,
+ const char *file,
+ const char *oldfile)
+{
+ /* if tempfile does not exist -- Return
+ * if oldfile is supplied -- Rename file to oldfile
+ * if tempfile does exist -- Rename tempfile to file
+ */
+ if (file_exists(tempfile))
+ {
+ if (oldfile != NULL && file_exists(file))
+ rename(file, oldfile);
+ rename(tempfile, file);
+ }
+}
+#endif
long lasttime = 0;
/** NVRAM stuff, if the target doesnt have NVRAM it is saved in ROCKBOX_DIR /nvram.bin **/
/* NVRAM is set out as
-[0] 'R'
-[1] 'b'
-[2] version
-[3] stored variable count
-[4-7] crc32 checksum
-[8-NVRAM_BLOCK_SIZE] data
-*/
-static char nvram_buffer[NVRAM_BLOCK_SIZE];
-
-static bool read_nvram_data(char* buf, int max_len)
+ *
+ * [0] 'R'
+ * [1] 'b'
+ * [2] version
+ * [3] stored variable count
+ * [4-7] crc32 checksum in host endian order
+ * [8+] data
+ */
+
+static unsigned int nvram_crc(char *buf, int max_len)
{
- unsigned crc32 = 0xffffffff;
- int var_count = 0, i = 0, buf_pos = 0;
-#ifndef HAVE_RTC_RAM
+ return crc_32(&buf[NVRAM_DATA_START], max_len - NVRAM_DATA_START - 1, 0xffffffff);
+}
+
+static void read_nvram_data(void)
+{
+ rename_temp_file(NVRAM_FILE_TEMP, NVRAM_FILE, NVRAM_FILE".old");
+
int fd = open(NVRAM_FILE, O_RDONLY);
- int bytes;
if (fd < 0)
- return false;
- memset(buf,0,max_len);
- bytes = read(fd,buf,max_len);
+ return;
+
+ char buf[NVRAM_BLOCK_SIZE];
+ memset(buf, 0, sizeof(buf));
+
+ ssize_t bytes = read(fd, buf, sizeof(buf));
close(fd);
+
if (bytes < 8) /* min is 8 bytes,magic, ver, vars, crc32 */
- return false;
-#else
- memset(buf,0,max_len);
- /* read rtc block */
- for (i=0; i < max_len; i++ )
- buf[i] = rtc_read(0x14+i);
-#endif
+ return;
+
/* check magic, version */
- if ((buf[0] != 'R') || (buf[1] != 'b')
- || (buf[2] != NVRAM_CONFIG_VERSION))
- return false;
+ if (buf[0] != 'R' || buf[1] != 'b' || buf[2] != NVRAM_CONFIG_VERSION)
+ return;
+
/* check crc32 */
- crc32 = crc_32(&buf[NVRAM_DATA_START],
- max_len-NVRAM_DATA_START-1,0xffffffff);
- if (memcmp(&crc32,&buf[4],4))
- return false;
+ unsigned int crc32 = nvram_crc(buf, sizeof(buf));
+ if (crc32 != load_h32(&buf[4]))
+ return;
+
/* all good, so read in the settings */
- var_count = buf[3];
- buf_pos = NVRAM_DATA_START;
- for(i=0; i<nb_settings; i++)
+ int var_count = buf[3];
+ size_t buf_pos = NVRAM_DATA_START;
+ for(int i = 0; i < nb_settings; i++)
{
- int nvram_bytes = (settings[i].flags&F_NVRAM_BYTES_MASK)
- >>F_NVRAM_MASK_SHIFT;
+ const struct settings_list *setting = &settings[i];
+ int nvram_bytes = (setting->flags & F_NVRAM_BYTES_MASK) >> F_NVRAM_MASK_SHIFT;
if (nvram_bytes)
{
- if ((var_count>0) && (buf_pos<max_len))
+ if (var_count > 0 && buf_pos < (size_t)bytes)
{
- memcpy(settings[i].setting,&buf[buf_pos],nvram_bytes);
+ memcpy(setting->setting, &buf[buf_pos], nvram_bytes);
buf_pos += nvram_bytes;
var_count--;
}
else /* should only happen when new items are added to the end */
{
- memcpy(settings[i].setting, &settings[i].default_val, nvram_bytes);
+ memcpy(setting->setting, &setting->default_val, nvram_bytes);
}
}
}
- return true;
}
-static bool write_nvram_data(char* buf, int max_len)
+
+static void write_nvram_data(void)
{
- unsigned crc32 = 0xffffffff;
- int i = 0, buf_pos = 0;
- char var_count = 0;
-#ifndef HAVE_RTC_RAM
- int fd;
-#endif
- memset(buf,0,max_len);
+ char buf[NVRAM_BLOCK_SIZE];
+ memset(buf, 0, sizeof(buf));
+
/* magic, version */
- buf[0] = 'R'; buf[1] = 'b';
+ buf[0] = 'R';
+ buf[1] = 'b';
buf[2] = NVRAM_CONFIG_VERSION;
- buf_pos = NVRAM_DATA_START;
- for(i=0; (i<nb_settings) && (buf_pos<max_len); i++)
+
+ size_t buf_pos = NVRAM_DATA_START;
+ int var_count = 0;
+ for(int i = 0; i < nb_settings && buf_pos < sizeof(buf); i++)
{
- int nvram_bytes = (settings[i].flags&F_NVRAM_BYTES_MASK)
- >>F_NVRAM_MASK_SHIFT;
+ const struct settings_list *setting = &settings[i];
+ int nvram_bytes = (setting->flags & F_NVRAM_BYTES_MASK) >> F_NVRAM_MASK_SHIFT;
if (nvram_bytes)
{
- memcpy(&buf[buf_pos],settings[i].setting,nvram_bytes);
+ memcpy(&buf[buf_pos], setting->setting, nvram_bytes);
buf_pos += nvram_bytes;
var_count++;
}
}
+
/* count and crc32 */
buf[3] = var_count;
- crc32 = crc_32(&buf[NVRAM_DATA_START],
- max_len-NVRAM_DATA_START-1,0xffffffff);
- memcpy(&buf[4],&crc32,4);
-#ifndef HAVE_RTC_RAM
- fd = open(NVRAM_FILE,O_CREAT|O_TRUNC|O_WRONLY, 0666);
- if (fd >= 0)
- {
- int len = write(fd,buf,max_len);
- close(fd);
- if (len < 8)
- return false;
- }
-#else
- /* FIXME: okay, it _would_ be cleaner and faster to implement rtc_write so
- that it would write a number of bytes at a time since the RTC chip
- supports that, but this will have to do for now 8-) */
- for (i=0; i < NVRAM_BLOCK_SIZE; i++ ) {
- int r = rtc_write(0x14+i, buf[i]);
- if (r)
- return false;
- }
-#endif
- return true;
+ store_h32(&buf[4], nvram_crc(buf, sizeof(buf)));
+
+ int fd = open(NVRAM_FILE_TEMP,O_CREAT|O_TRUNC|O_WRONLY, 0666);
+ if (fd < 0)
+ return;
+
+ write(fd, buf, sizeof(buf));
+ close(fd);
}
/** Reading from a config file **/
@@ -223,53 +240,107 @@ static bool write_nvram_data(char* buf, int max_len)
*/
void settings_load(int which)
{
- if (which&SETTINGS_RTC)
- read_nvram_data(nvram_buffer,NVRAM_BLOCK_SIZE);
- if (which&SETTINGS_HD)
+ logf("\r\n%s()\r\n", __func__);
+ debug_available_settings();
+
+ if (which & SETTINGS_RTC)
+ read_nvram_data();
+ if (which & SETTINGS_HD)
{
+ rename_temp_file(CONFIGFILE_TEMP, CONFIGFILE, CONFIGFILE".old");
settings_load_config(CONFIGFILE, false);
settings_load_config(FIXEDSETTINGSFILE, false);
}
}
-bool cfg_string_to_int(int setting_id, int* out, const char* str)
+bool cfg_string_to_int(const struct settings_list *setting, int* out, const char* str)
{
- const char* start = settings[setting_id].cfg_vals;
- char* end = NULL;
- char temp[MAX_PATH];
- int count = 0;
- while (1)
+ const char* ptr = setting->cfg_vals;
+ size_t len = strlen(str);
+ int index = 0;
+
+ while (true)
{
- end = strchr(start, ',');
- if (!end)
+ if (!strncmp(ptr, str, len))
{
- if (!strcmp(str, start))
+ ptr += len;
+ /* if the next character is not a comma or end of string,
+ * it means the comparison was only a partial match. */
+ if (*ptr == ',' || *ptr == '\0')
{
- *out = count;
+ *out = index;
return true;
}
- else return false;
}
- strlcpy(temp, start, end-start+1);
- if (!strcmp(str, temp))
+
+ while (*ptr != ',')
{
- *out = count;
- return true;
+ if (!*ptr)
+ return false;
+ ptr++;
+ }
+
+ ptr++;
+ index++;
+ }
+}
+
+/**
+ * Copy an input string to an output buffer, stripping the prefix and
+ * suffix listed in the filename setting. Returns false if the output
+ * string does not fit in the buffer or is longer than the setting's
+ * max_len, and the output buffer will not be modified.
+ *
+ * Returns true if the setting was copied successfully. The input and
+ * output buffers are allowed to alias.
+ */
+bool copy_filename_setting(char *buf, size_t buflen, const char *input,
+ const struct filename_setting *fs)
+{
+ size_t input_len = strlen(input);
+ size_t len;
+
+ if (fs->prefix)
+ {
+ len = strlen(fs->prefix);
+ if (len <= input_len && !strncasecmp(input, fs->prefix, len))
+ {
+ input += len;
+ input_len -= len;
+ }
+ }
+
+ if (fs->suffix)
+ {
+ len = strlen(fs->suffix);
+ if (len <= input_len &&
+ !strcasecmp(input + input_len - len, fs->suffix))
+ {
+ input_len -= len;
}
- start = end +1;
- count++;
}
- return false;
+
+ /* Make sure it fits the output buffer and repsects the setting's max_len.
+ * Note that max_len is a buffer size and thus includes a null terminator */
+ if (input_len >= (size_t)fs->max_len || input_len >= buflen)
+ return false;
+
+ /* Copy what remains into buf - use memmove in case of aliasing */
+ memmove(buf, input, input_len);
+ buf[input_len] = '\0';
+ return true;
}
bool settings_load_config(const char* file, bool apply)
{
+ logf("%s()\r\n", __func__);
+ const struct settings_list *setting;
int fd;
char line[128];
char* name;
char* value;
- int i;
bool theme_changed = false;
+
fd = open_utf8(file, O_RDONLY);
if (fd < 0)
return false;
@@ -278,88 +349,104 @@ bool settings_load_config(const char* file, bool apply)
{
if (!settings_parseline(line, &name, &value))
continue;
- for(i=0; i<nb_settings; i++)
+
+ setting = find_setting_by_cfgname(name);
+ if (!setting)
+ continue;
+
+ if (setting->flags & F_THEMESETTING)
+ theme_changed = true;
+
+ switch (setting->flags & F_T_MASK)
{
- if (settings[i].cfg_name == NULL)
- continue;
- if (!strcasecmp(name,settings[i].cfg_name))
- {
- if (settings[i].flags&F_THEMESETTING)
- theme_changed = true;
- switch (settings[i].flags&F_T_MASK)
- {
- case F_T_CUSTOM:
- settings[i].custom_setting->load_from_cfg(settings[i].setting, value);
- break;
- case F_T_INT:
- case F_T_UINT:
+ case F_T_CUSTOM:
+ setting->custom_setting->load_from_cfg(setting->setting, value);
+ logf("Val: %s\r\n",value);
+ break;
+ case F_T_INT:
+ case F_T_UINT:
#ifdef HAVE_LCD_COLOR
- if (settings[i].flags&F_RGB)
- hex_to_rgb(value, (int*)settings[i].setting);
- else
+ if (setting->flags & F_RGB)
+ {
+ hex_to_rgb(value, (int*)setting->setting);
+ logf("Val: %s\r\n", value);
+ }
+ else
#endif
- if (settings[i].cfg_vals == NULL)
- {
- *(int*)settings[i].setting = atoi(value);
- }
+ if (setting->cfg_vals == NULL)
+ {
+ *(int*)setting->setting = atoi(value);
+ logf("Val: %s\r\n",value);
+ }
+ else
+ {
+ int temp, *v = (int*)setting->setting;
+ bool found = cfg_string_to_int(setting, &temp, value);
+ if (found)
+ {
+ if (setting->flags & F_TABLE_SETTING)
+ *v = setting->table_setting->values[temp];
else
- {
- int temp, *v = (int*)settings[i].setting;
- bool found = cfg_string_to_int(i, &temp, value);
- if (found)
- {
- if (settings[i].flags&F_TABLE_SETTING)
- *v = settings[i].table_setting->values[temp];
- else
- *v = temp;
- }
- else
- { /* atoi breaks choice settings because they
- * don't have int-like values, and would
- * fall back to the first value (i.e. 0)
- * due to atoi */
- if (!(settings[i].flags&F_CHOICE_SETTING))
- *v = atoi(value);
- }
- }
- break;
- case F_T_BOOL:
+ *v = temp;
+ logf("Val: %d\r\n", *v);
+ }
+ else if (setting->flags & F_ALLOW_ARBITRARY_VALS)
{
- int temp;
- if (cfg_string_to_int(i,&temp,value))
- *(bool*)settings[i].setting = (temp!=0);
- if (settings[i].bool_setting->option_callback)
- settings[i].bool_setting->option_callback(temp!=0);
- break;
+ *v = atoi(value);
+ logf("Val: %s = %d\r\n", value, *v);
}
- case F_T_CHARPTR:
- case F_T_UCHARPTR:
+ else if (setting->flags & F_TABLE_SETTING)
{
- char storage[MAX_PATH];
- if (settings[i].filename_setting->prefix)
+ const struct table_setting *info = setting->table_setting;
+ temp = atoi(value);
+ *v = setting->default_val.int_;
+ if (info->values)
{
- const char *dir = settings[i].filename_setting->prefix;
- size_t len = strlen(dir);
- if (!strncasecmp(value, dir, len))
+ for(int i = 0; i < info->count; i++)
{
- strlcpy(storage, &value[len], MAX_PATH);
+ if (info->values[i] == temp)
+ {
+ *v = temp;
+ break;
+ }
}
- else strlcpy(storage, value, MAX_PATH);
}
- else strlcpy(storage, value, MAX_PATH);
- if (settings[i].filename_setting->suffix)
- {
- char *s = strcasestr(storage,settings[i].filename_setting->suffix);
- if (s) *s = '\0';
- }
- strlcpy((char*)settings[i].setting, storage,
- settings[i].filename_setting->max_len);
- break;
+ logf("Val: %s", *v == temp ? "Found":"Error Not Found");
+ logf("Val: %s = %d\r\n", value, *v);
+ }
+
+ else
+ {
+ logf("Error: %s: Not Found! [%s]\r\n",
+ setting->cfg_name, value);
}
}
- break;
- } /* if (!strcmp(name,settings[i].cfg_name)) */
- } /* for(...) */
+ break;
+ case F_T_BOOL:
+ {
+ int temp;
+ if (cfg_string_to_int(setting, &temp, value))
+ {
+ *(bool*)setting->setting = !!temp;
+ logf("Val: %s\r\n", value);
+ }
+ if (setting->bool_setting->option_callback)
+ {
+ setting->bool_setting->option_callback(!!temp);
+ }
+ break;
+ }
+ /* these can be plain text, filenames, or dirnames */
+ case F_T_CHARPTR:
+ case F_T_UCHARPTR:
+ {
+ const struct filename_setting *fs = setting->filename_setting;
+ copy_filename_setting((char*)setting->setting,
+ fs->max_len, value, fs);
+ logf("Val: %s\r\n", value);
+ break;
+ }
+ }
} /* while(...) */
close(fd);
@@ -375,74 +462,53 @@ bool settings_load_config(const char* file, bool apply)
/** Writing to a config file and saving settings **/
-bool cfg_int_to_string(int setting_id, int val, char* buf, int buf_len)
+bool cfg_int_to_string(const struct settings_list *setting, int val, char* buf, int buf_len)
{
- int flags = settings[setting_id].flags;
- const char* start = settings[setting_id].cfg_vals;
- char* end = NULL;
- int count = 0;
+ const char* ptr = setting->cfg_vals;
+ const int *values = NULL;
+ int index = 0;
- if ((flags&F_T_MASK)==F_T_INT &&
- flags&F_TABLE_SETTING)
+ if (setting->flags & F_TABLE_SETTING)
+ values = setting->table_setting->values;
+
+ while (true)
{
- const int *value = settings[setting_id].table_setting->values;
- while (start)
+ if ((values && values[index] == val) ||
+ (!values && index == val))
{
- end = strchr(start,',');
- if (value[count] == val)
- {
- if (end == NULL)
- strlcpy(buf, start, buf_len);
- else
- {
- int len = MIN(buf_len, (end-start) + 1);
- strlcpy(buf, start, len);
- }
- return true;
- }
- count++;
+ char *buf_end = buf + buf_len - 1;
+ while (*ptr && *ptr != ',' && buf != buf_end)
+ *buf++ = *ptr++;
- if (end)
- start = end+1;
- else
- break;
+ *buf++ = '\0';
+ return true;
}
- return false;
- }
- while (count < val)
- {
- start = strchr(start,',');
- if (!start)
- return false;
- count++;
- start++;
- }
- end = strchr(start,',');
- if (end == NULL)
- strlcpy(buf, start, buf_len);
- else
- {
- int len = MIN(buf_len, (end-start) + 1);
- strlcpy(buf, start, len);
+ while (*ptr != ',')
+ {
+ if (!*ptr)
+ return false;
+ ptr++;
+ }
+
+ ptr++;
+ index++;
}
- return true;
}
-bool cfg_to_string(int i/*setting_id*/, char* buf, int buf_len)
+void cfg_to_string(const struct settings_list *setting, char* buf, int buf_len)
{
- switch (settings[i].flags&F_T_MASK)
+ switch (setting->flags & F_T_MASK)
{
case F_T_CUSTOM:
- settings[i].custom_setting->write_to_cfg(settings[i].setting,
- buf, buf_len);
+ setting->custom_setting->write_to_cfg(setting->setting, buf, buf_len);
break;
case F_T_INT:
case F_T_UINT:
#ifdef HAVE_LCD_COLOR
- if (settings[i].flags&F_RGB)
+ if (setting->flags & F_RGB)
{
- int colour = *(int*)settings[i].setting;
+ int colour = *(int*)setting->setting;
snprintf(buf,buf_len,"%02x%02x%02x",
(int)RGB_UNPACK_RED(colour),
(int)RGB_UNPACK_GREEN(colour),
@@ -450,57 +516,50 @@ bool cfg_to_string(int i/*setting_id*/, char* buf, int buf_len)
}
else
#endif
- if (settings[i].cfg_vals == NULL)
+ if (setting->cfg_vals == NULL)
{
- snprintf(buf,buf_len,"%d",*(int*)settings[i].setting);
+ snprintf(buf, buf_len, "%d", *(int*)setting->setting);
}
else
{
- if (cfg_int_to_string(i, *(int*)settings[i].setting,
- buf, buf_len) == false)
- {
- snprintf(buf,buf_len,"%d",*(int*)settings[i].setting);
- }
- else
- return false;
+ if (!cfg_int_to_string(setting, *(int*)setting->setting,
+ buf, buf_len))
+ snprintf(buf, buf_len, "%d", *(int*)setting->setting);
}
break;
case F_T_BOOL:
- cfg_int_to_string(i,
- *(bool*)settings[i].setting==false?0:1, buf, buf_len);
+ cfg_int_to_string(setting, *(bool*)setting->setting, buf, buf_len);
break;
case F_T_CHARPTR:
case F_T_UCHARPTR:
- if (((char*)settings[i].setting)[0]
- && settings[i].filename_setting->prefix)
+ {
+ char *value = setting->setting;
+ const struct filename_setting *fs = setting->filename_setting;
+ if (value[0] && fs->prefix)
{
- if (((char*)settings[i].setting)[0] == '-')
+ if (value[0] == '-')
{
buf[0] = '-';
buf[1] = '\0';
}
else
{
- snprintf(buf,buf_len,"%s%s%s",
- settings[i].filename_setting->prefix,
- (char*)settings[i].setting,
- settings[i].filename_setting->suffix);
+ snprintf(buf, buf_len, "%s%s%s",
+ fs->prefix, value, fs->suffix);
}
}
else
{
- int len = MIN(buf_len, settings[i].filename_setting->max_len);
- strlcpy(buf,(char*)settings[i].setting,len);
+ strmemccpy(buf, value, buf_len);
}
break;
+ }
} /* switch () */
- return true;
}
-static bool is_changed(int setting_id)
+static bool is_changed(const struct settings_list *setting)
{
- const struct settings_list *setting = &settings[setting_id];
switch (setting->flags&F_T_MASK)
{
case F_T_CUSTOM:
@@ -538,6 +597,7 @@ static bool is_changed(int setting_id)
static bool settings_write_config(const char* filename, int options)
{
+ logf("%s\r\n", __func__);
int i;
int fd;
char value[MAX_PATH];
@@ -548,57 +608,57 @@ static bool settings_write_config(const char* filename, int options)
"http://www.rockbox.org\r\n\r\n", rbversion);
for(i=0; i<nb_settings; i++)
{
- if (settings[i].cfg_name == NULL)
- continue;
- value[0] = '\0';
- if (settings[i].flags & F_DEPRECATED)
+ const struct settings_list *setting = &settings[i];
+ if (!setting->cfg_name || (setting->flags & F_DEPRECATED))
continue;
switch (options)
{
case SETTINGS_SAVE_CHANGED:
- if (!is_changed(i))
+ if (!is_changed(setting))
continue;
break;
case SETTINGS_SAVE_SOUND:
- if ((settings[i].flags&F_SOUNDSETTING) == 0)
+ if (!(setting->flags & F_SOUNDSETTING))
continue;
break;
case SETTINGS_SAVE_THEME:
- if ((settings[i].flags&F_THEMESETTING) == 0)
+ if (!(setting->flags & F_THEMESETTING))
continue;
break;
#ifdef HAVE_RECORDING
case SETTINGS_SAVE_RECPRESETS:
- if ((settings[i].flags&F_RECSETTING) == 0)
+ if (!(setting->flags & F_RECSETTING))
continue;
break;
#endif
case SETTINGS_SAVE_EQPRESET:
- if ((settings[i].flags&F_EQSETTING) == 0)
+ if (!(setting->flags & F_EQSETTING))
continue;
break;
}
+ cfg_to_string(setting, value, MAX_PATH);
+ logf("Written: '%s: %s'\r\n",setting->cfg_name, value);
- cfg_to_string(i, value, MAX_PATH);
- fdprintf(fd,"%s: %s\r\n",settings[i].cfg_name,value);
+ fdprintf(fd,"%s: %s\r\n",setting->cfg_name,value);
} /* for(...) */
close(fd);
return true;
}
-#ifndef HAVE_RTC_RAM
+
static void flush_global_status_callback(void)
{
- write_nvram_data(nvram_buffer,NVRAM_BLOCK_SIZE);
+ write_nvram_data();
}
-#endif
+
static void flush_config_block_callback(void)
{
- write_nvram_data(nvram_buffer,NVRAM_BLOCK_SIZE);
- settings_write_config(CONFIGFILE, SETTINGS_SAVE_CHANGED);
+ write_nvram_data();
+ settings_write_config(CONFIGFILE_TEMP, SETTINGS_SAVE_CHANGED);
}
-void reset_runtime(void) {
+void reset_runtime(void)
+{
lasttime = current_tick;
global_status.runtime = 0;
}
@@ -621,29 +681,24 @@ static void update_runtime(void)
void status_save(void)
{
update_runtime();
-#ifdef HAVE_RTC_RAM
- /* this will be done in the storage_callback if
- target doesnt have rtc ram */
- write_nvram_data(nvram_buffer,NVRAM_BLOCK_SIZE);
-#else
register_storage_idle_func(flush_global_status_callback);
-#endif
}
int settings_save(void)
{
+ logf("%s", __func__);
update_runtime();
-#ifdef HAVE_RTC_RAM
- /* this will be done in the storage_callback if
- target doesnt have rtc ram */
- write_nvram_data(nvram_buffer,NVRAM_BLOCK_SIZE);
-#endif
register_storage_idle_func(flush_config_block_callback);
return 0;
}
bool settings_save_config(int options)
{
+ /* if we have outstanding temp files it would be a good idea to flush
+ them before the user starts saving things */
+ rename_temp_file(NVRAM_FILE_TEMP, NVRAM_FILE, NULL); /* dont overwrite .old */
+ rename_temp_file(CONFIGFILE_TEMP, CONFIGFILE, NULL); /* files from last boot */
+
char filename[MAX_PATH];
const char *folder, *namebase;
switch (options)
@@ -773,6 +828,7 @@ void sound_settings_apply(void)
void settings_apply(bool read_disk)
{
+ logf("%s", __func__);
int rc;
CHART(">set_codepage");
set_codepage(global_settings.default_codepage);
@@ -840,7 +896,7 @@ void settings_apply(bool read_disk)
set_keypress_restarts_sleep_timer(
global_settings.keypress_restarts_sleeptimer);
-#if defined(BATTERY_CAPACITY_INC) && BATTERY_CAPACITY_INC > 0
+#if BATTERY_CAPACITY_INC > 0
/* only call if it's really exchangable */
set_battery_capacity(global_settings.battery_capacity);
#endif
@@ -873,11 +929,9 @@ void settings_apply(bool read_disk)
if (global_settings.font_file[0]
&& global_settings.font_file[0] != '-') {
int font_ui = screens[SCREEN_MAIN].getuifont();
- const char* loaded_font = font_filename(font_ui);
-
snprintf(buf, sizeof buf, FONT_DIR "/%s.fnt",
global_settings.font_file);
- if (!loaded_font || strcmp(loaded_font, buf))
+ if (!font_filename_matches_loaded_id(font_ui, buf))
{
CHART2(">font_load ", global_settings.font_file);
if (font_ui >= 0)
@@ -892,10 +946,9 @@ void settings_apply(bool read_disk)
if ( global_settings.remote_font_file[0]
&& global_settings.remote_font_file[0] != '-') {
int font_ui = screens[SCREEN_REMOTE].getuifont();
- const char* loaded_font = font_filename(font_ui);
snprintf(buf, sizeof buf, FONT_DIR "/%s.fnt",
global_settings.remote_font_file);
- if (!loaded_font || strcmp(loaded_font, buf))
+ if (!font_filename_matches_loaded_id(font_ui, buf))
{
CHART2(">font_load_remoteui ", global_settings.remote_font_file);
if (font_ui >= 0)
@@ -945,11 +998,12 @@ void settings_apply(bool read_disk)
#endif
lcd_scroll_step(global_settings.scroll_step);
- gui_list_screen_scroll_step(global_settings.screen_scroll_step);
- gui_list_screen_scroll_out_of_view(global_settings.offset_out_of_view);
lcd_bidir_scroll(global_settings.bidir_limit);
lcd_scroll_delay(global_settings.scroll_delay);
+#ifdef HAVE_ALBUMART
+ set_albumart_mode(global_settings.album_art);
+#endif
#ifdef HAVE_PLAY_FREQ
/* before crossfade */
@@ -997,9 +1051,7 @@ void settings_apply(bool read_disk)
#ifdef HAVE_REMOTE_LCD
set_remote_backlight_filter_keypress(global_settings.remote_bl_filter_first_keypress);
#endif
-#ifdef HAS_BUTTON_HOLD
backlight_set_on_button_hold(global_settings.backlight_on_button_hold);
-#endif
#ifdef HAVE_LCD_SLEEP_SETTING
lcd_set_sleep_after_backlight_off(global_settings.lcd_sleep_after_backlight_off);
@@ -1075,8 +1127,8 @@ void reset_setting(const struct settings_list *setting, void *var)
break;
case F_T_CHARPTR:
case F_T_UCHARPTR:
- strlcpy((char*)var, setting->default_val.charptr,
- setting->filename_setting->max_len);
+ strmemccpy((char*)var, setting->default_val.charptr,
+ setting->filename_setting->max_len);
break;
}
}
@@ -1100,32 +1152,32 @@ void settings_reset(void)
}
/** Changing setting values **/
-const struct settings_list* find_setting(const void* variable, int *id)
+const struct settings_list* find_setting(const void* variable)
{
- int i;
- for(i=0;i<nb_settings;i++)
+ for(int i = 0; i < nb_settings; i++)
{
- if (settings[i].setting == variable)
- {
- if (id)
- *id = i;
- return &settings[i];
- }
+ const struct settings_list *setting = &settings[i];
+ if (setting->setting == variable)
+ return setting;
}
+
return NULL;
}
-const struct settings_list* find_setting_by_cfgname(const char* name, int *id)
+
+const struct settings_list* find_setting_by_cfgname(const char* name)
{
- int i;
- for (i=0; i<nb_settings; i++)
+ logf("Searching for Setting: '%s'",name);
+ for(int i = 0; i < nb_settings; i++)
{
- if (settings[i].cfg_name &&
- !strcmp(settings[i].cfg_name, name))
+ const struct settings_list *setting = &settings[i];
+ if (setting->cfg_name && !strcasecmp(setting->cfg_name, name))
{
- if (id) *id = i;
- return &settings[i];
+ logf("Found, flags: %s", debug_get_flags(settings[i].flags));
+ return setting;
}
}
+ logf("Setting: '%s' Not Found!",name);
+
return NULL;
}
@@ -1149,7 +1201,7 @@ bool set_bool_options(const char* string, const bool* variable,
};
bool result;
- result = set_option(string, variable, BOOL, names, 2,
+ result = set_option(string, variable, RB_BOOL, names, 2,
(void (*)(int))(void (*)(void))function);
return result;
}
@@ -1181,9 +1233,14 @@ bool set_int_ex(const unsigned char* string,
{
(void)unit;
struct settings_list item;
- struct int_setting data = {
- function, voice_unit, min, max, step,
- formatter, get_talk_id
+ const struct int_setting data = {
+ .option_callback = function,
+ .unit = voice_unit,
+ .step = step,
+ .min = min,
+ .max = max,
+ .formatter = formatter,
+ .get_talk_id = get_talk_id,
};
item.int_setting = &data;
item.flags = F_INT_SETTING|F_T_INT;
@@ -1213,9 +1270,14 @@ bool set_option(const char* string, const void* variable, enum optiontype type,
{
int temp;
struct settings_list item;
- struct int_setting data = {
- function, UNIT_INT, 0, numoptions-1, 1,
- set_option_formatter, set_option_get_talk_id
+ const struct int_setting data = {
+ .option_callback = function,
+ .unit = UNIT_INT,
+ .step = 1,
+ .min = 0,
+ .max = numoptions-1,
+ .formatter = set_option_formatter,
+ .get_talk_id = set_option_get_talk_id
};
memset(&item, 0, sizeof(struct settings_list));
set_option_options = options;
@@ -1224,13 +1286,13 @@ bool set_option(const char* string, const void* variable, enum optiontype type,
item.lang_id = -1;
item.cfg_vals = (char*)string;
item.setting = &temp;
- if (type == BOOL)
+ if (type == RB_BOOL)
temp = *(bool*)variable? 1: 0;
else
temp = *(int*)variable;
if (!option_screen(&item, NULL, false, NULL))
{
- if (type == BOOL)
+ if (type == RB_BOOL)
*(bool*)variable = (temp == 1);
else
@@ -1269,6 +1331,111 @@ void set_file(const char* filename, char* setting, const int maxlen)
if (len > maxlen)
return;
- strlcpy(setting, fptr, len);
+ strmemccpy(setting, fptr, len);
settings_save();
}
+
+#ifdef LOGF_ENABLE
+static char *debug_get_flags(uint32_t flags)
+{
+ static char buf[256] = {0};
+ uint32_t ftype = flags & F_T_MASK; /* the variable type for the setting */
+ flags &= ~F_T_MASK;
+ switch (ftype)
+ {
+ case F_T_CUSTOM:
+ strlcpy(buf, "[Type CUSTOM] ", sizeof(buf));
+ break;
+ case F_T_INT:
+ strlcpy(buf, "[Type INT] ", sizeof(buf));
+ break;
+ case F_T_UINT:
+ strlcpy(buf, "[Type UINT] ", sizeof(buf));
+ break;
+ case F_T_BOOL:
+ strlcpy(buf, "[Type BOOL] ", sizeof(buf));
+ break;
+ case F_T_CHARPTR:
+ strlcpy(buf, "[Type CHARPTR] ", sizeof(buf));
+ break;
+ case F_T_UCHARPTR:
+ strlcpy(buf, "[Type UCHARPTR] ", sizeof(buf));
+ break;
+ }
+
+#define SETTINGFLAGS(n) \
+ if(flags & n) { \
+ flags &= ~n; \
+ strlcat(buf, "["#n"]", sizeof(buf));}
+
+ SETTINGFLAGS(F_T_SOUND);
+ SETTINGFLAGS(F_BOOL_SETTING);
+ SETTINGFLAGS(F_RGB);
+ SETTINGFLAGS(F_FILENAME);
+ SETTINGFLAGS(F_INT_SETTING);
+ SETTINGFLAGS(F_CHOICE_SETTING);
+ SETTINGFLAGS(F_CHOICETALKS);
+ SETTINGFLAGS(F_TABLE_SETTING);
+ SETTINGFLAGS(F_ALLOW_ARBITRARY_VALS);
+ SETTINGFLAGS(F_CB_ON_SELECT_ONLY);
+ SETTINGFLAGS(F_CB_ONLY_IF_CHANGED);
+ SETTINGFLAGS(F_MIN_ISFUNC);
+ SETTINGFLAGS(F_MAX_ISFUNC);
+ SETTINGFLAGS(F_DEF_ISFUNC);
+ SETTINGFLAGS(F_CUSTOM_SETTING);
+ SETTINGFLAGS(F_TIME_SETTING);
+ SETTINGFLAGS(F_THEMESETTING);
+ SETTINGFLAGS(F_RECSETTING);
+ SETTINGFLAGS(F_EQSETTING);
+ SETTINGFLAGS(F_SOUNDSETTING);
+ SETTINGFLAGS(F_TEMPVAR);
+ SETTINGFLAGS(F_PADTITLE);
+ SETTINGFLAGS(F_NO_WRAP);
+ SETTINGFLAGS(F_BANFROMQS);
+ SETTINGFLAGS(F_DEPRECATED);
+#undef SETTINGFLAGS
+
+ if (flags & F_NVRAM_BYTES_MASK)
+ {
+ flags &= ~F_NVRAM_BYTES_MASK;
+ strlcat(buf, "[NVRAM]", sizeof(buf));
+ }
+ /* anything left is unknown */
+ if (flags)
+ {
+ strlcat(buf, "[UNKNOWN FLAGS]", sizeof(buf));
+ size_t len = strlen(buf);
+ if (len < sizeof(buf))
+ snprintf(buf + len, sizeof(buf) - len - 1, "[%x]", flags);
+ }
+ return buf;
+}
+#endif
+static void debug_available_settings(void)
+{
+#if defined(DEBUG_AVAIL_SETTINGS) && defined(LOGF_ENABLE)
+ logf("\r\nAvailable Settings:");
+ for (int i=0; i<nb_settings; i++)
+ {
+ uint32_t flags = settings[i].flags;
+ const char *name;
+ if (settings[i].cfg_name)
+ name = settings[i].cfg_name;
+ else if (settings[i].RESERVED == NULL)
+ {
+ name = "SYS (NVRAM?)";
+ if (flags & F_NVRAM_BYTES_MASK)
+ {
+ flags &= ~F_NVRAM_BYTES_MASK;
+ flags |= 0x80000; /* unused by other flags */
+ }
+ }
+ else
+ {
+ name = "?? UNKNOWN NAME ?? ";
+ }
+ logf("'%s' flags: %s",name, debug_get_flags(flags));
+ }
+ logf("End Available Settings\r\n");
+#endif
+}
diff --git a/apps/settings.h b/apps/settings.h
index fedec8e025..13550ffd2a 100644
--- a/apps/settings.h
+++ b/apps/settings.h
@@ -108,6 +108,18 @@ enum
NUM_REPEAT_MODES
};
+/* single mode options */
+enum {
+ SINGLE_MODE_OFF = 0,
+ SINGLE_MODE_TRACK,
+ SINGLE_MODE_ALBUM,
+ SINGLE_MODE_ALBUM_ARTIST,
+ SINGLE_MODE_ARTIST,
+ SINGLE_MODE_COMPOSER,
+ SINGLE_MODE_GROUPING,
+ SINGLE_MODE_GENRE
+};
+
enum
{
QUEUE_HIDE = 0,
@@ -115,6 +127,15 @@ enum
QUEUE_SHOW_IN_SUBMENU
};
+#ifdef HAVE_ALBUMART
+enum
+{
+ AA_OFF = 0,
+ AA_PREFER_EMBEDDED,
+ AA_PREFER_IMAGE_FILE
+};
+#endif
+
/* dir filter options */
/* Note: Any new filter modes need to be added before NUM_FILTER_MODES.
* Any new rockbox browse filter modes (accessible through the menu)
@@ -226,6 +247,7 @@ enum {
bool settings_save_config(int options);
struct settings_list;
+struct filename_setting;
void reset_setting(const struct settings_list *setting, void *var);
void settings_reset(void);
void sound_settings_apply(void);
@@ -239,13 +261,15 @@ void settings_apply(bool read_disk);
void settings_apply_pm_range(void);
void settings_display(void);
-enum optiontype { INT, BOOL };
+enum optiontype { RB_INT, RB_BOOL };
-const struct settings_list* find_setting(const void* variable, int *id);
-const struct settings_list* find_setting_by_cfgname(const char* name, int *id);
-bool cfg_int_to_string(int setting_id, int val, char* buf, int buf_len);
-bool cfg_string_to_int(int setting_id, int* out, const char* str);
-bool cfg_to_string(int setting_id, char* buf, int buf_len);
+const struct settings_list* find_setting(const void* variable);
+const struct settings_list* find_setting_by_cfgname(const char* name);
+bool cfg_int_to_string(const struct settings_list *setting, int val, char* buf, int buf_len);
+bool cfg_string_to_int(const struct settings_list *setting, int* out, const char* str);
+void cfg_to_string(const struct settings_list *setting, char* buf, int buf_len);
+bool copy_filename_setting(char *buf, size_t buflen, const char *input,
+ const struct filename_setting *fs);
bool set_bool_options(const char* string, const bool* variable,
const char* yes_str, int yes_voice,
const char* no_str, int no_voice,
@@ -437,6 +461,9 @@ struct user_settings
unsigned char rfms_file[MAX_FILENAME+1]; /* last remote-fms */
#endif
#endif /* CONFIG_TUNER */
+#if defined(HAVE_RDS_CAP) && defined(CONFIG_RTC)
+ bool sync_rds_time; /* use RDS time to set the clock */
+#endif
/* misc options */
#ifndef HAVE_WHEEL_ACCELERATION
@@ -459,7 +486,7 @@ struct user_settings
#endif
#ifdef HAVE_QUICKSCREEN
- int qs_items[QUICKSCREEN_ITEM_COUNT];
+ const struct settings_list *qs_items[QUICKSCREEN_ITEM_COUNT];
#endif
int timeformat; /* time format: 0=24 hour clock, 1=12 hour clock */
@@ -476,8 +503,9 @@ struct user_settings
int default_codepage; /* set default codepage for tag conversion */
bool hold_lr_for_scroll_in_list; /* hold L/R scrolls the list left/right */
bool play_selected; /* Plays selected file even in shuffle mode */
+ int single_mode; /* single mode - stop after every track, album, album artist,
+ artist, composer, work, or genre */
bool party_mode; /* party mode - unstoppable music */
- bool audioscrobbler; /* Audioscrobbler logging */
bool cuesheet;
bool car_adapter_mode; /* 0=off 1=on */
int car_adapter_mode_delay; /* delay before resume, in seconds*/
@@ -529,6 +557,8 @@ struct user_settings
bool browse_current; /* 1=goto current song,
0=goto previous location */
bool scroll_paginated; /* 0=dont 1=do */
+ bool list_wraparound; /* wrap around to opposite end of list when scrolling */
+ int list_order; /* order for numeric lists (ascending or descending) */
int scroll_speed; /* long texts scrolling speed: 1-30 */
int bidir_limit; /* bidir scroll length limit */
int scroll_delay; /* delay (in 1/10s) before starting scroll */
@@ -555,6 +585,7 @@ struct user_settings
unsigned char autoresume_paths[MAX_PATHNAME+1]; /* colon-separated list */
bool runtimedb; /* runtime database active? */
unsigned char tagcache_scan_paths[MAX_PATHNAME+1];
+ unsigned char tagcache_db_path[MAX_PATHNAME+1];
#endif /* HAVE_TAGCACHE */
#if LCD_DEPTH > 1
@@ -580,8 +611,13 @@ struct user_settings
bool fade_on_stop; /* fade on pause/unpause/stop */
bool playlist_shuffle;
bool warnon_erase_dynplaylist; /* warn when erasing dynamic playlist */
+ bool keep_current_track_on_replace_playlist;
bool show_shuffled_adding_options; /* whether to display options for adding shuffled tracks to dynamic playlist */
int show_queue_options; /* how and whether to display options to queue tracks */
+#ifdef HAVE_ALBUMART
+ int album_art; /* switch off album art display or choose preferred source */
+#endif
+ bool rewind_across_tracks;
/* playlist viewer settings */
bool playlist_viewer_icons; /* display icons on viewer */
@@ -606,10 +642,9 @@ struct user_settings
/* power settings */
int poweroff; /* idle power off timer */
-#if BATTERY_CAPACITY_DEFAULT > 0
+#if BATTERY_CAPACITY_INC > 0
int battery_capacity; /* in mAh */
#endif
-
#if BATTERY_TYPES_COUNT > 1
int battery_type; /* for units which can take multiple types (Ondio). */
#endif
@@ -656,10 +691,8 @@ struct user_settings
#ifdef HAVE_BACKLIGHT
bool bl_selective_actions; /* backlight disable on some actions */
int bl_selective_actions_mask;/* mask of actions that will not enable backlight */
-#ifdef HAS_BUTTON_HOLD
int backlight_on_button_hold; /* what to do with backlight when hold
switch is on */
-#endif
#ifdef HAVE_LCD_SLEEP_SETTING
int lcd_sleep_after_backlight_off; /* when to put lcd to sleep after backlight
has turned off: -1=never, 0=always,
@@ -760,6 +793,9 @@ struct user_settings
bool sleeptimer_on_startup;
bool keypress_restarts_sleeptimer;
+ bool show_shutdown_message; /* toggle whether display lights up and displays message
+ when shutting down */
+
#ifdef HAVE_MORSE_INPUT
bool morse_input; /* text input method setting */
#endif
@@ -817,6 +853,11 @@ struct user_settings
#endif
int volume_limit; /* maximum volume limit */
+#ifdef HAVE_PERCEPTUAL_VOLUME
+ int volume_adjust_mode;
+ int volume_adjust_norm_steps;
+#endif
+
int surround_enabled;
int surround_balance;
int surround_fx1;
@@ -835,6 +876,12 @@ struct user_settings
#if defined(DX50) || defined(DX90) || (defined(HAVE_USB_POWER) && !defined(USB_NONE) && !defined(SIMULATOR))
int usb_mode;
#endif
+#if defined(BUTTON_REC) || \
+ (CONFIG_KEYPAD == GIGABEAT_PAD) || \
+ (CONFIG_KEYPAD == IPOD_4G_PAD) || \
+ (CONFIG_KEYPAD == IRIVER_H10_PAD)
+ bool clear_settings_on_hold;
+#endif
};
/** global variables **/
diff --git a/apps/settings_list.c b/apps/settings_list.c
index c13df734e6..74db7550bf 100644
--- a/apps/settings_list.c
+++ b/apps/settings_list.c
@@ -40,6 +40,8 @@
#include "powermgmt.h"
#include "kernel.h"
#include "open_plugin.h"
+#include "misc.h"
+#include "playback.h"
#ifdef HAVE_REMOTE_LCD
#include "lcd-remote.h"
#endif
@@ -60,7 +62,9 @@
#endif
#ifdef HAVE_HOTKEY
#include "onplay.h"
+#include "misc.h" /* current activity */
#endif
+#include "playlist.h"
#include "voice_thread.h"
@@ -138,6 +142,12 @@
(struct filename_setting[]){ \
{prefix,suffix,sizeof(global_settings.var)}}} }
+#define DIRECTORY_SETTING(flags,var,lang_id,name,default) \
+ {flags|F_DIRNAME|F_T_UCHARPTR, &global_settings.var, lang_id, \
+ CHARPTR(default), name, NULL, \
+ {.filename_setting=(struct filename_setting[]){ \
+ {NULL, NULL, sizeof(global_settings.var)}}}}
+
/* Used for settings which use the set_option() setting screen.
The ... arg is a list of pointers to strings to display in the setting
screen. These can either be literal strings, or ID2P(LANG_*) */
@@ -162,24 +172,18 @@
unit is the UNIT_ define to display/talk.
the first one saves a string to the config file,
the second one saves the variable value to the config file */
-#define INT_SETTING_W_CFGVALS(flags, var, lang_id, default, name, cfg_vals, \
- unit, min, max, step, formatter, get_talk_id, cb) \
- {flags|F_INT_SETTING|F_T_INT, &global_settings.var, \
- lang_id, INT(default), name, cfg_vals, \
- {.int_setting = (struct int_setting[]){ \
- {cb, unit, min, max, step, formatter, get_talk_id}}}}
#define INT_SETTING(flags, var, lang_id, default, name, \
unit, min, max, step, formatter, get_talk_id, cb) \
{flags|F_INT_SETTING|F_T_INT, &global_settings.var, \
lang_id, INT(default), name, NULL, \
{.int_setting = (struct int_setting[]){ \
- {cb, unit, min, max, step, formatter, get_talk_id}}}}
+ {cb, unit, step, min, max, formatter, get_talk_id}}}}
#define INT_SETTING_NOWRAP(flags, var, lang_id, default, name, \
unit, min, max, step, formatter, get_talk_id, cb) \
{flags|F_INT_SETTING|F_T_INT|F_NO_WRAP, &global_settings.var, \
lang_id, INT(default), name, NULL, \
{.int_setting = (struct int_setting[]){ \
- {cb, unit, min, max, step, formatter, get_talk_id}}}}
+ {cb, unit, step, min, max, formatter, get_talk_id}}}}
#define TABLE_SETTING(flags, var, lang_id, default, name, cfg_vals, \
unit, formatter, get_talk_id, cb, count, ...) \
@@ -240,8 +244,13 @@ static const char graphic_numeric[] = "graphic,numeric";
#define DEFAULT_FONTNAME "08-Rockfont"
#elif LCD_HEIGHT <= 220
#define DEFAULT_FONT_HEIGHT 12
-#elif LCD_HEIGHT <= 320
+#elif LCD_HEIGHT <= 240
#define DEFAULT_FONT_HEIGHT 15
+#elif LCD_HEIGHT <= 320
+ #define DEFAULT_FONT_HEIGHT 18
+#elif defined(SHANLING_Q1)
+ /* 16pt font looks pretty aliased & ugly */
+ #define DEFAULT_FONT_HEIGHT 18
#elif LCD_HEIGHT <= 400
#define DEFAULT_FONT_HEIGHT 16
#elif LCD_HEIGHT <= 480 && LCD_WIDTH < 800
@@ -261,7 +270,7 @@ static const char graphic_numeric[] = "graphic,numeric";
#endif
#ifdef HAVE_LCD_COLOR
- #if DEFAULT_FONT_HEIGHT >= 31
+ #if DEFAULT_FONT_HEIGHT >= 31 || defined(SHANLING_Q1)
#define DEFAULT_ICONSET "tango_icons.32x32"
#define DEFAULT_VIEWERS_ICONSET "tango_icons_viewers.32x32"
#elif DEFAULT_FONT_HEIGHT >= 23
@@ -342,13 +351,50 @@ static const char graphic_numeric[] = "graphic,numeric";
#endif
#endif /* HAVE_BACKLIGHT */
+#if defined(HAVE_USB_CHARGING_ENABLE)
+# if !defined(TARGET_USB_CHARGING_DEFAULT)
+# define TARGET_USB_CHARGING_DEFAULT USB_CHARGING_ENABLE
+# endif
+#endif
+
+#ifdef AUDIOHW_HAVE_POWER_MODE
+# ifndef TARGET_DEFAULT_DAC_POWER_MODE
+# define TARGET_DEFAULT_DAC_POWER_MODE SOUND_HIGH_POWER
+# endif
+#endif
+
+/*
+ * Total buffer size due to this setting = max files in dir * 52 bytes
+ * Keep this in mind when selecting the maximum - if the maximum is too
+ * high it's possible rockbox could hit OOM and become unusable until
+ * the config file is deleted manually.
+ *
+ * Note the FAT32 limit is 65534 files per directory, but this limit
+ * also applies to the database browser so it makes sense to support
+ * larger maximums.
+ */
+#if MEMORYSIZE >= 16
+# define MAX_FILES_IN_DIR_DEFAULT 5000
+# define MAX_FILES_IN_DIR_MAX 100000
+# define MAX_FILES_IN_DIR_STEP 1000
+#elif MEMORYSIZE >= 8
+# define MAX_FILES_IN_DIR_DEFAULT 5000
+# define MAX_FILES_IN_DIR_MAX 40000
+# define MAX_FILES_IN_DIR_STEP 500
+#else
+/* historical defaults, only for 2 MiB targets these days */
+# define MAX_FILES_IN_DIR_DEFAULT 1000
+# define MAX_FILES_IN_DIR_MAX 10000
+# define MAX_FILES_IN_DIR_STEP 50
+#endif
+
#if LCD_DEPTH > 1
static const char* list_pad_formatter(char *buffer, size_t buffer_size,
int val, const char *unit)
{
switch (val)
{
- case -1: return str(LANG_AUTOMATIC);
+ case -1: return str(LANG_AUTO);
case 0: return str(LANG_OFF);
default: break;
}
@@ -360,7 +406,7 @@ static int32_t list_pad_getlang(int value, int unit)
{
switch (value)
{
- case -1: return LANG_AUTOMATIC;
+ case -1: return LANG_AUTO;
case 0: return LANG_OFF;
default: return TALK_ID(value, unit);
}
@@ -580,53 +626,89 @@ static void eq_set_default(void* setting, void* defaultval)
memcpy(setting, defaultval, sizeof(struct eq_band_setting));
}
-#ifdef HAVE_QUICKSCREEN
-static int find_setting_by_name(char*name)
+#ifdef HAVE_PLAY_FREQ
+static const char* formatter_freq_unit_0_is_auto(char *buffer, size_t buffer_size,
+ int value, const char *unit)
+{
+ if (value == 0)
+ return str(LANG_AUTO);
+ else
+ return db_format(buffer, buffer_size, value / 100, unit);
+}
+
+static int32_t getlang_freq_unit_0_is_auto(int value, int unit)
{
- int i = 0;
- const struct settings_list *setting;
- if (!strcmp(name, "-"))
- return -1;
- while (i<nb_settings)
+ if (value == 0)
+ return LANG_AUTO;
+ else
+ return talk_value_decimal(value, unit, 3, false);
+}
+
+static void playback_frequency_callback(int sample_rate_hz)
+{
+ audio_set_playback_frequency(sample_rate_hz);
+}
+#endif /* HAVE_PLAY_FREQ */
+
+/* perform shuffle/unshuffle of the current playlist based on the boolean provided */
+static void shuffle_playlist_callback(bool shuffle)
+{
+ struct playlist_info *playlist = playlist_get_current();
+ if (playlist->started)
{
- setting = &settings[i];
- if (setting->cfg_name && !strcmp(setting->cfg_name, name))
+ if ((audio_status() & AUDIO_STATUS_PLAY) == AUDIO_STATUS_PLAY)
{
- return i;
+ replaygain_update();
+ if (shuffle)
+ {
+ playlist_randomise(playlist, current_tick, true);
+ }
+ else
+ {
+ playlist_sort(playlist, true);
+ }
}
- i++;
}
- return -1;
-}
-static void qs_load_from_cfg(void* var, char*value)
-{
- *(int*)var = find_setting_by_name(value);
}
-static char* qs_write_to_cfg(void* setting, char*buf, int buf_len)
+
+static void repeat_mode_callback(int repeat)
{
- int index = *(int*)setting;
- if (index < 0 || index >= nb_settings)
+ if ((audio_status() & AUDIO_STATUS_PLAY) == AUDIO_STATUS_PLAY)
{
- strlcpy(buf, "-", buf_len);
- return buf;
+ audio_flush_and_reload_tracks();
}
- const struct settings_list *var = &settings[index];
- strlcpy(buf, var->cfg_name, buf_len);
+ (void)repeat;
+}
+
+#ifdef HAVE_QUICKSCREEN
+static void qs_load_from_cfg(void *var, char *value)
+{
+ const struct settings_list **item = var;
+
+ if (*value == '-')
+ *item = NULL;
+ else
+ *item = find_setting_by_cfgname(value);
+}
+
+static char* qs_write_to_cfg(void *var, char *buf, int buf_len)
+{
+ const struct settings_list *setting = *(const struct settings_list **)var;
+
+ strmemccpy(buf, setting ? setting->cfg_name : "-", buf_len);
return buf;
}
-static bool qs_is_changed(void* setting, void* defaultval)
+
+static bool qs_is_changed(void* var, void* defaultval)
{
- int i = *(int*)setting;
- if (i < 0 || i >= nb_settings)
- return false;
- const struct settings_list *var = &settings[i];
- return var != find_setting(defaultval, NULL);
+ const struct settings_list *defaultsetting = find_setting(defaultval);
+
+ return var != defaultsetting;
}
-static void qs_set_default(void* setting, void* defaultval)
+
+static void qs_set_default(void* var, void* defaultval)
{
- if (defaultval == NULL)
- *(int*)setting = -1;
- find_setting(defaultval, (int*)setting);
+ *(const struct settings_list **)var = find_setting(defaultval);
}
#endif
#ifdef HAVE_TOUCHSCREEN
@@ -686,8 +768,11 @@ static void tsc_set_default(void* setting, void* defaultval)
#ifdef HAVE_HOTKEY
static void hotkey_callback(int var)
{
- if (get_hotkey_lang_id(var) == LANG_OPEN_PLUGIN)
- open_plugin_browse(ID2P(LANG_HOTKEY_WPS));
+ if (get_current_activity() != ACTIVITY_QUICKSCREEN)
+ {
+ if (get_hotkey(var)->action == HOTKEY_PLUGIN)
+ open_plugin_browse(ID2P(LANG_HOTKEY_WPS));
+ }
}
static const char* hotkey_formatter(char* buffer, size_t buffer_size, int value,
const char* unit)
@@ -695,12 +780,12 @@ static const char* hotkey_formatter(char* buffer, size_t buffer_size, int value,
(void)buffer;
(void)buffer_size;
(void)unit;
- return str(get_hotkey_lang_id(value));
+ return str(get_hotkey(value)->lang_id);
}
static int32_t hotkey_getlang(int value, int unit)
{
(void)unit;
- return get_hotkey_lang_id(value);
+ return get_hotkey(value)->lang_id;
}
#endif /* HAVE_HOTKEY */
@@ -848,9 +933,16 @@ const struct settings_list settings[] = {
#ifdef AUDIOHW_HAVE_FILTER_ROLL_OFF
CHOICE_SETTING(F_SOUNDSETTING, roll_off, LANG_FILTER_ROLL_OFF, 0,
-#if defined(AUDIOHW_HAVE_SHORT2_ROLL_OFF)
+#if defined(AUDIOHW_HAVE_ES9218_ROLL_OFF)
+ "roll_off", "linear fast,linear slow,minimum fast,minimum slow,apodizing 1,apodizing 2,hybrid fast,brick wall", sound_set_filter_roll_off,
+ 8, ID2P(LANG_FILTER_LINEAR_FAST), ID2P(LANG_FILTER_LINEAR_SLOW), ID2P(LANG_FILTER_MINIMUM_FAST), ID2P(LANG_FILTER_MINIMUM_SLOW),
+ ID2P(LANG_FILTER_APODIZING_1), ID2P(LANG_FILTER_APODIZING_2), ID2P(LANG_FILTER_HYBRID_FAST), ID2P(LANG_FILTER_BRICK_WALL)),
+#elif defined(AUDIOHW_HAVE_SHORT2_ROLL_OFF)
"roll_off", "sharp,slow,short sharp,short slow", sound_set_filter_roll_off,
4, ID2P(LANG_FILTER_SHARP), ID2P(LANG_FILTER_SLOW), ID2P(LANG_FILTER_SHORT_SHARP), ID2P(LANG_FILTER_SHORT_SLOW)),
+#elif defined(AUDIOHW_HAVE_SS_ROLL_OFF)
+ "roll_off", "sharp,slow,short sharp,short slow,super slow", sound_set_filter_roll_off,
+ 5, ID2P(LANG_FILTER_SHARP), ID2P(LANG_FILTER_SLOW), ID2P(LANG_FILTER_SHORT_SHARP), ID2P(LANG_FILTER_SHORT_SLOW), ID2P(LANG_FILTER_SUPER_SLOW)),
#elif defined(AUDIOHW_HAVE_SHORT_ROLL_OFF)
"roll_off", "sharp,slow,short,bypass", sound_set_filter_roll_off,
4, ID2P(LANG_FILTER_SHARP), ID2P(LANG_FILTER_SLOW), ID2P(LANG_FILTER_SHORT), ID2P(LANG_FILTER_BYPASS)),
@@ -861,23 +953,26 @@ const struct settings_list settings[] = {
#endif
#ifdef AUDIOHW_HAVE_POWER_MODE
- CHOICE_SETTING(F_SOUNDSETTING, power_mode, LANG_DAC_POWER_MODE, 0,
+ CHOICE_SETTING(F_SOUNDSETTING, power_mode, LANG_DAC_POWER_MODE,
+ TARGET_DEFAULT_DAC_POWER_MODE,
"dac_power_mode", "high,low", sound_set_power_mode,
2, ID2P(LANG_DAC_POWER_HIGH), ID2P(LANG_DAC_POWER_LOW)),
#endif
/* playback */
- OFFON_SETTING(0, playlist_shuffle, LANG_SHUFFLE, false, "shuffle", NULL),
+ OFFON_SETTING(F_CB_ON_SELECT_ONLY|F_CB_ONLY_IF_CHANGED, playlist_shuffle,
+ LANG_SHUFFLE, false, "shuffle", shuffle_playlist_callback),
+
SYSTEM_SETTING(NVRAM(4), resume_index, -1),
SYSTEM_SETTING(NVRAM(4), resume_crc32, -1),
SYSTEM_SETTING(NVRAM(4), resume_elapsed, -1),
SYSTEM_SETTING(NVRAM(4), resume_offset, -1),
- CHOICE_SETTING(0, repeat_mode, LANG_REPEAT, REPEAT_OFF, "repeat",
- "off,all,one,shuffle"
+ CHOICE_SETTING(F_CB_ON_SELECT_ONLY|F_CB_ONLY_IF_CHANGED, repeat_mode,
+ LANG_REPEAT, REPEAT_OFF, "repeat", "off,all,one,shuffle"
#ifdef AB_REPEAT_ENABLE
",ab"
#endif
- , NULL,
+ , repeat_mode_callback,
#ifdef AB_REPEAT_ENABLE
5,
#else
@@ -890,28 +985,41 @@ const struct settings_list settings[] = {
#endif
), /* CHOICE_SETTING( repeat_mode ) */
#ifdef HAVE_PLAY_FREQ
- STRINGCHOICE_SETTING(0, play_frequency, LANG_FREQUENCY, 0,
+ TABLE_SETTING(F_SOUNDSETTING|F_CB_ON_SELECT_ONLY|F_CB_ONLY_IF_CHANGED,
+ play_frequency, LANG_FREQUENCY, 0, "playback frequency", "auto",
+ UNIT_KHZ, formatter_freq_unit_0_is_auto,
+ getlang_freq_unit_0_is_auto,
+ playback_frequency_callback,
#if HAVE_PLAY_FREQ >= 192
- "playback frequency", "auto,44.1 kHz,48 kHz,88.2 kHz,96 kHz,176.4 kHz,192 kHz", NULL, 7,
- LANG_AUTOMATIC, TALK_ID_DECIMAL(441, 1, UNIT_KHZ), TALK_ID(48, UNIT_KHZ), TALK_ID_DECIMAL(882, 1, UNIT_KHZ), TALK_ID(96, UNIT_KHZ), TALK_ID_DECIMAL(1764, 1, UNIT_KHZ), TALK_ID(192, UNIT_KHZ)),
+ 7,0,SAMPR_44,SAMPR_48,SAMPR_88,SAMPR_96,SAMPR_176,SAMPR_192),
#elif HAVE_PLAY_FREQ >= 96
- "playback frequency", "auto,44.1 kHz,48 kHz,88.2 kHz,96 kHz", NULL, 5,
- LANG_AUTOMATIC, TALK_ID_DECIMAL(441, 1, UNIT_KHZ), TALK_ID(48, UNIT_KHZ), TALK_ID_DECIMAL(882, 1, UNIT_KHZ), TALK_ID(96, UNIT_KHZ)),
+ 5,0,SAMPR_44,SAMPR_48,SAMPR_88,SAMPR_96),
#elif HAVE_PLAY_FREQ >= 48
- "playback frequency", "auto,44.1 kHz,48 kHz", NULL, 3,
- LANG_AUTOMATIC, TALK_ID_DECIMAL(441, 1, UNIT_KHZ), TALK_ID(48, UNIT_KHZ)),
+ 3,0,SAMPR_44,SAMPR_48),
#else
#error "HAVE_PLAY_FREQ < 48???"
#endif
#endif /* HAVE_PLAY_FREQ */
+
+#ifdef HAVE_ALBUMART
+ CHOICE_SETTING(0, album_art, LANG_ALBUM_ART, 1,
+ "album art", "off,prefer embedded,prefer image file",
+ NULL, 3,
+ ID2P(LANG_OFF),
+ ID2P(LANG_PREFER_EMBEDDED),
+ ID2P(LANG_PREFER_IMAGE_FILE)),
+#endif
+
/* LCD */
#ifdef HAVE_LCD_CONTRAST
/* its easier to leave this one un-macro()ed for the time being */
{ F_T_INT|F_DEF_ISFUNC|F_INT_SETTING, &global_settings.contrast,
LANG_CONTRAST, FUNCTYPE(lcd_default_contrast), "contrast", NULL , {
.int_setting = (struct int_setting[]) {
- { lcd_set_contrast, UNIT_INT, MIN_CONTRAST_SETTING,
- MAX_CONTRAST_SETTING, 1, NULL, NULL }}}},
+ { .option_callback = lcd_set_contrast,
+ .unit = UNIT_INT, .step = 1,
+ .min = MIN_CONTRAST_SETTING, .max = MAX_CONTRAST_SETTING,
+ .formatter = NULL, .get_talk_id = NULL }}}},
#endif
#ifdef HAVE_BACKLIGHT
TABLE_SETTING_LIST(F_TIME_SETTING | F_ALLOW_ARBITRARY_VALS,
@@ -1011,30 +1119,33 @@ const struct settings_list settings[] = {
"max files in playlist", UNIT_INT, 1000, 32000, 1000,
NULL, NULL, NULL),
INT_SETTING(F_BANFROMQS, max_files_in_dir, LANG_MAX_FILES_IN_DIR,
-#if MEMORYSIZE > 1
- 1000,
-#else
- 200,
+ MAX_FILES_IN_DIR_DEFAULT, "max files in dir", UNIT_INT,
+ MAX_FILES_IN_DIR_STEP /* min */, MAX_FILES_IN_DIR_MAX,
+ MAX_FILES_IN_DIR_STEP,
+ NULL, NULL, NULL),
+#ifdef HAVE_PERCEPTUAL_VOLUME
+ CHOICE_SETTING(0, volume_adjust_mode, LANG_VOLUME_ADJUST_MODE,
+ VOLUME_ADJUST_DIRECT, "volume adjustment mode",
+ "direct,perceptual", NULL, 2,
+ ID2P(LANG_DIRECT), ID2P(LANG_PERCEPTUAL)),
+ INT_SETTING_NOWRAP(0, volume_adjust_norm_steps, LANG_VOLUME_ADJUST_NORM_STEPS,
+ 50, "perceptual volume step count", UNIT_INT,
+ MIN_NORM_VOLUME_STEPS, MAX_NORM_VOLUME_STEPS, 5,
+ NULL, NULL, NULL),
#endif
- "max files in dir", UNIT_INT, 50, 10000, 50,
- NULL, NULL, NULL),
/* use this setting for user code even if there's no exchangable battery
* support enabled */
-#if BATTERY_CAPACITY_DEFAULT > 0
-/* define min/max/inc for this file if there's only one battery */
-#ifndef BATTERY_CAPACITY_MIN
-#define BATTERY_CAPACITY_MIN BATTERY_CAPACITY_DEFAULT
-#define BATTERY_CAPACITY_MAX BATTERY_CAPACITY_DEFAULT
-#define BATTERY_CAPACITY_INC 0
-#endif
+#if BATTERY_CAPACITY_INC > 0
#if defined(IPOD_VIDEO) && !defined(SIMULATOR)
/* its easier to leave this one un-macro()ed for the time being */
{ F_T_INT|F_DEF_ISFUNC|F_INT_SETTING, &global_settings.battery_capacity,
LANG_BATTERY_CAPACITY, FUNCTYPE(battery_default_capacity),
"battery capacity", NULL , {
.int_setting = (struct int_setting[]) {
- { set_battery_capacity, UNIT_MAH, BATTERY_CAPACITY_MIN,
- BATTERY_CAPACITY_MAX, BATTERY_CAPACITY_INC, NULL, NULL }}}},
+ { .option_callback = set_battery_capacity,
+ .unit = UNIT_MAH, .step = BATTERY_CAPACITY_INC,
+ .min = BATTERY_CAPACITY_MIN, .max = BATTERY_CAPACITY_MAX,
+ .formatter = NULL, .get_talk_id = NULL }}}},
#else /* IPOD_VIDEO */
INT_SETTING(0, battery_capacity, LANG_BATTERY_CAPACITY,
BATTERY_CAPACITY_DEFAULT, "battery capacity", UNIT_MAH,
@@ -1065,9 +1176,12 @@ const struct settings_list settings[] = {
#endif
/* tuner */
#if CONFIG_TUNER
- OFFON_SETTING(0,fm_force_mono, LANG_FM_MONO_MODE,
+ OFFON_SETTING(0, fm_force_mono, LANG_FM_MONO_MODE,
false, "force fm mono", toggle_mono_mode),
- SYSTEM_SETTING(NVRAM(4),last_frequency,0),
+ SYSTEM_SETTING(NVRAM(4), last_frequency, 0),
+#endif
+#if defined(HAVE_RDS_CAP) && defined(CONFIG_RTC)
+ OFFON_SETTING(0, sync_rds_time, LANG_FM_SYNC_RDS_TIME, false, "sync_rds_time", NULL),
#endif
#if BATTERY_TYPES_COUNT > 1
@@ -1195,15 +1309,24 @@ const struct settings_list settings[] = {
lcd_remote_bidir_scroll),
#endif
OFFON_SETTING(0, offset_out_of_view, LANG_SCREEN_SCROLL_VIEW,
- false, "Screen Scrolls Out Of View",
- gui_list_screen_scroll_out_of_view),
+ false, "Screen Scrolls Out Of View", NULL),
INT_SETTING(F_PADTITLE, scroll_step, LANG_SCROLL_STEP, 6, "scroll step",
UNIT_PIXEL, 1, LCD_WIDTH, 1, NULL, NULL, lcd_scroll_step),
INT_SETTING(F_PADTITLE, screen_scroll_step, LANG_SCREEN_SCROLL_STEP, 16,
- "screen scroll step", UNIT_PIXEL, 1, LCD_WIDTH, 1, NULL, NULL,
- gui_list_screen_scroll_step),
+ "screen scroll step", UNIT_PIXEL, 1, LCD_WIDTH, 1, NULL, NULL, NULL),
OFFON_SETTING(0,scroll_paginated,LANG_SCROLL_PAGINATED,
false,"scroll paginated",NULL),
+ OFFON_SETTING(0,list_wraparound,LANG_LIST_WRAPAROUND,
+ true,"list wraparound",NULL),
+ CHOICE_SETTING(0, list_order, LANG_LIST_ORDER,
+#if defined(HAVE_SCROLLWHEEL) && !defined(FIIO_M3K)
+ 1,
+#else
+ 0,
+#endif
+ /* values are defined by the enum in option_select.h */
+ "list order", "descending,ascending",
+ NULL, 2, ID2P(LANG_DESCENDING), ID2P(LANG_ASCENDING)),
#ifdef HAVE_LCD_COLOR
{F_T_INT|F_RGB|F_THEMESETTING ,&global_settings.fg_color,-1,
@@ -1222,6 +1345,18 @@ const struct settings_list settings[] = {
#endif
/* more playback */
OFFON_SETTING(0,play_selected,LANG_PLAY_SELECTED,true,"play selected",NULL),
+ CHOICE_SETTING(0, single_mode, LANG_SINGLE_MODE, 0,
+ "single mode",
+ "off,track,album,album artist,artist,composer,work,genre",
+ NULL, 8,
+ ID2P(LANG_OFF),
+ ID2P(LANG_TRACK),
+ ID2P(LANG_ID3_ALBUM),
+ ID2P(LANG_ID3_ALBUMARTIST),
+ ID2P(LANG_ID3_ARTIST),
+ ID2P(LANG_ID3_COMPOSER),
+ ID2P(LANG_ID3_GROUPING),
+ ID2P(LANG_ID3_GENRE)),
OFFON_SETTING(0,party_mode,LANG_PARTY_MODE,false,"party mode",NULL),
OFFON_SETTING(0,fade_on_stop,LANG_FADE_ON_STOP,true,"volume fade",NULL),
INT_SETTING(F_TIME_SETTING, ff_rewind_min_step, LANG_FFRW_STEP, 1,
@@ -1415,7 +1550,7 @@ const struct settings_list settings[] = {
LANG_FREQUENCY,INT(REC_FREQ_DEFAULT),
"rec frequency",REC_FREQ_CFG_VAL_LIST,UNUSED},
{F_T_INT|F_RECSETTING,&global_settings.rec_format,
- LANG_RECORDING_FORMAT,INT(REC_FORMAT_DEFAULT),
+ LANG_FORMAT,INT(REC_FORMAT_DEFAULT),
"rec format",REC_FORMAT_CFG_VAL_LIST,UNUSED},
/** Encoder settings start - keep these together **/
/* aiff_enc */
@@ -1493,6 +1628,8 @@ const struct settings_list settings[] = {
"gather runtime data", NULL),
TEXT_SETTING(0, tagcache_scan_paths, "database scan paths",
DEFAULT_TAGCACHE_SCAN_PATHS, NULL, NULL),
+ TEXT_SETTING(0, tagcache_db_path, "database path",
+ ROCKBOX_DIR, NULL, NULL),
#endif
/* replay gain */
@@ -1560,106 +1697,12 @@ const struct settings_list settings[] = {
/* equalizer */
OFFON_SETTING(F_EQSETTING, eq_enabled, LANG_EQUALIZER_ENABLED, false,
- "eq enabled", NULL),
+ "eq enabled", eq_enabled_option_callback),
INT_SETTING_NOWRAP(F_EQSETTING, eq_precut, LANG_EQUALIZER_PRECUT, 0,
"eq precut", UNIT_DB, 0, 240, 1, eq_precut_format,
get_precut_talkid, dsp_set_eq_precut),
- /* 0..32768 Hz */
- INT_SETTING_NOWRAP(F_DEPRECATED|F_EQSETTING, eq_band_settings[0].cutoff, LANG_EQUALIZER_BAND_CUTOFF,
- 32, "eq band 0 cutoff", UNIT_HERTZ, EQ_CUTOFF_MIN,
- EQ_CUTOFF_MAX, EQ_CUTOFF_STEP, NULL, NULL, NULL),
- INT_SETTING_NOWRAP(F_DEPRECATED|F_EQSETTING, eq_band_settings[1].cutoff, LANG_EQUALIZER_BAND_CENTER,
- 64, "eq band 1 cutoff", UNIT_HERTZ, EQ_CUTOFF_MIN,
- EQ_CUTOFF_MAX, EQ_CUTOFF_STEP, NULL, NULL, NULL),
- INT_SETTING_NOWRAP(F_DEPRECATED|F_EQSETTING, eq_band_settings[2].cutoff, LANG_EQUALIZER_BAND_CENTER,
- 125, "eq band 2 cutoff", UNIT_HERTZ, EQ_CUTOFF_MIN,
- EQ_CUTOFF_MAX, EQ_CUTOFF_STEP, NULL, NULL, NULL),
- INT_SETTING_NOWRAP(F_DEPRECATED|F_EQSETTING, eq_band_settings[3].cutoff, LANG_EQUALIZER_BAND_CENTER,
- 250, "eq band 3 cutoff", UNIT_HERTZ, EQ_CUTOFF_MIN,
- EQ_CUTOFF_MAX, EQ_CUTOFF_STEP, NULL, NULL, NULL),
- INT_SETTING_NOWRAP(F_DEPRECATED|F_EQSETTING, eq_band_settings[4].cutoff, LANG_EQUALIZER_BAND_CENTER,
- 500, "eq band 4 cutoff", UNIT_HERTZ, EQ_CUTOFF_MIN,
- EQ_CUTOFF_MAX, EQ_CUTOFF_STEP, NULL, NULL, NULL),
- INT_SETTING_NOWRAP(F_DEPRECATED|F_EQSETTING, eq_band_settings[5].cutoff, LANG_EQUALIZER_BAND_CENTER,
- 1000, "eq band 5 cutoff", UNIT_HERTZ, EQ_CUTOFF_MIN,
- EQ_CUTOFF_MAX, EQ_CUTOFF_STEP, NULL, NULL, NULL),
- INT_SETTING_NOWRAP(F_DEPRECATED|F_EQSETTING, eq_band_settings[6].cutoff, LANG_EQUALIZER_BAND_CENTER,
- 2000, "eq band 6 cutoff", UNIT_HERTZ, EQ_CUTOFF_MIN,
- EQ_CUTOFF_MAX, EQ_CUTOFF_STEP, NULL, NULL, NULL),
- INT_SETTING_NOWRAP(F_DEPRECATED|F_EQSETTING, eq_band_settings[7].cutoff, LANG_EQUALIZER_BAND_CENTER,
- 4000, "eq band 7 cutoff", UNIT_HERTZ, EQ_CUTOFF_MIN,
- EQ_CUTOFF_MAX, EQ_CUTOFF_STEP, NULL, NULL, NULL),
- INT_SETTING_NOWRAP(F_DEPRECATED|F_EQSETTING, eq_band_settings[8].cutoff, LANG_EQUALIZER_BAND_CENTER,
- 8000, "eq band 8 cutoff", UNIT_HERTZ, EQ_CUTOFF_MIN,
- EQ_CUTOFF_MAX, EQ_CUTOFF_STEP, NULL, NULL, NULL),
- INT_SETTING_NOWRAP(F_DEPRECATED|F_EQSETTING, eq_band_settings[9].cutoff, LANG_EQUALIZER_BAND_CUTOFF,
- 16000, "eq band 9 cutoff", UNIT_HERTZ, EQ_CUTOFF_MIN,
- EQ_CUTOFF_MAX, EQ_CUTOFF_STEP, NULL, NULL, NULL),
- /* 0..64 (or 0.0 to 6.4) */
- INT_SETTING_NOWRAP(F_DEPRECATED|F_EQSETTING, eq_band_settings[0].q, LANG_EQUALIZER_BAND_Q, 7,
- "eq band 0 q", UNIT_INT, EQ_Q_MIN, EQ_Q_MAX, EQ_Q_STEP,
- eq_q_format, get_dec_talkid, NULL),
- INT_SETTING_NOWRAP(F_DEPRECATED|F_EQSETTING, eq_band_settings[1].q, LANG_EQUALIZER_BAND_Q, 10,
- "eq band 1 q", UNIT_INT, EQ_Q_MIN, EQ_Q_MAX, EQ_Q_STEP,
- eq_q_format, get_dec_talkid, NULL),
- INT_SETTING_NOWRAP(F_DEPRECATED|F_EQSETTING, eq_band_settings[2].q, LANG_EQUALIZER_BAND_Q, 10,
- "eq band 2 q", UNIT_INT, EQ_Q_MIN, EQ_Q_MAX, EQ_Q_STEP,
- eq_q_format, get_dec_talkid, NULL),
- INT_SETTING_NOWRAP(F_DEPRECATED|F_EQSETTING, eq_band_settings[3].q, LANG_EQUALIZER_BAND_Q, 10,
- "eq band 3 q", UNIT_INT, EQ_Q_MIN, EQ_Q_MAX, EQ_Q_STEP,
- eq_q_format, get_dec_talkid, NULL),
- INT_SETTING_NOWRAP(F_DEPRECATED|F_EQSETTING, eq_band_settings[4].q, LANG_EQUALIZER_BAND_Q, 10,
- "eq band 4 q", UNIT_INT, EQ_Q_MIN, EQ_Q_MAX, EQ_Q_STEP,
- eq_q_format, get_dec_talkid, NULL),
- INT_SETTING_NOWRAP(F_DEPRECATED|F_EQSETTING, eq_band_settings[5].q, LANG_EQUALIZER_BAND_Q, 10,
- "eq band 5 q", UNIT_INT, EQ_Q_MIN, EQ_Q_MAX, EQ_Q_STEP,
- eq_q_format, get_dec_talkid, NULL),
- INT_SETTING_NOWRAP(F_DEPRECATED|F_EQSETTING, eq_band_settings[6].q, LANG_EQUALIZER_BAND_Q, 10,
- "eq band 6 q", UNIT_INT, EQ_Q_MIN, EQ_Q_MAX, EQ_Q_STEP,
- eq_q_format, get_dec_talkid, NULL),
- INT_SETTING_NOWRAP(F_DEPRECATED|F_EQSETTING, eq_band_settings[7].q, LANG_EQUALIZER_BAND_Q, 10,
- "eq band 7 q", UNIT_INT, EQ_Q_MIN, EQ_Q_MAX, EQ_Q_STEP,
- eq_q_format, get_dec_talkid, NULL),
- INT_SETTING_NOWRAP(F_DEPRECATED|F_EQSETTING, eq_band_settings[8].q, LANG_EQUALIZER_BAND_Q, 10,
- "eq band 8 q", UNIT_INT, EQ_Q_MIN, EQ_Q_MAX, EQ_Q_STEP,
- eq_q_format, get_dec_talkid, NULL),
- INT_SETTING_NOWRAP(F_DEPRECATED|F_EQSETTING, eq_band_settings[9].q, LANG_EQUALIZER_BAND_Q, 7,
- "eq band 9 q", UNIT_INT, EQ_Q_MIN, EQ_Q_MAX, EQ_Q_STEP,
- eq_q_format, get_dec_talkid, NULL),
- /* -240..240 (or -24db to +24db) */
- INT_SETTING_NOWRAP(F_DEPRECATED|F_EQSETTING, eq_band_settings[0].gain, LANG_GAIN, 0,
- "eq band 0 gain", UNIT_DB, EQ_GAIN_MIN, EQ_GAIN_MAX,
- EQ_GAIN_STEP, db_format, get_dec_talkid, NULL),
- INT_SETTING_NOWRAP(F_DEPRECATED|F_EQSETTING, eq_band_settings[1].gain, LANG_GAIN, 0,
- "eq band 1 gain", UNIT_DB, EQ_GAIN_MIN, EQ_GAIN_MAX,
- EQ_GAIN_STEP, db_format, get_dec_talkid, NULL),
- INT_SETTING_NOWRAP(F_DEPRECATED|F_EQSETTING, eq_band_settings[2].gain, LANG_GAIN, 0,
- "eq band 2 gain", UNIT_DB, EQ_GAIN_MIN, EQ_GAIN_MAX,
- EQ_GAIN_STEP, db_format, get_dec_talkid, NULL),
- INT_SETTING_NOWRAP(F_DEPRECATED|F_EQSETTING, eq_band_settings[3].gain, LANG_GAIN, 0,
- "eq band 3 gain", UNIT_DB, EQ_GAIN_MIN, EQ_GAIN_MAX,
- EQ_GAIN_STEP, db_format, get_dec_talkid, NULL),
- INT_SETTING_NOWRAP(F_DEPRECATED|F_EQSETTING, eq_band_settings[4].gain, LANG_GAIN, 0,
- "eq band 4 gain", UNIT_DB, EQ_GAIN_MIN, EQ_GAIN_MAX,
- EQ_GAIN_STEP, db_format, get_dec_talkid, NULL),
- INT_SETTING_NOWRAP(F_DEPRECATED|F_EQSETTING, eq_band_settings[5].gain, LANG_GAIN, 0,
- "eq band 5 gain", UNIT_DB, EQ_GAIN_MIN, EQ_GAIN_MAX,
- EQ_GAIN_STEP, db_format, get_dec_talkid, NULL),
- INT_SETTING_NOWRAP(F_DEPRECATED|F_EQSETTING, eq_band_settings[6].gain, LANG_GAIN, 0,
- "eq band 6 gain", UNIT_DB, EQ_GAIN_MIN, EQ_GAIN_MAX,
- EQ_GAIN_STEP, db_format, get_dec_talkid, NULL),
- INT_SETTING_NOWRAP(F_DEPRECATED|F_EQSETTING, eq_band_settings[7].gain, LANG_GAIN, 0,
- "eq band 7 gain", UNIT_DB, EQ_GAIN_MIN, EQ_GAIN_MAX,
- EQ_GAIN_STEP, db_format, get_dec_talkid, NULL),
- INT_SETTING_NOWRAP(F_DEPRECATED|F_EQSETTING, eq_band_settings[8].gain, LANG_GAIN, 0,
- "eq band 8 gain", UNIT_DB, EQ_GAIN_MIN, EQ_GAIN_MAX,
- EQ_GAIN_STEP, db_format, get_dec_talkid, NULL),
- INT_SETTING_NOWRAP(F_DEPRECATED|F_EQSETTING, eq_band_settings[9].gain, LANG_GAIN, 0,
- "eq band 9 gain", UNIT_DB, EQ_GAIN_MIN, EQ_GAIN_MAX,
- EQ_GAIN_STEP, db_format, get_dec_talkid, NULL),
-
#define EQ_BAND(id, string) \
CUSTOM_SETTING(F_EQSETTING, eq_band_settings[id], -1, \
&eq_defaults[id], string, \
@@ -1794,12 +1837,14 @@ const struct settings_list settings[] = {
ID2P(LANG_CODEPAGE_JAPANESE),
ID2P(LANG_CODEPAGE_SIMPLIFIED), ID2P(LANG_CODEPAGE_KOREAN),
ID2P(LANG_CODEPAGE_TRADITIONAL), ID2P(LANG_CODEPAGE_UTF8)),
+
OFFON_SETTING(0, warnon_erase_dynplaylist, LANG_WARN_ERASEDYNPLAYLIST_MENU,
true, "warn when erasing dynamic playlist",NULL),
-
- OFFON_SETTING(0, show_shuffled_adding_options, LANG_SHOW_SHUFFLED_ADDING_OPTIONS, true,
+ OFFON_SETTING(0, keep_current_track_on_replace_playlist, LANG_KEEP_CURRENT_TRACK_ON_REPLACE,
+ true, "keep current track when replacing playlist",NULL),
+ OFFON_SETTING(0, show_shuffled_adding_options, LANG_SHOW_SHUFFLED_ADDING_OPTIONS, false,
"show shuffled adding options", NULL),
- CHOICE_SETTING(0, show_queue_options, LANG_SHOW_QUEUE_OPTIONS, 1,
+ CHOICE_SETTING(0, show_queue_options, LANG_SHOW_QUEUE_OPTIONS, 0,
"show queue options", "off,on,in submenu",
NULL, 3,
ID2P(LANG_SET_BOOL_NO),
@@ -1807,12 +1852,16 @@ const struct settings_list settings[] = {
ID2P(LANG_IN_SUBMENU)),
#ifdef HAVE_BACKLIGHT
-#ifdef HAS_BUTTON_HOLD
- CHOICE_SETTING(0, backlight_on_button_hold, LANG_BACKLIGHT_ON_BUTTON_HOLD,
- 1, "backlight on button hold", "normal,off,on",
+ CHOICE_SETTING(0, backlight_on_button_hold,
+ LANG_BACKLIGHT_ON_BUTTON_HOLD,
+#ifdef HAS_BUTTON_HOLD
+ 1,
+#else
+ 0,
+#endif
+ "backlight on button hold", "normal,off,on",
backlight_set_on_button_hold, 3,
ID2P(LANG_NORMAL), ID2P(LANG_OFF), ID2P(LANG_ON)),
-#endif
#ifdef HAVE_LCD_SLEEP_SETTING
TABLE_SETTING_LIST(F_TIME_SETTING | F_ALLOW_ARBITRARY_VALS,
@@ -1873,8 +1922,6 @@ const struct settings_list settings[] = {
ID2P(LANG_FM_ITALY), ID2P(LANG_FM_OTHER)),
#endif
- OFFON_SETTING(F_BANFROMQS, audioscrobbler, LANG_AUDIOSCROBBLER, false,
- "Last.fm Logging", NULL),
#if CONFIG_TUNER
TEXT_SETTING(0, fmr_file, "fmr", "-",
FMPRESET_PATH "/", ".fmr"),
@@ -1911,7 +1958,7 @@ const struct settings_list settings[] = {
#endif
TEXT_SETTING(0,kbd_file,"kbd","-",ROCKBOX_DIR "/",".kbd"),
#ifdef HAVE_USB_CHARGING_ENABLE
- CHOICE_SETTING(0, usb_charging, LANG_USB_CHARGING, 1, "usb charging",
+ CHOICE_SETTING(0, usb_charging, LANG_USB_CHARGING, TARGET_USB_CHARGING_DEFAULT, "usb charging",
"off,on,force", NULL, 3, ID2P(LANG_SET_BOOL_NO),
ID2P(LANG_SET_BOOL_YES), ID2P(LANG_FORCE)),
#endif
@@ -2037,6 +2084,10 @@ const struct settings_list settings[] = {
"sleeptimer on startup", NULL),
OFFON_SETTING(0, keypress_restarts_sleeptimer, LANG_KEYPRESS_RESTARTS_SLEEP_TIMER, false,
"keypress restarts sleeptimer", set_keypress_restarts_sleep_timer),
+
+ OFFON_SETTING(0, show_shutdown_message, LANG_SHOW_SHUTDOWN_MESSAGE, true,
+ "show shutdown message", NULL),
+
#ifdef HAVE_TOUCHPAD_SENSITIVITY_SETTING
/* If specific values are set for touchpad sensitivity setting we use those */
#if (defined(MAX_TOUCHPAD_SENSITIVITY_SETTING) \
@@ -2098,7 +2149,7 @@ const struct settings_list settings[] = {
tsc_is_changed, tsc_set_default),
#endif
OFFON_SETTING(0, prevent_skip, LANG_PREVENT_SKIPPING, false, "prevent track skip", NULL),
-
+ OFFON_SETTING(0, rewind_across_tracks, LANG_REWIND_ACROSS_TRACKS, false, "rewind across tracks", NULL),
#ifdef HAVE_PITCHCONTROL
OFFON_SETTING(0, pitch_mode_semitone, LANG_SEMITONE, false,
"Semitone pitch change", NULL),
@@ -2142,18 +2193,31 @@ const struct settings_list settings[] = {
#endif
#ifdef HAVE_HOTKEY
- TABLE_SETTING(F_ALLOW_ARBITRARY_VALS | F_CB_ON_SELECT_ONLY, hotkey_wps,
+ TABLE_SETTING(F_CB_ON_SELECT_ONLY, hotkey_wps,
LANG_HOTKEY_WPS, HOTKEY_VIEW_PLAYLIST, "hotkey wps",
- "off,view playlist,show track info,pitchscreen,open with,delete,bookmark,plugin"
- ,UNIT_INT, hotkey_formatter, hotkey_getlang, hotkey_callback,8, HOTKEY_OFF,
+ "off,view playlist,show track info,pitchscreen,open with,delete,bookmark,plugin,bookmark list"
+ ,UNIT_INT, hotkey_formatter, hotkey_getlang, hotkey_callback,9, HOTKEY_OFF,
HOTKEY_VIEW_PLAYLIST, HOTKEY_SHOW_TRACK_INFO, HOTKEY_PITCHSCREEN,
- HOTKEY_OPEN_WITH, HOTKEY_DELETE, HOTKEY_BOOKMARK, HOTKEY_PLUGIN),
- TABLE_SETTING(F_ALLOW_ARBITRARY_VALS, hotkey_tree,
+ HOTKEY_OPEN_WITH, HOTKEY_DELETE, HOTKEY_BOOKMARK, HOTKEY_PLUGIN, HOTKEY_BOOKMARK_LIST),
+ TABLE_SETTING(0, hotkey_tree,
LANG_HOTKEY_FILE_BROWSER, HOTKEY_OFF, "hotkey tree",
- "off,open with,delete,insert,insert shuffled",
- UNIT_INT, hotkey_formatter, hotkey_getlang, NULL, 5, HOTKEY_OFF,
- HOTKEY_OPEN_WITH, HOTKEY_DELETE, HOTKEY_INSERT, HOTKEY_INSERT_SHUFFLED),
+#ifdef HAVE_TAGCACHE
+ "off,properties,pictureflow,open with,delete,insert,insert shuffled",
+#else
+ "off,properties,open with,delete,insert,insert shuffled",
#endif
+ UNIT_INT, hotkey_formatter, hotkey_getlang, NULL,
+#ifdef HAVE_TAGCACHE
+ 7,
+#else
+ 6,
+#endif
+ HOTKEY_OFF,HOTKEY_PROPERTIES,
+#ifdef HAVE_TAGCACHE
+ HOTKEY_PICTUREFLOW,
+#endif
+ HOTKEY_OPEN_WITH, HOTKEY_DELETE, HOTKEY_INSERT, HOTKEY_INSERT_SHUFFLED),
+#endif /* HAVE_HOTKEY */
INT_SETTING(F_TIME_SETTING, resume_rewind, LANG_RESUME_REWIND, 0,
"resume rewind", UNIT_SEC, 0, 60, 5,
@@ -2185,19 +2249,18 @@ const struct settings_list settings[] = {
LANG_USB_MODE,
USBMODE_DEFAULT,
"usb mode",
- "ask,mass storage,charge"
+ "mass storage,charge"
#if defined(DX50) || defined(DX90)
",adb"
#endif
,
#if defined(DX50) || defined(DX90)
ibasso_set_usb_mode,
- 4,
+ 3,
#else
usb_set_mode,
- 3,
+ 2,
#endif
- ID2P(LANG_ASK),
ID2P(LANG_USB_MODE_MASS_STORAGE),
ID2P(LANG_USB_MODE_CHARGE)
#if defined(DX50) || defined(DX90)
@@ -2205,6 +2268,13 @@ const struct settings_list settings[] = {
#endif
),
#endif
+#if defined(BUTTON_REC) || \
+ (CONFIG_KEYPAD == GIGABEAT_PAD) || \
+ (CONFIG_KEYPAD == IPOD_4G_PAD) || \
+ (CONFIG_KEYPAD == IRIVER_H10_PAD)
+ OFFON_SETTING(0, clear_settings_on_hold, LANG_CLEAR_SETTINGS_ON_HOLD,
+ true, "clear settings on hold", NULL),
+#endif
};
const int nb_settings = sizeof(settings)/sizeof(*settings);
diff --git a/apps/settings_list.h b/apps/settings_list.h
index 36c4d8062f..d16d7e255b 100644
--- a/apps/settings_list.h
+++ b/apps/settings_list.h
@@ -69,10 +69,11 @@ struct filename_setting {
struct int_setting {
void (*option_callback)(int);
- int unit;
+ int16_t unit;
+ int16_t step;
int min;
int max;
- int step;
+
const char* (*formatter)(char*, size_t, int, const char*);
int32_t (*get_talk_id)(int, int);
};
@@ -100,7 +101,7 @@ struct table_setting {
};
#define F_TABLE_SETTING 0x2000
#define F_ALLOW_ARBITRARY_VALS 0x4000
-#define F_CB_ON_SELECT_ONLY 0x20000
+
/* these use the _isfunc_type type for the function */
/* typedef int (*_isfunc_type)(void); */
#define F_MIN_ISFUNC 0x100000 /* min(above) is function pointer to above type */
@@ -151,19 +152,25 @@ struct custom_setting {
- a NVRAM setting is removed
*/
#define F_TEMPVAR 0x0400 /* used if the setting should be set using a temp var */
-#define F_PADTITLE 0x800 /* pad the title with spaces to force it to scroll */
+#define F_PADTITLE 0x0800 /* pad the title with spaces to force it to scroll */
#define F_NO_WRAP 0x1000 /* used if the list should not wrap */
-#define F_BANFROMQS 0x80000000 /* ban the setting from the quickscreen items */
+#define F_CB_ON_SELECT_ONLY 0x10000000 /* option_callback only called if selected */
+#define F_CB_ONLY_IF_CHANGED 0x20000000 /* option_callback only called if setting changed */
+
#define F_DEPRECATED 0x40000000 /* DEPRECATED setting, don't write to .cfg */
+#define F_BANFROMQS 0x80000000 /* ban the setting from the quickscreen items */
+
+
struct settings_list {
uint32_t flags; /* BD__ _SER TFFF NNN_ _ATW PTVC IFRB STTT */
void *setting;
int lang_id; /* -1 for none */
union storage_type default_val;
const char *cfg_name; /* this settings name in the cfg file */
- const char *cfg_vals; /*comma seperated legal values, or NULL */
- /* used with F_T_UCHARPTR this is the folder prefix */
+ const char *cfg_vals; /* comma seperated symbolic values for bool
+ * or choice/table settings -- the i'th value
+ * maps to the integer i */
union {
const void *RESERVED; /* to stop compile errors, will be removed */
const struct sound_setting *sound_setting; /* use F_T_SOUND for this */
diff --git a/apps/shortcuts.c b/apps/shortcuts.c
index 9955b7f19d..ad19ec14fc 100644
--- a/apps/shortcuts.c
+++ b/apps/shortcuts.c
@@ -47,9 +47,11 @@
#include "screens.h"
#include "talk.h"
#include "yesno.h"
+#ifdef HAVE_ALBUMART
+#include "playback.h"
+#endif
-
-#define MAX_SHORTCUT_NAME 32
+#define MAX_SHORTCUT_NAME 64
#define SHORTCUTS_FILENAME ROCKBOX_DIR "/shortcuts.txt"
static const char * const type_strings[SHORTCUT_TYPE_COUNT] = {
[SHORTCUT_SETTING] = "setting",
@@ -78,7 +80,7 @@ struct shortcut {
} timedata;
} u;
};
-#define SHORTCUTS_PER_HANDLE 32
+#define SHORTCUTS_PER_HANDLE 4
struct shortcut_handle {
struct shortcut shortcuts[SHORTCUTS_PER_HANDLE];
int next_handle;
@@ -126,8 +128,7 @@ static struct shortcut* get_shortcut(int index)
if (first_handle == 0)
{
- first_handle = core_alloc_ex("shortcuts_head",
- sizeof(struct shortcut_handle), &shortcut_ops);
+ first_handle = core_alloc_ex(sizeof(struct shortcut_handle), &shortcut_ops);
if (first_handle <= 0)
return NULL;
h = core_get_data(first_handle);
@@ -144,11 +145,9 @@ static struct shortcut* get_shortcut(int index)
} while (handle_count > 0 && current_handle > 0);
if (handle_count > 0 && handle_index == 0)
{
- char buf[32];
- snprintf(buf, sizeof buf, "shortcuts_%d", index/SHORTCUTS_PER_HANDLE);
/* prevent invalidation of 'h' during compaction */
++buflib_move_lock;
- h->next_handle = core_alloc_ex(buf, sizeof(struct shortcut_handle), &shortcut_ops);
+ h->next_handle = core_alloc_ex(sizeof(struct shortcut_handle), &shortcut_ops);
--buflib_move_lock;
if (h->next_handle <= 0)
return NULL;
@@ -236,6 +235,19 @@ static void shortcuts_ata_idle_callback(void)
write(fd, buf, len);
if (sc->type == SHORTCUT_SETTING)
write(fd, sc->u.setting->cfg_name, strlen(sc->u.setting->cfg_name));
+ else if (sc->type == SHORTCUT_TIME)
+ {
+#if CONFIG_RTC
+ if (sc->u.timedata.talktime)
+ write(fd, "talk", 4);
+ else
+#endif
+ {
+ write(fd, "sleep ", 6);
+ len = snprintf(buf, MAX_PATH, "%d", sc->u.timedata.sleep_timeout);
+ write(fd, buf, len);
+ }
+ }
else
write(fd, sc->u.path, strlen(sc->u.path));
@@ -269,7 +281,7 @@ void shortcuts_add(enum shortcut_type type, const char* value)
if (type == SHORTCUT_SETTING)
sc->u.setting = (void*)value;
else
- strlcpy(sc->u.path, value, MAX_PATH);
+ strmemccpy(sc->u.path, value, MAX_PATH);
if (first_idx_to_writeback < 0)
first_idx_to_writeback = shortcut_count - 1;
@@ -297,18 +309,22 @@ static int readline_cb(int n, char *buf, void *parameters)
}
else if (sc && settings_parseline(buf, &name, &value))
{
- if (!strcmp(name, "type"))
+ static const char * const nm_options[] = {"type", "name", "data",
+ "icon", "talkclip", NULL};
+ int nm_op = string_option(name, nm_options, false);
+
+ if (nm_op == 0) /*type*/
{
int t = 0;
for (t=0; t<SHORTCUT_TYPE_COUNT && sc->type == SHORTCUT_UNDEFINED; t++)
if (!strcmp(value, type_strings[t]))
sc->type = t;
}
- else if (!strcmp(name, "name"))
+ else if (nm_op == 1) /*name*/
{
- strlcpy(sc->name, value, MAX_SHORTCUT_NAME);
+ strmemccpy(sc->name, value, MAX_SHORTCUT_NAME);
}
- else if (!strcmp(name, "data"))
+ else if (nm_op == 2) /*data*/
{
switch (sc->type)
{
@@ -320,10 +336,10 @@ static int readline_cb(int n, char *buf, void *parameters)
case SHORTCUT_FILE:
case SHORTCUT_DEBUGITEM:
case SHORTCUT_PLAYLISTMENU:
- strlcpy(sc->u.path, value, MAX_PATH);
+ strmemccpy(sc->u.path, value, MAX_PATH);
break;
case SHORTCUT_SETTING:
- sc->u.setting = find_setting_by_cfgname(value, NULL);
+ sc->u.setting = find_setting_by_cfgname(value);
break;
case SHORTCUT_TIME:
#if CONFIG_RTC
@@ -342,7 +358,7 @@ static int readline_cb(int n, char *buf, void *parameters)
break;
}
}
- else if (!strcmp(name, "icon"))
+ else if (nm_op == 3) /*icon*/
{
if (!strcmp(value, "filetype") && sc->type != SHORTCUT_SETTING && sc->u.path[0])
{
@@ -353,9 +369,9 @@ static int readline_cb(int n, char *buf, void *parameters)
sc->icon = atoi(value);
}
}
- else if (!strcmp(name, "talkclip"))
+ else if (nm_op == 4) /*talkclip*/
{
- strlcpy(sc->talk_clip, value, MAX_PATH);
+ strmemccpy(sc->talk_clip, value, MAX_PATH);
}
}
return 0;
@@ -371,9 +387,12 @@ void shortcuts_init(void)
fd = open_utf8(SHORTCUTS_FILENAME, O_RDONLY);
if (fd < 0)
return;
- first_handle = core_alloc_ex("shortcuts_head", sizeof(struct shortcut_handle), &shortcut_ops);
- if (first_handle <= 0)
+ first_handle = core_alloc_ex(sizeof(struct shortcut_handle), &shortcut_ops);
+ if (first_handle <= 0) {
+ close(fd);
return;
+ }
+
h = core_get_data(first_handle);
h->next_handle = 0;
@@ -424,7 +443,7 @@ static const char * shortcut_menu_get_name(int selected_item, void * data,
static int shortcut_menu_get_action(int action, struct gui_synclist *lists)
{
(void)lists;
- if (action == ACTION_STD_OK)
+ if (action == ACTION_STD_OK || action == ACTION_STD_MENU)
return ACTION_STD_CANCEL;
else if (action == ACTION_STD_QUICKSCREEN && action != ACTION_STD_CONTEXT)
return ACTION_STD_CANCEL;
@@ -432,11 +451,15 @@ static int shortcut_menu_get_action(int action, struct gui_synclist *lists)
{
int selection = gui_synclist_get_sel_pos(lists);
- if (!yesno_pop(ID2P(LANG_REALLY_DELETE)))
+ if (confirm_delete_yesno("") != YESNO_YES)
+ {
+ gui_synclist_set_title(lists, lists->title, lists->title_icon);
return ACTION_REDRAW;
+ }
remove_shortcut(selection);
gui_synclist_set_nb_items(lists, shortcut_count);
+ gui_synclist_set_title(lists, lists->title, lists->title_icon);
if (selection >= shortcut_count)
gui_synclist_select_item(lists, shortcut_count - 1);
first_idx_to_writeback = 0;
@@ -508,7 +531,7 @@ static int shortcut_menu_speak_item(int selected_item, void * data)
if (*filename != '\0')
{
int dirlen = (filename - sc->u.path);
- strlcpy(path, sc->u.path, dirlen + 1);
+ strmemccpy(path, sc->u.path, dirlen + 1);
dir = opendir(path);
if (dir)
{
@@ -596,13 +619,17 @@ int do_shortcut_menu(void *ignored)
while (done == GO_TO_PREVIOUS)
{
+ list.count = shortcut_count;
+
if (simplelist_show_list(&list))
break; /* some error happened?! */
+
if (list.selection == -1)
break;
else
{
sc = get_shortcut(list.selection);
+
if (!sc)
continue;
@@ -616,7 +643,10 @@ int do_shortcut_menu(void *ignored)
}
else
{
- onplay_show_playlist_menu(sc->u.path);
+ onplay_show_playlist_menu(sc->u.path,
+ dir_exists(sc->u.path) ? ATTR_DIRECTORY :
+ filetype_get_attr(sc->u.path),
+ NULL);
}
break;
case SHORTCUT_FILE:
@@ -633,18 +663,35 @@ int do_shortcut_menu(void *ignored)
done = GO_TO_PLUGIN;
break;
}
- struct browse_context browse;
- browse_context_init(&browse, global_settings.dirfilter, 0,
- NULL, NOICON, sc->u.path, NULL);
+ struct browse_context browse = {
+ .dirfilter = global_settings.dirfilter,
+ .icon = Icon_NOICON,
+ .root = sc->u.path,
+ };
if (sc->type == SHORTCUT_FILE)
browse.flags |= BROWSE_RUNFILE;
done = rockbox_browse(&browse);
+
}
break;
case SHORTCUT_SETTING:
+ {
+ int old_sleeptimer_duration = global_settings.sleeptimer_duration;
+#ifdef HAVE_ALBUMART
+ int old_album_art = global_settings.album_art;
+#endif
do_setting_screen(sc->u.setting,
sc->name[0] ? sc->name : P2STR(ID2P(sc->u.setting->lang_id)),NULL);
+
+#ifdef HAVE_ALBUMART
+ if (old_album_art != global_settings.album_art)
+ set_albumart_mode(global_settings.album_art);
+#endif
+ if (old_sleeptimer_duration != global_settings.sleeptimer_duration &&
+ get_sleep_timer())
+ set_sleeptimer_duration(global_settings.sleeptimer_duration);
break;
+ }
case SHORTCUT_DEBUGITEM:
run_debug_screen(sc->u.path);
break;
@@ -666,7 +713,7 @@ int do_shortcut_menu(void *ignored)
{
char timer_buf[10];
set_sleeptimer_duration(sc->u.timedata.sleep_timeout);
- splashf(HZ, "%s (%s)", str(LANG_SLEEP_TIMER),
+ splashf(HZ, "%s (%s)", str(LANG_SLEEP_TIMER),
sleep_timer_formatter(timer_buf, sizeof(timer_buf),
sc->u.timedata.sleep_timeout, NULL));
}
@@ -677,7 +724,10 @@ int do_shortcut_menu(void *ignored)
}
}
}
- pop_current_activity();
+ if (GO_TO_PLUGIN == done)
+ pop_current_activity_without_refresh();
+ else
+ pop_current_activity();
--buflib_move_lock;
return done;
diff --git a/apps/status.h b/apps/status.h
index 1add203a0c..c038b793af 100644
--- a/apps/status.h
+++ b/apps/status.h
@@ -21,6 +21,8 @@
#ifndef _STATUS_H
#define _STATUS_H
+/* Do not reorder these, inbuilt statusbar icons and the
+ * skin engine %mp tag depend on this ordering. */
enum playmode
{
STATUS_PLAY,
diff --git a/apps/tagcache.c b/apps/tagcache.c
index e6a4e8e3c4..90f7e01a2f 100644
--- a/apps/tagcache.c
+++ b/apps/tagcache.c
@@ -21,12 +21,12 @@
/*
* TagCache API
- *
+ *
* ----------x---------x------------------x-----
* | | | External
* +---------------x-------+ | TagCache | Libraries
- * | Modification routines | | Core |
- * +-x---------x-----------+ | |
+ * | Modification routines | | Core |
+ * +-x---------x-----------+ | |
* | (R/W) | | | |
* | +------x-------------x-+ +-------------x-----+ |
* | | x==x Filters & clauses | |
@@ -50,12 +50,15 @@
* +-----------------+ \===x | |
* | | | | (R) | Ram DB Loader x============x DirCache
* +-x----x---x---x---+ /==x | | (optional)
- * | Tagcache Disk DB x==/ +-----------------+ |
+ * | Tagcache Disk DB x==/ +-----------------+ |
* +------------------+ |
- *
+ *
*/
+#if !defined(PLUGIN)
+
/*#define LOGF_ENABLE*/
+/*#define LOGF_CLAUSES define to enable logf clause matching (LOGF_ENABLE req'd) */
#include <stdio.h>
#include <stdlib.h>
@@ -74,20 +77,89 @@
#include "usb.h"
#include "metadata.h"
#include "tagcache.h"
+#include "yesno.h"
#include "core_alloc.h"
#include "crc32.h"
#include "misc.h"
#include "settings.h"
#include "dir.h"
#include "pathfuncs.h"
-#include "structec.h"
#include "debug.h"
#include "dircache.h"
+#include "errno.h"
#ifndef __PCTOOL__
#include "lang.h"
#include "eeprom_settings.h"
#endif
+#define USR_CANCEL false
+#else/*!defined(PLUGIN)*/
+#define USR_CANCEL (tc_stat.commit_delayed == true)
+#endif /*!defined(PLUGIN)*/
+/*
+ * Define this to support non-native endian tagcache files.
+ * Databases are always written in native endian so this is
+ * basically only necessary to support databases generated
+ * by the PC database tool.
+ *
+ * Adds around 0.5-1.0k of code.
+ */
+#define TAGCACHE_SUPPORT_FOREIGN_ENDIAN
+
+/* Allow a little drift to the filename ordering (should not be too high/low). */
+#define POS_HISTORY_COUNT 4
+
+/* How much to pre-load entries while committing to prevent seeking. */
+#define IDX_BUF_DEPTH 64
+
+/* Tag Cache Header version 'TCHxx'. Increment when changing internal structures. */
+#define TAGCACHE_MAGIC 0x54434810
+
+/* Dump store/restore header version 'TCSxx'. */
+#define TAGCACHE_STATEFILE_MAGIC 0x54435301
+
+/* How much to allocate extra space for ramcache. */
+#define TAGCACHE_RESERVE 32768
+
+/*
+ * Define how long one entry must be at least (longer -> less memory at commit).
+ * Must be at least 4 bytes in length for correct alignment.
+ */
+#define TAGFILE_ENTRY_CHUNK_LENGTH 8
+
+/* Used to guess the necessary buffer size at commit. */
+#define TAGFILE_ENTRY_AVG_LENGTH 16
+
+/* Max events in the internal tagcache command queue. */
+#define TAGCACHE_COMMAND_QUEUE_LENGTH 32
+
+/* Idle time before committing events in the command queue. */
+#define TAGCACHE_COMMAND_QUEUE_COMMIT_DELAY HZ*2
+
+/* Dont commit database_tmp data. */
+#define TAGCACHE_FILE_NOCOMMIT "database_commit.ignore"
+
+/* Temporary database containing new tags to be committed to the main db. */
+#define TAGCACHE_FILE_TEMP "database_tmp.tcd"
+
+/* The main database master index and numeric data. */
+#define TAGCACHE_FILE_MASTER "database_idx.tcd"
+
+/* The main database string data. */
+#define TAGCACHE_FILE_INDEX "database_%d.tcd"
+
+/* ASCII dumpfile of the DB contents. */
+#define TAGCACHE_FILE_CHANGELOG "database_changelog.txt"
+
+/* Serialized DB. */
+#define TAGCACHE_STATEFILE "database_state.tcd"
+
+/* Flags */
+#define FLAG_DELETED 0x0001 /* Entry has been removed from db */
+#define FLAG_DIRCACHE 0x0002 /* Filename is a dircache pointer */
+#define FLAG_DIRTYNUM 0x0004 /* Numeric data has been modified */
+#define FLAG_TRKNUMGEN 0x0008 /* Track number has been generated */
+#define FLAG_RESURRECTED 0x0010 /* Statistics data has been resurrected */
#ifdef __PCTOOL__
#define yield() do { } while(0)
@@ -103,8 +175,10 @@ static const char tagcache_thread_name[] = "tagcache";
#endif
/* Previous path when scanning directory tree recursively. */
-static char curpath[TAG_MAXLEN+32];
-
+static char curpath[TAGCACHE_BUFSZ];
+/* Shared buffer for several build_index fns to reduce stack usage */
+static char build_idx_buf[TAGCACHE_BUFSZ];
+static const long build_idx_bufsz = sizeof(build_idx_buf);
/* Used when removing duplicates. */
static char *tempbuf; /* Allocated when needed. */
static long tempbufidx; /* Current location in buffer. */
@@ -115,7 +189,7 @@ static long tempbuf_pos;
static int tempbuf_handle;
#endif
-#define SORTED_TAGS_COUNT 8
+#define SORTED_TAGS_COUNT 9
#define TAGCACHE_IS_UNIQUE(tag) (BIT_N(tag) & TAGCACHE_UNIQUE_TAGS)
#define TAGCACHE_IS_SORTED(tag) (BIT_N(tag) & TAGCACHE_SORTED_TAGS)
#define TAGCACHE_IS_NUMERIC_OR_NONUNIQUE(tag) \
@@ -123,18 +197,47 @@ static int tempbuf_handle;
/* Tags we want to get sorted (loaded to the tempbuf). */
#define TAGCACHE_SORTED_TAGS ((1LU << tag_artist) | (1LU << tag_album) | \
(1LU << tag_genre) | (1LU << tag_composer) | (1LU << tag_comment) | \
- (1LU << tag_albumartist) | (1LU << tag_grouping) | (1LU << tag_title))
+ (1LU << tag_albumartist) | (1LU << tag_grouping) | (1LU << tag_title) | \
+ (1LU << tag_virt_canonicalartist))
/* Uniqued tags (we can use these tags with filters and conditional clauses). */
#define TAGCACHE_UNIQUE_TAGS ((1LU << tag_artist) | (1LU << tag_album) | \
(1LU << tag_genre) | (1LU << tag_composer) | (1LU << tag_comment) | \
- (1LU << tag_albumartist) | (1LU << tag_grouping))
+ (1LU << tag_albumartist) | (1LU << tag_grouping) | \
+ (1LU << tag_virt_canonicalartist))
/* String presentation of the tags defined in tagcache.h. Must be in correct order! */
-static const char *tags_str[] = { "artist", "album", "genre", "title",
- "filename", "composer", "comment", "albumartist", "grouping", "year",
- "discnumber", "tracknumber", "bitrate", "length", "playcount", "rating",
- "playtime", "lastplayed", "commitid", "mtime", "lastelapsed", "lastoffset" };
+static const char * const tags_str[] = { "artist", "album", "genre", "title",
+ "filename", "composer", "comment", "albumartist", "grouping", "year",
+ "discnumber", "tracknumber", "canonicalartist", "bitrate", "length",
+ "playcount", "rating", "playtime", "lastplayed", "commitid", "mtime",
+ "lastelapsed", "lastoffset"
+#if !defined(LOGF_ENABLE)
+};
+#define logf_clauses(...) do { } while(0)
+#elif defined(LOGF_CLAUSES) /* strings for logf debugging */
+ "tag_virt_basename", "tag_virt_length_min", "tag_virt_length_sec",
+ "tag_virt_playtime_min", "tag_virt_playtime_sec",
+ "tag_virt_entryage", "tag_virt_autoscore"
+};
+/* more debug strings */
+static const char * const tag_type_str[] = {
+ [clause_none] = "clause_none", [clause_is] = "clause_is",
+ [clause_is_not] = "clause_is_not", [clause_gt] = "clause_gt",
+ [clause_gteq] = "clause_gteq", [clause_lt] = "clause_lt",
+ [clause_lteq] = "clause_lteq", [clause_contains] = "clause_contains",
+ [clause_not_contains] = "clause_not_contains",
+ [clause_begins_with] = "clause_begins_with",
+ [clause_not_begins_with] = "clause_not_begins_with",
+ [clause_ends_with] = "clause_ends_with",
+ [clause_not_ends_with] = "clause_not_ends_with",
+ [clause_oneof] = "clause_oneof",
+ [clause_begins_oneof] = "clause_begins_oneof",
+ [clause_ends_oneof] = "clause_ends_oneof",
+ [clause_logical_or] = "clause_logical_or"
+ };
+#define logf_clauses logf
+#endif /* ndef LOGF_ENABLE */
/* Status information of the tagcache. */
static struct tagcache_stat tc_stat;
@@ -146,7 +249,7 @@ enum tagcache_queue {
Q_IMPORT_CHANGELOG,
Q_UPDATE,
Q_REBUILD,
-
+
/* Internal tagcache command queue. */
CMD_UPDATE_MASTER_HEADER,
CMD_UPDATE_NUMERIC,
@@ -195,16 +298,6 @@ struct master_header {
int32_t dirty;
};
-/* For the endianess correction */
-static const char * const tagfile_entry_ec = "ll";
-/**
- Note: This should be (1 + TAG_COUNT) amount of l's.
- */
-static const char * const index_entry_ec = "lllllllllllllllllllllll";
-
-static const char * const tagcache_header_ec = "lll";
-static const char * const master_header_ec = "llllll";
-
static struct master_header current_tcmh;
#ifdef HAVE_TC_RAMCACHE
@@ -244,17 +337,16 @@ static struct tcramcache
{
struct ramcache_header *hdr; /* allocated ramcache_header */
int handle; /* buffer handle */
- int move_lock;
} tcramcache;
static inline void tcrc_buffer_lock(void)
{
- tcramcache.move_lock++;
+ core_pin(tcramcache.handle);
}
static inline void tcrc_buffer_unlock(void)
{
- tcramcache.move_lock--;
+ core_unpin(tcramcache.handle);
}
#else /* ndef HAVE_TC_RAMCACHE */
@@ -263,15 +355,14 @@ static inline void tcrc_buffer_unlock(void)
#endif /* HAVE_TC_RAMCACHE */
-/**
- * Full tag entries stored in a temporary file waiting
+/**
+ * Full tag entries stored in a temporary file waiting
* for commit to the cache. */
struct temp_file_entry {
- long tag_offset[TAG_COUNT];
- short tag_length[TAG_COUNT];
- long flag;
-
- long data_length;
+ int32_t tag_offset[TAG_COUNT];
+ int16_t tag_length[TAG_COUNT];
+ int32_t flag;
+ int32_t data_length;
};
struct tempbuf_id_list {
@@ -303,84 +394,218 @@ static volatile int read_lock;
static bool delete_entry(long idx_id);
-static void allocate_tempbuf(void)
+static inline void str_setlen(char *buf, size_t len)
{
- /* Yeah, malloc would be really nice now :) */
- size_t size;
- tempbuf_size = 0;
+ buf[len] = '\0';
+}
-#ifdef __PCTOOL__
- size = 32*1024*1024;
- tempbuf = malloc(size);
- if (tempbuf)
- tempbuf_size = size;
-#else /* !__PCTOOL__ */
- tempbuf_handle = core_alloc_maximum("tc tempbuf", &size, NULL);
- if (tempbuf_handle > 0)
+const char* tagcache_tag_to_str(int tag)
+{
+ return tags_str[tag];
+}
+
+#ifdef TAGCACHE_SUPPORT_FOREIGN_ENDIAN
+static void swap_tagfile_entry(struct tagfile_entry *buf)
+{
+ if (tc_stat.econ)
{
- tempbuf = core_get_data(tempbuf_handle);
- tempbuf_size = size;
+ buf->tag_length = swap32(buf->tag_length);
+ buf->idx_id = swap32(buf->idx_id);
}
-#endif /* __PCTOOL__ */
}
-static void free_tempbuf(void)
+static void swap_index_entry(struct index_entry *buf)
{
- if (tempbuf_size == 0)
- return ;
+ if (tc_stat.econ)
+ {
+ for (int i = 0; i < TAG_COUNT; ++i)
+ buf->tag_seek[i] = swap32(buf->tag_seek[i]);
+ buf->flag = swap32(buf->flag);
+ }
+}
-#ifdef __PCTOOL__
- free(tempbuf);
+static void swap_tagcache_header(struct tagcache_header *buf)
+{
+ if (tc_stat.econ)
+ {
+ buf->magic = swap32(buf->magic);
+ buf->datasize = swap32(buf->datasize);
+ buf->entry_count = swap32(buf->entry_count);
+ }
+}
+
+static void swap_master_header(struct master_header *buf)
+{
+ if (tc_stat.econ)
+ {
+ swap_tagcache_header(&buf->tch);
+ buf->serial = swap32(buf->serial);
+ buf->commitid = swap32(buf->commitid);
+ buf->dirty = swap32(buf->dirty);
+ }
+}
#else
- tempbuf_handle = core_free(tempbuf_handle);
+static void swap_tagfile_entry(struct tagfile_entry *buf) { (void)buf; }
+static void swap_index_entry(struct index_entry *buf) { (void)buf; }
+static void swap_tagcache_header(struct tagcache_header *buf) { (void)buf; }
+static void swap_master_header(struct master_header *buf) { (void)buf; }
#endif
- tempbuf = NULL;
- tempbuf_size = 0;
+
+static ssize_t read_tagfile_entry(int fd, struct tagfile_entry *buf)
+{
+ ssize_t ret = read(fd, buf, sizeof(*buf));
+ if (ret == sizeof(*buf) && tc_stat.econ)
+ swap_tagfile_entry(buf);
+
+ return ret;
}
-const char* tagcache_tag_to_str(int tag)
+static ssize_t write_tagfile_entry(int fd, struct tagfile_entry *buf)
{
- return tags_str[tag];
+ struct tagfile_entry e = *buf;
+
+ swap_tagfile_entry(&e);
+
+ return write(fd, &e, sizeof(e));
}
-/* Helper functions for the two most read/write data structure: tagfile_entry and index_entry */
-static ssize_t ecread_tagfile_entry(int fd, struct tagfile_entry *buf)
+enum e_read_errors {
+ e_SUCCESS = 0,
+ e_SUCCESS_LEN_ZERO = 1,
+ e_ENTRY_SIZEMISMATCH,
+ e_TAG_TOOLONG,
+ e_TAG_SIZEMISMATCH
+};
+
+static enum e_read_errors
+read_tagfile_entry_and_tag(int fd, struct tagfile_entry *tfe,
+ char* buf, int bufsz)
+{
+ if (read_tagfile_entry(fd, tfe) != sizeof(struct tagfile_entry))
+ return e_ENTRY_SIZEMISMATCH;
+
+ long tag_length = tfe->tag_length;
+ if (tag_length >= bufsz)
+ return e_TAG_TOOLONG;
+
+ if (tag_length > 0 && read(fd, buf, tag_length) != tag_length)
+ return e_TAG_SIZEMISMATCH;
+
+ str_setlen(buf, tag_length);
+ return tag_length > 0 ? e_SUCCESS : e_SUCCESS_LEN_ZERO;
+}
+
+static ssize_t read_index_entries(int fd, struct index_entry *buf, size_t count)
+{
+ ssize_t ret = read(fd, buf, sizeof(*buf) * count);
+ for (ssize_t i = 0; i < ret; i += sizeof(*buf))
+ swap_index_entry(buf++);
+
+ return ret;
+}
+
+static ssize_t write_index_entries(int fd, struct index_entry *buf, size_t count)
+{
+#ifdef TAGCACHE_SUPPORT_FOREIGN_ENDIAN
+ ssize_t ret = 0;
+ for (; count > 0; count--)
+ {
+ struct index_entry e = *buf++;
+ swap_index_entry(&e);
+
+ ssize_t rc = write(fd, &e, sizeof(e));
+ if (rc < 0)
+ return rc;
+ ret += rc;
+ }
+
+ return ret;
+#else
+ return write(fd, buf, sizeof(*buf) * count);
+#endif
+}
+
+static ssize_t read_tagcache_header(int fd, struct tagcache_header *buf)
{
- return ecread(fd, buf, 1, tagfile_entry_ec, tc_stat.econ);
+ ssize_t ret = read(fd, buf, sizeof(*buf));
+ if (ret == sizeof(*buf))
+ swap_tagcache_header(buf);
+
+ return ret;
}
-static ssize_t ecread_index_entry(int fd, struct index_entry *buf)
+static ssize_t write_tagcache_header(int fd, struct tagcache_header *buf)
{
- return ecread(fd, buf, 1, index_entry_ec, tc_stat.econ);
+ struct tagcache_header e = *buf;
+ swap_tagcache_header(&e);
+ return write(fd, &e, sizeof(e));
+}
+
+static ssize_t read_master_header(int fd, struct master_header *buf)
+{
+ ssize_t ret = read(fd, buf, sizeof(*buf));
+ if (ret == sizeof(*buf))
+ swap_master_header(buf);
+
+ return ret;
+}
+
+static ssize_t write_master_header(int fd, struct master_header *buf)
+{
+ struct master_header e = *buf;
+ swap_master_header(&e);
+ return write(fd, &e, sizeof(e));
+}
+
+/*
+ * open_db_fd and remove_db_file are noinline to minimize stack usage
+ */
+static int NO_INLINE open_db_fd(const char* filename, int mode)
+{
+ char buf[MAX_PATH];
+
+ if(mode & O_CREAT)
+ {
+ if (mkdir(tc_stat.db_path) < 0 && errno != EEXIST)
+ return -1;
+ }
+
+ return open_pathfmt(buf, sizeof(buf), mode, "%s/%s",
+ tc_stat.db_path, filename);
}
-static ssize_t ecwrite_index_entry(int fd, struct index_entry *buf)
+static int NO_INLINE remove_db_file(const char* filename)
{
- return ecwrite(fd, buf, 1, index_entry_ec, tc_stat.econ);
+ char buf[MAX_PATH];
+
+ snprintf(buf, sizeof(buf), "%s/%s",
+ tc_stat.db_path, filename);
+
+ return remove(buf);
}
static int open_tag_fd(struct tagcache_header *hdr, int tag, bool write)
{
int fd;
- char buf[MAX_PATH];
- int rc;
-
+ char fname[MAX_PATH];
+
if (TAGCACHE_IS_NUMERIC(tag) || tag < 0 || tag >= TAG_COUNT)
return -1;
-
- snprintf(buf, sizeof buf, TAGCACHE_FILE_INDEX, tag);
-
- fd = open(buf, write ? O_RDWR : O_RDONLY);
+
+ fd = open_pathfmt(fname, sizeof(fname),
+ write ? O_RDWR : O_RDONLY, "%s/" TAGCACHE_FILE_INDEX,
+ tc_stat.db_path, tag);
if (fd < 0)
{
- logf("tag file open failed: tag=%d write=%d file=%s", tag, write, buf);
+ logf("%s failed: tag=%d write=%d file= " TAGCACHE_FILE_INDEX,
+ __func__, tag, write, tag);
tc_stat.ready = false;
return fd;
}
-
+
/* Check the header. */
- rc = ecread(fd, hdr, 1, tagcache_header_ec, tc_stat.econ);
- if (hdr->magic != TAGCACHE_MAGIC || rc != sizeof(struct tagcache_header))
+ if (read_tagcache_header(fd, hdr) != sizeof(struct tagcache_header) ||
+ hdr->magic != TAGCACHE_MAGIC)
{
logf("header error");
tc_stat.ready = false;
@@ -395,42 +620,124 @@ static int open_master_fd(struct master_header *hdr, bool write)
{
int fd;
int rc;
-
- fd = open(TAGCACHE_FILE_MASTER, write ? O_RDWR : O_RDONLY);
+
+ fd = open_db_fd(TAGCACHE_FILE_MASTER, write ? O_RDWR : O_RDONLY);
if (fd < 0)
{
logf("master file open failed for R/W");
tc_stat.ready = false;
return fd;
}
-
- tc_stat.econ = false;
-
- /* Check the header. */
+
rc = read(fd, hdr, sizeof(struct master_header));
- if (hdr->tch.magic == TAGCACHE_MAGIC && rc == sizeof(struct master_header))
+ if (rc != sizeof(struct master_header))
{
- /* Success. */
- return fd;
+ logf("master file read failed");
+ close(fd);
+ return -1;
}
-
- /* Trying to read again, this time with endianess correction enabled. */
- lseek(fd, 0, SEEK_SET);
-
- rc = ecread(fd, hdr, 1, master_header_ec, true);
- if (hdr->tch.magic != TAGCACHE_MAGIC || rc != sizeof(struct master_header))
+
+ /* Tagcache files can have either endianness. A device will always
+ * create files in its native endianness, but we accept non-native
+ * endian files for compatibility reasons. */
+ if (hdr->tch.magic == TAGCACHE_MAGIC)
+ tc_stat.econ = false;
+#ifdef TAGCACHE_SUPPORT_FOREIGN_ENDIAN
+ else if (hdr->tch.magic == swap32(TAGCACHE_MAGIC))
{
- logf("header error");
- tc_stat.ready = false;
+ tc_stat.econ = true;
+ swap_master_header(hdr);
+ }
+#endif
+ else
+ {
+ logf("master file bad magic: %08lx\n", (unsigned long)hdr->tch.magic);
close(fd);
return -2;
}
- tc_stat.econ = true;
-
return fd;
}
+static void remove_files(void)
+{
+ int i;
+ char buf[MAX_PATH];
+ const int bufsz = sizeof(buf);
+ logf("%s", __func__);
+
+ tc_stat.ready = false;
+ tc_stat.ramcache = false;
+ tc_stat.econ = false;
+ remove_db_file(TAGCACHE_FILE_MASTER);
+ for (i = 0; i < TAG_COUNT; i++)
+ {
+ if (TAGCACHE_IS_NUMERIC(i))
+ continue;
+
+ snprintf(buf, bufsz, "%s/" TAGCACHE_FILE_INDEX,
+ tc_stat.db_path, i);
+ remove(buf);
+ }
+}
+
+static bool check_all_headers(void)
+{
+ struct master_header myhdr;
+ struct tagcache_header tch;
+ int tag;
+ int fd;
+
+ if ( (fd = open_master_fd(&myhdr, false)) < 0)
+ return false;
+
+ close(fd);
+ if (myhdr.dirty)
+ {
+ logf("tagcache is dirty!");
+ return false;
+ }
+
+ memcpy(&current_tcmh, &myhdr, sizeof(struct master_header));
+
+ for (tag = 0; tag < TAG_COUNT; tag++)
+ {
+ if (TAGCACHE_IS_NUMERIC(tag))
+ continue;
+
+ if ( (fd = open_tag_fd(&tch, tag, false)) < 0)
+ return false;
+
+ close(fd);
+ }
+
+ return true;
+}
+
+static bool update_master_header(void)
+{
+ struct master_header myhdr;
+ int fd;
+
+ if (!tc_stat.ready)
+ return false;
+
+ if ( (fd = open_master_fd(&myhdr, true)) < 0)
+ return false;
+
+ myhdr.serial = current_tcmh.serial;
+ myhdr.commitid = current_tcmh.commitid;
+ myhdr.dirty = current_tcmh.dirty;
+
+ /* Write it back */
+ lseek(fd, 0, SEEK_SET);
+ write_master_header(fd, &myhdr);
+ close(fd);
+
+ return true;
+}
+
+#if !defined(PLUGIN)
#ifndef __PCTOOL__
static bool do_timed_yield(void)
{
@@ -439,13 +746,51 @@ static bool do_timed_yield(void)
if (TIME_AFTER(current_tick, wakeup_tick))
{
yield();
- wakeup_tick = current_tick + (HZ/50);
+ wakeup_tick = current_tick + (HZ/25);
return true;
}
return false;
}
#endif /* __PCTOOL__ */
+static void allocate_tempbuf(void)
+{
+ /* Yeah, malloc would be really nice now :) */
+ size_t size;
+ tempbuf_size = 0;
+
+#ifdef __PCTOOL__
+ size = 32*1024*1024;
+ tempbuf = malloc(size);
+ if (tempbuf)
+ tempbuf_size = size;
+#else /* !__PCTOOL__ */
+ /* Need to pass dummy ops to prevent the buffer being moved
+ * out from under us, since we yield during the tagcache commit. */
+ tempbuf_handle = core_alloc_maximum(&size, &buflib_ops_locked);
+ if (tempbuf_handle > 0)
+ {
+ tempbuf = core_get_data(tempbuf_handle);
+ tempbuf_size = size;
+ }
+#endif /* __PCTOOL__ */
+
+}
+
+static void free_tempbuf(void)
+{
+ if (tempbuf_size == 0)
+ return ;
+
+#ifdef __PCTOOL__
+ free(tempbuf);
+#else
+ tempbuf_handle = core_free(tempbuf_handle);
+#endif
+ tempbuf = NULL;
+ tempbuf_size = 0;
+}
+
#if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE)
/* find the ramcache entry corresponding to the file indicated by
* filename and dc (it's corresponding dircache id). */
@@ -453,7 +798,7 @@ static long find_entry_ram(const char *filename)
{
static long last_pos = 0;
struct dircache_fileref dcfref;
-
+
/* Check if tagcache is loaded into ram. */
if (!tc_stat.ramcache)
return -1;
@@ -500,18 +845,24 @@ static long find_entry_ram(const char *filename)
static long find_entry_disk(const char *filename_raw, bool localfd)
{
+ struct tagfile_entry tfe;
struct tagcache_header tch;
static long last_pos = -1;
long pos_history[POS_HISTORY_COUNT];
- long pos_history_idx = 0;
- bool found = false;
- struct tagfile_entry tfe;
+ unsigned int pos_history_idx = 0;
+ unsigned int i;
+
+ char buf[TAGCACHE_BUFSZ];
+ const long bufsz = sizeof(buf);
+
int fd;
- char buf[TAG_MAXLEN+32];
- int i;
int pos = -1;
+ long idx = -1;
+
+ bool found = false;
const char *filename = filename_raw;
+
#ifdef APPLICATION
char pathbuf[PATH_MAX]; /* Note: Don't use MAX_PATH here, it's too small */
if (realpath(filename, pathbuf) == pathbuf)
@@ -520,7 +871,7 @@ static long find_entry_disk(const char *filename_raw, bool localfd)
if (!tc_stat.ready)
return -2;
-
+
fd = filenametag_fd;
if (fd < 0 || localfd)
{
@@ -528,124 +879,125 @@ static long find_entry_disk(const char *filename_raw, bool localfd)
if ( (fd = open_tag_fd(&tch, tag_filename, false)) < 0)
return -1;
}
-
+
check_again:
-
- if (last_pos > 0)
- lseek(fd, last_pos, SEEK_SET);
- else
- lseek(fd, sizeof(struct tagcache_header), SEEK_SET);
- while (true)
- {
- pos = lseek(fd, 0, SEEK_CUR);
- for (i = pos_history_idx-1; i >= 0; i--)
- pos_history[i+1] = pos_history[i];
- pos_history[0] = pos;
+ if (last_pos > 0) /* pos gets cached to prevent reading from beginning */
+ pos = lseek(fd, last_pos, SEEK_SET);
+ else /* start back at beginning */
+ pos = lseek(fd, sizeof(struct tagcache_header), SEEK_SET);
- if (ecread_tagfile_entry(fd, &tfe)
- != sizeof(struct tagfile_entry))
- {
- break ;
- }
-
- if (tfe.tag_length >= (long)sizeof(buf))
- {
- logf("too long tag #1");
- close(fd);
- if (!localfd)
- filenametag_fd = -1;
- last_pos = -1;
- return -2;
- }
-
- if (read(fd, buf, tfe.tag_length) != tfe.tag_length)
- {
- logf("read error #2");
- close(fd);
- if (!localfd)
- filenametag_fd = -1;
- last_pos = -1;
- return -3;
- }
-
- if (!strcmp(filename, buf))
+ long tag_length = strlen(filename) + 1; /* include NULL */
+
+ if (tag_length < bufsz)
+ {
+ while (true)
{
- last_pos = pos_history[pos_history_idx];
- found = true;
- break ;
+ for (i = pos_history_idx-1; i < pos_history_idx; i--)
+ pos_history[i+1] = pos_history[i];
+ pos_history[0] = pos;
+
+ if (read_tagfile_entry(fd, &tfe) != sizeof(struct tagfile_entry))
+ {
+ logf("size mismatch find entry");
+ break;
+ }
+ else
+ {
+ pos += sizeof(struct tagfile_entry) + tfe.tag_length;
+ /* don't read the entry unless the length matches */
+ if (tfe.tag_length == tag_length)
+ {
+ if(read(fd, buf, tfe.tag_length) != tag_length)
+ {
+ logf("read error #2");
+ close(fd);
+ if (!localfd)
+ filenametag_fd = -1;
+ last_pos = -1;
+ return -3;
+ }
+ if (!strncmp(filename, buf, tag_length))
+ {
+ last_pos = pos_history[pos_history_idx];
+ found = true;
+ idx = tfe.idx_id;
+ break ;
+ }
+ }
+ else
+ lseek(fd, pos, SEEK_SET);
+
+ }
}
-
if (pos_history_idx < POS_HISTORY_COUNT - 1)
pos_history_idx++;
}
-
+
/* Not found? */
if (!found)
{
- if (last_pos > 0)
+ if (last_pos > 0) /* start back at the beginning */
{
last_pos = -1;
logf("seek again");
goto check_again;
}
-
- if (fd != filenametag_fd || localfd)
- close(fd);
- return -4;
- }
-
+
+ idx = -4;
+ }
+
if (fd != filenametag_fd || localfd)
close(fd);
-
- return tfe.idx_id;
+
+ return idx;
}
static int find_index(const char *filename)
{
long idx_id = -1;
-
+
#if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE)
idx_id = find_entry_ram(filename);
#endif
-
+
if (idx_id < 0)
idx_id = find_entry_disk(filename, true);
-
+
return idx_id;
}
bool tagcache_find_index(struct tagcache_search *tcs, const char *filename)
{
int idx_id;
-
+
if (!tc_stat.ready)
return false;
-
+
idx_id = find_index(filename);
if (idx_id < 0)
return false;
-
+
if (!tagcache_search(tcs, tag_filename))
return false;
-
+
tcs->entry_count = 0;
tcs->idx_id = idx_id;
-
+
return true;
}
-static bool get_index(int masterfd, int idxid,
+static bool get_index(int masterfd, int idxid,
struct index_entry *idx, bool use_ram)
{
bool localfd = false;
-
+
if (idxid < 0)
{
logf("Incorrect idxid: %d", idxid);
return false;
}
-
+
#ifdef HAVE_TC_RAMCACHE
if (tc_stat.ramcache && use_ram)
{
@@ -656,35 +1008,34 @@ static bool get_index(int masterfd, int idxid,
return true;
}
#endif /* HAVE_TC_RAMCACHE */
-
+
if (masterfd < 0)
{
struct master_header tcmh;
-
+
localfd = true;
masterfd = open_master_fd(&tcmh, false);
if (masterfd < 0)
return false;
}
-
- lseek(masterfd, idxid * sizeof(struct index_entry)
+
+ lseek(masterfd, idxid * sizeof(struct index_entry)
+ sizeof(struct master_header), SEEK_SET);
- if (ecread_index_entry(masterfd, idx)
- != sizeof(struct index_entry))
+ if (read_index_entries(masterfd, idx, 1) != sizeof(struct index_entry))
{
logf("read error #3");
if (localfd)
close(masterfd);
-
+
return false;
}
-
+
if (localfd)
close(masterfd);
-
+
if (idx->flag & FLAG_DELETED)
return false;
-
+
return true;
(void)use_ram;
@@ -700,7 +1051,7 @@ static bool write_index(int masterfd, int idxid, struct index_entry *idx)
logf("memory only flags!");
return false;
}
-
+
#ifdef HAVE_TC_RAMCACHE
/* Only update numeric data. Writing the whole index to RAM by memcpy
* destroys dircache pointers!
@@ -708,7 +1059,7 @@ static bool write_index(int masterfd, int idxid, struct index_entry *idx)
if (tc_stat.ramcache)
{
struct index_entry *idx_ram = &tcramcache.hdr->indices[idxid];
-
+
for (int tag = 0; tag < TAG_COUNT; tag++)
{
if (TAGCACHE_IS_NUMERIC(tag))
@@ -716,22 +1067,22 @@ static bool write_index(int masterfd, int idxid, struct index_entry *idx)
idx_ram->tag_seek[tag] = idx->tag_seek[tag];
}
}
-
+
/* Don't touch the dircache flag or attributes. */
- idx_ram->flag = (idx->flag & 0x0000ffff)
+ idx_ram->flag = (idx->flag & 0x0000ffff)
| (idx_ram->flag & (0xffff0000 | FLAG_DIRCACHE));
}
#endif /* HAVE_TC_RAMCACHE */
-
- lseek(masterfd, idxid * sizeof(struct index_entry)
+
+ lseek(masterfd, idxid * sizeof(struct index_entry)
+ sizeof(struct master_header), SEEK_SET);
- if (ecwrite_index_entry(masterfd, idx) != sizeof(struct index_entry))
+ if (write_index_entries(masterfd, idx, 1) != sizeof(struct index_entry))
{
logf("write error #3");
logf("idxid: %d", idxid);
return false;
}
-
+
return true;
}
@@ -741,47 +1092,52 @@ static bool open_files(struct tagcache_search *tcs, int tag)
{
if (tcs->idxfd[tag] < 0)
{
- char fn[MAX_PATH];
-
- snprintf(fn, sizeof fn, TAGCACHE_FILE_INDEX, tag);
- tcs->idxfd[tag] = open(fn, O_RDONLY);
- }
-
- if (tcs->idxfd[tag] < 0)
- {
- logf("File not open!");
- return false;
+ char fname[MAX_PATH];
+ tcs->idxfd[tag] = open_pathfmt(fname, sizeof(fname),
+ O_RDONLY, "%s/" TAGCACHE_FILE_INDEX,
+ tc_stat.db_path, tag);
+ if (tcs->idxfd[tag] < 0)
+ {
+ logf("File not open!");
+ return false;
+ }
}
-
+
return true;
}
static bool retrieve(struct tagcache_search *tcs, IF_DIRCACHE(int idx_id,)
- struct index_entry *idx, int tag, char *buf, long size)
+ struct index_entry *idx, int tag, char *buf, long bufsz)
{
+ bool success = false;
+ bool is_basename = false;
struct tagfile_entry tfe;
long seek;
-
- *buf = '\0';
-
+
+ if (tag == tag_virt_basename)
+ {
+ tag = tag_filename;
+ is_basename = true;
+ }
+
if (TAGCACHE_IS_NUMERIC(tag))
- return false;
-
+ goto failure;
+
seek = idx->tag_seek[tag];
if (seek < 0)
{
logf("Retrieve failed");
- return false;
+ goto failure;
}
-
+
#ifdef HAVE_TC_RAMCACHE
if (tcs->ramsearch)
{
#ifdef HAVE_DIRCACHE
if (tag == tag_filename && (idx->flag & FLAG_DIRCACHE))
{
- if (dircache_get_fileref_path(&tcrc_dcfrefs[idx_id], buf, size) >= 0)
- return true;
+ if (dircache_get_fileref_path(&tcrc_dcfrefs[idx_id], buf, bufsz) >= 0)
+ success = true;
}
else
#endif /* HAVE_DIRCACHE */
@@ -789,45 +1145,55 @@ static bool retrieve(struct tagcache_search *tcs, IF_DIRCACHE(int idx_id,)
{
struct tagfile_entry *ep =
(struct tagfile_entry *)&tcramcache.hdr->tags[tag][seek];
- strlcpy(buf, ep->tag_data, size);
-
- return true;
+ strmemccpy(buf, ep->tag_data, bufsz);
+ success = true;
}
}
#endif /* HAVE_TC_RAMCACHE */
-
- if (!open_files(tcs, tag))
- return false;
-
- lseek(tcs->idxfd[tag], seek, SEEK_SET);
- if (ecread_tagfile_entry(tcs->idxfd[tag], &tfe)
- != sizeof(struct tagfile_entry))
- {
- logf("read error #5");
- return false;
- }
-
- if (tfe.tag_length >= size)
+
+ if (!success && open_files(tcs, tag))
{
- logf("too small buffer");
- return false;
+ lseek(tcs->idxfd[tag], seek, SEEK_SET);
+ switch (read_tagfile_entry_and_tag(tcs->idxfd[tag], &tfe, buf, bufsz))
+ {
+ case e_ENTRY_SIZEMISMATCH:
+ logf("read error #5");
+ break;
+ case e_TAG_TOOLONG:
+ logf("too long tag #5");
+ break;
+ case e_TAG_SIZEMISMATCH:
+ logf("read error #6");
+ break;
+ default:
+ logf("unknown_error");
+ break;
+ case e_SUCCESS_LEN_ZERO:
+ case e_SUCCESS:
+ success = true;
+ break;
+ }
}
-
- if (read(tcs->idxfd[tag], buf, tfe.tag_length) !=
- tfe.tag_length)
+
+ if (success)
{
- logf("read error #6");
- return false;
+ if (is_basename)
+ {
+ char* basename = strrchr(buf, '/');
+ if (basename != NULL)
+ memmove(buf, basename + 1, strlen(basename)); /* includes NULL */
+ }
+ return true;
}
-
- buf[tfe.tag_length] = '\0';
- return true;
+failure:
+ str_setlen(buf, 0);
+ return false;
}
#define COMMAND_QUEUE_IS_EMPTY (command_queue_ridx == command_queue_widx)
-static long find_tag(int tag, int idx_id, const struct index_entry *idx)
+static long tc_find_tag(int tag, int idx_id, const struct index_entry *idx)
{
#ifndef __PCTOOL__
if (! COMMAND_QUEUE_IS_EMPTY && TAGCACHE_IS_NUMERIC(tag))
@@ -844,7 +1210,7 @@ static long find_tag(int tag, int idx_id, const struct index_entry *idx)
{
if (--ridx < 0)
ridx = TAGCACHE_COMMAND_QUEUE_LENGTH - 1;
-
+
if (command_queue[ridx].command == CMD_UPDATE_NUMERIC
&& command_queue[ridx].idx_id == idx_id
&& command_queue[ridx].tag == tag)
@@ -858,8 +1224,7 @@ static long find_tag(int tag, int idx_id, const struct index_entry *idx)
if (result >= 0)
{
- logf("find_tag: "
- "Recovered tag %d value %lX from write queue",
+ logf("tc_find_tag: Recovered tag %d value %lX from write queue",
tag, result);
return result;
}
@@ -871,33 +1236,42 @@ static long find_tag(int tag, int idx_id, const struct index_entry *idx)
return idx->tag_seek[tag];
}
+static inline long sec_in_ms(long ms)
+{
+ return (ms/1000) % 60;
+}
-static long check_virtual_tags(int tag, int idx_id,
+static inline long min_in_ms(long ms)
+{
+ return (ms/1000) / 60;
+}
+
+static long check_virtual_tags(int tag, int idx_id,
const struct index_entry *idx)
{
long data = 0;
-
- switch (tag)
+
+ switch (tag)
{
case tag_virt_length_sec:
- data = (find_tag(tag_length, idx_id, idx)/1000) % 60;
+ data = sec_in_ms(tc_find_tag(tag_length, idx_id, idx));
break;
-
+
case tag_virt_length_min:
- data = (find_tag(tag_length, idx_id, idx)/1000) / 60;
+ data = min_in_ms(tc_find_tag(tag_length, idx_id, idx));
break;
-
+
case tag_virt_playtime_sec:
- data = (find_tag(tag_playtime, idx_id, idx)/1000) % 60;
+ data = sec_in_ms(tc_find_tag(tag_playtime, idx_id, idx));
break;
-
+
case tag_virt_playtime_min:
- data = (find_tag(tag_playtime, idx_id, idx)/1000) / 60;
+ data = min_in_ms(tc_find_tag(tag_playtime, idx_id, idx));
break;
-
+
case tag_virt_autoscore:
- if (find_tag(tag_length, idx_id, idx) == 0
- || find_tag(tag_playcount, idx_id, idx) == 0)
+ if (tc_find_tag(tag_length, idx_id, idx) == 0
+ || tc_find_tag(tag_playcount, idx_id, idx) == 0)
{
data = 0;
}
@@ -913,64 +1287,65 @@ static long check_virtual_tags(int tag, int idx_id,
autoscore = 100 * (alpha / playcout + beta / length / playcount)
Both terms should be small enough to avoid any overflow
*/
- data = 100 * (find_tag(tag_playtime, idx_id, idx)
- / find_tag(tag_length, idx_id, idx))
- + (100 * (find_tag(tag_playtime, idx_id, idx)
- % find_tag(tag_length, idx_id, idx)))
- / find_tag(tag_length, idx_id, idx);
- data /= find_tag(tag_playcount, idx_id, idx);
+ long playtime = tc_find_tag(tag_playtime, idx_id, idx);
+ long length = tc_find_tag(tag_length, idx_id, idx);
+ long playcount = tc_find_tag(tag_playcount, idx_id, idx);
+ data = 100 * (playtime / length) + (100 * (playtime % length)) / length;
+ data /= playcount;
}
break;
-
+
/* How many commits before the file has been added to the DB. */
case tag_virt_entryage:
- data = current_tcmh.commitid
- - find_tag(tag_commitid, idx_id, idx) - 1;
+ data = current_tcmh.commitid
+ - tc_find_tag(tag_commitid, idx_id, idx) - 1;
break;
-
+
case tag_virt_basename:
tag = tag_filename; /* return filename; caller handles basename */
/* FALLTHRU */
default:
- data = find_tag(tag, idx_id, idx);
+ data = tc_find_tag(tag, idx_id, idx);
}
-
+
return data;
}
long tagcache_get_numeric(const struct tagcache_search *tcs, int tag)
{
struct index_entry idx;
-
+
if (!tc_stat.ready)
return false;
-
+
if (!TAGCACHE_IS_NUMERIC(tag))
return -1;
-
+
if (!get_index(tcs->masterfd, tcs->idx_id, &idx, true))
return -2;
-
+
return check_virtual_tags(tag, tcs->idx_id, &idx);
}
inline static bool str_ends_with(const char *str1, const char *str2)
{
+ logf_clauses("%s %s %s", str1, __func__, str2);
int str_len = strlen(str1);
int clause_len = strlen(str2);
-
+
if (clause_len > str_len)
return false;
-
+
return !strcasecmp(&str1[str_len - clause_len], str2);
}
inline static bool str_oneof(const char *str, const char *list)
{
+ logf_clauses("%s %s %s", str, __func__, list);
const char *sep;
int l, len = strlen(str);
-
+
while (*list)
{
sep = strchr(list, '|');
@@ -983,6 +1358,25 @@ inline static bool str_oneof(const char *str, const char *list)
return false;
}
+inline static bool str_begins_ends_oneof(const char *str, const char *list, bool begins)
+{
+ logf_clauses("%s %s (%s) %s", str, __func__, begins ? "begins" : "ends", list);
+ const char *sep;
+ int l, p, len = strlen(str);
+
+ while (*list)
+ {
+ sep = strchr(list, '|');
+ l = sep ? (intptr_t)sep - (intptr_t)list : (int)strlen(list);
+ p = begins ? 0 : len - l;
+ if (l <= len && !strncasecmp(&str[p], list, l))
+ return true;
+ list += sep ? l + 1 : l;
+ }
+
+ return false;
+}
+
static bool check_against_clause(long numeric, const char *str,
const struct tagcache_search_clause *clause)
{
@@ -1001,7 +1395,7 @@ static bool check_against_clause(long numeric, const char *str,
case clause_lt:
return numeric < clause->numeric_data;
case clause_lteq:
- return numeric <= clause->numeric_data;
+ return numeric <= clause->numeric_data;
default:
logf("Incorrect numeric tag: %d", clause->type);
}
@@ -1036,7 +1430,11 @@ static bool check_against_clause(long numeric, const char *str,
return !str_ends_with(str, clause->str);
case clause_oneof:
return str_oneof(str, clause->str);
-
+ case clause_ends_oneof:
+ /* Fall-Through */
+ case clause_begins_oneof:
+ return str_begins_ends_oneof(str, clause->str,
+ clause->type == clause_begins_oneof);
default:
logf("Incorrect tag: %d", clause->type);
}
@@ -1050,33 +1448,41 @@ static bool check_clauses(struct tagcache_search *tcs,
struct tagcache_search_clause **clauses, int count)
{
int i;
-
+
/* Go through all conditional clauses. */
for (i = 0; i < count; i++)
{
int seek;
char buf[256];
+ const int bufsz = sizeof(buf);
char *str = buf;
struct tagcache_search_clause *clause = clauses[i];
-
+
+ logf_clauses("%s clause %d %s %s [%ld] %s",
+ "Checking", i, tag_type_str[clause->type],
+ tags_str[clause->tag], clause->numeric_data,
+ (clause->numeric || clause->str == NULL) ? "[NUMERIC?]" : clause->str);
+
if (clause->type == clause_logical_or)
+ {
+ logf_clauses("Bailing");
break; /* all conditions before logical-or satisfied --
stop processing clauses */
-
+ }
seek = check_virtual_tags(clause->tag, tcs->idx_id, idx);
#ifdef HAVE_TC_RAMCACHE
if (tcs->ramsearch)
{
struct tagfile_entry *tfe;
-
+
if (!TAGCACHE_IS_NUMERIC(clause->tag))
{
if (clause->tag == tag_filename
|| clause->tag == tag_virt_basename)
{
- retrieve(tcs, IF_DIRCACHE(tcs->idx_id,) idx, tag_filename,
- buf, sizeof buf);
+ retrieve(tcs, IF_DIRCACHE(tcs->idx_id,) idx, clause->tag,
+ buf, bufsz);
}
else
{
@@ -1092,7 +1498,7 @@ static bool check_clauses(struct tagcache_search *tcs,
#endif /* HAVE_TC_RAMCACHE */
{
struct tagfile_entry tfe;
-
+
if (!TAGCACHE_IS_NUMERIC(clause->tag))
{
int tag = clause->tag;
@@ -1101,29 +1507,35 @@ static bool check_clauses(struct tagcache_search *tcs,
int fd = tcs->idxfd[tag];
lseek(fd, seek, SEEK_SET);
- ecread_tagfile_entry(fd, &tfe);
- if (tfe.tag_length >= (int)sizeof(buf))
+
+ switch (read_tagfile_entry_and_tag(fd, &tfe, str, bufsz))
{
- logf("Too long tag read!");
- return false;
+ case e_SUCCESS_LEN_ZERO: /* Check if entry has been deleted. */
+ return false;
+ case e_SUCCESS:
+ if (clause->tag == tag_virt_basename)
+ {
+ char *basename = strrchr(str, '/');
+ if (basename)
+ str = basename + 1;
+ }
+ break;
+ case e_ENTRY_SIZEMISMATCH:
+ logf("read error #15");
+ return false;
+ case e_TAG_TOOLONG:
+ logf("too long tag #6");
+ return false;
+ case e_TAG_SIZEMISMATCH:
+ logf("read error #16");
+ return false;
+ default:
+ logf("unknown_error");
+ break;;
}
-
- read(fd, str, tfe.tag_length);
- str[tfe.tag_length] = '\0';
-
- /* Check if entry has been deleted. */
- if (str[0] == '\0')
- return false;
}
}
- if (clause->tag == tag_virt_basename)
- {
- char *basename = strrchr(str, '/');
- if (basename)
- str = basename + 1;
- }
-
if (!check_against_clause(seek, str, clause))
{
/* Clause failed -- try finding a logical-or clause */
@@ -1132,14 +1544,19 @@ static bool check_clauses(struct tagcache_search *tcs,
if (clauses[i]->type == clause_logical_or)
break;
}
-
+
if (i < count) /* Found logical-or? */
continue; /* Check clauses after logical-or */
return false;
}
+
+ logf_clauses("%s clause %d %s %s [%ld] %s",
+ "Found", i, tag_type_str[clause->type],
+ tags_str[clause->tag], clause->numeric_data,
+ (clause->numeric || clause->str == NULL) ? "[NUMERIC?]" : clause->str);
}
-
+
return true;
}
@@ -1147,40 +1564,43 @@ bool tagcache_check_clauses(struct tagcache_search *tcs,
struct tagcache_search_clause **clause, int count)
{
struct index_entry idx;
-
+
if (count == 0)
return true;
-
+
if (!get_index(tcs->masterfd, tcs->idx_id, &idx, true))
return false;
-
+
return check_clauses(tcs, &idx, clause, count);
}
-static bool add_uniqbuf(struct tagcache_search *tcs, unsigned long id)
+static bool add_uniqbuf(struct tagcache_search *tcs, uint32_t id)
{
int i;
-
+
/* If uniq buffer is not defined we must return true for search to work. */
if (tcs->unique_list == NULL || (!TAGCACHE_IS_UNIQUE(tcs->type)
&& !TAGCACHE_IS_NUMERIC(tcs->type)))
{
return true;
}
-
+
for (i = 0; i < tcs->unique_list_count; i++)
{
/* Return false if entry is found. */
if (tcs->unique_list[i] == id)
+ {
+ //logf("%d Exists @ %d", id, i);
return false;
+ }
}
-
+
if (tcs->unique_list_count < tcs->unique_list_capacity)
{
tcs->unique_list[i] = id;
tcs->unique_list_count++;
}
-
+
return true;
}
@@ -1188,9 +1608,9 @@ static bool build_lookup_list(struct tagcache_search *tcs)
{
struct index_entry entry;
int i, j;
-
+
tcs->seek_list_count = 0;
-
+
#ifdef HAVE_TC_RAMCACHE
if (tcs->ramsearch)
{
@@ -1203,11 +1623,11 @@ static bool build_lookup_list(struct tagcache_search *tcs)
struct index_entry *idx = &tcramcache.hdr->indices[i];
if (tcs->seek_list_count == SEEK_LIST_SIZE)
break ;
-
+
/* Skip deleted files. */
if (idx->flag & FLAG_DELETED)
continue;
-
+
/* Go through all filters.. */
for (j = 0; j < tcs->filter_count; j++)
{
@@ -1216,7 +1636,7 @@ static bool build_lookup_list(struct tagcache_search *tcs)
break ;
}
}
-
+
if (j < tcs->filter_count)
continue ;
@@ -1226,7 +1646,7 @@ static bool build_lookup_list(struct tagcache_search *tcs)
/* Add to the seek list if not already in uniq buffer (doesn't yield)*/
if (!add_uniqbuf(tcs, idx->tag_seek[tcs->type]))
continue;
-
+
/* Lets add it. */
seeklist = &tcs->seeklist[tcs->seek_list_count];
seeklist->seek = idx->tag_seek[tcs->type];
@@ -1238,119 +1658,65 @@ static bool build_lookup_list(struct tagcache_search *tcs)
tcrc_buffer_unlock();
tcs->seek_pos = i;
-
+
return tcs->seek_list_count > 0;
}
#endif /* HAVE_TC_RAMCACHE */
-
+
if (tcs->masterfd < 0)
{
struct master_header tcmh;
tcs->masterfd = open_master_fd(&tcmh, false);
}
-
+
lseek(tcs->masterfd, tcs->seek_pos * sizeof(struct index_entry) +
sizeof(struct master_header), SEEK_SET);
-
- while (ecread_index_entry(tcs->masterfd, &entry)
- == sizeof(struct index_entry))
+
+ while (read_index_entries(tcs->masterfd, &entry, 1) == sizeof(struct index_entry))
{
struct tagcache_seeklist_entry *seeklist;
-
+
if (tcs->seek_list_count == SEEK_LIST_SIZE)
break ;
-
+
i = tcs->seek_pos;
tcs->seek_pos++;
-
+
/* Check if entry has been deleted. */
if (entry.flag & FLAG_DELETED)
continue;
-
+
/* Go through all filters.. */
for (j = 0; j < tcs->filter_count; j++)
{
if (entry.tag_seek[tcs->filter_tag[j]] != tcs->filter_seek[j])
break ;
}
-
+
if (j < tcs->filter_count)
continue ;
-
+
/* Check for conditions. */
if (!check_clauses(tcs, &entry, tcs->clause, tcs->clause_count))
continue;
-
+
/* Add to the seek list if not already in uniq buffer. */
if (!add_uniqbuf(tcs, entry.tag_seek[tcs->type]))
continue;
-
+
/* Lets add it. */
seeklist = &tcs->seeklist[tcs->seek_list_count];
seeklist->seek = entry.tag_seek[tcs->type];
seeklist->flag = entry.flag;
seeklist->idx_id = i;
tcs->seek_list_count++;
-
+
yield();
}
return tcs->seek_list_count > 0;
}
-
-static void remove_files(void)
-{
- int i;
- char buf[MAX_PATH];
-
- tc_stat.ready = false;
- tc_stat.ramcache = false;
- tc_stat.econ = false;
- remove(TAGCACHE_FILE_MASTER);
- for (i = 0; i < TAG_COUNT; i++)
- {
- if (TAGCACHE_IS_NUMERIC(i))
- continue;
-
- snprintf(buf, sizeof buf, TAGCACHE_FILE_INDEX, i);
- remove(buf);
- }
-}
-
-
-static bool check_all_headers(void)
-{
- struct master_header myhdr;
- struct tagcache_header tch;
- int tag;
- int fd;
-
- if ( (fd = open_master_fd(&myhdr, false)) < 0)
- return false;
-
- close(fd);
- if (myhdr.dirty)
- {
- logf("tagcache is dirty!");
- return false;
- }
-
- memcpy(&current_tcmh, &myhdr, sizeof(struct master_header));
-
- for (tag = 0; tag < TAG_COUNT; tag++)
- {
- if (TAGCACHE_IS_NUMERIC(tag))
- continue;
-
- if ( (fd = open_tag_fd(&tch, tag, false)) < 0)
- return false;
-
- close(fd);
- }
-
- return true;
-}
bool tagcache_search(struct tagcache_search *tcs, int tag)
{
@@ -1360,11 +1726,11 @@ bool tagcache_search(struct tagcache_search *tcs, int tag)
while (read_lock)
sleep(1);
-
+
memset(tcs, 0, sizeof(struct tagcache_search));
if (tc_stat.commit_step > 0 || !tc_stat.ready)
return false;
-
+
tcs->position = sizeof(struct tagcache_header);
tcs->type = tag;
tcs->seek_pos = 0;
@@ -1392,13 +1758,13 @@ bool tagcache_search(struct tagcache_search *tcs, int tag)
tcs->masterfd = open_master_fd(&master_hdr, true);
if (tcs->masterfd < 0)
return false;
-
+
if (!TAGCACHE_IS_NUMERIC(tcs->type))
{
tcs->idxfd[tcs->type] = open_tag_fd(&tag_hdr, tcs->type, false);
if (tcs->idxfd[tcs->type] < 0)
return false;
-
+
tcs->entry_count = tag_hdr.entry_count;
}
else
@@ -1410,16 +1776,17 @@ bool tagcache_search(struct tagcache_search *tcs, int tag)
tcs->valid = true;
tcs->initialized = true;
write_lock++;
-
+
return true;
}
void tagcache_search_set_uniqbuf(struct tagcache_search *tcs,
void *buffer, long length)
{
- tcs->unique_list = (unsigned long *)buffer;
+ tcs->unique_list = (uint32_t *)buffer;
tcs->unique_list_capacity = length / sizeof(*tcs->unique_list);
tcs->unique_list_count = 0;
+ memset(tcs->unique_list, 0, tcs->unique_list_capacity);
}
bool tagcache_search_add_filter(struct tagcache_search *tcs,
@@ -1430,7 +1797,7 @@ bool tagcache_search_add_filter(struct tagcache_search *tcs,
if (TAGCACHE_IS_NUMERIC_OR_NONUNIQUE(tag))
return false;
-
+
tcs->filter_tag[tcs->filter_count] = tag;
tcs->filter_seek[tcs->filter_count] = seek;
tcs->filter_count++;
@@ -1442,8 +1809,9 @@ bool tagcache_search_add_clause(struct tagcache_search *tcs,
struct tagcache_search_clause *clause)
{
int i;
-
- if (tcs->clause_count >= TAGCACHE_MAX_CLAUSES)
+ int clause_count = tcs->clause_count;
+
+ if (clause_count >= TAGCACHE_MAX_CLAUSES)
{
logf("Too many clauses");
return false;
@@ -1451,51 +1819,52 @@ bool tagcache_search_add_clause(struct tagcache_search *tcs,
if (clause->type != clause_logical_or)
{
- /* Check if there is already a similar filter in present (filters are
- * much faster than clauses).
- */
- for (i = 0; i < tcs->filter_count; i++)
+ /* BUGFIX OR'd clauses seem to be mishandled once made into a filter */
+ if (clause_count <= 1 || tcs->clause[clause_count - 1]->type != clause_logical_or)
{
- if (tcs->filter_tag[i] == clause->tag)
- return true;
+ /* Check if there is already a similar filter in present (filters are
+ * much faster than clauses).
+ */
+ for (i = 0; i < tcs->filter_count; i++)
+ {
+ if (tcs->filter_tag[i] == clause->tag)
+ {
+ return true;
+ }
+ }
}
-
+
if (!TAGCACHE_IS_NUMERIC(clause->tag) && tcs->idxfd[clause->tag] < 0)
{
- char buf[MAX_PATH];
-
- snprintf(buf, sizeof buf, TAGCACHE_FILE_INDEX, clause->tag);
- tcs->idxfd[clause->tag] = open(buf, O_RDONLY);
+ char fname[MAX_PATH];
+ tcs->idxfd[clause->tag] = open_pathfmt(fname, sizeof(fname), O_RDONLY,
+ "%s/" TAGCACHE_FILE_INDEX,
+ tc_stat.db_path, clause->tag);
}
}
-
+
tcs->clause[tcs->clause_count] = clause;
tcs->clause_count++;
-
+
return true;
}
-static bool get_next(struct tagcache_search *tcs)
+static bool get_next(struct tagcache_search *tcs, bool is_numeric, char *buf, long bufsz)
{
- static char buf[TAG_MAXLEN+32];
struct tagfile_entry entry;
#if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE)
long flag = 0;
#endif
- if (!tcs->valid || !tc_stat.ready)
- return false;
-
- if (tcs->idxfd[tcs->type] < 0 && !TAGCACHE_IS_NUMERIC(tcs->type)
+ if (tcs->idxfd[tcs->type] < 0 && !is_numeric
#ifdef HAVE_TC_RAMCACHE
&& !tcs->ramsearch
#endif
)
return false;
-
+
/* Relative fetch. */
- if (tcs->filter_count > 0 || tcs->clause_count > 0
- || TAGCACHE_IS_NUMERIC(tcs->type)
+ if (tcs->filter_count > 0 || tcs->clause_count > 0 || is_numeric
#if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE)
/* We need to retrieve flag status for dircache. */
|| (tcs->ramsearch && tcs->type == tag_filename)
@@ -1503,12 +1872,12 @@ static bool get_next(struct tagcache_search *tcs)
)
{
struct tagcache_seeklist_entry *seeklist;
-
+
/* Check for end of list. */
if (tcs->list_position == tcs->seek_list_count)
{
tcs->list_position = 0;
-
+
/* Try to fetch more. */
if (!build_lookup_list(tcs))
{
@@ -1516,7 +1885,7 @@ static bool get_next(struct tagcache_search *tcs)
return false;
}
}
-
+
seeklist = &tcs->seeklist[tcs->list_position];
#if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE)
flag = seeklist->flag;
@@ -1532,20 +1901,20 @@ static bool get_next(struct tagcache_search *tcs)
tcs->valid = false;
return false;
}
-
+
tcs->entry_count--;
}
-
+
tcs->result_seek = tcs->position;
-
- if (TAGCACHE_IS_NUMERIC(tcs->type))
+
+ if (is_numeric)
{
- snprintf(buf, sizeof(buf), "%ld", tcs->position);
+ snprintf(buf, bufsz, "%ld", tcs->position);
tcs->result = buf;
tcs->result_len = strlen(buf) + 1;
return true;
}
-
+
/* Direct fetch. */
#ifdef HAVE_TC_RAMCACHE
if (tcs->ramsearch)
@@ -1554,7 +1923,7 @@ static bool get_next(struct tagcache_search *tcs)
if (tcs->type == tag_filename && (flag & FLAG_DIRCACHE))
{
ssize_t len = dircache_get_fileref_path(&tcrc_dcfrefs[tcs->idx_id],
- buf, sizeof (buf));
+ buf, bufsz);
if (len >= 0)
{
tcs->result_len = len + 1;
@@ -1569,119 +1938,104 @@ static bool get_next(struct tagcache_search *tcs)
if (tcs->type != tag_filename)
{
struct tagfile_entry *ep;
-
+
ep = (struct tagfile_entry *)&tcramcache.hdr->tags[tcs->type][tcs->position];
/* don't return ep->tag_data directly as it may move */
- tcs->result_len = strlcpy(buf, ep->tag_data, sizeof(buf)) + 1;
+ tcs->result_len = strlcpy(buf, ep->tag_data, bufsz) + 1;
tcs->result = buf;
tcs->idx_id = ep->idx_id;
tcs->ramresult = false; /* was true before we copied to buf too */
-
+
/* Increase position for the next run. This may get overwritten. */
tcs->position += sizeof(struct tagfile_entry) + ep->tag_length;
-
+
return true;
}
}
#endif /* HAVE_TC_RAMCACHE */
-
+
if (!open_files(tcs, tcs->type))
{
tcs->valid = false;
return false;
}
-
+
/* Seek stream to the correct position and continue to direct fetch. */
lseek(tcs->idxfd[tcs->type], tcs->position, SEEK_SET);
-
- if (ecread_tagfile_entry(tcs->idxfd[tcs->type], &entry) != sizeof(struct tagfile_entry))
- {
- logf("read error #5");
- tcs->valid = false;
- return false;
- }
-
- if (entry.tag_length > (long)sizeof(buf))
- {
- tcs->valid = false;
- logf("too long tag #2");
- logf("P:%lX/%lX", tcs->position, entry.tag_length);
- return false;
- }
-
- if (read(tcs->idxfd[tcs->type], buf, entry.tag_length) != entry.tag_length)
+
+ switch (read_tagfile_entry_and_tag(tcs->idxfd[tcs->type], &entry, buf, bufsz))
{
- tcs->valid = false;
- logf("read error #4");
- return false;
+ case e_SUCCESS_LEN_ZERO:
+ case e_SUCCESS:
+ break;
+ case e_ENTRY_SIZEMISMATCH:
+ logf("read error #5");
+ tcs->valid = false;
+ return false;
+ case e_TAG_TOOLONG:
+ tcs->valid = false;
+ logf("too long tag #2");
+ logf("P:%lX/%lX", tcs->position, entry.tag_length);
+ return false;
+ case e_TAG_SIZEMISMATCH:
+ tcs->valid = false;
+ logf("read error #4");
+ return false;
}
-
+
/**
Update the position for the next read (this may be overridden
if filters or clauses are being used).
*/
tcs->position += sizeof(struct tagfile_entry) + entry.tag_length;
+ str_setlen(buf, entry.tag_length);
+
tcs->result = buf;
- tcs->result_len = strlen(tcs->result) + 1;
+ tcs->result_len = entry.tag_length + 1;
tcs->idx_id = entry.idx_id;
tcs->ramresult = false;
return true;
}
-bool tagcache_get_next(struct tagcache_search *tcs)
+bool tagcache_get_next(struct tagcache_search *tcs, char *buf, long size)
{
- while (get_next(tcs))
+ if (tcs->valid && tagcache_is_usable())
{
- if (tcs->result_len > 1)
- return true;
+ bool is_numeric = TAGCACHE_IS_NUMERIC(tcs->type);
+ while (get_next(tcs, is_numeric, buf, size))
+ {
+ if (tcs->result_len > 1)
+ return true;
+ }
}
-
+#ifdef LOGF_ENABLE
+ if (tcs->unique_list_count > 0)
+ logf(" uniqbuf: %d used / %d avail", tcs->unique_list_count, tcs->unique_list_capacity);
+#endif
+
return false;
}
-bool tagcache_retrieve(struct tagcache_search *tcs, int idxid,
+bool tagcache_retrieve(struct tagcache_search *tcs, int idxid,
int tag, char *buf, long size)
{
struct index_entry idx;
-
+
*buf = '\0';
if (!get_index(tcs->masterfd, idxid, &idx, true))
return false;
-
- return retrieve(tcs, IF_DIRCACHE(idxid,) &idx, tag, buf, size);
-}
-static bool update_master_header(void)
-{
- struct master_header myhdr;
- int fd;
-
- if (!tc_stat.ready)
- return false;
-
- if ( (fd = open_master_fd(&myhdr, true)) < 0)
- return false;
-
- myhdr.serial = current_tcmh.serial;
- myhdr.commitid = current_tcmh.commitid;
- myhdr.dirty = current_tcmh.dirty;
-
- /* Write it back */
- lseek(fd, 0, SEEK_SET);
- ecwrite(fd, &myhdr, 1, master_header_ec, tc_stat.econ);
- close(fd);
-
- return true;
+ return retrieve(tcs, IF_DIRCACHE(idxid,) &idx, tag, buf, size);
}
void tagcache_search_finish(struct tagcache_search *tcs)
{
int i;
-
+
if (!tcs->initialized)
return;
-
+
if (tcs->masterfd >= 0)
{
close(tcs->masterfd);
@@ -1696,7 +2050,7 @@ void tagcache_search_finish(struct tagcache_search *tcs)
tcs->idxfd[i] = -1;
}
}
-
+
tcs->ramsearch = false;
tcs->valid = false;
tcs->initialized = 0;
@@ -1725,17 +2079,17 @@ bool tagcache_fill_tags(struct mp3entry *id3, const char *filename)
{
struct index_entry *entry;
int idx_id;
-
+
if (!tc_stat.ready || !tc_stat.ramcache)
return false;
-
+
/* Find the corresponding entry in tagcache. */
idx_id = find_entry_ram(filename);
if (idx_id < 0)
return false;
-
+
entry = &tcramcache.hdr->indices[idx_id];
-
+
memset(id3, 0, sizeof(struct mp3entry));
char* buf = id3->id3v2buf;
ssize_t remaining = sizeof(id3->id3v2buf);
@@ -1755,8 +2109,8 @@ bool tagcache_fill_tags(struct mp3entry *id3, const char *filename)
} \
} \
} while(0)
-
-
+
+
SET(id3->title, tag_title);
SET(id3->artist, tag_artist);
SET(id3->album, tag_album);
@@ -1786,7 +2140,7 @@ bool tagcache_fill_tags(struct mp3entry *id3, const char *filename)
id3->title, id3->elapsed);
id3->offset = get_tag_numeric(entry, tag_lastoffset, idx_id);
- logf("tagcache_fill_tags: Set offset for %s to %lX\n",
+ logf("tagcache_fill_tags: Set offset for %s to %lX\n",
id3->title, id3->offset);
}
@@ -1817,7 +2171,7 @@ static int check_if_empty(char **tag)
{
logf("over length tag: %s", *tag);
length = TAG_MAXLEN;
- (*tag)[length] = '\0';
+ str_setlen((*tag), length);
}
return length + 1;
@@ -1831,8 +2185,8 @@ static void NO_INLINE add_tagcache(char *path, unsigned long mtime)
{
#define ADD_TAG(entry, tag, data) \
/* Adding tag */ \
- entry.tag_offset[tag] = offset; \
entry.tag_length[tag] = check_if_empty(data); \
+ entry.tag_offset[tag] = offset; \
offset += entry.tag_length[tag]
struct mp3entry id3;
@@ -1843,7 +2197,7 @@ static void NO_INLINE add_tagcache(char *path, unsigned long mtime)
char tracknumfix[3];
int offset = 0;
int path_length = strlen(path);
- bool has_albumartist;
+ bool has_artist;
bool has_grouping;
#ifdef SIMULATOR
@@ -1868,11 +2222,11 @@ static void NO_INLINE add_tagcache(char *path, unsigned long mtime)
logf("Too long path: %s", path);
return ;
}
-
+
/* Check if the file is supported. */
if (probe_file_format(path) == AFMT_UNKNOWN)
return ;
-
+
/* Check if the file is already cached. */
#if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE)
idx_id = find_entry_ram(path);
@@ -1881,27 +2235,27 @@ static void NO_INLINE add_tagcache(char *path, unsigned long mtime)
/* Be sure the entry doesn't exist. */
if (filenametag_fd >= 0 && idx_id < 0)
idx_id = find_entry_disk(path, false);
-
+
/* Check if file has been modified. */
if (idx_id >= 0)
{
struct index_entry idx;
-
+
/* TODO: Mark that the index exists (for fast reverse scan) */
//found_idx[idx_id/8] |= idx_id%8;
-
+
if (!get_index(-1, idx_id, &idx, true))
{
logf("failed to retrieve index entry");
return ;
}
-
+
if ((unsigned long)idx.tag_seek[tag_mtime] == mtime)
{
/* No changes to file. */
return ;
}
-
+
/* Metadata might have been changed. Delete the entry. */
logf("Re-adding: %s", path);
if (!delete_entry(idx_id))
@@ -1910,7 +2264,7 @@ static void NO_INLINE add_tagcache(char *path, unsigned long mtime)
return ;
}
}
-
+
fd = open(path, O_RDONLY);
if (fd < 0)
{
@@ -1928,12 +2282,12 @@ static void NO_INLINE add_tagcache(char *path, unsigned long mtime)
return ;
logf("-> %s", path);
-
+
if (id3.tracknum <= 0) /* Track number missing? */
{
id3.tracknum = -1;
}
-
+
/* Numeric tags */
entry.tag_offset[tag_year] = id3.year;
entry.tag_offset[tag_discnumber] = id3.discnum;
@@ -1941,10 +2295,10 @@ static void NO_INLINE add_tagcache(char *path, unsigned long mtime)
entry.tag_offset[tag_length] = id3.length;
entry.tag_offset[tag_bitrate] = id3.bitrate;
entry.tag_offset[tag_mtime] = mtime;
-
+
/* String tags. */
- has_albumartist = id3.albumartist != NULL
- && strlen(id3.albumartist) > 0;
+ has_artist = id3.artist != NULL
+ && strlen(id3.artist) > 0;
has_grouping = id3.grouping != NULL
&& strlen(id3.grouping) > 0;
@@ -1955,13 +2309,14 @@ static void NO_INLINE add_tagcache(char *path, unsigned long mtime)
ADD_TAG(entry, tag_genre, &id3.genre_string);
ADD_TAG(entry, tag_composer, &id3.composer);
ADD_TAG(entry, tag_comment, &id3.comment);
- if (has_albumartist)
+ ADD_TAG(entry, tag_albumartist, &id3.albumartist);
+ if (has_artist)
{
- ADD_TAG(entry, tag_albumartist, &id3.albumartist);
+ ADD_TAG(entry, tag_virt_canonicalartist, &id3.artist);
}
else
{
- ADD_TAG(entry, tag_albumartist, &id3.artist);
+ ADD_TAG(entry, tag_virt_canonicalartist, &id3.albumartist);
}
if (has_grouping)
{
@@ -1972,10 +2327,10 @@ static void NO_INLINE add_tagcache(char *path, unsigned long mtime)
ADD_TAG(entry, tag_grouping, &id3.title);
}
entry.data_length = offset;
-
+
/* Write the header */
write(cachefd, &entry, sizeof(struct temp_file_entry));
-
+
/* And tags also... Correct order is critical */
write_item(path);
write_item(id3.title);
@@ -1984,13 +2339,14 @@ static void NO_INLINE add_tagcache(char *path, unsigned long mtime)
write_item(id3.genre_string);
write_item(id3.composer);
write_item(id3.comment);
- if (has_albumartist)
+ write_item(id3.albumartist);
+ if (has_artist)
{
- write_item(id3.albumartist);
+ write_item(id3.artist);
}
else
{
- write_item(id3.artist);
+ write_item(id3.albumartist);
}
if (has_grouping)
{
@@ -2005,22 +2361,23 @@ static void NO_INLINE add_tagcache(char *path, unsigned long mtime)
#undef ADD_TAG
}
+#endif /*!defined(PLUGIN)*/
+
static bool tempbuf_insert(char *str, int id, int idx_id, bool unique)
{
struct tempbuf_searchidx *index = (struct tempbuf_searchidx *)tempbuf;
int len = strlen(str)+1;
int i;
- unsigned crc32;
unsigned *crcbuf = (unsigned *)&tempbuf[tempbuf_size-4];
- char buf[TAG_MAXLEN+32];
-
- for (i = 0; str[i] != '\0' && i < (int)sizeof(buf)-1; i++)
- buf[i] = tolower(str[i]);
- buf[i] = '\0';
-
- crc32 = crc_32(buf, i, 0xffffffff);
-
+ unsigned crc32 = 0xffffffff;
+ char chr_lower;
+ for (i = 0; str[i] != '\0' && i < len -1; i++)
+ {
+ chr_lower = tolower(str[i]);
+ crc32 = crc_32(&chr_lower, 1, crc32);
+ }
+
if (unique)
{
/* Check if the crc does not exist -> entry does not exist for sure. */
@@ -2028,7 +2385,7 @@ static bool tempbuf_insert(char *str, int id, int idx_id, bool unique)
{
if (crcbuf[-i] != crc32)
continue;
-
+
if (!strcasecmp(str, index[i].str))
{
if (id < 0 || id >= lookup_buffer_depth)
@@ -2036,22 +2393,25 @@ static bool tempbuf_insert(char *str, int id, int idx_id, bool unique)
logf("lookup buf overf.: %d", id);
return false;
}
-
+
lookup[id] = &index[i];
return true;
}
}
}
-
+
/* Insert to CRC buffer. */
crcbuf[-tempbufidx] = crc32;
tempbuf_left -= 4;
-
+
/* Insert it to the buffer. */
tempbuf_left -= len;
- if (tempbuf_left - 4 < 0 || tempbufidx >= commit_entry_count-1)
+ if (tempbuf_left - 4 < 0 || tempbufidx >= commit_entry_count)
+ {
+ logf("temp buf error rem: %ld idx: %d / %d",
+ tempbuf_left, tempbufidx, commit_entry_count-1);
return false;
-
+ }
if (id >= lookup_buffer_depth)
{
logf("lookup buf overf. #2: %d", id);
@@ -2065,7 +2425,7 @@ static bool tempbuf_insert(char *str, int id, int idx_id, bool unique)
}
else
index[tempbufidx].idlist.id = -1;
-
+
index[tempbufidx].idlist.next = NULL;
index[tempbufidx].idx_id = idx_id;
index[tempbufidx].seek = -1;
@@ -2073,7 +2433,7 @@ static bool tempbuf_insert(char *str, int id, int idx_id, bool unique)
memcpy(index[tempbufidx].str, str, len);
tempbuf_pos += len;
tempbufidx++;
-
+
return true;
}
@@ -2083,7 +2443,7 @@ static int compare(const void *p1, const void *p2)
struct tempbuf_searchidx *e1 = (struct tempbuf_searchidx *)p1;
struct tempbuf_searchidx *e2 = (struct tempbuf_searchidx *)p2;
-
+
if (strcmp(e1->str, UNTAGGED) == 0)
{
if (strcmp(e2->str, UNTAGGED) == 0)
@@ -2092,7 +2452,7 @@ static int compare(const void *p1, const void *p2)
}
else if (strcmp(e2->str, UNTAGGED) == 0)
return 1;
-
+
return strncasecmp(e1->str, e2->str, TAG_MAXLEN);
}
@@ -2102,49 +2462,44 @@ static int tempbuf_sort(int fd)
struct tagfile_entry fe;
int i;
int length;
-
+
/* Generate reverse lookup entries. */
for (i = 0; i < lookup_buffer_depth; i++)
{
struct tempbuf_id_list *idlist;
-
+
if (!lookup[i])
continue;
-
+
if (lookup[i]->idlist.id == i)
continue;
-
+
idlist = &lookup[i]->idlist;
while (idlist->next != NULL)
idlist = idlist->next;
-
+
+ ALIGN_BUFFER(tempbuf_pos, tempbuf_left, alignof(struct tempbuf_id_list));
tempbuf_left -= sizeof(struct tempbuf_id_list);
- if (tempbuf_left - 4 < 0)
+ if (tempbuf_left < 0)
return -1;
-
+
idlist->next = (struct tempbuf_id_list *)&tempbuf[tempbuf_pos];
- if (tempbuf_pos & 0x03)
- {
- tempbuf_pos = (tempbuf_pos & ~0x03) + 0x04;
- tempbuf_left -= 3;
- idlist->next = (struct tempbuf_id_list *)&tempbuf[tempbuf_pos];
- }
tempbuf_pos += sizeof(struct tempbuf_id_list);
-
+
idlist = idlist->next;
idlist->id = i;
idlist->next = NULL;
do_timed_yield();
}
-
+
qsort(index, tempbufidx, sizeof(struct tempbuf_searchidx), compare);
memset(lookup, 0, lookup_buffer_depth * sizeof(struct tempbuf_searchidx **));
-
+
for (i = 0; i < tempbufidx; i++)
{
struct tempbuf_id_list *idlist = &index[i].idlist;
-
+
/* Fix the lookup list. */
while (idlist != NULL)
{
@@ -2152,43 +2507,33 @@ static int tempbuf_sort(int fd)
lookup[idlist->id] = &index[i];
idlist = idlist->next;
}
-
+
index[i].seek = lseek(fd, 0, SEEK_CUR);
length = strlen(index[i].str) + 1;
fe.tag_length = length;
fe.idx_id = index[i].idx_id;
-
+
/* Check the chunk alignment. */
- if ((fe.tag_length + sizeof(struct tagfile_entry))
+ if ((fe.tag_length + sizeof(struct tagfile_entry))
% TAGFILE_ENTRY_CHUNK_LENGTH)
{
- fe.tag_length += TAGFILE_ENTRY_CHUNK_LENGTH -
- ((fe.tag_length + sizeof(struct tagfile_entry))
+ fe.tag_length += TAGFILE_ENTRY_CHUNK_LENGTH -
+ ((fe.tag_length + sizeof(struct tagfile_entry))
% TAGFILE_ENTRY_CHUNK_LENGTH);
}
-
-#ifdef TAGCACHE_STRICT_ALIGN
- /* Make sure the entry is long aligned. */
- if (index[i].seek & 0x03)
- {
- logf("tempbuf_sort: alignment error!");
- return -3;
- }
-#endif
-
- if (ecwrite(fd, &fe, 1, tagfile_entry_ec, tc_stat.econ) !=
- sizeof(struct tagfile_entry))
+
+ if (write_tagfile_entry(fd, &fe) != sizeof(struct tagfile_entry))
{
logf("tempbuf_sort: write error #1");
return -1;
}
-
+
if (write(fd, index[i].str, length) != length)
{
logf("tempbuf_sort: write error #2");
return -2;
}
-
+
/* Write some padding. */
if (fe.tag_length - length > 0)
write(fd, "XXXXXXXX", fe.tag_length - length);
@@ -2196,12 +2541,12 @@ static int tempbuf_sort(int fd)
return i;
}
-
+
inline static struct tempbuf_searchidx* tempbuf_locate(int id)
{
if (id < 0 || id >= lookup_buffer_depth)
return NULL;
-
+
return lookup[id];
}
@@ -2227,16 +2572,15 @@ static bool build_numeric_indices(struct tagcache_header *h, int tmpfd)
int max_entries;
int entries_processed = 0;
int i, j;
- char buf[TAG_MAXLEN];
-
+
max_entries = tempbuf_size / sizeof(struct temp_file_entry) - 1;
-
+
logf("Building numeric indices...");
lseek(tmpfd, sizeof(struct tagcache_header), SEEK_SET);
-
+
if ( (masterfd = open_master_fd(&tcmh, true)) < 0)
return false;
-
+
masterfd_pos = lseek(masterfd, tcmh.tch.entry_count * sizeof(struct index_entry),
SEEK_CUR);
if (masterfd_pos < 0)
@@ -2246,16 +2590,16 @@ static bool build_numeric_indices(struct tagcache_header *h, int tmpfd)
return false;
}
- while (entries_processed < h->entry_count)
+ while (entries_processed < h->entry_count && !USR_CANCEL)
{
int count = MIN(h->entry_count - entries_processed, max_entries);
-
+
/* Read in as many entries as possible. */
for (i = 0; i < count; i++)
{
struct temp_file_entry *tfe = &entrybuf[i];
int datastart;
-
+
/* Read in numeric data. */
if (read(tmpfd, tfe, sizeof(struct temp_file_entry)) !=
sizeof(struct temp_file_entry))
@@ -2266,115 +2610,115 @@ static bool build_numeric_indices(struct tagcache_header *h, int tmpfd)
}
datastart = lseek(tmpfd, 0, SEEK_CUR);
-
+
/**
* Read string data from the following tags:
* - tag_filename
* - tag_artist
* - tag_album
* - tag_title
- *
+ *
* A crc32 hash is calculated from the read data
* and stored back to the data offset field kept in memory.
*/
#define tmpdb_read_string_tag(tag) \
lseek(tmpfd, tfe->tag_offset[tag], SEEK_CUR); \
- if ((unsigned long)tfe->tag_length[tag] > sizeof buf) \
+ if ((unsigned long)tfe->tag_length[tag] > (unsigned long)build_idx_bufsz) \
{ \
logf("read fail: buffer overflow"); \
close(masterfd); \
return false; \
} \
\
- if (read(tmpfd, buf, tfe->tag_length[tag]) != \
+ if (read(tmpfd, build_idx_buf, tfe->tag_length[tag]) != \
tfe->tag_length[tag]) \
{ \
logf("read fail #2"); \
close(masterfd); \
return false; \
} \
+ str_setlen(build_idx_buf, tfe->tag_length[tag]); \
\
- tfe->tag_offset[tag] = crc_32(buf, strlen(buf), 0xffffffff); \
+ tfe->tag_offset[tag] = crc_32(build_idx_buf, strlen(build_idx_buf), 0xffffffff); \
lseek(tmpfd, datastart, SEEK_SET)
-
+
tmpdb_read_string_tag(tag_filename);
tmpdb_read_string_tag(tag_artist);
tmpdb_read_string_tag(tag_album);
tmpdb_read_string_tag(tag_title);
-
+
/* Seek to the end of the string data. */
lseek(tmpfd, tfe->data_length, SEEK_CUR);
}
-
+
/* Backup the master index position. */
masterfd_pos = lseek(masterfd, 0, SEEK_CUR);
lseek(masterfd, sizeof(struct master_header), SEEK_SET);
-
+
/* Check if we can resurrect some deleted runtime statistics data. */
- for (i = 0; i < tcmh.tch.entry_count; i++)
+ for (i = 0; i < tcmh.tch.entry_count && !USR_CANCEL; i++)
{
/* Read the index entry. */
- if (ecread_index_entry(masterfd, &idx)
- != sizeof(struct index_entry))
+ if (read_index_entries(masterfd, &idx, 1) != sizeof(struct index_entry))
{
logf("read fail #3");
close(masterfd);
return false;
}
-
+
/**
* Skip unless the entry is marked as being deleted
* or the data has already been resurrected.
*/
if (!(idx.flag & FLAG_DELETED) || (idx.flag & FLAG_RESURRECTED))
continue;
-
+
/* Now try to match the entry. */
/**
* To succesfully match a song, the following conditions
* must apply:
- *
+ *
* For numeric fields: tag_length
* - Full identical match is required
- *
+ *
* If tag_filename matches, no further checking necessary.
- *
+ *
* For string hashes: tag_artist, tag_album, tag_title
* - All three of these must match
*/
for (j = 0; j < count; j++)
{
struct temp_file_entry *tfe = &entrybuf[j];
-
+
/* Try to match numeric fields first. */
if (tfe->tag_offset[tag_length] != idx.tag_seek[tag_length])
continue;
-
+
/* Now it's time to do the hash matching. */
if (tfe->tag_offset[tag_filename] != idx.tag_seek[tag_filename])
{
int match_count = 0;
-
+
/* No filename match, check if we can match two other tags. */
#define tmpdb_match(tag) \
if (tfe->tag_offset[tag] == idx.tag_seek[tag]) \
match_count++
-
+
tmpdb_match(tag_artist);
tmpdb_match(tag_album);
tmpdb_match(tag_title);
-
+
if (match_count < 3)
{
/* Still no match found, give up. */
continue;
}
}
-
+
/* A match found, now copy & resurrect the statistical data. */
#define tmpdb_copy_tag(tag) \
tfe->tag_offset[tag] = idx.tag_seek[tag]
-
+
tmpdb_copy_tag(tag_playcount);
tmpdb_copy_tag(tag_rating);
tmpdb_copy_tag(tag_playtime);
@@ -2382,47 +2726,47 @@ static bool build_numeric_indices(struct tagcache_header *h, int tmpfd)
tmpdb_copy_tag(tag_commitid);
tmpdb_copy_tag(tag_lastelapsed);
tmpdb_copy_tag(tag_lastoffset);
-
+
/* Avoid processing this entry again. */
idx.flag |= FLAG_RESURRECTED;
-
+
lseek(masterfd, -(off_t)sizeof(struct index_entry), SEEK_CUR);
- if (ecwrite_index_entry(masterfd, &idx) != sizeof(struct index_entry))
+ if (write_index_entries(masterfd, &idx, 1) != sizeof(struct index_entry))
{
logf("masterfd writeback fail #1");
close(masterfd);
return false;
}
-
+
logf("Entry resurrected");
}
}
-
-
+
+
/* Restore the master index position. */
lseek(masterfd, masterfd_pos, SEEK_SET);
-
+
/* Commit the data to the index. */
- for (i = 0; i < count; i++)
+ for (i = 0; i < count && !USR_CANCEL; i++)
{
int loc = lseek(masterfd, 0, SEEK_CUR);
-
- if (ecread_index_entry(masterfd, &idx) != sizeof(struct index_entry))
+
+ if (read_index_entries(masterfd, &idx, 1) != sizeof(struct index_entry))
{
logf("read fail #3");
close(masterfd);
return false;
}
-
+
for (j = 0; j < TAG_COUNT; j++)
{
if (!TAGCACHE_IS_NUMERIC(j))
continue;
-
+
idx.tag_seek[j] = entrybuf[i].tag_offset[j];
}
idx.flag = entrybuf[i].flag;
-
+
if (idx.tag_seek[tag_commitid])
{
/* Data has been resurrected. */
@@ -2433,23 +2777,23 @@ static bool build_numeric_indices(struct tagcache_header *h, int tmpfd)
idx.tag_seek[tag_commitid] = current_tcmh.commitid;
idx.flag |= FLAG_DIRTYNUM;
}
-
+
/* Write back the updated index. */
lseek(masterfd, loc, SEEK_SET);
- if (ecwrite_index_entry(masterfd, &idx) != sizeof(struct index_entry))
+ if (write_index_entries(masterfd, &idx, 1) != sizeof(struct index_entry))
{
logf("write fail");
close(masterfd);
return false;
}
}
-
+
entries_processed += count;
logf("%d/%ld entries processed", entries_processed, h->entry_count);
}
-
+
close(masterfd);
-
+
return true;
}
@@ -2466,17 +2810,16 @@ static int build_index(int index_type, struct tagcache_header *h, int tmpfd)
struct master_header tcmh;
struct index_entry idxbuf[IDX_BUF_DEPTH];
int idxbuf_pos;
- char buf[TAG_MAXLEN+32];
int fd = -1, masterfd;
bool error = false;
int init;
int masterfd_pos;
-
+
logf("Building index: %d", index_type);
-
+
/* Check the number of entries we need to allocate ram for. */
commit_entry_count = h->entry_count + 1;
-
+
masterfd = open_master_fd(&tcmh, false);
if (masterfd >= 0)
{
@@ -2501,10 +2844,10 @@ static int build_index(int index_type, struct tagcache_header *h, int tmpfd)
/* First part */ commit_entry_count +
/* Second part */ 0;
}
-
+
logf("lookup_buffer_depth=%ld", lookup_buffer_depth);
logf("commit_entry_count=%ld", commit_entry_count);
-
+
/* Allocate buffer for all index entries from both old and new
* tag files. */
tempbufidx = 0;
@@ -2513,21 +2856,21 @@ static int build_index(int index_type, struct tagcache_header *h, int tmpfd)
/* Allocate lookup buffer. The first portion of commit_entry_count
* contains the new tags in the temporary file and the second
* part for locating entries already in the db.
- *
+ *
* New tags Old tags
* +---------+---------------------------+
* | index | position/ENTRY_CHUNK_SIZE | lookup buffer
* +---------+---------------------------+
- *
+ *
* Old tags are inserted to a temporary buffer with position:
* tempbuf_insert(position/ENTRY_CHUNK_SIZE, ...);
* And new tags with index:
* tempbuf_insert(idx, ...);
- *
+ *
* The buffer is sorted and written into tag file:
* tempbuf_sort(...);
* leaving master index locations messed up.
- *
+ *
* That is fixed using the lookup buffer for old tags:
* new_seek = tempbuf_find_location(old_seek, ...);
* and for new tags:
@@ -2536,13 +2879,14 @@ static int build_index(int index_type, struct tagcache_header *h, int tmpfd)
lookup = (struct tempbuf_searchidx **)&tempbuf[tempbuf_pos];
tempbuf_pos += lookup_buffer_depth * sizeof(void **);
memset(lookup, 0, lookup_buffer_depth * sizeof(void **));
-
+
/* And calculate the remaining data space used mainly for storing
* tag data (strings). */
tempbuf_left = tempbuf_size - tempbuf_pos - 8;
if (tempbuf_left - TAGFILE_ENTRY_AVG_LENGTH * commit_entry_count < 0)
{
logf("Buffer way too small!");
+ close(fd);
return 0;
}
@@ -2556,43 +2900,37 @@ static int build_index(int index_type, struct tagcache_header *h, int tmpfd)
if (TAGCACHE_IS_SORTED(index_type))
{
logf("loading tags...");
- for (i = 0; i < tch.entry_count; i++)
+ for (i = 0; i < tch.entry_count && !USR_CANCEL; i++)
{
struct tagfile_entry entry;
int loc = lseek(fd, 0, SEEK_CUR);
bool ret;
-
- if (ecread_tagfile_entry(fd, &entry) != sizeof(struct tagfile_entry))
+ switch (read_tagfile_entry_and_tag(fd, &entry, build_idx_buf, build_idx_bufsz))
{
- logf("read error #7");
- close(fd);
- return -2;
- }
-
- if (entry.tag_length >= (int)sizeof(buf))
- {
- logf("too long tag #3");
- close(fd);
- return -2;
- }
-
- if (read(fd, buf, entry.tag_length) != entry.tag_length)
- {
- logf("read error #8");
- close(fd);
- return -2;
+ case e_SUCCESS_LEN_ZERO: /* Skip deleted entries. */
+ continue;
+ case e_SUCCESS:
+ break;
+ case e_ENTRY_SIZEMISMATCH:
+ logf("read error #7");
+ close(fd);
+ return -2;
+ case e_TAG_TOOLONG:
+ logf("too long tag #3");
+ close(fd);
+ return -2;
+ case e_TAG_SIZEMISMATCH:
+ logf("read error #8");
+ close(fd);
+ return -2;
}
- /* Skip deleted entries. */
- if (buf[0] == '\0')
- continue;
-
/**
* Save the tag and tag id in the memory buffer. Tag id
* is saved so we can later reindex the master lookup
* table when the index gets resorted.
*/
- ret = tempbuf_insert(buf, loc/TAGFILE_ENTRY_CHUNK_LENGTH
+ ret = tempbuf_insert(build_idx_buf, loc/TAGFILE_ENTRY_CHUNK_LENGTH
+ commit_entry_count, entry.idx_id,
TAGCACHE_IS_UNIQUE(index_type));
if (!ret)
@@ -2609,24 +2947,29 @@ static int build_index(int index_type, struct tagcache_header *h, int tmpfd)
}
else
{
+ logf("Create New Index: %d", index_type);
/**
* Creating new index file to store the tags. No need to preload
* anything whether the index type is sorted or not.
+ *
+ * Note: although we are creating a file under the db path, it must
+ * already exist by this point so no mkdir is required.
*/
- snprintf(buf, sizeof buf, TAGCACHE_FILE_INDEX, index_type);
- fd = open(buf, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+ fd = open_pathfmt(build_idx_buf, build_idx_bufsz,
+ O_WRONLY | O_CREAT | O_TRUNC,
+ "%s/" TAGCACHE_FILE_INDEX,
+ tc_stat.db_path, index_type);
if (fd < 0)
{
- logf("%s open fail", buf);
+ logf(TAGCACHE_FILE_INDEX " open fail", index_type);
return -2;
}
-
+
tch.magic = TAGCACHE_MAGIC;
tch.entry_count = 0;
tch.datasize = 0;
-
- if (ecwrite(fd, &tch, 1, tagcache_header_ec, tc_stat.econ)
- != sizeof(struct tagcache_header))
+
+ if (write_tagcache_header(fd, &tch) != sizeof(struct tagcache_header))
{
logf("header write failed");
close(fd);
@@ -2636,12 +2979,12 @@ static int build_index(int index_type, struct tagcache_header *h, int tmpfd)
/* Loading the tag lookup file as "master file". */
logf("Loading index file");
- masterfd = open(TAGCACHE_FILE_MASTER, O_RDWR);
+ masterfd = open_db_fd(TAGCACHE_FILE_MASTER, O_RDWR);
if (masterfd < 0)
{
logf("Creating new DB");
- masterfd = open(TAGCACHE_FILE_MASTER, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+ masterfd = open_db_fd(TAGCACHE_FILE_MASTER, O_WRONLY | O_CREAT | O_TRUNC);
if (masterfd < 0)
{
@@ -2656,7 +2999,7 @@ static int build_index(int index_type, struct tagcache_header *h, int tmpfd)
tcmh.tch.entry_count = 0;
tcmh.tch.datasize = 0;
tcmh.dirty = true;
- ecwrite(masterfd, &tcmh, 1, master_header_ec, tc_stat.econ);
+ write_master_header(masterfd, &tcmh);
init = true;
masterfd_pos = lseek(masterfd, 0, SEEK_CUR);
}
@@ -2668,8 +3011,8 @@ static int build_index(int index_type, struct tagcache_header *h, int tmpfd)
*/
init = false;
- if (ecread(masterfd, &tcmh, 1, master_header_ec, tc_stat.econ) !=
- sizeof(struct master_header) || tcmh.tch.magic != TAGCACHE_MAGIC)
+ if (read_master_header(masterfd, &tcmh) != sizeof(struct master_header) ||
+ tcmh.tch.magic != TAGCACHE_MAGIC)
{
logf("header error");
close(fd);
@@ -2702,10 +3045,10 @@ static int build_index(int index_type, struct tagcache_header *h, int tmpfd)
lseek(tmpfd, sizeof(struct tagcache_header), SEEK_SET);
/* h is the header of the temporary file containing new tags. */
logf("inserting new tags...");
- for (i = 0; i < h->entry_count; i++)
+ for (i = 0; i < h->entry_count && !USR_CANCEL; i++)
{
struct temp_file_entry entry;
-
+
if (read(tmpfd, &entry, sizeof(struct temp_file_entry)) !=
sizeof(struct temp_file_entry))
{
@@ -2715,7 +3058,7 @@ static int build_index(int index_type, struct tagcache_header *h, int tmpfd)
}
/* Read data. */
- if (entry.tag_length[index_type] >= (long)sizeof(buf))
+ if (entry.tag_length[index_type] >= build_idx_bufsz)
{
logf("too long entry!");
error = true;
@@ -2723,25 +3066,31 @@ static int build_index(int index_type, struct tagcache_header *h, int tmpfd)
}
lseek(tmpfd, entry.tag_offset[index_type], SEEK_CUR);
- if (read(tmpfd, buf, entry.tag_length[index_type]) !=
+ if (read(tmpfd, build_idx_buf, entry.tag_length[index_type]) !=
entry.tag_length[index_type])
{
logf("read fail #4");
error = true;
goto error_exit;
}
-
- if (TAGCACHE_IS_UNIQUE(index_type))
- error = !tempbuf_insert(buf, i, -1, true);
- else
- error = !tempbuf_insert(buf, i, tcmh.tch.entry_count + i, false);
-
- if (error)
+ str_setlen(build_idx_buf, entry.tag_length[index_type]);
+
+#if defined(PLUGIN)
+ if (user_check_tag(index_type, build_idx_buf))
+#endif /*defined(PLUGIN)*/
{
- logf("insert error");
- goto error_exit;
+ if (TAGCACHE_IS_UNIQUE(index_type))
+ error = !tempbuf_insert(build_idx_buf, i, -1, true);
+ else
+ error = !tempbuf_insert(build_idx_buf, i,
+ tcmh.tch.entry_count + i, false);
+
+ if (error)
+ {
+ logf("insert error");
+ goto error_exit;
+ }
}
-
/* Skip to next. */
lseek(tmpfd, entry.data_length - entry.tag_offset[index_type] -
entry.tag_length[index_type], SEEK_CUR);
@@ -2757,33 +3106,33 @@ static int build_index(int index_type, struct tagcache_header *h, int tmpfd)
* entry_count and don't crash with that).
*/
ftruncate(fd, lseek(fd, 0, SEEK_CUR));
-
+
i = tempbuf_sort(fd);
if (i < 0)
goto error_exit;
logf("sorted %d tags", i);
-
+
/**
* Now update all indexes in the master lookup file.
*/
logf("updating indices...");
lseek(masterfd, sizeof(struct master_header), SEEK_SET);
- for (i = 0; i < tcmh.tch.entry_count; i += idxbuf_pos)
+ for (i = 0; i < tcmh.tch.entry_count && !USR_CANCEL; i += idxbuf_pos)
{
int j;
int loc = lseek(masterfd, 0, SEEK_CUR);
-
+
idxbuf_pos = MIN(tcmh.tch.entry_count - i, IDX_BUF_DEPTH);
-
- if (ecread(masterfd, idxbuf, idxbuf_pos, index_entry_ec, tc_stat.econ)
- != (int)sizeof(struct index_entry)*idxbuf_pos)
+
+ if (read_index_entries(masterfd, idxbuf, idxbuf_pos) !=
+ (ssize_t)sizeof(struct index_entry) * idxbuf_pos)
{
logf("read fail #5");
error = true;
goto error_exit ;
}
lseek(masterfd, loc, SEEK_SET);
-
+
for (j = 0; j < idxbuf_pos; j++)
{
if (idxbuf[j].flag & FLAG_DELETED)
@@ -2792,26 +3141,25 @@ static int build_index(int index_type, struct tagcache_header *h, int tmpfd)
// idxbuf[j].tag_seek[index_type] = 0;
continue;
}
-
+
idxbuf[j].tag_seek[index_type] = tempbuf_find_location(
idxbuf[j].tag_seek[index_type]/TAGFILE_ENTRY_CHUNK_LENGTH
+ commit_entry_count);
-
+
if (idxbuf[j].tag_seek[index_type] < 0)
{
- logf("update error: %ld/%d/%ld",
+ logf("update error: %ld/%d/%ld",
idxbuf[j].flag, i+j, tcmh.tch.entry_count);
error = true;
goto error_exit;
}
-
+
do_timed_yield();
}
-
+
/* Write back the updated index. */
- if (ecwrite(masterfd, idxbuf, idxbuf_pos,
- index_entry_ec, tc_stat.econ) !=
- (int)sizeof(struct index_entry)*idxbuf_pos)
+ if (write_index_entries(masterfd, idxbuf, idxbuf_pos) !=
+ (ssize_t)sizeof(struct index_entry) * idxbuf_pos)
{
logf("write fail");
error = true;
@@ -2829,10 +3177,10 @@ static int build_index(int index_type, struct tagcache_header *h, int tmpfd)
lseek(masterfd, masterfd_pos, SEEK_SET);
lseek(tmpfd, sizeof(struct tagcache_header), SEEK_SET);
lseek(fd, 0, SEEK_END);
- for (i = 0; i < h->entry_count; i += idxbuf_pos)
+ for (i = 0; i < h->entry_count && !USR_CANCEL; i += idxbuf_pos)
{
int j;
-
+
idxbuf_pos = MIN(h->entry_count - i, IDX_BUF_DEPTH);
if (init)
{
@@ -2841,9 +3189,9 @@ static int build_index(int index_type, struct tagcache_header *h, int tmpfd)
else
{
int loc = lseek(masterfd, 0, SEEK_CUR);
-
- if (ecread(masterfd, idxbuf, idxbuf_pos, index_entry_ec, tc_stat.econ)
- != (int)sizeof(struct index_entry)*idxbuf_pos)
+
+ if (read_index_entries(masterfd, idxbuf, idxbuf_pos) !=
+ (ssize_t)sizeof(struct index_entry) * idxbuf_pos)
{
logf("read fail #6");
error = true;
@@ -2859,7 +3207,7 @@ static int build_index(int index_type, struct tagcache_header *h, int tmpfd)
{
struct temp_file_entry entry;
struct tagfile_entry fe;
-
+
if (read(tmpfd, &entry, sizeof(struct temp_file_entry)) !=
sizeof(struct temp_file_entry))
{
@@ -2867,9 +3215,9 @@ static int build_index(int index_type, struct tagcache_header *h, int tmpfd)
error = true;
break ;
}
-
+
/* Read data. */
- if (entry.tag_length[index_type] >= (int)sizeof(buf))
+ if (entry.tag_length[index_type] >= build_idx_bufsz)
{
logf("too long entry!");
logf("length=%d", entry.tag_length[index_type]);
@@ -2877,9 +3225,9 @@ static int build_index(int index_type, struct tagcache_header *h, int tmpfd)
error = true;
break ;
}
-
+
lseek(tmpfd, entry.tag_offset[index_type], SEEK_CUR);
- if (read(tmpfd, buf, entry.tag_length[index_type]) !=
+ if (read(tmpfd, build_idx_buf, entry.tag_length[index_type]) !=
entry.tag_length[index_type])
{
logf("read fail #8");
@@ -2893,8 +3241,8 @@ static int build_index(int index_type, struct tagcache_header *h, int tmpfd)
idxbuf[j].tag_seek[index_type] = lseek(fd, 0, SEEK_CUR);
fe.tag_length = entry.tag_length[index_type];
fe.idx_id = tcmh.tch.entry_count + i + j;
- ecwrite(fd, &fe, 1, tagfile_entry_ec, tc_stat.econ);
- write(fd, buf, fe.tag_length);
+ write_tagfile_entry(fd, &fe);
+ write(fd, build_idx_buf, fe.tag_length);
tempbufidx++;
/* Skip to next. */
@@ -2915,37 +3263,36 @@ static int build_index(int index_type, struct tagcache_header *h, int tmpfd)
}
/* Write index. */
- if (ecwrite(masterfd, idxbuf, idxbuf_pos,
- index_entry_ec, tc_stat.econ) !=
- (int)sizeof(struct index_entry)*idxbuf_pos)
+ if (write_index_entries(masterfd, idxbuf, idxbuf_pos) !=
+ (ssize_t)sizeof(struct index_entry) * idxbuf_pos)
{
logf("tagcache: write fail #4");
error = true;
break ;
}
-
+
do_timed_yield();
}
logf("done");
-
+
/* Finally write the header. */
tch.magic = TAGCACHE_MAGIC;
tch.entry_count = tempbufidx;
tch.datasize = lseek(fd, 0, SEEK_END) - sizeof(struct tagcache_header);
lseek(fd, 0, SEEK_SET);
- ecwrite(fd, &tch, 1, tagcache_header_ec, tc_stat.econ);
-
+ write_tagcache_header(fd, &tch);
+
if (index_type != tag_filename)
h->datasize += tch.datasize;
logf("s:%d/%ld/%ld", index_type, tch.datasize, h->datasize);
error_exit:
-
+
close(fd);
close(masterfd);
if (error)
return -2;
-
+
return 1;
}
@@ -2963,27 +3310,40 @@ static bool commit(void)
bool ramcache_buffer_stolen = false;
#endif
logf("committing tagcache");
-
+
while (write_lock)
sleep(1);
- tmpfd = open(TAGCACHE_FILE_TEMP, O_RDONLY);
+#if !defined(PLUGIN)
+ int fd = open_db_fd(TAGCACHE_FILE_NOCOMMIT, O_RDONLY);
+ if (fd >= 0)
+ {
+ logf("canceling commit");
+ tc_stat.commit_delayed = true;
+ close(fd);
+ tmpfd = -1;
+ }
+ else
+#endif /*!defined(PLUGIN)*/
+ {
+ tmpfd = open_db_fd(TAGCACHE_FILE_TEMP, O_RDONLY);
+ }
if (tmpfd < 0)
{
logf("nothing to commit");
return true;
}
-
-
+
+
/* Load the header. */
len = sizeof(struct tagcache_header);
rc = read(tmpfd, &tch, len);
-
+
if (tch.magic != TAGCACHE_MAGIC || rc != len)
{
logf("incorrect tmpheader");
close(tmpfd);
- remove(TAGCACHE_FILE_TEMP);
+ remove_db_file(TAGCACHE_FILE_TEMP);
return false;
}
@@ -2992,11 +3352,11 @@ static bool commit(void)
/* Fully initialize existing headers (if any) before going further. */
tc_stat.ready = check_all_headers();
-
+
#ifdef HAVE_EEPROM_SETTINGS
- remove(TAGCACHE_STATEFILE);
+ remove_db_file(TAGCACHE_STATEFILE);
#endif
-
+
/* At first be sure to unload the ramcache! */
#ifdef HAVE_TC_RAMCACHE
tc_stat.ramcache = false;
@@ -3005,7 +3365,7 @@ static bool commit(void)
/* Beyond here, jump to commit_error to undo locks and restore dircache */
rc = false;
read_lock++;
-
+
/* Try to steal every buffer we can :) */
#ifdef HAVE_DIRCACHE
if (tempbuf_size == 0)
@@ -3017,7 +3377,7 @@ static bool commit(void)
allocate_tempbuf();
}
#endif /* HAVE_DIRCACHE */
-
+
#ifdef HAVE_TC_RAMCACHE
if (tempbuf_size == 0 && tc_stat.ramcache_allocated > 0)
{
@@ -3028,7 +3388,15 @@ static bool commit(void)
ramcache_buffer_stolen = true;
}
#endif /* HAVE_TC_RAMCACHE */
-
+
+#if defined(PLUGIN)
+ if (tempbuf_size == 0)
+ {
+ tempbuf = rb->plugin_get_audio_buffer(&tempbuf_size);
+ tempbuf_size &= ~0x03;
+ }
+#endif /*defined(PLUGIN)*/
+
/* And finally fail if there are no buffers available. */
if (tempbuf_size == 0)
{
@@ -3037,25 +3405,25 @@ static bool commit(void)
close(tmpfd);
goto commit_error;
}
-
+
logf("commit %ld entries...", tch.entry_count);
-
+
/* Mark DB dirty so it will stay disabled if commit fails. */
current_tcmh.dirty = true;
update_master_header();
-
+
/* Now create the index files. */
tc_stat.commit_step = 0;
tch.datasize = 0;
tc_stat.commit_delayed = false;
-
- for (i = 0; i < TAG_COUNT; i++)
+
+ for (i = 0; i < TAG_COUNT && !USR_CANCEL; i++)
{
int ret;
-
+
if (TAGCACHE_IS_NUMERIC(i))
continue;
-
+
tc_stat.commit_step++;
ret = build_index(i, &tch, tmpfd);
if (ret <= 0)
@@ -3064,12 +3432,13 @@ static bool commit(void)
logf("tagcache failed init");
if (ret == 0)
tc_stat.commit_delayed = true;
-
+
tc_stat.commit_step = 0;
goto commit_error;
}
+ do_timed_yield();
}
-
+
if (!build_numeric_indices(&tch, tmpfd))
{
logf("Failure to commit numeric indices");
@@ -3077,47 +3446,49 @@ static bool commit(void)
tc_stat.commit_step = 0;
goto commit_error;
}
-
+
close(tmpfd);
-
- tc_stat.commit_step = 0;
-
- /* Update the master index headers. */
- if ( (masterfd = open_master_fd(&tcmh, true)) < 0)
- goto commit_error;
-
- remove(TAGCACHE_FILE_TEMP);
- tcmh.tch.entry_count += tch.entry_count;
- tcmh.tch.datasize = sizeof(struct master_header)
- + sizeof(struct index_entry) * tcmh.tch.entry_count
- + tch.datasize;
- tcmh.dirty = false;
- tcmh.commitid++;
+ tc_stat.commit_step = 0;
- lseek(masterfd, 0, SEEK_SET);
- ecwrite(masterfd, &tcmh, 1, master_header_ec, tc_stat.econ);
- close(masterfd);
-
- logf("tagcache committed");
- tc_stat.ready = check_all_headers();
- tc_stat.readyvalid = true;
-
-#ifdef HAVE_TC_RAMCACHE
- if (ramcache_buffer_stolen)
+ if (!USR_CANCEL)
{
- tempbuf = NULL;
- tempbuf_size = 0;
- ramcache_buffer_stolen = false;
- tcrc_buffer_unlock();
- }
+ /* Update the master index headers. */
+ if ( (masterfd = open_master_fd(&tcmh, true)) < 0)
+ goto commit_error;
+
+ remove_db_file(TAGCACHE_FILE_TEMP);
+
+ tcmh.tch.entry_count += tch.entry_count;
+ tcmh.tch.datasize = sizeof(struct master_header)
+ + sizeof(struct index_entry) * tcmh.tch.entry_count
+ + tch.datasize;
+ tcmh.dirty = false;
+ tcmh.commitid++;
+
+ lseek(masterfd, 0, SEEK_SET);
+ write_master_header(masterfd, &tcmh);
+ close(masterfd);
+
+ logf("tagcache committed");
+ tagcache_commit_finalize();
+
+#if defined(HAVE_TC_RAMCACHE)
+ if (ramcache_buffer_stolen)
+ {
+ tempbuf = NULL;
+ tempbuf_size = 0;
+ ramcache_buffer_stolen = false;
+ tcrc_buffer_unlock();
+ }
- /* Reload tagcache. */
- if (tc_stat.ramcache_allocated > 0)
- tagcache_start_scan();
+ /* Reload tagcache. */
+ if (tc_stat.ramcache_allocated > 0)
+ tagcache_start_scan();
#endif /* HAVE_TC_RAMCACHE */
-
- rc = true;
+
+ rc = true;
+ } /*!USR_CANCEL*/
commit_error:
#ifdef HAVE_TC_RAMCACHE
@@ -3139,43 +3510,50 @@ commit_error:
dircache_resume();
}
#endif /* HAVE_DIRCACHE */
-
+
return rc;
}
+void tagcache_commit_finalize(void)
+{
+ tc_stat.ready = check_all_headers();
+ tc_stat.readyvalid = true;
+}
+
+#if !defined(PLUGIN)
#ifndef __PCTOOL__
static bool modify_numeric_entry(int masterfd, int idx_id, int tag, long data)
{
struct index_entry idx;
-
+
if (!tc_stat.ready)
return false;
-
+
if (!TAGCACHE_IS_NUMERIC(tag))
return false;
-
+
if (!get_index(masterfd, idx_id, &idx, false))
return false;
-
+
idx.tag_seek[tag] = data;
idx.flag |= FLAG_DIRTYNUM;
-
+
return write_index(masterfd, idx_id, &idx);
}
#if 0
-bool tagcache_modify_numeric_entry(struct tagcache_search *tcs,
+bool tagcache_modify_numeric_entry(struct tagcache_search *tcs,
int tag, long data)
{
struct master_header myhdr;
-
+
if (tcs->masterfd < 0)
{
if ( (tcs->masterfd = open_master_fd(&myhdr, true)) < 0)
return false;
}
-
+
return modify_numeric_entry(tcs->masterfd, tcs->idx_id, tag, data);
}
#endif
@@ -3183,11 +3561,11 @@ bool tagcache_modify_numeric_entry(struct tagcache_search *tcs,
static bool command_queue_is_full(void)
{
int next;
-
+
next = command_queue_widx + 1;
if (next >= TAGCACHE_COMMAND_QUEUE_LENGTH)
next = 0;
-
+
return (next == command_queue_ridx);
}
@@ -3195,27 +3573,27 @@ static void command_queue_sync_callback(void)
{
struct master_header myhdr;
int masterfd;
-
+
mutex_lock(&command_queue_mutex);
-
+
if ( (masterfd = open_master_fd(&myhdr, true)) < 0)
return;
-
+
while (command_queue_ridx != command_queue_widx)
{
struct tagcache_command_entry *ce = &command_queue[command_queue_ridx];
-
+
switch (ce->command)
{
case CMD_UPDATE_MASTER_HEADER:
{
close(masterfd);
update_master_header();
-
+
/* Re-open the masterfd. */
if ( (masterfd = open_master_fd(&myhdr, true)) < 0)
return;
-
+
break;
}
case CMD_UPDATE_NUMERIC:
@@ -3224,13 +3602,13 @@ static void command_queue_sync_callback(void)
break;
}
}
-
+
if (++command_queue_ridx >= TAGCACHE_COMMAND_QUEUE_LENGTH)
command_queue_ridx = 0;
}
-
+
close(masterfd);
-
+
tc_stat.queue_length = 0;
mutex_unlock(&command_queue_mutex);
}
@@ -3239,7 +3617,7 @@ static void run_command_queue(bool force)
{
if (COMMAND_QUEUE_IS_EMPTY)
return;
-
+
if (force || command_queue_is_full())
command_queue_sync_callback();
else
@@ -3251,30 +3629,30 @@ static void queue_command(int cmd, long idx_id, int tag, long data)
while (1)
{
int next;
-
+
mutex_lock(&command_queue_mutex);
next = command_queue_widx + 1;
if (next >= TAGCACHE_COMMAND_QUEUE_LENGTH)
next = 0;
-
+
/* Make sure queue is not full. */
if (next != command_queue_ridx)
{
struct tagcache_command_entry *ce = &command_queue[command_queue_widx];
-
+
ce->command = cmd;
ce->idx_id = idx_id;
ce->tag = tag;
ce->data = data;
-
+
command_queue_widx = next;
-
+
tc_stat.queue_length++;
-
+
mutex_unlock(&command_queue_mutex);
break;
}
-
+
/* Queue is full, try again later... */
mutex_unlock(&command_queue_mutex);
sleep(1);
@@ -3284,16 +3662,16 @@ static void queue_command(int cmd, long idx_id, int tag, long data)
long tagcache_increase_serial(void)
{
long old;
-
+
if (!tc_stat.ready)
return -2;
-
+
while (read_lock)
sleep(1);
-
+
old = current_tcmh.serial++;
queue_command(CMD_UPDATE_MASTER_HEADER, 0, 0, 0);
-
+
return old;
}
@@ -3306,51 +3684,54 @@ void tagcache_update_numeric(int idx_id, int tag, long data)
static bool write_tag(int fd, const char *tagstr, const char *datastr)
{
char buf[512];
+ const int bufsz = sizeof(buf);
int i;
-
- snprintf(buf, sizeof buf, "%s=\"", tagstr);
+
+ snprintf(buf, bufsz, "%s=\"", tagstr);
+
for (i = strlen(buf); i < (long)sizeof(buf)-4; i++)
{
if (*datastr == '\0')
break;
-
+
if (*datastr == '"' || *datastr == '\\')
buf[i++] = '\\';
-
+
else if (*datastr == '\n')
{
buf[i++] = '\\';
buf[i] = 'n';
continue;
}
-
+
buf[i] = *(datastr++);
}
-
- strcpy(&buf[i], "\" ");
-
+
+ str_setlen(buf, bufsz - 1);
+ strmemccpy(&buf[i], "\" ", (bufsz - i - 1));
+
write(fd, buf, i + 2);
-
+
return true;
}
#ifndef __PCTOOL__
-static bool read_tag(char *dest, long size,
+static bool read_tag(char *dest, long size,
const char *src, const char *tagstr)
{
int pos;
char current_tag[32];
-
+
while (*src != '\0')
{
/* Skip all whitespace */
while (*src == ' ')
src++;
-
+
if (*src == '\0')
break;
-
+
pos = 0;
/* Read in tag name */
while (*src != '=' && *src != ' ')
@@ -3358,27 +3739,28 @@ static bool read_tag(char *dest, long size,
current_tag[pos] = *src;
src++;
pos++;
-
- if (*src == '\0' || pos >= (long)sizeof(current_tag))
+
+ if (*src == '\0' || pos >= (int) sizeof(current_tag))
return false;
}
- current_tag[pos] = '\0';
-
+
+ str_setlen(current_tag, pos);
+
/* Read in tag data */
-
+
/* Find the start. */
while (*src != '"' && *src != '\0')
src++;
if (*src == '\0' || *(++src) == '\0')
return false;
-
+
/* Read the data. */
for (pos = 0; pos < size; pos++)
{
if (*src == '\0')
break;
-
+
if (*src == '\\')
{
src++;
@@ -3386,36 +3768,36 @@ static bool read_tag(char *dest, long size,
dest[pos] = '\n';
else
dest[pos] = *src;
-
+
src++;
continue;
}
-
+
if (*src == '\0')
break;
-
+
if (*src == '"')
{
src++;
break;
}
-
+
dest[pos] = *(src++);
}
-
- dest[pos] = '\0';
+
+ str_setlen(dest, pos);
if (!strcasecmp(tagstr, current_tag))
return true;
}
-
+
return false;
}
static int parse_changelog_line(int line_n, char *buf, void *parameters)
{
struct index_entry idx;
- char tag_data[TAG_MAXLEN+32];
+ char tag_data[TAGCACHE_BUFSZ];
int idx_id;
long masterfd = (long)(intptr_t)parameters;
const int import_tags[] = { tag_playcount, tag_rating, tag_playtime,
@@ -3423,10 +3805,10 @@ static int parse_changelog_line(int line_n, char *buf, void *parameters)
tag_lastoffset };
int i;
(void)line_n;
-
+
if (*buf == '#')
return 0;
-
+
/* logf("%d/%s", line_n, buf); */
if (!read_tag(tag_data, sizeof tag_data, buf, "filename"))
{
@@ -3434,49 +3816,49 @@ static int parse_changelog_line(int line_n, char *buf, void *parameters)
logf("-> %s", buf);
return 0;
}
-
+
idx_id = find_index(tag_data);
if (idx_id < 0)
{
logf("%d/entry not found", line_n);
return 0;
}
-
+
if (!get_index(masterfd, idx_id, &idx, false))
{
logf("%d/failed to retrieve index entry", line_n);
return 0;
}
-
+
/* Stop if tag has already been modified. */
if (idx.flag & FLAG_DIRTYNUM)
return 0;
-
+
logf("%d/import: %s", line_n, tag_data);
-
+
idx.flag |= FLAG_DIRTYNUM;
for (i = 0; i < (long)(sizeof(import_tags)/sizeof(import_tags[0])); i++)
{
int data;
-
+
if (!read_tag(tag_data, sizeof tag_data, buf,
tagcache_tag_to_str(import_tags[i])))
{
continue;
}
-
+
data = atoi(tag_data);
if (data < 0)
continue;
-
+
idx.tag_seek[import_tags[i]] = data;
-
+
if (import_tags[i] == tag_lastplayed && data >= current_tcmh.serial)
current_tcmh.serial = data + 1;
else if (import_tags[i] == tag_commitid && data >= current_tcmh.commitid)
current_tcmh.commitid = data + 1;
}
-
+
return write_index(masterfd, idx_id, &idx) ? 0 : -5;
}
@@ -3487,46 +3869,47 @@ bool tagcache_import_changelog(void)
int clfd;
long masterfd;
char buf[2048];
-
+ const int bufsz = sizeof(buf);
+
if (!tc_stat.ready)
return false;
-
+
while (read_lock)
sleep(1);
-
- clfd = open(TAGCACHE_FILE_CHANGELOG, O_RDONLY);
+
+ clfd = open_db_fd(TAGCACHE_FILE_CHANGELOG, O_RDONLY);
if (clfd < 0)
{
logf("failure to open changelog");
return false;
}
-
+
if ( (masterfd = open_master_fd(&myhdr, true)) < 0)
{
close(clfd);
return false;
}
-
+
write_lock++;
-
+
filenametag_fd = open_tag_fd(&tch, tag_filename, false);
- fast_readline(clfd, buf, sizeof buf, (void *)(intptr_t)masterfd,
+ fast_readline(clfd, buf, bufsz, (void *)(intptr_t)masterfd,
parse_changelog_line);
-
+
close(clfd);
close(masterfd);
-
+
if (filenametag_fd >= 0)
{
close(filenametag_fd);
filenametag_fd = -1;
}
-
+
write_lock--;
-
+
update_master_header();
-
+
return true;
}
@@ -3536,25 +3919,26 @@ bool tagcache_create_changelog(struct tagcache_search *tcs)
{
struct master_header myhdr;
struct index_entry idx;
- char buf[TAG_MAXLEN+32];
+ char buf[TAGCACHE_BUFSZ];
+ const int bufsz = sizeof(buf);
char temp[32];
int clfd;
int i, j;
-
+
if (!tc_stat.ready)
return false;
-
+
if (!tagcache_search(tcs, tag_filename))
return false;
-
+
/* Initialize the changelog */
- clfd = open(TAGCACHE_FILE_CHANGELOG, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+ clfd = open_db_fd(TAGCACHE_FILE_CHANGELOG, O_WRONLY | O_CREAT | O_TRUNC);
if (clfd < 0)
{
logf("failure to open changelog");
return false;
}
-
+
if (tcs->masterfd < 0)
{
if ( (tcs->masterfd = open_master_fd(&myhdr, false)) < 0)
@@ -3566,29 +3950,29 @@ bool tagcache_create_changelog(struct tagcache_search *tcs)
else
{
lseek(tcs->masterfd, 0, SEEK_SET);
- ecread(tcs->masterfd, &myhdr, 1, master_header_ec, tc_stat.econ);
+ read_master_header(tcs->masterfd, &myhdr);
}
-
+
write(clfd, "## Changelog version 1\n", 23);
-
+
for (i = 0; i < myhdr.tch.entry_count; i++)
{
- if (ecread_index_entry(tcs->masterfd, &idx) != sizeof(struct index_entry))
+ if (read_index_entries(tcs->masterfd, &idx, 1) != sizeof(struct index_entry))
{
logf("read error #9");
tagcache_search_finish(tcs);
close(clfd);
return false;
}
-
+
/* Skip until the entry found has been modified. */
if (! (idx.flag & FLAG_DIRTYNUM) )
continue;
-
+
/* Skip deleted entries too. */
if (idx.flag & FLAG_DELETED)
continue;
-
+
/* Now retrieve all tags. */
for (j = 0; j < TAG_COUNT; j++)
{
@@ -3598,20 +3982,20 @@ bool tagcache_create_changelog(struct tagcache_search *tcs)
write_tag(clfd, tagcache_tag_to_str(j), temp);
continue;
}
-
+
tcs->type = j;
- tagcache_retrieve(tcs, i, tcs->type, buf, sizeof buf);
+ tagcache_retrieve(tcs, i, tcs->type, buf, bufsz);
write_tag(clfd, tagcache_tag_to_str(j), buf);
}
-
+
write(clfd, "\n", 1);
do_timed_yield();
}
-
+
close(clfd);
-
+
tagcache_search_finish(tcs);
-
+
return true;
}
@@ -3622,36 +4006,35 @@ static bool delete_entry(long idx_id)
int tag, i;
struct index_entry idx, myidx;
struct master_header myhdr;
- char buf[TAG_MAXLEN+32];
int in_use[TAG_COUNT];
-
+
logf("delete_entry(): %ld", idx_id);
-
+
#ifdef HAVE_TC_RAMCACHE
/* At first mark the entry removed from ram cache. */
if (tc_stat.ramcache)
tcramcache.hdr->indices[idx_id].flag |= FLAG_DELETED;
#endif
-
+
if ( (masterfd = open_master_fd(&myhdr, true) ) < 0)
return false;
-
+
lseek(masterfd, idx_id * sizeof(struct index_entry), SEEK_CUR);
- if (ecread_index_entry(masterfd, &myidx) != sizeof(struct index_entry))
+ if (read_index_entries(masterfd, &myidx, 1) != sizeof(struct index_entry))
{
logf("delete_entry(): read error");
goto cleanup;
}
-
+
if (myidx.flag & FLAG_DELETED)
{
logf("delete_entry(): already deleted!");
goto cleanup;
}
-
+
myidx.flag |= FLAG_DELETED;
lseek(masterfd, -(off_t)sizeof(struct index_entry), SEEK_CUR);
- if (ecwrite_index_entry(masterfd, &myidx) != sizeof(struct index_entry))
+ if (write_index_entries(masterfd, &myidx, 1) != sizeof(struct index_entry))
{
logf("delete_entry(): write_error #1");
goto cleanup;
@@ -3660,12 +4043,12 @@ static bool delete_entry(long idx_id)
/* Now check which tags are no longer in use (if any) */
for (tag = 0; tag < TAG_COUNT; tag++)
in_use[tag] = 0;
-
+
lseek(masterfd, sizeof(struct master_header), SEEK_SET);
for (i = 0; i < myhdr.tch.entry_count; i++)
{
struct index_entry *idxp;
-
+
#ifdef HAVE_TC_RAMCACHE
/* Use RAM DB if available for greater speed */
if (tc_stat.ramcache)
@@ -3673,37 +4056,37 @@ static bool delete_entry(long idx_id)
else
#endif
{
- if (ecread_index_entry(masterfd, &idx) != sizeof(struct index_entry))
+ if (read_index_entries(masterfd, &idx, 1) != sizeof(struct index_entry))
{
logf("delete_entry(): read error #2");
goto cleanup;
}
idxp = &idx;
}
-
+
if (idxp->flag & FLAG_DELETED)
continue;
-
+
for (tag = 0; tag < TAG_COUNT; tag++)
{
if (TAGCACHE_IS_NUMERIC(tag))
continue;
-
+
if (idxp->tag_seek[tag] == myidx.tag_seek[tag])
in_use[tag]++;
}
}
-
+
/* Now delete all tags no longer in use. */
for (tag = 0; tag < TAG_COUNT; tag++)
{
struct tagcache_header tch;
int oldseek = myidx.tag_seek[tag];
-
+
if (TAGCACHE_IS_NUMERIC(tag))
continue;
-
- /**
+
+ /**
* Replace tag seek with a hash value of the field string data.
* That way runtime statistics of moved or altered files can be
* resurrected.
@@ -3714,38 +4097,46 @@ static bool delete_entry(long idx_id)
struct tagfile_entry *tfe;
int32_t *seek = &tcramcache.hdr->indices[idx_id].tag_seek[tag];
+ /* crc_32 is assumed not to yield (why would it...?) */
tfe = (struct tagfile_entry *)&tcramcache.hdr->tags[tag][*seek];
- tcrc_buffer_lock(); /* protect tfe and seek if crc_32() yield()s */
*seek = crc_32(tfe->tag_data, strlen(tfe->tag_data), 0xffffffff);
- tcrc_buffer_unlock();
myidx.tag_seek[tag] = *seek;
}
else
#endif /* HAVE_TC_RAMCACHE */
{
struct tagfile_entry tfe;
-
+
/* Open the index file, which contains the tag names. */
if ((fd = open_tag_fd(&tch, tag, true)) < 0)
goto cleanup;
-
+
/* Skip the header block */
lseek(fd, myidx.tag_seek[tag], SEEK_SET);
- if (ecread_tagfile_entry(fd, &tfe) != sizeof(struct tagfile_entry))
- {
- logf("delete_entry(): read error #3");
- goto cleanup;
- }
-
- if (read(fd, buf, tfe.tag_length) != tfe.tag_length)
+
+ switch (read_tagfile_entry_and_tag(fd, &tfe,
+ build_idx_buf, build_idx_bufsz))
{
- logf("delete_entry(): read error #4");
- goto cleanup;
+ case e_SUCCESS_LEN_ZERO:
+ logf("deleted_entry(): SUCCESS");
+ /* FALL THROUGH */
+ case e_SUCCESS:
+ break;
+ case e_ENTRY_SIZEMISMATCH:
+ logf("delete_entry(): read error #3");
+ goto cleanup;
+ case e_TAG_TOOLONG:
+ logf("too long tag #4");
+ goto cleanup;
+ case e_TAG_SIZEMISMATCH:
+ logf("delete_entry(): read error #3");
+ goto cleanup;
}
-
- myidx.tag_seek[tag] = crc_32(buf, strlen(buf), 0xffffffff);
+
+ myidx.tag_seek[tag] = crc_32(build_idx_buf,
+ strlen(build_idx_buf), 0xffffffff);
}
-
+
if (in_use[tag])
{
logf("in use: %d/%d", tag, in_use[tag]);
@@ -3756,61 +4147,61 @@ static bool delete_entry(long idx_id)
}
continue;
}
-
+
#ifdef HAVE_TC_RAMCACHE
/* Delete from ram. */
if (tc_stat.ramcache && tag != tag_filename)
{
struct tagfile_entry *tagentry =
(struct tagfile_entry *)&tcramcache.hdr->tags[tag][oldseek];
- tagentry->tag_data[0] = '\0';
+ str_setlen(tagentry->tag_data, 0);
}
#endif /* HAVE_TC_RAMCACHE */
-
+
/* Open the index file, which contains the tag names. */
if (fd < 0)
{
if ((fd = open_tag_fd(&tch, tag, true)) < 0)
goto cleanup;
}
-
+
/* Skip the header block */
lseek(fd, oldseek + sizeof(struct tagfile_entry), SEEK_SET);
-
+
/* Debug, print 10 first characters of the tag
read(fd, buf, 10);
buf[10]='\0';
logf("TAG:%s", buf);
lseek(fd, -10, SEEK_CUR);
*/
-
+
/* Write first data byte in tag as \0 */
write(fd, "", 1);
-
+
/* Now tag data has been removed */
close(fd);
fd = -1;
}
-
+
/* Write index entry back into master index. */
lseek(masterfd, sizeof(struct master_header) +
(idx_id * sizeof(struct index_entry)), SEEK_SET);
- if (ecwrite_index_entry(masterfd, &myidx) != sizeof(struct index_entry))
+ if (write_index_entries(masterfd, &myidx, 1) != sizeof(struct index_entry))
{
logf("delete_entry(): write_error #2");
goto cleanup;
}
-
+
close(masterfd);
-
+
return true;
-
+
cleanup:
if (fd >= 0)
close(fd);
if (masterfd >= 0)
close(masterfd);
-
+
return false;
}
@@ -3822,19 +4213,20 @@ static bool check_event_queue(void)
{
#ifndef __PCTOOL__
struct queue_event ev;
-
+
if(!queue_peek(&tagcache_queue, &ev))
return false;
-
+
switch (ev.id)
{
case Q_STOP_SCAN:
case SYS_POWEROFF:
+ case SYS_REBOOT:
case SYS_USB_CONNECTED:
return true;
}
#endif /* __PCTOOL__ */
-
+
return false;
}
@@ -3849,13 +4241,10 @@ static void fix_ramcache(void* old_addr, void* new_addr)
static int move_cb(int handle, void* current, void* new)
{
- if (tcramcache.move_lock > 0)
- return BUFLIB_CB_CANNOT_MOVE;
-
+ (void)handle;
fix_ramcache(current, new);
tcramcache.hdr = new;
return BUFLIB_CB_OK;
- (void)handle;
}
static struct buflib_callbacks ops = {
@@ -3874,12 +4263,12 @@ static bool allocate_tagcache(void)
int fd = open_master_fd(&tcmh, false);
if (fd < 0)
return false;
-
+
close(fd);
-
- /**
- * Now calculate the required cache size plus
- * some extra space for alignment fixes.
+
+ /**
+ * Now calculate the required cache size plus
+ * some extra space for alignment fixes.
*/
size_t alloc_size = tcmh.tch.datasize + 256 + TAGCACHE_RESERVE +
sizeof(struct ramcache_header) + TAG_COUNT*sizeof(void *);
@@ -3887,7 +4276,7 @@ static bool allocate_tagcache(void)
alloc_size += tcmh.tch.entry_count*sizeof(struct dircache_fileref);
#endif
- int handle = core_alloc_ex("tc ramcache", alloc_size, &ops);
+ int handle = core_alloc_ex(alloc_size, &ops);
if (handle <= 0)
return false;
@@ -3910,14 +4299,14 @@ static bool tagcache_dumpload(void)
tcramcache.handle = 0;
tcramcache.hdr = NULL;
-
- fd = open(TAGCACHE_STATEFILE, O_RDONLY);
+
+ fd = open_db_fd(TAGCACHE_STATEFILE, O_RDONLY);
if (fd < 0)
{
logf("no tagcache statedump");
return false;
}
-
+
/* Check the statefile memory placement */
rc = read(fd, &shdr, sizeof(struct statefile_header));
if (rc != sizeof(struct statefile_header)
@@ -3930,15 +4319,15 @@ static bool tagcache_dumpload(void)
}
/* Lets allocate real memory and load it */
- handle = core_alloc_ex("tc ramcache", shdr.tc_stat.ramcache_allocated, &ops);
+ handle = core_alloc_ex(shdr.tc_stat.ramcache_allocated, &ops);
if (handle <= 0)
{
logf("alloc failure");
return false;
}
- tcrc_buffer_lock();
tcramcache.handle = handle;
+ tcrc_buffer_lock();
tcramcache.hdr = core_get_data(handle);
rc = read(fd, tcramcache.hdr, shdr.tc_stat.ramcache_allocated);
tcrc_buffer_unlock();
@@ -3953,13 +4342,13 @@ static bool tagcache_dumpload(void)
}
tc_stat = shdr.tc_stat;
-
+
/* Now fix the pointers */
fix_ramcache(shdr.hdr, tcramcache.hdr);
-
+
/* Load the tagcache master header (should match the actual DB file header). */
memcpy(&current_tcmh, &shdr.mh, sizeof current_tcmh);
-
+
return true;
}
@@ -3967,30 +4356,30 @@ static bool tagcache_dumpsave(void)
{
struct statefile_header shdr;
int fd;
-
+
if (!tc_stat.ramcache)
return false;
-
- fd = open(TAGCACHE_STATEFILE, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+
+ fd = open_db_fd(TAGCACHE_STATEFILE, O_WRONLY | O_CREAT | O_TRUNC);
if (fd < 0)
{
logf("failed to create a statedump");
return false;
}
-
+
/* Create the header */
shdr.magic = TAGCACHE_STATEFILE_MAGIC;
shdr.hdr = tcramcache.hdr;
memcpy(&shdr.mh, &current_tcmh, sizeof current_tcmh);
memcpy(&shdr.tc_stat, &tc_stat, sizeof tc_stat);
write(fd, &shdr, sizeof shdr);
-
+
/* And dump the data too */
tcrc_buffer_lock();
write(fd, tcramcache.hdr, tc_stat.ramcache_allocated);
tcrc_buffer_unlock();
close(fd);
-
+
return true;
}
#endif /* HAVE_EEPROM_SETTINGS */
@@ -4000,8 +4389,6 @@ static bool load_tagcache(void)
/* DEBUG: After tagcache commit and dircache rebuild, hdr-sturcture
* may become corrupt. */
- bool const auto_update = global_settings.tagcache_autoupdate;
-
bool ok = false;
ssize_t bytesleft = tc_stat.ramcache_allocated - sizeof(struct ramcache_header);
int fd;
@@ -4014,8 +4401,8 @@ static bool load_tagcache(void)
logf("loading tagcache to ram...");
tcrc_buffer_lock(); /* lock for the rest of the scan, simpler to handle */
-
- fd = open(TAGCACHE_FILE_MASTER, O_RDONLY);
+
+ fd = open_db_fd(TAGCACHE_FILE_MASTER, O_RDONLY);
if (fd < 0)
{
logf("tagcache open failed");
@@ -4023,9 +4410,8 @@ static bool load_tagcache(void)
}
struct master_header tcmh;
- if (ecread(fd, &tcmh, 1, master_header_ec, tc_stat.econ)
- != sizeof(struct master_header)
- || tcmh.tch.magic != TAGCACHE_MAGIC)
+ if (read_master_header(fd, &tcmh) != sizeof(struct master_header) ||
+ tcmh.tch.magic != TAGCACHE_MAGIC)
{
logf("incorrect header");
goto failure;
@@ -4044,7 +4430,7 @@ static bool load_tagcache(void)
goto failure;
}
- int rc = ecread_index_entry(fd, &tcramcache.hdr->indices[i]);
+ int rc = read_index_entries(fd, &tcramcache.hdr->indices[i], 1);
if (rc != sizeof (struct index_entry))
{
logf("read error #10");
@@ -4100,12 +4486,12 @@ static bool load_tagcache(void)
logf("Too big tagcache #10.75");
goto failure;
}
-
+
struct tagfile_entry *fe = (struct tagfile_entry *)p;
off_t pos = lseek(fd, 0, SEEK_CUR);
/* Load the header for the tag itself */
- if (ecread_tagfile_entry(fd, fe) != sizeof(struct tagfile_entry))
+ if (read_tagfile_entry(fd, fe) != sizeof(struct tagfile_entry))
{
/* End of lookup table. */
logf("read error #11");
@@ -4149,17 +4535,18 @@ static bool load_tagcache(void)
bytesleft -= sizeof (struct dircache_fileref);
#endif /* HAVE_DIRCACHE */
- char filename[TAG_MAXLEN+32];
+ char filename[TAGCACHE_BUFSZ];
if (fe->tag_length >= (long)sizeof(filename)-1)
{
read(fd, filename, 10);
- filename[10] = '\0';
+ str_setlen(filename, 10);
logf("TAG:%s", filename);
logf("too long filename");
goto failure;
}
- if ((idx->flag & FLAG_DELETED) IFN_DIRCACHE( || !auto_update ))
+ if ((idx->flag & FLAG_DELETED)
+ IFN_DIRCACHE( || !global_settings.tagcache_autoupdate ))
{
/* seek over tag data instead of reading */
if (lseek(fd, fe->tag_length, SEEK_CUR) < 0)
@@ -4176,30 +4563,6 @@ static bool load_tagcache(void)
logf("read error #12");
goto failure;
}
-
- #ifdef HAVE_DIRCACHE
- /* If auto updating, check storage too, otherwise simply
- attempt to load cache references */
- unsigned int searchflag = auto_update ? DCS_STORAGE_PATH :
- DCS_CACHED_PATH;
-
- int rc = dircache_search(searchflag | DCS_UPDATE_FILEREF,
- &tcrc_dcfrefs[idx_id], filename);
- if (rc > 0) /* in cache and we have fileref */
- idx->flag |= FLAG_DIRCACHE;
- else if (rc == 0) /* not in cache but okay */
- ;
- else if (auto_update)
- #else /* ndef HAVE_DIRCACHE */
- /* Check if path is no longer valid */
- if (!file_exists(filename))
- #endif /* HAVE_DIRCACHE */
- {
- logf("Entry no longer valid.");
- logf("-> %s", filename);
- delete_entry(idx_id);
- }
-
continue;
}
@@ -4234,7 +4597,7 @@ static bool load_tagcache(void)
close(fd);
}
-
+
tc_stat.ramcache_used = tc_stat.ramcache_allocated - bytesleft;
logf("tagcache loaded into ram!");
logf("utilization: %d%%", 100*tc_stat.ramcache_used / tc_stat.ramcache_allocated);
@@ -4250,62 +4613,119 @@ failure:
}
#endif /* HAVE_TC_RAMCACHE */
-static bool check_deleted_files(void)
+static bool check_file_refs(bool auto_update)
{
int fd;
- char buf[TAG_MAXLEN+32];
+ bool ret = true;
+ char buf[TAGCACHE_BUFSZ];
+ const int bufsz = sizeof(buf);
struct tagfile_entry tfe;
+ struct tagcache_header hdr;
logf("reverse scan...");
- snprintf(buf, sizeof buf, TAGCACHE_FILE_INDEX, tag_filename);
- fd = open(buf, O_RDONLY);
-
+
+#ifdef HAVE_DIRCACHE
+ if (tcramcache.handle > 0)
+ tcrc_buffer_lock();
+ else
+ return false;
+ /* Wait for any in-progress dircache build to complete */
+ dircache_wait();
+#else
+ if (!auto_update)
+ return false;
+#endif
+
+ fd = open_tag_fd(&hdr, tag_filename, false); /* open read only*/
+
if (fd < 0)
{
- logf("%s open fail", buf);
+ logf(TAGCACHE_FILE_INDEX " open fail", tag_filename);
return false;
}
- lseek(fd, sizeof(struct tagcache_header), SEEK_SET);
- while (ecread_tagfile_entry(fd, &tfe) == sizeof(struct tagfile_entry)
- && !check_event_queue())
+ processed_dir_count = 0;
+
+ while (!check_event_queue())
{
- if (tfe.tag_length >= (long)sizeof(buf)-1)
+ int res = read_tagfile_entry_and_tag(fd, &tfe, buf, bufsz);
+ processed_dir_count++;
+
+ switch (res)
{
- logf("too long tag");
- close(fd);
- return false;
+ case e_ENTRY_SIZEMISMATCH:
+ logf("size mismatch entry EOF?"); /* likely EOF */
+ ret = false;
+ goto wend_finished;
+ case e_TAG_TOOLONG:
+ logf("too long tag");
+ ret = false;
+ goto wend_finished;
+ case e_TAG_SIZEMISMATCH:
+ logf("size mismatch tag - read error #14");
+ ret = false;
+ goto wend_finished;
+ case e_SUCCESS:
+ break;
+ case e_SUCCESS_LEN_ZERO:
+ continue;
+ }
+
+ int idx_id = tfe.idx_id; /* dircache reference clobbers *tfe */
+#ifdef HAVE_DIRCACHE
+ struct index_entry *idx = &tcramcache.hdr->indices[idx_id];
+ unsigned int searchflag;
+ if (!auto_update)
+ {
+ if(idx->flag & FLAG_DIRCACHE) /* already found */
+ {
+ continue;
+ }
+ searchflag = DCS_CACHED_PATH; /* attempt to load cache references */
}
-
- if (read(fd, buf, tfe.tag_length) != tfe.tag_length)
+ else /* If auto updating, check storage too */
{
- logf("read error #14");
- close(fd);
- return false;
+ searchflag = DCS_STORAGE_PATH;
}
-
- /* Check if the file has already deleted from the db. */
- if (*buf == '\0')
- continue;
-
- /* Now check if the file exists. */
+
+ int rc_cache = dircache_search(searchflag | DCS_UPDATE_FILEREF,
+ &tcrc_dcfrefs[idx_id], buf);
+
+ if (rc_cache > 0) /* in cache and we have fileref */
+ {
+ idx->flag |= FLAG_DIRCACHE;
+ }
+ else if (rc_cache == 0) /* not in cache but okay */
+ {;}
+ else if (auto_update && rc_cache == ENOENT)
+#else
if (!file_exists(buf))
+#endif /* HAVE_DIRCACHE */
{
logf("Entry no longer valid.");
logf("-> %s / %ld", buf, tfe.tag_length);
- delete_entry(tfe.idx_id);
+ delete_entry(idx_id);
}
do_timed_yield();
}
-
+
+wend_finished:
+
+#ifdef HAVE_DIRCACHE
+ if (tcramcache.handle > 0)
+ tcrc_buffer_unlock();
+#endif
close(fd);
-
logf("done");
-
- return true;
+
+ return ret;
}
+static bool check_deleted_files(void)
+{
+ return check_file_refs(true);
+}
/* Note that this function must not be inlined, otherwise the whole point
* of having the code in a separate function is lost.
@@ -4314,12 +4734,13 @@ static void NO_INLINE check_ignore(const char *dirname,
int *ignore, int *unignore)
{
char newpath[MAX_PATH];
+ const int bufsz = sizeof(newpath);
/* check for a database.ignore file */
- snprintf(newpath, MAX_PATH, "%s/database.ignore", dirname);
+ snprintf(newpath, bufsz, "%s/database.ignore", dirname);
*ignore = file_exists(newpath);
/* check for a database.unignore file */
- snprintf(newpath, MAX_PATH, "%s/database.unignore", dirname);
+ snprintf(newpath, bufsz, "%s/database.unignore", dirname);
*unignore = file_exists(newpath);
}
@@ -4354,7 +4775,7 @@ static bool search_root_exists(const char *path)
* search root.
*
* Returns true if it added the path to the search roots
- *
+ *
* Windows 2000 and greater supports symlinks, but they don't provide
* realpath() or readlink(), and symlinks are rarely used on them so
* ignore this for windows for now
@@ -4365,6 +4786,7 @@ static bool add_search_root(const char *name)
#ifndef WIN32
struct search_roots_ll *this, *prev = NULL;
char target[MAX_PATH];
+ const int target_bufsz = sizeof(target);
/* Okay, realpath() is almost completely broken on android
*
* It doesn't accept NULL for resolved_name to dynamically allocate
@@ -4378,11 +4800,11 @@ static bool add_search_root(const char *name)
static char abs_target[PATH_MAX];
ssize_t len;
- len = readlink(name, target, sizeof(target)-1);
+ len = readlink(name, target, target_bufsz-1);
if (len < 0)
return false;
- target[len] = '\0';
+ str_setlen(target, len);
if (realpath(target, abs_target) == NULL)
return false;
@@ -4396,7 +4818,7 @@ static bool add_search_root(const char *name)
{
size_t len = strlen(abs_target) + 1; /* count \0 */
this = malloc(sizeof(struct search_roots_ll) + len );
- if (!this || len > MAX_PATH)
+ if (!this || len > MIN(PATH_MAX, MAX_PATH))
{
logf("Error at adding a search root: %s", this ? "path too long":"OOM");
free(this);
@@ -4490,20 +4912,20 @@ static bool check_dir(const char *dirname, int add_files)
else if (add_files)
{
tc_stat.curentry = curpath;
-
+
/* Add a new entry to the temporary db file. */
add_tagcache(curpath, info.mtime);
-
+
/* Wait until current path for debug screen is read and unset. */
while (tc_stat.syncscreen && tc_stat.curentry != NULL)
yield();
-
+
tc_stat.curentry = NULL;
}
- curpath[len] = '\0';
+ str_setlen(curpath, len);
}
-
+
closedir(dir);
return success;
@@ -4528,26 +4950,26 @@ void do_tagcache_build(const char *path[])
struct tagcache_header header;
bool ret;
- curpath[0] = '\0';
+ str_setlen(curpath, 0);
data_size = 0;
total_entry_count = 0;
processed_dir_count = 0;
-
+
#ifdef HAVE_DIRCACHE
dircache_wait();
#endif
-
+
logf("updating tagcache");
-
- cachefd = open(TAGCACHE_FILE_TEMP, O_RDONLY);
+
+ cachefd = open_db_fd(TAGCACHE_FILE_TEMP, O_RDONLY);
if (cachefd >= 0)
{
logf("skipping, cache already waiting for commit");
close(cachefd);
return ;
}
-
- cachefd = open(TAGCACHE_FILE_TEMP, O_RDWR | O_CREAT | O_TRUNC, 0666);
+
+ cachefd = open_db_fd(TAGCACHE_FILE_TEMP, O_RDWR | O_CREAT | O_TRUNC);
if (cachefd < 0)
{
logf("master file open failed: %s", TAGCACHE_FILE_TEMP);
@@ -4555,7 +4977,7 @@ void do_tagcache_build(const char *path[])
}
filenametag_fd = open_tag_fd(&header, tag_filename, false);
-
+
cpu_boost(true);
logf("Scanning files...");
@@ -4567,10 +4989,56 @@ void do_tagcache_build(const char *path[])
roots_ll[0].path = path[0];
roots_ll[0].next = NULL;
+
+#if defined(HAVE_MULTIVOLUME) && !defined(SIMULATOR) && !defined(__PCTOOL__) && !defined(PLATFORM_NATIVE)
+ extern bool ns_volume_is_visible(int volume); /*rb_namespace.c*/
+ /* i is for the path vector, j for the roots_ll array */
+ int i = 1, j = 1;
+ bool added = false;
+ char volnamebuf[NUM_VOLUMES][VOL_MAX_LEN + 1];
+ /* we can just parse the root directory ('/') and get to any mounted
+ * volume but we can also enumerate a volume in the root directory
+ * when this occurs it leads to multiple entries since the files can
+ * be reached through multiple paths ex, /Foo could also be /SD1/Foo
+ * we used to hide the volume that was mapped but then when you switch
+ * from the sd to the internal the paths don't map to the right volume
+ * instead we will attempt to rewrite the root with any non-hidden volumes
+ * failing that just leave the paths alone */
+ if (!strcmp(PATH_ROOTSTR, path[0]))
+ {
+ i = 0;
+ j = 0;
+ }
+ /* path can be skipped , but root_ll entries can't */
+ for(; path[i] && j < MAX_STATIC_ROOTS; i++)
+ {
+ /* check if the link target is inside of an existing search root
+ * don't add if target is inside, we'll scan it later */
+ if (!added && !strcmp(PATH_ROOTSTR, path[i]))
+ {
+ for (int v = 0; v < NUM_VOLUMES; v++)
+ {
+ if (ns_volume_is_visible(v))
+ {
+ make_volume_root(v, volnamebuf[v]);
+ roots_ll[j].path = volnamebuf[v];
+ if (j > 0)
+ roots_ll[j-1].next = &roots_ll[j];
+ j++;
+ added = true;
+ }
+ }
+ if(!added)
+ j = 1;
+ added = true;
+ continue;
+ }
+#else
/* i is for the path vector, j for the roots_ll array
* path can be skipped , but root_ll entries can't */
for(int i = 1, j = 1; path[i] && j < MAX_STATIC_ROOTS; i++)
{
+#endif /*def HAVE_MULTIVOLUME*/
if (search_root_exists(path[i])) /* skip this path */
continue;
@@ -4583,7 +5051,8 @@ void do_tagcache_build(const char *path[])
/* check_dir might add new roots */
for(this = &roots_ll[0]; this; this = this->next)
{
- strcpy(curpath, this->path);
+ logf("Search root %s", this->path);
+ strmemccpy(curpath, this->path, sizeof(curpath));
ret = ret && check_dir(this->path, true);
}
free_search_roots(&roots_ll[0]);
@@ -4620,7 +5089,7 @@ void do_tagcache_build(const char *path[])
#ifdef __PCTOOL__
free_tempbuf();
#endif
-
+
#ifdef HAVE_TC_RAMCACHE
if (tcramcache.hdr)
{
@@ -4629,7 +5098,7 @@ void do_tagcache_build(const char *path[])
queue_post(&tagcache_queue, Q_IMPORT_CHANGELOG, 0);
}
#endif
-
+
cpu_boost(false);
}
@@ -4638,7 +5107,7 @@ void tagcache_build(void)
{
char *vect[MAX_STATIC_ROOTS + 1]; /* +1 to ensure NULL sentinel */
char str[sizeof(global_settings.tagcache_scan_paths)];
- strlcpy(str, global_settings.tagcache_scan_paths, sizeof(str));
+ strmemccpy(str, global_settings.tagcache_scan_paths, sizeof(str));
int res = split_string(str, ':', vect, MAX_STATIC_ROOTS);
vect[res] = NULL;
@@ -4652,9 +5121,9 @@ static void load_ramcache(void)
{
if (!tcramcache.hdr)
return ;
-
+
cpu_boost(true);
-
+
/* At first we should load the cache (if exists). */
tc_stat.ramcache = load_tagcache();
@@ -4668,7 +5137,7 @@ static void load_ramcache(void)
tcramcache.handle = 0;
core_free(handle);
}
-
+
cpu_boost(false);
}
@@ -4676,23 +5145,45 @@ void tagcache_unload_ramcache(void)
{
tc_stat.ramcache = false;
/* Just to make sure there is no statefile present. */
- // remove(TAGCACHE_STATEFILE);
+ // remove_db_file(TAGCACHE_STATEFILE);
}
#endif /* HAVE_TC_RAMCACHE */
#ifndef __PCTOOL__
+
+/*
+ * db_file_exists is noinline to minimize stack usage
+ */
+static bool NO_INLINE db_file_exists(const char* filename)
+{
+ char buf[MAX_PATH];
+
+ snprintf(buf, sizeof(buf), "%s/%s", tc_stat.db_path, filename);
+
+ return file_exists(buf);
+}
+
static void tagcache_thread(void)
{
struct queue_event ev;
bool check_done = false;
-
+ cpu_boost(true);
/* If the previous cache build/update was interrupted, commit
* the changes first in foreground. */
- cpu_boost(true);
- allocate_tempbuf();
- commit();
- free_tempbuf();
-
+ if (db_file_exists(TAGCACHE_FILE_TEMP))
+ {
+ static const char *lines[] = {ID2P(LANG_TAGCACHE_BUSY),
+ ID2P(LANG_TAGCACHE_UPDATE)};
+ static const struct text_message message = {lines, 2};
+
+ if (gui_syncyesno_run_w_tmo(HZ * 5, YESNO_YES, &message, NULL, NULL) == YESNO_YES)
+ {
+ allocate_tempbuf();
+ commit();
+ free_tempbuf();
+ }
+ }
+
#ifdef HAVE_TC_RAMCACHE
#ifdef HAVE_EEPROM_SETTINGS
if (firmware_settings.initialized && firmware_settings.disk_clean
@@ -4701,29 +5192,28 @@ static void tagcache_thread(void)
check_done = tagcache_dumpload();
}
- remove(TAGCACHE_STATEFILE);
+ remove_db_file(TAGCACHE_STATEFILE);
#endif /* HAVE_EEPROM_SETTINGS */
-
+
/* Allocate space for the tagcache if found on disk. */
if (global_settings.tagcache_ram && !tc_stat.ramcache)
allocate_tagcache();
#endif /* HAVE_TC_RAMCACHE */
-
+
cpu_boost(false);
tc_stat.initialized = true;
-
+
/* Don't delay bootup with the header check but do it on background. */
if (!tc_stat.ready)
{
sleep(HZ);
- tc_stat.ready = check_all_headers();
- tc_stat.readyvalid = true;
+ tagcache_commit_finalize();
}
-
+
while (1)
{
run_command_queue(false);
-
+
queue_wait_w_tmo(&tagcache_queue, &ev, HZ);
switch (ev.id)
@@ -4731,13 +5221,13 @@ static void tagcache_thread(void)
case Q_IMPORT_CHANGELOG:
tagcache_import_changelog();
break;
-
+
case Q_REBUILD:
remove_files();
- remove(TAGCACHE_FILE_TEMP);
+ remove_db_file(TAGCACHE_FILE_TEMP);
tagcache_build();
break;
-
+
case Q_UPDATE:
tagcache_build();
#ifdef HAVE_TC_RAMCACHE
@@ -4745,17 +5235,19 @@ static void tagcache_thread(void)
#endif
check_deleted_files();
break ;
-
+
case Q_START_SCAN:
check_done = false;
+ /* fallthrough */
case SYS_TIMEOUT:
if (check_done || !tc_stat.ready)
break ;
-
+
#ifdef HAVE_TC_RAMCACHE
if (!tc_stat.ramcache && global_settings.tagcache_ram)
{
load_ramcache();
+ check_file_refs(global_settings.tagcache_autoupdate);
if (tc_stat.ramcache && global_settings.tagcache_autoupdate)
tagcache_build();
}
@@ -4764,24 +5256,25 @@ static void tagcache_thread(void)
if (global_settings.tagcache_autoupdate)
{
tagcache_build();
-
+
/* This will be very slow unless dircache is enabled
or target is flash based, but do it anyway for
consistency. */
check_deleted_files();
}
-
+
logf("tagcache check done");
-
+
check_done = true;
break ;
case Q_STOP_SCAN:
break ;
-
+
case SYS_POWEROFF:
+ case SYS_REBOOT:
break ;
-
+
case SYS_USB_CONNECTED:
logf("USB: TagCache");
usb_acknowledge(SYS_USB_CONNECTED_ACK);
@@ -4795,11 +5288,11 @@ bool tagcache_prepare_shutdown(void)
{
if (tagcache_get_commit_step() > 0)
return false;
-
+
tagcache_stop_scan();
while (read_lock || write_lock)
sleep(1);
-
+
return true;
}
@@ -4807,13 +5300,18 @@ void tagcache_shutdown(void)
{
/* Flush the command queue. */
run_command_queue(true);
-
+
#if defined(HAVE_EEPROM_SETTINGS) && defined(HAVE_TC_RAMCACHE)
if (tc_stat.ramcache)
tagcache_dumpsave();
#endif
}
+void tagcache_remove_statefile(void)
+{
+ remove_db_file(TAGCACHE_STATEFILE);
+}
+
static int get_progress(void)
{
int total_count = -1;
@@ -4821,7 +5319,8 @@ static int get_progress(void)
#ifdef HAVE_DIRCACHE
struct dircache_info dcinfo;
dircache_get_info(&dcinfo);
- if (dcinfo.status != DIRCACHE_IDLE)
+ if (dcinfo.status != DIRCACHE_IDLE &&
+ ((!tc_stat.ramcache) || current_tcmh.tch.entry_count == 0))
{
total_count = dcinfo.entry_count;
}
@@ -4846,9 +5345,10 @@ static int get_progress(void)
struct tagcache_stat* tagcache_get_stat(void)
{
+ tc_stat.total_entries = current_tcmh.tch.entry_count;
tc_stat.progress = get_progress();
tc_stat.processed_entries = processed_dir_count;
-
+
return &tc_stat;
}
@@ -4861,12 +5361,12 @@ bool tagcache_update(void)
{
if (!tc_stat.ready)
return false;
-
+
queue_post(&tagcache_queue, Q_UPDATE, 0);
return false;
}
-bool tagcache_rebuild()
+bool tagcache_rebuild(void)
{
queue_post(&tagcache_queue, Q_REBUILD, 0);
return false;
@@ -4886,15 +5386,19 @@ void tagcache_init(void)
memset(&current_tcmh, 0, sizeof(struct master_header));
filenametag_fd = -1;
write_lock = read_lock = 0;
-
+
#ifndef __PCTOOL__
+ strmemccpy(tc_stat.db_path, global_settings.tagcache_db_path,
+ sizeof(tc_stat.db_path));
mutex_init(&command_queue_mutex);
queue_init(&tagcache_queue, true);
create_thread(tagcache_thread, tagcache_stack,
- sizeof(tagcache_stack), 0, tagcache_thread_name
+ sizeof(tagcache_stack), 0, tagcache_thread_name
IF_PRIO(, PRIORITY_BACKGROUND)
IF_COP(, CPU));
#else
+ /* use default DB path */
+ strcpy(tc_stat.db_path, ROCKBOX_DIR);
tc_stat.initialized = true;
allocate_tempbuf();
commit();
@@ -4923,6 +5427,12 @@ bool tagcache_is_usable(void)
{
return tc_stat.initialized && tc_stat.ready;
}
+#ifdef HAVE_TC_RAMCACHE
+bool tagcache_is_in_ram(void)
+{
+ return tc_stat.ramcache;
+}
+#endif
int tagcache_get_commit_step(void)
{
return tc_stat.commit_step;
@@ -4931,3 +5441,4 @@ int tagcache_get_max_commit_step(void)
{
return (int)(SORTED_TAGS_COUNT)+1;
}
+#endif /*!defined(PLUGIN)*/
diff --git a/apps/tagcache.h b/apps/tagcache.h
index e358cc26e0..a20a13900a 100644
--- a/apps/tagcache.h
+++ b/apps/tagcache.h
@@ -25,83 +25,38 @@
#include "config.h"
#include "system.h"
#include "metadata.h"
+#include "settings.h"
/**
Note: When adding new tags, make sure to update index_entry_ec and tags_str in
tagcache.c and bump up the header version too.
*/
enum tag_type { tag_artist = 0, tag_album, tag_genre, tag_title,
- tag_filename, tag_composer, tag_comment, tag_albumartist, tag_grouping, tag_year,
- tag_discnumber, tag_tracknumber, tag_bitrate, tag_length, tag_playcount, tag_rating,
- tag_playtime, tag_lastplayed, tag_commitid, tag_mtime, tag_lastelapsed,
- tag_lastoffset,
+ tag_filename, tag_composer, tag_comment, tag_albumartist, tag_grouping, tag_year,
+ tag_discnumber, tag_tracknumber, tag_virt_canonicalartist, tag_bitrate, tag_length,
+ tag_playcount, tag_rating, tag_playtime, tag_lastplayed, tag_commitid, tag_mtime,
+ tag_lastelapsed, tag_lastoffset,
/* Real tags end here, count them. */
TAG_COUNT,
/* Virtual tags */
- tag_virt_basename, tag_virt_length_min, tag_virt_length_sec,
+ tag_virt_basename=TAG_COUNT,
+ tag_virt_length_min, tag_virt_length_sec,
tag_virt_playtime_min, tag_virt_playtime_sec,
- tag_virt_entryage, tag_virt_autoscore };
-
-/* Maximum length of a single tag. */
-#define TAG_MAXLEN (MAX_PATH*2)
-
-/* Allow a little drift to the filename ordering (should not be too high/low). */
-#define POS_HISTORY_COUNT 4
-
-/* How much to pre-load entries while committing to prevent seeking. */
-#define IDX_BUF_DEPTH 64
-
-/* Tag Cache Header version 'TCHxx'. Increment when changing internal structures. */
-#define TAGCACHE_MAGIC 0x5443480f
-
-/* Dump store/restore header version 'TCSxx'. */
-#define TAGCACHE_STATEFILE_MAGIC 0x54435301
-
-/* How much to allocate extra space for ramcache. */
-#define TAGCACHE_RESERVE 32768
-
-/**
- * Define how long one entry must be at least (longer -> less memory at commit).
- * Must be at least 4 bytes in length for correct alignment.
- */
-#define TAGFILE_ENTRY_CHUNK_LENGTH 8
-
-/* Used to guess the necessary buffer size at commit. */
-#define TAGFILE_ENTRY_AVG_LENGTH 16
+ tag_virt_entryage, tag_virt_autoscore,
+ TAG_COUNT_ALL};
/* How many entries to fetch to the seek table at once while searching. */
#define SEEK_LIST_SIZE 32
-/* Always strict align entries for best performance and binary compatibility. */
-#define TAGCACHE_STRICT_ALIGN 1
-
-/* Max events in the internal tagcache command queue. */
-#define TAGCACHE_COMMAND_QUEUE_LENGTH 32
-/* Idle time before committing events in the command queue. */
-#define TAGCACHE_COMMAND_QUEUE_COMMIT_DELAY HZ*2
-
#define TAGCACHE_MAX_FILTERS 4
#define TAGCACHE_MAX_CLAUSES 32
-/* Tag database files. */
-
-/* Temporary database containing new tags to be committed to the main db. */
-#define TAGCACHE_FILE_TEMP ROCKBOX_DIR "/database_tmp.tcd"
-
-/* The main database master index and numeric data. */
-#define TAGCACHE_FILE_MASTER ROCKBOX_DIR "/database_idx.tcd"
-
-/* The main database string data. */
-#define TAGCACHE_FILE_INDEX ROCKBOX_DIR "/database_%d.tcd"
-
-/* ASCII dumpfile of the DB contents. */
-#define TAGCACHE_FILE_CHANGELOG ROCKBOX_DIR "/database_changelog.txt"
-
-/* Serialized DB. */
-#define TAGCACHE_STATEFILE ROCKBOX_DIR "/database_state.tcd"
-
/* Tag to be used on untagged files. */
#define UNTAGGED "<Untagged>"
+/* Maximum length of a single tag. */
+#define TAG_MAXLEN (MAX_PATH*2)
+/* buffer size for all the (stack allocated & static) buffers handling tc data */
+#define TAGCACHE_BUFSZ (TAG_MAXLEN+32)
/* Numeric tags (we can use these tags with conditional clauses). */
#define TAGCACHE_NUMERIC_TAGS ((1LU << tag_year) | (1LU << tag_discnumber) | \
@@ -109,42 +64,41 @@ enum tag_type { tag_artist = 0, tag_album, tag_genre, tag_title,
(1LU << tag_playcount) | (1LU << tag_rating) | (1LU << tag_playtime) | \
(1LU << tag_lastplayed) | (1LU << tag_commitid) | (1LU << tag_mtime) | \
(1LU << tag_lastelapsed) | (1LU << tag_lastoffset) | \
- (1LU << tag_virt_basename) | (1LU << tag_virt_length_min) | \
- (1LU << tag_virt_length_sec) | (1LU << tag_virt_playtime_min) | \
- (1LU << tag_virt_playtime_sec) | (1LU << tag_virt_entryage) | \
- (1LU << tag_virt_autoscore))
+ (1LU << tag_virt_length_min) | (1LU << tag_virt_length_sec) | \
+ (1LU << tag_virt_playtime_min) | (1LU << tag_virt_playtime_sec) | \
+ (1LU << tag_virt_entryage) | (1LU << tag_virt_autoscore))
#define TAGCACHE_IS_NUMERIC(tag) (BIT_N(tag) & TAGCACHE_NUMERIC_TAGS)
-/* Flags */
-#define FLAG_DELETED 0x0001 /* Entry has been removed from db */
-#define FLAG_DIRCACHE 0x0002 /* Filename is a dircache pointer */
-#define FLAG_DIRTYNUM 0x0004 /* Numeric data has been modified */
-#define FLAG_TRKNUMGEN 0x0008 /* Track number has been generated */
-#define FLAG_RESURRECTED 0x0010 /* Statistics data has been resurrected */
-
enum clause { clause_none, clause_is, clause_is_not, clause_gt, clause_gteq,
clause_lt, clause_lteq, clause_contains, clause_not_contains,
clause_begins_with, clause_not_begins_with, clause_ends_with,
- clause_not_ends_with, clause_oneof, clause_logical_or };
+ clause_not_ends_with, clause_oneof,
+ clause_begins_oneof, clause_ends_oneof,
+ clause_logical_or };
struct tagcache_stat {
+ char db_path[MAX_PATHNAME+1]; /* Path to DB root directory */
+
bool initialized; /* Is tagcache currently busy? */
bool readyvalid; /* Has tagcache ready status been ascertained */
bool ready; /* Is tagcache ready to be used? */
bool ramcache; /* Is tagcache loaded in ram? */
bool commit_delayed; /* Has commit been delayed until next reboot? */
bool econ; /* Is endianess correction enabled? */
+ volatile bool syncscreen;/* Synchronous operation with debug screen? */
+ volatile const char
+ *curentry; /* Path of the current entry being scanned. */
+
int commit_step; /* Commit progress */
int ramcache_allocated; /* Has ram been allocated for ramcache? */
int ramcache_used; /* How much ram has been really used */
int progress; /* Current progress of disk scan */
int processed_entries; /* Scanned disk entries so far */
+ int total_entries; /* Total entries in tagcache */
int queue_length; /* Command queue length */
- volatile const char
- *curentry; /* Path of the current entry being scanned. */
- volatile bool syncscreen;/* Synchronous operation with debug screen? */
- // const char *uimessage; /* Pending error message. Implement soon. */
+
+ //const char *uimessage; /* Pending error message. Implement soon. */
};
enum source_type {source_constant,
@@ -187,7 +141,7 @@ struct tagcache_search {
int entry_count;
bool valid;
bool initialized;
- unsigned long *unique_list;
+ uint32_t *unique_list;
int unique_list_capacity;
int unique_list_count;
@@ -222,7 +176,7 @@ bool tagcache_search_add_filter(struct tagcache_search *tcs,
int tag, int seek);
bool tagcache_search_add_clause(struct tagcache_search *tcs,
struct tagcache_search_clause *clause);
-bool tagcache_get_next(struct tagcache_search *tcs);
+bool tagcache_get_next(struct tagcache_search *tcs, char *buf, long size);
bool tagcache_retrieve(struct tagcache_search *tcs, int idxid,
int tag, char *buf, long size);
void tagcache_search_finish(struct tagcache_search *tcs);
@@ -238,16 +192,19 @@ struct tagcache_stat* tagcache_get_stat(void);
int tagcache_get_commit_step(void);
bool tagcache_prepare_shutdown(void);
void tagcache_shutdown(void);
+void tagcache_remove_statefile(void);
void tagcache_screensync_event(void);
void tagcache_screensync_enable(bool state);
#ifdef HAVE_TC_RAMCACHE
+bool tagcache_is_in_ram(void);
#ifdef HAVE_DIRCACHE
bool tagcache_fill_tags(struct mp3entry *id3, const char *filename);
#endif
void tagcache_unload_ramcache(void);
#endif
+void tagcache_commit_finalize(void);
void tagcache_init(void) INIT_ATTR;
bool tagcache_is_initialized(void);
bool tagcache_is_fully_initialized(void);
diff --git a/apps/tagnavi.config b/apps/tagnavi.config
index 80f891dd94..6eda05ec44 100644
--- a/apps/tagnavi.config
+++ b/apps/tagnavi.config
@@ -2,22 +2,25 @@
# ^ Version header must be the first line of every file
# Tag Browser configuration file, do not edit as changes will be lost!
-# Instead, you can modify "/.rockbox/tagnavi_custom.config" which will never
-# get overwritten automatically.
+# Instead, copy this file to "/.rockbox/tagnavi_user.config" and edit
+# that, so your changes will not be overwritten automatically.
+#
+# If you only want to add menus and don't need to modify the default
+# ones, you can edit "/.rockbox/tagnavi_custom.config" instead, which
+# is included by this file and will not be overwritten automatically.
# Basic format declarations
%format "fmt_title" "%s - %02d:%02d (%s)" basename Lm Ls filename ? title == "<Untagged>"
%format "fmt_title" "%d.%02d. %s - %02d:%02d" discnum tracknum title Lm Ls ? discnum > "0"
%format "fmt_title" "%02d. %s - %02d:%02d" tracknum title Lm Ls ? tracknum > "0"
%format "fmt_title" "%s - %02d:%02d" title Lm Ls
-%format "fmt_alphanum_title" "%s - %02d:%02d (%s)" basename Lm Ls filename ? title == "<Untagged>"
%format "fmt_alphanum_title" "%s - %02d:%02d" title Lm Ls
-%format "fmt_mostplayed" "%2d|%3d %s (%s)" playcount autoscore title artist %sort = "inverse" %limit = "100"
-%format "fmt_lastplayed" "%06d%s - %s" lastplayed artist title %sort = "inverse" %limit = "99" %strip = "6"
-%format "fmt_forgotten" "%06d%s - %s" lastplayed artist title %limit = "99" %strip = "6"
+%format "fmt_mostplayed" "%2d|%3d %s (%s)" playcount autoscore title canonicalartist %sort = "inverse" %limit = "100"
+%format "fmt_lastplayed" "%06d%s - %s" lastplayed canonicalartist title %sort = "inverse" %limit = "99" %strip = "6"
+%format "fmt_forgotten" "%06d%s - %s" lastplayed canonicalartist title %limit = "99" %strip = "6"
%format "fmt_best_tracks" "%02d. %s (%3d)" tracknum title autoscore
-%format "fmt_score" "(%3d) %s-%s" autoscore title artist
-%format "fmt_rating" "(%2d) %s-%s" rating title artist %sort = "inverse"
+%format "fmt_score" "(%3d) %s-%s" autoscore title canonicalartist
+%format "fmt_rating" "(%2d) %s-%s" rating title canonicalartist %sort = "inverse"
# Include our custom menu
%include "/.rockbox/tagnavi_custom.config"
@@ -28,33 +31,33 @@
# Define the A to Z Artist sub menu
%menu_start "custom_artist" "Artist A to Z"
-"Numeric" -> artist ? artist < "A" -> album -> title = "fmt_title"
-"A" -> artist ? artist ^ "A" -> album -> title = "fmt_title"
-"B" -> artist ? artist ^ "B" -> album -> title = "fmt_title"
-"C" -> artist ? artist ^ "C" -> album -> title = "fmt_title"
-"D" -> artist ? artist ^ "D" -> album -> title = "fmt_title"
-"E" -> artist ? artist ^ "E" -> album -> title = "fmt_title"
-"F" -> artist ? artist ^ "F" -> album -> title = "fmt_title"
-"G" -> artist ? artist ^ "G" -> album -> title = "fmt_title"
-"H" -> artist ? artist ^ "H" -> album -> title = "fmt_title"
-"I" -> artist ? artist ^ "I" -> album -> title = "fmt_title"
-"J" -> artist ? artist ^ "J" -> album -> title = "fmt_title"
-"K" -> artist ? artist ^ "K" -> album -> title = "fmt_title"
-"L" -> artist ? artist ^ "L" -> album -> title = "fmt_title"
-"M" -> artist ? artist ^ "M" -> album -> title = "fmt_title"
-"N" -> artist ? artist ^ "N" -> album -> title = "fmt_title"
-"O" -> artist ? artist ^ "O" -> album -> title = "fmt_title"
-"P" -> artist ? artist ^ "P" -> album -> title = "fmt_title"
-"Q" -> artist ? artist ^ "Q" -> album -> title = "fmt_title"
-"R" -> artist ? artist ^ "R" -> album -> title = "fmt_title"
-"S" -> artist ? artist ^ "S" -> album -> title = "fmt_title"
-"T" -> artist ? artist ^ "T" -> album -> title = "fmt_title"
-"U" -> artist ? artist ^ "U" -> album -> title = "fmt_title"
-"V" -> artist ? artist ^ "V" -> album -> title = "fmt_title"
-"W" -> artist ? artist ^ "W" -> album -> title = "fmt_title"
-"X" -> artist ? artist ^ "X" -> album -> title = "fmt_title"
-"Y" -> artist ? artist ^ "Y" -> album -> title = "fmt_title"
-"Z" -> artist ? artist ^ "Z" -> album -> title = "fmt_title"
+"Numeric" -> canonicalartist ? canonicalartist < "A" -> album -> title = "fmt_title"
+"A" -> canonicalartist ? canonicalartist ^ "A" -> album -> title = "fmt_title"
+"B" -> canonicalartist ? canonicalartist ^ "B" -> album -> title = "fmt_title"
+"C" -> canonicalartist ? canonicalartist ^ "C" -> album -> title = "fmt_title"
+"D" -> canonicalartist ? canonicalartist ^ "D" -> album -> title = "fmt_title"
+"E" -> canonicalartist ? canonicalartist ^ "E" -> album -> title = "fmt_title"
+"F" -> canonicalartist ? canonicalartist ^ "F" -> album -> title = "fmt_title"
+"G" -> canonicalartist ? canonicalartist ^ "G" -> album -> title = "fmt_title"
+"H" -> canonicalartist ? canonicalartist ^ "H" -> album -> title = "fmt_title"
+"I" -> canonicalartist ? canonicalartist ^ "I" -> album -> title = "fmt_title"
+"J" -> canonicalartist ? canonicalartist ^ "J" -> album -> title = "fmt_title"
+"K" -> canonicalartist ? canonicalartist ^ "K" -> album -> title = "fmt_title"
+"L" -> canonicalartist ? canonicalartist ^ "L" -> album -> title = "fmt_title"
+"M" -> canonicalartist ? canonicalartist ^ "M" -> album -> title = "fmt_title"
+"N" -> canonicalartist ? canonicalartist ^ "N" -> album -> title = "fmt_title"
+"O" -> canonicalartist ? canonicalartist ^ "O" -> album -> title = "fmt_title"
+"P" -> canonicalartist ? canonicalartist ^ "P" -> album -> title = "fmt_title"
+"Q" -> canonicalartist ? canonicalartist ^ "Q" -> album -> title = "fmt_title"
+"R" -> canonicalartist ? canonicalartist ^ "R" -> album -> title = "fmt_title"
+"S" -> canonicalartist ? canonicalartist ^ "S" -> album -> title = "fmt_title"
+"T" -> canonicalartist ? canonicalartist ^ "T" -> album -> title = "fmt_title"
+"U" -> canonicalartist ? canonicalartist ^ "U" -> album -> title = "fmt_title"
+"V" -> canonicalartist ? canonicalartist ^ "V" -> album -> title = "fmt_title"
+"W" -> canonicalartist ? canonicalartist ^ "W" -> album -> title = "fmt_title"
+"X" -> canonicalartist ? canonicalartist ^ "X" -> album -> title = "fmt_title"
+"Y" -> canonicalartist ? canonicalartist ^ "Y" -> album -> title = "fmt_title"
+"Z" -> canonicalartist ? canonicalartist ^ "Z" -> album -> title = "fmt_title"
# ^ An empy line ends the menu
@@ -130,12 +133,12 @@
# Define the search sub menu
%menu_start "search" "Search by..."
-"Artist" -> artist ? artist ~ "" -> album -> title = "fmt_title"
+"Artist" -> canonicalartist ? canonicalartist ~ "" -> album -> title = "fmt_title"
"Album Artist" -> albumartist ? albumartist ~ "" -> album -> title = "fmt_title"
"Album" -> album ? album ~ "" -> title = "fmt_title"
"Title" -> title = "fmt_title" ? title ~ ""
"Album by year" -> album ? year = "" -> title = "fmt_title"
-"Artist between years" -> artist ? year >= "" & year <= "" -> album -> title = "fmt_title"
+"Artist between years" -> canonicalartist ? year >= "" & year <= "" -> album -> title = "fmt_title"
"Album between years" -> album ? year >= "" & year <= "" -> title = "fmt_title"
"Filename" -> filename ? filename ~ ""
"Score" -> title = "fmt_score" ? autoscore > ""
@@ -146,7 +149,7 @@
%menu_start "same" "Same as current"
"Directory" -> title ? filename ^ "#directory#"
"Title" -> title = "fmt_title" ? title = "#title#"
-"Artist" -> album ? artist = "#artist#" -> title = "fmt_title"
+"Artist" -> album ? artist = "#artist#" | artist = "#albumartist#" | albumartist = "#artist#" | albumartist = "#albumartist#" -> title = "fmt_title"
"Album" -> title = "fmt_title" ? album = "#album#"
"Composer" -> title = "fmt_title" ? composer = "#composer#"
@@ -154,26 +157,32 @@
%menu_start "runtime" "Play history"
"Most played (Plays|Score)" -> title = "fmt_mostplayed" ? playcount > "0"
"Recently played tracks" -> title = "fmt_lastplayed" ? playcount > "0"
-"Never played tracks" -> artist ? playcount == "0" -> album -> title = "fmt_title"
-"Favourite artists" -> artist ? playcount > "3" & autoscore > "85" -> album -> title = "fmt_best_tracks"
+"Never played tracks" -> canonicalartist ? playcount == "0" -> album -> title = "fmt_title"
+"Favourite artists" -> canonicalartist ? playcount > "3" & autoscore > "85" -> album -> title = "fmt_best_tracks"
"Favourite albums" -> album ? playcount > "3" & autoscore > "85" -> title = "fmt_best_tracks"
"Recent favourites" -> title = "fmt_lastplayed" ? playcount > "3" & autoscore > "85"
-"New favourites" -> artist ? playcount <= "3" & autoscore > "85" -> album -> title = "fmt_best_tracks"
+"New favourites" -> canonicalartist ? playcount <= "3" & autoscore > "85" -> album -> title = "fmt_best_tracks"
"Forgotten favourites" -> title = "fmt_forgotten" ? playcount > "3" & autoscore > "85"
+# Define track submenu
+%menu_start "track" "Tracks by"
+"Filename" -> basename
+"Title" -> title
+"Title mm:ss" -> title = "fmt_alphanum_title"
+
#
# === Begin of main menu
#
# Define the title of the main menu
%menu_start "main" "Database"
-"Artist" -> artist -> album -> title = "fmt_title"
-"Album Artist" -> albumartist -> album -> title = "fmt_title"
+"Artist" -> canonicalartist -> album -> title = "fmt_title"
+"Album Artist" -> albumartist -> album -> title = "fmt_title"
"Album" -> album -> title = "fmt_title"
-"Genre" -> genre -> artist -> album -> title = "fmt_title"
+"Genre" -> genre -> canonicalartist -> album -> title = "fmt_title"
"Composer" -> composer -> album -> title = "fmt_title"
-"Track" -> title = "fmt_alphanum_title"
-"Year" -> year ? year > "0" -> artist -> album -> title = "fmt_title"
+"Track" ==> "track"
+"Year" -> year ? year > "0" -> canonicalartist -> album -> title = "fmt_title"
"User Rating" -> rating -> title = "fmt_title"
"Recently Added" -> album ? entryage < "4" & commitid > "0" -> title = "fmt_title"
"A to Z..." ==> "a2z"
diff --git a/apps/tagtree.c b/apps/tagtree.c
index 4b118f6d0d..736493bfd9 100644
--- a/apps/tagtree.c
+++ b/apps/tagtree.c
@@ -58,7 +58,8 @@
#define str_or_empty(x) (x ? x : "(NULL)")
-#define FILE_SEARCH_INSTRUCTIONS ROCKBOX_DIR "/tagnavi.config"
+#define TAGNAVI_DEFAULT_CONFIG ROCKBOX_DIR "/tagnavi.config"
+#define TAGNAVI_USER_CONFIG ROCKBOX_DIR "/tagnavi_user.config"
static int tagtree_play_folder(struct tree_context* c);
@@ -110,13 +111,29 @@ enum variables {
/* Capacity 10 000 entries (for example 10k different artists) */
#define UNIQBUF_SIZE (64*1024)
-static long uniqbuf[UNIQBUF_SIZE / sizeof(long)];
+static uint32_t uniqbuf[UNIQBUF_SIZE / sizeof(uint32_t)];
#define MAX_TAGS 5
#define MAX_MENU_ID_SIZE 32
#define RELOAD_TAGTREE (-1024)
-static bool sort_inverse;
+
+static int(*qsort_fn)(const char*, const char*, size_t);
+/* dummmy functions to allow compatibility strncasecmp */
+static int strnatcasecmp_n(const char *a, const char *b, size_t n)
+{
+ (void)n;
+ return strnatcasecmp(a, b);
+}
+static int strnatcasecmp_n_inv(const char *a, const char *b, size_t n)
+{
+ (void)n;
+ return strnatcasecmp(b, a);
+}
+static int strncasecmp_inv(const char *a, const char *b, size_t n)
+{
+ return strncasecmp(b, a, n);
+}
/*
* "%3d. %s" autoscore title %sort = "inverse" %limit = "100"
@@ -172,7 +189,8 @@ struct menu_root {
struct match
{
const char* str;
- int symbol;
+ uint16_t len;
+ uint16_t symbol;
};
/* Statusbar text of the current view. */
@@ -190,8 +208,13 @@ static int current_entry_count;
static struct tree_context *tc;
+static int max_history_level; /* depth of menu levels with applicable history */
+static int selected_item_history[MAX_DIR_LEVELS];
+static int table_history[MAX_DIR_LEVELS];
+static int extra_history[MAX_DIR_LEVELS];
+
/* a few memory alloc helper */
-static int tagtree_handle, lock_count;
+static int tagtree_handle;
static size_t tagtree_bufsize, tagtree_buf_used;
#define UPDATE(x, y) { x = (typeof(x))((char*)(x) + (y)); }
@@ -200,9 +223,6 @@ static int move_callback(int handle, void* current, void* new)
(void)handle; (void)current; (void)new;
ptrdiff_t diff = new - current;
- if (lock_count > 0)
- return BUFLIB_CB_CANNOT_MOVE;
-
if (menu)
UPDATE(menu, diff);
@@ -212,21 +232,22 @@ static int move_callback(int handle, void* current, void* new)
/* loop over menus */
for(int i = 0; i < menu_count; i++)
{
- struct menu_root* menu = menus[i];
+ struct menu_root* menuroot = menus[i];
/* then over the menu_entries of a menu */
- for(int j = 0; j < menu->itemcount; j++)
+ for(int j = 0; j < menuroot->itemcount; j++)
{
- struct menu_entry* mentry = menu->items[j];
+ struct menu_entry* mentry = menuroot->items[j];
/* then over the search_instructions of each menu_entry */
for(int k = 0; k < mentry->si.tagorder_count; k++)
{
for(int l = 0; l < mentry->si.clause_count[k]; l++)
{
- UPDATE(mentry->si.clause[k][l]->str, diff);
+ if(mentry->si.clause[k][l]->str)
+ UPDATE(mentry->si.clause[k][l]->str, diff);
UPDATE(mentry->si.clause[k][l], diff);
}
}
- UPDATE(menu->items[j], diff);
+ UPDATE(menuroot->items[j], diff);
}
UPDATE(menus[i], diff);
}
@@ -249,16 +270,6 @@ static int move_callback(int handle, void* current, void* new)
}
#undef UPDATE
-static inline void tagtree_lock(void)
-{
- lock_count++;
-}
-
-static inline void tagtree_unlock(void)
-{
- lock_count--;
-}
-
static struct buflib_callbacks ops = {
.move_callback = move_callback,
.shrink_callback = NULL,
@@ -318,105 +329,126 @@ static int get_token_str(char *buf, int size)
static int get_tag(int *tag)
{
+ #define TAG_MATCH(str, tag) {str, sizeof(str) - 1, tag}
static const struct match get_tag_match[] =
{
- {"album", tag_album},
- {"artist", tag_artist},
- {"bitrate", tag_bitrate},
- {"composer", tag_composer},
- {"comment", tag_comment},
- {"albumartist", tag_albumartist},
- {"ensemble", tag_albumartist},
- {"grouping", tag_grouping},
- {"genre", tag_genre},
- {"length", tag_length},
- {"Lm", tag_virt_length_min},
- {"Ls", tag_virt_length_sec},
- {"Pm", tag_virt_playtime_min},
- {"Ps", tag_virt_playtime_sec},
- {"title", tag_title},
- {"filename", tag_filename},
- {"basename", tag_virt_basename},
- {"tracknum", tag_tracknumber},
- {"discnum", tag_discnumber},
- {"year", tag_year},
- {"playcount", tag_playcount},
- {"rating", tag_rating},
- {"lastplayed", tag_lastplayed},
- {"lastelapsed", tag_lastelapsed},
- {"lastoffset", tag_lastoffset},
- {"commitid", tag_commitid},
- {"entryage", tag_virt_entryage},
- {"autoscore", tag_virt_autoscore},
- {"%sort", var_sorttype},
- {"%limit", var_limit},
- {"%strip", var_strip},
- {"%menu_start", var_menu_start},
- {"%include", var_include},
- {"%root_menu", var_rootmenu},
- {"%format", var_format},
- {"->", menu_next},
- {"==>", menu_load},
- {"%reload", menu_reload}
+ TAG_MATCH("Lm", tag_virt_length_min),
+ TAG_MATCH("Ls", tag_virt_length_sec),
+ TAG_MATCH("Pm", tag_virt_playtime_min),
+ TAG_MATCH("Ps", tag_virt_playtime_sec),
+ TAG_MATCH("->", menu_next),
+
+ TAG_MATCH("==>", menu_load),
+
+ TAG_MATCH("year", tag_year),
+
+ TAG_MATCH("album", tag_album),
+ TAG_MATCH("genre", tag_genre),
+ TAG_MATCH("title", tag_title),
+ TAG_MATCH("%sort", var_sorttype),
+
+ TAG_MATCH("artist", tag_artist),
+ TAG_MATCH("length", tag_length),
+ TAG_MATCH("rating", tag_rating),
+ TAG_MATCH("%limit", var_limit),
+ TAG_MATCH("%strip", var_strip),
+
+ TAG_MATCH("bitrate", tag_bitrate),
+ TAG_MATCH("comment", tag_comment),
+ TAG_MATCH("discnum", tag_discnumber),
+ TAG_MATCH("%format", var_format),
+ TAG_MATCH("%reload", menu_reload),
+
+ TAG_MATCH("filename", tag_filename),
+ TAG_MATCH("basename", tag_virt_basename),
+ TAG_MATCH("tracknum", tag_tracknumber),
+ TAG_MATCH("composer", tag_composer),
+ TAG_MATCH("ensemble", tag_albumartist),
+ TAG_MATCH("grouping", tag_grouping),
+ TAG_MATCH("entryage", tag_virt_entryage),
+ TAG_MATCH("commitid", tag_commitid),
+ TAG_MATCH("%include", var_include),
+
+ TAG_MATCH("playcount", tag_playcount),
+ TAG_MATCH("autoscore", tag_virt_autoscore),
+
+ TAG_MATCH("lastplayed", tag_lastplayed),
+ TAG_MATCH("lastoffset", tag_lastoffset),
+ TAG_MATCH("%root_menu", var_rootmenu),
+
+ TAG_MATCH("albumartist", tag_albumartist),
+ TAG_MATCH("lastelapsed", tag_lastelapsed),
+ TAG_MATCH("%menu_start", var_menu_start),
+
+ TAG_MATCH("canonicalartist", tag_virt_canonicalartist),
+ TAG_MATCH("", 0) /* sentinel */
};
- char buf[128];
- unsigned int i;
+ #undef TAG_MATCH
+ const size_t max_cmd_sz = 32; /* needs to be >= to len of longest tagstr */
+ const char *tagstr;
+ unsigned int tagstr_len;
+ const struct match *match;
/* Find the start. */
- while ((*strp == ' ' || *strp == '>') && *strp != '\0')
+ while (*strp == ' ' || *strp == '>')
strp++;
if (*strp == '\0' || *strp == '?')
return 0;
- for (i = 0; i < sizeof(buf)-1; i++)
+ tagstr = strp;
+ for (tagstr_len = 0; tagstr_len < max_cmd_sz; tagstr_len++)
{
if (*strp == '\0' || *strp == ' ')
break ;
- buf[i] = *strp;
strp++;
}
- buf[i] = '\0';
- for (i = 0; i < ARRAYLEN(get_tag_match); i++)
+ for (match = get_tag_match; match->len != 0; match++)
{
- if (!strcasecmp(buf, get_tag_match[i].str))
+ if (tagstr_len != match->len)
+ continue;
+ else if (strncasecmp(tagstr, match->str, match->len) == 0)
{
- *tag = get_tag_match[i].symbol;
+ *tag = match->symbol;
return 1;
}
}
- logf("NO MATCH: %s\n", buf);
- if (buf[0] == '?')
- return 0;
+ logf("NO MATCH: %.*s\n", tagstr_len, tagstr);
return -1;
}
static int get_clause(int *condition)
{
- static const struct match get_clause_match[] =
- {
- {"=", clause_is},
- {"==", clause_is},
- {"!=", clause_is_not},
- {">", clause_gt},
- {">=", clause_gteq},
- {"<", clause_lt},
- {"<=", clause_lteq},
- {"~", clause_contains},
- {"!~", clause_not_contains},
- {"^", clause_begins_with},
- {"!^", clause_not_begins_with},
- {"$", clause_ends_with},
- {"!$", clause_not_ends_with},
- {"@", clause_oneof}
+ /* one or two operator conditionals */
+ #define OPS2VAL(op1, op2) ((int)op1 << 8 | (int)op2)
+ #define CLAUSE(op1, op2, symbol) {OPS2VAL(op1, op2), symbol }
+
+ struct clause_symbol {int value;int symbol;};
+ const struct clause_symbol *match;
+ static const struct clause_symbol get_clause_match[] =
+ {
+ CLAUSE('=', ' ', clause_is),
+ CLAUSE('=', '=', clause_is),
+ CLAUSE('!', '=', clause_is_not),
+ CLAUSE('>', ' ', clause_gt),
+ CLAUSE('>', '=', clause_gteq),
+ CLAUSE('<', ' ', clause_lt),
+ CLAUSE('<', '=', clause_lteq),
+ CLAUSE('~', ' ', clause_contains),
+ CLAUSE('!', '~', clause_not_contains),
+ CLAUSE('^', ' ', clause_begins_with),
+ CLAUSE('!', '^', clause_not_begins_with),
+ CLAUSE('$', ' ', clause_ends_with),
+ CLAUSE('!', '$', clause_not_ends_with),
+ CLAUSE('@', '^', clause_begins_oneof),
+ CLAUSE('@', '$', clause_ends_oneof),
+ CLAUSE('@', ' ', clause_oneof),
+ CLAUSE(0, 0, 0) /* sentinel */
};
- char buf[4];
- unsigned int i;
-
/* Find the start. */
while (*strp == ' ' && *strp != '\0')
strp++;
@@ -424,25 +456,25 @@ static int get_clause(int *condition)
if (*strp == '\0')
return 0;
- for (i = 0; i < sizeof(buf)-1; i++)
- {
- if (*strp == '\0' || *strp == ' ')
- break ;
- buf[i] = *strp;
- strp++;
- }
- buf[i] = '\0';
+ char op1 = strp[0];
+ char op2 = strp[1];
+ if (op2 == '"') /*allow " to end a single op conditional */
+ op2 = ' ';
+
+ int value = OPS2VAL(op1, op2);
- for (i = 0; i < ARRAYLEN(get_clause_match); i++)
+ for (match = get_clause_match; match->value != 0; match++)
{
- if (!strcasecmp(buf, get_clause_match[i].str))
+ if (value == match->value)
{
- *condition = get_clause_match[i].symbol;
+ *condition = match->symbol;
return 1;
}
}
return 0;
+#undef OPS2VAL
+#undef CLAUSE
}
static bool read_clause(struct tagcache_search_clause *clause)
@@ -616,7 +648,7 @@ static int add_format(const char *buf)
int clause_count = 0;
strp++;
- tagtree_lock();
+ core_pin(tagtree_handle);
while (1)
{
struct tagcache_search_clause *new_clause;
@@ -639,7 +671,7 @@ static int add_format(const char *buf)
clause_count++;
}
- tagtree_unlock();
+ core_unpin(tagtree_handle);
formats[format_count]->clause_count = clause_count;
}
@@ -696,7 +728,7 @@ static int get_condition(struct search_instruction *inst)
return -2;
}
- new_clause = tagtree_alloc(sizeof(struct tagcache_search_clause));
+ new_clause = tagtree_alloc0(sizeof(struct tagcache_search_clause));
if (!new_clause)
{
logf("tagtree failed to allocate %s", "search clause");
@@ -712,9 +744,9 @@ static int get_condition(struct search_instruction *inst)
}
else
{
- tagtree_lock();
+ core_pin(tagtree_handle);
bool ret = read_clause(new_clause);
- tagtree_unlock();
+ core_unpin(tagtree_handle);
if (!ret)
return -1;
}
@@ -780,7 +812,7 @@ static bool parse_search(struct menu_entry *entry, const char *str)
logf("tagtree failed to allocate %s", "menu");
return false;
}
- strlcpy(menus[menu_count]->id, buf, MAX_MENU_ID_SIZE);
+ strmemccpy(menus[menu_count]->id, buf, MAX_MENU_ID_SIZE);
entry->link = menu_count;
++menu_count;
@@ -805,9 +837,9 @@ static bool parse_search(struct menu_entry *entry, const char *str)
logf("tag: %d", inst->tagorder[inst->tagorder_count]);
- tagtree_lock();
+ core_pin(tagtree_handle);
while ( (ret = get_condition(inst)) > 0 ) ;
- tagtree_unlock();
+ core_unpin(tagtree_handle);
if (ret < 0)
return false;
@@ -825,22 +857,7 @@ static int compare(const void *p1, const void *p2)
{
struct tagentry *e1 = (struct tagentry *)p1;
struct tagentry *e2 = (struct tagentry *)p2;
-
- if (sort_inverse)
- return strncasecmp(e2->name, e1->name, MAX_PATH);
-
- return strncasecmp(e1->name, e2->name, MAX_PATH);
-}
-
-static int nat_compare(const void *p1, const void *p2)
-{
- struct tagentry *e1 = (struct tagentry *)p1;
- struct tagentry *e2 = (struct tagentry *)p2;
-
- if (sort_inverse)
- return strnatcasecmp(e2->name, e1->name);
-
- return strnatcasecmp(e1->name, e2->name);
+ return qsort_fn(e1->name, e2->name, MAX_PATH);
}
static void tagtree_buffer_event(unsigned short id, void *ev_data)
@@ -1119,7 +1136,7 @@ static int parse_line(int n, char *buf, void *parameters)
}
menu = menus[menu_count];
++menu_count;
- strlcpy(menu->id, data, MAX_MENU_ID_SIZE);
+ strmemccpy(menu->id, data, MAX_MENU_ID_SIZE);
}
if (get_token_str(menu->title, sizeof(menu->title)) < 0)
@@ -1169,10 +1186,10 @@ static int parse_line(int n, char *buf, void *parameters)
logf("tagtree failed to allocate %s", "menu items");
return -2;
}
- tagtree_lock();
+ core_pin(tagtree_handle);
if (parse_search(menu->items[menu->itemcount], buf))
menu->itemcount++;
- tagtree_unlock();
+ core_unpin(tagtree_handle);
return 0;
}
@@ -1205,8 +1222,8 @@ static bool parse_menu(const char *filename)
static void tagtree_unload(struct tree_context *c)
{
- int i;
- tagtree_lock();
+ /* may be spurious... */
+ core_pin(tagtree_handle);
remove_event(PLAYBACK_EVENT_TRACK_BUFFER, tagtree_buffer_event);
remove_event(PLAYBACK_EVENT_TRACK_FINISH, tagtree_track_finish_event);
@@ -1222,7 +1239,7 @@ static void tagtree_unload(struct tree_context *c)
return;
}
- for (i = 0; i < menu->itemcount; i++)
+ for (int i = 0; i < menu->itemcount; i++)
{
dptr->name = NULL;
dptr->newtable = 0;
@@ -1231,11 +1248,11 @@ static void tagtree_unload(struct tree_context *c)
}
}
- for (i = 0; i < menu_count; i++)
+ for (int i = 0; i < menu_count; i++)
menus[i] = NULL;
menu_count = 0;
- for (i = 0; i < format_count; i++)
+ for (int i = 0; i < format_count; i++)
formats[i] = NULL;
format_count = 0;
@@ -1246,33 +1263,36 @@ static void tagtree_unload(struct tree_context *c)
if (c)
tree_unlock_cache(c);
- tagtree_unlock();
- if (lock_count > 0)
- tagtree_unlock();/* second unlock to enable re-init */
}
-void tagtree_init(void)
+static bool initialize_tagtree(void) /* also used when user selects 'Reload' in 'custom view'*/
{
+ max_history_level = 0;
format_count = 0;
menu_count = 0;
menu = NULL;
rootmenu = -1;
- tagtree_handle = core_alloc_maximum("tagtree", &tagtree_bufsize, &ops);
+ tagtree_handle = core_alloc_maximum(&tagtree_bufsize, &ops);
if (tagtree_handle < 0)
panicf("tagtree OOM");
- if (!parse_menu(FILE_SEARCH_INSTRUCTIONS))
+ /* Use the user tagnavi config if present, otherwise use the default. */
+ const char* tagnavi_file;
+ if(file_exists(TAGNAVI_USER_CONFIG))
+ tagnavi_file = TAGNAVI_USER_CONFIG;
+ else
+ tagnavi_file = TAGNAVI_DEFAULT_CONFIG;
+
+ if (!parse_menu(tagnavi_file))
{
tagtree_unload(NULL);
- return;
+ return false;
}
/* safety check since tree.c needs to cast tagentry to entry */
if (sizeof(struct tagentry) != sizeof(struct entry))
panicf("tagentry(%zu) and entry mismatch(%zu)",
sizeof(struct tagentry), sizeof(struct entry));
- if (lock_count > 0)
- panicf("tagtree locked after parsing");
/* If no root menu is set, assume it's the first single menu
* we have. That shouldn't normally happen. */
@@ -1282,7 +1302,13 @@ void tagtree_init(void)
add_event(PLAYBACK_EVENT_TRACK_BUFFER, tagtree_buffer_event);
add_event(PLAYBACK_EVENT_TRACK_FINISH, tagtree_track_finish_event);
- core_shrink(tagtree_handle, core_get_data(tagtree_handle), tagtree_buf_used);
+ core_shrink(tagtree_handle, NULL, tagtree_buf_used);
+ return true;
+}
+
+void tagtree_init(void)
+{
+ initialize_tagtree();
}
static bool show_search_progress(bool init, int count)
@@ -1312,14 +1338,14 @@ static bool show_search_progress(bool init, int count)
static int format_str(struct tagcache_search *tcs, struct display_format *fmt,
char *buf, int buf_size)
{
- char fmtbuf[20];
+ static char fmtbuf[20];
bool read_format = false;
unsigned fmtbuf_pos = 0;
int parpos = 0;
int buf_pos = 0;
int i;
- memset(buf, 0, buf_size);
+ /* memset(buf, 0, buf_size); probably uneeded */
for (i = 0; fmt->formatstr[i] != '\0'; i++)
{
if (fmt->formatstr[i] == '%')
@@ -1366,21 +1392,13 @@ static int format_str(struct tagcache_search *tcs, struct display_format *fmt,
int tag = fmt->tags[parpos];
if (!tagcache_retrieve(tcs, tcs->idx_id,
- (tag == tag_virt_basename ?
- tag_filename : tag),
- tmpbuf, sizeof tmpbuf))
+ tag, tmpbuf, sizeof tmpbuf))
{
logf("retrieve failed");
return -3;
}
- if (tag == tag_virt_basename
- && (result = strrchr(tmpbuf, '/')) != NULL)
- {
- result++;
- }
- else
- result = tmpbuf;
+ result = tmpbuf;
}
buf_pos +=
snprintf(&buf[buf_pos], space_left, fmtbuf, result);
@@ -1415,8 +1433,23 @@ static struct tagentry* get_entries(struct tree_context *tc)
return core_get_data(tc->cache.entries_handle);
}
+static void tcs_get_basename(struct tagcache_search *tcs, bool is_basename)
+{
+ if (is_basename)
+ {
+ char* basename = strrchr(tcs->result, '/');
+ if (basename != NULL)
+ {
+ tcs->result = basename + 1;
+ tcs->result_len = strlen(tcs->result) + 1;
+ }
+ }
+}
+
static int retrieve_entries(struct tree_context *c, int offset, bool init)
{
+ char tcs_buf[TAGCACHE_BUFSZ];
+ const long tcs_bufsz = sizeof(tcs_buf);
struct tagcache_search tcs;
struct display_format *fmt;
int i;
@@ -1426,6 +1459,8 @@ static int retrieve_entries(struct tree_context *c, int offset, bool init)
int level = c->currextra;
int tag;
bool sort = false;
+ bool sort_inverse;
+ bool is_basename = false;
int sort_limit;
int strip;
@@ -1433,6 +1468,9 @@ static int retrieve_entries(struct tree_context *c, int offset, bool init)
otherwise show it after the normal 1/2 second delay */
show_search_progress(
#ifdef HAVE_DISK_STORAGE
+#ifdef HAVE_TC_RAMCACHE
+ tagcache_is_in_ram() ? true :
+#endif
storage_disk_is_active()
#else
true
@@ -1450,13 +1488,19 @@ static int retrieve_entries(struct tree_context *c, int offset, bool init)
if (tag == menu_reload)
return RELOAD_TAGTREE;
+ if (tag == tag_virt_basename) /* basename shortcut */
+ {
+ is_basename = true;
+ tag = tag_filename;
+ }
+
if (!tagcache_search(&tcs, tag))
return -1;
/* Prevent duplicate entries in the search list. */
tagcache_search_set_uniqbuf(&tcs, uniqbuf, UNIQBUF_SIZE);
- if (level || csi->clause_count[0] || TAGCACHE_IS_NUMERIC(tag))
+ if (level || is_basename|| csi->clause_count[0] || TAGCACHE_IS_NUMERIC(tag))
sort = true;
for (i = 0; i < level; i++)
@@ -1481,7 +1525,7 @@ static int retrieve_entries(struct tree_context *c, int offset, bool init)
/* because tagcache saves the clauses, we need to lock the buffer
* for the entire duration of the search */
- tagtree_lock();
+ core_pin(tagtree_handle);
for (i = 0; i <= level; i++)
{
int j;
@@ -1542,7 +1586,7 @@ static int retrieve_entries(struct tree_context *c, int offset, bool init)
total_count += 2;
}
- while (tagcache_get_next(&tcs))
+ while (tagcache_get_next(&tcs, tcs_buf, tcs_bufsz))
{
if (total_count++ < offset)
continue;
@@ -1558,7 +1602,6 @@ static int retrieve_entries(struct tree_context *c, int offset, bool init)
fmt = NULL;
/* Check the format */
- tagtree_lock();
for (i = 0; i < format_count; i++)
{
if (formats[i]->group_id != csi->format_id[level])
@@ -1571,10 +1614,22 @@ static int retrieve_entries(struct tree_context *c, int offset, bool init)
break;
}
}
- tagtree_unlock();
if (strcmp(tcs.result, UNTAGGED) == 0)
{
+ if (tag == tag_title && tcs.type == tag_title && tcs.filter_count <= 1)
+ { /* Fallback to basename */
+ char *lastname = dptr->name;
+ dptr->name = core_get_data(c->cache.name_buffer_handle)+namebufused;
+ if (tagcache_retrieve(&tcs, tcs.idx_id, tag_virt_basename, dptr->name,
+ c->cache.name_buffer_size - namebufused))
+ {
+ namebufused += strlen(dptr->name)+1;
+ goto entry_skip_formatter;
+ }
+ dptr->name = lastname; /* restore last entry if filename failed */
+ }
+
tcs.result = str(LANG_TAGNAVI_UNTAGGED);
tcs.result_len = strlen(tcs.result);
tcs.ramresult = true;
@@ -1588,26 +1643,31 @@ static int retrieve_entries(struct tree_context *c, int offset, bool init)
{
int ret = format_str(&tcs, fmt, dptr->name,
c->cache.name_buffer_size - namebufused);
- if (ret == -4) /* buffer full */
+ if (ret >= 0)
{
- logf("chunk mode #2: %d", current_entry_count);
- c->dirfull = true;
- sort = false;
- break ;
+ namebufused += strlen(dptr->name)+1; /* include NULL */
}
- else if (ret < 0)
+ else
{
+ dptr->name[0] = '\0';
+ if (ret == -4) /* buffer full */
+ {
+ logf("chunk mode #2: %d", current_entry_count);
+ c->dirfull = true;
+ sort = false;
+ break ;
+ }
+
logf("format_str() failed");
tagcache_search_finish(&tcs);
tree_unlock_cache(c);
- tagtree_unlock();
+ core_unpin(tagtree_handle);
return 0;
}
- else
- namebufused += strlen(dptr->name)+1;
}
else
{
+ tcs_get_basename(&tcs, is_basename);
namebufused += tcs.result_len;
if (namebufused < c->cache.name_buffer_size)
strcpy(dptr->name, tcs.result);
@@ -1621,8 +1681,11 @@ static int retrieve_entries(struct tree_context *c, int offset, bool init)
}
}
else
+ {
+ tcs_get_basename(&tcs, is_basename);
dptr->name = tcs.result;
-
+ }
+entry_skip_formatter:
dptr++;
current_entry_count++;
@@ -1634,13 +1697,13 @@ static int retrieve_entries(struct tree_context *c, int offset, bool init)
break ;
}
- if (init && !tcs.ramsearch)
+ if (init)
{
if (!show_search_progress(false, total_count))
{ /* user aborted */
tagcache_search_finish(&tcs);
tree_unlock_cache(c);
- tagtree_unlock();
+ core_unpin(tagtree_handle);
return current_entry_count;
}
}
@@ -1648,34 +1711,36 @@ static int retrieve_entries(struct tree_context *c, int offset, bool init)
if (sort)
{
+ if (global_settings.interpret_numbers)
+ qsort_fn = sort_inverse ? strnatcasecmp_n_inv : strnatcasecmp_n;
+ else
+ qsort_fn = sort_inverse ? strncasecmp_inv : strncasecmp;
+
struct tagentry *entries = get_entries(c);
qsort(&entries[special_entry_count],
current_entry_count - special_entry_count,
sizeof(struct tagentry),
- global_settings.interpret_numbers ? nat_compare : compare);
+ compare);
}
if (!init)
{
tagcache_search_finish(&tcs);
tree_unlock_cache(c);
- tagtree_unlock();
+ core_unpin(tagtree_handle);
return current_entry_count;
}
- while (tagcache_get_next(&tcs))
+ while (tagcache_get_next(&tcs, tcs_buf, tcs_bufsz))
{
- if (!tcs.ramsearch)
- {
- if (!show_search_progress(false, total_count))
- break;
- }
+ if (!show_search_progress(false, total_count))
+ break;
total_count++;
}
tagcache_search_finish(&tcs);
tree_unlock_cache(c);
- tagtree_unlock();
+ core_unpin(tagtree_handle);
if (!sort && (sort_inverse || sort_limit))
{
@@ -1789,7 +1854,8 @@ int tagtree_load(struct tree_context* c)
{
splash(HZ, str(LANG_WAIT));
tagtree_unload(c);
- tagtree_init();
+ if (!initialize_tagtree())
+ return 0;
}
c->dirlevel = 0;
count = load_root(c);
@@ -1801,7 +1867,16 @@ int tagtree_load(struct tree_context* c)
return count;
}
-int tagtree_enter(struct tree_context* c)
+/* Enters menu or table for selected item in the database.
+ *
+ * Call this with the is_visible parameter set to false to
+ * prevent selected_item_history from being updated or applied, in
+ * case the menus aren't displayed to the user.
+ * Before calling tagtree_enter again with the parameter set to
+ * true, make sure that you are back at the previous dirlevel, by
+ * calling tagtree_exit as needed, with is_visible set to false.
+ */
+int tagtree_enter(struct tree_context* c, bool is_visible)
{
int rc = 0;
struct tagentry *dptr;
@@ -1809,14 +1884,17 @@ int tagtree_enter(struct tree_context* c)
int newextra;
int seek;
int source;
+ bool is_random_item = false;
+ bool adjust_selection = true;
dptr = tagtree_get_entry(c, c->selected_item);
c->dirfull = false;
seek = dptr->extraseek;
- if (seek == -1)
+ if (seek == -1) /* <Random> menu item was selected */
{
- if(c->filesindir<=2)
+ is_random_item = true;
+ if(c->filesindir<=2) /* Menu contains only <All> and <Random> menu items */
return 0;
srand(current_tick);
dptr = (tagtree_get_entry(c, 2+(rand() % (c->filesindir-2))));
@@ -1827,15 +1905,28 @@ int tagtree_enter(struct tree_context* c)
if (c->dirlevel >= MAX_DIR_LEVELS)
return 0;
- c->selected_item_history[c->dirlevel]=c->selected_item;
- c->table_history[c->dirlevel] = c->currtable;
- c->extra_history[c->dirlevel] = c->currextra;
- c->pos_history[c->dirlevel] = c->firstpos;
+ if (is_visible) /* update selection history only for user-selected items */
+ {
+ /* We need to discard selected item history for levels
+ descending from current one if selection has changed */
+ if (max_history_level < c->dirlevel + 1
+ || (max_history_level > c->dirlevel
+ && selected_item_history[c->dirlevel] != c->selected_item)
+ || is_random_item)
+ {
+ max_history_level = c->dirlevel + 1;
+ selected_item_history[c->dirlevel + 1] = 0;
+ }
+
+ selected_item_history[c->dirlevel]=c->selected_item;
+ }
+ table_history[c->dirlevel] = c->currtable;
+ extra_history[c->dirlevel] = c->currextra;
c->dirlevel++;
/* lock buflib for possible I/O to protect dptr */
tree_lock_cache(c);
- tagtree_lock();
+ core_pin(tagtree_handle);
switch (c->currtable) {
case ROOT:
@@ -1854,8 +1945,8 @@ int tagtree_enter(struct tree_context* c)
csi = &menu->items[seek]->si;
c->currextra = 0;
- strlcpy(current_title[c->currextra], dptr->name,
- sizeof(current_title[0]));
+ strmemccpy(current_title[c->currextra], dptr->name,
+ sizeof(current_title[0]));
/* Read input as necessary. */
for (i = 0; i < csi->tagorder_count; i++)
@@ -1872,6 +1963,10 @@ int tagtree_enter(struct tree_context* c)
if (source == source_constant)
continue;
+ /* discard history for lower levels when doing runtime searches */
+ if (is_visible)
+ max_history_level = c->dirlevel - 1;
+
searchstring=csi->clause[i][j]->str;
*searchstring = '\0';
@@ -1880,7 +1975,7 @@ int tagtree_enter(struct tree_context* c)
if (source == source_current_path && id3)
{
char *e;
- strlcpy(searchstring, id3->path, SEARCHSTR_SIZE);
+ strmemccpy(searchstring, id3->path, SEARCHSTR_SIZE);
e = strrchr(searchstring, '/');
if (e)
*e = '\0';
@@ -1893,7 +1988,7 @@ int tagtree_enter(struct tree_context* c)
char **src = (char**)((char*)id3 + offset);
if (*src)
{
- strlcpy(searchstring, *src, SEARCHSTR_SIZE);
+ strmemccpy(searchstring, *src, SEARCHSTR_SIZE);
}
}
else
@@ -1901,9 +1996,9 @@ int tagtree_enter(struct tree_context* c)
rc = kbd_input(searchstring, SEARCHSTR_SIZE, NULL);
if (rc < 0 || !searchstring[0])
{
- tagtree_exit(c);
+ tagtree_exit(c, is_visible);
tree_unlock_cache(c);
- tagtree_unlock();
+ core_unpin(tagtree_handle);
return 0;
}
if (csi->clause[i][j]->numeric)
@@ -1922,6 +2017,8 @@ int tagtree_enter(struct tree_context* c)
case ALLSUBENTRIES:
if (newextra == PLAYTRACK)
{
+ adjust_selection = false;
+
if (global_settings.party_mode && audio_status()) {
splash(HZ, ID2P(LANG_PARTY_MODE));
break;
@@ -1944,8 +2041,8 @@ int tagtree_enter(struct tree_context* c)
c->dirlevel--;
/* Update the statusbar title */
- strlcpy(current_title[c->currextra], dptr->name,
- sizeof(current_title[0]));
+ strmemccpy(current_title[c->currextra], dptr->name,
+ sizeof(current_title[0]));
break;
default:
@@ -1953,25 +2050,36 @@ int tagtree_enter(struct tree_context* c)
break;
}
+ if (adjust_selection)
+ {
+ if (is_visible && c->dirlevel <= max_history_level)
+ c->selected_item = selected_item_history[c->dirlevel];
+ else
+ c->selected_item = 0;
+ }
- c->selected_item=0;
- gui_synclist_select_item(&tree_lists, c->selected_item);
tree_unlock_cache(c);
- tagtree_unlock();
+ core_unpin(tagtree_handle);
return rc;
}
-void tagtree_exit(struct tree_context* c)
+/* Exits current database menu or table */
+void tagtree_exit(struct tree_context* c, bool is_visible)
{
+ if (is_visible) /* update selection history only for user-selected items */
+ {
+ if (c->selected_item != selected_item_history[c->dirlevel])
+ max_history_level = c->dirlevel; /* discard descending item history */
+ selected_item_history[c->dirlevel] = c->selected_item;
+ }
c->dirfull = false;
if (c->dirlevel > 0)
c->dirlevel--;
- c->selected_item=c->selected_item_history[c->dirlevel];
- gui_synclist_select_item(&tree_lists, c->selected_item);
- c->currtable = c->table_history[c->dirlevel];
- c->currextra = c->extra_history[c->dirlevel];
- c->firstpos = c->pos_history[c->dirlevel];
+ if (is_visible)
+ c->selected_item = selected_item_history[c->dirlevel];
+ c->currtable = table_history[c->dirlevel];
+ c->currextra = extra_history[c->dirlevel];
}
int tagtree_get_filename(struct tree_context* c, char *buf, int buflen)
@@ -1994,13 +2102,16 @@ int tagtree_get_filename(struct tree_context* c, char *buf, int buflen)
return 0;
}
-static bool insert_all_playlist(struct tree_context *c, int position, bool queue)
+
+static bool insert_all_playlist(struct tree_context *c,
+ const char* playlist, bool new_playlist,
+ int position, bool queue)
{
struct tagcache_search tcs;
- int i;
+ int i, n;
+ int fd = -1;
+ unsigned long last_tick;
char buf[MAX_PATH];
- int from, to, direction;
- int files_left = c->filesindir;
cpu_boost(true);
if (!tagcache_search(&tcs, tag_filename))
@@ -2010,7 +2121,7 @@ static bool insert_all_playlist(struct tree_context *c, int position, bool queue
return false;
}
- if (position == PLAYLIST_REPLACE)
+ if (playlist == NULL && position == PLAYLIST_REPLACE)
{
if (playlist_remove_all_tracks(NULL) == 0)
position = PLAYLIST_INSERT_LAST;
@@ -2020,16 +2131,33 @@ static bool insert_all_playlist(struct tree_context *c, int position, bool queue
return false;
}
}
+ else if (playlist != NULL)
+ {
+ if (new_playlist)
+ fd = open_utf8(playlist, O_CREAT|O_WRONLY|O_TRUNC);
+ else
+ fd = open(playlist, O_CREAT|O_WRONLY|O_APPEND, 0666);
- from = 0;
- to = c->filesindir;
- direction = 1;
+ if(fd < 0)
+ {
+ cpu_boost(false);
+ return false;
+ }
+ }
- for (i = from; i != to; i += direction)
+ last_tick = current_tick + HZ/2; /* Show splash after 0.5 seconds have passed */
+ splash_progress_set_delay(HZ / 2); /* wait 1/2 sec before progress */
+ n = c->filesindir;
+ for (i = 0; i < n; i++)
{
- /* Count back to zero */
- if (!show_search_progress(false, files_left--))
- break;
+
+ splash_progress(i, n, "%s (%s)", str(LANG_WAIT), str(LANG_OFF_ABORT));
+ if (TIME_AFTER(current_tick, last_tick + HZ/4))
+ {
+ if (action_userabort(TIMEOUT_NOBLOCK))
+ break;
+ last_tick = current_tick;
+ }
if (!tagcache_retrieve(&tcs, tagtree_get_entry(c, i)->extraseek,
tcs.type, buf, sizeof buf))
@@ -2037,30 +2165,63 @@ static bool insert_all_playlist(struct tree_context *c, int position, bool queue
continue;
}
- if (playlist_insert_track(NULL, buf, position, queue, false) < 0)
+ if (playlist == NULL)
{
- logf("playlist_insert_track failed");
- break;
+ if (playlist_insert_track(NULL, buf, position, queue, false) < 0)
+ {
+ logf("playlist_insert_track failed");
+ break;
+ }
}
+ else if (fdprintf(fd, "%s\n", buf) <= 0)
+ break;
+
yield();
- if (position == PLAYLIST_INSERT_FIRST)
+ if (playlist == NULL && position == PLAYLIST_INSERT_FIRST)
{
position = PLAYLIST_INSERT;
}
}
- playlist_sync(NULL);
+ if (playlist == NULL)
+ playlist_sync(NULL);
+ else
+ close(fd);
tagcache_search_finish(&tcs);
cpu_boost(false);
return true;
}
-bool tagtree_insert_selection_playlist(int position, bool queue)
+static bool goto_allsubentries(int newtable)
+{
+ int i = 0;
+ while (i < 2 && (newtable == NAVIBROWSE || newtable == ALLSUBENTRIES))
+ {
+ tagtree_enter(tc, false);
+ tagtree_load(tc);
+ newtable = tagtree_get_entry(tc, tc->selected_item)->newtable;
+ i++;
+ }
+ return (newtable == PLAYTRACK);
+}
+
+static void reset_tc_to_prev(int dirlevel, int selected_item)
+{
+ while (tc->dirlevel > dirlevel)
+ tagtree_exit(tc, false);
+ tc->selected_item = selected_item;
+ tagtree_load(tc);
+}
+
+static bool tagtree_insert_selection(int position, bool queue,
+ const char* playlist, bool new_playlist)
{
char buf[MAX_PATH];
int dirlevel = tc->dirlevel;
+ int selected_item = tc->selected_item;
int newtable;
+ int ret;
show_search_progress(
#ifdef HAVE_DISK_STORAGE
@@ -2070,87 +2231,136 @@ bool tagtree_insert_selection_playlist(int position, bool queue)
#endif
, 0);
-
- /* We need to set the table to allsubentries. */
newtable = tagtree_get_entry(tc, tc->selected_item)->newtable;
- /* Insert a single track? */
- if (newtable == PLAYTRACK)
+ if (newtable == PLAYTRACK) /* Insert a single track? */
{
if (tagtree_get_filename(tc, buf, sizeof buf) < 0)
- {
- logf("tagtree_get_filename failed");
return false;
- }
+
playlist_insert_track(NULL, buf, position, queue, true);
return true;
}
- if (newtable == NAVIBROWSE)
- {
- tagtree_enter(tc);
- tagtree_load(tc);
- newtable = tagtree_get_entry(tc, tc->selected_item)->newtable;
- }
- else if (newtable != ALLSUBENTRIES)
+ ret = goto_allsubentries(newtable);
+ if (ret)
{
- logf("unsupported table: %d", newtable);
- return false;
+ if (tc->filesindir <= 0)
+ splash(HZ, ID2P(LANG_END_PLAYLIST));
+ else if (!insert_all_playlist(tc, playlist, new_playlist, position, queue))
+ splash(HZ*2, ID2P(LANG_FAILED));
}
- /* Now the current table should be allsubentries. */
- if (newtable != PLAYTRACK)
- {
- tagtree_enter(tc);
- tagtree_load(tc);
- newtable = tagtree_get_entry(tc, tc->selected_item)->newtable;
+ reset_tc_to_prev(dirlevel, selected_item);
+ return ret;
+}
+
+/* Execute action_cb for all subentries of the current table's
+ * selected item, handing over each entry's filename in the
+ * callback function parameter. Parameter will be NULL for
+ * entries whose filename couldn't be retrieved.
+ */
+bool tagtree_subentries_do_action(bool (*action_cb)(const char *file_name))
+{
+ struct tagcache_search tcs;
+ int i, n;
+ unsigned long last_tick;
+ char buf[MAX_PATH];
+ int ret = true;
+ int dirlevel = tc->dirlevel;
+ int selected_item = tc->selected_item;
+ int newtable = tagtree_get_entry(tc, tc->selected_item)->newtable;
- /* And now the newtable should be playtrack. */
- if (newtable != PLAYTRACK)
+ cpu_boost(true);
+ if (!goto_allsubentries(newtable))
+ ret = false;
+ else if (tagcache_search(&tcs, tag_filename))
+ {
+ last_tick = current_tick + HZ/2;
+ splash_progress_set_delay(HZ / 2); /* wait 1/2 sec before progress */
+ n = tc->filesindir;
+ for (i = 0; i < n; i++)
{
- logf("newtable: %d !!", newtable);
- tc->dirlevel = dirlevel;
- return false;
+ splash_progress(i, n, "%s (%s)", str(LANG_WAIT), str(LANG_OFF_ABORT));
+ if (TIME_AFTER(current_tick, last_tick + HZ/4))
+ {
+ if (action_userabort(TIMEOUT_NOBLOCK))
+ break;
+ last_tick = current_tick;
+ }
+
+ if (!action_cb(tagcache_retrieve(&tcs, tagtree_get_entry(tc, i)->extraseek,
+ tcs.type, buf, sizeof buf) ? buf : NULL))
+ {
+ ret = false;
+ break;
+ }
+ yield();
}
- }
- if (tc->filesindir <= 0)
- splash(HZ, ID2P(LANG_END_PLAYLIST));
+ tagcache_search_finish(&tcs);
+ }
else
{
- logf("insert_all_playlist");
- if (!insert_all_playlist(tc, position, queue))
- splash(HZ*2, ID2P(LANG_FAILED));
+ splash(HZ, ID2P(LANG_TAGCACHE_BUSY));
+ ret = false;
}
+ reset_tc_to_prev(dirlevel, selected_item);
+ cpu_boost(false);
+ return ret;
+}
- /* Finally return the dirlevel to its original value. */
- while (tc->dirlevel > dirlevel)
- tagtree_exit(tc);
- tagtree_load(tc);
+/* Try to return first subentry's filename for current selection
+ */
+bool tagtree_get_subentry_filename(char *buf, size_t bufsize)
+{
+ int ret = true;
+ int dirlevel = tc->dirlevel;
+ int selected_item = tc->selected_item;
+ int newtable = tagtree_get_entry(tc, tc->selected_item)->newtable;
- return true;
+ if (!goto_allsubentries(newtable) || tagtree_get_filename(tc, buf, bufsize) < 0)
+ ret = false;
+
+ reset_tc_to_prev(dirlevel, selected_item);
+ return ret;
+}
+
+bool tagtree_current_playlist_insert(int position, bool queue)
+{
+ return tagtree_insert_selection(position, queue, NULL, false);
+}
+
+
+int tagtree_add_to_playlist(const char* playlist, bool new_playlist)
+{
+ if (!new_playlist)
+ tagtree_load(tc); /* because display_playlists was called */
+ return tagtree_insert_selection(0, false, playlist, new_playlist) ? 0 : -1;
}
static int tagtree_play_folder(struct tree_context* c)
{
+ int start_index = c->selected_item;
+
if (playlist_create(NULL, NULL) < 0)
{
logf("Failed creating playlist\n");
return -1;
}
- if (!insert_all_playlist(c, PLAYLIST_INSERT_LAST, false))
+ if (!insert_all_playlist(c, NULL, false, PLAYLIST_INSERT_LAST, false))
return -2;
if (global_settings.playlist_shuffle)
- c->selected_item = playlist_shuffle(current_tick, c->selected_item);
- if (!global_settings.play_selected)
- c->selected_item = 0;
- gui_synclist_select_item(&tree_lists, c->selected_item);
+ {
+ start_index = playlist_shuffle(current_tick, c->selected_item);
+ if (!global_settings.play_selected)
+ start_index = 0;
+ }
- playlist_start(c->selected_item, 0, 0);
- playlist_get_current()->num_inserted_tracks = 0; /* make warn on playlist erase work */
+ playlist_start(start_index, 0, 0);
return 0;
}
@@ -2184,7 +2394,7 @@ char* tagtree_get_entry_name(struct tree_context *c, int id,
struct tagentry *entry = tagtree_get_entry(c, id);
if (!entry)
return NULL;
- strlcpy(buf, entry->name, bufsize);
+ strmemccpy(buf, entry->name, bufsize);
return buf;
}
@@ -2210,7 +2420,8 @@ int tagtree_get_attr(struct tree_context* c)
switch (c->currtable)
{
case NAVIBROWSE:
- if (csi->tagorder[c->currextra] == tag_title)
+ if (csi->tagorder[c->currextra] == tag_title
+ || csi->tagorder[c->currextra] == tag_virt_basename)
attr = FILE_ATTR_AUDIO;
else
attr = ATTR_DIRECTORY;
diff --git a/apps/tagtree.h b/apps/tagtree.h
index 427a602df6..39ab545bd0 100644
--- a/apps/tagtree.h
+++ b/apps/tagtree.h
@@ -34,16 +34,19 @@
int tagtree_export(void);
int tagtree_import(void);
void tagtree_init(void) INIT_ATTR;
-int tagtree_enter(struct tree_context* c);
-void tagtree_exit(struct tree_context* c);
+int tagtree_enter(struct tree_context* c, bool is_visible);
+void tagtree_exit(struct tree_context* c, bool is_visible);
int tagtree_load(struct tree_context* c);
char* tagtree_get_entry_name(struct tree_context *c, int id,
char* buf, size_t bufsize);
-bool tagtree_insert_selection_playlist(int position, bool queue);
+bool tagtree_current_playlist_insert(int position, bool queue);
+int tagtree_add_to_playlist(const char* playlist, bool new_playlist);
char *tagtree_get_title(struct tree_context* c);
int tagtree_get_attr(struct tree_context* c);
int tagtree_get_icon(struct tree_context* c);
int tagtree_get_filename(struct tree_context* c, char *buf, int buflen);
+bool tagtree_get_subentry_filename(char *buf, size_t bufsize);
+bool tagtree_subentries_do_action(bool (*action_cb)(const char *file_name));
#endif
#endif
diff --git a/apps/talk.c b/apps/talk.c
index 551d9672bc..fb2f9cf3df 100644
--- a/apps/talk.c
+++ b/apps/talk.c
@@ -35,12 +35,12 @@
#include "voice_thread.h"
#include "audio.h"
#include "lang.h"
+#include "language.h"
#include "talk.h"
#include "metadata.h"
/*#define LOGF_ENABLE*/
#include "logf.h"
#include "bitswap.h"
-#include "structec.h"
#include "plugin.h" /* plugin_get_buffer() */
#include "debug.h"
#include "panic.h"
@@ -97,7 +97,7 @@ struct voicefile_header /* file format of our voice file */
#define MAX_CLIP_BUFFER_SIZE (1<<30)
#endif
#define THUMBNAIL_RESERVE (50000)
-
+#define NULL_TERMINATED ((size_t)-1)
/* Multiple thumbnails can be loaded back-to-back in this buffer. */
static volatile int thumbnail_buf_used SHAREDBSS_ATTR; /* length of data in
thumbnail buffer */
@@ -118,6 +118,7 @@ static unsigned char last_lang[MAX_FILENAME+1]; /* name of last used lang file (
static bool talk_initialized; /* true if talk_init has been called */
static bool give_buffer_away; /* true if we should give the buffers away in shrink_callback if requested */
static int talk_temp_disable_count; /* if positive, temporarily disable voice UI (not saved) */
+
/* size of the voice data in the voice file and the actually allocated buffer
* for it. voicebuf_size is always smaller or equal to voicefile_size */
static unsigned long voicefile_size, voicebuf_size;
@@ -226,7 +227,7 @@ static int shrink_callback(int handle, unsigned hints, void *start, size_t old_s
mutex_lock(&read_buffer_mutex);
/* the clip buffer isn't usable without index table */
- if (handle == index_handle && talk_handle > 0)
+ if (handle == index_handle)
talk_handle = core_free(talk_handle);
if (h)
*h = core_free(handle);
@@ -246,7 +247,7 @@ static struct buflib_callbacks talk_ops = {
static int open_voicefile(void)
{
- char buf[64];
+ char fname[MAX_PATH];
char* p_lang = DEFAULT_VOICE_LANG; /* default */
if ( global_settings.lang_file[0] &&
@@ -255,9 +256,8 @@ static int open_voicefile(void)
p_lang = (char *)global_settings.lang_file;
}
- snprintf(buf, sizeof(buf), LANG_DIR "/%s.voice", p_lang);
-
- return open(buf, O_RDONLY);
+ return open_pathfmt(fname, sizeof(fname),
+ O_RDONLY, LANG_DIR "/%s.voice", p_lang);
}
@@ -306,14 +306,15 @@ static int free_oldest_clip(void)
unsigned i;
int oldest = 0;
bool thumb = false;
- long age, now;
+ long age, now, next_age;
struct clip_entry* clipbuf;
struct clip_cache_metadata *cc = buflib_get_data(&clip_ctx, metadata_table_handle);
for(age = i = 0, now = current_tick; i < max_clips; i++)
{
if (cc[i].handle)
{
- if (thumb && cc[i].voice_id == VOICEONLY_DELIMITER && (now - cc[i].tick) > age)
+ next_age = (now - cc[i].tick);
+ if (thumb && cc[i].voice_id == VOICEONLY_DELIMITER && next_age > age)
{
/* thumb clips are freed first */
age = now - cc[i].tick;
@@ -323,14 +324,14 @@ static int free_oldest_clip(void)
{
if (cc[i].voice_id == VOICEONLY_DELIMITER)
{
- age = now - cc[i].tick;
+ age = next_age;
oldest = i;
thumb = true;
}
- else if ((now - cc[i].tick) > age && cc[i].voice_id != VOICE_PAUSE)
+ else if (next_age > age && cc[i].voice_id != VOICE_PAUSE)
{
/* find the last-used clip but never consider silence */
- age = now - cc[i].tick;
+ age = next_age;
oldest = i;
}
}
@@ -348,7 +349,6 @@ static int free_oldest_clip(void)
return oldest;
}
-
/* common code for load_initial_clips() and get_clip() */
static void add_cache_entry(int clip_handle, int table_index, int id)
{
@@ -377,7 +377,7 @@ static ssize_t read_clip_data(int fd, int index, int clip_handle)
{
struct clip_entry* clipbuf;
size_t clipsize;
- ssize_t ret;
+ ssize_t ret = -1;
if (fd < 0)
{
@@ -388,8 +388,8 @@ static ssize_t read_clip_data(int fd, int index, int clip_handle)
clipbuf = core_get_data(index_handle);
/* this must not be called with LOADED_MASK set in clipsize */
clipsize = clipbuf[index].size;
- lseek(fd, clipbuf[index].offset, SEEK_SET);
- ret = read_to_handle_ex(fd, &clip_ctx, clip_handle, 0, clipsize);
+ if (lseek(fd, clipbuf[index].offset, SEEK_SET) >= 0)
+ ret = read_to_handle_ex(fd, &clip_ctx, clip_handle, 0, clipsize);
if (ret < 0 || clipsize != (size_t)ret)
{
@@ -496,28 +496,29 @@ static bool load_index_table(int fd, const struct voicefile_header *hdr)
return true;
ssize_t alloc_size = (hdr->id1_max + hdr->id2_max) * sizeof(struct clip_entry);
- index_handle = core_alloc_ex("voice index", alloc_size, &talk_ops);
+ index_handle = core_alloc_ex(alloc_size, &talk_ops);
if (index_handle < 0)
return false;
ret = read_to_handle(fd, index_handle, 0, alloc_size);
-
- if (ret == alloc_size)
+ if (ret != alloc_size)
{
+ index_handle = core_free(index_handle);
+ return false;
+ }
+
#ifdef ROCKBOX_LITTLE_ENDIAN
- struct clip_entry *buf;
- buf = core_get_data(index_handle);
- for (int i = 0; i < hdr->id1_max + hdr->id2_max; i++)
- {
- /* doesn't yield() */
- structec_convert(&buf[i], "ll", 1, true);
- }
-#endif
+ struct clip_entry *buf, *end;
+ buf = core_get_data(index_handle);
+ end = buf + hdr->id1_max + hdr->id2_max;
+ for (; buf != end; buf++)
+ {
+ buf->offset = swap32(buf->offset);
+ buf->size = swap32(buf->size);
}
- else
- index_handle = core_free(index_handle);
+#endif
- return ret == alloc_size;
+ return true;
}
static bool load_header(int fd, struct voicefile_header *hdr)
@@ -527,8 +528,11 @@ static bool load_header(int fd, struct voicefile_header *hdr)
return false;
#ifdef ROCKBOX_LITTLE_ENDIAN
- logf("Byte swapping voice file");
- structec_convert(&voicefile, "lllll", 1, true);
+ hdr->version = swap32(hdr->version);
+ hdr->target_id = swap32(hdr->target_id);
+ hdr->table = swap32(hdr->table);
+ hdr->id1_max = swap32(hdr->id1_max);
+ hdr->id2_max = swap32(hdr->id2_max);
#endif
return true;
}
@@ -536,7 +540,7 @@ static bool load_header(int fd, struct voicefile_header *hdr)
static bool create_clip_buffer(size_t max_size)
{
/* just allocate, populate on an as-needed basis later */
- talk_handle = core_alloc_ex("voice data", max_size, &talk_ops);
+ talk_handle = core_alloc_ex(max_size, &talk_ops);
if (talk_handle < 0)
goto alloc_err;
@@ -546,11 +550,17 @@ static bool create_clip_buffer(size_t max_size)
alloc_err:
talk_status = TALK_STATUS_ERR_ALLOC;
- if (index_handle > 0)
- index_handle = core_free(index_handle);
+ index_handle = core_free(index_handle);
return false;
}
+static inline int load_voicefile_failure(int fd)
+{
+ if (fd >= 0)
+ close(fd);
+ return -1;
+}
+
/* load the voice file into the mp3 buffer */
static bool load_voicefile_index(int fd)
{
@@ -616,8 +626,7 @@ static bool load_voicefile_data(int fd)
/* just allocate, populate on an as-needed basis later
* re-create the clip buffer to ensure clip_ctx is up-to-date */
- if (talk_handle > 0)
- talk_handle = core_free(talk_handle);
+ talk_handle = core_free(talk_handle);
if (!create_clip_buffer(voicebuf_size))
return false;
@@ -716,6 +725,102 @@ static void mp3_callback(const void** start, size_t* size)
talk_queue_unlock();
}
+/***************** Private routines *****************/
+
+/* return if a voice codec is required or not */
+static bool talk_voice_required(void)
+{
+ return (has_voicefile) /* Voice file is available */
+ || (global_settings.talk_dir_clip) /* Thumbnail clips are required */
+ || (global_settings.talk_file_clip);
+}
+
+static bool talk_is_disabled(void)
+{
+ if (talk_temp_disable_count > 0 || (!check_audio_status()))
+ return true;
+ return false;
+}
+
+static void do_enqueue(bool enqueue)
+{
+ if (!enqueue)
+ talk_shutup(); /* cut off all the pending stuff */
+}
+
+/* spell a string */
+static int _talk_spell(const char* spell, size_t len, bool enqueue)
+{
+ char c; /* currently processed char */
+
+ if (talk_is_disabled())
+ return -1;
+
+ do_enqueue(enqueue); /* cut off all the pending stuff */
+
+ size_t len0 = len - 1;
+
+ while ((c = *spell++) != '\0' && len0-- < len)
+ {
+ /* if this grows into too many cases, I should use a table */
+ if (c >= 'A' && c <= 'Z')
+ talk_id(VOICE_CHAR_A + c - 'A', true);
+ else if (c >= 'a' && c <= 'z')
+ talk_id(VOICE_CHAR_A + c - 'a', true);
+ else if (c >= '0' && c <= '9')
+ talk_id(VOICE_ZERO + c - '0', true);
+ else if (c == '-')
+ talk_id(VOICE_MINUS, true);
+ else if (c == '+')
+ talk_id(VOICE_PLUS, true);
+ else if (c == '.')
+ talk_id(VOICE_DOT, true);
+ else if (c == ' ')
+ talk_id(VOICE_PAUSE, true);
+ else if (c == '/')
+ talk_id(VOICE_CHAR_SLASH, true);
+
+ while (QUEUE_LEVEL == QUEUE_SIZE - 1) /* queue full - busy loop */
+ yield();
+ }
+ return 0;
+}
+
+static int talk_spell_basename(const char *path,
+ const long *prefix_ids, bool enqueue)
+{
+ if(prefix_ids)
+ {
+ talk_idarray(prefix_ids, enqueue);
+ enqueue = true;
+ }
+ const char *basename;
+ size_t len = path_basename(path, &basename);
+
+ return _talk_spell(basename, len, enqueue);
+}
+
+/* Say year like "nineteen ninety nine" instead of "one thousand 9
+ hundred ninety nine". */
+static int talk_year(long year, bool enqueue)
+{
+ int rem;
+ if(year < 1100 || (year >=2000 && year < 2100))
+ /* just say it as a regular number */
+ return talk_number(year, enqueue);
+ /* Say century */
+ talk_number(year/100, enqueue);
+ rem = year%100;
+ if(rem == 0)
+ /* as in 1900 */
+ return talk_id(VOICE_HUNDRED, true);
+ if(rem <10)
+ /* as in 1905 */
+ talk_id(VOICE_ZERO, true);
+ /* sub-century year */
+ return talk_number(rem, true);
+}
+
/***************** Public routines *****************/
/* stop the playback and the pending clips */
@@ -743,8 +848,8 @@ static void queue_clip(struct queue_entry *clip, bool enqueue)
struct queue_entry *qe;
int queue_level;
- if (!enqueue)
- talk_shutup(); /* cut off all the pending stuff */
+ do_enqueue(enqueue); /* cut off all the pending stuff */
+
/* Something is being enqueued, force_enqueue_next override is no
longer in effect. */
force_enqueue_next = false;
@@ -778,14 +883,6 @@ static void queue_clip(struct queue_entry *clip, bool enqueue)
return;
}
-/* return if a voice codec is required or not */
-static bool talk_voice_required(void)
-{
- return (has_voicefile) /* Voice file is available */
- || (global_settings.talk_dir_clip) /* Thumbnail clips are required */
- || (global_settings.talk_file_clip);
-}
-
/***************** Public implementation *****************/
void talk_init(void)
@@ -807,8 +904,8 @@ void talk_init(void)
talk_force_shutup(); /* In case we have something speaking! */
talk_initialized = true;
- strlcpy((char *)last_lang, (char *)global_settings.lang_file,
- MAX_FILENAME);
+ strmemccpy((char *)last_lang, (char *)global_settings.lang_file,
+ MAX_FILENAME);
/* reset some states */
queue_write = queue_read = 0; /* reset the queue */
@@ -818,13 +915,13 @@ void talk_init(void)
voicefile_size = has_voicefile = 0;
/* need to free these as their size depends on the voice file, and
* this function is called when the talk voice file changes */
- if (index_handle > 0) index_handle = core_free(index_handle);
- if (talk_handle > 0) talk_handle = core_free(talk_handle);
+ index_handle = core_free(index_handle);
+ talk_handle = core_free(talk_handle);
/* don't free thumb handle, it doesn't depend on the actual voice file
* and so we can re-use it if it's already allocated in any event */
filehandle = open_voicefile();
- if (filehandle > -1)
+ if (filehandle >= 0)
{
if (!load_voicefile_index(filehandle))
{
@@ -875,12 +972,14 @@ void talk_init(void)
voice_thread_init();
out:
- close(filehandle); /* close again, this was just to detect presence */
+ if (filehandle >= 0)
+ close(filehandle); /* close again, this was just to detect presence */
}
/* somebody else claims the mp3 buffer, e.g. for regular play/record */
void talk_buffer_set_policy(int policy)
{
+#ifdef DEBUG
switch(policy)
{
case TALK_BUFFER_DEFAULT:
@@ -888,6 +987,9 @@ void talk_buffer_set_policy(int policy)
case TALK_BUFFER_LOOSE: give_buffer_away = true; break;
default: DEBUGF("Ignoring unknown policy\n"); break;
}
+#else
+ give_buffer_away = (policy == TALK_BUFFER_LOOSE);
+#endif
}
/* play a voice ID from voicefile */
@@ -899,17 +1001,15 @@ int talk_id(int32_t id, bool enqueue)
bool isloaded = true;
if (!has_voicefile)
- return 0; /* no voicefile loaded, not an error -> pretent success */
- if (talk_temp_disable_count > 0)
- return -1; /* talking has been disabled */
- if (!check_audio_status())
+ return 0; /* no voicefile loaded, not an error -> pretend success */
+ if (talk_is_disabled())
return -1;
if (talk_handle <= 0 || index_handle <= 0) /* reload needed? */
{
int fd = open_voicefile();
if (fd < 0 || !load_voicefile_index(fd))
- return -1;
+ return load_voicefile_failure(fd);
isloaded = load_voicefile_data(fd);
close(fd);
}
@@ -944,6 +1044,7 @@ int talk_id(int32_t id, bool enqueue)
return 0;
}
+
/* Speaks zero or more IDs (from an array). */
int talk_idarray(const long *ids, bool enqueue)
{
@@ -973,25 +1074,22 @@ static int _talk_file(const char* filename,
{
int fd;
int size;
- int handle, oldest = -1;
+ int handle = -1;
+ int oldest = -1;
/* reload needed? */
- if (talk_temp_disable_count > 0)
- return -1; /* talking has been disabled */
- if (!check_audio_status())
+ if (talk_is_disabled())
return -1;
if (talk_handle <= 0 || index_handle <= 0)
{
- int fd = open_voicefile();
+ fd = open_voicefile();
if (fd < 0 || !load_voicefile_index(fd))
- return -1;
+ return load_voicefile_failure(fd);
load_voicefile_data(fd);
close(fd);
}
- if (!enqueue)
- /* shutup now to free the thumbnail buffer */
- talk_shutup();
+ do_enqueue(enqueue); /* shutup now to free the thumbnail buffer */
fd = open(filename, O_RDONLY);
if (fd < 0) /* failed to open */
@@ -1000,11 +1098,15 @@ static int _talk_file(const char* filename,
}
size = filesize(fd);
- /* free clips from cache until this one succeeds to allocate */
- while ((handle = buflib_alloc(&clip_ctx, size)) < 0)
- oldest = free_oldest_clip();
+ if (size > 0)
+ {
+ /* free clips from cache until this one succeeds to allocate */
+ while ((handle = buflib_alloc(&clip_ctx, size)) < 0)
+ oldest = free_oldest_clip();
+
+ size = read_to_handle_ex(fd, &clip_ctx, handle, 0, size);
+ }
- size = read_to_handle_ex(fd, &clip_ctx, handle, 0, size);
close(fd);
/* ToDo: find audio, skip ID headers and trailers */
@@ -1036,38 +1138,18 @@ int talk_file(const char *root, const char *dir, const char *file,
/* Play a thumbnail file */
{
char buf[MAX_PATH];
+ const char *fmt = "%s%s%s%s%s";
/* Does root end with a slash */
- char *slash = (root && root[0]
- && root[strlen(root)-1] != '/') ? "/" : "";
- snprintf(buf, MAX_PATH, "%s%s%s%s%s%s",
- root ? root : "", slash,
+ if(root && root[0] && root[strlen(root)-1] != '/')
+ fmt = "%s/%s%s%s%s";
+ snprintf(buf, MAX_PATH, fmt,
+ root ? root : "",
dir ? dir : "", dir ? "/" : "",
file ? file : "",
ext ? ext : "");
return _talk_file(buf, prefix_ids, enqueue);
}
-static int talk_spell_basename(const char *path,
- const long *prefix_ids, bool enqueue)
-{
- if(prefix_ids)
- {
- talk_idarray(prefix_ids, enqueue);
- enqueue = true;
- }
- char buf[MAX_PATH];
- /* Spell only the path component after the last slash */
- strlcpy(buf, path, sizeof(buf));
- if(strlen(buf) >1 && buf[strlen(buf)-1] == '/')
- /* strip trailing slash */
- buf[strlen(buf)-1] = '\0';
- char *ptr = strrchr(buf, '/');
- if(ptr && strlen(buf) >1)
- ++ptr;
- else ptr = buf;
- return talk_spell(ptr, enqueue);
-}
-
/* Play a file's .talk thumbnail, fallback to spelling the filename, or
go straight to spelling depending on settings. */
int talk_file_or_spell(const char *dirname, const char *filename,
@@ -1079,7 +1161,7 @@ int talk_file_or_spell(const char *dirname, const char *filename,
prefix_ids, enqueue) >0)
return 0;
}
- if (global_settings.talk_file == 2)
+ if (global_settings.talk_file == TALK_SPEAK_SPELL)
/* Either .talk clips are disabled, or as a fallback */
return talk_spell_basename(filename, prefix_ids, enqueue);
return 0;
@@ -1096,7 +1178,7 @@ int talk_dir_or_spell(const char* dirname,
prefix_ids, enqueue) >0)
return 0;
}
- if (global_settings.talk_dir == 2)
+ if (global_settings.talk_dir == TALK_SPEAK_SPELL)
/* Either .talk clips disabled or as a fallback */
return talk_spell_basename(dirname, prefix_ids, enqueue);
return 0;
@@ -1106,14 +1188,14 @@ int talk_dir_or_spell(const char* dirname,
back or going straight to spelling depending on settings. */
int talk_fullpath(const char* path, bool enqueue)
{
- if (!enqueue)
- talk_shutup();
+ do_enqueue(enqueue); /* cut off all the pending stuff */
+
if(path[0] != '/')
/* path ought to start with /... */
return talk_spell(path, true);
talk_id(VOICE_CHAR_SLASH, true);
char buf[MAX_PATH];
- strlcpy(buf, path, MAX_PATH);
+ strmemccpy(buf, path, MAX_PATH);
char *start = buf+1; /* start of current component */
char *ptr = strchr(start, '/'); /* end of current component */
while(ptr) { /* There are more slashes ahead */
@@ -1136,13 +1218,10 @@ int talk_number(long n, bool enqueue)
int level = 2; /* mille count */
long mil = 1000000000; /* highest possible "-illion" */
- if (talk_temp_disable_count > 0)
- return -1; /* talking has been disabled */
- if (!check_audio_status())
+ if (talk_is_disabled())
return -1;
- if (!enqueue)
- talk_shutup(); /* cut off all the pending stuff */
+ do_enqueue(enqueue); /* cut off all the pending stuff */
if (n==0)
{ /* special case */
@@ -1158,6 +1237,11 @@ int talk_number(long n, bool enqueue)
while (n)
{
+ if (mil < 1)
+ {
+ talk_id(VOICE_CHAR_E, true);
+ return 0;
+ }
int segment = n / mil; /* extract in groups of 3 digits */
n -= segment * mil; /* remove the used digits from number */
mil /= 1000; /* digit place for next round */
@@ -1173,17 +1257,44 @@ int talk_number(long n, bool enqueue)
talk_id(VOICE_HUNDRED, true);
}
- /* combination indexing */
- if (ones > 20)
+ struct queue_entry tens_swap;
+ if (get_clip(VOICE_NUMERIC_TENS_SWAP_SEPARATOR, &tens_swap) >= 0)
{
- int tens = ones/10 + 18;
- talk_id(VOICE_ZERO + tens, true);
- ones %= 10;
+ /* direct indexing */
+ if (ones <= 20)
+ {
+ talk_id(VOICE_ZERO + ones, true);
+ }
+ else if (ones)
+ {
+ int tmp = ones % 10;
+ if (tmp)
+ {
+ talk_id(VOICE_ZERO + tmp, true);
+ talk_id(VOICE_NUMERIC_TENS_SWAP_SEPARATOR, true);
+ }
+ }
+ /* combination indexing */
+ if (ones > 20)
+ {
+ int tens = ones/10 + 18;
+ talk_id(VOICE_ZERO + tens, true);
+ }
}
+ else
+ {
+ /* combination indexing */
+ if (ones > 20)
+ {
+ int tens = ones/10 + 18;
+ talk_id(VOICE_ZERO + tens, true);
+ ones %= 10;
+ }
- /* direct indexing */
- if (ones)
- talk_id(VOICE_ZERO + ones, true);
+ /* direct indexing */
+ if (ones)
+ talk_id(VOICE_ZERO + ones, true);
+ }
/* add billion, million, thousand */
if (mil)
@@ -1195,27 +1306,6 @@ int talk_number(long n, bool enqueue)
return 0;
}
-/* Say year like "nineteen ninety nine" instead of "one thousand 9
- hundred ninety nine". */
-static int talk_year(long year, bool enqueue)
-{
- int rem;
- if(year < 1100 || year >=2000)
- /* just say it as a regular number */
- return talk_number(year, enqueue);
- /* Say century */
- talk_number(year/100, enqueue);
- rem = year%100;
- if(rem == 0)
- /* as in 1900 */
- return talk_id(VOICE_HUNDRED, true);
- if(rem <10)
- /* as in 1905 */
- talk_id(VOICE_ZERO, true);
- /* sub-century year */
- return talk_number(rem, true);
-}
-
/* Say time duration/interval. Input is time in seconds,
say hours,minutes,seconds. */
static int talk_time_unit(long secs, bool enqueue)
@@ -1288,9 +1378,7 @@ int talk_value_decimal(long n, int unit, int decimals, bool enqueue)
char tbuf[8];
char fmt[] = "%0nd";
- if (talk_temp_disable_count > 0)
- return -1; /* talking has been disabled */
- if (!check_audio_status())
+ if (talk_is_disabled())
return -1;
/* special pronounciation for year number */
@@ -1335,34 +1423,40 @@ int talk_value_decimal(long n, int unit, int decimals, bool enqueue)
return 0;
}
+ if (lang_units_first())
+ talk_id(unit_id, true); /* say the unit, if any */
talk_number(n, enqueue); /* say the number */
- talk_id(unit_id, true); /* say the unit, if any */
+ if (!lang_units_first())
+ talk_id(unit_id, true); /* say the unit, if any */
return 0;
}
+static inline void talk_time_value(long n, int unit, bool enqueue)
+{
+ if (n != 0)
+ talk_value_decimal(n, unit, 0, enqueue);
+}
+
/* Say time duration/interval. Input is time unit specifies base unit,
say hours,minutes,seconds, milliseconds. or any combination thereof */
int talk_time_intervals(long time, int unit_idx, bool enqueue)
{
unsigned long units_in[UNIT_IDX_TIME_COUNT];
- if (talk_temp_disable_count > 0)
- return -1; /* talking has been disabled */
- if (!check_audio_status())
+ if (talk_is_disabled())
return -1;
if (talk_handle <= 0 || index_handle <= 0) /* reload needed? */
{
int fd = open_voicefile();
if (fd < 0 || !load_voicefile_index(fd))
- return -1;
+ return load_voicefile_failure(fd);
load_voicefile_data(fd);
close(fd);
}
- if (!enqueue)
- talk_shutup(); /* cut off all the pending stuff */
+ do_enqueue(enqueue); /* cut off all the pending stuff */
time_split_units(unit_idx, labs(time), &units_in);
@@ -1373,22 +1467,10 @@ int talk_time_intervals(long time, int unit_idx, bool enqueue)
talk_value(0, unit_idx, true);
else
{
- if (units_in[UNIT_IDX_HR] != 0)
- {
- talk_value(units_in[UNIT_IDX_HR], UNIT_HOUR, true);
- }
- if (units_in[UNIT_IDX_MIN] != 0)
- {
- talk_value(units_in[UNIT_IDX_MIN], UNIT_MIN, true);
- }
- if (units_in[UNIT_IDX_SEC] != 0)
- {
- talk_value(units_in[UNIT_IDX_SEC], UNIT_SEC, true);
- }
- if (units_in[UNIT_IDX_MS] != 0)
- {
- talk_value(units_in[UNIT_IDX_MS], UNIT_MS, true);
- }
+ talk_time_value(units_in[UNIT_IDX_HR], UNIT_HOUR, true);
+ talk_time_value(units_in[UNIT_IDX_MIN], UNIT_MIN, true);
+ talk_time_value(units_in[UNIT_IDX_SEC], UNIT_SEC, true);
+ talk_time_value(units_in[UNIT_IDX_MS], UNIT_MS, true);
}
return -1;
@@ -1397,38 +1479,7 @@ int talk_time_intervals(long time, int unit_idx, bool enqueue)
/* spell a string */
int talk_spell(const char* spell, bool enqueue)
{
- char c; /* currently processed char */
-
- if (talk_temp_disable_count > 0)
- return -1; /* talking has been disabled */
- if (!check_audio_status())
- return -1;
-
- if (!enqueue)
- talk_shutup(); /* cut off all the pending stuff */
-
- while ((c = *spell++) != '\0')
- {
- /* if this grows into too many cases, I should use a table */
- if (c >= 'A' && c <= 'Z')
- talk_id(VOICE_CHAR_A + c - 'A', true);
- else if (c >= 'a' && c <= 'z')
- talk_id(VOICE_CHAR_A + c - 'a', true);
- else if (c >= '0' && c <= '9')
- talk_id(VOICE_ZERO + c - '0', true);
- else if (c == '-')
- talk_id(VOICE_MINUS, true);
- else if (c == '+')
- talk_id(VOICE_PLUS, true);
- else if (c == '.')
- talk_id(VOICE_DOT, true);
- else if (c == ' ')
- talk_id(VOICE_PAUSE, true);
- else if (c == '/')
- talk_id(VOICE_CHAR_SLASH, true);
- }
-
- return 0;
+ return _talk_spell(spell, NULL_TERMINATED, enqueue);
}
void talk_disable(bool disable)
@@ -1444,19 +1495,38 @@ void talk_setting(const void *global_settings_variable)
const struct settings_list *setting;
if (!global_settings.talk_menu)
return;
- setting = find_setting(global_settings_variable, NULL);
+ setting = find_setting(global_settings_variable);
if (setting == NULL)
return;
if (setting->lang_id)
talk_id(setting->lang_id,false);
}
-
void talk_date(const struct tm *tm, bool enqueue)
{
- talk_id(LANG_MONTH_JANUARY + tm->tm_mon, enqueue);
- talk_number(tm->tm_mday, true);
- talk_number(1900 + tm->tm_year, true);
+ const char *format = str(LANG_VOICED_DATE_FORMAT);
+ const char *ptr;
+
+ do_enqueue(enqueue); /* cut off all the pending stuff */
+
+ for (ptr = format ; *ptr ; ptr++) {
+ switch(*ptr) {
+ case 'Y':
+ talk_number(1900 + tm->tm_year, true);
+ break;
+ case 'A':
+ talk_id(LANG_MONTH_JANUARY + tm->tm_mon, true);
+ break;
+ case 'm':
+ talk_number(tm->tm_mon + 1, true);
+ break;
+ case 'd':
+ talk_number(tm->tm_mday, true);
+ break;
+ default:
+ break;
+ }
+ }
}
void talk_time(const struct tm *tm, bool enqueue)
@@ -1493,9 +1563,13 @@ void talk_time(const struct tm *tm, bool enqueue)
else
{
/* Voice the time in 24 hour format */
+ if(tm->tm_hour < 10)
+ talk_id(VOICE_OH, true);
talk_number(tm->tm_hour, enqueue);
if (tm->tm_min == 0)
- talk_ids(true, VOICE_HUNDRED, VOICE_HOUR);
+ {
+ talk_ids(true, VOICE_HUNDRED, VOICE_HOURS);
+ }
else
{
/* Pronounce the leading 0 */
@@ -1513,8 +1587,7 @@ void talk_announce_voice_invalid(void)
int buf_handle;
struct queue_entry qe;
- const char talkfile[] =
- LANG_DIR "/InvalidVoice_" DEFAULT_VOICE_LANG ".talk";
+ const char talkfile[] = LANG_DIR "/InvalidVoice_" DEFAULT_VOICE_LANG ".talk";
if (global_settings.talk_menu && talk_status != TALK_STATUS_OK)
{
@@ -1522,21 +1595,21 @@ void talk_announce_voice_invalid(void)
voice_fd = open(talkfile, O_RDONLY);
if (voice_fd < 0)
- return; /* can't open */
+ goto out; /* can't open */
voice_sz= lseek(voice_fd, 0, SEEK_END);
if (voice_sz == 0 || voice_sz > (64<<10))
- return; /* nothing here or too big */
+ goto out; /* nothing here or too big */
lseek(voice_fd, 0, SEEK_SET);
/* add a bit extra for buflib overhead (2K) */
if (!create_clip_buffer(ALIGN_UP(voice_sz, sizeof(long)) + (2<<10)))
- return;
+ goto out;
mutex_lock(&read_buffer_mutex);
buf_handle = buflib_alloc(&clip_ctx, ALIGN_UP(voice_sz, sizeof(long)));
if (buf_handle < 0)
- return;
+ goto out;
if (read_to_handle_ex(voice_fd, &clip_ctx, buf_handle, 0, voice_sz) > 0)
{
@@ -1549,10 +1622,12 @@ void talk_announce_voice_invalid(void)
}
mutex_unlock(&read_buffer_mutex);
- close(voice_fd);
buf_handle = buflib_free(&clip_ctx, buf_handle);
talk_handle = core_free(talk_handle);
+ out:
+ close(voice_fd);
+ return;
}
}
@@ -1568,7 +1643,7 @@ bool talk_get_debug_data(struct talk_debug_data *data)
if (global_settings.lang_file[0] && global_settings.lang_file[0] != 0xff)
p_lang = (char *)global_settings.lang_file;
- strlcpy(data->voicefile, p_lang, sizeof(data->voicefile));
+ strmemccpy(data->voicefile, p_lang, sizeof(data->voicefile));
if (!has_voicefile || index_handle <= 0)
{
@@ -1596,6 +1671,14 @@ bool talk_get_debug_data(struct talk_debug_data *data)
data->max_clipsize = size;
data->avg_clipsize += size;
}
+ if (!(real_clips > 0))
+ {
+ if (data->status == TALK_STATUS_OK)
+ data->status = TALK_STATUS_ERR_NOFILE;
+
+ return false;
+ }
+
cc = buflib_get_data(&clip_ctx, metadata_table_handle);
for (int i = 0; i < (int) max_clips; i++)
{
diff --git a/apps/talk.h b/apps/talk.h
index b4aa344916..6139b9ec46 100644
--- a/apps/talk.h
+++ b/apps/talk.h
@@ -64,19 +64,26 @@ enum talk_status {
TALK_STATUS_ERR_INCOMPATIBLE
};
+enum talk_speakmode {
+ /* voice mode: 0=off, 1=number, 2=spell */
+ TALK_SPEAK_OFF = 0,
+ TALK_SPEAK_NUMBER,
+ TALK_SPEAK_SPELL
+};
+
#define UNIT_SHIFT (32-5) /* this many bits left from UNIT_xx enum */
#define DECIMAL_SHIFT (32 - 8)
/* make a "talkable" ID from number + unit
unit is upper 4 bits, number the remaining (in regular 2's complement) */
-#define TALK_ID(n,u) (((long)(u))<<UNIT_SHIFT | ((n) & ~(-1L<<DECIMAL_SHIFT)))
+#define TALK_ID(n,u) (((long)(u))<<UNIT_SHIFT | ((n) & ~(((unsigned int)-1L)<<DECIMAL_SHIFT)))
/* make a "talkable" ID from a decimal number + unit, the decimal number
is represented like x*10^d where d is the number of decimal digits */
#define TALK_ID_DECIMAL(n,d,u) (((long)(u))<<UNIT_SHIFT |\
((long)(d))<<DECIMAL_SHIFT |\
- ((n) & ~(-1L<<DECIMAL_SHIFT)))
+ ((n) & ~(((unsigned int)-1L)<<DECIMAL_SHIFT)))
/* convenience macro to have both virtual pointer and ID as arguments */
#define STR(id) ID2P(id), id
diff --git a/apps/tree.c b/apps/tree.c
index 88ccff5e37..ea2ef23e71 100644
--- a/apps/tree.c
+++ b/apps/tree.c
@@ -72,14 +72,12 @@
#include "list.h"
#include "splash.h"
#include "quickscreen.h"
+#include "shortcuts.h"
#include "appevents.h"
#include "root_menu.h"
-static const struct filetype *filetypes;
-static int filetypes_count;
-
-struct gui_synclist tree_lists;
+static struct gui_synclist tree_lists;
/* I put it here because other files doesn't use it yet,
* but should be elsewhere since it will be used mostly everywhere */
@@ -88,7 +86,7 @@ static struct tree_context tc;
char lastfile[MAX_PATH];
static char lastdir[MAX_PATH];
#ifdef HAVE_TAGCACHE
-static int lasttable, lastextra, lastfirstpos;
+static int lasttable, lastextra;
#endif
static bool reload_dir = false;
@@ -128,7 +126,7 @@ static const char* tree_get_filename(int selected_item, void *data,
{
return tagtree_get_entry_name(&tc, selected_item, buffer, buffer_len);
}
- else
+ else
#endif
{
struct entry *entry = tree_get_entry_at(local_tc, selected_item);
@@ -137,7 +135,7 @@ static const char* tree_get_filename(int selected_item, void *data,
name = entry->name;
attr = entry->attr;
}
-
+
if(!(attr & ATTR_DIRECTORY))
{
switch(global_settings.show_filename_ext)
@@ -247,6 +245,7 @@ static int tree_voice_cb(int selected_item, void * data)
did_clip = false;
}
}
+ bool spell_name = false;
if(!did_clip)
{
/* say the number or spell if required or as a fallback */
@@ -256,9 +255,6 @@ static int tree_voice_cb(int selected_item, void * data)
talk_id(is_dir ? VOICE_DIR : VOICE_FILE, false);
talk_number(selected_item+1 - (is_dir ? 0 : local_tc->dirsindir),
true);
- if(global_settings.talk_filetype
- && !is_dir && *local_tc->dirfilter < NUM_FILTER_MODES)
- say_filetype(attr);
break;
case 2: /* spelled */
talk_shutup();
@@ -266,13 +262,22 @@ static int tree_voice_cb(int selected_item, void * data)
{
if(is_dir)
talk_id(VOICE_DIR, true);
- else if(*local_tc->dirfilter < NUM_FILTER_MODES)
- say_filetype(attr);
}
- talk_spell(name, true);
+ spell_name = true;
break;
}
}
+
+ if(global_settings.talk_filetype && !is_dir
+ && *local_tc->dirfilter < NUM_FILTER_MODES)
+ {
+ say_filetype(attr);
+ }
+
+ /* spell name AFTER voicing filetype */
+ if (spell_name)
+ talk_spell(name, true);
+
return 0;
}
@@ -293,19 +298,10 @@ bool check_rockboxdir(void)
}
/* do this really late in the init sequence */
-void tree_gui_init(void)
+void tree_init(void)
{
check_rockboxdir();
-
strcpy(tc.currdir, "/");
-
- gui_synclist_init(&tree_lists, &tree_get_filename, &tc, false, 1, NULL);
- gui_synclist_set_voice_callback(&tree_lists, tree_voice_cb);
- gui_synclist_set_icon_callback(&tree_lists,
- global_settings.show_icons?&tree_get_fileicon:NULL);
-#ifdef HAVE_LCD_COLOR
- gui_synclist_set_color_callback(&tree_lists, &tree_get_filecolor);
-#endif
}
@@ -314,6 +310,18 @@ struct tree_context* tree_get_context(void)
return &tc;
}
+void tree_lock_cache(struct tree_context *t)
+{
+ core_pin(t->cache.name_buffer_handle);
+ core_pin(t->cache.entries_handle);
+}
+
+void tree_unlock_cache(struct tree_context *t)
+{
+ core_unpin(t->cache.name_buffer_handle);
+ core_unpin(t->cache.entries_handle);
+}
+
/*
* Returns the position of a given file in the current directory
* returns -1 if not found
@@ -344,14 +352,24 @@ static int tree_get_file_position(char * filename)
*/
static int update_dir(void)
{
+ struct gui_synclist * const list = &tree_lists;
+ int show_path_in_browser = global_settings.show_path_in_browser;
bool changed = false;
+
+ const char* title = NULL;/* Must clear the title as the list is reused */
+ int icon = NOICON;
+
#ifdef HAVE_TAGCACHE
bool id3db = *tc.dirfilter == SHOW_ID3DB;
+#else
+ const bool id3db = false;
+#endif
+
+#ifdef HAVE_TAGCACHE
/* Checks for changes */
if (id3db) {
if (tc.currtable != lasttable ||
tc.currextra != lastextra ||
- tc.firstpos != lastfirstpos ||
reload_dir)
{
if (tagtree_load(&tc) < 0)
@@ -359,11 +377,10 @@ static int update_dir(void)
lasttable = tc.currtable;
lastextra = tc.currextra;
- lastfirstpos = tc.firstpos;
changed = true;
}
}
- else
+ else
#endif
{
tc.sort_dir = global_settings.sort_dir;
@@ -372,15 +389,16 @@ static int update_dir(void)
{
if (ft_load(&tc, NULL) < 0)
return -1;
- strcpy(lastdir, tc.currdir);
+ strmemccpy(lastdir, tc.currdir, MAX_PATH);
changed = true;
}
}
/* if selected item is undefined */
if (tc.selected_item == -1)
{
- /* use lastfile to determine the selected item */
- tc.selected_item = tree_get_file_position(lastfile);
+ if (!id3db)
+ /* use lastfile to determine the selected item */
+ tc.selected_item = tree_get_file_position(lastfile);
/* If the file doesn't exists, select the first one (default) */
if(tc.selected_item < 0)
@@ -389,28 +407,22 @@ static int update_dir(void)
}
if (changed)
{
- if(
-#ifdef HAVE_TAGCACHE
- !id3db &&
-#endif
- tc.dirfull )
+ if( !id3db && tc.dirfull )
{
splash(HZ, ID2P(LANG_SHOWDIR_BUFFER_FULL));
}
}
+
+ gui_synclist_init(list, &tree_get_filename, &tc, false, 1, NULL);
+
#ifdef HAVE_TAGCACHE
if (id3db)
{
- if (global_settings.show_path_in_browser == SHOW_PATH_FULL
- || global_settings.show_path_in_browser == SHOW_PATH_CURRENT)
+ if (show_path_in_browser == SHOW_PATH_FULL
+ || show_path_in_browser == SHOW_PATH_CURRENT)
{
- gui_synclist_set_title(&tree_lists, tagtree_get_title(&tc),
- filetype_get_icon(ATTR_DIRECTORY));
- }
- else
- {
- /* Must clear the title as the list is reused */
- gui_synclist_set_title(&tree_lists, NULL, NOICON);
+ title = tagtree_get_title(&tc);
+ icon = filetype_get_icon(ATTR_DIRECTORY);
}
}
else
@@ -418,45 +430,52 @@ static int update_dir(void)
{
if (tc.browse && tc.browse->title)
{
- int icon = tc.browse->icon;
+ title = tc.browse->title;
+ icon = tc.browse->icon;
if (icon == NOICON)
icon = filetype_get_icon(ATTR_DIRECTORY);
- gui_synclist_set_title(&tree_lists, tc.browse->title, icon);
- }
- else if (global_settings.show_path_in_browser == SHOW_PATH_FULL)
- {
- gui_synclist_set_title(&tree_lists, tc.currdir,
- filetype_get_icon(ATTR_DIRECTORY));
}
- else if (global_settings.show_path_in_browser == SHOW_PATH_CURRENT)
+ else
{
- char *title = strrchr(tc.currdir, '/') + 1;
- if (*title == '\0')
+ if (show_path_in_browser == SHOW_PATH_FULL)
{
- /* Display "Files" for the root dir */
- gui_synclist_set_title(&tree_lists, str(LANG_DIR_BROWSER),
- filetype_get_icon(ATTR_DIRECTORY));
+ title = tc.currdir;
+ icon = filetype_get_icon(ATTR_DIRECTORY);
}
- else
- gui_synclist_set_title(&tree_lists, title,
- filetype_get_icon(ATTR_DIRECTORY));
- }
- else
- {
- /* Must clear the title as the list is reused */
- gui_synclist_set_title(&tree_lists, NULL, NOICON);
+ else if (show_path_in_browser == SHOW_PATH_CURRENT)
+ {
+ title = strrchr(tc.currdir, '/');
+ if (title != NULL)
+ {
+ title++; /* step past the separator */
+ if (*title == '\0')
+ {
+ /* Display "Files" for the root dir */
+ title = str(LANG_DIR_BROWSER);
+ }
+ icon = filetype_get_icon(ATTR_DIRECTORY);
+ }
+ }
}
}
- gui_synclist_set_nb_items(&tree_lists, tc.filesindir);
- gui_synclist_set_icon_callback(&tree_lists,
- global_settings.show_icons?tree_get_fileicon:NULL);
+ /* set title and icon, if nothing is set, clear the title
+ * with NULL and icon as NOICON as the list is reused */
+ gui_synclist_set_title(list, title, icon);
+
+ gui_synclist_set_nb_items(list, tc.filesindir);
+ gui_synclist_set_icon_callback(list,
+ global_settings.show_icons?tree_get_fileicon:NULL);
+ gui_synclist_set_voice_callback(list, &tree_voice_cb);
+#ifdef HAVE_LCD_COLOR
+ gui_synclist_set_color_callback(list, &tree_get_filecolor);
+#endif
if( tc.selected_item >= tc.filesindir)
tc.selected_item=tc.filesindir-1;
- gui_synclist_select_item(&tree_lists, tc.selected_item);
- gui_synclist_draw(&tree_lists);
- gui_synclist_speak_item(&tree_lists);
+ gui_synclist_select_item(list, tc.selected_item);
+ gui_synclist_draw(list);
+ gui_synclist_speak_item(list);
return tc.filesindir;
}
@@ -467,14 +486,14 @@ void resume_directory(const char *dir)
int ret;
#ifdef HAVE_TAGCACHE
bool id3db = *tc.dirfilter == SHOW_ID3DB;
+#else
+ const bool id3db = false;
#endif
/* make sure the dirfilter is sane. The only time it should be possible
* thats its not is when resume playlist is called from a plugin
*/
-#ifdef HAVE_TAGCACHE
if (!id3db)
-#endif
- *tc.dirfilter = global_settings.dirfilter;
+ *tc.dirfilter = global_settings.dirfilter;
ret = ft_load(&tc, dir);
*tc.dirfilter = dirfilter;
if (ret < 0)
@@ -497,10 +516,10 @@ char *getcwd(char *buf, getcwd_size_t size)
return tc.currdir;
else if (size)
{
- if ((getcwd_size_t)strlcpy(buf, tc.currdir, size) < size)
+ if (strmemccpy(buf, tc.currdir, size) != NULL)
return buf;
}
- /* size == 0, or truncation in strlcpy */
+ /* size == 0, or truncation in strmemccpy */
return NULL;
}
@@ -534,17 +553,16 @@ char* get_current_file(char* buffer, size_t buffer_len)
return NULL;
}
-/* Allow apps to change our dirfilter directly (required for sub browsers)
+/* Allow apps to change our dirfilter directly (required for sub browsers)
if they're suddenly going to become a file browser for example */
void set_dirfilter(int l_dirfilter)
{
*tc.dirfilter = l_dirfilter;
}
-/* Selects a file and update tree context properly */
-void set_current_file(const char *path)
+/* Selects a path + file and update tree context properly */
+static void set_current_file_ex(const char *path, const char *filename)
{
- const char *name;
int i;
#ifdef HAVE_TAGCACHE
@@ -554,21 +572,37 @@ void set_current_file(const char *path)
return;
#endif
- /* separate directory from filename */
- /* gets the directory's name and put it into tc.currdir */
- name = strrchr(path+1,'/');
- if (name)
+ if (!filename) /* path and filename supplied combined */
{
- strlcpy(tc.currdir, path, name - path + 1);
- name++;
+ /* separate directory from filename */
+ /* gets the directory's name and put it into tc.currdir */
+ filename = strrchr(path+1,'/');
+ size_t endpos = filename - path;
+ if (endpos < MAX_PATH - 1)
+ {
+ strmemccpy(tc.currdir, path, endpos + 1);
+ filename++;
+ }
+ else
+ {
+ strcpy(tc.currdir, "/");
+ filename = path+1;
+ }
}
- else
+ else /* path and filename came in separate ensure an ending '/' */
{
- strcpy(tc.currdir, "/");
- name = path+1;
+ char *end_p = strmemccpy(tc.currdir, path, MAX_PATH);
+ size_t endpos = end_p - tc.currdir;
+ if (endpos < MAX_PATH)
+ {
+ if (tc.currdir[endpos - 2] != '/')
+ {
+ tc.currdir[endpos - 1] = '/';
+ tc.currdir[endpos] = '\0';
+ }
+ }
}
-
- strcpy(lastfile, name);
+ strmemccpy(lastfile, filename, MAX_PATH);
/* If we changed dir we must recalculate the dirlevel
@@ -591,16 +625,27 @@ void set_current_file(const char *path)
if (ft_load(&tc, NULL) >= 0)
{
tc.selected_item = tree_get_file_position(lastfile);
+ if (!tc.is_browsing && tc.out_of_tree == 0)
+ {
+ /* the browser is closed */
+ /* don't allow the previous items to overwrite what we just loaded */
+ tc.out_of_tree = tc.selected_item + 1;
+ }
}
}
+/* Selects a file and update tree context properly */
+void set_current_file(const char *path)
+{
+ set_current_file_ex(path, NULL);
+}
+
/* main loop, handles key events */
static int dirbrowse(void)
{
int numentries=0;
char buf[MAX_PATH];
- int len;
int button;
int oldbutton;
bool reload_root = false;
@@ -620,10 +665,8 @@ static int dirbrowse(void)
if (tc.selected_item < 0)
tc.selected_item = 0;
#ifdef HAVE_TAGCACHE
- tc.firstpos = 0;
lasttable = -1;
lastextra = -1;
- lastfirstpos = 0;
#endif
start_wps = false;
@@ -638,9 +681,7 @@ static int dirbrowse(void)
return GO_TO_PREVIOUS; /* No files found for rockbox_browse() */
}
- send_event(GUI_EVENT_ACTIONUPDATE, (void*)1); /* force a redraw */
- gui_synclist_draw(&tree_lists);
- while(1) {
+ while(tc.browse && tc.is_browsing) {
bool restore = false;
if (tc.dirlevel < 0)
tc.dirlevel = 0; /* shouldnt be needed.. this code needs work! */
@@ -649,7 +690,7 @@ static int dirbrowse(void)
button = get_action(CONTEXT_TREE|ALLOW_SOFTLOCK,
list_do_action_timeout(&tree_lists, HZ/2));
oldbutton = button;
- gui_synclist_do_button(&tree_lists, &button,LIST_WRAP_UNLESS_HELD);
+ gui_synclist_do_button(&tree_lists, &button);
tc.selected_item = gui_synclist_get_sel_pos(&tree_lists);
switch ( button ) {
case ACTION_STD_OK:
@@ -671,7 +712,7 @@ static int dirbrowse(void)
}
}
#ifdef HAVE_TAGCACHE
- switch (id3db?tagtree_enter(&tc):ft_enter(&tc))
+ switch (id3db ? tagtree_enter(&tc, true) : ft_enter(&tc))
#else
switch (ft_enter(&tc))
#endif
@@ -686,7 +727,8 @@ static int dirbrowse(void)
return GO_TO_FM;
#endif
case GO_TO_ROOT: exit_func = true; break;
- default: break;
+ default:
+ break;
}
restore = true;
break;
@@ -707,12 +749,12 @@ static int dirbrowse(void)
#ifdef HAVE_TAGCACHE
if (id3db)
- tagtree_exit(&tc);
+ tagtree_exit(&tc, true);
else
#endif
if (ft_exit(&tc) == 3)
exit_func = true;
-
+
restore = true;
break;
@@ -735,19 +777,40 @@ static int dirbrowse(void)
break;
#ifdef HAVE_QUICKSCREEN
case ACTION_STD_QUICKSCREEN:
- if (global_settings.shortcuts_replaces_qs)
- {
- if (*tc.dirfilter < NUM_FILTER_MODES)
- {
- global_status.last_screen = GO_TO_SHORTCUTMENU;
- return quick_screen_quick(button);
- }
+ {
+ bool enter_shortcuts_menu = global_settings.shortcuts_replaces_qs;
+ if (enter_shortcuts_menu && *tc.dirfilter >= NUM_FILTER_MODES)
break;
+ else if (!enter_shortcuts_menu)
+ {
+ int ret = quick_screen_quick(button);
+ if (ret == QUICKSCREEN_IN_USB)
+ reload_dir = true;
+ else if (ret == QUICKSCREEN_GOTO_SHORTCUTS_MENU)
+ enter_shortcuts_menu = true;
}
- else if (quick_screen_quick(button))
- reload_dir = true;
+
+ if (enter_shortcuts_menu && *tc.dirfilter < NUM_FILTER_MODES)
+ {
+ int last_screen = global_status.last_screen;
+ global_status.last_screen = GO_TO_SHORTCUTMENU;
+ int shortcut_ret = do_shortcut_menu(NULL);
+ if (shortcut_ret == GO_TO_PREVIOUS)
+ global_status.last_screen = last_screen;
+ else
+ return shortcut_ret;
+ }
+ else if (enter_shortcuts_menu) /* currently disabled */
+ {
+ /* QuickScreen defers skin updates, popping its activity, when
+ switching to Shortcuts Menu, so make up for that here: */
+ FOR_NB_SCREENS(i)
+ skin_update(CUSTOM_STATUSBAR, i, SKIN_REFRESH_ALL);
+ }
+
restore = true;
break;
+ }
#endif
#ifdef HAVE_HOTKEY
@@ -777,7 +840,12 @@ static int dirbrowse(void)
tagtree_get_filename(&tc, buf, sizeof(buf));
}
else
+ {
attr = ATTR_DIRECTORY;
+ tagtree_get_entry_name(&tc, tc.selected_item,
+ buf, sizeof(buf));
+ fix_path_part(buf, 0, sizeof(buf));
+ }
}
else
#endif
@@ -788,16 +856,8 @@ static int dirbrowse(void)
attr = entry->attr;
- if (currdir[1]) /* Not in / */
- {
- len = snprintf(buf, sizeof buf, "%s/%s",
- currdir, entry->name);
+ ft_assemble_path(buf, sizeof(buf), currdir, entry->name);
- if ((unsigned) len > sizeof(buf))
- splash(HZ, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
- }
- else /* In / */
- snprintf(buf, sizeof buf, "/%s", entry->name);
}
onplay_result = onplay(buf, attr, curr_context, hotkey);
}
@@ -805,6 +865,7 @@ static int dirbrowse(void)
{
case ONPLAY_MAINMENU:
return GO_TO_ROOT;
+ break;
case ONPLAY_OK:
restore = true;
@@ -817,6 +878,10 @@ static int dirbrowse(void)
case ONPLAY_START_PLAY:
return GO_TO_WPS;
break;
+
+ case ONPLAY_PLUGIN:
+ return GO_TO_PLUGIN;
+ break;
}
break;
}
@@ -917,35 +982,19 @@ int create_playlist(void)
#endif
trigger_cpu_boost();
- ret = catalog_add_to_a_playlist(tc.currdir, ATTR_DIRECTORY, true, NULL);
+ ret = catalog_add_to_a_playlist(tc.currdir, ATTR_DIRECTORY, true, NULL, NULL);
cancel_cpu_boost();
return (ret) ? 1 : 0;
}
-void browse_context_init(struct browse_context *browse,
- int dirfilter, unsigned flags,
- char *title, enum themable_icons icon,
- const char *root, const char *selected)
-{
- browse->dirfilter = dirfilter;
- browse->flags = flags;
- browse->callback_show_item = NULL;
- browse->title = title;
- browse->icon = icon;
- browse->root = root;
- browse->selected = selected;
- browse->buf = NULL;
- browse->bufsize = 0;
-}
-
#define NUM_TC_BACKUP 3
static struct tree_context backups[NUM_TC_BACKUP];
/* do not make backup if it is not recursive call */
static int backup_count = -1;
int rockbox_browse(struct browse_context *browse)
{
- static char current[MAX_PATH];
+ tc.is_browsing = (browse != NULL);
int ret_val = 0;
int dirfilter = browse->dirfilter;
@@ -959,53 +1008,68 @@ int rockbox_browse(struct browse_context *browse)
tc.sort_dir = global_settings.sort_dir;
reload_dir = true;
- if (*tc.dirfilter >= NUM_FILTER_MODES)
- {
- int last_context;
-
- tc.browse = browse;
- tc.selected_item = 0;
- tc.dirlevel = 0;
- strlcpy(tc.currdir, browse->root, sizeof(tc.currdir));
- start_wps = false;
- last_context = curr_context;
- if (browse->selected)
- {
- snprintf(current, sizeof(current), "%s/%s",
- browse->root, browse->selected);
- set_current_file(current);
- /* set_current_file changes dirlevel, change it back */
- tc.dirlevel = 0;
- }
-
- ret_val = dirbrowse();
- curr_context = last_context;
+ if (tc.out_of_tree > 0)
+ {
+ /* an item has already been loaded out_of_tree holds the selected index
+ * what happens with the item is dependent on the browse context */
+ tc.selected_item = tc.out_of_tree - 1;
+ tc.out_of_tree = 0;
+ ret_val = ft_enter(&tc);
}
else
{
- if (dirfilter != SHOW_ID3DB)
- tc.dirfilter = &global_settings.dirfilter;
- tc.browse = browse;
- strcpy(current, browse->root);
- set_current_file(current);
- if (browse->flags&BROWSE_RUNFILE)
- ret_val = ft_enter(&tc);
- else
+ if (*tc.dirfilter >= NUM_FILTER_MODES)
+ {
+ int last_context;
+ /* don't reset if its the same browse already loaded */
+ if (tc.browse != browse ||
+ !(tc.currdir[1] && strcmp(tc.currdir, browse->root) == 0))
+ {
+ tc.browse = browse;
+ tc.selected_item = 0;
+ tc.dirlevel = 0;
+
+ strmemccpy(tc.currdir, browse->root, sizeof(tc.currdir));
+ }
+
+ start_wps = false;
+ last_context = curr_context;
+
+ if (browse->selected)
+ {
+ set_current_file_ex(browse->root, browse->selected);
+ /* set_current_file changes dirlevel, change it back */
+ tc.dirlevel = 0;
+ }
+
ret_val = dirbrowse();
+ curr_context = last_context;
+ }
+ else
+ {
+ if (dirfilter != SHOW_ID3DB && (browse->flags & BROWSE_DIRFILTER) == 0)
+ tc.dirfilter = &global_settings.dirfilter;
+ tc.browse = browse;
+ set_current_file(browse->root);
+ if (browse->flags&BROWSE_RUNFILE)
+ ret_val = ft_enter(&tc);
+ else
+ ret_val = dirbrowse();
+ }
}
backup_count--;
if (backup_count >= 0)
tc = backups[backup_count];
+
+ tc.is_browsing = false;
+
return ret_val;
}
static int move_callback(int handle, void* current, void* new)
{
struct tree_cache* cache = &tc.cache;
- if (cache->lock_count > 0)
- return BUFLIB_CB_CANNOT_MOVE;
-
ptrdiff_t diff = new - current;
/* FIX_PTR makes sure to not accidentally update static allocations */
#define FIX_PTR(x) \
@@ -1037,15 +1101,11 @@ void tree_mem_init(void)
cache->name_buffer_size = AVERAGE_FILENAME_LENGTH *
global_settings.max_files_in_dir;
- cache->name_buffer_handle = core_alloc_ex("tree names",
- cache->name_buffer_size,
- &ops);
+ cache->name_buffer_handle = core_alloc_ex(cache->name_buffer_size, &ops);
cache->max_entries = global_settings.max_files_in_dir;
- cache->entries_handle = core_alloc_ex("tree entries",
- cache->max_entries*(sizeof(struct entry)),
- &ops);
- tree_get_filetypes(&filetypes, &filetypes_count);
+ cache->entries_handle =
+ core_alloc_ex(cache->max_entries*(sizeof(struct entry)), &ops);
}
bool bookmark_play(char *resume_file, int index, unsigned long elapsed,
@@ -1078,6 +1138,7 @@ bool bookmark_play(char *resume_file, int index, unsigned long elapsed,
{
if (global_settings.playlist_shuffle)
playlist_shuffle(seed, -1);
+
playlist_start(index, elapsed, offset);
started = true;
}
@@ -1100,7 +1161,7 @@ bool bookmark_play(char *resume_file, int index, unsigned long elapsed,
else search for it */
peek_filename = playlist_peek(index, filename_buf,
sizeof(filename_buf));
-
+
if (peek_filename == NULL)
{
/* playlist has shrunk, search from the top */
@@ -1110,7 +1171,7 @@ bool bookmark_play(char *resume_file, int index, unsigned long elapsed,
if (peek_filename == NULL)
return false;
}
-
+
if (strcmp(strrchr(peek_filename, '/') + 1, filename))
{
for ( i=0; i < playlist_amount(); i++ )
@@ -1129,6 +1190,7 @@ bool bookmark_play(char *resume_file, int index, unsigned long elapsed,
else
return false;
}
+
playlist_start(index, elapsed, offset);
started = true;
}
@@ -1139,25 +1201,9 @@ bool bookmark_play(char *resume_file, int index, unsigned long elapsed,
return started;
}
-static long filetype_voiceclip(int attr)
-{
- int j;
- if (global_settings.talk_filetype)
- {
- /* try to find a voice ID for the extension, if known */
- attr &= FILE_ATTR_MASK; /* file type */
- for (j=0; j<filetypes_count; j++)
- if (attr == filetypes[j].tree_attr)
- {
- return filetypes[j].voiceclip;
- }
- }
- return -1;
-}
-
static void say_filetype(int attr)
{
- talk_id(filetype_voiceclip(attr), true);
+ talk_id(tree_get_filetype_voiceclip(attr), true);
}
static int ft_play_dirname(char* name)
@@ -1175,7 +1221,7 @@ static int ft_play_filename(char *dir, char *file, int attr)
file_thumbnail_ext))
/* file has no .talk extension */
return talk_file(dir, NULL, file, file_thumbnail_ext,
- TALK_IDARRAY(filetype_voiceclip(attr)), false);
+ TALK_IDARRAY(tree_get_filetype_voiceclip(attr)), false);
/* it already is a .talk file, play this directly, but prefix it. */
return talk_file(dir, NULL, file, NULL,
@@ -1185,6 +1231,7 @@ static int ft_play_filename(char *dir, char *file, int attr)
/* These two functions are called by the USB and shutdown handlers */
void tree_flush(void)
{
+ tc.is_browsing = false;/* clear browse to prevent reentry to a possibly missing file */
#ifdef HAVE_TAGCACHE
tagcache_shutdown();
#endif
@@ -1209,7 +1256,7 @@ void tree_flush(void)
global_status.dircache_size = info.last_size;
#ifdef HAVE_EEPROM_SETTINGS
savecache = firmware_settings.initialized;
- #endif
+ #endif
}
else
{
@@ -1231,11 +1278,11 @@ void tree_restore(void)
#ifdef HAVE_EEPROM_SETTINGS
firmware_settings.disk_clean = false;
#endif
-
+
#ifdef HAVE_TC_RAMCACHE
- remove(TAGCACHE_STATEFILE);
+ tagcache_remove_statefile();
#endif
-
+
#ifdef HAVE_DIRCACHE
if (global_settings.dircache && dircache_resume() > 0)
{
diff --git a/apps/tree.h b/apps/tree.h
index c70ae8dac1..d13c75d434 100644
--- a/apps/tree.h
+++ b/apps/tree.h
@@ -38,6 +38,7 @@ struct entry {
#define BROWSE_SELECTONLY 0x0001 /* exit on selecting a file */
#define BROWSE_NO_CONTEXT_MENU 0x0002 /* disable context menu */
#define BROWSE_RUNFILE 0x0004 /* do ft_open() on the file instead of browsing */
+#define BROWSE_DIRFILTER 0x0080 /* override global_settings.dirfilter with browse_context.dirfilter */
#define BROWSE_SELECTED 0x0100 /* this bit is set if user selected item */
@@ -52,7 +53,6 @@ struct tree_cache {
int name_buffer_handle; /* handle to the name cache */
int max_entries; /* Max entries in the cache */
int name_buffer_size; /* in bytes */
- volatile int lock_count; /* non-0 if buffers may not move */
};
struct browse_context {
@@ -82,23 +82,20 @@ struct tree_context {
* (used when we want to return back to a previouws directory)*/
int selected_item_history[MAX_DIR_LEVELS];
- int firstpos; /* which dir entry is on first
- position in dir buffer */
- int pos_history[MAX_DIR_LEVELS];
-
int *dirfilter; /* file use */
int filesindir; /* The number of files in the dircache */
int dirsindir; /* file use */
int dirlength; /* total number of entries in dir, incl. those not loaded */
#ifdef HAVE_TAGCACHE
- int table_history[MAX_DIR_LEVELS]; /* db use */
- int extra_history[MAX_DIR_LEVELS]; /* db use */
int currtable; /* db use */
int currextra; /* db use */
#endif
+ int sort_dir; /* directory sort order */
+ int out_of_tree; /* shortcut from elsewhere */
struct tree_cache cache;
bool dirfull;
- int sort_dir; /* directory sort order */
+ bool is_browsing; /* valid browse context? */
+
struct browse_context *browse;
};
@@ -109,25 +106,17 @@ struct entry* tree_get_entries(struct tree_context *t);
struct entry* tree_get_entry_at(struct tree_context *t, int index);
void tree_mem_init(void) INIT_ATTR;
-void tree_gui_init(void) INIT_ATTR;
+void tree_init(void) INIT_ATTR;
char* get_current_file(char* buffer, size_t buffer_len);
void set_dirfilter(int l_dirfilter);
void set_current_file(const char *path);
-void browse_context_init(struct browse_context *browse,
- int dirfilter, unsigned flags,
- char *title, enum themable_icons icon,
- const char *root, const char *selected);
int rockbox_browse(struct browse_context *browse);
int create_playlist(void);
void resume_directory(const char *dir);
-static inline void tree_lock_cache(struct tree_context *t)
-{
- t->cache.lock_count++;
-}
-static inline void tree_unlock_cache(struct tree_context *t)
-{
- t->cache.lock_count--;
-}
+
+void tree_lock_cache(struct tree_context *t);
+void tree_unlock_cache(struct tree_context *t);
+
#ifdef WIN32
/* it takes an int on windows */
#define getcwd_size_t int
@@ -144,5 +133,4 @@ void tree_restore(void);
bool bookmark_play(char* resume_file, int index, unsigned long elapsed,
unsigned long offset, int seed, char *filename);
-extern struct gui_synclist tree_lists;
#endif
diff --git a/apps/usb_keymaps.c b/apps/usb_keymaps.c
index bd72d95be8..2781fb5532 100644
--- a/apps/usb_keymaps.c
+++ b/apps/usb_keymaps.c
@@ -181,6 +181,7 @@ int get_hid_usb_action(void)
{
case ACTION_USB_HID_MODE_SWITCH_NEXT:
step = 1;
+ /* fallthrough */
case ACTION_USB_HID_MODE_SWITCH_PREV:
/* Switch key mappings in a cyclic way */
usb_keypad_mode = clamp_value_wrap(usb_keypad_mode + step,
diff --git a/apps/voice_thread.c b/apps/voice_thread.c
index 77bdd08d44..dfc2dd8ed6 100644
--- a/apps/voice_thread.c
+++ b/apps/voice_thread.c
@@ -361,12 +361,11 @@ static enum voice_state voice_message(struct voice_thread_data *td)
{
case Q_VOICE_PLAY:
LOGFQUEUE("voice < Q_VOICE_PLAY");
- if (quiet_counter == 0)
- {
- /* Boost CPU now */
- trigger_cpu_boost();
- }
- else
+
+ /* Boost CPU now */
+ trigger_cpu_boost();
+
+ if (quiet_counter != 0)
{
/* Stop any clip still playing */
voice_stop_playback();
@@ -570,7 +569,7 @@ void voice_thread_init(void)
if (voice_thread_id != 0)
return; /* Already did an init and succeeded at it */
- voice_buf_hid = core_alloc_ex("voice buf", sizeof (*voice_buf), &ops);
+ voice_buf_hid = core_alloc_ex(sizeof (*voice_buf), &ops);
if (voice_buf_hid <= 0)
{