summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--apps/SOURCES2
-rw-r--r--apps/action.c113
-rw-r--r--apps/action.h10
-rw-r--r--apps/alarm_menu.c161
-rw-r--r--apps/buffering.c109
-rw-r--r--apps/buffering.h7
-rw-r--r--apps/core_keymap.c146
-rw-r--r--apps/core_keymap.h72
-rw-r--r--apps/cuesheet.c101
-rw-r--r--apps/debug_menu.c134
-rw-r--r--apps/features.txt11
-rw-r--r--apps/filetree.c83
-rw-r--r--apps/filetypes.c5
-rw-r--r--apps/gui/bitmap/list.c46
-rw-r--r--apps/gui/folder_select.c2
-rw-r--r--apps/gui/list.c25
-rw-r--r--apps/gui/list.h23
-rw-r--r--apps/gui/quickscreen.c24
-rw-r--r--apps/gui/skin_engine/skin_backdrops.c18
-rw-r--r--apps/gui/skin_engine/skin_engine.c33
-rw-r--r--apps/gui/skin_engine/skin_engine.h2
-rw-r--r--apps/gui/skin_engine/skin_parser.c245
-rw-r--r--apps/gui/statusbar-skinned.c7
-rw-r--r--apps/gui/statusbar-skinned.h2
-rw-r--r--apps/gui/viewport.c2
-rw-r--r--apps/iap/iap-core.c1
-rw-r--r--apps/keymaps/keymap-fiiom3k.c44
-rw-r--r--apps/lang/arabic.lang13
-rw-r--r--apps/lang/basque.lang13
-rw-r--r--apps/lang/bulgarian.lang13
-rw-r--r--apps/lang/catala.lang13
-rw-r--r--apps/lang/chinese-simp.lang13
-rw-r--r--apps/lang/chinese-trad.lang13
-rw-r--r--apps/lang/czech.lang13
-rw-r--r--apps/lang/dansk.lang13
-rw-r--r--apps/lang/deutsch.lang13
-rw-r--r--apps/lang/english-us.lang382
-rw-r--r--apps/lang/english.lang383
-rw-r--r--apps/lang/espanol.lang13
-rw-r--r--apps/lang/finnish.lang13
-rw-r--r--apps/lang/francais.lang13
-rw-r--r--apps/lang/galego.lang13
-rw-r--r--apps/lang/greek.lang13
-rw-r--r--apps/lang/hebrew.lang13
-rw-r--r--apps/lang/hrvatski.lang13
-rw-r--r--apps/lang/italiano.lang185
-rw-r--r--apps/lang/japanese.lang13
-rw-r--r--apps/lang/korean.lang13
-rw-r--r--apps/lang/latviesu.lang13
-rw-r--r--apps/lang/lietuviu.lang13
-rw-r--r--apps/lang/magyar.lang13
-rw-r--r--apps/lang/nederlands.lang13
-rw-r--r--apps/lang/norsk-nynorsk.lang13
-rw-r--r--apps/lang/norsk.lang13
-rw-r--r--apps/lang/polski.lang369
-rw-r--r--apps/lang/portugues-brasileiro.lang13
-rw-r--r--apps/lang/portugues.lang13
-rw-r--r--apps/lang/romaneste.lang13
-rw-r--r--apps/lang/russian.lang13
-rw-r--r--apps/lang/slovak.lang1133
-rw-r--r--apps/lang/slovenscina.lang13
-rw-r--r--apps/lang/srpski.lang1013
-rw-r--r--apps/lang/svenska.lang13
-rw-r--r--apps/lang/tagalog.lang13
-rw-r--r--apps/lang/thai.lang13
-rw-r--r--apps/lang/turkce.lang13
-rw-r--r--apps/lang/ukrainian.lang13
-rw-r--r--apps/lang/vlaams.lang13
-rw-r--r--apps/lang/walon.lang13
-rw-r--r--apps/main.c36
-rw-r--r--apps/menu.c2
-rw-r--r--apps/menus/display_menu.c1
-rw-r--r--apps/menus/playback_menu.c48
-rw-r--r--apps/menus/playlist_menu.c7
-rw-r--r--apps/menus/radio_menu.c6
-rw-r--r--apps/menus/recording_menu.c4
-rw-r--r--apps/menus/settings_menu.c36
-rw-r--r--apps/menus/theme_menu.c19
-rw-r--r--apps/menus/time_menu.c8
-rw-r--r--apps/misc.c38
-rw-r--r--apps/misc.h1
-rw-r--r--apps/onplay.c301
-rw-r--r--apps/onplay.h1
-rw-r--r--apps/open_plugin.c2
-rw-r--r--apps/playback.c48
-rw-r--r--apps/playback.h3
-rw-r--r--apps/playlist.c121
-rw-r--r--apps/playlist_viewer.c19
-rw-r--r--apps/plugin.c37
-rw-r--r--apps/plugin.h15
-rw-r--r--apps/plugins/CATEGORIES5
-rw-r--r--apps/plugins/SOURCES9
-rw-r--r--apps/plugins/SOURCES.app_build1
-rw-r--r--apps/plugins/battery_bench.c1
-rw-r--r--apps/plugins/chessbox/chessbox.c3
-rw-r--r--apps/plugins/chopper.c25
-rw-r--r--apps/plugins/imageviewer/imageviewer.c28
-rw-r--r--apps/plugins/imageviewer/imageviewer_button.h2
-rw-r--r--apps/plugins/keyremap.c2131
-rw-r--r--apps/plugins/lastfm_scrobbler.c609
-rw-r--r--apps/plugins/lib/action_helper.h2
-rwxr-xr-xapps/plugins/lib/action_helper.pl2
-rw-r--r--apps/plugins/lib/button_helper.h2
-rwxr-xr-xapps/plugins/lib/button_helper.pl5
-rwxr-xr-xapps/plugins/lua/rbdefines_helper.pl2
-rw-r--r--apps/plugins/lua/rocklib.c1
-rw-r--r--apps/plugins/mpegplayer/mpeg_misc.c1
-rw-r--r--apps/plugins/multiboot_select.c346
-rw-r--r--apps/plugins/pictureflow/pictureflow.c597
-rw-r--r--apps/plugins/plugin_crt0.c3
-rw-r--r--apps/plugins/properties.c236
-rw-r--r--apps/plugins/sliding_puzzle.c4
-rw-r--r--apps/plugins/solitaire.c1
-rw-r--r--apps/plugins/test_kbd.c46
-rw-r--r--apps/plugins/test_usb.c136
-rw-r--r--apps/plugins/viewers.config2
-rw-r--r--apps/rbcodec_helpers.c10
-rw-r--r--apps/recorder/albumart.c2
-rw-r--r--apps/recorder/jpeg_load.c15
-rw-r--r--apps/recorder/jpeg_load.h7
-rw-r--r--apps/recorder/keyboard.c183
-rw-r--r--apps/recorder/pcm_record.c7
-rw-r--r--apps/recorder/recording.c16
-rw-r--r--apps/root_menu.c2
-rw-r--r--apps/screens.c23
-rw-r--r--apps/screens.h3
-rw-r--r--apps/scrobbler.c287
-rw-r--r--apps/settings.c3
-rw-r--r--apps/settings.h25
-rw-r--r--apps/settings_list.c35
-rw-r--r--apps/shortcuts.c29
-rw-r--r--apps/tagcache.c127
-rw-r--r--apps/tagcache.h12
-rw-r--r--apps/tagtree.c83
-rw-r--r--apps/talk.c9
-rw-r--r--apps/tree.c8
-rw-r--r--bootloader/SOURCES7
-rw-r--r--bootloader/iaudio_coldfire.c6
-rw-r--r--bootloader/iriver_h1x0.c4
-rw-r--r--bootloader/iriver_h300.c4
-rw-r--r--bootloader/mpio_hd200_hd300.c6
-rw-r--r--bootloader/x1000.c499
-rw-r--r--bootloader/x1000/boot.c252
-rw-r--r--bootloader/x1000/gui.c237
-rw-r--r--bootloader/x1000/install.c81
-rw-r--r--bootloader/x1000/main.c99
-rw-r--r--bootloader/x1000/recovery.c207
-rw-r--r--bootloader/x1000/utils.c413
-rw-r--r--bootloader/x1000/x1000bootloader.h204
-rw-r--r--docs/CREDITS3
-rw-r--r--docs/PLUGIN_API15
-rw-r--r--firmware/SOURCES16
-rw-r--r--firmware/asm/thread.h10
-rw-r--r--firmware/backlight.c1
-rw-r--r--firmware/buflib.c6
-rw-r--r--firmware/common/dir.c93
-rw-r--r--firmware/common/dircache.c13
-rw-r--r--firmware/common/disk.c18
-rw-r--r--firmware/common/file.c21
-rw-r--r--firmware/common/file_internal.c108
-rw-r--r--firmware/common/fileobj_mgr.c103
-rw-r--r--firmware/common/inflate.c34
-rw-r--r--firmware/common/multiboot.c113
-rw-r--r--firmware/common/pathfuncs.c78
-rw-r--r--firmware/common/rb-loader.c95
-rw-r--r--firmware/common/rb_namespace.c341
-rw-r--r--firmware/common/zip.c4
-rw-r--r--firmware/drivers/fat.c2
-rw-r--r--firmware/drivers/lcd-1bit-vert.c1
-rw-r--r--firmware/drivers/lcd-2bit-horz.c1
-rw-r--r--firmware/drivers/lcd-2bit-vert.c1
-rw-r--r--firmware/drivers/lcd-2bit-vi.c1
-rw-r--r--firmware/drivers/lcd-bitmap-common.c34
-rw-r--r--firmware/drivers/lcd-color-common.c19
-rw-r--r--firmware/drivers/rds.c37
-rw-r--r--firmware/drivers/tuner/si4700.c29
-rw-r--r--firmware/export/ak4376.h4
-rw-r--r--firmware/export/backtrace.h3
-rw-r--r--firmware/export/config.h12
-rw-r--r--firmware/export/config/erosqnative.h6
-rw-r--r--firmware/export/config/fiiom3k.h15
-rw-r--r--firmware/export/config/gigabeats.h3
-rw-r--r--firmware/export/config/sansaclipplus.h1
-rw-r--r--firmware/export/config/sansaclipzip.h1
-rw-r--r--firmware/export/config/sansae200.h1
-rw-r--r--firmware/export/config/sansafuze.h1
-rw-r--r--firmware/export/config/sansafuzeplus.h1
-rw-r--r--firmware/export/config/sansafuzev2.h1
-rw-r--r--firmware/export/config/shanlingq1.h6
-rw-r--r--firmware/export/lcd.h11
-rw-r--r--firmware/export/linuxboot.h192
-rw-r--r--firmware/export/mi4-loader.h22
-rw-r--r--firmware/export/multiboot.h (renamed from apps/scrobbler.h)17
-rw-r--r--firmware/export/mv.h4
-rw-r--r--firmware/export/pathfuncs.h7
-rw-r--r--firmware/export/powermgmt.h9
-rw-r--r--firmware/export/rbpaths.h3
-rw-r--r--firmware/export/screendump.h14
-rw-r--r--firmware/export/si4700.h8
-rw-r--r--firmware/export/system.h7
-rw-r--r--firmware/export/usb.h1
-rw-r--r--firmware/export/x1000.h55
-rw-r--r--firmware/include/buflib.h6
-rw-r--r--firmware/include/dircache_redirect.h69
-rw-r--r--firmware/include/file_internal.h32
-rw-r--r--firmware/include/fileobj_mgr.h5
-rw-r--r--firmware/include/fs_defines.h6
-rw-r--r--firmware/include/inflate.h24
-rw-r--r--firmware/include/rb-loader.h20
-rw-r--r--firmware/include/rb_namespace.h79
-rw-r--r--firmware/kernel/include/mutex.h5
-rw-r--r--firmware/kernel/include/queue.h3
-rw-r--r--firmware/kernel/pthread/corelock.c18
-rw-r--r--firmware/kernel/pthread/mutex.c21
-rw-r--r--firmware/kernel/pthread/thread.c204
-rw-r--r--firmware/linuxboot.c336
-rw-r--r--firmware/panic.c10
-rw-r--r--firmware/powermgmt.c38
-rw-r--r--firmware/rolo.c17
-rw-r--r--firmware/target/arm/as3525/lcd-ssd1303.c3
-rw-r--r--firmware/target/arm/as3525/sansa-clipzip/lcd-clipzip.c3
-rw-r--r--firmware/target/arm/imx233/creative-zen/lcd-zenmozaic.c4
-rw-r--r--firmware/target/arm/imx233/creative-zen/lcd-zenv.c3
-rw-r--r--firmware/target/arm/imx233/creative-zen/lcd-zenxfistyle.c3
-rw-r--r--firmware/target/arm/imx233/creative-zenxfi2/lcd-zenxfi2.c3
-rw-r--r--firmware/target/arm/imx233/creative-zenxfi3/lcd-zenxfi3.c3
-rw-r--r--firmware/target/arm/imx233/debug-imx233.c6
-rw-r--r--firmware/target/arm/imx233/sansa-fuzeplus/lcd-fuzeplus.c3
-rw-r--r--firmware/target/arm/imx233/sony-nwz/lcd-nwze360.c3
-rw-r--r--firmware/target/arm/imx233/sony-nwz/lcd-nwze370.c3
-rw-r--r--firmware/target/arm/imx31/gigabeat-s/fmradio-i2c-gigabeat-s.c49
-rw-r--r--firmware/target/arm/ipod/lcd-gray.c9
-rw-r--r--firmware/target/arm/lcd-ssd1815.c6
-rw-r--r--firmware/target/arm/olympus/mrobe-100/lcd-mr100.c6
-rw-r--r--firmware/target/arm/pp/mi4-loader.c89
-rw-r--r--firmware/target/arm/rk27xx/lcdif-rk27xx.c3
-rw-r--r--firmware/target/arm/s5l8700/debug-s5l8700.c4
-rw-r--r--firmware/target/arm/s5l8700/yps3/lcd-yps3.c11
-rw-r--r--firmware/target/arm/s5l8702/debug-s5l8702.c4
-rw-r--r--firmware/target/arm/samsung/yh920/lcd-yh920.c3
-rw-r--r--firmware/target/coldfire/iaudio/m3/lcd-m3.c6
-rw-r--r--firmware/target/coldfire/iaudio/m5/lcd-m5.c6
-rw-r--r--firmware/target/coldfire/iriver/h100/lcd-h100.c6
-rw-r--r--firmware/target/coldfire/mpio/hd300/lcd-hd300.c3
-rw-r--r--firmware/target/hosted/ibasso/pcm-ibasso.c2
-rw-r--r--firmware/target/hosted/ibasso/sysfs-ibasso.c2
-rw-r--r--firmware/target/hosted/rolo.c16
-rw-r--r--firmware/target/hosted/system-hosted.c1
-rw-r--r--firmware/target/hosted/usb-hiby.c4
-rw-r--r--firmware/target/mips/ingenic_jz47xx/crt0.S2
-rw-r--r--firmware/target/mips/ingenic_jz47xx/usb-jz4760.c251
-rw-r--r--firmware/target/mips/ingenic_jz47xx/xduoo_x3/lcd-xduoo_x3.c6
-rw-r--r--firmware/target/mips/ingenic_x1000/app.lds18
-rw-r--r--firmware/target/mips/ingenic_x1000/boot-x1000.c280
-rw-r--r--firmware/target/mips/ingenic_x1000/boot-x1000.h38
-rw-r--r--firmware/target/mips/ingenic_x1000/clk-x1000.c59
-rw-r--r--firmware/target/mips/ingenic_x1000/crt0.S126
-rw-r--r--firmware/target/mips/ingenic_x1000/erosqnative/lcd-erosqnative.c23
-rw-r--r--firmware/target/mips/ingenic_x1000/erosqnative/power-erosqnative.c2
-rw-r--r--firmware/target/mips/ingenic_x1000/fiiom3k/audiohw-fiiom3k.c127
-rw-r--r--firmware/target/mips/ingenic_x1000/fiiom3k/spl-fiiom3k.c116
-rw-r--r--firmware/target/mips/ingenic_x1000/installer-x1000.c2
-rw-r--r--firmware/target/mips/ingenic_x1000/lcd-x1000.h8
-rw-r--r--firmware/target/mips/ingenic_x1000/nand-x1000.c50
-rw-r--r--firmware/target/mips/ingenic_x1000/nand-x1000.h37
-rw-r--r--firmware/target/mips/ingenic_x1000/shanlingq1/audiohw-shanlingq1.c2
-rw-r--r--firmware/target/mips/ingenic_x1000/shanlingq1/spl-shanlingq1.c116
-rw-r--r--firmware/target/mips/ingenic_x1000/spl-nand-x1000.c (renamed from firmware/target/mips/ingenic_x1000/erosqnative/spl-erosqnative.c)51
-rw-r--r--firmware/target/mips/ingenic_x1000/spl-x1000.c224
-rw-r--r--firmware/target/mips/ingenic_x1000/spl-x1000.h24
-rw-r--r--firmware/target/mips/ingenic_x1000/spl.lds6
-rw-r--r--firmware/target/mips/ingenic_x1000/system-x1000.c1
-rw-r--r--firmware/usb.c25
-rw-r--r--firmware/usbstack/usb_storage.c7
-rw-r--r--lib/arm_support/support-arm.S51
-rw-r--r--lib/mipsunwinder/SOURCES2
-rw-r--r--lib/mipsunwinder/backtrace-mips32.c236
-rw-r--r--lib/mipsunwinder/backtrace-mipsunwinder.h65
-rw-r--r--lib/mipsunwinder/init_context_32.S12
-rw-r--r--lib/mipsunwinder/mipsunwinder.make23
-rw-r--r--lib/rbcodec/codecs/libm4a/demux.c31
-rw-r--r--lib/rbcodec/codecs/libm4a/m4a.c148
-rw-r--r--lib/rbcodec/codecs/libm4a/m4a.h1
-rw-r--r--lib/rbcodec/codecs/libopus/silk/NLSF2A.c2
-rw-r--r--lib/rbcodec/dsp/tdspeed.c9
-rw-r--r--lib/rbcodec/dsp/tdspeed.h1
-rw-r--r--lib/rbcodec/metadata/aac.c2
-rw-r--r--lib/rbcodec/metadata/asap.c19
-rw-r--r--lib/rbcodec/metadata/asf.c81
-rw-r--r--lib/rbcodec/metadata/id3tags.c15
-rw-r--r--lib/rbcodec/metadata/metadata.c38
-rw-r--r--lib/rbcodec/metadata/metadata.h1
-rw-r--r--lib/rbcodec/metadata/metadata_common.c90
-rw-r--r--lib/rbcodec/metadata/metadata_common.h4
-rw-r--r--lib/rbcodec/metadata/mp4.c21
-rw-r--r--lib/rbcodec/metadata/replaygain.c31
-rw-r--r--lib/skin_parser/skin_buffer.c4
-rw-r--r--lib/unwarminder/backtrace-unwarminder.c3
-rw-r--r--lib/x1000-installer/src/xf_nandio.c2
-rw-r--r--lib/x1000-installer/src/xf_package.c2
-rw-r--r--lib/x1000-installer/test_lib/core_alloc.c7
-rw-r--r--lib/x1000-installer/test_lib/core_alloc.h10
-rwxr-xr-xmanual/advanced_topics/main.tex8
-rw-r--r--manual/appendix/config_file_options.tex13
-rw-r--r--manual/appendix/file_formats.tex126
-rw-r--r--manual/configure_rockbox/bookmarking.tex9
-rw-r--r--manual/configure_rockbox/playback_options.tex20
-rw-r--r--manual/configure_rockbox/playlist_options.tex6
-rw-r--r--manual/configure_rockbox/sound_settings.tex37
-rw-r--r--manual/configure_rockbox/startup_shutdown_options.tex39
-rwxr-xr-xmanual/configure_rockbox/system_options.tex118
-rw-r--r--manual/configure_rockbox/time_and_date.tex5
-rw-r--r--manual/getting_started/installation.tex21
-rw-r--r--manual/getting_started/jztool_install.tex3
-rw-r--r--manual/main_menu/main.tex16
-rw-r--r--manual/platform/erosqnative.tex28
-rw-r--r--manual/platform/keymap-erosqnative.tex118
-rw-r--r--manual/platform/keymap-fiiom3k.tex34
-rw-r--r--manual/platform/keymap-xduoox3.tex4
-rw-r--r--manual/plugins/pictureflow.tex126
-rw-r--r--manual/plugins/text_viewer.tex5
-rw-r--r--manual/preamble.tex10
-rw-r--r--manual/rockbox_interface/browsing_and_playing.tex14
-rw-r--r--manual/rockbox_interface/main.tex29
-rw-r--r--manual/rockbox_interface/wps.tex32
-rw-r--r--manual/working_with_playlists/main.tex38
-rwxr-xr-xtools/configure13
-rwxr-xr-xtools/rockboxdev.sh2
-rw-r--r--tools/root.make6
-rw-r--r--uisimulator/common/filesystem-sim.c2
-rw-r--r--uisimulator/common/sim_tasks.c4
-rw-r--r--utils/CMakeLists.txt35
-rw-r--r--utils/cmake/deploy.cmake162
-rwxr-xr-xutils/common/deploy-themeeditor.py62
-rwxr-xr-xutils/common/deploy.py677
-rwxr-xr-xutils/common/gitscraper.py17
-rwxr-xr-xutils/common/tarball-rbutil.py (renamed from utils/common/deploy-rbutil.py)87
-rw-r--r--utils/ipodpatcher/ipodio-posix.c1
-rw-r--r--utils/ipodpatcher/ipodio-win32.c1
-rw-r--r--utils/ipodpatcher/ipodpatcher-aupd.c398
-rw-r--r--utils/ipodpatcher/ipodpatcher.c355
-rw-r--r--utils/jztool/src/x1000.c99
-rw-r--r--utils/rbutilqt/CMakeLists.txt47
-rw-r--r--utils/rbutilqt/INSTALL37
-rw-r--r--utils/rbutilqt/Info.plist4
-rw-r--r--utils/rbutilqt/base/autodetection.cpp10
-rw-r--r--utils/rbutilqt/base/autodetection.h5
-rw-r--r--utils/rbutilqt/base/bootloaderinstallbase.cpp65
-rw-r--r--utils/rbutilqt/base/bootloaderinstallbase.h11
-rw-r--r--utils/rbutilqt/base/bootloaderinstallipod.cpp43
-rw-r--r--utils/rbutilqt/base/bootloaderinstallsansa.cpp51
-rw-r--r--utils/rbutilqt/base/encttssettings.h3
-rw-r--r--utils/rbutilqt/base/httpget.cpp29
-rw-r--r--utils/rbutilqt/base/httpget.h7
-rw-r--r--utils/rbutilqt/base/playerbuildinfo.cpp52
-rw-r--r--utils/rbutilqt/base/playerbuildinfo.h2
-rw-r--r--utils/rbutilqt/base/rbsettings.cpp1
-rw-r--r--utils/rbutilqt/base/rbsettings.h1
-rw-r--r--utils/rbutilqt/base/system.cpp7
-rw-r--r--utils/rbutilqt/base/talkfile.cpp6
-rw-r--r--utils/rbutilqt/base/talkgenerator.cpp26
-rw-r--r--utils/rbutilqt/base/talkgenerator.h4
-rw-r--r--utils/rbutilqt/base/ttsbase.h4
-rw-r--r--utils/rbutilqt/base/ttscarbon.cpp2
-rw-r--r--utils/rbutilqt/base/ttscarbon.h2
-rw-r--r--utils/rbutilqt/base/ttsexes.cpp2
-rw-r--r--utils/rbutilqt/base/ttsexes.h2
-rw-r--r--utils/rbutilqt/base/ttsfestival.cpp94
-rw-r--r--utils/rbutilqt/base/ttsfestival.h2
-rw-r--r--utils/rbutilqt/base/ttssapi.cpp2
-rw-r--r--utils/rbutilqt/base/ttssapi.h2
-rw-r--r--utils/rbutilqt/base/uninstall.cpp20
-rw-r--r--utils/rbutilqt/base/utils.cpp4
-rw-r--r--utils/rbutilqt/base/voicefile.cpp14
-rw-r--r--utils/rbutilqt/base/voicefile.h2
-rw-r--r--utils/rbutilqt/base/zipinstaller.cpp13
-rw-r--r--utils/rbutilqt/base/zipinstaller.h13
-rw-r--r--utils/rbutilqt/base/ziputil.cpp1
-rw-r--r--utils/rbutilqt/changelog.txt8
-rw-r--r--utils/rbutilqt/configure.cpp6
-rw-r--r--utils/rbutilqt/encttscfggui.cpp275
-rw-r--r--utils/rbutilqt/encttscfggui.h16
-rw-r--r--utils/rbutilqt/gui/infowidget.cpp52
-rw-r--r--utils/rbutilqt/gui/selectiveinstallwidget.cpp38
-rw-r--r--utils/rbutilqt/gui/selectiveinstallwidget.h1
-rw-r--r--utils/rbutilqt/lang/rbutil_cs.ts1348
-rw-r--r--utils/rbutilqt/lang/rbutil_de.ts684
-rw-r--r--utils/rbutilqt/lang/rbutil_fr.ts1460
-rw-r--r--utils/rbutilqt/lang/rbutil_gr.ts1469
-rw-r--r--utils/rbutilqt/lang/rbutil_he.ts1468
-rw-r--r--utils/rbutilqt/lang/rbutil_it.ts1463
-rw-r--r--utils/rbutilqt/lang/rbutil_ja.ts1464
-rw-r--r--utils/rbutilqt/lang/rbutil_nl.ts1462
-rw-r--r--utils/rbutilqt/lang/rbutil_pl.ts1460
-rw-r--r--utils/rbutilqt/lang/rbutil_pt.ts1463
-rw-r--r--utils/rbutilqt/lang/rbutil_pt_BR.ts438
-rw-r--r--utils/rbutilqt/lang/rbutil_ru.ts436
-rw-r--r--utils/rbutilqt/lang/rbutil_tr.ts876
-rw-r--r--utils/rbutilqt/lang/rbutil_zh_TW.ts1464
-rwxr-xr-xutils/rbutilqt/langstats.py39
-rw-r--r--utils/rbutilqt/main.cpp9
-rw-r--r--utils/rbutilqt/rbutil.ini26
-rw-r--r--utils/rbutilqt/rbutilqt.cpp76
-rw-r--r--utils/rbutilqt/rbutilqt.h5
-rw-r--r--utils/rbutilqt/rbutilqtfrm.ui40
-rw-r--r--utils/rbutilqt/test/stubs/stubs-talkgenerator.cpp104
-rw-r--r--utils/rbutilqt/test/test-httpget.cpp16
-rw-r--r--utils/rbutilqt/test/test-playerbuildinfo.cpp35
-rw-r--r--utils/rbutilqt/test/test-talkgenerator.cpp83
-rw-r--r--utils/rbutilqt/test/test-talkgenerator.qrc5
-rw-r--r--utils/rbutilqt/themesinstallwindow.cpp41
-rw-r--r--utils/rbutilqt/themesinstallwindow.h13
-rw-r--r--utils/rbutilqt/version.h2
-rw-r--r--utils/sansapatcher/sansaio-posix.c1
-rw-r--r--utils/sansapatcher/sansaio-win32.c1
-rw-r--r--utils/themeeditor/CMakeLists.txt11
416 files changed, 26461 insertions, 13260 deletions
diff --git a/apps/SOURCES b/apps/SOURCES
index 1628524805..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
diff --git a/apps/action.c b/apps/action.c
index b31c4fa927..9ef10936f2 100644
--- a/apps/action.c
+++ b/apps/action.c
@@ -69,6 +69,10 @@ static action_last_t action_last =
.tick = 0,
.wait_for_release = false,
+#ifndef DISABLE_ACTION_REMAP
+ .core_keymap = NULL,
+#endif
+
#ifdef HAVE_TOUCHSCREEN
.ts_data = 0,
.ts_short_press = false,
@@ -499,7 +503,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)
{
@@ -585,10 +589,8 @@ static inline int get_next_context(const struct button_mapping *items, int i)
*/
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
@@ -598,6 +600,41 @@ static inline void action_code_lookup(action_last_t *last, action_cur_t *cur)
context |= CONTEXT_LOCKED;
#endif
+#ifndef DISABLE_ACTION_REMAP
+ bool check_remap = (last->core_keymap != NULL);
+ /* attempt to look up the button in user supplied remap */
+ if(check_remap && (context & CONTEXT_PLUGIN) == 0)
+ {
+#if 0 /*Disable the REMOTE context for remap for now (BUTTON_REMOTE != 0)*/
+ if ((cur->button & BUTTON_REMOTE) != 0)
+ {
+ context |= CONTEXT_REMOTE;
+ }
+#endif
+ cur->items = last->core_keymap;
+ 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); */
@@ -609,9 +646,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)
{
@@ -1150,6 +1191,70 @@ int get_action(int context, int timeout)
return action;
}
+int action_set_keymap(struct button_mapping* core_keymap, int count)
+{
+
+#ifdef DISABLE_ACTION_REMAP
+ count = -1;
+#else
+ if (count > 0 && core_keymap != NULL) /* saf-tey checks :) */
+ {
+ int i = 0;
+ 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*/
+ {
+ count = -1;
+ }
+
+ while (count > 0 && /* check the lut at the beginning for invalid offsets */
+ (entry = &core_keymap[i])->action_code != (int) CONTEXT_STOPSEARCHING)
+ {
+
+ 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 */
+ count = -2;
+ break;
+ }
+
+ if (core_keymap[endpos].button_code != BUTTON_NONE)
+ {
+ /* stop sentinel is not at end of action lut*/
+ count = -3;
+ }
+ }
+ else /* something other than a context remap in the lut */
+ {
+ count = -4;
+ break;
+ }
+
+ i++;
+
+ if (i >= count) /* no sentinel in the lut */
+ {
+ count = -5;
+ break;
+ }
+ }
+
+ if (count <= 0)
+ core_keymap = NULL;
+ }
+ else
+#endif
+ {
+ core_keymap = NULL;
+ }
+ action_last.core_keymap = core_keymap;
+ return count;
+}
+
int get_custom_action(int context,int timeout,
const struct button_mapping* (*get_context_map)(int))
{
diff --git a/apps/action.h b/apps/action.h
index 3217ce4d6f..35f08a3dbd 100644
--- a/apps/action.h
+++ b/apps/action.h
@@ -28,10 +28,13 @@
#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 */
+#define CONTEXT_REMAPPED 0x08000000 /* marker for key remap context table */
+#define CORE_CONTEXT_REMAP(context) (CONTEXT_REMAPPED | context)
#ifdef HAVE_LOCKED_ACTIONS
#define CONTEXT_LOCKED 0x04000000 /* flag to use alternate keymap when screen is locked */
#endif
@@ -415,6 +418,10 @@ typedef struct
bool repeated;
bool wait_for_release;
+#ifndef DISABLE_ACTION_REMAP
+ struct button_mapping* core_keymap;
+#endif
+
#ifdef HAVE_TOUCHSCREEN
bool ts_short_press;
int ts_data;
@@ -441,6 +448,9 @@ 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_button_map, 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/buffering.c b/apps/buffering.c
index 3adbc4a6b9..8661a42ab8 100644
--- a/apps/buffering.c
+++ b/apps/buffering.c
@@ -71,8 +71,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 +293,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;
}
@@ -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,11 +963,18 @@ 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
}
#endif
@@ -983,7 +983,10 @@ int bufopen(const char *file, off_t offset, enum data_type type,
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 +1037,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 +1436,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/core_keymap.c b/apps/core_keymap.c
new file mode 100644
index 0000000000..dbe7ae0072
--- /dev/null
+++ b/apps/core_keymap.c
@@ -0,0 +1,146 @@
+/***************************************************************************
+ * __________ __ ___.
+ * 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)
+static int keymap_handle = -1;
+
+static int core_alloc_keymap(size_t bufsz)
+{
+ keymap_handle = core_alloc_ex("key remap", bufsz, &buflib_ops_locked);
+ return keymap_handle;
+}
+
+static void core_free_keymap(void)
+{
+ action_set_keymap(NULL, -1);
+ if (keymap_handle > 0) /* free old buffer */
+ {
+ keymap_handle = core_free(keymap_handle);
+ }
+}
+
+/* Allocates buffer from core and copies keymap into it */
+int core_set_keyremap(struct button_mapping* core_keymap, int count)
+{
+
+ core_free_keymap();
+ if (count > 0)
+ {
+ size_t bufsize = count * sizeof(struct button_mapping);
+ if (core_keymap != NULL && core_alloc_keymap(bufsize) > 0)
+ {
+ char *buf = core_get_data(keymap_handle);
+ memcpy(buf, core_keymap, bufsize);
+ count = action_set_keymap((struct button_mapping *) buf, count);
+ }
+ else
+ count = -1;
+ }
+ return count;
+}
+
+int core_load_key_remap(const char *filename)
+{
+ char *buf;
+ int fd = -1;
+ int count = 0;
+ size_t fsize = 0;
+ core_free_keymap();
+
+ if (filename != NULL)
+ count = open_key_remap(filename, &fd, &fsize);
+ while (count > 0)
+ {
+ if (core_alloc_keymap(fsize) <= 0)
+ {
+ count = -30;
+ logf("core_keymap: %d Failed to allocate buffer", count);
+ break;
+ }
+ buf = core_get_data(keymap_handle);
+ if (read(fd, buf, fsize) == (ssize_t) fsize)
+ {
+ count = action_set_keymap((struct button_mapping *) buf, count);
+ }
+ else
+ {
+ count = -40;
+ logf("core_keymap: %d Failed to read", count);
+ }
+ break;
+ }
+ close(fd);
+ return count;
+}
+
+int open_key_remap(const char *filename, int *fd, size_t *fsize)
+{
+ int count = 0;
+
+ while (filename && fd && fsize)
+ {
+ *fsize = 0;
+ *fd = open(filename, O_RDONLY);
+ if (*fd)
+ {
+ *fsize = filesize(*fd);
+
+ count = *fsize / sizeof(struct button_mapping);
+
+ if (count * sizeof(struct button_mapping) != *fsize)
+ {
+ count = -10;
+ logf("core_keymap: %d Size mismatch", count);
+ break;
+ }
+
+ if (count > 1)
+ {
+ struct button_mapping header = {0};
+ 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;
+ logf("core_keymap: %d Header mismatch", count);
+ break;
+ }
+ }
+ }
+ break;
+ }
+ return count;
+}
+
+#endif /* !defined(__PCTOOL__) */
diff --git a/apps/core_keymap.h b/apps/core_keymap.h
new file mode 100644
index 0000000000..dad9875364
--- /dev/null
+++ b/apps/core_keymap.h
@@ -0,0 +1,72 @@
+/***************************************************************************
+ * __________ __ ___.
+ * 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);
+
+/* open_key_remap(filename , *fd (you must close file_descriptor), *fsize)
+ * checks/strips header and returns remaining count
+ * fd is opened and set to first record
+ * filesize contains the size of the remaining records
+*/
+int open_key_remap(const char *filename, int *fd, size_t *filesize);
+
+/* 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 be89ef96cf..98040f9992 100644
--- a/apps/cuesheet.c
+++ b/apps/cuesheet.c
@@ -123,6 +123,7 @@ 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 */
@@ -138,7 +139,7 @@ static unsigned long parse_cue_index(const char *line)
while (isdigit(*line))
{
value = 10 * value + (*line - '0');
- if (value > 99) /* Sanity check bail early */
+ if (value > field_max[field]) /* Sanity check bail early */
return 0;
line++;
}
@@ -165,6 +166,38 @@ static unsigned long parse_cue_index(const char *line)
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)
{
@@ -190,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)
@@ -245,11 +281,16 @@ 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,' ');
@@ -265,10 +306,7 @@ bool parse_cuesheet(struct cuesheet_file *cue_file, struct cuesheet *cue)
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);
@@ -278,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;
@@ -303,6 +341,16 @@ 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)
@@ -319,6 +367,7 @@ bool parse_cuesheet(struct cuesheet_file *cue_file, struct cuesheet *cue)
}
}
}
+
if (is_embedded)
{
bytes_left -= line_len;
diff --git a/apps/debug_menu.c b/apps/debug_menu.c
index 3a9218bc84..f510597ad2 100644
--- a/apps/debug_menu.c
+++ b/apps/debug_menu.c
@@ -39,7 +39,6 @@
#include "audio.h"
#include "settings.h"
#include "list.h"
-#include "statusbar.h"
#include "dir.h"
#include "panic.h"
#include "screens.h"
@@ -128,12 +127,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)
@@ -153,36 +158,77 @@ 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 (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);
+ MAXTHREADS IF_COP( + NUM_CORES ), &xoffset);
info.hide_selection = true;
- info.scroll_all = true;
+ info.scroll_all = false;
info.action_callback = dbg_threads_action_callback;
info.get_name = threads_getname;
return simplelist_show_list(&info);
@@ -330,6 +376,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;
@@ -366,13 +419,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)
@@ -382,7 +435,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,
@@ -391,7 +444,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)
@@ -403,7 +456,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());
@@ -442,6 +495,7 @@ static bool dbg_buffering_thread(void)
screens[i].setfont(FONT_UI);
return false;
+#undef STR_DATAREM
}
static const char* bf_getname(int selected_item, void *data,
@@ -816,8 +870,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(lists->title, 32, "tsc2100 registers - Page %d", *page);
return ACTION_REDRAW;
}
return action;
@@ -825,7 +878,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;
@@ -876,7 +930,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,
@@ -887,7 +941,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
@@ -942,16 +996,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
@@ -965,7 +1019,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");
@@ -1019,7 +1073,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();
@@ -1042,11 +1096,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[] =
@@ -1073,7 +1127,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);
@@ -1099,9 +1153,9 @@ static bool view_battery(void)
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, "Battery temperature: %d C", x);
+ 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, "Battery current: %d.%01d mA", x / 10, x % 10);
+ 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;
@@ -1139,7 +1193,7 @@ 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)
@@ -1148,7 +1202,7 @@ static bool view_battery(void)
lcd_puts(0, 7, "Estimation n/a");
#if (CONFIG_BATTERY_MEASURE & CURRENT_MEASURE)
- lcd_putsf(0, 8, "battery current: %d mA", battery_current());
+ lcd_putsf(0, 8, "%s current: %d mA", "Battery", battery_current());
#endif
break;
}
@@ -2030,12 +2084,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]);
@@ -2523,19 +2577,25 @@ static bool dbg_skin_engine(void)
#if defined(HAVE_BOOTDATA) && !defined(SIMULATOR)
static bool dbg_boot_data(void)
{
- unsigned int crc = 0;
+ unsigned int crc = crc_32(boot_data.payload, boot_data.length, 0xffffffff);
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)
+ char rootpath[MAX_PATH / 2] = RB_ROOT_CONTENTS_DIR;
int boot_volume = 0;
if(crc == boot_data.crc)
{
boot_volume = boot_data.boot_volume; /* boot volume contained in uint8_t payload */
+ int rtlen = get_redirect_dir(rootpath, sizeof(rootpath), boot_volume, "", "");
+ while (rtlen > 0 && rootpath[--rtlen] == PATH_SEPCH) /* remove extra separators */
+ rootpath[rtlen] = '\0';
}
simplelist_addline("Boot Volume: <%lu>", boot_volume);
+ simplelist_addline("Root:");
+ simplelist_addline("%s", rootpath);
simplelist_addline("");
#endif
simplelist_addline("Bootdata RAW:");
diff --git a/apps/features.txt b/apps/features.txt
index ec2113cbc8..2262f7502e 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)
@@ -285,3 +289,10 @@ 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
diff --git a/apps/filetree.c b/apps/filetree.c
index 75a32a9e42..1944713d13 100644
--- a/apps/filetree.c
+++ b/apps/filetree.c
@@ -330,42 +330,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,7 +385,7 @@ 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;
@@ -432,6 +433,13 @@ 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_enter(struct tree_context* c)
{
int rc = GO_TO_PREVIOUS;
@@ -539,49 +547,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))
diff --git a/apps/filetypes.c b/apps/filetypes.c
index d68bab3daa..02d2af282e 100644
--- a/apps/filetypes.c
+++ b/apps/filetypes.c
@@ -66,6 +66,7 @@ static const struct filetype inbuilt_filetypes[] = {
{ "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 },
+ { "mpga", 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 },
@@ -301,8 +302,8 @@ void read_viewer_theme_file(void)
{
custom_filetype_icons[i] = filetypes[i].icon;
}
-
- snprintf(buffer, MAX_PATH, "%s/%s.icons", ICON_DIR,
+
+ snprintf(buffer, MAX_PATH, "%s/%s.icons", ICON_DIR,
global_settings.viewers_icon_file);
fd = open(buffer, O_RDONLY);
if (fd < 0)
diff --git a/apps/gui/bitmap/list.c b/apps/gui/bitmap/list.c
index 1b051cd800..53874a8dfa 100644
--- a/apps/gui/bitmap/list.c
+++ b/apps/gui/bitmap/list.c
@@ -168,8 +168,10 @@ static bool draw_title(struct screen *display,
int icon = list->title_icon;
int icon_w = list_icon_width(display->screen_type);
bool have_icons = false;
- if (icon != Icon_NOICON && global_settings.show_icons)
+ if (icon != Icon_NOICON && list->show_icons)
+ {
have_icons = true;
+ }
struct list_putlineinfo_t list_info =
{
@@ -192,11 +194,14 @@ void list_draw(struct screen *display, struct gui_synclist *list)
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->show_selection_marker &&
+ (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;
@@ -264,7 +269,7 @@ void list_draw(struct screen *display, struct gui_synclist *list)
#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)
@@ -340,7 +345,7 @@ void list_draw(struct screen *display, struct gui_synclist *list)
}
if (line_indent)
{
- if (global_settings.show_icons)
+ if (list->show_icons)
line_indent *= icon_w;
else
line_indent *= character_width;
@@ -374,12 +379,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
)
{
@@ -387,14 +392,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;
@@ -753,7 +758,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;
@@ -771,14 +776,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;
diff --git a/apps/gui/folder_select.c b/apps/gui/folder_select.c
index e324e8649a..cef95e179e 100644
--- a/apps/gui/folder_select.c
+++ b/apps/gui/folder_select.c
@@ -516,7 +516,7 @@ static int select_paths(struct folder* root, const char* filenames)
sstr = lastfnp;
lastfnp = fnp;
- if (len <= 0 || len > buflen)
+ if (len <= 0 || len + 1 >= buflen)
continue;
strlcpy(buf, sstr, len + 1);
struct child *item = find_from_filename(buf, root);
diff --git a/apps/gui/list.c b/apps/gui/list.c
index a8055c4581..29c80574c2 100644
--- a/apps/gui/list.c
+++ b/apps/gui/list.c
@@ -129,6 +129,18 @@ void list_init_item_height(struct gui_synclist *list, enum screen_type screen)
#endif
}
+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
@@ -153,6 +165,8 @@ void gui_synclist_init(struct gui_synclist * gui_list,
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
@@ -263,7 +277,7 @@ static void gui_list_put_selection_on_screen(struct gui_synclist * gui_list,
{
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)
@@ -293,7 +307,7 @@ static void gui_list_put_selection_on_screen(struct gui_synclist * gui_list,
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 */
@@ -356,7 +370,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);
@@ -683,11 +697,10 @@ bool gui_synclist_do_button(struct gui_synclist * lists,
/* Disable the skin redraw callback */
current_lists = NULL;
-
switch (wrap)
{
case LIST_WRAP_ON:
- gui_synclist_limit_scroll(lists, !global_settings.list_wraparound);
+ gui_synclist_limit_scroll(lists, !(lists->wraparound));
break;
case LIST_WRAP_OFF:
gui_synclist_limit_scroll(lists, true);
@@ -698,7 +711,7 @@ bool gui_synclist_do_button(struct gui_synclist * lists,
action == ACTION_LISTTREE_PGUP ||
action == ACTION_LISTTREE_PGDOWN)
gui_synclist_limit_scroll(lists, true);
- else gui_synclist_limit_scroll(lists, !global_settings.list_wraparound);
+ else gui_synclist_limit_scroll(lists, !(lists->wraparound));
break;
};
diff --git a/apps/gui/list.h b/apps/gui/list.h
index 1f910577a1..4dc83a1b27 100644
--- a/apps/gui/list.h
+++ b/apps/gui/list.h
@@ -36,6 +36,14 @@ enum list_wrap {
LIST_WRAP_UNLESS_HELD,
};
+enum synclist_cursor
+{
+ SYNCLIST_CURSOR_NOSTYLE = 0,
+ SYNCLIST_CURSOR_INVERT,
+ SYNCLIST_CURSOR_COLOR,
+ SYNCLIST_CURSOR_GRADIENT,
+};
+
/*
* The gui_list is based on callback functions, if you want the list
* to display something you have to provide it a function that
@@ -139,14 +147,24 @@ struct list_selection_color
struct gui_synclist
{
+ /*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;
/* 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
+ /* whether the text of the whole items of the list have to be
* scrolled or only for the selected item */
bool scroll_all;
+ bool show_selection_marker; /* set to true by default */
int nb_items;
int selected_item;
+
#ifdef HAVE_TOUCHSCREEN
/* absolute Y coordinate, used for smooth scrolling */
int y_pos;
@@ -170,7 +188,6 @@ struct gui_synclist
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;
@@ -187,7 +204,7 @@ 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_display_settings(struct gui_synclist * list);
extern void gui_synclist_init(
struct gui_synclist * lists,
list_get_name callback_get_item_name,
diff --git a/apps/gui/quickscreen.c b/apps/gui/quickscreen.c
index b2f5050ab3..e403e13e6c 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 */
@@ -285,14 +288,22 @@ static bool gui_quickscreen_do_button(struct gui_quickscreen * qs, int button)
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;
enum { left=1, right=2, top=4, bottom=8 };
- int bits = (x < LCD_WIDTH/3 ? left : (x > 2*LCD_WIDTH/3 ? 2 : right)) |
- (y < LCD_WIDTH/3 ? top : (y > 2*LCD_WIDTH/3 ? 8 : bottom));
+ 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:
@@ -411,6 +422,9 @@ 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)
@@ -446,6 +460,10 @@ int quick_screen_quick(int button_enter)
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);
}
diff --git a/apps/gui/skin_engine/skin_backdrops.c b/apps/gui/skin_engine/skin_backdrops.c
index 41de1e1f76..146dccb18a 100644
--- a/apps/gui/skin_engine/skin_backdrops.c
+++ b/apps/gui/skin_engine/skin_backdrops.c
@@ -81,8 +81,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++)
@@ -98,6 +99,7 @@ void skin_backdrop_init(void)
handle_being_loaded = -1;
first_go = false;
}
+ return go_status;
}
int skin_backdrop_assign(char* backdrop, char *bmpdir,
@@ -331,8 +333,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_load_setting(void)
+{
+}
-void skin_backdrop_init(void)
+void skin_backdrop_show(int backdrop_id)
{
+ (void) backdrop_id;
}
#endif
diff --git a/apps/gui/skin_engine/skin_engine.c b/apps/gui/skin_engine/skin_engine.c
index b3626b681d..8ba76e5739 100644
--- a/apps/gui/skin_engine/skin_engine.c
+++ b/apps/gui/skin_engine/skin_engine.c
@@ -149,16 +149,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;
if (audio_status() & AUDIO_STATUS_PLAY)
audio_stop();
- skin_backdrop_init();
+ bool first_run = skin_backdrop_init();
skins_initialised = true;
/* Make sure each skin is loaded */
@@ -170,15 +182,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];
@@ -186,17 +190,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,
diff --git a/apps/gui/skin_engine/skin_engine.h b/apps/gui/skin_engine/skin_engine.h
index 3b757a5f8b..e26ec34d1f 100644
--- a/apps/gui/skin_engine/skin_engine.h
+++ b/apps/gui/skin_engine/skin_engine.h
@@ -62,7 +62,7 @@ bool skin_has_sbs(enum screen_type screen, struct wps_data *data);
/* 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);
diff --git a/apps/gui/skin_engine/skin_parser.c b/apps/gui/skin_engine/skin_parser.c
index b3840f689f..d89ca8b534 100644
--- a/apps/gui/skin_engine/skin_parser.c
+++ b/apps/gui/skin_engine/skin_parser.c
@@ -343,11 +343,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 +392,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;
@@ -561,6 +564,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 +573,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,11 +601,16 @@ 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))
@@ -605,7 +619,7 @@ static int parse_viewporttextstyle(struct skin_element *element,
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 +632,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 +648,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;
@@ -811,19 +830,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;
@@ -1009,20 +1047,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 +1090,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 +1101,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 +1115,31 @@ 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;
+ if (find_setting_by_cfgname(text, &pb->setting_id) == NULL)
+ return WPS_ERROR_INVALID_PARAM;
#endif
- }
- }
+ }
+ }
else if (curr_param == 4)
image_filename = text;
@@ -1219,22 +1274,27 @@ 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);
- 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;
+ 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, 1)) && get_param(element, 1)->type == PERCENT)
- aa->y = get_param(element, 1)->data.number * curr_vp->vp.height / 1000;
+ if (!isdefault(param0) && param0->type == PERCENT)
+ aa->x = param0->data.number * curr_vp->vp.width / 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(param1) && param1->type == PERCENT)
+ aa->y = param1->data.number * curr_vp->vp.height / 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(param2) && param2->type == PERCENT)
+ aa->width = param2->data.number * curr_vp->vp.width / 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;
@@ -1341,27 +1401,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;
@@ -1403,12 +1468,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);
@@ -1640,16 +1705,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);
@@ -2468,8 +2538,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;
diff --git a/apps/gui/statusbar-skinned.c b/apps/gui/statusbar-skinned.c
index 63f3197faa..fd695bf6b6 100644
--- a/apps/gui/statusbar-skinned.c
+++ b/apps/gui/statusbar-skinned.c
@@ -156,7 +156,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)
diff --git a/apps/gui/statusbar-skinned.h b/apps/gui/statusbar-skinned.h
index ad102bef47..3ed36f1a84 100644
--- a/apps/gui/statusbar-skinned.h
+++ b/apps/gui/statusbar-skinned.h
@@ -49,9 +49,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/viewport.c b/apps/gui/viewport.c
index 3dd8bca979..9f9cb186f5 100644
--- a/apps/gui/viewport.c
+++ b/apps/gui/viewport.c
@@ -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))
diff --git a/apps/iap/iap-core.c b/apps/iap/iap-core.c
index 885ba2c188..ae05806ae9 100644
--- a/apps/iap/iap-core.c
+++ b/apps/iap/iap-core.c
@@ -364,6 +364,7 @@ static void iap_thread(void)
/* Handle poweroff message */
case SYS_POWEROFF:
+ case SYS_REBOOT:
{
iap_shutdown = true;
break;
diff --git a/apps/keymaps/keymap-fiiom3k.c b/apps/keymaps/keymap-fiiom3k.c
index a97be0870d..d0fbbb2e98 100644
--- a/apps/keymaps/keymap-fiiom3k.c
+++ b/apps/keymaps/keymap-fiiom3k.c
@@ -138,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},
@@ -193,7 +203,7 @@ 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},
{ACTION_STD_CANCEL, BUTTON_RIGHT, BUTTON_NONE},
@@ -203,6 +213,33 @@ static const struct button_mapping button_context_yesnoscreen[] = {
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
}; /* button_context_yesnoscreen */
+static const struct button_mapping button_context_recscreen[] = {
+ {ACTION_REC_PAUSE, BUTTON_SELECT, BUTTON_NONE},
+ {ACTION_REC_PAUSE, BUTTON_PLAY, BUTTON_NONE},
+ {ACTION_REC_NEWFILE, BUTTON_SELECT|BUTTON_REPEAT, BUTTON_SELECT},
+ {ACTION_REC_NEWFILE, BUTTON_PLAY|BUTTON_REPEAT, BUTTON_PLAY},
+ {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},
@@ -315,8 +352,9 @@ const struct button_mapping* get_context_mapping(int context)
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;
@@ -326,6 +364,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/lang/arabic.lang b/apps/lang/arabic.lang
index e94bd19cef..c9b295956b 100644
--- a/apps/lang/arabic.lang
+++ b/apps/lang/arabic.lang
@@ -766,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>
diff --git a/apps/lang/basque.lang b/apps/lang/basque.lang
index 34c02555c2..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>
diff --git a/apps/lang/bulgarian.lang b/apps/lang/bulgarian.lang
index 137fd4e99b..77f2d58361 100644
--- a/apps/lang/bulgarian.lang
+++ b/apps/lang/bulgarian.lang
@@ -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>
diff --git a/apps/lang/catala.lang b/apps/lang/catala.lang
index d6a65a7205..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>
diff --git a/apps/lang/chinese-simp.lang b/apps/lang/chinese-simp.lang
index 5e137150e8..3d1d0ecaa3 100644
--- a/apps/lang/chinese-simp.lang
+++ b/apps/lang/chinese-simp.lang
@@ -7487,20 +7487,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>
diff --git a/apps/lang/chinese-trad.lang b/apps/lang/chinese-trad.lang
index 6fcc779855..02e14645e0 100644
--- a/apps/lang/chinese-trad.lang
+++ b/apps/lang/chinese-trad.lang
@@ -7422,20 +7422,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>
diff --git a/apps/lang/czech.lang b/apps/lang/czech.lang
index 348c08de3f..b2d5e6e8e2 100644
--- a/apps/lang/czech.lang
+++ b/apps/lang/czech.lang
@@ -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>
diff --git a/apps/lang/dansk.lang b/apps/lang/dansk.lang
index a7be6c16a1..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>
diff --git a/apps/lang/deutsch.lang b/apps/lang/deutsch.lang
index f2d898b79e..ef7ee7fe6a 100644
--- a/apps/lang/deutsch.lang
+++ b/apps/lang/deutsch.lang
@@ -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>
diff --git a/apps/lang/english-us.lang b/apps/lang/english-us.lang
index 26736ffd16..bf5e9e93ce 100644
--- a/apps/lang/english-us.lang
+++ b/apps/lang/english-us.lang
@@ -254,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"
@@ -1695,12 +1694,18 @@
*: "Random"
</voice>
</phrase>
+### The 'desc' field for 'LANG_AUDIOSCROBBLER' differs from the english!
+### the previously used desc is commented below:
+### desc: "Last.fm Log" in the playback menu
+### The <source> section for 'LANG_AUDIOSCROBBLER:*' differs from the english!
+### the previously used one is commented below:
+### Last.fm Logger
<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"
@@ -4108,31 +4113,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>
@@ -4959,20 +4949,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>
@@ -14310,16 +14297,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>
@@ -14436,16 +14423,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>
@@ -14492,16 +14479,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>
@@ -14548,16 +14535,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>
@@ -15597,16 +15584,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>
@@ -15667,16 +15654,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>
@@ -15890,128 +15877,128 @@
</phrase>
<phrase>
id: LANG_PROPERTIES_ALBUMARTIST
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Album Artist]"
+ *: ""
</source>
<dest>
- *: "[Album Artist]"
+ *: ""
</dest>
<voice>
- *: "Album Artist"
+ *: ""
</voice>
</phrase>
<phrase>
id: LANG_PROPERTIES_GENRE
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Genre]"
+ *: ""
</source>
<dest>
- *: "[Genre]"
+ *: ""
</dest>
<voice>
- *: "Genre"
+ *: ""
</voice>
</phrase>
<phrase>
id: LANG_PROPERTIES_COMMENT
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Comment]"
+ *: ""
</source>
<dest>
- *: "[Comment]"
+ *: ""
</dest>
<voice>
- *: "Comment"
+ *: ""
</voice>
</phrase>
<phrase>
id: LANG_PROPERTIES_COMPOSER
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Composer]"
+ *: ""
</source>
<dest>
- *: "[Composer]"
+ *: ""
</dest>
<voice>
- *: "Composer"
+ *: ""
</voice>
</phrase>
<phrase>
id: LANG_PROPERTIES_YEAR
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Year]"
+ *: ""
</source>
<dest>
- *: "[Year]"
+ *: ""
</dest>
<voice>
- *: "Year"
+ *: ""
</voice>
</phrase>
<phrase>
id: LANG_PROPERTIES_TRACKNUM
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Tracknum]"
+ *: ""
</source>
<dest>
- *: "[Tracknum]"
+ *: ""
</dest>
<voice>
- *: "Track number"
+ *: ""
</voice>
</phrase>
<phrase>
id: LANG_PROPERTIES_DISCNUM
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Discnum]"
+ *: ""
</source>
<dest>
- *: "[Discnum]"
+ *: ""
</dest>
<voice>
- *: "Disc number"
+ *: ""
</voice>
</phrase>
<phrase>
id: LANG_PROPERTIES_FREQUENCY
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Frequency]"
+ *: ""
</source>
<dest>
- *: "[Frequency]"
+ *: ""
</dest>
<voice>
- *: "Frequency"
+ *: ""
</voice>
</phrase>
<phrase>
id: LANG_PROPERTIES_BITRATE
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Bitrate]"
+ *: ""
</source>
<dest>
- *: "[Bitrate]"
+ *: ""
</dest>
<voice>
- *: "Bit rate"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -16112,3 +16099,216 @@
*: "Descending"
</voice>
</phrase>
+### This phrase is missing entirely, copying from english!
+<phrase>
+ id: LANG_ALBUM_ART
+ desc: in Settings
+ user: core
+ <source>
+ *: "Album Art"
+ </source>
+ <dest>
+ *: "Album Art"
+ </dest>
+ <voice>
+ *: "Album Art"
+ </voice>
+</phrase>
+### This phrase is missing entirely, copying from english!
+<phrase>
+ id: LANG_PREFER_EMBEDDED
+ desc: in Settings
+ user: core
+ <source>
+ *: "Prefer Embedded"
+ </source>
+ <dest>
+ *: "Prefer Embedded"
+ </dest>
+ <voice>
+ *: "Prefer Embedded"
+ </voice>
+</phrase>
+### This phrase is missing entirely, copying from english!
+<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>
+### This phrase is missing entirely, copying from english!
+<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>
+### This phrase is missing entirely, copying from english!
+<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>
+### This phrase is missing entirely, copying from english!
+<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>
+### This phrase is missing entirely, copying from english!
+<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>
+### This phrase is missing entirely, copying from english!
+<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>
+### This phrase is missing entirely, copying from english!
+<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>
+### This phrase is missing entirely, copying from english!
+<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>
+### This phrase is missing entirely, copying from english!
+<phrase>
+ id: LANG_TRACK_INFO
+ desc: Track Info Title
+ user: core
+ <source>
+ *: "Track Info"
+ </source>
+ <dest>
+ *: "Track Info"
+ </dest>
+ <voice>
+ *: "Track Info"
+ </voice>
+</phrase>
+### This phrase is missing entirely, copying from english!
+<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>
+### This phrase is missing entirely, copying from english!
+<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>
+### This phrase is missing entirely, copying from english!
+<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>
diff --git a/apps/lang/english.lang b/apps/lang/english.lang
index 86d1dc5daf..e8d646b258 100644
--- a/apps/lang/english.lang
+++ b/apps/lang/english.lang
@@ -1772,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>
@@ -4183,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>
@@ -5034,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>
@@ -14740,58 +14722,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>
@@ -15825,16 +15807,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>
@@ -15895,16 +15877,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>
@@ -15965,128 +15947,128 @@
</phrase>
<phrase>
id: LANG_PROPERTIES_ALBUMARTIST
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Album Artist]"
+ *: ""
</source>
<dest>
- *: "[Album Artist]"
+ *: ""
</dest>
<voice>
- *: "Album Artist"
+ *: ""
</voice>
</phrase>
<phrase>
id: LANG_PROPERTIES_GENRE
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Genre]"
+ *: ""
</source>
<dest>
- *: "[Genre]"
+ *: ""
</dest>
<voice>
- *: "Genre"
+ *: ""
</voice>
</phrase>
<phrase>
id: LANG_PROPERTIES_COMMENT
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Comment]"
+ *: ""
</source>
<dest>
- *: "[Comment]"
+ *: ""
</dest>
<voice>
- *: "Comment"
+ *: ""
</voice>
</phrase>
<phrase>
id: LANG_PROPERTIES_COMPOSER
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Composer]"
+ *: ""
</source>
<dest>
- *: "[Composer]"
+ *: ""
</dest>
<voice>
- *: "Composer"
+ *: ""
</voice>
</phrase>
<phrase>
id: LANG_PROPERTIES_YEAR
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Year]"
+ *: ""
</source>
<dest>
- *: "[Year]"
+ *: ""
</dest>
<voice>
- *: "Year"
+ *: ""
</voice>
</phrase>
<phrase>
id: LANG_PROPERTIES_TRACKNUM
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Tracknum]"
+ *: ""
</source>
<dest>
- *: "[Tracknum]"
+ *: ""
</dest>
<voice>
- *: "Track number"
+ *: ""
</voice>
</phrase>
<phrase>
id: LANG_PROPERTIES_DISCNUM
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Discnum]"
+ *: ""
</source>
<dest>
- *: "[Discnum]"
+ *: ""
</dest>
<voice>
- *: "Disc number"
+ *: ""
</voice>
</phrase>
<phrase>
id: LANG_PROPERTIES_FREQUENCY
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Frequency]"
+ *: ""
</source>
<dest>
- *: "[Frequency]"
+ *: ""
</dest>
<voice>
- *: "Frequency"
+ *: ""
</voice>
</phrase>
<phrase>
id: LANG_PROPERTIES_BITRATE
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Bitrate]"
+ *: ""
</source>
<dest>
- *: "[Bitrate]"
+ *: ""
</dest>
<voice>
- *: "Bit rate"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -16187,3 +16169,222 @@
*: "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>
diff --git a/apps/lang/espanol.lang b/apps/lang/espanol.lang
index 3d11cdca48..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>
diff --git a/apps/lang/finnish.lang b/apps/lang/finnish.lang
index 53a294d27c..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>
diff --git a/apps/lang/francais.lang b/apps/lang/francais.lang
index c2c54c9d04..ca61c27830 100644
--- a/apps/lang/francais.lang
+++ b/apps/lang/francais.lang
@@ -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>
diff --git a/apps/lang/galego.lang b/apps/lang/galego.lang
index 14dc1b5fb4..958cfd2b2f 100644
--- a/apps/lang/galego.lang
+++ b/apps/lang/galego.lang
@@ -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 502d64c54e..e094fd0479 100644
--- a/apps/lang/greek.lang
+++ b/apps/lang/greek.lang
@@ -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>
diff --git a/apps/lang/hebrew.lang b/apps/lang/hebrew.lang
index f7f03a562b..272676a62e 100644
--- a/apps/lang/hebrew.lang
+++ b/apps/lang/hebrew.lang
@@ -5296,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>
diff --git a/apps/lang/hrvatski.lang b/apps/lang/hrvatski.lang
index 6dc90216fa..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>
diff --git a/apps/lang/italiano.lang b/apps/lang/italiano.lang
index a3e4eac740..0688ca7846 100644
--- a/apps/lang/italiano.lang
+++ b/apps/lang/italiano.lang
@@ -1685,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"
@@ -4096,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>
@@ -4947,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>
@@ -16038,7 +16020,7 @@
*: "dAY"
</source>
<dest>
- *: "dAY"
+ *: "dmY"
</dest>
<voice>
*: ""
@@ -16114,3 +16096,146 @@
*: "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>
diff --git a/apps/lang/japanese.lang b/apps/lang/japanese.lang
index 62b010c1d1..9b829ceb56 100644
--- a/apps/lang/japanese.lang
+++ b/apps/lang/japanese.lang
@@ -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>
diff --git a/apps/lang/korean.lang b/apps/lang/korean.lang
index 1780ae6c84..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>
diff --git a/apps/lang/latviesu.lang b/apps/lang/latviesu.lang
index 9c4163f72c..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>
diff --git a/apps/lang/lietuviu.lang b/apps/lang/lietuviu.lang
index 3f2fb478b4..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>
diff --git a/apps/lang/magyar.lang b/apps/lang/magyar.lang
index 28c44fb9dc..d6a8df5d62 100644
--- a/apps/lang/magyar.lang
+++ b/apps/lang/magyar.lang
@@ -4924,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>
diff --git a/apps/lang/nederlands.lang b/apps/lang/nederlands.lang
index 8c560c8e9a..5fbd93df81 100644
--- a/apps/lang/nederlands.lang
+++ b/apps/lang/nederlands.lang
@@ -7621,20 +7621,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>
diff --git a/apps/lang/norsk-nynorsk.lang b/apps/lang/norsk-nynorsk.lang
index 2e8fad08c8..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>
diff --git a/apps/lang/norsk.lang b/apps/lang/norsk.lang
index 784da5e0bb..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>
diff --git a/apps/lang/polski.lang b/apps/lang/polski.lang
index dac5032be8..4cef4ba59a 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>
@@ -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>
@@ -13187,16 +13167,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>
@@ -13659,16 +13639,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 +14209,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,16 +14885,16 @@
</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>
@@ -15533,16 +15513,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>
@@ -15603,16 +15583,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>
@@ -15886,128 +15866,128 @@
</phrase>
<phrase>
id: LANG_PROPERTIES_ALBUMARTIST
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Album Artist]"
+ *: ""
</source>
<dest>
- *: "[Wykonawca albumu]"
+ *: ""
</dest>
<voice>
- *: "Wykonawca albumu"
+ *: ""
</voice>
</phrase>
<phrase>
id: LANG_PROPERTIES_GENRE
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Genre]"
+ *: ""
</source>
<dest>
- *: "[Gatunek]"
+ *: ""
</dest>
<voice>
- *: "Gatunek"
+ *: ""
</voice>
</phrase>
<phrase>
id: LANG_PROPERTIES_COMMENT
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Comment]"
+ *: ""
</source>
<dest>
- *: "[Komentarz]"
+ *: ""
</dest>
<voice>
- *: "Komentarz"
+ *: ""
</voice>
</phrase>
<phrase>
id: LANG_PROPERTIES_COMPOSER
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Composer]"
+ *: ""
</source>
<dest>
- *: "[Kompozytor]"
+ *: ""
</dest>
<voice>
- *: "Kompozytor"
+ *: ""
</voice>
</phrase>
<phrase>
id: LANG_PROPERTIES_YEAR
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Year]"
+ *: ""
</source>
<dest>
- *: "[Rok]"
+ *: ""
</dest>
<voice>
- *: "Rok"
+ *: ""
</voice>
</phrase>
<phrase>
id: LANG_PROPERTIES_TRACKNUM
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Tracknum]"
+ *: ""
</source>
<dest>
- *: "[Nr utworu]"
+ *: ""
</dest>
<voice>
- *: "Numer utworu"
+ *: ""
</voice>
</phrase>
<phrase>
id: LANG_PROPERTIES_DISCNUM
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Discnum]"
+ *: ""
</source>
<dest>
- *: "[Nr płyty]"
+ *: ""
</dest>
<voice>
- *: "Numer płyty"
+ *: ""
</voice>
</phrase>
<phrase>
id: LANG_PROPERTIES_FREQUENCY
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Frequency]"
+ *: ""
</source>
<dest>
- *: "[Częstotliwość]"
+ *: ""
</dest>
<voice>
- *: "Częstotliwość"
+ *: ""
</voice>
</phrase>
<phrase>
id: LANG_PROPERTIES_BITRATE
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Bitrate]"
+ *: ""
</source>
<dest>
- *: "[Prędkość transmisji]"
+ *: ""
</dest>
<voice>
- *: "Prędkość transmisji"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -16122,3 +16102,202 @@
*: "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>
diff --git a/apps/lang/portugues-brasileiro.lang b/apps/lang/portugues-brasileiro.lang
index 8c0795bec1..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>
diff --git a/apps/lang/portugues.lang b/apps/lang/portugues.lang
index 1f55429c77..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>
diff --git a/apps/lang/romaneste.lang b/apps/lang/romaneste.lang
index cb04552786..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>
diff --git a/apps/lang/russian.lang b/apps/lang/russian.lang
index ce112d7ca0..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>
diff --git a/apps/lang/slovak.lang b/apps/lang/slovak.lang
index ac5b579dcf..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>
@@ -9611,8 +9598,8 @@
</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>
@@ -15303,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 99e2e66014..bda482762d 100644
--- a/apps/lang/slovenscina.lang
+++ b/apps/lang/slovenscina.lang
@@ -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 100dfc3cfc..8e64e3f938 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>
@@ -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>
@@ -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>
@@ -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,14 +5999,17 @@
user: core
<source>
*: "Int:"
+ hibylinux: "mSD:"
xduoox3: "mSD1:"
</source>
<dest>
*: "Инт:"
+ hibylinux: "mSD:"
xduoox3: "mSD1:"
</dest>
<voice>
*: "Интерни"
+ hibylinux: "мајкро Ес Де"
xduoox3: "мајкро Ес Де 1"
</voice>
</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>
@@ -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>
@@ -9694,7 +9674,7 @@
</dest>
<voice>
*: none
- recording: ""
+ recording: "време дељења"
</voice>
</phrase>
<phrase>
@@ -9725,7 +9705,7 @@
</dest>
<voice>
*: none
- recording: ""
+ recording: "клип"
</voice>
</phrase>
<phrase>
@@ -9759,7 +9739,7 @@
</dest>
<voice>
*: none
- recording: ""
+ recording: "фајл"
</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>
@@ -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>
@@ -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>
@@ -11982,7 +11962,7 @@
*: "Q"
</dest>
<voice>
- *: "Q"
+ *: "Ку фактор"
</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>
@@ -12251,13 +12231,13 @@
desc: Selective Actions
user: core
<source>
- *: "Skip"
+ *: "Exempt Skip"
</source>
<dest>
- *: "Прескочи"
+ *: "Изузми прескакање"
</dest>
<voice>
- *: "Прескочи"
+ *: "Изузми прескакање"
</voice>
</phrase>
<phrase>
@@ -12279,13 +12259,13 @@
desc: Selective Actions
user: core
<source>
- *: "Seek"
+ *: "Exempt Seek"
</source>
<dest>
- *: "Премотај"
+ *: "Изузми премотавање"
</dest>
<voice>
- *: "Премотај"
+ *: "Изузми премотавање"
</voice>
</phrase>
<phrase>
@@ -14302,7 +14282,7 @@
*: ""
</dest>
<voice>
- *: ""
+ *: "Притисните репродукцију за покретање теста батерије или стоп за отказивање"
</voice>
</phrase>
<phrase>
@@ -14586,7 +14566,7 @@
*: ""
</dest>
<voice>
- *: ""
+ *: "Обележено"
</voice>
</phrase>
<phrase>
@@ -15352,7 +15332,6 @@
mpiohd200: "Дупли тап REC за прекид."
mpiohd300: "Дупли тап MENU за прекид."
rx27generic: "Притисните VOLUME за прекид."
- sonynwza860: "Мапе тастера нису комплетне."
touchscreen: "Притисните Middle Left за прекид."
vibe500: "Притисните PREV за прекид."
xduoox20,xduoox3,xduoox3ii: "Дупли тап HOME за прекид."
@@ -15462,3 +15441,799 @@
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: "Високо појачање (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: "Штедња батерије"
+ es9218: "Ниско појачање (1 Vrms)"
+ </dest>
+ <voice>
+ *: none
+ dac_power_mode: "Штедња батерије"
+ es9218: "Ниско појачање (1 Vrms)"
+ </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>
+ *: "Да пређем у режим USB масовне меморије?"
+ </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>
+ *: "Очисти листу & репродукуј наредну"
+ </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: in onplay menu. Replace current playlist with selected tracks in random order.
+ user: core
+ <source>
+ *: "Clear List & Play Shuffled"
+ </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: in properties plugin
+ user: core
+ <source>
+ *: "[Album Artist]"
+ </source>
+ <dest>
+ *: "[Уметник албума]"
+ </dest>
+ <voice>
+ *: "Уметник албума"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_GENRE
+ desc: in properties plugin
+ user: core
+ <source>
+ *: "[Genre]"
+ </source>
+ <dest>
+ *: "[Жанр]"
+ </dest>
+ <voice>
+ *: "Жанр"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_COMMENT
+ desc: in properties plugin
+ user: core
+ <source>
+ *: "[Comment]"
+ </source>
+ <dest>
+ *: "[Коментар]"
+ </dest>
+ <voice>
+ *: "Коментар"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_COMPOSER
+ desc: in properties plugin
+ user: core
+ <source>
+ *: "[Composer]"
+ </source>
+ <dest>
+ *: "[Композитор]"
+ </dest>
+ <voice>
+ *: "Композитор"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_YEAR
+ desc: in properties plugin
+ user: core
+ <source>
+ *: "[Year]"
+ </source>
+ <dest>
+ *: "[Година]"
+ </dest>
+ <voice>
+ *: "Година"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_TRACKNUM
+ desc: in properties plugin
+ user: core
+ <source>
+ *: "[Tracknum]"
+ </source>
+ <dest>
+ *: "[Брнумере]"
+ </dest>
+ <voice>
+ *: "Број нумере"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_DISCNUM
+ desc: in properties plugin
+ user: core
+ <source>
+ *: "[Discnum]"
+ </source>
+ <dest>
+ *: "[Брдиска]"
+ </dest>
+ <voice>
+ *: "Број диска"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_FREQUENCY
+ desc: in properties plugin
+ user: core
+ <source>
+ *: "[Frequency]"
+ </source>
+ <dest>
+ *: "[Фреквенција]"
+ </dest>
+ <voice>
+ *: "Фреквенција"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PROPERTIES_BITRATE
+ desc: in properties plugin
+ user: core
+ <source>
+ *: "[Bitrate]"
+ </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: "Синхронизуј са 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>
diff --git a/apps/lang/svenska.lang b/apps/lang/svenska.lang
index 0203652e78..4c4b7b21cc 100644
--- a/apps/lang/svenska.lang
+++ b/apps/lang/svenska.lang
@@ -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>
diff --git a/apps/lang/tagalog.lang b/apps/lang/tagalog.lang
index 0c7f6d1387..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>
diff --git a/apps/lang/thai.lang b/apps/lang/thai.lang
index 7b18edff76..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>
diff --git a/apps/lang/turkce.lang b/apps/lang/turkce.lang
index fd99418456..a5596afaf1 100644
--- a/apps/lang/turkce.lang
+++ b/apps/lang/turkce.lang
@@ -12186,20 +12186,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: "Ses Formatı"
+ *: "Ses Formatı"
</dest>
<voice>
- *: none
- recording: "Ses Formatı"
+ *: "Ses Formatı"
</voice>
</phrase>
<phrase>
diff --git a/apps/lang/ukrainian.lang b/apps/lang/ukrainian.lang
index 38873acbf5..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>
diff --git a/apps/lang/vlaams.lang b/apps/lang/vlaams.lang
index 1a35454e33..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>
diff --git a/apps/lang/walon.lang b/apps/lang/walon.lang
index d42c54797a..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>
diff --git a/apps/main.c b/apps/main.c
index a88cd73ef7..59932d6185 100644
--- a/apps/main.c
+++ b/apps/main.c
@@ -31,6 +31,7 @@
#include "led.h"
#include "../kernel-internal.h"
#include "button.h"
+#include "core_keymap.h"
#include "tree.h"
#include "filetypes.h"
#include "panic.h"
@@ -69,7 +70,6 @@
#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"
@@ -175,6 +175,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];
@@ -363,9 +372,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();
@@ -560,27 +566,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)");
@@ -620,9 +627,6 @@ static void init(void)
tree_mem_init();
filetype_init();
- if (global_settings.audioscrobbler)
- scrobbler_init();
-
shortcuts_init();
CHART(">audio_init");
diff --git a/apps/menu.c b/apps/menu.c
index 802a16bbb8..ab5578dede 100644
--- a/apps/menu.c
+++ b/apps/menu.c
@@ -738,6 +738,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);
}
diff --git a/apps/menus/display_menu.c b/apps/menus/display_menu.c
index ea3fdc0858..6ed0f34ab6 100644
--- a/apps/menus/display_menu.c
+++ b/apps/menus/display_menu.c
@@ -360,6 +360,7 @@ static int listwraparound_callback(int action,
{
case ACTION_EXIT_MENUITEM:
gui_synclist_limit_scroll(this_list, !global_settings.list_wraparound);
+ gui_synclist_init_display_settings(this_list);
break;
}
return action;
diff --git a/apps/menus/playback_menu.c b/apps/menus/playback_menu.c
index 5f9479fae3..881a4b5a99 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"
@@ -150,26 +149,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,
@@ -201,6 +180,28 @@ MENUITEM_SETTING(pause_rewind, &global_settings.pause_rewind, NULL);
MENUITEM_SETTING(play_frequency, &global_settings.play_frequency,
playback_callback);
#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,
Icon_Playback_menu,
@@ -220,7 +221,7 @@ 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
@@ -231,6 +232,9 @@ MAKE_MENU(playback_settings,ID2P(LANG_PLAYBACK),0,
#ifdef HAVE_PLAY_FREQ
,&play_frequency
#endif
+#ifdef HAVE_ALBUMART
+ ,&album_art
+#endif
);
static int playback_callback(int action,
diff --git a/apps/menus/playlist_menu.c b/apps/menus/playlist_menu.c
index b84abe0b37..e1e83d4311 100644
--- a/apps/menus/playlist_menu.c
+++ b/apps/menus/playlist_menu.c
@@ -169,12 +169,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/radio_menu.c b/apps/menus/radio_menu.c
index a6d259a21d..682cecf2b4 100644
--- a/apps/menus/radio_menu.c
+++ b/apps/menus/radio_menu.c
@@ -87,6 +87,9 @@ MENUITEM_FUNCTION(presetclear_item, 0, ID2P(LANG_FM_PRESET_CLEAR),
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;
@@ -147,5 +150,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..c9ff975269 100644
--- a/apps/menus/recording_menu.c
+++ b/apps/menus/recording_menu.c
@@ -261,7 +261,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, INT,
names, REC_NUM_FORMATS, NULL );
if (rec_format != global_settings.rec_format)
@@ -272,7 +272,7 @@ static int recformat_func(void)
return res;
} /* recformat */
-MENUITEM_FUNCTION(recformat, 0, ID2P(LANG_RECORDING_FORMAT),
+MENUITEM_FUNCTION(recformat, 0, ID2P(LANG_FORMAT),
recformat_func, NULL, NULL, Icon_Menu_setting);
MENUITEM_FUNCTION(enc_global_config_menu_item, 0, ID2P(LANG_ENCODER_SETTINGS),
diff --git a/apps/menus/settings_menu.c b/apps/menus/settings_menu.c
index 562a89e85a..460909318a 100644
--- a/apps/menus/settings_menu.c
+++ b/apps/menus/settings_menu.c
@@ -56,6 +56,7 @@
#include "usb-ibasso.h"
#endif
#include "plugin.h"
+#include "onplay.h"
#ifndef HAS_BUTTON_HOLD
static int selectivesoftlock_callback(int action,
@@ -221,12 +222,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 */
/***********************************/
@@ -565,6 +584,15 @@ 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,
@@ -573,7 +601,11 @@ MAKE_MENU(startup_shutdown_menu, ID2P(LANG_STARTUP_SHUTDOWN),
&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 */
diff --git a/apps/menus/theme_menu.c b/apps/menus/theme_menu.c
index 2a50ed44b5..61a6937e3c 100644
--- a/apps/menus/theme_menu.c
+++ b/apps/menus/theme_menu.c
@@ -165,6 +165,15 @@ MAKE_MENU(colors_settings, ID2P(LANG_COLORS_MENU),
/* BARS MENU */
/* */
+static int list_update_callback(int action,
+ const struct menu_item_ex *this_item,
+ struct gui_synclist *this_list)
+{
+ (void)this_item;
+ if (action == ACTION_EXIT_MENUITEM)
+ gui_synclist_init_display_settings(this_list);
+ return ACTION_REDRAW;
+}
static int statusbar_callback_ex(int action,const struct menu_item_ex *this_item,
enum screen_type screen)
@@ -204,10 +213,9 @@ static int statusbar_callback(int action,
return statusbar_callback_ex(action, this_item, SCREEN_MAIN);
}
-MENUITEM_SETTING(scrollbar_item, &global_settings.scrollbar, NULL);
+MENUITEM_SETTING(scrollbar_item, &global_settings.scrollbar, list_update_callback);
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);
@@ -354,13 +362,11 @@ MENUITEM_FUNCTION(browse_rfms, MENU_FUNC_USEPARAM,
#endif
#endif
-
static int showicons_callback(int action,
const struct menu_item_ex *this_item,
struct gui_synclist *this_list)
{
(void)this_item;
- (void)this_list;
static bool old_icons;
switch (action)
{
@@ -370,6 +376,7 @@ static int showicons_callback(int action,
case ACTION_EXIT_MENUITEM:
if (old_icons != global_settings.show_icons)
icons_init();
+ gui_synclist_init_display_settings(this_list);
break;
}
return ACTION_REDRAW;
@@ -379,7 +386,7 @@ 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_SETTING(cursor_style, &global_settings.cursor_style, NULL);
+MENUITEM_SETTING(cursor_style, &global_settings.cursor_style, list_update_callback);
#if LCD_DEPTH > 1
MENUITEM_SETTING(sep_menu, &global_settings.list_separator_height, NULL);
#endif
diff --git a/apps/menus/time_menu.c b/apps/menus/time_menu.c
index edd2e19a2b..e37e2b5637 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);
@@ -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)
diff --git a/apps/misc.c b/apps/misc.c
index 2668ba714d..63aa3589b2 100644
--- a/apps/misc.c
+++ b/apps/misc.c
@@ -58,7 +58,6 @@
#include "font.h"
#include "splash.h"
#include "tagcache.h"
-#include "scrobbler.h"
#include "sound.h"
#include "playlist.h"
#include "yesno.h"
@@ -289,7 +288,8 @@ 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;
@@ -365,7 +365,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
@@ -394,7 +393,7 @@ static bool clean_shutdown(void (*callback)(void *), void *parameter)
voice_wait();
}
- shutdown_hw();
+ shutdown_hw(sd_type);
}
return false;
}
@@ -607,8 +606,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:
@@ -1384,6 +1392,24 @@ int split_string(char *str, const char split_char, char *vector[], const int vec
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;
+}
/** Open a UTF-8 file and set file descriptor to first byte after BOM.
* If no BOM is present this behaves like open().
diff --git a/apps/misc.h b/apps/misc.h
index c4057f4ebc..ed40de3e1f 100644
--- a/apps/misc.h
+++ b/apps/misc.h
@@ -123,6 +123,7 @@ extern int show_logo(void);
int split_string(char *str, const char needle, char *vector[], int vector_length);
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) \
diff --git a/apps/onplay.c b/apps/onplay.c
index 61ef8acad5..c52bd6101c 100644
--- a/apps/onplay.c
+++ b/apps/onplay.c
@@ -425,6 +425,7 @@ static bool playing_time(void)
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_set_title(&pt_lists, str(LANG_PLAYING_TIME), NOICON);
gui_synclist_draw(&pt_lists);
gui_synclist_speak_item(&pt_lists);
while (true) {
@@ -475,16 +476,40 @@ MAKE_ONPLAYMENU( wps_playlist_menu, ID2P(LANG_PLAYLIST),
&playlist_save_item, &reshuffle_item, &playing_time_item
);
+/* argument for add_to_playlist (for use by menu callbacks) */
+struct add_to_pl_param
+{
+ 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 bool add_to_playlist(int position, bool queue)
+static int add_to_playlist(void* arg)
{
- bool new_playlist = false;
- if (!(audio_status() & AUDIO_STATUS_PLAY))
- {
- new_playlist = true;
- if (position == PLAYLIST_REPLACE)
- position = PLAYLIST_INSERT;
- }
+ 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),
@@ -494,6 +519,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);
@@ -567,121 +601,59 @@ 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 || (intptr_t)param == PLAYLIST_INSERT)
- && !(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);
+ add_to_playlist, &addtopl_insert,
+ treeplaylist_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);
+ add_to_playlist, &addtopl_insert_first,
+ treeplaylist_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,
+ add_to_playlist, &addtopl_insert_last,
+ treeplaylist_callback, Icon_Playlist);
+MENUITEM_FUNCTION(i_shuf_pl_item, MENU_FUNC_USEPARAM, ID2P(LANG_INSERT_SHUFFLED),
+ add_to_playlist, &addtopl_insert_shuf,
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(i_last_shuf_pl_item, MENU_FUNC_USEPARAM, ID2P(LANG_INSERT_LAST_SHUFFLED),
+ add_to_playlist, &addtopl_insert_last_shuf,
treeplaylist_callback, Icon_Playlist);
/* queue items */
MENUITEM_FUNCTION(q_pl_item, MENU_FUNC_USEPARAM, ID2P(LANG_QUEUE),
- playlist_queue_func, (intptr_t*)PLAYLIST_INSERT,
+ add_to_playlist, &addtopl_queue,
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,
+ add_to_playlist, &addtopl_queue_first,
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,
+ add_to_playlist, &addtopl_queue_last,
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,
+MENUITEM_FUNCTION(q_shuf_pl_item, MENU_FUNC_USEPARAM, ID2P(LANG_QUEUE_SHUFFLED),
+ add_to_playlist, &addtopl_queue_shuf,
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,
- 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,
- 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(q_last_shuf_pl_item, MENU_FUNC_USEPARAM, 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_pl_item,
+ &q_first_pl_item,
+ &q_last_pl_item,
+ &q_shuf_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(replace_pl_item, MENU_FUNC_USEPARAM, 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(replace_shuf_pl_item, MENU_FUNC_USEPARAM, ID2P(LANG_PLAY_SHUFFLED),
+ add_to_playlist, &addtopl_replace_shuffled,
treeplaylist_callback, Icon_Playlist);
MAKE_ONPLAYMENU(tree_playlist_menu, ID2P(LANG_CURRENT_PLAYLIST),
@@ -712,70 +684,71 @@ static int treeplaylist_callback(int action,
const struct menu_item_ex *this_item,
struct gui_synclist *this_list)
{
+ static bool in_queue_submenu = false;
+
(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 &&
+ this_item->function->function_w_param == add_to_playlist)
+ {
+ struct add_to_pl_param *param = this_item->function->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:
+ if (this_item == &queue_menu)
+ in_queue_submenu = true;
+ break;
+
+ case ACTION_EXIT_MENUITEM:
+ if (this_item == &queue_menu)
+ in_queue_submenu = false;
+ break;
}
+
return action;
}
@@ -1594,6 +1567,9 @@ MENUITEM_FUNCTION(list_viewers_item, 0, ID2P(LANG_ONPLAY_OPEN_WITH),
MENUITEM_FUNCTION(properties_item, MENU_FUNC_USEPARAM, ID2P(LANG_PROPERTIES),
onplay_load_plugin, (void *)"properties",
clipboard_callback, Icon_NOICON);
+MENUITEM_FUNCTION(track_info_item, MENU_FUNC_USEPARAM, ID2P(LANG_MENU_SHOW_ID3_INFO),
+ onplay_load_plugin, (void *)"properties",
+ clipboard_callback, Icon_NOICON);
#ifdef HAVE_TAGCACHE
MENUITEM_FUNCTION(pictureflow_item, MENU_FUNC_USEPARAM, ID2P(LANG_ONPLAY_PICTUREFLOW),
onplay_load_plugin, (void *)"pictureflow",
@@ -1665,7 +1641,7 @@ static int clipboard_callback(int action,
{
if (((selected_file_attr & FILE_ATTR_MASK) ==
FILE_ATTR_AUDIO) &&
- (this_item == &properties_item ||
+ (this_item == &track_info_item ||
this_item == &pictureflow_item))
return action;
return ACTION_EXIT_MENUITEM;
@@ -1687,7 +1663,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;
@@ -1764,7 +1743,7 @@ MAKE_ONPLAYMENU( tree_onplay_menu, ID2P(LANG_ONPLAY_MENU_TITLE),
#if LCD_DEPTH > 1
&set_backdrop_item,
#endif
- &list_viewers_item, &create_dir_item, &properties_item,
+ &list_viewers_item, &create_dir_item, &properties_item, &track_info_item,
#ifdef HAVE_TAGCACHE
&pictureflow_item,
#endif
@@ -1833,7 +1812,7 @@ static int playlist_insert_shuffled(void)
(selected_file_attr & ATTR_DIRECTORY) ||
((selected_file_attr & FILE_ATTR_MASK) == FILE_ATTR_M3U))
{
- playlist_insert_func((intptr_t*)PLAYLIST_INSERT_SHUFFLED);
+ add_to_playlist(&addtopl_insert_shuf);
return ONPLAY_START_PLAY;
}
@@ -1876,7 +1855,7 @@ static struct hotkey_assignment hotkey_items[] = {
HOTKEY_FUNC(delete_item, NULL),
ONPLAY_RELOAD_DIR },
{ HOTKEY_INSERT, LANG_INSERT,
- HOTKEY_FUNC(playlist_insert_func, (intptr_t*)PLAYLIST_INSERT),
+ HOTKEY_FUNC(add_to_playlist, (intptr_t*)&addtopl_insert),
ONPLAY_RELOAD_DIR },
{ HOTKEY_INSERT_SHUFFLED, LANG_INSERT_SHUFFLED,
HOTKEY_FUNC(playlist_insert_shuffled, NULL),
@@ -1985,3 +1964,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 bff8dafff0..3a259d68e6 100644
--- a/apps/onplay.h
+++ b/apps/onplay.h
@@ -22,6 +22,7 @@
#define _ONPLAY_H_
int onplay(char* file, int attr, int from_screen, bool hotkey);
+int get_onplay_context(void);
enum {
ONPLAY_MAINMENU = -1,
diff --git a/apps/open_plugin.c b/apps/open_plugin.c
index f7f55d58cd..fad528e215 100644
--- a/apps/open_plugin.c
+++ b/apps/open_plugin.c
@@ -222,6 +222,8 @@ static int op_get_entry(uint32_t hash, int32_t lang_id,
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);
}
diff --git a/apps/playback.c b/apps/playback.c
index 4162d9b647..7fdb302d71 100644
--- a/apps/playback.c
+++ b/apps/playback.c
@@ -172,6 +172,8 @@ 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 */
@@ -1690,6 +1692,15 @@ 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;
+}
+
/* 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)
@@ -1699,6 +1710,7 @@ static int audio_load_albumart(struct track_info *infop,
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,18 +1721,31 @@ 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))
+ {
+ user_data.embedded_albumart = NULL;
+ hid = bufopen(path, 0, TYPE_BITMAP, &user_data);
+ }
+ 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)
{
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))
{
@@ -1886,11 +1911,13 @@ static int audio_load_track(void)
break;
/* Test for broken playlists by probing for the files */
- fd = open(path, O_RDONLY);
- if (fd >= 0)
- break;
-
- logf("Open failed");
+ 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))
@@ -3440,9 +3467,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);
diff --git a/apps/playback.h b/apps/playback.h
index a87ef873d0..b9aa413ef3 100644
--- a/apps/playback.h
+++ b/apps/playback.h
@@ -85,6 +85,9 @@ void audio_set_crossfade(int enable);
#ifdef HAVE_PLAY_FREQ
void audio_set_playback_frequency(int setting);
#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 e93feb6abb..b1d5d5a4be 100644
--- a/apps/playlist.c
+++ b/apps/playlist.c
@@ -107,6 +107,27 @@
#include "dircache.h"
#endif
+#if 0//def ROCKBOX_HAS_LOGDISKF
+#warning LOGF enabled
+#define LOGF_ENABLE
+#include "logf.h"
+#undef DEBUGF
+#undef ERRORF
+#undef WARNF
+#undef NOTEF
+#define DEBUGF logf
+#define ERRORF DEBUGF
+#define WARNF DEBUGF
+#define NOTEF DEBUGF
+//ERRORF
+//WARNF
+//NOTEF
+#endif
+
+
+
+
+
#define PLAYLIST_CONTROL_FILE_VERSION 2
/*
@@ -428,7 +449,7 @@ static int recreate_control(struct playlist_info* playlist)
char c = playlist->filename[playlist->dirlen-1];
close(playlist->control_fd);
- playlist->control_fd = 0;
+ playlist->control_fd = -1;
snprintf(temp_file, sizeof(temp_file), "%s%s",
playlist->control_filename, file_suffix);
@@ -1720,6 +1741,20 @@ static int check_subdir_for_music(char *dir, const char *subdir, bool recurse)
/*
* 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
+ *
+ * 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 ssize_t format_track_path(char *dest, char *src, int buf_length,
const char *dir)
@@ -1749,27 +1784,13 @@ static ssize_t format_track_path(char *dest, char *src, int buf_length,
/* Replace backslashes with forward slashes */
path_correct_separators(src, src);
- /* 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. Absolute UNIX-style paths
- * remain unaltered.
- */
+ /* 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_volume(dir, &p, false);
+ path_strip_last_volume(dir, &p, false);
dir = strmemdupa(dir, p - dir); /* empty if no volspec on dir */
#else
dir = ""; /* only volume is root */
@@ -2019,7 +2040,7 @@ void playlist_init(void)
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->max_playlist_size * sizeof(*playlist->indices), &ops);
playlist->indices = core_get_data(handle);
playlist->buffer_size =
AVERAGE_FILENAME_LENGTH * global_settings.max_files_in_dir;
@@ -2076,13 +2097,10 @@ int playlist_create(const char *dir, const char *file)
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);
+ handle = core_alloc_maximum("temp", &buflen, &buflib_ops_locked);
if (handle > 0)
{
/* load the playlist file */
@@ -2119,13 +2137,16 @@ int playlist_resume(void)
bool sorted = true;
int result = -1;
- /* 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 */
+ splash(0, ID2P(LANG_WAIT));
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
+
+ /* use mp3 buffer for maximum load speed */
+ handle = core_alloc_maximum("temp", &buflen, &buflib_ops_locked);
if (handle < 0)
{
splashf(HZ * 2, "%s(): OOM", __func__);
@@ -2135,7 +2156,6 @@ int playlist_resume(void)
empty_playlist(playlist, true);
- splash(0, ID2P(LANG_WAIT));
playlist->control_fd = open(playlist->control_filename, O_RDWR);
if (playlist->control_fd < 0)
{
@@ -2211,7 +2231,7 @@ int playlist_resume(void)
if (!str1)
{
- result = -1;
+ result = -2;
exit_loop = true;
break;
}
@@ -2226,7 +2246,7 @@ int playlist_resume(void)
if (version != PLAYLIST_CONTROL_FILE_VERSION)
{
- result = -1;
+ result = -3;
goto out;
}
@@ -2260,7 +2280,7 @@ int playlist_resume(void)
if (!str1 || !str2 || !str3)
{
- result = -1;
+ result = -4;
exit_loop = true;
break;
}
@@ -2276,7 +2296,7 @@ int playlist_resume(void)
if (add_track_to_playlist(playlist, str3, position,
queue, total_read+(str3-buffer)) < 0)
{
- result = -1;
+ result = -5;
goto out;
}
@@ -2291,7 +2311,7 @@ int playlist_resume(void)
if (!str1)
{
- result = -1;
+ result = -6;
exit_loop = true;
break;
}
@@ -2301,7 +2321,7 @@ int playlist_resume(void)
if (remove_track_from_playlist(playlist, position,
false) < 0)
{
- result = -1;
+ result = -7;
goto out;
}
@@ -2314,7 +2334,7 @@ int playlist_resume(void)
if (!str1 || !str2)
{
- result = -1;
+ result = -8;
exit_loop = true;
break;
}
@@ -2331,7 +2351,7 @@ int playlist_resume(void)
if (randomise_playlist(playlist, seed, false,
false) < 0)
{
- result = -1;
+ result = -9;
goto out;
}
sorted = false;
@@ -2342,7 +2362,7 @@ int playlist_resume(void)
/* str1=first_index */
if (!str1)
{
- result = -1;
+ result = -10;
exit_loop = true;
break;
}
@@ -2351,7 +2371,7 @@ int playlist_resume(void)
if (sort_playlist(playlist, false, false) < 0)
{
- result = -1;
+ result = -11;
goto out;
}
@@ -2380,7 +2400,7 @@ int playlist_resume(void)
/* first non-comment line must always specify playlist */
if (first && *p != 'P' && *p != '#')
{
- result = -1;
+ result = -12;
exit_loop = true;
break;
}
@@ -2391,7 +2411,7 @@ int playlist_resume(void)
/* playlist can only be specified once */
if (!first)
{
- result = -1;
+ result = -13;
exit_loop = true;
break;
}
@@ -2420,7 +2440,7 @@ int playlist_resume(void)
current_command = PLAYLIST_COMMAND_COMMENT;
break;
default:
- result = -1;
+ result = -14;
exit_loop = true;
break;
}
@@ -2465,7 +2485,7 @@ int playlist_resume(void)
if (result < 0)
{
- splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_INVALID));
+ splashf(HZ*2, "Err: %d, %s", result, str(LANG_PLAYLIST_CONTROL_INVALID));
goto out;
}
@@ -2480,8 +2500,8 @@ int playlist_resume(void)
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;
}
@@ -2572,8 +2592,15 @@ unsigned int playlist_get_filename_crc32(struct playlist_info *playlist,
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);
+ const char *basename;
+#ifdef HAVE_MULTIVOLUME
+ /* remove the volume identifier it might change just use the relative part*/
+ path_strip_volume(track_info.filename, &basename, false);
+ if (basename == NULL)
+#endif
+ basename = track_info.filename;
+ NOTEF("%s: %s", __func__, basename);
+ return crc_32(basename, strlen(basename), -1);
}
/* resume a playlist track with the given crc_32 of the track name. */
@@ -2954,7 +2981,7 @@ int playlist_set_current(struct playlist_info* playlist)
if (playlist->indices && playlist->indices != current_playlist.indices)
{
memcpy((void*)current_playlist.indices, (void*)playlist->indices,
- playlist->max_playlist_size*sizeof(int));
+ playlist->max_playlist_size*sizeof(*playlist->indices));
#ifdef HAVE_DIRCACHE
copy_filerefs(current_playlist.dcfrefs, playlist->dcfrefs,
playlist->max_playlist_size);
diff --git a/apps/playlist_viewer.c b/apps/playlist_viewer.c
index b0f30ea8a1..a94e07643e 100644
--- a/apps/playlist_viewer.c
+++ b/apps/playlist_viewer.c
@@ -484,19 +484,12 @@ static int show_track_info(struct playlist_entry *current_track)
struct mp3entry id3;
bool id3_retrieval_successful = false;
-#if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE)
- if (tagcache_fill_tags(&id3, current_track->name))
- id3_retrieval_successful = true;
- else
-#endif
+ int fd = open(current_track->name, O_RDONLY);
+ if (fd >= 0)
{
- 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);
- }
+ if (get_metadata(&id3, fd, current_track->name))
+ id3_retrieval_successful = true;
+ close(fd);
}
return id3_retrieval_successful &&
@@ -826,6 +819,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;
}
@@ -875,6 +869,7 @@ enum playlist_viewer_result playlist_viewer_ex(const char* filename)
gui_synclist_set_icon_callback(&playlist_lists,
global_settings.playlist_viewer_icons?
&playlist_callback_icons:NULL);
+ gui_synclist_set_title(&playlist_lists, playlist_lists.title, playlist_lists.title_icon);
gui_synclist_draw(&playlist_lists);
gui_synclist_speak_item(&playlist_lists);
break;
diff --git a/apps/plugin.c b/apps/plugin.c
index aa69b9ca03..eb76eb7753 100644
--- a/apps/plugin.c
+++ b/apps/plugin.c
@@ -7,7 +7,7 @@
* \/ \/ \/ \/ \/
* $Id$
*
- * Copyright (C) 2002 Bjrn 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"
@@ -807,6 +808,14 @@ static const struct plugin_api rockbox_api = {
battery_current,
onplay_show_playlist_menu,
queue_remove_from_head,
+ core_set_keyremap,
+ plugin_reserve_buffer,
+#ifdef HAVE_MULTIVOLUME
+ path_strip_volume,
+#endif
+ sys_poweroff,
+ sys_reboot,
+ browse_id3,
};
static int plugin_buffer_handle;
@@ -961,6 +970,27 @@ int plugin_load(const char* plugin, const void* parameter)
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)
@@ -990,14 +1020,11 @@ 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);
+ &buflib_ops_locked);
}
if (buffer_size)
diff --git a/apps/plugin.h b/apps/plugin.h
index 8ade3a05ac..a487f64168 100644
--- a/apps/plugin.h
+++ b/apps/plugin.h
@@ -7,7 +7,7 @@
* \/ \/ \/ \/ \/
* $Id$
*
- * Copyright (C) 2002 Bjrn 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,6 +49,7 @@
char* strncpy(char *, const char *, size_t);
void* plugin_get_buffer(size_t *buffer_size);
+size_t plugin_reserve_buffer(size_t buffer_size);
int plugin_open(const char *plugin, const char *parameter);
#ifndef __PCTOOL__
@@ -111,6 +112,7 @@ int plugin_open(const char *plugin, const char *parameter);
#include "core_alloc.h"
#include "screen_access.h"
#include "onplay.h"
+#include "screens.h"
#ifdef HAVE_ALBUMART
#include "albumart.h"
@@ -155,7 +157,7 @@ int plugin_open(const char *plugin, const char *parameter);
#define PLUGIN_MAGIC 0x526F634B /* RocK */
/* increase this every time the api struct changes */
-#define PLUGIN_API_VERSION 249
+#define PLUGIN_API_VERSION 251
/* update this to latest version if a change to the api struct breaks
backwards compatibility (and please take the opportunity to sort in any
@@ -934,6 +936,15 @@ struct plugin_api {
int (*battery_current)(void);
void (*onplay_show_playlist_menu)(const char* path, void (*playlist_insert_cb));
void (*queue_remove_from_head)(struct event_queue *q, long id);
+ int (*core_set_keyremap)(struct button_mapping* core_keymap, int count);
+ size_t (*plugin_reserve_buffer)(size_t buffer_size);
+#ifdef HAVE_MULTIVOLUME
+ int (*path_strip_volume)(const char *name, const char **nameptr, bool greedy);
+#endif
+ void (*sys_poweroff)(void);
+ void (*sys_reboot)(void);
+ bool (*browse_id3)(struct mp3entry *id3,
+ int playlist_display_index, int playlist_amount);
};
/* plugin header */
diff --git a/apps/plugins/CATEGORIES b/apps/plugins/CATEGORIES
index bb0960f501..f2ab4843c2 100644
--- a/apps/plugins/CATEGORIES
+++ b/apps/plugins/CATEGORIES
@@ -47,7 +47,9 @@ jackpot,games
jewels,games
jpeg,viewers
keybox,apps
+keyremap,apps
lamp,apps
+lastfm_scrobbler,apps
logo,demos
lrcplayer,apps
lua,viewers
@@ -67,6 +69,7 @@ minesweeper,games
mosaique,demos
mp3_encoder,apps
mpegplayer,viewers
+multiboot_select,apps
nim,games
open_plugins,viewers
oscilloscope,demos
@@ -170,10 +173,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 ab77dcde58..942f9a5b20 100644
--- a/apps/plugins/SOURCES
+++ b/apps/plugins/SOURCES
@@ -11,6 +11,8 @@ cube.c
dict.c
jackpot.c
keybox.c
+keyremap.c
+lastfm_scrobbler.c
logo.c
lrcplayer.c
mosaique.c
@@ -156,6 +158,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
@@ -202,6 +209,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
@@ -218,5 +226,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/battery_bench.c b/apps/plugins/battery_bench.c
index d8e29d73ca..17d3b918cf 100644
--- a/apps/plugins/battery_bench.c
+++ b/apps/plugins/battery_bench.c
@@ -462,6 +462,7 @@ static void thread(void)
in_usb_mode = false;
break;
case SYS_POWEROFF:
+ case SYS_REBOOT:
exit_reason = "power off";
exit = true;
break;
diff --git a/apps/plugins/chessbox/chessbox.c b/apps/plugins/chessbox/chessbox.c
index 13df4f177e..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:
@@ -848,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/chopper.c b/apps/plugins/chopper.c
index 70763a1b67..392b840317 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)
diff --git a/apps/plugins/imageviewer/imageviewer.c b/apps/plugins/imageviewer/imageviewer.c
index 0dd140d1ab..e30a98ef68 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;
}
@@ -428,7 +435,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 +456,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)
@@ -616,11 +632,19 @@ static int scroll_bmp(struct image_info *info)
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;
diff --git a/apps/plugins/imageviewer/imageviewer_button.h b/apps/plugins/imageviewer/imageviewer_button.h
index e6cd2ac089..b8b8c3baf7 100644
--- a/apps/plugins/imageviewer/imageviewer_button.h
+++ b/apps/plugins/imageviewer/imageviewer_button.h
@@ -529,7 +529,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
diff --git a/apps/plugins/keyremap.c b/apps/plugins/keyremap.c
new file mode 100644
index 0000000000..a4ce1c48e6
--- /dev/null
+++ b/apps/plugins/keyremap.c
@@ -0,0 +1,2131 @@
+/***************************************************************************
+ * __________ __ ___.
+ * 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 TEST_COUNTDOWN_MS 1590
+
+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 ),
+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);
+}
+
+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)
+{
+ 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("keyremap create temp entry count: %d", entry_count);
+ logf("keyremap bytes: %ld, avail: %ld", 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 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; }
+
+ 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)
+ 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)
+ {
+ 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;;/* (ctx_count + ctx_count + act_count + 1) */
+
+ if (entry_count <= 3)
+ 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;
+
+ rb->browse_context_init(&browse, SHOW_ALL, BROWSE_SELECTONLY, "Select Keymap",
+ Icon_Plugin, "/", NULL);
+
+ browse.buf = buf;
+ browse.bufsize = sizeof(buf);
+
+ 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;
+ for (int i=0;i < LAST_CONTEXT_PLACEHOLDER;i++)
+ {
+ if (rb->strncasecmp(pctx, context_name(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);
+ 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",
+ context_name(ctx_data.ctx_map[i].context),
+ "Select$to add$actions");
+ else
+ rb->snprintf(buf, buf_len, ctxfmt, context_name(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, context_name(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 >", context_name(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 context_name(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
+ {
+ 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_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(lists, 1, true);
+ *action = ACTION_NONE;
+ }
+ }
+ }
+ else if (*action == ACTION_STD_CANCEL)
+ {
+ keyset.view_lastcol = printcell_increment_column(lists, -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_limit_scroll(&lists,true);
+ rb->gui_synclist_select_item(&lists, selected_item);
+ printcell_enable(&lists, false, 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))
+ {
+ printcell_enable(&lists, true, true);
+ keyset.view_columns = printcell_set_columns(&lists, ACTVIEW_HEADER, Icon_Rockbox);
+ int curcol = printcell_increment_column(&lists, 0, true);
+ 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(&lists, 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(&lists, true, 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,LIST_WRAP_UNLESS_HELD))
+ 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/lastfm_scrobbler.c b/apps/plugins/lastfm_scrobbler.c
new file mode 100644
index 0000000000..3269f1820b
--- /dev/null
+++ b/apps/plugins/lastfm_scrobbler.c
@@ -0,0 +1,609 @@
+/***************************************************************************
+ * __________ __ ___.
+ * 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:
+http://www.audioscrobbler.net/wiki/Portable_Player_Logging
+*/
+
+#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_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 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
+
+#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 ".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 */
+
+/****************** globals ******************/
+unsigned char **language_strings; /* for use with str() macro; must be init */
+/* communication to the worker thread */
+static struct
+{
+ bool exiting; /* signal to the thread that we want to exit */
+ 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;
+
+static struct
+{
+ char *buf;
+ int pos;
+ size_t size;
+ bool pending;
+ bool force_flush;
+} gCache;
+
+static struct
+{
+ int savepct;
+ bool playback;
+ bool verbose;
+} gConfig;
+
+static struct configdata config[] =
+{
+ {TYPE_INT, 0, 100, { .int_p = &gConfig.savepct }, "SavePct", NULL},
+ {TYPE_BOOL, 0, 1, { .bool_p = &gConfig.playback }, "Playback", NULL},
+ {TYPE_BOOL, 0, 1, { .bool_p = &gConfig.verbose }, "Verbose", NULL},
+};
+const int gCfg_sz = sizeof(config)/sizeof(*config);
+/****************** config functions *****************/
+static void config_set_defaults(void)
+{
+ gConfig.savepct = 50;
+ gConfig.playback = false;
+ gConfig.verbose = true;
+}
+
+static int config_settings_menu(void)
+{
+ 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_RESUME_PLAYBACK),
+ "Save Threshold",
+ "Verbose",
+ 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(str(LANG_RESUME_PLAYBACK), &gConfig.playback);
+ break;
+ case 1:
+ rb->set_int("Save Threshold", "%", UNIT_PERCENT,
+ &gConfig.savepct, NULL, 10, 0, 100, NULL );
+ break;
+ case 2:
+ rb->set_bool("Verbose", &gConfig.verbose);
+ break;
+ case 3: /*sep*/
+ continue;
+ case 4:
+ return -1;
+ break;
+ case 5:
+ {
+ int res = configfile_save(CFG_FILE, config, gCfg_sz, CFG_VER);
+ if (res >= 0)
+ {
+ 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 ******************/
+
+int scrobbler_init(void)
+{
+ gCache.buf = rb->plugin_get_buffer(&gCache.size);
+
+ size_t reqsz = SCROBBLER_MAX_CACHE*SCROBBLER_CACHE_LEN;
+ 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.pos = 0;
+ gCache.pending = false;
+ gCache.force_flush = true;
+ logf("Scrobbler Initialized");
+ return 1;
+}
+
+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 file", __func__);
+ rb->memset(path, 0, size);
+ }
+}
+
+static void scrobbler_write_cache(void)
+{
+ int i;
+ int fd;
+ logf("%s", __func__);
+ char scrobbler_file[MAX_PATH];
+ 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)
+ {
+ 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);
+ }
+ }
+
+ /* write the cache entries */
+ fd = rb->open(scrobbler_file, O_WRONLY | O_APPEND);
+ if(fd >= 0)
+ {
+ logf("SCROBBLER: writing %d entries", gCache.pos);
+ /* copy data to temporary storage in case data moves during I/O */
+ char temp_buf[SCROBBLER_CACHE_LEN];
+ for ( i=0; i < gCache.pos; i++ )
+ {
+ logf("SCROBBLER: write %d", i);
+ char* scrobbler_buf = gCache.buf;
+ ssize_t len = rb->strlcpy(temp_buf, scrobbler_buf+(SCROBBLER_CACHE_LEN*i),
+ sizeof(temp_buf));
+ if (rb->write(fd, temp_buf, len) != len)
+ break;
+ }
+ rb->close(fd);
+ }
+ else
+ {
+ logf("SCROBBLER: error writing file");
+ }
+
+ /* clear even if unsuccessful - don't want to overflow the buffer */
+ gCache.pos = 0;
+}
+
+#if USING_STORAGE_CALLBACK
+static void scrobbler_flush_callback(void)
+{
+ (void) gCache.force_flush;
+ 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 inline char* str_chk_valid(char *s, char *alt)
+{
+ return (s != NULL ? s : alt);
+}
+
+static unsigned long scrobbler_get_threshold(unsigned long length)
+{
+ /* length is assumed to be in miliseconds */
+ return length / 100 * gConfig.savepct;
+
+}
+
+static void scrobbler_add_to_cache(const struct mp3entry *id)
+{
+ static uint32_t last_crc = 0;
+ int trk_info_len = 0;
+
+ if ( gCache.pos >= SCROBBLER_MAX_CACHE )
+ scrobbler_write_cache();
+
+ char rating = 'S'; /* Skipped */
+ char* scrobbler_buf = gCache.buf;
+
+ logf("SCROBBLER: add_to_cache[%d]", gCache.pos);
+
+ 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);
+
+ char* artist = id->artist ? id->artist : id->albumartist;
+
+ int ret = rb->snprintf(&scrobbler_buf[(SCROBBLER_CACHE_LEN*gCache.pos)],
+ SCROBBLER_CACHE_LEN,
+ "%s\t%s\t%s\t%s\t%d\t%c%n\t%ld\t%s\n",
+ str_chk_valid(artist, UNTAGGED),
+ str_chk_valid(id->album, ""),
+ str_chk_valid(id->title, ""),
+ tracknum,
+ (int)(id->length / 1000),
+ rating,
+ &trk_info_len,
+ get_timestamp(),
+ str_chk_valid(id->mb_track_id, ""));
+
+ if ( ret >= SCROBBLER_CACHE_LEN )
+ {
+ logf("SCROBBLER: entry too long:");
+ logf("SCROBBLER: %s", id->path);
+ }
+ else
+ {
+ uint32_t crc = rb->crc_32(&scrobbler_buf[(SCROBBLER_CACHE_LEN*gCache.pos)],
+ trk_info_len, 0xFFFFFFFF);
+ if (crc != last_crc)
+ {
+ last_crc = crc;
+ logf("Added %s", scrobbler_buf);
+ gCache.pos++;
+#if USING_STORAGE_CALLBACK
+ rb->register_storage_idle_func(scrobbler_flush_callback);
+#endif
+ }
+ else
+ logf("SCROBBLER: skipping repeat entry: %s", id->path);
+ }
+
+}
+
+static void scrobbler_flush_cache(void)
+{
+ logf("%s", __func__);
+ /* Add any pending entries to the cache */
+ if (gCache.pending)
+ {
+ 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 scrobbler_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 )
+ check for blank artist or track name */
+ if ((id3->elapsed > scrobbler_get_threshold(id3->length))
+ || (!id3->artist && !id3->albumartist) || !id3->title)
+ {
+ 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("flag %d", te->flags);
+ return strflags[te->flags&0x7];
+}
+
+#endif
+static void scrobbler_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, scrobbler_change_event);
+ rb->remove_event(PLAYBACK_EVENT_TRACK_FINISH, scrobbler_finish_event);
+}
+
+static void events_register(void)
+{
+ rb->add_event(PLAYBACK_EVENT_TRACK_CHANGE, scrobbler_change_event);
+ rb->add_event(PLAYBACK_EVENT_TRACK_FINISH, scrobbler_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:
+ events_register();
+ rb->beep_play(1500, 100, 1000);
+ break;
+ case SYS_POWEROFF:
+ case SYS_REBOOT:
+ gCache.force_flush = true;
+ /*fall through*/
+ case EV_EXIT:
+#if USING_STORAGE_CALLBACK
+ rb->unregister_storage_idle_func(scrobbler_flush_callback, !in_usb);
+#else
+ if (!in_usb)
+ scrobbler_flush_cache();
+#endif
+ events_unregister();
+ return;
+ case EV_FLUSHCACHE:
+ scrobbler_flush_cache();
+ rb->queue_reply(&gThread.queue, 0);
+ break;
+ default:
+ logf("default %ld", ev.id);
+ break;
+ }
+ }
+}
+
+void thread_create(void)
+{
+ /* put the thread's queue in the bcast 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 one gets loaded */
+static bool 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
+ };
+
+ 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 */
+ rb->queue_send(&gThread.queue, EV_FLUSHCACHE, 0);
+ if (gConfig.verbose)
+ rb->splashf(2*HZ, "%s Cache Flushed", str(LANG_AUDIOSCROBBLER));
+ break;
+
+ case 2: /* exit plugin - quit */
+ if(rb->gui_syncyesno_run(&quit_prompt, NULL, NULL) == YESNO_YES)
+ {
+ thread_quit();
+ if (reenter)
+ rb->plugin_tsr(NULL); /* remove TSR cb */
+ return !reenter;
+ }
+
+ if(!reenter)
+ return false;
+
+ break;
+
+ case 3: /* back to menu */
+ return false;
+ }
+ }
+}
+
+/****************** main ******************/
+static int plugin_main(const void* parameter)
+{
+ (void)parameter;
+
+ rb->memset(&gThread, 0, sizeof(gThread));
+ if (gConfig.verbose)
+ rb->splashf(HZ / 2, "%s Started",str(LANG_AUDIOSCROBBLER));
+ logf("%s: %s Started", __func__, str(LANG_AUDIOSCROBBLER));
+
+ rb->plugin_tsr(exit_tsr); /* stay resident */
+
+ thread_create();
+
+ if (gConfig.playback)
+ 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;
+ language_strings = rb->language_strings;
+ if (scrobbler_init() < 0)
+ return PLUGIN_ERROR;
+
+ 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));
+ }
+
+ int ret = plugin_main(parameter);
+ return ret;
+}
diff --git a/apps/plugins/lib/action_helper.h b/apps/plugins/lib/action_helper.h
index 58d9c6c303..53f5c840f8 100644
--- a/apps/plugins/lib/action_helper.h
+++ b/apps/plugins/lib/action_helper.h
@@ -27,7 +27,7 @@
*/
#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);
diff --git a/apps/plugins/lib/action_helper.pl b/apps/plugins/lib/action_helper.pl
index 1dfdcfd070..742419e23b 100755
--- a/apps/plugins/lib/action_helper.pl
+++ b/apps/plugins/lib/action_helper.pl
@@ -140,10 +140,12 @@ 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
diff --git a/apps/plugins/lib/button_helper.h b/apps/plugins/lib/button_helper.h
index 1197b172b0..4087ba898a 100644
--- a/apps/plugins/lib/button_helper.h
+++ b/apps/plugins/lib/button_helper.h
@@ -32,7 +32,9 @@ struct available_button
* 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
index 45c3fd9073..192df18d7f 100755
--- a/apps/plugins/lib/button_helper.pl
+++ b/apps/plugins/lib/button_helper.pl
@@ -26,12 +26,15 @@ 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/)
{
@@ -53,6 +56,8 @@ print <<EOF
#include "button.h"
#include "button_helper.h"
+const size_t button_helper_maxbuffer = $len_max_button;
+
static const struct available_button buttons[$count] = {
EOF
;
diff --git a/apps/plugins/lua/rbdefines_helper.pl b/apps/plugins/lua/rbdefines_helper.pl
index 5fb0946a6a..095ec55515 100755
--- a/apps/plugins/lua/rbdefines_helper.pl
+++ b/apps/plugins/lua/rbdefines_helper.pl
@@ -55,6 +55,8 @@ if ($def_type eq "rb_defines") {
'^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/rocklib.c b/apps/plugins/lua/rocklib.c
index 6131a479db..cadc8be6ac 100644
--- a/apps/plugins/lua/rocklib.c
+++ b/apps/plugins/lua/rocklib.c
@@ -1090,6 +1090,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/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/multiboot_select.c b/apps/plugins/multiboot_select.c
new file mode 100644
index 0000000000..63babbb2c1
--- /dev/null
+++ b/apps/plugins/multiboot_select.c
@@ -0,0 +1,346 @@
+/***************************************************************************
+ * __________ __ ___.
+ * 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))) {
+ int r = rb->snprintf(tmpbuf, sizeof(tmpbuf), "/<%d>/%s/%s/%s",
+ vol, ent->d_name, bootdir, BOOTFILE);
+ if(r < 0 || (size_t)r >= sizeof(tmpbuf))
+ continue;
+
+ if(check_firmware(tmpbuf)) {
+ rb->snprintf(roots[nroots], MAX_PATH, "/<%d>/%s",
+ vol, ent->d_name);
+ 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/pictureflow/pictureflow.c b/apps/plugins/pictureflow/pictureflow.c
index bee1f580f7..2f075a7e61 100644
--- a/apps/plugins/pictureflow/pictureflow.c
+++ b/apps/plugins/pictureflow/pictureflow.c
@@ -60,6 +60,8 @@ static fb_data *lcd_fb;
#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
@@ -151,10 +153,6 @@ const struct button_mapping pf_context_buttons[] =
|| (CONFIG_KEYPAD == IPOD_3G_PAD) \
|| (CONFIG_KEYPAD == IPOD_4G_PAD) \
|| (CONFIG_KEYPAD == MPIO_HD300_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},
{PF_QUIT, BUTTON_MENU|BUTTON_REPEAT, BUTTON_MENU},
#elif CONFIG_KEYPAD == IAUDIO_M3_PAD
{PF_QUIT, BUTTON_RC_REC, BUTTON_NONE},
@@ -177,6 +175,8 @@ const struct button_mapping pf_context_buttons[] =
{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},
@@ -268,7 +268,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
@@ -278,7 +277,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"
@@ -300,8 +299,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 {
@@ -368,6 +373,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 */
};
@@ -451,6 +457,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
@@ -475,7 +506,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_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))
@@ -496,6 +533,7 @@ static int itilt;
static PFreal offsetX;
static PFreal offsetY;
static int number_of_slides;
+static bool is_initial_slide = true;
static struct pf_slide_cache pf_sldcache;
@@ -631,9 +669,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);
}
@@ -648,9 +687,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;
+ 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)
@@ -1001,6 +1044,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;
@@ -1015,6 +1103,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,
@@ -1218,9 +1307,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];
@@ -1228,7 +1316,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)
{
@@ -1242,6 +1329,49 @@ static int build_artist_index(struct tagcache_search *tcs,
}
+static int assign_album_year(void)
+{
+ 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)) {
+ 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.
@@ -1256,14 +1386,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;
@@ -1279,7 +1409,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);
@@ -1287,9 +1416,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];
@@ -1297,9 +1425,8 @@ 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++)
@@ -1356,6 +1483,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);
@@ -1405,11 +1540,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;
}
@@ -1479,7 +1616,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;
@@ -1488,7 +1624,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;
@@ -1497,7 +1632,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;
@@ -1523,6 +1658,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;
}
}
@@ -1576,6 +1714,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.
@@ -1597,29 +1743,91 @@ static char* get_track_filename(const int track_index)
-static int get_album_artist_alpha_prev_index(void)
+static int jmp_idx_prev(void)
{
- char* current_album_artist = get_album_artist(center_index);
- for (int i = center_index - 1; i > 0; i-- )
+ if (aa_cache.inspected < pf_idx.album_ct)
{
- int artist_idx = pf_idx.album_index[i].artist_idx;
- if(rb->strncmp(pf_idx.artist_names + artist_idx, current_album_artist, 1))
- current_album_artist = pf_idx.artist_names + artist_idx;
- while (i > 0 && !rb->strncmp(pf_idx.artist_names + pf_idx.album_index[i-1].artist_idx, current_album_artist, 1))
- i--;
- return i;
+#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 get_album_artist_alpha_next_index(void)
+static int jmp_idx_next(void)
{
- char* current_album_artist = get_album_artist(center_index);
- for (int i = center_index + 1; i < pf_idx.album_ct; i++ )
+ if (aa_cache.inspected < pf_idx.album_ct)
{
- int artist_idx = pf_idx.album_index[i].artist_idx;
- if(rb->strncmp(pf_idx.artist_names + artist_idx, current_album_artist, 1))
- return i;
+#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;
}
@@ -2026,13 +2234,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;
}
@@ -2052,8 +2254,6 @@ static bool incremental_albumart_cache(bool verbose)
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;
@@ -2065,8 +2265,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));
@@ -2074,13 +2272,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;
@@ -2195,12 +2395,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();
}
}
}
@@ -2527,7 +2730,6 @@ static inline bool load_and_prepare_surface(const int slide_index,
*/
bool load_new_slide(void)
{
- buf_ctx_lock();
if (wants_to_quit)
return false;
@@ -2581,7 +2783,6 @@ bool load_new_slide(void)
pf_sldcache.center_idx = i;
pf_sldcache.left_idx = i;
pf_sldcache.right_idx = i;
- buf_ctx_unlock();
return true;
}
}
@@ -2615,7 +2816,6 @@ bool load_new_slide(void)
{
if (pf_sldcache.free == -1 && !free_slide_prio(prio_l))
{
- buf_ctx_unlock();
return false;
}
@@ -2624,14 +2824,12 @@ bool load_new_slide(void)
{
lla_insert_before(&pf_sldcache.used, i, pf_sldcache.left_idx);
pf_sldcache.left_idx = i;
- buf_ctx_unlock();
return true;
}
} else if(right < number_of_slides - 1)
{
if (pf_sldcache.free == -1 && !free_slide_prio(prio_r))
{
- buf_ctx_unlock();
return false;
}
@@ -2640,7 +2838,6 @@ bool load_new_slide(void)
{
lla_insert_after(i, pf_sldcache.right_idx);
pf_sldcache.right_idx = i;
- buf_ctx_unlock();
return true;
}
}
@@ -2655,7 +2852,6 @@ insert_first_slide:
pf_sldcache.left_idx = i;
pf_sldcache.right_idx = i;
pf_sldcache.used = i;
- buf_ctx_unlock();
return true;
}
}
@@ -2664,12 +2860,10 @@ fail_and_refree:
{
lla_insert_tail(&pf_sldcache.free, i);
}
- buf_ctx_unlock();
return false;
fatal_fail:
free_all_slide_prio(0);
initialize_slide_cache();
- buf_ctx_unlock();
return false;
}
@@ -2704,13 +2898,19 @@ static inline struct dim *surface(const int slide_index)
{
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;
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);
}
/**
@@ -2971,17 +3171,97 @@ 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 interrupt_cover_out_animation(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)
+ interrupt_cover_out_animation();
+ 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);
+#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);
+ }
+ return true;
+}
+
/**
Start the animation for changing slides
*/
@@ -3215,15 +3495,18 @@ static void cleanup(void)
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),
@@ -3237,6 +3520,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) },
@@ -3251,11 +3544,37 @@ static int settings_menu(void)
selection=rb->do_menu(&settings_menu,&selection, NULL, false);
switch(selection) {
case 0:
+ 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();
+ 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, 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, 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:
rb->set_bool(rb->str(LANG_DISPLAY_FPS), &pf_cfg.show_fps);
reset_track_list();
break;
- case 1:
+ case 5:
rb->set_int(rb->str(LANG_SPACING), "", 1,
&pf_cfg.slide_spacing,
NULL, 1, 0, 100, NULL );
@@ -3263,7 +3582,7 @@ static int settings_menu(void)
reset_slides();
break;
- case 2:
+ case 6:
rb->set_int(rb->str(LANG_CENTRE_MARGIN), "", 1,
&pf_cfg.center_margin,
NULL, 1, 0, 80, NULL );
@@ -3271,51 +3590,47 @@ static int settings_menu(void)
reset_slides();
break;
- case 3:
+ case 7:
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();
break;
- case 4:
+ case 8:
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();
- 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);
break;
- case 10:
+ case 13:
rb->set_option(rb->str(LANG_BACKLIGHT),
&pf_cfg.backlight_mode, INT, backlight_options, 2, NULL);
break;
@@ -3763,7 +4078,9 @@ static bool start_playback(bool return_to_WPS)
#endif
rb->lcd_clear_display();
rb->lcd_update();
-#endif /* USEGSLIB */
+#else /* if !USEGSLIB */
+ (void) return_to_WPS;
+#endif
if (!rb->warn_on_pl_erase() || !track_list_ready())
{
@@ -3793,10 +4110,8 @@ static bool start_playback(bool return_to_WPS)
rb->playlist_start(start_index, 0, 0);
rb->playlist_get_current()->num_inserted_tracks = 0; /* prevent warn_on_pl_erase */
old_shuffle = shuffle;
- if (return_to_WPS)
- pf_cfg.last_album = center_index;
#ifdef USEGSLIB
- else
+ if (!return_to_WPS)
grey_show(true);
#endif
return true;
@@ -3808,10 +4123,13 @@ static bool start_playback(bool return_to_WPS)
*/
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;
@@ -3839,10 +4157,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();
@@ -3867,7 +4193,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);
@@ -3875,7 +4201,7 @@ 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);
}
}
@@ -3883,7 +4209,9 @@ static void draw_album_text(void)
static void set_initial_slide(const char* selected_file)
{
if (selected_file == NULL)
- set_current_slide(id3_get_index(rb->audio_current_track()));
+ set_current_slide(rb->audio_status() ?
+ id3_get_index(rb->audio_current_track()) :
+ pf_cfg.last_album);
else
{
struct mp3entry id3;
@@ -3954,8 +4282,6 @@ static int pictureflow_main(const char* selected_file)
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");
@@ -3980,32 +4306,30 @@ static int pictureflow_main(const char* selected_file)
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");
@@ -4013,7 +4337,7 @@ static int pictureflow_main(const char* selected_file)
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);
@@ -4096,7 +4420,12 @@ static int pictureflow_main(const char* selected_file)
break;
case pf_idle:
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;
}
@@ -4133,7 +4462,8 @@ static int pictureflow_main(const char* selected_file)
/* Copy offscreen buffer to LCD and give time to other threads */
- mylcd_update();
+ if (is_initial_slide == false)
+ mylcd_update();
rb->yield();
/*/ Handle buttons */
@@ -4205,24 +4535,38 @@ static int pictureflow_main(const char* selected_file)
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)
+ 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(get_album_artist_alpha_next_index());
+ set_current_slide(new_idx);
}
- else if ( pf_state == pf_show_tracks )
- select_next_album();
- break;
+ }
+ else if ( pf_state == pf_show_tracks )
+ select_next_album();
+ break;
case PF_JMP_PREV:
- if (pf_state == pf_idle || pf_state == pf_scrolling)
+ 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(get_album_artist_alpha_prev_index());
+ set_current_slide(new_idx);
}
- else if ( pf_state == pf_show_tracks )
- select_prev_album();
- break;
+ }
+ else if ( pf_state == pf_show_tracks )
+ select_prev_album();
+ break;
#if PF_PLAYBACK_CAPABLE
case PF_CONTEXT:
if (pf_cfg.auto_wps != 0 &&
@@ -4342,6 +4686,7 @@ enum plugin_status plugin_start(const void *parameter)
ret = file_id3 ? pictureflow_main(file) : pictureflow_main(NULL);
if ( ret == PLUGIN_OK || ret == PLUGIN_GOTO_WPS) {
+ pf_cfg.last_album = center_index;
if (configfile_save(CONFIG_FILE, config, CONFIG_NUM_ITEMS,
CONFIG_VERSION))
{
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/properties.c b/apps/plugins/properties.c
index fa3517726a..e5f00e307b 100644
--- a/apps/plugins/properties.c
+++ b/apps/plugins/properties.c
@@ -20,9 +20,19 @@
****************************************************************************/
#include "plugin.h"
+#if !defined(ARRAY_SIZE)
+ #define ARRAY_SIZE(x) (sizeof((x)) / sizeof((x)[0]))
+#endif
+
+enum props_types {
+ PROPS_FILE = 0,
+ PROPS_ID3,
+ PROPS_DIR
+};
+static int props_type = PROPS_FILE;
-bool its_a_dir = false;
+static struct mp3entry id3;
char str_filename[MAX_PATH];
char str_dirname[MAX_PATH];
@@ -32,27 +42,12 @@ char str_filecount[64];
char str_date[64];
char str_time[64];
-char str_title[MAX_PATH];
-char str_composer[MAX_PATH];
-char str_artist[MAX_PATH];
-char str_albumartist[MAX_PATH];
-char str_album[MAX_PATH];
-char str_genre[MAX_PATH];
-char str_comment[MAX_PATH];
-char str_year[MAX_PATH];
-char str_discnum[MAX_PATH];
-char str_tracknum[MAX_PATH];
-char str_duration[32];
-char str_bitrate[32];
-char str_frequency[32];
-
unsigned nseconds;
unsigned long nsize;
int32_t size_unit;
struct tm tm;
-int num_properties;
-
+#define NUM_FILE_PROPERTIES 5
static const unsigned char* const props_file[] =
{
ID2P(LANG_PROPERTIES_PATH), str_dirname,
@@ -60,20 +55,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_COMPOSER), str_composer,
- ID2P(LANG_PROPERTIES_ARTIST), str_artist,
- ID2P(LANG_PROPERTIES_ALBUMARTIST), str_albumartist,
- ID2P(LANG_PROPERTIES_TITLE), str_title,
- ID2P(LANG_PROPERTIES_ALBUM), str_album,
- ID2P(LANG_PROPERTIES_GENRE), str_genre,
- ID2P(LANG_PROPERTIES_COMMENT), str_comment,
- ID2P(LANG_PROPERTIES_YEAR), str_year,
- ID2P(LANG_PROPERTIES_DISCNUM), str_discnum,
- ID2P(LANG_PROPERTIES_TRACKNUM), str_tracknum,
- ID2P(LANG_PROPERTIES_DURATION), str_duration,
- ID2P(LANG_PROPERTIES_BITRATE), str_bitrate,
- ID2P(LANG_PROPERTIES_FREQUENCY), str_frequency,
};
+
+#define NUM_DIR_PROPERTIES 4
static const unsigned char* const props_dir[] =
{
ID2P(LANG_PROPERTIES_PATH), str_dirname,
@@ -107,7 +91,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)
@@ -128,81 +111,14 @@ 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))
+ if (fd >= 0)
{
- long dur = id3.length / 1000; /* seconds */
- rb->snprintf(str_composer, sizeof str_composer,
- "%s", id3.composer ? id3.composer : "");
- rb->snprintf(str_artist, sizeof str_artist,
- "%s", id3.artist ? id3.artist : "");
- rb->snprintf(str_albumartist, sizeof str_albumartist,
- "%s", id3.albumartist ? id3.albumartist : "");
- rb->snprintf(str_title, sizeof str_title,
- "%s", id3.title ? id3.title : "");
- rb->snprintf(str_album, sizeof str_album,
- "%s", id3.album ? id3.album : "");
- rb->snprintf(str_genre, sizeof str_genre,
- "%s", id3.genre_string ? id3.genre_string : "");
- rb->snprintf(str_comment, sizeof str_comment,
- "%s", id3.comment ? id3.comment : "");
-
- if (id3.year_string)
- rb->snprintf(str_year, sizeof str_year,
- "%s", id3.year_string);
- else if (id3.year)
- rb->snprintf(str_year, sizeof str_year,
- "%d", id3.year);
- else
- rb->snprintf(str_year, sizeof str_year,
- "%s", "");
-
- if (id3.disc_string)
- rb->snprintf(str_discnum, sizeof str_discnum,
- "%s", id3.disc_string);
- else if (id3.discnum)
- rb->snprintf(str_discnum, sizeof str_discnum,
- "%d", id3.discnum);
- else
- rb->snprintf(str_discnum, sizeof str_discnum,
- "%s", "");
-
- if (id3.track_string)
- rb->snprintf(str_tracknum, sizeof str_tracknum,
- "%s", id3.track_string);
- else if(id3.tracknum)
- rb->snprintf(str_tracknum, sizeof str_tracknum,
- "%d", id3.tracknum);
- else
- rb->snprintf(str_tracknum, sizeof str_tracknum,
- "%s", "");
-
- rb->snprintf(str_bitrate, sizeof str_bitrate,
- "%d kbps", id3.bitrate ? : 0);
- rb->snprintf(str_frequency, sizeof str_frequency,
- "%ld Hz", id3.frequency ? : 0);
- num_properties += 12;
-
- 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++;
- }
+ if (rb->get_metadata(&id3, fd, selected_file))
+ props_type = PROPS_ID3;
+
+ rb->close(fd);
}
- rb->close(fd);
found = true;
break;
}
@@ -265,7 +181,7 @@ static bool _dir_properties(DPS *dps)
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->str(units[log]));
rb->lcd_update();
}
@@ -314,7 +230,6 @@ static bool dir_properties(const char* selected_file, DPS *dps)
nsize = (long) (dps->bc >> (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;
}
@@ -328,35 +243,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]);
+ int32_t id = P2ID((props_type == PROPS_DIR ? props_dir : props_file)[selected_item]);
rb->talk_id(id, false);
switch (id)
{
@@ -394,9 +294,6 @@ 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_decimal(nseconds, UNIT_TIME, 0, true);
- break;
case LANG_PROPERTIES_SUBDIRS:
rb->talk_number(dps->dc, true);
break;
@@ -422,10 +319,10 @@ enum plugin_status plugin_start(const void* parameter)
#endif
static DPS dps = {
- .len = MAX_PATH,
- .dc = 0,
- .fc = 0,
- .bc = 0,
+ .len = MAX_PATH,
+ .dc = 0,
+ .fc = 0,
+ .bc = 0,
};
/* determine if it's a file or a directory */
@@ -446,7 +343,7 @@ 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;
+ props_type = info.attribute & ATTR_DIRECTORY ? PROPS_DIR : PROPS_FILE;
found = true;
break;
}
@@ -464,7 +361,7 @@ enum plugin_status plugin_start(const void* parameter)
}
/* get the info depending on its_a_dir */
- if(!(its_a_dir ? dir_properties(file, &dps) : file_properties(file)))
+ if(!(props_type == PROPS_DIR ? dir_properties(file, &dps) : file_properties(file)))
{
/* something went wrong (to do: tell user what it was (nesting,...) */
rb->splash(0, ID2P(LANG_PROPERTIES_FAIL));
@@ -475,40 +372,51 @@ enum plugin_status plugin_start(const void* parameter)
FOR_NB_SCREENS(i)
rb->viewportmanager_theme_enable(i, true, NULL);
- 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);
-
- while(!quit)
+ if (props_type == PROPS_ID3)
+ usb = rb->browse_id3(&id3, 0, 0);
+ else
{
- 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_UNLESS_HELD))
- continue;
- switch(button)
+ rb->gui_synclist_init(&properties_lists, &get_props, &dps, 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_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);
+
+ while(!quit)
{
- case ACTION_STD_CANCEL:
- quit = true;
- break;
- default:
- if (rb->default_event_handler(button) == SYS_USB_CONNECTED)
- {
+ 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_UNLESS_HELD))
+ continue;
+ switch(button)
+ {
+ case ACTION_STD_CANCEL:
quit = true;
- usb = true;
- }
- break;
+ break;
+ default:
+ if (rb->default_event_handler(button) == SYS_USB_CONNECTED)
+ {
+ quit = true;
+ usb = true;
+ }
+ break;
+ }
}
}
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/sliding_puzzle.c b/apps/plugins/sliding_puzzle.c
index a34cb77669..af6aa40ea0 100644
--- a/apps/plugins/sliding_puzzle.c
+++ b/apps/plugins/sliding_puzzle.c
@@ -468,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 ) )
diff --git a/apps/plugins/solitaire.c b/apps/plugins/solitaire.c
index b1ede16f90..ebc042f6db 100644
--- a/apps/plugins/solitaire.c
+++ b/apps/plugins/solitaire.c
@@ -2149,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/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_usb.c b/apps/plugins/test_usb.c
new file mode 100644
index 0000000000..6bb77c40be
--- /dev/null
+++ b/apps/plugins/test_usb.c
@@ -0,0 +1,136 @@
+/***************************************************************************
+ * __________ __ ___.
+ * 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 bool 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 true;
+ case 2:
+ return false;
+ }
+ }
+}
+
+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)
+{
+ (void)parameter;
+ MENUITEM_STRINGLIST(menu, "USB test menu", NULL,
+ "Start", "Quit");
+
+ switch(rb->do_menu(&menu, NULL, NULL, false)) {
+ case 0:
+ run_tsr();
+ rb->splashf(HZ, "Thread started");
+ return PLUGIN_OK;
+ case 1:
+ return PLUGIN_OK;
+ default:
+ return PLUGIN_ERROR;
+ }
+}
diff --git a/apps/plugins/viewers.config b/apps/plugins/viewers.config
index 8aa0ac370a..8d266966d0 100644
--- a/apps/plugins/viewers.config
+++ b/apps/plugins/viewers.config
@@ -105,4 +105,4 @@ shopper,viewers/shopper,1
lnk,viewers/windows_lnk,-
#ifdef HAVE_TAGCACHE
*,demos/pictureflow,-
-#endif \ No newline at end of file
+#endif
diff --git a/apps/rbcodec_helpers.c b/apps/rbcodec_helpers.c
index b412bb3aa4..78e068ded7 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("tdspeed", buf_s[i], &ops);
if (handles[i] <= 0)
return false;
diff --git a/apps/recorder/albumart.c b/apps/recorder/albumart.c
index 5b3658a1cb..9ff9c72f80 100644
--- a/apps/recorder/albumart.c
+++ b/apps/recorder/albumart.c
@@ -98,7 +98,7 @@ static void fix_path_part(char* path, int offset, int count)
}
#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.
diff --git a/apps/recorder/jpeg_load.c b/apps/recorder/jpeg_load.c
index 9ab42b7a9f..34d543b56e 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 */
+ (32 * 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 fd6099c8c6..b6b5affb6a 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,63 @@ 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 */
+ vp->y = text_height; /* TOP */
+ 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 */
+ vp->y = text_height - 2; /* TOP */
+ 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 +376,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,13 +410,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;
@@ -443,7 +516,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? */
@@ -698,7 +779,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);
@@ -706,18 +787,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
@@ -765,9 +842,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;
@@ -776,8 +865,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)
{
@@ -824,7 +915,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
@@ -843,13 +934,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);
@@ -926,6 +1022,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,
@@ -933,22 +1030,33 @@ 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);
@@ -958,12 +1066,13 @@ static void kbd_draw_edit_line(struct keyboard_parameters *pm,
strlcpy(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 "<". */
@@ -971,12 +1080,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, "<");
}
}
@@ -987,11 +1096,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, ">");
}
}
@@ -999,17 +1108,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
@@ -1287,6 +1398,7 @@ static void kbd_move_picker_vertical(struct keyboard_parameters *pm,
if (!global_settings.list_wraparound)
{
+#if 0 /* edit line below picker */
if (pm->y >= pm->lines)
{
pm->y = pm->lines;
@@ -1296,7 +1408,22 @@ static void kbd_move_picker_vertical(struct keyboard_parameters *pm,