From 3ce9174229de91411a9abf5381a1f335fe0c6a98 Mon Sep 17 00:00:00 2001 From: tpearson Date: Sat, 9 Jan 2010 23:52:48 +0000 Subject: [PATCH] Added abandoned KDE3 version of Amarok git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/amarok@1072335 283d02a7-25f6-0310-bc7c-ecb5cbfe19da --- AUTHORS | 15 + CMakeLists.txt | 51 + COPYING | 340 + ChangeLog | 2876 + ConfigureChecks.cmake | 34 + INSTALL | 185 + Makefile.am | 5 + Makefile.am.in | 4 + Makefile.cvs | 14 + README | 223 + TODO | 297 + acinclude.m4 | 11945 +++ aclocal.m4 | 881 + amarok/COPYING-DOCS | 397 + amarok/COPYING.LIB | 481 + amarok/HACKING | 269 + amarok/Makefile.am | 17 + amarok/VIS_PLAN | 163 + amarok/amarok.kdevelop | 239 + amarok/configure.in.bot | 336 + amarok/configure.in.in | 1138 + amarok/docs/collection_redesign.xmi | 810 + amarok/docs/use_umbrello_to_open_xmi_files | 0 amarok/src/Makefile.am | 272 + amarok/src/Options1.ui | 700 + amarok/src/Options1.ui.h | 218 + amarok/src/Options2.ui | 768 + amarok/src/Options2.ui.h | 189 + amarok/src/Options4.ui | 622 + amarok/src/Options5.ui | 530 + amarok/src/Options5.ui.h | 126 + amarok/src/Options7.ui | 81 + amarok/src/Options8.ui | 305 + amarok/src/Options8.ui.h | 22 + amarok/src/actionclasses.cpp | 665 + amarok/src/actionclasses.h | 215 + amarok/src/amarok.desktop | 114 + amarok/src/amarok.h | 345 + amarok/src/amarok.profile.xml | 60 + amarok/src/amarok_addaspodcast.desktop | 50 + amarok/src/amarok_append.desktop | 175 + amarok/src/amarok_codecinstall.desktop | 15 + amarok/src/amarok_export.h | 17 + amarok/src/amarok_play_audiocd.desktop | 55 + amarok/src/amarok_plugin.desktop | 88 + amarok/src/amarok_proxy.rb | 238 + amarok/src/amarokcore/Makefile.am | 28 + amarok/src/amarokcore/amarok.kcfg | 705 + amarok/src/amarokcore/amarokconfig.kcfgc | 8 + amarok/src/amarokcore/amarokdcophandler.cpp | 1059 + amarok/src/amarokcore/amarokdcophandler.h | 261 + amarok/src/amarokcore/amarokdcopiface.h | 247 + amarok/src/amarokcore/crashhandler.cpp | 303 + amarok/src/amarokcore/crashhandler.h | 32 + amarok/src/amarokitpc.protocol | 11 + amarok/src/amaroklastfm.protocol | 11 + amarok/src/amarokpcast.protocol | 11 + amarok/src/amarokrc | 14 + amarok/src/amarokui.rc | 20 + amarok/src/amarokui_xmms.rc | 13 + amarok/src/analyzers/Makefile.am | 30 + amarok/src/analyzers/analyzerbase.cpp | 279 + amarok/src/analyzers/analyzerbase.h | 153 + amarok/src/analyzers/analyzerfactory.cpp | 127 + amarok/src/analyzers/baranalyzer.cpp | 171 + amarok/src/analyzers/baranalyzer.h | 57 + amarok/src/analyzers/blockanalyzer.cpp | 451 + amarok/src/analyzers/blockanalyzer.h | 62 + amarok/src/analyzers/boomanalyzer.cpp | 157 + amarok/src/analyzers/boomanalyzer.h | 52 + amarok/src/analyzers/glanalyzer.cpp | 342 + amarok/src/analyzers/glanalyzer.h | 62 + amarok/src/analyzers/glanalyzer2.cpp | 333 + amarok/src/analyzers/glanalyzer2.h | 73 + amarok/src/analyzers/glanalyzer3.cpp | 480 + amarok/src/analyzers/glanalyzer3.h | 80 + amarok/src/analyzers/sonogram.cpp | 91 + amarok/src/analyzers/sonogram.h | 37 + amarok/src/analyzers/turbine.cpp | 78 + amarok/src/analyzers/turbine.h | 22 + amarok/src/app.cpp | 1487 + amarok/src/app.h | 125 + amarok/src/atomicstring.cpp | 175 + amarok/src/atomicstring.h | 191 + amarok/src/atomicstring_unittest.cpp | 62 + amarok/src/atomicurl.cpp | 127 + amarok/src/atomicurl.h | 72 + amarok/src/bcpp.cfg | 7 + amarok/src/browserToolBar.h | 32 + amarok/src/browserbar.cpp | 408 + amarok/src/browserbar.h | 103 + amarok/src/clicklineedit.cpp | 104 + amarok/src/clicklineedit.h | 59 + amarok/src/collectionbrowser.cpp | 4718 ++ amarok/src/collectionbrowser.h | 397 + amarok/src/collectiondb.cpp | 8074 ++ amarok/src/collectiondb.h | 868 + amarok/src/collectionscanner/Makefile.am | 28 + .../collectionscanner/collectionscanner.cpp | 480 + .../src/collectionscanner/collectionscanner.h | 116 + .../collectionscannerdcophandler.cpp | 49 + .../collectionscannerdcophandler.h | 43 + .../collectionscannerdcopiface.h | 37 + amarok/src/collectionscanner/main.cpp | 80 + amarok/src/colorgenerator.h | 81 + amarok/src/columnlist.cpp | 226 + amarok/src/columnlist.h | 76 + amarok/src/configdialog.cpp | 449 + amarok/src/configdialog.h | 81 + amarok/src/contextbrowser.cpp | 4501 + amarok/src/contextbrowser.h | 222 + amarok/src/coverfetcher.cpp | 678 + amarok/src/coverfetcher.h | 121 + amarok/src/covermanager.cpp | 1067 + amarok/src/covermanager.h | 166 + amarok/src/cuefile.cpp | 268 + amarok/src/cuefile.h | 82 + amarok/src/data/Amarok_1.4_Welcome.ogg | Bin 0 -> 131454 bytes amarok/src/data/Cool-Streams.xml | 81 + amarok/src/data/Makefile.am | 14 + amarok/src/data/ball.png | Bin 0 -> 1773 bytes amarok/src/data/dot.png | Bin 0 -> 890 bytes amarok/src/data/equalizer_presets.xml | 219 + amarok/src/data/firstrun.m3u | 2 + amarok/src/data/grid.png | Bin 0 -> 320 bytes amarok/src/data/magnatune_logo.png | Bin 0 -> 4193 bytes amarok/src/data/wirl1.png | Bin 0 -> 46390 bytes amarok/src/data/wirl2.png | Bin 0 -> 44209 bytes amarok/src/database_refactor/README | 9 + amarok/src/database_refactor/_Makefile.am | 35 + amarok/src/database_refactor/collectiondb.cpp | 1975 + amarok/src/database_refactor/collectiondb.h | 239 + amarok/src/database_refactor/dbenginebase.cpp | 544 + amarok/src/database_refactor/dbenginebase.h | 133 + .../src/database_refactor/sqlite/_Makefile.am | 38 + .../amarok_sqlite_dbengine_plugin.desktop | 103 + .../sqlite/sqlite_dbengine.cpp | 227 + .../sqlite/sqlite_dbengine.h | 58 + amarok/src/dbsetup.ui | 468 + amarok/src/dbsetup.ui.h | 55 + amarok/src/debug.h | 245 + amarok/src/deletedialog.cpp | 157 + amarok/src/deletedialog.h | 67 + amarok/src/deletedialogbase.ui | 135 + amarok/src/device/Makefile.am | 3 + amarok/src/device/massstorage/Makefile.am | 28 + .../amarok_massstorage-device.desktop | 110 + .../massstorage/massstoragedevicehandler.cpp | 162 + .../massstorage/massstoragedevicehandler.h | 72 + amarok/src/device/nfs/Makefile.am | 30 + .../src/device/nfs/amarok_nfs-device.desktop | 107 + amarok/src/device/nfs/nfsdevicehandler.cpp | 164 + amarok/src/device/nfs/nfsdevicehandler.h | 69 + amarok/src/device/smb/Makefile.am | 24 + .../src/device/smb/amarok_smb-device.desktop | 106 + amarok/src/device/smb/smbdevicehandler.cpp | 166 + amarok/src/device/smb/smbdevicehandler.h | 69 + amarok/src/deviceconfiguredialog.cpp | 158 + amarok/src/deviceconfiguredialog.h | 55 + amarok/src/devicemanager.cpp | 284 + amarok/src/devicemanager.h | 79 + amarok/src/directorylist.cpp | 318 + amarok/src/directorylist.h | 100 + amarok/src/dynamicmode.cpp | 399 + amarok/src/dynamicmode.h | 124 + amarok/src/editfilterdialog.cpp | 791 + amarok/src/editfilterdialog.h | 107 + amarok/src/engine/ENGINE_TODO | 13 + amarok/src/engine/Makefile.am | 27 + amarok/src/engine/akode/Makefile.am | 15 + amarok/src/engine/akode/akode-engine.cpp | 196 + amarok/src/engine/akode/akode-engine.h | 42 + amarok/src/engine/akode/akode-scope.cpp | 91 + amarok/src/engine/akode/akode-scope.h | 45 + .../engine/akode/amarok_aKode-engine.desktop | 120 + amarok/src/engine/helix/COPYING | 340 + amarok/src/engine/helix/Makefile.am | 44 + amarok/src/engine/helix/Makefile.test | 119 + amarok/src/engine/helix/TODO | 3 + .../helix/amarok_helixengine_plugin.desktop | 118 + amarok/src/engine/helix/config/Makefile.am | 15 + amarok/src/engine/helix/config/dummy.cpp | 0 .../src/engine/helix/config/helixconfig.kcfg | 44 + .../src/engine/helix/config/helixconfig.kcfgc | 7 + .../src/engine/helix/helix-configdialog.cpp | 482 + amarok/src/engine/helix/helix-configdialog.h | 121 + amarok/src/engine/helix/helix-engine.cpp | 897 + amarok/src/engine/helix/helix-engine.h | 131 + amarok/src/engine/helix/helix-errors.cpp | 457 + amarok/src/engine/helix/helix-errors.h | 27 + amarok/src/engine/helix/helix-sp/Makefile.am | 38 + .../src/engine/helix/helix-sp/Makefile.test | 388 + amarok/src/engine/helix/helix-sp/gain.cpp | 517 + amarok/src/engine/helix/helix-sp/gain.h | 58 + .../helix-include/client/include/hxclsnk.h | 230 + .../helix-include/common/container/carray.h | 358 + .../common/container/chxmapbuckets.h | 69 + .../common/container/chxmaplongtoobj.h | 286 + .../common/container/chxmapptrtoptr.h | 285 + .../common/container/chxmapstringtoob.h | 322 + .../common/container/chxmapstringtostring.h | 316 + .../helix-include/common/container/hxbuffer.h | 206 + .../helix-include/common/container/hxmap.h | 22 + .../common/container/hxmaputils.h | 211 + .../helix-include/common/container/hxstring.h | 559 + .../helix-include/common/dbgtool/hxassert.h | 727 + .../helix-include/common/include/atomicbase.h | 1566 + .../helix-include/common/include/hxausvc.h | 1729 + .../helix-include/common/include/hxauth.h | 385 + .../helix-include/common/include/hxccf.h | 106 + .../helix-include/common/include/hxcom.h | 862 + .../helix-include/common/include/hxcomm.h | 625 + .../helix-include/common/include/hxcore.h | 2110 + .../helix-include/common/include/hxengin.h | 2812 + .../helix-include/common/include/hxerror.h | 272 + .../helix-include/common/include/hxfiles.h | 2573 + .../helix-include/common/include/hxiids.h | 1899 + .../helix-include/common/include/hxmon.h | 2356 + .../helix-include/common/include/hxpiids.h | 1055 + .../helix-include/common/include/hxplugn.h | 1424 + .../common/include/hxplugncompat.h | 38 + .../helix-include/common/include/hxprefs.h | 348 + .../helix-include/common/include/hxresult.h | 583 + .../helix-include/common/include/hxtbuf.h | 64 + .../helix-include/common/include/hxtypes.h | 730 + .../helix-include/common/include/hxvalue.h | 379 + .../helix-include/common/include/hxvsrc.h | 297 + .../helix-include/common/include/hxwin.h | 1703 + .../helix-include/common/include/hxwintyp.h | 374 + .../helix-include/common/include/ihxpckts.h | 660 + .../helix-include/common/system/dllpath.h | 238 + .../helix-include/common/util/hxmangle.h | 30 + .../helix-include/common/util/hxstrutl.h | 143 + .../helix-include/runtime/hlxclib/assert.h | 51 + .../helix-include/runtime/hlxclib/limits.h | 31 + .../helix-include/runtime/hlxclib/memory.h | 29 + .../helix-include/runtime/hlxclib/stdio.h | 117 + .../helix-include/runtime/hlxclib/stdlib.h | 136 + .../helix-include/runtime/hlxclib/string.h | 254 + .../helix-include/runtime/hlxclib/sys/stat.h | 67 + .../helix-include/runtime/hlxclib/sys/types.h | 66 + .../helix-include/runtime/hlxclib/time.h | 148 + .../helix-include/runtime/safestring.h | 37 + amarok/src/engine/helix/helix-sp/helix-sp.cpp | 2108 + amarok/src/engine/helix/helix-sp/helix-sp.h | 373 + .../src/engine/helix/helix-sp/helixdefines.h | 87 + .../engine/helix/helix-sp/hspadvisesink.cpp | 668 + .../src/engine/helix/helix-sp/hspadvisesink.h | 203 + .../engine/helix/helix-sp/hspalsadevice.cpp | 2204 + .../src/engine/helix/helix-sp/hspalsadevice.h | 248 + .../src/engine/helix/helix-sp/hspauthmgr.cpp | 112 + amarok/src/engine/helix/helix-sp/hspauthmgr.h | 37 + .../src/engine/helix/helix-sp/hspcontext.cpp | 472 + amarok/src/engine/helix/helix-sp/hspcontext.h | 101 + amarok/src/engine/helix/helix-sp/hsperror.cpp | 194 + amarok/src/engine/helix/helix-sp/hsperror.h | 70 + amarok/src/engine/helix/helix-sp/hsphook.cpp | 758 + amarok/src/engine/helix/helix-sp/hsphook.h | 250 + .../src/engine/helix/helix-sp/hspvoladvise.h | 52 + amarok/src/engine/helix/helix-sp/iids.cpp | 21 + amarok/src/engine/helix/helix-sp/iir_cf.h | 546 + amarok/src/engine/helix/helix-sp/utils.cpp | 36 + amarok/src/engine/helix/helix-sp/utils.h | 22 + amarok/src/engine/helix/hxplayercontrol.cpp | 1296 + amarok/src/engine/helix/hxplayercontrol.h | 221 + amarok/src/engine/kdemm/Makefile.am | 28 + .../kdemm/amarok_kdemmengine_plugin.desktop | 119 + amarok/src/engine/kdemm/kdemmengine.cpp | 261 + amarok/src/engine/kdemm/kdemmengine.h | 83 + amarok/src/engine/mas/HOWTO | 29 + amarok/src/engine/mas/Makefile.am | 28 + amarok/src/engine/mas/TODO | 12 + .../mas/amarok_masengine_plugin.desktop | 119 + amarok/src/engine/mas/masengine.cpp | 591 + amarok/src/engine/mas/masengine.h | 108 + amarok/src/engine/nmm/HostList.cpp | 156 + amarok/src/engine/nmm/HostList.h | 63 + amarok/src/engine/nmm/HostListItem.cpp | 266 + amarok/src/engine/nmm/HostListItem.h | 90 + amarok/src/engine/nmm/Makefile.am | 47 + amarok/src/engine/nmm/NmmLocation.cpp | 59 + amarok/src/engine/nmm/NmmLocation.h | 60 + amarok/src/engine/nmm/ServerregistryPing.cpp | 60 + amarok/src/engine/nmm/ServerregistryPing.h | 55 + .../nmm/amarok_nmmengine_plugin.desktop | 122 + amarok/src/engine/nmm/icons/Makefile.am | 2 + .../nmm/icons/hi16-action-nmm_option_off.png | Bin 0 -> 113 bytes .../nmm/icons/hi16-action-nmm_option_on.png | Bin 0 -> 578 bytes .../hi16-action-nmm_option_on_readonly.png | Bin 0 -> 639 bytes amarok/src/engine/nmm/nmm-gradient-left.png | Bin 0 -> 182 bytes amarok/src/engine/nmm/nmm-gradient-right.png | Bin 0 -> 184 bytes amarok/src/engine/nmm/nmm-volume-inset.png | Bin 0 -> 931 bytes amarok/src/engine/nmm/nmm_configdialog.cpp | 258 + amarok/src/engine/nmm/nmm_configdialog.h | 125 + amarok/src/engine/nmm/nmm_configdialogbase.ui | 227 + amarok/src/engine/nmm/nmm_engine.cpp | 712 + amarok/src/engine/nmm/nmm_engine.h | 264 + amarok/src/engine/nmm/nmm_kdeconfig.kcfg | 42 + amarok/src/engine/nmm/nmm_kdeconfig.kcfgc | 7 + amarok/src/engine/void/Makefile.am | 25 + .../void/amarok_void-engine_plugin.desktop | 118 + amarok/src/engine/void/void-engine.cpp | 33 + amarok/src/engine/void/void-engine.h | 39 + amarok/src/engine/xine/Makefile.am | 31 + .../engine/xine/amarok_xine-engine.desktop | 121 + .../xine/amarok_xine-mp3_install.desktop | 9 + amarok/src/engine/xine/xine-config.cpp | 310 + amarok/src/engine/xine/xine-config.h | 131 + amarok/src/engine/xine/xine-engine.cpp | 1342 + amarok/src/engine/xine/xine-engine.h | 141 + amarok/src/engine/xine/xine-scope.c | 186 + amarok/src/engine/xine/xine-scope.h | 50 + amarok/src/engine/xine/xinecfg.kcfg | 23 + amarok/src/engine/xine/xinecfg.kcfgc | 7 + amarok/src/engine/xine/xineconfigbase.ui | 506 + amarok/src/engine/yauap/Makefile.am | 29 + .../yauap/amarok_yauap-engine_plugin.desktop | 109 + amarok/src/engine/yauap/yauap-engine.cpp | 676 + amarok/src/engine/yauap/yauap-engine.h | 107 + amarok/src/engine_fwd.h | 26 + amarok/src/enginebase.cpp | 53 + amarok/src/enginebase.h | 282 + amarok/src/enginecontroller.cpp | 802 + amarok/src/enginecontroller.h | 143 + amarok/src/engineobserver.cpp | 151 + amarok/src/engineobserver.h | 76 + amarok/src/equalizergraph.cpp | 203 + amarok/src/equalizergraph.h | 50 + amarok/src/equalizerpresetmanager.cpp | 196 + amarok/src/equalizerpresetmanager.h | 59 + amarok/src/equalizersetup.cpp | 495 + amarok/src/equalizersetup.h | 77 + amarok/src/expression.cpp | 191 + amarok/src/expression.h | 72 + amarok/src/fht.cpp | 242 + amarok/src/fht.h | 119 + amarok/src/filebrowser.cpp | 710 + amarok/src/filebrowser.h | 127 + amarok/src/firstrunwizard.ui | 302 + amarok/src/firstrunwizard.ui.h | 79 + amarok/src/hi128-app-amarok.png | Bin 0 -> 13989 bytes amarok/src/hi16-app-amarok.png | Bin 0 -> 740 bytes amarok/src/hi22-app-amarok.png | Bin 0 -> 1110 bytes amarok/src/hi32-app-amarok.png | Bin 0 -> 2355 bytes amarok/src/hi48-app-amarok.png | Bin 0 -> 4018 bytes amarok/src/hi64-app-amarok.png | Bin 0 -> 5676 bytes amarok/src/hintlineedit.cpp | 58 + amarok/src/hintlineedit.h | 27 + amarok/src/htmlview.cpp | 312 + amarok/src/htmlview.h | 44 + amarok/src/iconloader.cpp | 126 + amarok/src/images/Makefile.am | 50 + amarok/src/images/amarok_cut.png | Bin 0 -> 6279 bytes amarok/src/images/amarok_icon.svg | 933 + amarok/src/images/amarok_icon_small.svg | 1133 + amarok/src/images/amarok_logo.svg | 2344 + amarok/src/images/amarok_rocks.jpg | Bin 0 -> 15945 bytes amarok/src/images/amarok_rocks.xcf | Bin 0 -> 119195 bytes amarok/src/images/b_next.png | Bin 0 -> 946 bytes amarok/src/images/b_pause.png | Bin 0 -> 889 bytes amarok/src/images/b_play.png | Bin 0 -> 879 bytes amarok/src/images/b_prev.png | Bin 0 -> 931 bytes amarok/src/images/b_stop.png | Bin 0 -> 822 bytes amarok/src/images/back_stars_grey.png | Bin 0 -> 519 bytes amarok/src/images/currenttrack_bar_left.png | Bin 0 -> 954 bytes amarok/src/images/currenttrack_bar_mid.png | Bin 0 -> 229 bytes amarok/src/images/currenttrack_bar_right.png | Bin 0 -> 931 bytes amarok/src/images/currenttrack_pause.png | Bin 0 -> 191 bytes amarok/src/images/currenttrack_play.png | Bin 0 -> 173 bytes amarok/src/images/currenttrack_repeat.png | Bin 0 -> 200 bytes .../src/images/currenttrack_repeat_small.png | Bin 0 -> 89 bytes amarok/src/images/currenttrack_stop.png | Bin 0 -> 124 bytes amarok/src/images/currenttrack_stop_small.png | Bin 0 -> 77 bytes amarok/src/images/eq_active2.png | Bin 0 -> 455 bytes amarok/src/images/eq_inactive2.png | Bin 0 -> 453 bytes amarok/src/images/icons/Makefile.am | 2 + .../images/icons/cr16-action-covermanager.png | Bin 0 -> 805 bytes .../src/images/icons/cr16-action-dynamic.png | Bin 0 -> 814 bytes .../images/icons/cr16-action-equalizer.png | Bin 0 -> 639 bytes .../images/icons/cr16-action-mini_dock.png | Bin 0 -> 429 bytes .../icons/cr16-action-player_playlist_2.png | Bin 0 -> 696 bytes .../src/images/icons/cr16-action-podcast.png | Bin 0 -> 771 bytes .../images/icons/cr16-action-podcast_new.png | Bin 0 -> 786 bytes .../src/images/icons/cr16-action-random.png | Bin 0 -> 876 bytes .../icons/cr16-action-repeat_playlist.png | Bin 0 -> 687 bytes .../images/icons/cr16-action-repeat_track.png | Bin 0 -> 242 bytes .../icons/cr16-action-visualizations.png | Bin 0 -> 522 bytes amarok/src/images/icons/cr16-action-wiki.png | Bin 0 -> 306 bytes .../icons/cr22-action-amarok_podcast.png | Bin 0 -> 1119 bytes .../icons/cr22-action-amarok_podcast_new.png | Bin 0 -> 1118 bytes .../images/icons/cr22-action-babelfish.png | Bin 0 -> 1138 bytes .../src/images/icons/cr22-action-dynamic.png | Bin 0 -> 1224 bytes .../icons/cr22-action-player_playlist_2.png | Bin 0 -> 964 bytes .../src/images/icons/cr22-action-random.png | Bin 0 -> 1279 bytes .../icons/cr22-action-repeat_playlist.png | Bin 0 -> 964 bytes .../icons/cr22-action-visualizations.png | Bin 0 -> 1018 bytes .../src/images/icons/cr64-action-dynamic.png | Bin 0 -> 2881 bytes .../src/images/icons/cr64-action-podcast.png | Bin 0 -> 2274 bytes .../images/icons/cr64-action-podcast_new.png | Bin 0 -> 2260 bytes .../src/images/icons/cr64-action-random.png | Bin 0 -> 2297 bytes .../icons/cr64-action-repeat_playlist.png | Bin 0 -> 2127 bytes .../icons/hi16-action-amarok_add_lyrics.png | Bin 0 -> 650 bytes .../icons/hi16-action-amarok_add_playlist.png | Bin 0 -> 661 bytes .../images/icons/hi16-action-amarok_album.png | Bin 0 -> 945 bytes .../icons/hi16-action-amarok_artist.png | Bin 0 -> 704 bytes .../hi16-action-amarok_audioscrobbler.png | Bin 0 -> 783 bytes .../images/icons/hi16-action-amarok_back.png | Bin 0 -> 945 bytes .../images/icons/hi16-action-amarok_burn.png | Bin 0 -> 852 bytes .../hi16-action-amarok_change_language.png | Bin 0 -> 868 bytes .../icons/hi16-action-amarok_circle.png | Bin 0 -> 801 bytes .../images/icons/hi16-action-amarok_clock.png | Bin 0 -> 935 bytes .../icons/hi16-action-amarok_collection.png | Bin 0 -> 922 bytes .../icons/hi16-action-amarok_configure.png | Bin 0 -> 927 bytes .../icons/hi16-action-amarok_covermanager.png | Bin 0 -> 730 bytes .../icons/hi16-action-amarok_device.png | Bin 0 -> 658 bytes .../icons/hi16-action-amarok_download.png | Bin 0 -> 874 bytes .../icons/hi16-action-amarok_dynamic.png | Bin 0 -> 876 bytes .../images/icons/hi16-action-amarok_edit.png | Bin 0 -> 670 bytes .../icons/hi16-action-amarok_editcopy.png | Bin 0 -> 629 bytes .../icons/hi16-action-amarok_equalizer.png | Bin 0 -> 639 bytes .../icons/hi16-action-amarok_external.png | Bin 0 -> 981 bytes .../icons/hi16-action-amarok_fastforward.png | Bin 0 -> 957 bytes .../hi16-action-amarok_favourite_genres.png | Bin 0 -> 471 bytes .../images/icons/hi16-action-amarok_files.png | Bin 0 -> 434 bytes .../icons/hi16-action-amarok_files2.png | Bin 0 -> 467 bytes .../images/icons/hi16-action-amarok_info.png | Bin 0 -> 868 bytes .../images/icons/hi16-action-amarok_love.png | Bin 0 -> 896 bytes .../icons/hi16-action-amarok_lyrics.png | Bin 0 -> 477 bytes .../icons/hi16-action-amarok_magnatune.png | Bin 0 -> 835 bytes .../icons/hi16-action-amarok_mostplayed.png | Bin 0 -> 914 bytes .../images/icons/hi16-action-amarok_music.png | Bin 0 -> 672 bytes .../images/icons/hi16-action-amarok_next.png | Bin 0 -> 943 bytes .../images/icons/hi16-action-amarok_pause.png | Bin 0 -> 920 bytes .../images/icons/hi16-action-amarok_play.png | Bin 0 -> 922 bytes .../icons/hi16-action-amarok_playlist.png | Bin 0 -> 696 bytes .../hi16-action-amarok_playlist_clear.png | Bin 0 -> 575 bytes .../hi16-action-amarok_playlist_refresh.png | Bin 0 -> 801 bytes .../icons/hi16-action-amarok_podcast.png | Bin 0 -> 640 bytes .../icons/hi16-action-amarok_podcast2.png | Bin 0 -> 786 bytes .../images/icons/hi16-action-amarok_queue.png | Bin 0 -> 738 bytes .../icons/hi16-action-amarok_random.png | Bin 0 -> 876 bytes .../icons/hi16-action-amarok_random_album.png | Bin 0 -> 874 bytes .../icons/hi16-action-amarok_random_no.png | Bin 0 -> 851 bytes .../icons/hi16-action-amarok_random_track.png | Bin 0 -> 864 bytes .../images/icons/hi16-action-amarok_redo.png | Bin 0 -> 918 bytes .../icons/hi16-action-amarok_refresh.png | Bin 0 -> 959 bytes .../icons/hi16-action-amarok_remove.png | Bin 0 -> 962 bytes ...i16-action-amarok_remove_from_playlist.png | Bin 0 -> 699 bytes .../icons/hi16-action-amarok_repeat_album.png | Bin 0 -> 1008 bytes .../icons/hi16-action-amarok_repeat_no.png | Bin 0 -> 840 bytes .../hi16-action-amarok_repeat_playlist.png | Bin 0 -> 752 bytes .../icons/hi16-action-amarok_repeat_track.png | Bin 0 -> 566 bytes .../icons/hi16-action-amarok_rescan.png | Bin 0 -> 965 bytes .../icons/hi16-action-amarok_rewind.png | Bin 0 -> 962 bytes .../images/icons/hi16-action-amarok_save.png | Bin 0 -> 572 bytes .../icons/hi16-action-amarok_scripts.png | Bin 0 -> 638 bytes .../icons/hi16-action-amarok_search.png | Bin 0 -> 646 bytes .../hi16-action-amarok_settings_engine.png | Bin 0 -> 934 bytes .../hi16-action-amarok_settings_general.png | Bin 0 -> 870 bytes .../hi16-action-amarok_settings_indicator.png | Bin 0 -> 747 bytes .../hi16-action-amarok_settings_playback.png | Bin 0 -> 756 bytes .../hi16-action-amarok_settings_view.png | Bin 0 -> 884 bytes .../images/icons/hi16-action-amarok_stop.png | Bin 0 -> 880 bytes .../images/icons/hi16-action-amarok_track.png | Bin 0 -> 889 bytes .../images/icons/hi16-action-amarok_undo.png | Bin 0 -> 919 bytes .../hi16-action-amarok_visualizations.png | Bin 0 -> 522 bytes .../images/icons/hi16-action-amarok_zoom.png | Bin 0 -> 714 bytes .../images/icons/hi16-action-collection.png | Bin 0 -> 942 bytes .../icons/hi22-action-amarok_add_lyrics.png | Bin 0 -> 901 bytes .../icons/hi22-action-amarok_add_playlist.png | Bin 0 -> 869 bytes .../images/icons/hi22-action-amarok_album.png | Bin 0 -> 1355 bytes .../icons/hi22-action-amarok_artist.png | Bin 0 -> 985 bytes .../hi22-action-amarok_audioscrobbler.png | Bin 0 -> 1212 bytes .../images/icons/hi22-action-amarok_back.png | Bin 0 -> 1413 bytes .../images/icons/hi22-action-amarok_burn.png | Bin 0 -> 1246 bytes .../hi22-action-amarok_change_language.png | Bin 0 -> 1361 bytes .../icons/hi22-action-amarok_circle.png | Bin 0 -> 1205 bytes .../images/icons/hi22-action-amarok_clock.png | Bin 0 -> 1310 bytes .../icons/hi22-action-amarok_collection.png | Bin 0 -> 1449 bytes .../icons/hi22-action-amarok_configure.png | Bin 0 -> 1438 bytes .../icons/hi22-action-amarok_covermanager.png | Bin 0 -> 1091 bytes .../icons/hi22-action-amarok_device.png | Bin 0 -> 863 bytes .../icons/hi22-action-amarok_download.png | Bin 0 -> 1279 bytes .../icons/hi22-action-amarok_dynamic.png | Bin 0 -> 1279 bytes .../images/icons/hi22-action-amarok_edit.png | Bin 0 -> 961 bytes .../icons/hi22-action-amarok_editcopy.png | Bin 0 -> 775 bytes .../icons/hi22-action-amarok_equalizer.png | Bin 0 -> 738 bytes .../icons/hi22-action-amarok_external.png | Bin 0 -> 1455 bytes .../icons/hi22-action-amarok_fastforward.png | Bin 0 -> 1443 bytes .../hi22-action-amarok_favourite_genres.png | Bin 0 -> 609 bytes .../images/icons/hi22-action-amarok_files.png | Bin 0 -> 536 bytes .../icons/hi22-action-amarok_files2.png | Bin 0 -> 577 bytes .../images/icons/hi22-action-amarok_info.png | Bin 0 -> 1211 bytes .../images/icons/hi22-action-amarok_love.png | Bin 0 -> 1252 bytes .../icons/hi22-action-amarok_lyrics.png | Bin 0 -> 619 bytes .../icons/hi22-action-amarok_magnatune.png | Bin 0 -> 1215 bytes .../icons/hi22-action-amarok_mostplayed.png | Bin 0 -> 1286 bytes .../images/icons/hi22-action-amarok_music.png | Bin 0 -> 909 bytes .../images/icons/hi22-action-amarok_next.png | Bin 0 -> 1422 bytes .../images/icons/hi22-action-amarok_pause.png | Bin 0 -> 1326 bytes .../images/icons/hi22-action-amarok_play.png | Bin 0 -> 1346 bytes .../icons/hi22-action-amarok_playlist.png | Bin 0 -> 964 bytes .../hi22-action-amarok_playlist_clear.png | Bin 0 -> 751 bytes .../hi22-action-amarok_playlist_refresh.png | Bin 0 -> 1143 bytes .../icons/hi22-action-amarok_podcast.png | Bin 0 -> 860 bytes .../icons/hi22-action-amarok_podcast2.png | Bin 0 -> 1118 bytes .../images/icons/hi22-action-amarok_queue.png | Bin 0 -> 1070 bytes .../icons/hi22-action-amarok_random.png | Bin 0 -> 1279 bytes .../icons/hi22-action-amarok_random_album.png | Bin 0 -> 1309 bytes .../icons/hi22-action-amarok_random_no.png | Bin 0 -> 1189 bytes .../icons/hi22-action-amarok_random_track.png | Bin 0 -> 1268 bytes .../images/icons/hi22-action-amarok_redo.png | Bin 0 -> 1394 bytes .../icons/hi22-action-amarok_refresh.png | Bin 0 -> 1413 bytes .../icons/hi22-action-amarok_remove.png | Bin 0 -> 1394 bytes ...i22-action-amarok_remove_from_playlist.png | Bin 0 -> 973 bytes .../icons/hi22-action-amarok_repeat_album.png | Bin 0 -> 1560 bytes .../icons/hi22-action-amarok_repeat_no.png | Bin 0 -> 1225 bytes .../hi22-action-amarok_repeat_playlist.png | Bin 0 -> 1042 bytes .../icons/hi22-action-amarok_repeat_track.png | Bin 0 -> 727 bytes .../icons/hi22-action-amarok_rescan.png | Bin 0 -> 1459 bytes .../icons/hi22-action-amarok_rewind.png | Bin 0 -> 1433 bytes .../images/icons/hi22-action-amarok_save.png | Bin 0 -> 764 bytes .../icons/hi22-action-amarok_scripts.png | Bin 0 -> 848 bytes .../icons/hi22-action-amarok_search.png | Bin 0 -> 889 bytes .../hi22-action-amarok_settings_engine.png | Bin 0 -> 1443 bytes .../hi22-action-amarok_settings_general.png | Bin 0 -> 1311 bytes .../hi22-action-amarok_settings_indicator.png | Bin 0 -> 1104 bytes .../hi22-action-amarok_settings_playback.png | Bin 0 -> 1125 bytes .../hi22-action-amarok_settings_view.png | Bin 0 -> 1300 bytes .../images/icons/hi22-action-amarok_stop.png | Bin 0 -> 1279 bytes .../images/icons/hi22-action-amarok_track.png | Bin 0 -> 1273 bytes .../images/icons/hi22-action-amarok_undo.png | Bin 0 -> 1385 bytes .../hi22-action-amarok_visualizations.png | Bin 0 -> 1018 bytes .../images/icons/hi22-action-amarok_zoom.png | Bin 0 -> 1001 bytes .../images/icons/hi22-action-collection.png | Bin 0 -> 1476 bytes .../icons/hi32-action-amarok_add_lyrics.png | Bin 0 -> 1208 bytes .../icons/hi32-action-amarok_add_playlist.png | Bin 0 -> 1210 bytes .../images/icons/hi32-action-amarok_album.png | Bin 0 -> 1689 bytes .../icons/hi32-action-amarok_artist.png | Bin 0 -> 1474 bytes .../hi32-action-amarok_audioscrobbler.png | Bin 0 -> 1600 bytes .../images/icons/hi32-action-amarok_back.png | Bin 0 -> 1693 bytes .../images/icons/hi32-action-amarok_burn.png | Bin 0 -> 1684 bytes .../hi32-action-amarok_change_language.png | Bin 0 -> 1740 bytes .../icons/hi32-action-amarok_circle.png | Bin 0 -> 1586 bytes .../images/icons/hi32-action-amarok_clock.png | Bin 0 -> 1734 bytes .../icons/hi32-action-amarok_collection.png | Bin 0 -> 1773 bytes .../icons/hi32-action-amarok_configure.png | Bin 0 -> 1748 bytes .../icons/hi32-action-amarok_covermanager.png | Bin 0 -> 1683 bytes .../icons/hi32-action-amarok_device.png | Bin 0 -> 1446 bytes .../icons/hi32-action-amarok_download.png | Bin 0 -> 1688 bytes .../icons/hi32-action-amarok_dynamic.png | Bin 0 -> 1683 bytes .../images/icons/hi32-action-amarok_edit.png | Bin 0 -> 1458 bytes .../icons/hi32-action-amarok_editcopy.png | Bin 0 -> 1409 bytes .../icons/hi32-action-amarok_equalizer.png | Bin 0 -> 1032 bytes .../icons/hi32-action-amarok_external.png | Bin 0 -> 1797 bytes .../icons/hi32-action-amarok_fastforward.png | Bin 0 -> 1684 bytes .../hi32-action-amarok_favourite_genres.png | Bin 0 -> 894 bytes .../images/icons/hi32-action-amarok_files.png | Bin 0 -> 740 bytes .../icons/hi32-action-amarok_files2.png | Bin 0 -> 788 bytes .../images/icons/hi32-action-amarok_info.png | Bin 0 -> 1646 bytes .../images/icons/hi32-action-amarok_love.png | Bin 0 -> 1584 bytes .../icons/hi32-action-amarok_lyrics.png | Bin 0 -> 846 bytes .../icons/hi32-action-amarok_magnatune.png | Bin 0 -> 1681 bytes .../icons/hi32-action-amarok_mostplayed.png | Bin 0 -> 1646 bytes .../images/icons/hi32-action-amarok_music.png | Bin 0 -> 1349 bytes .../images/icons/hi32-action-amarok_next.png | Bin 0 -> 1691 bytes .../images/icons/hi32-action-amarok_pause.png | Bin 0 -> 1670 bytes .../images/icons/hi32-action-amarok_play.png | Bin 0 -> 1644 bytes .../icons/hi32-action-amarok_playlist.png | Bin 0 -> 1481 bytes .../hi32-action-amarok_playlist_clear.png | Bin 0 -> 992 bytes .../hi32-action-amarok_playlist_refresh.png | Bin 0 -> 1510 bytes .../icons/hi32-action-amarok_podcast.png | Bin 0 -> 1178 bytes .../icons/hi32-action-amarok_podcast2.png | Bin 0 -> 1482 bytes .../images/icons/hi32-action-amarok_queue.png | Bin 0 -> 1598 bytes .../icons/hi32-action-amarok_random.png | Bin 0 -> 1683 bytes .../icons/hi32-action-amarok_random_album.png | Bin 0 -> 1731 bytes .../icons/hi32-action-amarok_random_no.png | Bin 0 -> 1661 bytes .../icons/hi32-action-amarok_random_track.png | Bin 0 -> 1666 bytes .../images/icons/hi32-action-amarok_redo.png | Bin 0 -> 1659 bytes .../icons/hi32-action-amarok_refresh.png | Bin 0 -> 1711 bytes .../icons/hi32-action-amarok_remove.png | Bin 0 -> 1638 bytes ...i32-action-amarok_remove_from_playlist.png | Bin 0 -> 1423 bytes .../icons/hi32-action-amarok_repeat_album.png | Bin 0 -> 1785 bytes .../icons/hi32-action-amarok_repeat_no.png | Bin 0 -> 1692 bytes .../hi32-action-amarok_repeat_playlist.png | Bin 0 -> 1491 bytes .../icons/hi32-action-amarok_repeat_track.png | Bin 0 -> 1508 bytes .../icons/hi32-action-amarok_rescan.png | Bin 0 -> 1776 bytes .../icons/hi32-action-amarok_rewind.png | Bin 0 -> 1680 bytes .../images/icons/hi32-action-amarok_save.png | Bin 0 -> 990 bytes .../icons/hi32-action-amarok_scripts.png | Bin 0 -> 1129 bytes .../icons/hi32-action-amarok_search.png | Bin 0 -> 1449 bytes .../hi32-action-amarok_settings_engine.png | Bin 0 -> 1747 bytes .../hi32-action-amarok_settings_general.png | Bin 0 -> 1726 bytes .../hi32-action-amarok_settings_indicator.png | Bin 0 -> 1706 bytes .../hi32-action-amarok_settings_playback.png | Bin 0 -> 1626 bytes .../hi32-action-amarok_settings_view.png | Bin 0 -> 1744 bytes .../images/icons/hi32-action-amarok_stop.png | Bin 0 -> 1561 bytes .../images/icons/hi32-action-amarok_track.png | Bin 0 -> 1672 bytes .../images/icons/hi32-action-amarok_undo.png | Bin 0 -> 1659 bytes .../hi32-action-amarok_visualizations.png | Bin 0 -> 578 bytes .../images/icons/hi32-action-amarok_zoom.png | Bin 0 -> 1546 bytes .../icons/hi32-action-audioscrobbler.png | Bin 0 -> 1634 bytes .../images/icons/hi32-action-collection.png | Bin 0 -> 1753 bytes .../icons/hi48-action-amarok_add_lyrics.png | Bin 0 -> 1754 bytes .../icons/hi48-action-amarok_add_playlist.png | Bin 0 -> 1656 bytes .../images/icons/hi48-action-amarok_album.png | Bin 0 -> 2168 bytes .../icons/hi48-action-amarok_artist.png | Bin 0 -> 1670 bytes .../hi48-action-amarok_audioscrobbler.png | Bin 0 -> 1922 bytes .../images/icons/hi48-action-amarok_back.png | Bin 0 -> 2172 bytes .../images/icons/hi48-action-amarok_burn.png | Bin 0 -> 2140 bytes .../hi48-action-amarok_change_language.png | Bin 0 -> 2407 bytes .../icons/hi48-action-amarok_circle.png | Bin 0 -> 2059 bytes .../images/icons/hi48-action-amarok_clock.png | Bin 0 -> 2275 bytes .../icons/hi48-action-amarok_collection.png | Bin 0 -> 2306 bytes .../icons/hi48-action-amarok_configure.png | Bin 0 -> 2259 bytes .../icons/hi48-action-amarok_covermanager.png | Bin 0 -> 2145 bytes .../icons/hi48-action-amarok_device.png | Bin 0 -> 1737 bytes .../icons/hi48-action-amarok_download.png | Bin 0 -> 2161 bytes .../icons/hi48-action-amarok_dynamic.png | Bin 0 -> 1947 bytes .../images/icons/hi48-action-amarok_edit.png | Bin 0 -> 1774 bytes .../icons/hi48-action-amarok_editcopy.png | Bin 0 -> 1566 bytes .../icons/hi48-action-amarok_equalizer.png | Bin 0 -> 1293 bytes .../icons/hi48-action-amarok_external.png | Bin 0 -> 2367 bytes .../icons/hi48-action-amarok_fastforward.png | Bin 0 -> 2206 bytes .../hi48-action-amarok_favourite_genres.png | Bin 0 -> 1435 bytes .../images/icons/hi48-action-amarok_files.png | Bin 0 -> 1106 bytes .../icons/hi48-action-amarok_files2.png | Bin 0 -> 1189 bytes .../images/icons/hi48-action-amarok_info.png | Bin 0 -> 2098 bytes .../images/icons/hi48-action-amarok_love.png | Bin 0 -> 2026 bytes .../icons/hi48-action-amarok_lyrics.png | Bin 0 -> 1236 bytes .../icons/hi48-action-amarok_magnatune.png | Bin 0 -> 2211 bytes .../icons/hi48-action-amarok_mostplayed.png | Bin 0 -> 2074 bytes .../images/icons/hi48-action-amarok_music.png | Bin 0 -> 1738 bytes .../images/icons/hi48-action-amarok_next.png | Bin 0 -> 2153 bytes .../images/icons/hi48-action-amarok_pause.png | Bin 0 -> 2137 bytes .../images/icons/hi48-action-amarok_play.png | Bin 0 -> 2143 bytes .../icons/hi48-action-amarok_playlist.png | Bin 0 -> 1845 bytes .../hi48-action-amarok_playlist_clear.png | Bin 0 -> 1495 bytes .../hi48-action-amarok_playlist_refresh.png | Bin 0 -> 1873 bytes .../icons/hi48-action-amarok_podcast.png | Bin 0 -> 1663 bytes .../icons/hi48-action-amarok_podcast2.png | Bin 0 -> 1874 bytes .../images/icons/hi48-action-amarok_queue.png | Bin 0 -> 2017 bytes .../icons/hi48-action-amarok_random.png | Bin 0 -> 1947 bytes .../icons/hi48-action-amarok_random_album.png | Bin 0 -> 2025 bytes .../icons/hi48-action-amarok_random_no.png | Bin 0 -> 2041 bytes .../icons/hi48-action-amarok_random_track.png | Bin 0 -> 2027 bytes .../images/icons/hi48-action-amarok_redo.png | Bin 0 -> 2128 bytes .../icons/hi48-action-amarok_refresh.png | Bin 0 -> 2195 bytes .../icons/hi48-action-amarok_remove.png | Bin 0 -> 2074 bytes ...i48-action-amarok_remove_from_playlist.png | Bin 0 -> 1674 bytes .../icons/hi48-action-amarok_repeat_album.png | Bin 0 -> 2424 bytes .../icons/hi48-action-amarok_repeat_no.png | Bin 0 -> 1981 bytes .../hi48-action-amarok_repeat_playlist.png | Bin 0 -> 1923 bytes .../icons/hi48-action-amarok_repeat_track.png | Bin 0 -> 1580 bytes .../icons/hi48-action-amarok_rescan.png | Bin 0 -> 2307 bytes .../icons/hi48-action-amarok_rewind.png | Bin 0 -> 2212 bytes .../images/icons/hi48-action-amarok_save.png | Bin 0 -> 1321 bytes .../icons/hi48-action-amarok_scripts.png | Bin 0 -> 1659 bytes .../icons/hi48-action-amarok_search.png | Bin 0 -> 1769 bytes .../hi48-action-amarok_settings_engine.png | Bin 0 -> 2276 bytes .../hi48-action-amarok_settings_general.png | Bin 0 -> 2362 bytes .../hi48-action-amarok_settings_indicator.png | Bin 0 -> 2830 bytes .../hi48-action-amarok_settings_playback.png | Bin 0 -> 1988 bytes .../hi48-action-amarok_settings_view.png | Bin 0 -> 2296 bytes .../images/icons/hi48-action-amarok_stop.png | Bin 0 -> 2007 bytes .../images/icons/hi48-action-amarok_track.png | Bin 0 -> 2155 bytes .../images/icons/hi48-action-amarok_undo.png | Bin 0 -> 2151 bytes .../hi48-action-amarok_visualizations.png | Bin 0 -> 1590 bytes .../images/icons/hi48-action-amarok_zoom.png | Bin 0 -> 1810 bytes .../images/icons/hi48-action-collection.png | Bin 0 -> 2313 bytes .../icons/hi64-action-amarok_add_lyrics.png | Bin 0 -> 1905 bytes .../icons/hi64-action-amarok_add_playlist.png | Bin 0 -> 1945 bytes .../images/icons/hi64-action-amarok_album.png | Bin 0 -> 2705 bytes .../icons/hi64-action-amarok_artist.png | Bin 0 -> 1890 bytes .../hi64-action-amarok_audioscrobbler.png | Bin 0 -> 2225 bytes .../images/icons/hi64-action-amarok_back.png | Bin 0 -> 2659 bytes .../images/icons/hi64-action-amarok_burn.png | Bin 0 -> 2685 bytes .../hi64-action-amarok_change_language.png | Bin 0 -> 2967 bytes .../icons/hi64-action-amarok_circle.png | Bin 0 -> 2575 bytes .../images/icons/hi64-action-amarok_clock.png | Bin 0 -> 2850 bytes .../icons/hi64-action-amarok_collection.png | Bin 0 -> 2920 bytes .../icons/hi64-action-amarok_configure.png | Bin 0 -> 3028 bytes .../icons/hi64-action-amarok_covermanager.png | Bin 0 -> 2677 bytes .../icons/hi64-action-amarok_device.png | Bin 0 -> 2008 bytes .../icons/hi64-action-amarok_download.png | Bin 0 -> 2699 bytes .../icons/hi64-action-amarok_dynamic.png | Bin 0 -> 2297 bytes .../images/icons/hi64-action-amarok_edit.png | Bin 0 -> 2111 bytes .../icons/hi64-action-amarok_editcopy.png | Bin 0 -> 1821 bytes .../icons/hi64-action-amarok_equalizer.png | Bin 0 -> 1306 bytes .../icons/hi64-action-amarok_external.png | Bin 0 -> 2919 bytes .../icons/hi64-action-amarok_fastforward.png | Bin 0 -> 2722 bytes .../hi64-action-amarok_favourite_genres.png | Bin 0 -> 2035 bytes .../images/icons/hi64-action-amarok_files.png | Bin 0 -> 1457 bytes .../icons/hi64-action-amarok_files2.png | Bin 0 -> 1577 bytes .../images/icons/hi64-action-amarok_info.png | Bin 0 -> 2525 bytes .../images/icons/hi64-action-amarok_love.png | Bin 0 -> 2480 bytes .../icons/hi64-action-amarok_lyrics.png | Bin 0 -> 1526 bytes .../icons/hi64-action-amarok_magnatune.png | Bin 0 -> 2754 bytes .../icons/hi64-action-amarok_mostplayed.png | Bin 0 -> 2591 bytes .../images/icons/hi64-action-amarok_music.png | Bin 0 -> 2132 bytes .../images/icons/hi64-action-amarok_next.png | Bin 0 -> 2604 bytes .../images/icons/hi64-action-amarok_pause.png | Bin 0 -> 2655 bytes .../images/icons/hi64-action-amarok_play.png | Bin 0 -> 2667 bytes .../icons/hi64-action-amarok_playlist.png | Bin 0 -> 2205 bytes .../hi64-action-amarok_playlist_clear.png | Bin 0 -> 1931 bytes .../hi64-action-amarok_playlist_refresh.png | Bin 0 -> 2212 bytes .../icons/hi64-action-amarok_podcast.png | Bin 0 -> 1980 bytes .../icons/hi64-action-amarok_podcast2.png | Bin 0 -> 2260 bytes .../images/icons/hi64-action-amarok_queue.png | Bin 0 -> 2346 bytes .../icons/hi64-action-amarok_random.png | Bin 0 -> 2297 bytes .../icons/hi64-action-amarok_random_album.png | Bin 0 -> 2584 bytes .../icons/hi64-action-amarok_random_no.png | Bin 0 -> 2273 bytes .../icons/hi64-action-amarok_random_track.png | Bin 0 -> 2439 bytes .../images/icons/hi64-action-amarok_redo.png | Bin 0 -> 2636 bytes .../icons/hi64-action-amarok_refresh.png | Bin 0 -> 2804 bytes .../icons/hi64-action-amarok_remove.png | Bin 0 -> 2504 bytes ...i64-action-amarok_remove_from_playlist.png | Bin 0 -> 2000 bytes .../icons/hi64-action-amarok_repeat_album.png | Bin 0 -> 3076 bytes .../icons/hi64-action-amarok_repeat_no.png | Bin 0 -> 2034 bytes .../hi64-action-amarok_repeat_playlist.png | Bin 0 -> 2237 bytes .../icons/hi64-action-amarok_repeat_track.png | Bin 0 -> 1880 bytes .../icons/hi64-action-amarok_rescan.png | Bin 0 -> 2846 bytes .../icons/hi64-action-amarok_rewind.png | Bin 0 -> 2736 bytes .../images/icons/hi64-action-amarok_save.png | Bin 0 -> 1533 bytes .../icons/hi64-action-amarok_scripts.png | Bin 0 -> 1886 bytes .../icons/hi64-action-amarok_search.png | Bin 0 -> 2049 bytes .../hi64-action-amarok_settings_engine.png | Bin 0 -> 2920 bytes .../hi64-action-amarok_settings_general.png | Bin 0 -> 3077 bytes .../hi64-action-amarok_settings_indicator.png | Bin 0 -> 2516 bytes .../hi64-action-amarok_settings_playback.png | Bin 0 -> 2463 bytes .../hi64-action-amarok_settings_view.png | Bin 0 -> 3072 bytes .../images/icons/hi64-action-amarok_stop.png | Bin 0 -> 2523 bytes .../images/icons/hi64-action-amarok_track.png | Bin 0 -> 2673 bytes .../images/icons/hi64-action-amarok_undo.png | Bin 0 -> 2637 bytes .../hi64-action-amarok_visualizations.png | Bin 0 -> 1504 bytes .../images/icons/hi64-action-amarok_zoom.png | Bin 0 -> 2068 bytes .../images/icons/hi64-action-collection.png | Bin 0 -> 2914 bytes .../images/icons/svg/crsc-action-dynamic.svg | 337 + .../svg/crsc-action-player_playlist_2.svg | 426 + amarok/src/images/icons/svg/sources.svg | 56296 +++++++++++++ amarok/src/images/lastfm.png | Bin 0 -> 1774 bytes amarok/src/images/loading1.png | Bin 0 -> 316 bytes amarok/src/images/loading2.png | Bin 0 -> 316 bytes amarok/src/images/menu_sidepixmap.png | Bin 0 -> 3534 bytes amarok/src/images/menu_sidepixmap.xcf | Bin 0 -> 17716 bytes amarok/src/images/more_albums.png | Bin 0 -> 5228 bytes amarok/src/images/musicbrainz.png | Bin 0 -> 1611 bytes amarok/src/images/nocover.png | Bin 0 -> 5448 bytes amarok/src/images/pl_active2.png | Bin 0 -> 403 bytes amarok/src/images/pl_inactive2.png | Bin 0 -> 401 bytes amarok/src/images/player_background.jpg | Bin 0 -> 612 bytes amarok/src/images/sbinner_stars.png | Bin 0 -> 474 bytes amarok/src/images/shadow_albumcover.png | Bin 0 -> 1277 bytes amarok/src/images/smallstar.png | Bin 0 -> 1037 bytes amarok/src/images/sound.png | Bin 0 -> 2510 bytes amarok/src/images/splash_screen.jpg | Bin 0 -> 32474 bytes amarok/src/images/star.png | Bin 0 -> 1194 bytes amarok/src/images/time_minus.png | Bin 0 -> 105 bytes amarok/src/images/time_plus.png | Bin 0 -> 109 bytes amarok/src/images/vol_speaker.png | Bin 0 -> 166 bytes amarok/src/images/volumeslider-gradient.png | Bin 0 -> 203 bytes amarok/src/images/volumeslider-handle.png | Bin 0 -> 352 bytes .../src/images/volumeslider-handle_glow.png | Bin 0 -> 744 bytes amarok/src/images/volumeslider-inset.png | Bin 0 -> 707 bytes amarok/src/images/wizard_compact.png | Bin 0 -> 20449 bytes amarok/src/images/wizard_xmms.png | Bin 0 -> 28320 bytes amarok/src/images/xine_logo.png | Bin 0 -> 6421 bytes amarok/src/inotify/inotify-syscalls.h | 61 + amarok/src/inotify/inotify.h | 113 + amarok/src/k3bexporter.cpp | 260 + amarok/src/k3bexporter.h | 103 + amarok/src/kbookmarkhandler.cpp | 52 + amarok/src/kbookmarkhandler.h | 39 + amarok/src/konquisidebar/Makefile.am | 21 + amarok/src/konquisidebar/amarok.desktop | 128 + amarok/src/konquisidebar/universalamarok.cpp | 324 + amarok/src/konquisidebar/universalamarok.h | 102 + amarok/src/ktrm.cpp | 892 + amarok/src/ktrm.h | 226 + amarok/src/lastfm.cpp | 1147 + amarok/src/lastfm.h | 300 + amarok/src/loader/Makefile.am | 16 + amarok/src/loader/loader.cpp | 241 + amarok/src/loader/loader.h | 48 + amarok/src/magnatunebrowser/Makefile.am | 16 + .../magnatunealbumdownloader.cpp | 206 + .../magnatunealbumdownloader.h | 86 + .../magnatuneartistinfobox.cpp | 155 + .../magnatunebrowser/magnatuneartistinfobox.h | 100 + .../src/magnatunebrowser/magnatunebrowser.cpp | 523 + .../src/magnatunebrowser/magnatunebrowser.h | 253 + .../magnatunedatabasehandler.cpp | 572 + .../magnatunedatabasehandler.h | 169 + .../magnatunedownloaddialog.cpp | 83 + .../magnatunedownloaddialog.h | 87 + .../magnatunedownloaddialogbase.ui | 120 + .../magnatunedownloadinfo.cpp | 264 + .../magnatunebrowser/magnatunedownloadinfo.h | 76 + .../magnatunebrowser/magnatunelistview.cpp | 91 + .../src/magnatunebrowser/magnatunelistview.h | 47 + .../magnatunelistviewitems.cpp | 180 + .../magnatunebrowser/magnatunelistviewitems.h | 81 + .../magnatunepurchasedialog.cpp | 154 + .../magnatunepurchasedialog.h | 116 + .../magnatunepurchasedialogbase.ui | 706 + .../magnatunepurchasehandler.cpp | 253 + .../magnatunepurchasehandler.h | 97 + .../magnatuneredownloaddialog.cpp | 74 + .../magnatuneredownloaddialog.h | 59 + .../magnatuneredownloaddialogbase.ui | 109 + .../magnatuneredownloadhandler.cpp | 160 + .../magnatuneredownloadhandler.h | 70 + .../src/magnatunebrowser/magnatunetypes.cpp | 291 + amarok/src/magnatunebrowser/magnatunetypes.h | 147 + .../magnatunebrowser/magnatunexmlparser.cpp | 282 + .../src/magnatunebrowser/magnatunexmlparser.h | 123 + amarok/src/main.cpp | 115 + amarok/src/mediabrowser.cpp | 3824 + amarok/src/mediabrowser.h | 668 + amarok/src/mediadevice/Makefile.am | 28 + amarok/src/mediadevice/daap/Makefile.am | 45 + amarok/src/mediadevice/daap/addhostbase.ui | 180 + .../daap/amarok_daap-mediadevice.desktop | 110 + .../src/mediadevice/daap/amarok_daapserver.rb | 418 + amarok/src/mediadevice/daap/codes.rb | 230 + amarok/src/mediadevice/daap/daapclient.cpp | 880 + amarok/src/mediadevice/daap/daapclient.h | 191 + .../mediadevice/daap/daapreader/Makefile.am | 21 + .../daapreader/authentication/Makefile.am | 22 + .../authentication/contentfetcher.cpp | 97 + .../authentication/contentfetcher.h | 57 + .../daap/daapreader/authentication/hasher.c | 205 + .../daap/daapreader/authentication/hasher.h | 43 + .../daap/daapreader/authentication/md5.c | 288 + .../daap/daapreader/authentication/md5.h | 17 + .../daapreader/authentication/portability.h | 74 + .../mediadevice/daap/daapreader/reader.cpp | 425 + .../src/mediadevice/daap/daapreader/reader.h | 111 + amarok/src/mediadevice/daap/daapserver.cpp | 87 + amarok/src/mediadevice/daap/daapserver.h | 39 + .../src/mediadevice/daap/mongrel/Makefile.am | 2 + .../daap/mongrel/http11/Makefile.am | 29 + .../daap/mongrel/http11/ext_help.h | 14 + .../mediadevice/daap/mongrel/http11/http11.c | 575 + .../mediadevice/daap/mongrel/http11/http11.rb | 2 + .../daap/mongrel/http11/http11_parser.c | 1096 + .../daap/mongrel/http11/http11_parser.h | 48 + .../daap/mongrel/http11/http11_parser.rl | 192 + .../src/mediadevice/daap/mongrel/http11/tst.h | 40 + .../daap/mongrel/http11/tst_cleanup.c | 24 + .../daap/mongrel/http11/tst_delete.c | 146 + .../mongrel/http11/tst_grow_node_free_list.c | 38 + .../daap/mongrel/http11/tst_init.c | 41 + .../daap/mongrel/http11/tst_insert.c | 192 + .../daap/mongrel/http11/tst_search.c | 68 + .../mediadevice/daap/mongrel/lib/Makefile.am | 11 + .../src/mediadevice/daap/mongrel/lib/README | 5 + .../daap/mongrel/lib/gem_plugin.rb | 287 + .../daap/mongrel/lib/gemconfigure.rb | 24 + .../mediadevice/daap/mongrel/lib/mongrel.rb | 806 + .../daap/mongrel/lib/mongrel/Makefile.am | 15 + .../daap/mongrel/lib/mongrel/cgi.rb | 181 + .../daap/mongrel/lib/mongrel/command.rb | 221 + .../daap/mongrel/lib/mongrel/configurator.rb | 378 + .../daap/mongrel/lib/mongrel/debug.rb | 201 + .../daap/mongrel/lib/mongrel/handlers.rb | 453 + .../daap/mongrel/lib/mongrel/init.rb | 13 + .../daap/mongrel/lib/mongrel/mime_types.yml | 615 + .../daap/mongrel/lib/mongrel/stats.rb | 89 + .../daap/mongrel/lib/mongrel/tcphack.rb | 18 + .../daap/mongrel/lib/rbconfig/Makefile.am | 5 + .../daap/mongrel/lib/rbconfig/datadir.rb | 23 + .../mediadevice/daap/mongrel/lib/rubygems.rb | 466 + .../mongrel/lib/rubygems/._gem_commands.rb | Bin 0 -> 82 bytes .../daap/mongrel/lib/rubygems/Makefile.am | 31 + .../daap/mongrel/lib/rubygems/builder.rb | 73 + .../daap/mongrel/lib/rubygems/cmd_manager.rb | 137 + .../daap/mongrel/lib/rubygems/command.rb | 280 + .../daap/mongrel/lib/rubygems/config_file.rb | 98 + .../mongrel/lib/rubygems/custom_require.rb | 124 + .../mongrel/lib/rubygems/dependency_list.rb | 136 + .../daap/mongrel/lib/rubygems/doc_manager.rb | 139 + .../daap/mongrel/lib/rubygems/format.rb | 71 + .../daap/mongrel/lib/rubygems/gem_commands.rb | 1441 + .../daap/mongrel/lib/rubygems/gem_openssl.rb | 46 + .../daap/mongrel/lib/rubygems/gem_runner.rb | 41 + .../lib/rubygems/incremental_fetcher.rb | 136 + .../daap/mongrel/lib/rubygems/installer.rb | 613 + .../mongrel/lib/rubygems/loadpath_manager.rb | 114 + .../daap/mongrel/lib/rubygems/old_format.rb | 156 + .../daap/mongrel/lib/rubygems/open-uri.rb | 756 + .../daap/mongrel/lib/rubygems/package.rb | 852 + .../mongrel/lib/rubygems/remote_installer.rb | 582 + .../mongrel/lib/rubygems/rubygems_version.rb | 6 + .../daap/mongrel/lib/rubygems/security.rb | 483 + .../daap/mongrel/lib/rubygems/source_index.rb | 207 + .../mongrel/lib/rubygems/specification.rb | 660 + .../daap/mongrel/lib/rubygems/timer.rb | 25 + .../mongrel/lib/rubygems/user_interaction.rb | 260 + .../daap/mongrel/lib/rubygems/validator.rb | 155 + .../daap/mongrel/lib/rubygems/version.rb | 306 + amarok/src/mediadevice/daap/proxy.cpp | 120 + amarok/src/mediadevice/daap/proxy.h | 55 + amarok/src/mediadevice/generic/Makefile.am | 31 + .../amarok_generic-mediadevice.desktop | 111 + .../generic/genericmediadevice.cpp | 1093 + .../mediadevice/generic/genericmediadevice.h | 150 + .../generic/genericmediadeviceconfigdialog.ui | 463 + .../genericmediadeviceconfigdialog.ui.h | 324 + amarok/src/mediadevice/ifp/Makefile.am | 27 + .../ifp/amarok_ifp-mediadevice.desktop | 112 + amarok/src/mediadevice/ifp/ifpmediadevice.cpp | 714 + amarok/src/mediadevice/ifp/ifpmediadevice.h | 110 + amarok/src/mediadevice/ipod/Makefile.am | 31 + .../ipod/amarok_ipod-mediadevice.desktop | 112 + .../src/mediadevice/ipod/ipodmediadevice.cpp | 2706 + amarok/src/mediadevice/ipod/ipodmediadevice.h | 176 + amarok/src/mediadevice/mtp/Makefile.am | 30 + .../mtp/amarok_mtp-mediadevice.desktop | 110 + amarok/src/mediadevice/mtp/mtpmediadevice.cpp | 1669 + amarok/src/mediadevice/mtp/mtpmediadevice.h | 205 + amarok/src/mediadevice/njb/Makefile.am | 26 + .../njb/amarok_njb-mediadevice.desktop | 109 + amarok/src/mediadevice/njb/njbmediadevice.cpp | 912 + amarok/src/mediadevice/njb/njbmediadevice.h | 187 + amarok/src/mediadevice/njb/playlist.cpp | 306 + amarok/src/mediadevice/njb/playlist.h | 64 + amarok/src/mediadevice/njb/track.cpp | 284 + amarok/src/mediadevice/njb/track.h | 72 + amarok/src/mediadevice/riokarma/Makefile.am | 30 + .../amarok_riokarma-mediadevice.desktop | 109 + .../riokarma/riokarmamediadevice.cpp | 610 + .../riokarma/riokarmamediadevice.h | 116 + amarok/src/mediadevicemanager.cpp | 174 + amarok/src/mediadevicemanager.h | 66 + amarok/src/medium.cpp | 273 + amarok/src/medium.h | 104 + amarok/src/mediumpluginmanager.cpp | 523 + amarok/src/mediumpluginmanager.h | 160 + amarok/src/metabundle.cpp | 1883 + amarok/src/metabundle.h | 545 + amarok/src/metabundlesaver.cpp | 308 + amarok/src/metabundlesaver.h | 51 + amarok/src/metadata/Makefile.am | 35 + amarok/src/metadata/aac/Makefile.am | 12 + .../src/metadata/aac/aacfiletyperesolver.cpp | 38 + amarok/src/metadata/aac/aacfiletyperesolver.h | 36 + amarok/src/metadata/asf/Makefile.am | 20 + amarok/src/metadata/asf/asfattribute.cpp | 304 + amarok/src/metadata/asf/asfattribute.h | 176 + amarok/src/metadata/asf/asffile.cpp | 547 + amarok/src/metadata/asf/asffile.h | 114 + amarok/src/metadata/asf/asfproperties.cpp | 95 + amarok/src/metadata/asf/asfproperties.h | 69 + amarok/src/metadata/asf/asftag.cpp | 202 + amarok/src/metadata/asf/asftag.h | 181 + .../asf/taglib_asffiletyperesolver.cpp | 47 + .../metadata/asf/taglib_asffiletyperesolver.h | 42 + amarok/src/metadata/audible/Makefile.am | 17 + .../metadata/audible/audibleproperties.cpp | 86 + .../src/metadata/audible/audibleproperties.h | 85 + amarok/src/metadata/audible/audibletag.cpp | 162 + amarok/src/metadata/audible/audibletag.h | 185 + .../metadata/audible/taglib_audiblefile.cpp | 121 + .../src/metadata/audible/taglib_audiblefile.h | 93 + .../taglib_audiblefiletyperesolver.cpp | 44 + .../audible/taglib_audiblefiletyperesolver.h | 36 + amarok/src/metadata/m4a/Makefile.am | 84 + amarok/src/metadata/m4a/boxfactory.cpp | 150 + amarok/src/metadata/m4a/boxfactory.h | 45 + amarok/src/metadata/m4a/itunesalbbox.cpp | 89 + amarok/src/metadata/m4a/itunesalbbox.h | 50 + amarok/src/metadata/m4a/itunesartbox.cpp | 89 + amarok/src/metadata/m4a/itunesartbox.h | 50 + amarok/src/metadata/m4a/itunescmtbox.cpp | 89 + amarok/src/metadata/m4a/itunescmtbox.h | 50 + amarok/src/metadata/m4a/itunescvrbox.cpp | 89 + amarok/src/metadata/m4a/itunescvrbox.h | 50 + amarok/src/metadata/m4a/itunesdatabox.cpp | 63 + amarok/src/metadata/m4a/itunesdatabox.h | 53 + amarok/src/metadata/m4a/itunesdaybox.cpp | 89 + amarok/src/metadata/m4a/itunesdaybox.h | 50 + amarok/src/metadata/m4a/itunesdiskbox.cpp | 93 + amarok/src/metadata/m4a/itunesdiskbox.h | 50 + amarok/src/metadata/m4a/itunesgenbox.cpp | 89 + amarok/src/metadata/m4a/itunesgenbox.h | 50 + amarok/src/metadata/m4a/itunesgrpbox.cpp | 89 + amarok/src/metadata/m4a/itunesgrpbox.h | 50 + amarok/src/metadata/m4a/itunesnambox.cpp | 89 + amarok/src/metadata/m4a/itunesnambox.h | 50 + amarok/src/metadata/m4a/itunestmpobox.cpp | 93 + amarok/src/metadata/m4a/itunestmpobox.h | 50 + amarok/src/metadata/m4a/itunestrknbox.cpp | 93 + amarok/src/metadata/m4a/itunestrknbox.h | 50 + amarok/src/metadata/m4a/ituneswrtbox.cpp | 89 + amarok/src/metadata/m4a/ituneswrtbox.h | 50 + .../src/metadata/m4a/mp4audioproperties.cpp | 75 + amarok/src/metadata/m4a/mp4audioproperties.h | 73 + .../src/metadata/m4a/mp4audiosampleentry.cpp | 146 + amarok/src/metadata/m4a/mp4audiosampleentry.h | 57 + amarok/src/metadata/m4a/mp4file.cpp | 377 + amarok/src/metadata/m4a/mp4file.h | 169 + amarok/src/metadata/m4a/mp4fourcc.cpp | 84 + amarok/src/metadata/m4a/mp4fourcc.h | 63 + amarok/src/metadata/m4a/mp4hdlrbox.cpp | 75 + amarok/src/metadata/m4a/mp4hdlrbox.h | 53 + amarok/src/metadata/m4a/mp4ilstbox.cpp | 97 + amarok/src/metadata/m4a/mp4ilstbox.h | 49 + amarok/src/metadata/m4a/mp4isobox.cpp | 76 + amarok/src/metadata/m4a/mp4isobox.h | 67 + amarok/src/metadata/m4a/mp4isofullbox.cpp | 67 + amarok/src/metadata/m4a/mp4isofullbox.h | 57 + amarok/src/metadata/m4a/mp4itunestag.cpp | 197 + amarok/src/metadata/m4a/mp4itunestag.h | 95 + amarok/src/metadata/m4a/mp4mdiabox.cpp | 111 + amarok/src/metadata/m4a/mp4mdiabox.h | 49 + amarok/src/metadata/m4a/mp4metabox.cpp | 86 + amarok/src/metadata/m4a/mp4metabox.h | 49 + amarok/src/metadata/m4a/mp4minfbox.cpp | 104 + amarok/src/metadata/m4a/mp4minfbox.h | 51 + amarok/src/metadata/m4a/mp4moovbox.cpp | 86 + amarok/src/metadata/m4a/mp4moovbox.h | 49 + amarok/src/metadata/m4a/mp4mvhdbox.cpp | 140 + amarok/src/metadata/m4a/mp4mvhdbox.h | 65 + amarok/src/metadata/m4a/mp4propsproxy.cpp | 89 + amarok/src/metadata/m4a/mp4propsproxy.h | 65 + amarok/src/metadata/m4a/mp4sampleentry.cpp | 59 + amarok/src/metadata/m4a/mp4sampleentry.h | 54 + amarok/src/metadata/m4a/mp4skipbox.cpp | 50 + amarok/src/metadata/m4a/mp4skipbox.h | 50 + amarok/src/metadata/m4a/mp4stblbox.cpp | 105 + amarok/src/metadata/m4a/mp4stblbox.h | 51 + amarok/src/metadata/m4a/mp4stsdbox.cpp | 91 + amarok/src/metadata/m4a/mp4stsdbox.h | 51 + amarok/src/metadata/m4a/mp4tagsproxy.cpp | 168 + amarok/src/metadata/m4a/mp4tagsproxy.h | 99 + amarok/src/metadata/m4a/mp4trakbox.cpp | 86 + amarok/src/metadata/m4a/mp4trakbox.h | 49 + amarok/src/metadata/m4a/mp4udtabox.cpp | 95 + amarok/src/metadata/m4a/mp4udtabox.h | 49 + .../m4a/taglib_mp4filetyperesolver.cpp | 42 + .../metadata/m4a/taglib_mp4filetyperesolver.h | 36 + amarok/src/metadata/mp4/Makefile.am | 18 + amarok/src/metadata/mp4/mp4file.cpp | 197 + amarok/src/metadata/mp4/mp4file.h | 86 + amarok/src/metadata/mp4/mp4properties.cpp | 120 + amarok/src/metadata/mp4/mp4properties.h | 86 + amarok/src/metadata/mp4/mp4tag.cpp | 127 + amarok/src/metadata/mp4/mp4tag.h | 227 + .../mp4/taglib_mp4filetyperesolver.cpp | 48 + .../metadata/mp4/taglib_mp4filetyperesolver.h | 42 + amarok/src/metadata/rmff/Makefile.am | 15 + amarok/src/metadata/rmff/rmff.cpp | 998 + amarok/src/metadata/rmff/rmff.h | 376 + .../metadata/rmff/taglib_realmediafile.cpp | 213 + .../src/metadata/rmff/taglib_realmediafile.h | 133 + .../rmff/taglib_realmediafiletyperesolver.cpp | 53 + .../rmff/taglib_realmediafiletyperesolver.h | 39 + amarok/src/metadata/speex/Makefile.am | 16 + amarok/src/metadata/speex/speexfile.cpp | 111 + amarok/src/metadata/speex/speexfile.h | 93 + amarok/src/metadata/speex/speexproperties.cpp | 171 + amarok/src/metadata/speex/speexproperties.h | 83 + .../speex/taglib_speexfiletyperesolver.cpp | 44 + .../speex/taglib_speexfiletyperesolver.h | 36 + amarok/src/metadata/tplugins.cpp | 146 + amarok/src/metadata/tplugins.h | 27 + amarok/src/metadata/trueaudio/Makefile.am | 16 + amarok/src/metadata/trueaudio/combinedtag.h | 171 + .../taglib_trueaudiofiletyperesolver.cpp | 44 + .../taglib_trueaudiofiletyperesolver.h | 36 + amarok/src/metadata/trueaudio/ttafile.cpp | 307 + amarok/src/metadata/trueaudio/ttafile.h | 178 + .../src/metadata/trueaudio/ttaproperties.cpp | 134 + amarok/src/metadata/trueaudio/ttaproperties.h | 86 + amarok/src/metadata/wav/Makefile.am | 15 + amarok/src/metadata/wav/wavfile.cpp | 114 + amarok/src/metadata/wav/wavfile.h | 92 + .../src/metadata/wav/wavfiletyperesolver.cpp | 43 + amarok/src/metadata/wav/wavfiletyperesolver.h | 36 + amarok/src/metadata/wav/wavproperties.cpp | 108 + amarok/src/metadata/wav/wavproperties.h | 85 + amarok/src/metadata/wavpack/Makefile.am | 16 + amarok/src/metadata/wavpack/combinedtag.h | 171 + .../taglib_wavpackfiletyperesolver.cpp | 44 + .../wavpack/taglib_wavpackfiletyperesolver.h | 36 + amarok/src/metadata/wavpack/wvfile.cpp | 311 + amarok/src/metadata/wavpack/wvfile.h | 160 + amarok/src/metadata/wavpack/wvproperties.cpp | 141 + amarok/src/metadata/wavpack/wvproperties.h | 86 + amarok/src/moodbar.cpp | 1386 + amarok/src/moodbar.h | 181 + amarok/src/mountpointmanager.cpp | 604 + amarok/src/mountpointmanager.h | 259 + amarok/src/multitabbar.cpp | 1308 + amarok/src/multitabbar.h | 297 + amarok/src/multitabbar_p.h | 73 + amarok/src/mydirlister.h | 37 + amarok/src/mydiroperator.cpp | 47 + amarok/src/mydiroperator.h | 35 + amarok/src/newdynamic.ui | 360 + amarok/src/organizecollectiondialog.ui | 596 + amarok/src/organizecollectiondialog.ui.h | 217 + amarok/src/osd.cpp | 948 + amarok/src/osd.h | 184 + amarok/src/pixmapviewer.cpp | 65 + amarok/src/pixmapviewer.h | 45 + amarok/src/playerwindow.cpp | 955 + amarok/src/playerwindow.h | 174 + amarok/src/playlist.cpp | 4981 ++ amarok/src/playlist.h | 549 + amarok/src/playlistbrowser.cpp | 3250 + amarok/src/playlistbrowser.h | 414 + amarok/src/playlistbrowseritem.cpp | 3746 + amarok/src/playlistbrowseritem.h | 637 + amarok/src/playlistitem.cpp | 1152 + amarok/src/playlistitem.h | 180 + amarok/src/playlistloader.cpp | 1108 + amarok/src/playlistloader.h | 207 + amarok/src/playlistselection.cpp | 222 + amarok/src/playlistselection.h | 59 + amarok/src/playlistwindow.cpp | 1243 + amarok/src/playlistwindow.h | 156 + amarok/src/plugin/Makefile.am | 16 + amarok/src/plugin/plugin.cpp | 41 + amarok/src/plugin/plugin.h | 52 + amarok/src/plugin/pluginconfig.h | 48 + amarok/src/pluginmanager.cpp | 226 + amarok/src/pluginmanager.h | 114 + amarok/src/podcastbundle.h | 264 + amarok/src/podcastsettings.cpp | 225 + amarok/src/podcastsettings.h | 77 + amarok/src/podcastsettingsbase.ui | 219 + amarok/src/prettypopupmenu.cpp | 184 + amarok/src/prettypopupmenu.h | 74 + amarok/src/qstringx.h | 106 + amarok/src/queuemanager.cpp | 536 + amarok/src/queuemanager.h | 106 + amarok/src/refreshimages.cpp | 163 + amarok/src/refreshimages.h | 38 + amarok/src/scancontroller.cpp | 553 + amarok/src/scancontroller.h | 141 + amarok/src/scriptmanager.cpp | 952 + amarok/src/scriptmanager.h | 211 + amarok/src/scriptmanagerbase.ui | 135 + amarok/src/scripts/Makefile.am | 10 + amarok/src/scripts/ScriptWriting-HOWTO | 7 + amarok/src/scripts/alarm/COPYING | 340 + amarok/src/scripts/alarm/README | 13 + amarok/src/scripts/alarm/alarm.py | 163 + amarok/src/scripts/amarok-svn/INFO | 8 + amarok/src/scripts/amarok-svn/README | 9 + amarok/src/scripts/amarok-svn/amarok-bench.sh | 72 + amarok/src/scripts/amarok-svn/amarok-svn.sh | 606 + amarok/src/scripts/amarok_live/Makefile.am_ | 9 + amarok/src/scripts/amarok_live/README | 44 + .../amarok_live/amarok.live.remaster.part1.sh | 151 + .../amarok_live/amarok.live.remaster.part2.sh | 79 + amarok/src/scripts/amarok_live/amarok_live.py | 427 + .../amarok_live/livecd/amarok_live_scan.sh | 24 + .../standalone/amarok.live.remaster.part1.sh | 139 + .../standalone/amarok.live.remaster.part2.sh | 63 + amarok/src/scripts/common/Makefile.am | 5 + amarok/src/scripts/common/Publisher.py | 56 + amarok/src/scripts/common/Zeroconf.py | 1578 + amarok/src/scripts/copy_icons.rb | 28 + amarok/src/scripts/databasescripts/README | 63 + amarok/src/scripts/databasescripts/TODO | 10 + .../scripts/databasescripts/backupDatabase.rb | 76 + .../databasescripts/databaseScripts.rb | 145 + .../scripts/databasescripts/redoPodcasts.rb | 17 + .../scripts/databasescripts/staleAlbums.rb | 40 + .../scripts/databasescripts/staleArtists.rb | 33 + .../scripts/databasescripts/staleImages.rb | 27 + .../databasescripts/staleStatistics.rb | 25 + amarok/src/scripts/embedcover/COPYING | 340 + amarok/src/scripts/embedcover/README | 52 + amarok/src/scripts/embedcover/addimage2mp3.rb | 219 + amarok/src/scripts/embedcover/embedcover.rb | 111 + amarok/src/scripts/gnome_media_keys/README | 24 + .../gnome_media_keys/gnome_media_keys.py | 45 + .../gnome_media_keys/gnome_media_keys.spec | 2 + amarok/src/scripts/graphequalizer/Makefile.am | 27 + amarok/src/scripts/graphequalizer/README | 14 + amarok/src/scripts/graphequalizer/eqdialog.ui | 212 + .../src/scripts/graphequalizer/eqdialog.ui.h | 46 + .../graphequalizer/equalizercanvasview.cpp | 289 + .../graphequalizer/equalizercanvasview.h | 82 + .../graphequalizer/equalizerdialog.cpp | 53 + .../scripts/graphequalizer/equalizerdialog.h | 39 + amarok/src/scripts/graphequalizer/main.cpp | 50 + .../src/scripts/graphequalizer/stdinreader.h | 41 + amarok/src/scripts/lyrics_astraweb/COPYING | 340 + .../src/scripts/lyrics_astraweb/Makefile.am | 8 + amarok/src/scripts/lyrics_astraweb/README | 30 + .../THIS_SCRIPT_WAS_INTENTIONALLY_DISABLED | 0 .../lyrics_astraweb/lyrics_astraweb.rb | 147 + .../lyrics_astraweb/lyrics_astraweb.spec | 6 + amarok/src/scripts/lyrics_lyrc/COPYING | 340 + amarok/src/scripts/lyrics_lyrc/Makefile.am | 10 + amarok/src/scripts/lyrics_lyrc/README | 30 + amarok/src/scripts/lyrics_lyrc/lyrics_lyrc.rb | 201 + .../src/scripts/lyrics_lyrc/lyrics_lyrc.spec | 7 + amarok/src/scripts/mp3fix/COPYING | 340 + amarok/src/scripts/mp3fix/README | 75 + amarok/src/scripts/mp3fix/TODO | 6 + amarok/src/scripts/mp3fix/mp3fix.rb | 278 + amarok/src/scripts/mp3fix/mp3fixer.rb | 93 + .../scripts/nowplaying/amaroknowplaying.rb | 52 + amarok/src/scripts/playlist2html/Makefile.am | 10 + amarok/src/scripts/playlist2html/Playlist.py | 153 + .../scripts/playlist2html/PlaylistServer.py | 44 + .../scripts/playlist2html/PlaylistServer.spec | 2 + amarok/src/scripts/playlist2html/README | 18 + .../scripts/playlist2html/playlist2html.py | 25 + .../scripts/playlist2html/playlist2html.spec | 2 + amarok/src/scripts/rbeautify.rb | 160 + amarok/src/scripts/ruby_debug/Makefile.am | 5 + amarok/src/scripts/ruby_debug/debug.rb | 157 + amarok/src/scripts/score2rating/README | 39 + .../src/scripts/score2rating/score2rating.rb | 77 + amarok/src/scripts/score_default/COPYING | 340 + amarok/src/scripts/score_default/Makefile.am | 10 + amarok/src/scripts/score_default/README | 25 + .../scripts/score_default/score_default.rb | 36 + .../scripts/score_default/score_default.spec | 2 + amarok/src/scripts/score_impulsive/COPYING | 340 + .../src/scripts/score_impulsive/Makefile.am | 10 + amarok/src/scripts/score_impulsive/README | 25 + .../score_impulsive/score_impulsive.rb | 31 + .../score_impulsive/score_impulsive.spec | 2 + amarok/src/scripts/templates/Makefile.am | 9 + amarok/src/scripts/templates/amarok.rb | 85 + .../scripts/templates/python_qt_template.py | 193 + .../src/scripts/templates/ruby_qt_template.rb | 65 + amarok/src/scripts/webcontrol/Globals.py | 74 + amarok/src/scripts/webcontrol/Makefile.am | 26 + amarok/src/scripts/webcontrol/Playlist.py | 309 + amarok/src/scripts/webcontrol/README | 24 + .../src/scripts/webcontrol/RequestHandler.py | 266 + amarok/src/scripts/webcontrol/WebControl.py | 289 + amarok/src/scripts/webcontrol/WebControl.spec | 2 + amarok/src/scripts/webcontrol/WebPublisher.py | 34 + amarok/src/scripts/webcontrol/amarok_cut.png | Bin 0 -> 6303 bytes .../scripts/webcontrol/controlbackground.png | Bin 0 -> 6392 bytes amarok/src/scripts/webcontrol/main.css | 161 + amarok/src/scripts/webcontrol/main.js | 253 + amarok/src/scripts/webcontrol/player_end.png | Bin 0 -> 809 bytes .../src/scripts/webcontrol/player_pause.png | Bin 0 -> 1191 bytes amarok/src/scripts/webcontrol/player_play.png | Bin 0 -> 1263 bytes .../src/scripts/webcontrol/player_start.png | Bin 0 -> 802 bytes amarok/src/scripts/webcontrol/player_stop.png | Bin 0 -> 719 bytes amarok/src/scripts/webcontrol/smallstar.png | Bin 0 -> 1037 bytes amarok/src/scripts/webcontrol/star.png | Bin 0 -> 1459 bytes amarok/src/scripts/webcontrol/template.thtml | 42 + amarok/src/scripts/webcontrol/vol_speaker.png | Bin 0 -> 1390 bytes amarok/src/scrobbler.cpp | 1185 + amarok/src/scrobbler.h | 184 + amarok/src/sliderwidget.cpp | 541 + amarok/src/sliderwidget.h | 161 + amarok/src/smartplaylisteditor.cpp | 1078 + amarok/src/smartplaylisteditor.h | 147 + amarok/src/socketserver.cpp | 313 + amarok/src/socketserver.h | 103 + amarok/src/sqlite/Makefile.am | 17 + amarok/src/sqlite/sqlite3.c | 67875 ++++++++++++++++ amarok/src/sqlite/sqlite3.h | 2702 + amarok/src/starmanager.cpp | 167 + amarok/src/starmanager.h | 72 + amarok/src/statistics.cpp | 1048 + amarok/src/statistics.h | 170 + amarok/src/statusbar/Makefile.am | 22 + amarok/src/statusbar/overlayWidget.cpp | 89 + amarok/src/statusbar/overlayWidget.h | 48 + amarok/src/statusbar/popupMessage.cpp | 333 + amarok/src/statusbar/popupMessage.h | 107 + amarok/src/statusbar/progressBar.cpp | 120 + amarok/src/statusbar/progressBar.h | 72 + amarok/src/statusbar/queueLabel.cpp | 305 + amarok/src/statusbar/queueLabel.h | 70 + amarok/src/statusbar/selectLabel.h | 175 + amarok/src/statusbar/squeezedtextlabel.cpp | 141 + amarok/src/statusbar/squeezedtextlabel.h | 54 + amarok/src/statusbar/statusBarBase.cpp | 678 + amarok/src/statusbar/statusBarBase.h | 233 + amarok/src/statusbar/statusbar.cpp | 390 + amarok/src/statusbar/statusbar.h | 95 + amarok/src/statusbar/timeLabel.h | 54 + amarok/src/statusbar/toggleLabel.h | 148 + amarok/src/systray.cpp | 280 + amarok/src/systray.h | 55 + amarok/src/tagdialog.cpp | 1498 + amarok/src/tagdialog.h | 135 + amarok/src/tagdialogbase.ui | 846 + amarok/src/tagdialogbase.ui.h | 19 + amarok/src/tagguesser.cpp | 266 + amarok/src/tagguesser.h | 97 + amarok/src/tagguesserconfigdialog.ui | 219 + amarok/src/tagguesserconfigdialog.ui.h | 134 + amarok/src/tdebug.h | 0 amarok/src/themes/Makefile.am | 3 + amarok/src/themes/example/Makefile.am | 3 + amarok/src/themes/example/stylesheet.css | 428 + amarok/src/themes/reinhardt/Makefile.am | 5 + .../src/themes/reinhardt/images/Makefile.am | 3 + .../themes/reinhardt/images/background.png | Bin 0 -> 100557 bytes .../themes/reinhardt/images/transparency.png | Bin 0 -> 761 bytes amarok/src/themes/reinhardt/stylesheet.css | 100 + amarok/src/threadmanager.cpp | 427 + amarok/src/threadmanager.h | 426 + amarok/src/tooltip.cpp | 235 + amarok/src/tooltip.h | 79 + amarok/src/trackpickerdialog.cpp | 106 + amarok/src/trackpickerdialog.h | 51 + amarok/src/trackpickerdialogbase.ui | 182 + amarok/src/tracktooltip.cpp | 351 + amarok/src/tracktooltip.h | 64 + amarok/src/transferdialog.cpp | 177 + amarok/src/transferdialog.h | 61 + amarok/src/vis/Makefile.am | 6 + amarok/src/vis/libvisual/Makefile.am | 13 + amarok/src/vis/libvisual/libvisual.cpp | 417 + amarok/src/vis/libvisual/libvisual.h | 88 + amarok/src/xmlloader.cpp | 222 + amarok/src/xmlloader.h | 180 + amarok/src/xmlloader_p.h | 81 + amarok/src/xspfplaylist.cpp | 434 + amarok/src/xspfplaylist.h | 90 + config.h.in | 385 + configure.files | 5 + configure.in | 1761 + configure.in.bot | 4 + configure.in.in | 11 + doc/Makefile.am | 5 + doc/amarok/Makefile.am | 4 + doc/amarok/add_dynamic.png | Bin 0 -> 34586 bytes doc/amarok/advanced.docbook | 1110 + doc/amarok/amarok_playlist.png | Bin 0 -> 136228 bytes doc/amarok/analyzer.png | Bin 0 -> 6711 bytes doc/amarok/buttons.png | Bin 0 -> 8512 bytes doc/amarok/collection.png | Bin 0 -> 8083 bytes doc/amarok/config.docbook | 252 + doc/amarok/config_appearance.png | Bin 0 -> 55097 bytes doc/amarok/config_collection.png | Bin 0 -> 56076 bytes doc/amarok/config_engine.png | Bin 0 -> 68294 bytes doc/amarok/config_general.png | Bin 0 -> 57975 bytes doc/amarok/config_osd.png | Bin 0 -> 56660 bytes doc/amarok/config_playback.png | Bin 0 -> 42952 bytes doc/amarok/config_scrobbler.png | Bin 0 -> 56180 bytes doc/amarok/coverman.png | Bin 0 -> 206414 bytes doc/amarok/dynamic_bar.png | Bin 0 -> 7996 bytes doc/amarok/dynamic_settings.png | Bin 0 -> 15842 bytes doc/amarok/equalizer.png | Bin 0 -> 12616 bytes doc/amarok/faq.docbook | 1038 + doc/amarok/file_browser.png | Bin 0 -> 19830 bytes doc/amarok/index.docbook | 392 + doc/amarok/logo.png | Bin 0 -> 3554 bytes doc/amarok/media_device.png | Bin 0 -> 27339 bytes doc/amarok/menubar.png | Bin 0 -> 2811 bytes doc/amarok/musicbrainz.png | Bin 0 -> 1611 bytes doc/amarok/pl_tip1.png | Bin 0 -> 8139 bytes doc/amarok/play_list.png | Bin 0 -> 16391 bytes doc/amarok/player_window.png | Bin 0 -> 11811 bytes doc/amarok/playlist_browser.png | Bin 0 -> 27593 bytes doc/amarok/playlist_window.png | Bin 0 -> 67451 bytes doc/amarok/queue_manager.png | Bin 0 -> 16560 bytes doc/amarok/quick.docbook | 85 + doc/amarok/requirements.docbook | 40 + doc/amarok/rmb_menu.png | Bin 0 -> 24242 bytes doc/amarok/script_manager.png | Bin 0 -> 22626 bytes doc/amarok/status_bar.png | Bin 0 -> 6710 bytes doc/amarok/tab_lyrics.png | Bin 0 -> 17497 bytes doc/amarok/tab_music.png | Bin 0 -> 86640 bytes doc/amarok/tab_wiki.png | Bin 0 -> 136912 bytes doc/amarok/using.docbook | 1270 + doc/amarok/vis_window.png | Bin 0 -> 11669 bytes doc/da/Makefile.am | 2 + doc/da/advanced.docbook | 1665 + doc/da/config.docbook | 555 + doc/da/faq.docbook | 1350 + doc/da/index.docbook | 651 + doc/da/quick.docbook | 148 + doc/da/requirements.docbook | 136 + doc/da/using.docbook | 2272 + doc/de/Makefile.am | 2 + doc/de/add_dynamic.png | Bin 0 -> 12467 bytes doc/de/advanced.docbook | 1690 + doc/de/amarok_playlist.png | Bin 0 -> 36346 bytes doc/de/analyzer.png | Bin 0 -> 2176 bytes doc/de/buttons.png | Bin 0 -> 2894 bytes doc/de/collection.png | Bin 0 -> 9728 bytes doc/de/config.docbook | 555 + doc/de/config_appearance.png | Bin 0 -> 21757 bytes doc/de/config_collection.png | Bin 0 -> 21806 bytes doc/de/config_engine.png | Bin 0 -> 25732 bytes doc/de/config_general.png | Bin 0 -> 22434 bytes doc/de/config_osd.png | Bin 0 -> 23297 bytes doc/de/config_playback.png | Bin 0 -> 17383 bytes doc/de/config_scrobbler.png | Bin 0 -> 24560 bytes doc/de/context.png | Bin 0 -> 25267 bytes doc/de/coverman.png | Bin 0 -> 61954 bytes doc/de/dynamic_bar.png | Bin 0 -> 6304 bytes doc/de/dynamic_settings.png | Bin 0 -> 12467 bytes doc/de/equalizer.png | Bin 0 -> 5721 bytes doc/de/faq.docbook | 1354 + doc/de/file_browser.png | Bin 0 -> 12412 bytes doc/de/index.docbook | 665 + doc/de/logo.png | Bin 0 -> 3560 bytes doc/de/media_device.png | Bin 0 -> 7279 bytes doc/de/menubar.png | Bin 0 -> 6247 bytes doc/de/musicbrainz.png | Bin 0 -> 1689 bytes doc/de/pl_browser.png | Bin 0 -> 10083 bytes doc/de/pl_tip1.png | Bin 0 -> 4595 bytes doc/de/play_list.png | Bin 0 -> 25495 bytes doc/de/player_window.png | Bin 0 -> 5164 bytes doc/de/playlist_browser.png | Bin 0 -> 8655 bytes doc/de/playlist_window.png | Bin 0 -> 32173 bytes doc/de/queue_manager.png | Bin 0 -> 9077 bytes doc/de/quick.docbook | 146 + doc/de/requirements.docbook | 136 + doc/de/rmb_menu.png | Bin 0 -> 11291 bytes doc/de/script_manager.png | Bin 0 -> 10278 bytes doc/de/status_bar.png | Bin 0 -> 3566 bytes doc/de/using.docbook | 2302 + doc/de/vis_window.png | Bin 0 -> 1713 bytes doc/es/Makefile.am | 2 + doc/es/add_dynamic.png | Bin 0 -> 16011 bytes doc/es/advanced.docbook | 1664 + doc/es/amarok_playlist.png | Bin 0 -> 42894 bytes doc/es/analyzer.png | Bin 0 -> 4493 bytes doc/es/browser_choice.png | Bin 0 -> 2908 bytes doc/es/buttons.png | Bin 0 -> 8512 bytes doc/es/collection.png | Bin 0 -> 9449 bytes doc/es/config.docbook | 555 + doc/es/config_appearance.png | Bin 0 -> 72661 bytes doc/es/config_collection.png | Bin 0 -> 72885 bytes doc/es/config_engine.png | Bin 0 -> 76477 bytes doc/es/config_general.png | Bin 0 -> 76343 bytes doc/es/config_osd.png | Bin 0 -> 69898 bytes doc/es/config_playback.png | Bin 0 -> 56056 bytes doc/es/config_scrobbler.png | Bin 0 -> 75139 bytes doc/es/context.png | Bin 0 -> 23292 bytes doc/es/coverman.png | Bin 0 -> 54511 bytes doc/es/dynamic_bar.png | Bin 0 -> 6585 bytes doc/es/dynamic_mode1.png | Bin 0 -> 9018 bytes doc/es/dynamic_mode2.png | Bin 0 -> 7940 bytes doc/es/dynamic_settings.png | Bin 0 -> 8343 bytes doc/es/equalizer.png | Bin 0 -> 14306 bytes doc/es/faq.docbook | 1330 + doc/es/file_browser.png | Bin 0 -> 14862 bytes doc/es/index.docbook | 653 + doc/es/logo.png | Bin 0 -> 3560 bytes doc/es/media_device.png | Bin 0 -> 8189 bytes doc/es/menubar.png | Bin 0 -> 3639 bytes doc/es/musicbrainz.png | Bin 0 -> 1689 bytes doc/es/pl_browser.png | Bin 0 -> 13025 bytes doc/es/pl_tip1.png | Bin 0 -> 5914 bytes doc/es/play_list.png | Bin 0 -> 9522 bytes doc/es/player_window.png | Bin 0 -> 5919 bytes doc/es/playlist_browser.png | Bin 0 -> 14835 bytes doc/es/playlist_window.png | Bin 0 -> 116971 bytes doc/es/queue_manager.png | Bin 0 -> 9524 bytes doc/es/quick.docbook | 148 + doc/es/requirements.docbook | 136 + doc/es/rmb_menu.png | Bin 0 -> 14786 bytes doc/es/script_manager.png | Bin 0 -> 12070 bytes doc/es/status_bar.png | Bin 0 -> 5134 bytes doc/es/tab_lyrics.png | Bin 0 -> 8995 bytes doc/es/tab_music.png | Bin 0 -> 24089 bytes doc/es/tab_wiki.png | Bin 0 -> 36286 bytes doc/es/using.docbook | 2250 + doc/es/vis_window.png | Bin 0 -> 7274 bytes doc/et/Makefile.am | 2 + doc/et/advanced.docbook | 1666 + doc/et/config.docbook | 555 + doc/et/develop.docbook | 213 + doc/et/faq.docbook | 1332 + doc/et/feature_guide.docbook | 216 + doc/et/hidden.docbook | 63 + doc/et/howto.docbook | 8 + doc/et/index.docbook | 665 + doc/et/plugin.docbook | 8 + doc/et/quick.docbook | 148 + doc/et/requirements.docbook | 136 + doc/et/using.docbook | 2274 + doc/fr/Makefile.am | 2 + doc/fr/advanced.docbook | 991 + doc/fr/config.docbook | 556 + doc/fr/faq.docbook | 1402 + doc/fr/feature_guide.docbook | 216 + doc/fr/index.docbook | 616 + doc/fr/quick.docbook | 148 + doc/fr/requirements.docbook | 136 + doc/fr/using.docbook | 1675 + doc/it/Makefile.am | 2 + doc/it/add_dynamic.png | Bin 0 -> 12835 bytes doc/it/advanced.docbook | 1666 + doc/it/amarok_playlist.png | Bin 0 -> 60682 bytes doc/it/analyzer.png | Bin 0 -> 4441 bytes doc/it/buttons.png | Bin 0 -> 3371 bytes doc/it/collection.png | Bin 0 -> 9401 bytes doc/it/completo.png | Bin 0 -> 67949 bytes doc/it/config.docbook | 555 + doc/it/config_appearance.png | Bin 0 -> 22770 bytes doc/it/config_collection.png | Bin 0 -> 18971 bytes doc/it/config_engine.png | Bin 0 -> 27952 bytes doc/it/config_general.png | Bin 0 -> 23294 bytes doc/it/config_osd.png | Bin 0 -> 25692 bytes doc/it/config_playback.png | Bin 0 -> 16776 bytes doc/it/config_scrobbler.png | Bin 0 -> 22993 bytes doc/it/context.png | Bin 0 -> 22241 bytes doc/it/coverman.png | Bin 0 -> 49875 bytes doc/it/develop.docbook | 213 + doc/it/dynamic_bar.png | Bin 0 -> 5678 bytes doc/it/dynamic_settings.png | Bin 0 -> 7617 bytes doc/it/equalizer.png | Bin 0 -> 7453 bytes doc/it/faq.docbook | 1334 + doc/it/feature_guide.docbook | 216 + doc/it/file_browser.png | Bin 0 -> 9178 bytes doc/it/hidden.docbook | 63 + doc/it/index.docbook | 707 + doc/it/media_device.png | Bin 0 -> 8605 bytes doc/it/menubar.png | Bin 0 -> 2820 bytes doc/it/pl_browser.png | Bin 0 -> 14021 bytes doc/it/pl_tip1.png | Bin 0 -> 4207 bytes doc/it/play_list.png | Bin 0 -> 7805 bytes doc/it/player_window.png | Bin 0 -> 6083 bytes doc/it/playlist_browser.png | Bin 0 -> 6703 bytes doc/it/playlist_window.png | Bin 0 -> 21228 bytes doc/it/plugin.docbook | 8 + doc/it/queue_manager.png | Bin 0 -> 8338 bytes doc/it/quick.docbook | 148 + doc/it/requirements.docbook | 136 + doc/it/rmb_menu.png | Bin 0 -> 12383 bytes doc/it/script_manager.png | Bin 0 -> 10083 bytes doc/it/status_bar.png | Bin 0 -> 4670 bytes doc/it/tab_lyrics.png | Bin 0 -> 9968 bytes doc/it/tab_music.png | Bin 0 -> 28294 bytes doc/it/tab_wiki.png | Bin 0 -> 33467 bytes doc/it/using.docbook | 2270 + doc/it/vis_window.png | Bin 0 -> 5735 bytes doc/nl/Makefile.am | 2 + doc/nl/add_dynamic.png | Bin 0 -> 15151 bytes doc/nl/advanced.docbook | 1667 + doc/nl/amarok_playlist.png | Bin 0 -> 74891 bytes doc/nl/analyzer.png | Bin 0 -> 3312 bytes doc/nl/buttons.png | Bin 0 -> 3540 bytes doc/nl/collection.png | Bin 0 -> 13243 bytes doc/nl/config.docbook | 555 + doc/nl/config_appearance.png | Bin 0 -> 27488 bytes doc/nl/config_collection.png | Bin 0 -> 25807 bytes doc/nl/config_engine.png | Bin 0 -> 32535 bytes doc/nl/config_general.png | Bin 0 -> 27812 bytes doc/nl/config_osd.png | Bin 0 -> 27702 bytes doc/nl/config_playback.png | Bin 0 -> 21249 bytes doc/nl/config_scrobbler.png | Bin 0 -> 31923 bytes doc/nl/context.png | Bin 0 -> 17901 bytes doc/nl/coverman.png | Bin 0 -> 61291 bytes doc/nl/develop.docbook | 213 + doc/nl/dynamic_bar.png | Bin 0 -> 6742 bytes doc/nl/dynamic_settings.png | Bin 0 -> 7571 bytes doc/nl/equalizer.png | Bin 0 -> 8034 bytes doc/nl/faq.docbook | 1366 + doc/nl/feature_guide.docbook | 216 + doc/nl/file_browser.png | Bin 0 -> 16865 bytes doc/nl/hidden.docbook | 63 + doc/nl/index.docbook | 665 + doc/nl/logo.png | Bin 0 -> 3509 bytes doc/nl/media_device.png | Bin 0 -> 11562 bytes doc/nl/menubar.png | Bin 0 -> 2148 bytes doc/nl/musicbrainz.png | Bin 0 -> 1689 bytes doc/nl/pl_browser.png | Bin 0 -> 14597 bytes doc/nl/pl_tip1.png | Bin 0 -> 5806 bytes doc/nl/play_list.png | Bin 0 -> 11369 bytes doc/nl/player_window.png | Bin 0 -> 4266 bytes doc/nl/playlist_browser.png | Bin 0 -> 15210 bytes doc/nl/playlist_window.png | Bin 0 -> 31095 bytes doc/nl/plugin.docbook | 8 + doc/nl/queue_manager.png | Bin 0 -> 6593 bytes doc/nl/quick.docbook | 148 + doc/nl/requirements.docbook | 136 + doc/nl/rmb_menu.png | Bin 0 -> 13078 bytes doc/nl/script_manager.png | Bin 0 -> 12140 bytes doc/nl/status_bar.png | Bin 0 -> 3410 bytes doc/nl/tab_lyrics.png | Bin 0 -> 11809 bytes doc/nl/tab_lyrics1.png | Bin 0 -> 9917 bytes doc/nl/tab_music.png | Bin 0 -> 25178 bytes doc/nl/tab_wiki.png | Bin 0 -> 33639 bytes doc/nl/using.docbook | 2270 + doc/nl/vis_window.png | Bin 0 -> 5469 bytes doc/pl/Makefile.am | 2 + doc/pl/advanced.docbook | 1665 + doc/pl/config.docbook | 555 + doc/pl/faq.docbook | 1332 + doc/pl/index.docbook | 707 + doc/pl/quick.docbook | 148 + doc/pl/requirements.docbook | 136 + doc/pl/using.docbook | 2272 + doc/pt/Makefile.am | 2 + doc/pt/add_dynamic.png | Bin 0 -> 13817 bytes doc/pt/advanced.docbook | 1667 + doc/pt/amarok_playlist.png | Bin 0 -> 48441 bytes doc/pt/analyzer.png | Bin 0 -> 2884 bytes doc/pt/buttons.png | Bin 0 -> 4436 bytes doc/pt/collection.png | Bin 0 -> 8850 bytes doc/pt/config.docbook | 555 + doc/pt/config_appearance.png | Bin 0 -> 26298 bytes doc/pt/config_collection.png | Bin 0 -> 21872 bytes doc/pt/config_engine.png | Bin 0 -> 29724 bytes doc/pt/config_general.png | Bin 0 -> 24084 bytes doc/pt/config_osd.png | Bin 0 -> 23577 bytes doc/pt/config_playback.png | Bin 0 -> 19875 bytes doc/pt/config_scrobbler.png | Bin 0 -> 27604 bytes doc/pt/coverman.png | Bin 0 -> 60162 bytes doc/pt/develop.docbook | 213 + doc/pt/dynamic_bar.png | Bin 0 -> 4312 bytes doc/pt/dynamic_settings.png | Bin 0 -> 6122 bytes doc/pt/equalizer.png | Bin 0 -> 6544 bytes doc/pt/faq.docbook | 1340 + doc/pt/feature_guide.docbook | 216 + doc/pt/file_browser.png | Bin 0 -> 12501 bytes doc/pt/hidden.docbook | 63 + doc/pt/index.docbook | 665 + doc/pt/logo.png | Bin 0 -> 3509 bytes doc/pt/media_device.png | Bin 0 -> 11562 bytes doc/pt/menubar.png | Bin 0 -> 2153 bytes doc/pt/musicbrainz.png | Bin 0 -> 1689 bytes doc/pt/pl_tip1.png | Bin 0 -> 5519 bytes doc/pt/play_list.png | Bin 0 -> 6860 bytes doc/pt/player_window.png | Bin 0 -> 5034 bytes doc/pt/playlist_browser.png | Bin 0 -> 19080 bytes doc/pt/playlist_window.png | Bin 0 -> 27932 bytes doc/pt/plugin.docbook | 8 + doc/pt/queue_manager.png | Bin 0 -> 7462 bytes doc/pt/quick.docbook | 146 + doc/pt/requirements.docbook | 136 + doc/pt/rmb_menu.png | Bin 0 -> 11572 bytes doc/pt/script_manager.png | Bin 0 -> 9823 bytes doc/pt/status_bar.png | Bin 0 -> 2980 bytes doc/pt/using.docbook | 2274 + doc/pt/vis_window.png | Bin 0 -> 4245 bytes doc/pt_BR/Makefile.am | 2 + doc/pt_BR/advanced.docbook | 991 + doc/pt_BR/config.docbook | 610 + doc/pt_BR/faq.docbook | 682 + doc/pt_BR/feature_guide.docbook | 216 + doc/pt_BR/index.docbook | 554 + doc/pt_BR/quick.docbook | 142 + doc/pt_BR/requirements.docbook | 118 + doc/pt_BR/using.docbook | 1675 + doc/ru/Makefile.am | 2 + doc/ru/requirements.docbook | 136 + doc/sv/Makefile.am | 2 + doc/sv/add_dynamic.png | Bin 0 -> 13914 bytes doc/sv/advanced.docbook | 1665 + doc/sv/amarok_playlist.png | Bin 0 -> 41223 bytes doc/sv/analyzer.png | Bin 0 -> 2498 bytes doc/sv/browser_choice.png | Bin 0 -> 2524 bytes doc/sv/buttons.png | Bin 0 -> 6833 bytes doc/sv/collection.png | Bin 0 -> 6482 bytes doc/sv/config.docbook | 555 + doc/sv/config_appearance.png | Bin 0 -> 19797 bytes doc/sv/config_collection.png | Bin 0 -> 18193 bytes doc/sv/config_colors.png | Bin 0 -> 37152 bytes doc/sv/config_engine.png | Bin 0 -> 17826 bytes doc/sv/config_fonts.png | Bin 0 -> 40911 bytes doc/sv/config_general.png | Bin 0 -> 24078 bytes doc/sv/config_mysql.png | Bin 0 -> 34618 bytes doc/sv/config_osd.png | Bin 0 -> 21536 bytes doc/sv/config_playback.png | Bin 0 -> 15325 bytes doc/sv/config_scrobbler.png | Bin 0 -> 24597 bytes doc/sv/context.png | Bin 0 -> 20248 bytes doc/sv/coverman.png | Bin 0 -> 41833 bytes doc/sv/develop.docbook | 213 + doc/sv/dynamic_bar.png | Bin 0 -> 5248 bytes doc/sv/dynamic_settings.png | Bin 0 -> 7679 bytes doc/sv/equalizer.png | Bin 0 -> 5897 bytes doc/sv/faq.docbook | 1350 + doc/sv/feature_guide.docbook | 216 + doc/sv/file_browser.png | Bin 0 -> 9790 bytes doc/sv/hidden.docbook | 61 + doc/sv/index.docbook | 665 + doc/sv/media_device.png | Bin 0 -> 8275 bytes doc/sv/menubar.png | Bin 0 -> 1967 bytes doc/sv/pl_browser.png | Bin 0 -> 10224 bytes doc/sv/pl_tip1.png | Bin 0 -> 4340 bytes doc/sv/play_list.png | Bin 0 -> 7623 bytes doc/sv/player_window.png | Bin 0 -> 4705 bytes doc/sv/playlist_browser.png | Bin 0 -> 6973 bytes doc/sv/playlist_window.png | Bin 0 -> 35556 bytes doc/sv/plugin.docbook | 8 + doc/sv/queue_manager.png | Bin 0 -> 7337 bytes doc/sv/quick.docbook | 148 + doc/sv/requirements.docbook | 136 + doc/sv/rmb_menu.png | Bin 0 -> 10402 bytes doc/sv/script_manager.png | Bin 0 -> 9522 bytes doc/sv/scripts_window.png | Bin 0 -> 19780 bytes doc/sv/search.png | Bin 0 -> 3769 bytes doc/sv/status_bar.png | Bin 0 -> 3378 bytes doc/sv/streams.png | Bin 0 -> 6181 bytes doc/sv/using.docbook | 2272 + doc/sv/vis_window.png | Bin 0 -> 3849 bytes doc/sv/welcome.png | Bin 0 -> 8602 bytes po/Makefile.am | 1 + po/af/Makefile.am | 3 + po/af/amarok.po | 13764 ++++ po/ar/Makefile.am | 3 + po/ar/amarok.po | 12756 +++ po/az/Makefile.am | 3 + po/az/amarok.po | 13875 ++++ po/be/Makefile.am | 3 + po/be/amarok.po | 12693 +++ po/bg/Makefile.am | 3 + po/bg/amarok.po | 13555 +++ po/bn/Makefile.am | 3 + po/bn/amarok.po | 14078 ++++ po/br/Makefile.am | 3 + po/br/amarok.po | 13698 ++++ po/ca/Makefile.am | 3 + po/ca/amarok.po | 13700 ++++ po/cs/Makefile.am | 3 + po/cs/amarok.po | 13325 +++ po/cy/Makefile.am | 3 + po/cy/amarok.po | 13862 ++++ po/da/Makefile.am | 3 + po/da/amarok.po | 13518 +++ po/de/Makefile.am | 3 + po/de/amarok.po | 13741 ++++ po/el/Makefile.am | 3 + po/el/amarok.po | 13691 ++++ po/en_GB/Makefile.am | 3 + po/en_GB/amarok.po | 13599 ++++ po/eo/Makefile.am | 3 + po/eo/amarok.po | 12764 +++ po/es/Makefile.am | 3 + po/es/amarok.po | 13705 ++++ po/et/Makefile.am | 3 + po/et/amarok.po | 13461 +++ po/eu/Makefile.am | 3 + po/eu/amarok.po | 14256 ++++ po/fa/Makefile.am | 3 + po/fa/amarok.po | 13356 +++ po/fi/Makefile.am | 3 + po/fi/amarok.po | 13610 ++++ po/fr/Makefile.am | 3 + po/fr/amarok.po | 13757 ++++ po/ga/Makefile.am | 3 + po/ga/amarok.po | 13671 ++++ po/gl/Makefile.am | 3 + po/gl/amarok.po | 14601 ++++ po/he/Makefile.am | 3 + po/he/amarok.po | 14000 ++++ po/hi/Makefile.am | 3 + po/hi/amarok.po | 14688 ++++ po/hu/Makefile.am | 3 + po/hu/amarok.po | 13493 +++ po/id/Makefile.am | 3 + po/id/amarok.po | 13021 +++ po/is/Makefile.am | 3 + po/is/amarok.po | 13394 +++ po/it/Makefile.am | 3 + po/it/amarok.po | 13644 ++++ po/ja/Makefile.am | 3 + po/ja/amarok.po | 12990 +++ po/ka/Makefile.am | 3 + po/ka/amarok.po | 13324 +++ po/km/Makefile.am | 3 + po/km/amarok.po | 13447 +++ po/ko/Makefile.am | 3 + po/ko/amarok.po | 13313 +++ po/ku/Makefile.am | 3 + po/ku/amarok.po | 12686 +++ po/lo/Makefile.am | 3 + po/lo/amarok.po | 14272 ++++ po/lt/Makefile.am | 3 + po/lt/amarok.po | 13575 ++++ po/mk/Makefile.am | 3 + po/mk/amarok.po | 13646 ++++ po/ms/Makefile.am | 3 + po/ms/amarok.po | 13802 ++++ po/nb/Makefile.am | 3 + po/nb/amarok.po | 14156 ++++ po/nds/Makefile.am | 3 + po/nds/amarok.po | 13578 ++++ po/ne/Makefile.am | 3 + po/ne/amarok.po | 13585 ++++ po/nl/Makefile.am | 3 + po/nl/amarok.po | 13659 ++++ po/nn/Makefile.am | 3 + po/nn/amarok.po | 13474 +++ po/pa/Makefile.am | 3 + po/pa/amarok.po | 13223 +++ po/pl/Makefile.am | 3 + po/pl/amarok.po | 13700 ++++ po/pt/Makefile.am | 3 + po/pt/amarok.po | 13683 ++++ po/pt_BR/Makefile.am | 3 + po/pt_BR/amarok.po | 13659 ++++ po/ro/Makefile.am | 3 + po/ro/amarok.po | 14525 ++++ po/ru/Makefile.am | 3 + po/ru/amarok.po | 13487 +++ po/rw/Makefile.am | 3 + po/rw/amarok.po | 15178 ++++ po/se/Makefile.am | 3 + po/se/amarok.po | 13254 +++ po/sk/Makefile.am | 3 + po/sk/amarok.po | 13614 ++++ po/sl/Makefile.am | 3 + po/sl/amarok.po | 13817 ++++ po/sq/Makefile.am | 3 + po/sq/amarok.po | 13195 +++ po/sr/Makefile.am | 3 + po/sr/amarok.po | 13574 +++ po/sr@Latn/Makefile.am | 3 + po/sr@Latn/amarok.po | 13587 ++++ po/ss/Makefile.am | 3 + po/ss/amarok.po | 14228 ++++ po/sv/Makefile.am | 3 + po/sv/amarok.po | 13517 +++ po/ta/Makefile.am | 3 + po/ta/amarok.po | 14856 ++++ po/tg/Makefile.am | 3 + po/tg/amarok.po | 14149 ++++ po/th/Makefile.am | 3 + po/th/amarok.po | 13322 +++ po/tr/Makefile.am | 3 + po/tr/amarok.po | 13141 +++ po/uk/Makefile.am | 3 + po/uk/amarok.po | 13621 ++++ po/uz/Makefile.am | 3 + po/uz/amarok.po | 12886 +++ po/uz@cyrillic/Makefile.am | 3 + po/uz@cyrillic/amarok.po | 12870 +++ po/zh_CN/Makefile.am | 3 + po/zh_CN/amarok.po | 12872 +++ po/zh_TW/Makefile.am | 3 + po/zh_TW/amarok.po | 13375 +++ subdirs | 3 + 1839 files changed, 1368989 insertions(+) create mode 100644 AUTHORS create mode 100644 CMakeLists.txt create mode 100644 COPYING create mode 100644 ChangeLog create mode 100644 ConfigureChecks.cmake create mode 100644 INSTALL create mode 100644 Makefile.am create mode 100644 Makefile.am.in create mode 100644 Makefile.cvs create mode 100644 README create mode 100644 TODO create mode 100644 acinclude.m4 create mode 100644 aclocal.m4 create mode 100644 amarok/COPYING-DOCS create mode 100644 amarok/COPYING.LIB create mode 100644 amarok/HACKING create mode 100644 amarok/Makefile.am create mode 100644 amarok/VIS_PLAN create mode 100644 amarok/amarok.kdevelop create mode 100644 amarok/configure.in.bot create mode 100644 amarok/configure.in.in create mode 100644 amarok/docs/collection_redesign.xmi create mode 100644 amarok/docs/use_umbrello_to_open_xmi_files create mode 100644 amarok/src/Makefile.am create mode 100644 amarok/src/Options1.ui create mode 100644 amarok/src/Options1.ui.h create mode 100644 amarok/src/Options2.ui create mode 100644 amarok/src/Options2.ui.h create mode 100644 amarok/src/Options4.ui create mode 100644 amarok/src/Options5.ui create mode 100644 amarok/src/Options5.ui.h create mode 100644 amarok/src/Options7.ui create mode 100644 amarok/src/Options8.ui create mode 100644 amarok/src/Options8.ui.h create mode 100644 amarok/src/actionclasses.cpp create mode 100644 amarok/src/actionclasses.h create mode 100644 amarok/src/amarok.desktop create mode 100644 amarok/src/amarok.h create mode 100644 amarok/src/amarok.profile.xml create mode 100644 amarok/src/amarok_addaspodcast.desktop create mode 100644 amarok/src/amarok_append.desktop create mode 100644 amarok/src/amarok_codecinstall.desktop create mode 100644 amarok/src/amarok_export.h create mode 100644 amarok/src/amarok_play_audiocd.desktop create mode 100644 amarok/src/amarok_plugin.desktop create mode 100644 amarok/src/amarok_proxy.rb create mode 100644 amarok/src/amarokcore/Makefile.am create mode 100644 amarok/src/amarokcore/amarok.kcfg create mode 100644 amarok/src/amarokcore/amarokconfig.kcfgc create mode 100644 amarok/src/amarokcore/amarokdcophandler.cpp create mode 100644 amarok/src/amarokcore/amarokdcophandler.h create mode 100644 amarok/src/amarokcore/amarokdcopiface.h create mode 100644 amarok/src/amarokcore/crashhandler.cpp create mode 100644 amarok/src/amarokcore/crashhandler.h create mode 100644 amarok/src/amarokitpc.protocol create mode 100644 amarok/src/amaroklastfm.protocol create mode 100644 amarok/src/amarokpcast.protocol create mode 100644 amarok/src/amarokrc create mode 100644 amarok/src/amarokui.rc create mode 100644 amarok/src/amarokui_xmms.rc create mode 100644 amarok/src/analyzers/Makefile.am create mode 100644 amarok/src/analyzers/analyzerbase.cpp create mode 100644 amarok/src/analyzers/analyzerbase.h create mode 100644 amarok/src/analyzers/analyzerfactory.cpp create mode 100644 amarok/src/analyzers/baranalyzer.cpp create mode 100644 amarok/src/analyzers/baranalyzer.h create mode 100644 amarok/src/analyzers/blockanalyzer.cpp create mode 100644 amarok/src/analyzers/blockanalyzer.h create mode 100644 amarok/src/analyzers/boomanalyzer.cpp create mode 100644 amarok/src/analyzers/boomanalyzer.h create mode 100644 amarok/src/analyzers/glanalyzer.cpp create mode 100644 amarok/src/analyzers/glanalyzer.h create mode 100644 amarok/src/analyzers/glanalyzer2.cpp create mode 100644 amarok/src/analyzers/glanalyzer2.h create mode 100644 amarok/src/analyzers/glanalyzer3.cpp create mode 100644 amarok/src/analyzers/glanalyzer3.h create mode 100644 amarok/src/analyzers/sonogram.cpp create mode 100644 amarok/src/analyzers/sonogram.h create mode 100644 amarok/src/analyzers/turbine.cpp create mode 100644 amarok/src/analyzers/turbine.h create mode 100644 amarok/src/app.cpp create mode 100644 amarok/src/app.h create mode 100644 amarok/src/atomicstring.cpp create mode 100644 amarok/src/atomicstring.h create mode 100644 amarok/src/atomicstring_unittest.cpp create mode 100644 amarok/src/atomicurl.cpp create mode 100644 amarok/src/atomicurl.h create mode 100644 amarok/src/bcpp.cfg create mode 100644 amarok/src/browserToolBar.h create mode 100644 amarok/src/browserbar.cpp create mode 100644 amarok/src/browserbar.h create mode 100644 amarok/src/clicklineedit.cpp create mode 100644 amarok/src/clicklineedit.h create mode 100644 amarok/src/collectionbrowser.cpp create mode 100644 amarok/src/collectionbrowser.h create mode 100644 amarok/src/collectiondb.cpp create mode 100644 amarok/src/collectiondb.h create mode 100644 amarok/src/collectionscanner/Makefile.am create mode 100644 amarok/src/collectionscanner/collectionscanner.cpp create mode 100644 amarok/src/collectionscanner/collectionscanner.h create mode 100644 amarok/src/collectionscanner/collectionscannerdcophandler.cpp create mode 100644 amarok/src/collectionscanner/collectionscannerdcophandler.h create mode 100644 amarok/src/collectionscanner/collectionscannerdcopiface.h create mode 100644 amarok/src/collectionscanner/main.cpp create mode 100644 amarok/src/colorgenerator.h create mode 100644 amarok/src/columnlist.cpp create mode 100644 amarok/src/columnlist.h create mode 100644 amarok/src/configdialog.cpp create mode 100644 amarok/src/configdialog.h create mode 100644 amarok/src/contextbrowser.cpp create mode 100644 amarok/src/contextbrowser.h create mode 100644 amarok/src/coverfetcher.cpp create mode 100644 amarok/src/coverfetcher.h create mode 100644 amarok/src/covermanager.cpp create mode 100644 amarok/src/covermanager.h create mode 100644 amarok/src/cuefile.cpp create mode 100644 amarok/src/cuefile.h create mode 100644 amarok/src/data/Amarok_1.4_Welcome.ogg create mode 100644 amarok/src/data/Cool-Streams.xml create mode 100644 amarok/src/data/Makefile.am create mode 100644 amarok/src/data/ball.png create mode 100644 amarok/src/data/dot.png create mode 100644 amarok/src/data/equalizer_presets.xml create mode 100644 amarok/src/data/firstrun.m3u create mode 100644 amarok/src/data/grid.png create mode 100644 amarok/src/data/magnatune_logo.png create mode 100644 amarok/src/data/wirl1.png create mode 100644 amarok/src/data/wirl2.png create mode 100644 amarok/src/database_refactor/README create mode 100644 amarok/src/database_refactor/_Makefile.am create mode 100644 amarok/src/database_refactor/collectiondb.cpp create mode 100644 amarok/src/database_refactor/collectiondb.h create mode 100644 amarok/src/database_refactor/dbenginebase.cpp create mode 100644 amarok/src/database_refactor/dbenginebase.h create mode 100644 amarok/src/database_refactor/sqlite/_Makefile.am create mode 100644 amarok/src/database_refactor/sqlite/amarok_sqlite_dbengine_plugin.desktop create mode 100644 amarok/src/database_refactor/sqlite/sqlite_dbengine.cpp create mode 100644 amarok/src/database_refactor/sqlite/sqlite_dbengine.h create mode 100644 amarok/src/dbsetup.ui create mode 100644 amarok/src/dbsetup.ui.h create mode 100644 amarok/src/debug.h create mode 100644 amarok/src/deletedialog.cpp create mode 100644 amarok/src/deletedialog.h create mode 100644 amarok/src/deletedialogbase.ui create mode 100644 amarok/src/device/Makefile.am create mode 100644 amarok/src/device/massstorage/Makefile.am create mode 100644 amarok/src/device/massstorage/amarok_massstorage-device.desktop create mode 100644 amarok/src/device/massstorage/massstoragedevicehandler.cpp create mode 100644 amarok/src/device/massstorage/massstoragedevicehandler.h create mode 100644 amarok/src/device/nfs/Makefile.am create mode 100644 amarok/src/device/nfs/amarok_nfs-device.desktop create mode 100644 amarok/src/device/nfs/nfsdevicehandler.cpp create mode 100644 amarok/src/device/nfs/nfsdevicehandler.h create mode 100644 amarok/src/device/smb/Makefile.am create mode 100644 amarok/src/device/smb/amarok_smb-device.desktop create mode 100644 amarok/src/device/smb/smbdevicehandler.cpp create mode 100644 amarok/src/device/smb/smbdevicehandler.h create mode 100644 amarok/src/deviceconfiguredialog.cpp create mode 100644 amarok/src/deviceconfiguredialog.h create mode 100644 amarok/src/devicemanager.cpp create mode 100644 amarok/src/devicemanager.h create mode 100644 amarok/src/directorylist.cpp create mode 100644 amarok/src/directorylist.h create mode 100644 amarok/src/dynamicmode.cpp create mode 100644 amarok/src/dynamicmode.h create mode 100644 amarok/src/editfilterdialog.cpp create mode 100644 amarok/src/editfilterdialog.h create mode 100644 amarok/src/engine/ENGINE_TODO create mode 100644 amarok/src/engine/Makefile.am create mode 100644 amarok/src/engine/akode/Makefile.am create mode 100644 amarok/src/engine/akode/akode-engine.cpp create mode 100644 amarok/src/engine/akode/akode-engine.h create mode 100644 amarok/src/engine/akode/akode-scope.cpp create mode 100644 amarok/src/engine/akode/akode-scope.h create mode 100644 amarok/src/engine/akode/amarok_aKode-engine.desktop create mode 100644 amarok/src/engine/helix/COPYING create mode 100644 amarok/src/engine/helix/Makefile.am create mode 100644 amarok/src/engine/helix/Makefile.test create mode 100644 amarok/src/engine/helix/TODO create mode 100644 amarok/src/engine/helix/amarok_helixengine_plugin.desktop create mode 100644 amarok/src/engine/helix/config/Makefile.am create mode 100644 amarok/src/engine/helix/config/dummy.cpp create mode 100644 amarok/src/engine/helix/config/helixconfig.kcfg create mode 100644 amarok/src/engine/helix/config/helixconfig.kcfgc create mode 100644 amarok/src/engine/helix/helix-configdialog.cpp create mode 100644 amarok/src/engine/helix/helix-configdialog.h create mode 100644 amarok/src/engine/helix/helix-engine.cpp create mode 100644 amarok/src/engine/helix/helix-engine.h create mode 100644 amarok/src/engine/helix/helix-errors.cpp create mode 100644 amarok/src/engine/helix/helix-errors.h create mode 100644 amarok/src/engine/helix/helix-sp/Makefile.am create mode 100644 amarok/src/engine/helix/helix-sp/Makefile.test create mode 100644 amarok/src/engine/helix/helix-sp/gain.cpp create mode 100644 amarok/src/engine/helix/helix-sp/gain.h create mode 100644 amarok/src/engine/helix/helix-sp/helix-include/client/include/hxclsnk.h create mode 100644 amarok/src/engine/helix/helix-sp/helix-include/common/container/carray.h create mode 100644 amarok/src/engine/helix/helix-sp/helix-include/common/container/chxmapbuckets.h create mode 100644 amarok/src/engine/helix/helix-sp/helix-include/common/container/chxmaplongtoobj.h create mode 100644 amarok/src/engine/helix/helix-sp/helix-include/common/container/chxmapptrtoptr.h create mode 100644 amarok/src/engine/helix/helix-sp/helix-include/common/container/chxmapstringtoob.h create mode 100644 amarok/src/engine/helix/helix-sp/helix-include/common/container/chxmapstringtostring.h create mode 100644 amarok/src/engine/helix/helix-sp/helix-include/common/container/hxbuffer.h create mode 100644 amarok/src/engine/helix/helix-sp/helix-include/common/container/hxmap.h create mode 100644 amarok/src/engine/helix/helix-sp/helix-include/common/container/hxmaputils.h create mode 100644 amarok/src/engine/helix/helix-sp/helix-include/common/container/hxstring.h create mode 100644 amarok/src/engine/helix/helix-sp/helix-include/common/dbgtool/hxassert.h create mode 100644 amarok/src/engine/helix/helix-sp/helix-include/common/include/atomicbase.h create mode 100644 amarok/src/engine/helix/helix-sp/helix-include/common/include/hxausvc.h create mode 100644 amarok/src/engine/helix/helix-sp/helix-include/common/include/hxauth.h create mode 100644 amarok/src/engine/helix/helix-sp/helix-include/common/include/hxccf.h create mode 100644 amarok/src/engine/helix/helix-sp/helix-include/common/include/hxcom.h create mode 100644 amarok/src/engine/helix/helix-sp/helix-include/common/include/hxcomm.h create mode 100644 amarok/src/engine/helix/helix-sp/helix-include/common/include/hxcore.h create mode 100644 amarok/src/engine/helix/helix-sp/helix-include/common/include/hxengin.h create mode 100644 amarok/src/engine/helix/helix-sp/helix-include/common/include/hxerror.h create mode 100644 amarok/src/engine/helix/helix-sp/helix-include/common/include/hxfiles.h create mode 100644 amarok/src/engine/helix/helix-sp/helix-include/common/include/hxiids.h create mode 100644 amarok/src/engine/helix/helix-sp/helix-include/common/include/hxmon.h create mode 100644 amarok/src/engine/helix/helix-sp/helix-include/common/include/hxpiids.h create mode 100644 amarok/src/engine/helix/helix-sp/helix-include/common/include/hxplugn.h create mode 100644 amarok/src/engine/helix/helix-sp/helix-include/common/include/hxplugncompat.h create mode 100644 amarok/src/engine/helix/helix-sp/helix-include/common/include/hxprefs.h create mode 100644 amarok/src/engine/helix/helix-sp/helix-include/common/include/hxresult.h create mode 100644 amarok/src/engine/helix/helix-sp/helix-include/common/include/hxtbuf.h create mode 100644 amarok/src/engine/helix/helix-sp/helix-include/common/include/hxtypes.h create mode 100644 amarok/src/engine/helix/helix-sp/helix-include/common/include/hxvalue.h create mode 100644 amarok/src/engine/helix/helix-sp/helix-include/common/include/hxvsrc.h create mode 100644 amarok/src/engine/helix/helix-sp/helix-include/common/include/hxwin.h create mode 100644 amarok/src/engine/helix/helix-sp/helix-include/common/include/hxwintyp.h create mode 100644 amarok/src/engine/helix/helix-sp/helix-include/common/include/ihxpckts.h create mode 100644 amarok/src/engine/helix/helix-sp/helix-include/common/system/dllpath.h create mode 100644 amarok/src/engine/helix/helix-sp/helix-include/common/util/hxmangle.h create mode 100644 amarok/src/engine/helix/helix-sp/helix-include/common/util/hxstrutl.h create mode 100644 amarok/src/engine/helix/helix-sp/helix-include/runtime/hlxclib/assert.h create mode 100644 amarok/src/engine/helix/helix-sp/helix-include/runtime/hlxclib/limits.h create mode 100644 amarok/src/engine/helix/helix-sp/helix-include/runtime/hlxclib/memory.h create mode 100644 amarok/src/engine/helix/helix-sp/helix-include/runtime/hlxclib/stdio.h create mode 100644 amarok/src/engine/helix/helix-sp/helix-include/runtime/hlxclib/stdlib.h create mode 100644 amarok/src/engine/helix/helix-sp/helix-include/runtime/hlxclib/string.h create mode 100644 amarok/src/engine/helix/helix-sp/helix-include/runtime/hlxclib/sys/stat.h create mode 100644 amarok/src/engine/helix/helix-sp/helix-include/runtime/hlxclib/sys/types.h create mode 100644 amarok/src/engine/helix/helix-sp/helix-include/runtime/hlxclib/time.h create mode 100644 amarok/src/engine/helix/helix-sp/helix-include/runtime/safestring.h create mode 100644 amarok/src/engine/helix/helix-sp/helix-sp.cpp create mode 100644 amarok/src/engine/helix/helix-sp/helix-sp.h create mode 100644 amarok/src/engine/helix/helix-sp/helixdefines.h create mode 100644 amarok/src/engine/helix/helix-sp/hspadvisesink.cpp create mode 100644 amarok/src/engine/helix/helix-sp/hspadvisesink.h create mode 100644 amarok/src/engine/helix/helix-sp/hspalsadevice.cpp create mode 100644 amarok/src/engine/helix/helix-sp/hspalsadevice.h create mode 100644 amarok/src/engine/helix/helix-sp/hspauthmgr.cpp create mode 100644 amarok/src/engine/helix/helix-sp/hspauthmgr.h create mode 100644 amarok/src/engine/helix/helix-sp/hspcontext.cpp create mode 100644 amarok/src/engine/helix/helix-sp/hspcontext.h create mode 100644 amarok/src/engine/helix/helix-sp/hsperror.cpp create mode 100644 amarok/src/engine/helix/helix-sp/hsperror.h create mode 100644 amarok/src/engine/helix/helix-sp/hsphook.cpp create mode 100644 amarok/src/engine/helix/helix-sp/hsphook.h create mode 100644 amarok/src/engine/helix/helix-sp/hspvoladvise.h create mode 100644 amarok/src/engine/helix/helix-sp/iids.cpp create mode 100644 amarok/src/engine/helix/helix-sp/iir_cf.h create mode 100644 amarok/src/engine/helix/helix-sp/utils.cpp create mode 100644 amarok/src/engine/helix/helix-sp/utils.h create mode 100644 amarok/src/engine/helix/hxplayercontrol.cpp create mode 100644 amarok/src/engine/helix/hxplayercontrol.h create mode 100644 amarok/src/engine/kdemm/Makefile.am create mode 100644 amarok/src/engine/kdemm/amarok_kdemmengine_plugin.desktop create mode 100644 amarok/src/engine/kdemm/kdemmengine.cpp create mode 100644 amarok/src/engine/kdemm/kdemmengine.h create mode 100644 amarok/src/engine/mas/HOWTO create mode 100644 amarok/src/engine/mas/Makefile.am create mode 100644 amarok/src/engine/mas/TODO create mode 100644 amarok/src/engine/mas/amarok_masengine_plugin.desktop create mode 100644 amarok/src/engine/mas/masengine.cpp create mode 100644 amarok/src/engine/mas/masengine.h create mode 100644 amarok/src/engine/nmm/HostList.cpp create mode 100644 amarok/src/engine/nmm/HostList.h create mode 100644 amarok/src/engine/nmm/HostListItem.cpp create mode 100644 amarok/src/engine/nmm/HostListItem.h create mode 100644 amarok/src/engine/nmm/Makefile.am create mode 100644 amarok/src/engine/nmm/NmmLocation.cpp create mode 100644 amarok/src/engine/nmm/NmmLocation.h create mode 100644 amarok/src/engine/nmm/ServerregistryPing.cpp create mode 100644 amarok/src/engine/nmm/ServerregistryPing.h create mode 100644 amarok/src/engine/nmm/amarok_nmmengine_plugin.desktop create mode 100644 amarok/src/engine/nmm/icons/Makefile.am create mode 100644 amarok/src/engine/nmm/icons/hi16-action-nmm_option_off.png create mode 100644 amarok/src/engine/nmm/icons/hi16-action-nmm_option_on.png create mode 100644 amarok/src/engine/nmm/icons/hi16-action-nmm_option_on_readonly.png create mode 100644 amarok/src/engine/nmm/nmm-gradient-left.png create mode 100644 amarok/src/engine/nmm/nmm-gradient-right.png create mode 100644 amarok/src/engine/nmm/nmm-volume-inset.png create mode 100644 amarok/src/engine/nmm/nmm_configdialog.cpp create mode 100644 amarok/src/engine/nmm/nmm_configdialog.h create mode 100644 amarok/src/engine/nmm/nmm_configdialogbase.ui create mode 100644 amarok/src/engine/nmm/nmm_engine.cpp create mode 100644 amarok/src/engine/nmm/nmm_engine.h create mode 100644 amarok/src/engine/nmm/nmm_kdeconfig.kcfg create mode 100644 amarok/src/engine/nmm/nmm_kdeconfig.kcfgc create mode 100644 amarok/src/engine/void/Makefile.am create mode 100644 amarok/src/engine/void/amarok_void-engine_plugin.desktop create mode 100644 amarok/src/engine/void/void-engine.cpp create mode 100644 amarok/src/engine/void/void-engine.h create mode 100644 amarok/src/engine/xine/Makefile.am create mode 100644 amarok/src/engine/xine/amarok_xine-engine.desktop create mode 100644 amarok/src/engine/xine/amarok_xine-mp3_install.desktop create mode 100644 amarok/src/engine/xine/xine-config.cpp create mode 100644 amarok/src/engine/xine/xine-config.h create mode 100644 amarok/src/engine/xine/xine-engine.cpp create mode 100644 amarok/src/engine/xine/xine-engine.h create mode 100644 amarok/src/engine/xine/xine-scope.c create mode 100644 amarok/src/engine/xine/xine-scope.h create mode 100644 amarok/src/engine/xine/xinecfg.kcfg create mode 100644 amarok/src/engine/xine/xinecfg.kcfgc create mode 100644 amarok/src/engine/xine/xineconfigbase.ui create mode 100644 amarok/src/engine/yauap/Makefile.am create mode 100644 amarok/src/engine/yauap/amarok_yauap-engine_plugin.desktop create mode 100644 amarok/src/engine/yauap/yauap-engine.cpp create mode 100644 amarok/src/engine/yauap/yauap-engine.h create mode 100644 amarok/src/engine_fwd.h create mode 100644 amarok/src/enginebase.cpp create mode 100644 amarok/src/enginebase.h create mode 100644 amarok/src/enginecontroller.cpp create mode 100644 amarok/src/enginecontroller.h create mode 100644 amarok/src/engineobserver.cpp create mode 100644 amarok/src/engineobserver.h create mode 100644 amarok/src/equalizergraph.cpp create mode 100644 amarok/src/equalizergraph.h create mode 100644 amarok/src/equalizerpresetmanager.cpp create mode 100644 amarok/src/equalizerpresetmanager.h create mode 100644 amarok/src/equalizersetup.cpp create mode 100644 amarok/src/equalizersetup.h create mode 100644 amarok/src/expression.cpp create mode 100644 amarok/src/expression.h create mode 100644 amarok/src/fht.cpp create mode 100644 amarok/src/fht.h create mode 100644 amarok/src/filebrowser.cpp create mode 100644 amarok/src/filebrowser.h create mode 100644 amarok/src/firstrunwizard.ui create mode 100644 amarok/src/firstrunwizard.ui.h create mode 100644 amarok/src/hi128-app-amarok.png create mode 100644 amarok/src/hi16-app-amarok.png create mode 100644 amarok/src/hi22-app-amarok.png create mode 100644 amarok/src/hi32-app-amarok.png create mode 100644 amarok/src/hi48-app-amarok.png create mode 100644 amarok/src/hi64-app-amarok.png create mode 100644 amarok/src/hintlineedit.cpp create mode 100644 amarok/src/hintlineedit.h create mode 100644 amarok/src/htmlview.cpp create mode 100644 amarok/src/htmlview.h create mode 100644 amarok/src/iconloader.cpp create mode 100644 amarok/src/images/Makefile.am create mode 100644 amarok/src/images/amarok_cut.png create mode 100644 amarok/src/images/amarok_icon.svg create mode 100644 amarok/src/images/amarok_icon_small.svg create mode 100644 amarok/src/images/amarok_logo.svg create mode 100644 amarok/src/images/amarok_rocks.jpg create mode 100644 amarok/src/images/amarok_rocks.xcf create mode 100644 amarok/src/images/b_next.png create mode 100644 amarok/src/images/b_pause.png create mode 100644 amarok/src/images/b_play.png create mode 100644 amarok/src/images/b_prev.png create mode 100644 amarok/src/images/b_stop.png create mode 100644 amarok/src/images/back_stars_grey.png create mode 100644 amarok/src/images/currenttrack_bar_left.png create mode 100644 amarok/src/images/currenttrack_bar_mid.png create mode 100644 amarok/src/images/currenttrack_bar_right.png create mode 100644 amarok/src/images/currenttrack_pause.png create mode 100644 amarok/src/images/currenttrack_play.png create mode 100644 amarok/src/images/currenttrack_repeat.png create mode 100644 amarok/src/images/currenttrack_repeat_small.png create mode 100644 amarok/src/images/currenttrack_stop.png create mode 100644 amarok/src/images/currenttrack_stop_small.png create mode 100644 amarok/src/images/eq_active2.png create mode 100644 amarok/src/images/eq_inactive2.png create mode 100644 amarok/src/images/icons/Makefile.am create mode 100644 amarok/src/images/icons/cr16-action-covermanager.png create mode 100644 amarok/src/images/icons/cr16-action-dynamic.png create mode 100644 amarok/src/images/icons/cr16-action-equalizer.png create mode 100644 amarok/src/images/icons/cr16-action-mini_dock.png create mode 100644 amarok/src/images/icons/cr16-action-player_playlist_2.png create mode 100644 amarok/src/images/icons/cr16-action-podcast.png create mode 100644 amarok/src/images/icons/cr16-action-podcast_new.png create mode 100644 amarok/src/images/icons/cr16-action-random.png create mode 100644 amarok/src/images/icons/cr16-action-repeat_playlist.png create mode 100644 amarok/src/images/icons/cr16-action-repeat_track.png create mode 100644 amarok/src/images/icons/cr16-action-visualizations.png create mode 100644 amarok/src/images/icons/cr16-action-wiki.png create mode 100644 amarok/src/images/icons/cr22-action-amarok_podcast.png create mode 100644 amarok/src/images/icons/cr22-action-amarok_podcast_new.png create mode 100644 amarok/src/images/icons/cr22-action-babelfish.png create mode 100644 amarok/src/images/icons/cr22-action-dynamic.png create mode 100644 amarok/src/images/icons/cr22-action-player_playlist_2.png create mode 100644 amarok/src/images/icons/cr22-action-random.png create mode 100644 amarok/src/images/icons/cr22-action-repeat_playlist.png create mode 100644 amarok/src/images/icons/cr22-action-visualizations.png create mode 100644 amarok/src/images/icons/cr64-action-dynamic.png create mode 100644 amarok/src/images/icons/cr64-action-podcast.png create mode 100644 amarok/src/images/icons/cr64-action-podcast_new.png create mode 100644 amarok/src/images/icons/cr64-action-random.png create mode 100644 amarok/src/images/icons/cr64-action-repeat_playlist.png create mode 100644 amarok/src/images/icons/hi16-action-amarok_add_lyrics.png create mode 100644 amarok/src/images/icons/hi16-action-amarok_add_playlist.png create mode 100644 amarok/src/images/icons/hi16-action-amarok_album.png create mode 100644 amarok/src/images/icons/hi16-action-amarok_artist.png create mode 100644 amarok/src/images/icons/hi16-action-amarok_audioscrobbler.png create mode 100644 amarok/src/images/icons/hi16-action-amarok_back.png create mode 100644 amarok/src/images/icons/hi16-action-amarok_burn.png create mode 100644 amarok/src/images/icons/hi16-action-amarok_change_language.png create mode 100644 amarok/src/images/icons/hi16-action-amarok_circle.png create mode 100644 amarok/src/images/icons/hi16-action-amarok_clock.png create mode 100644 amarok/src/images/icons/hi16-action-amarok_collection.png create mode 100644 amarok/src/images/icons/hi16-action-amarok_configure.png create mode 100644 amarok/src/images/icons/hi16-action-amarok_covermanager.png create mode 100644 amarok/src/images/icons/hi16-action-amarok_device.png create mode 100644 amarok/src/images/icons/hi16-action-amarok_download.png create mode 100644 amarok/src/images/icons/hi16-action-amarok_dynamic.png create mode 100644 amarok/src/images/icons/hi16-action-amarok_edit.png create mode 100644 amarok/src/images/icons/hi16-action-amarok_editcopy.png create mode 100644 amarok/src/images/icons/hi16-action-amarok_equalizer.png create mode 100644 amarok/src/images/icons/hi16-action-amarok_external.png create mode 100644 amarok/src/images/icons/hi16-action-amarok_fastforward.png create mode 100644 amarok/src/images/icons/hi16-action-amarok_favourite_genres.png create mode 100644 amarok/src/images/icons/hi16-action-amarok_files.png create mode 100644 amarok/src/images/icons/hi16-action-amarok_files2.png create mode 100644 amarok/src/images/icons/hi16-action-amarok_info.png create mode 100644 amarok/src/images/icons/hi16-action-amarok_love.png create mode 100644 amarok/src/images/icons/hi16-action-amarok_lyrics.png create mode 100644 amarok/src/images/icons/hi16-action-amarok_magnatune.png create mode 100644 amarok/src/images/icons/hi16-action-amarok_mostplayed.png create mode 100644 amarok/src/images/icons/hi16-action-amarok_music.png create mode 100644 amarok/src/images/icons/hi16-action-amarok_next.png create mode 100644 amarok/src/images/icons/hi16-action-amarok_pause.png create mode 100644 amarok/src/images/icons/hi16-action-amarok_play.png create mode 100644 amarok/src/images/icons/hi16-action-amarok_playlist.png create mode 100644 amarok/src/images/icons/hi16-action-amarok_playlist_clear.png create mode 100644 amarok/src/images/icons/hi16-action-amarok_playlist_refresh.png create mode 100644 amarok/src/images/icons/hi16-action-amarok_podcast.png create mode 100644 amarok/src/images/icons/hi16-action-amarok_podcast2.png create mode 100644 amarok/src/images/icons/hi16-action-amarok_queue.png create mode 100644 amarok/src/images/icons/hi16-action-amarok_random.png create mode 100644 amarok/src/images/icons/hi16-action-amarok_random_album.png create mode 100644 amarok/src/images/icons/hi16-action-amarok_random_no.png create mode 100644 amarok/src/images/icons/hi16-action-amarok_random_track.png create mode 100644 amarok/src/images/icons/hi16-action-amarok_redo.png create mode 100644 amarok/src/images/icons/hi16-action-amarok_refresh.png create mode 100644 amarok/src/images/icons/hi16-action-amarok_remove.png create mode 100644 amarok/src/images/icons/hi16-action-amarok_remove_from_playlist.png create mode 100644 amarok/src/images/icons/hi16-action-amarok_repeat_album.png create mode 100644 amarok/src/images/icons/hi16-action-amarok_repeat_no.png create mode 100644 amarok/src/images/icons/hi16-action-amarok_repeat_playlist.png create mode 100644 amarok/src/images/icons/hi16-action-amarok_repeat_track.png create mode 100644 amarok/src/images/icons/hi16-action-amarok_rescan.png create mode 100644 amarok/src/images/icons/hi16-action-amarok_rewind.png create mode 100644 amarok/src/images/icons/hi16-action-amarok_save.png create mode 100644 amarok/src/images/icons/hi16-action-amarok_scripts.png create mode 100644 amarok/src/images/icons/hi16-action-amarok_search.png create mode 100644 amarok/src/images/icons/hi16-action-amarok_settings_engine.png create mode 100644 amarok/src/images/icons/hi16-action-amarok_settings_general.png create mode 100644 amarok/src/images/icons/hi16-action-amarok_settings_indicator.png create mode 100644 amarok/src/images/icons/hi16-action-amarok_settings_playback.png create mode 100644 amarok/src/images/icons/hi16-action-amarok_settings_view.png create mode 100644 amarok/src/images/icons/hi16-action-amarok_stop.png create mode 100644 amarok/src/images/icons/hi16-action-amarok_track.png create mode 100644 amarok/src/images/icons/hi16-action-amarok_undo.png create mode 100644 amarok/src/images/icons/hi16-action-amarok_visualizations.png create mode 100644 amarok/src/images/icons/hi16-action-amarok_zoom.png create mode 100644 amarok/src/images/icons/hi16-action-collection.png create mode 100644 amarok/src/images/icons/hi22-action-amarok_add_lyrics.png create mode 100644 amarok/src/images/icons/hi22-action-amarok_add_playlist.png create mode 100644 amarok/src/images/icons/hi22-action-amarok_album.png create mode 100644 amarok/src/images/icons/hi22-action-amarok_artist.png create mode 100644 amarok/src/images/icons/hi22-action-amarok_audioscrobbler.png create mode 100644 amarok/src/images/icons/hi22-action-amarok_back.png create mode 100644 amarok/src/images/icons/hi22-action-amarok_burn.png create mode 100644 amarok/src/images/icons/hi22-action-amarok_change_language.png create mode 100644 amarok/src/images/icons/hi22-action-amarok_circle.png create mode 100644 amarok/src/images/icons/hi22-action-amarok_clock.png create mode 100644 amarok/src/images/icons/hi22-action-amarok_collection.png create mode 100644 amarok/src/images/icons/hi22-action-amarok_configure.png create mode 100644 amarok/src/images/icons/hi22-action-amarok_covermanager.png create mode 100644 amarok/src/images/icons/hi22-action-amarok_device.png create mode 100644 amarok/src/images/icons/hi22-action-amarok_download.png create mode 100644 amarok/src/images/icons/hi22-action-amarok_dynamic.png create mode 100644 amarok/src/images/icons/hi22-action-amarok_edit.png create mode 100644 amarok/src/images/icons/hi22-action-amarok_editcopy.png create mode 100644 amarok/src/images/icons/hi22-action-amarok_equalizer.png create mode 100644 amarok/src/images/icons/hi22-action-amarok_external.png create mode 100644 amarok/src/images/icons/hi22-action-amarok_fastforward.png create mode 100644 amarok/src/images/icons/hi22-action-amarok_favourite_genres.png create mode 100644 amarok/src/images/icons/hi22-action-amarok_files.png create mode 100644 amarok/src/images/icons/hi22-action-amarok_files2.png create mode 100644 amarok/src/images/icons/hi22-action-amarok_info.png create mode 100644 amarok/src/images/icons/hi22-action-amarok_love.png create mode 100644 amarok/src/images/icons/hi22-action-amarok_lyrics.png create mode 100644 amarok/src/images/icons/hi22-action-amarok_magnatune.png create mode 100644 amarok/src/images/icons/hi22-action-amarok_mostplayed.png create mode 100644 amarok/src/images/icons/hi22-action-amarok_music.png create mode 100644 amarok/src/images/icons/hi22-action-amarok_next.png create mode 100644 amarok/src/images/icons/hi22-action-amarok_pause.png create mode 100644 amarok/src/images/icons/hi22-action-amarok_play.png create mode 100644 amarok/src/images/icons/hi22-action-amarok_playlist.png create mode 100644 amarok/src/images/icons/hi22-action-amarok_playlist_clear.png create mode 100644 amarok/src/images/icons/hi22-action-amarok_playlist_refresh.png create mode 100644 amarok/src/images/icons/hi22-action-amarok_podcast.png create mode 100644 amarok/src/images/icons/hi22-action-amarok_podcast2.png create mode 100644 amarok/src/images/icons/hi22-action-amarok_queue.png create mode 100644 amarok/src/images/icons/hi22-action-amarok_random.png create mode 100644 amarok/src/images/icons/hi22-action-amarok_random_album.png create mode 100644 amarok/src/images/icons/hi22-action-amarok_random_no.png create mode 100644 amarok/src/images/icons/hi22-action-amarok_random_track.png create mode 100644 amarok/src/images/icons/hi22-action-amarok_redo.png create mode 100644 amarok/src/images/icons/hi22-action-amarok_refresh.png create mode 100644 amarok/src/images/icons/hi22-action-amarok_remove.png create mode 100644 amarok/src/images/icons/hi22-action-amarok_remove_from_playlist.png create mode 100644 amarok/src/images/icons/hi22-action-amarok_repeat_album.png create mode 100644 amarok/src/images/icons/hi22-action-amarok_repeat_no.png create mode 100644 amarok/src/images/icons/hi22-action-amarok_repeat_playlist.png create mode 100644 amarok/src/images/icons/hi22-action-amarok_repeat_track.png create mode 100644 amarok/src/images/icons/hi22-action-amarok_rescan.png create mode 100644 amarok/src/images/icons/hi22-action-amarok_rewind.png create mode 100644 amarok/src/images/icons/hi22-action-amarok_save.png create mode 100644 amarok/src/images/icons/hi22-action-amarok_scripts.png create mode 100644 amarok/src/images/icons/hi22-action-amarok_search.png create mode 100644 amarok/src/images/icons/hi22-action-amarok_settings_engine.png create mode 100644 amarok/src/images/icons/hi22-action-amarok_settings_general.png create mode 100644 amarok/src/images/icons/hi22-action-amarok_settings_indicator.png create mode 100644 amarok/src/images/icons/hi22-action-amarok_settings_playback.png create mode 100644 amarok/src/images/icons/hi22-action-amarok_settings_view.png create mode 100644 amarok/src/images/icons/hi22-action-amarok_stop.png create mode 100644 amarok/src/images/icons/hi22-action-amarok_track.png create mode 100644 amarok/src/images/icons/hi22-action-amarok_undo.png create mode 100644 amarok/src/images/icons/hi22-action-amarok_visualizations.png create mode 100644 amarok/src/images/icons/hi22-action-amarok_zoom.png create mode 100644 amarok/src/images/icons/hi22-action-collection.png create mode 100644 amarok/src/images/icons/hi32-action-amarok_add_lyrics.png create mode 100644 amarok/src/images/icons/hi32-action-amarok_add_playlist.png create mode 100644 amarok/src/images/icons/hi32-action-amarok_album.png create mode 100644 amarok/src/images/icons/hi32-action-amarok_artist.png create mode 100644 amarok/src/images/icons/hi32-action-amarok_audioscrobbler.png create mode 100644 amarok/src/images/icons/hi32-action-amarok_back.png create mode 100644 amarok/src/images/icons/hi32-action-amarok_burn.png create mode 100644 amarok/src/images/icons/hi32-action-amarok_change_language.png create mode 100644 amarok/src/images/icons/hi32-action-amarok_circle.png create mode 100644 amarok/src/images/icons/hi32-action-amarok_clock.png create mode 100644 amarok/src/images/icons/hi32-action-amarok_collection.png create mode 100644 amarok/src/images/icons/hi32-action-amarok_configure.png create mode 100644 amarok/src/images/icons/hi32-action-amarok_covermanager.png create mode 100644 amarok/src/images/icons/hi32-action-amarok_device.png create mode 100644 amarok/src/images/icons/hi32-action-amarok_download.png create mode 100644 amarok/src/images/icons/hi32-action-amarok_dynamic.png create mode 100644 amarok/src/images/icons/hi32-action-amarok_edit.png create mode 100644 amarok/src/images/icons/hi32-action-amarok_editcopy.png create mode 100644 amarok/src/images/icons/hi32-action-amarok_equalizer.png create mode 100644 amarok/src/images/icons/hi32-action-amarok_external.png create mode 100644 amarok/src/images/icons/hi32-action-amarok_fastforward.png create mode 100644 amarok/src/images/icons/hi32-action-amarok_favourite_genres.png create mode 100644 amarok/src/images/icons/hi32-action-amarok_files.png create mode 100644 amarok/src/images/icons/hi32-action-amarok_files2.png create mode 100644 amarok/src/images/icons/hi32-action-amarok_info.png create mode 100644 amarok/src/images/icons/hi32-action-amarok_love.png create mode 100644 amarok/src/images/icons/hi32-action-amarok_lyrics.png create mode 100644 amarok/src/images/icons/hi32-action-amarok_magnatune.png create mode 100644 amarok/src/images/icons/hi32-action-amarok_mostplayed.png create mode 100644 amarok/src/images/icons/hi32-action-amarok_music.png create mode 100644 amarok/src/images/icons/hi32-action-amarok_next.png create mode 100644 amarok/src/images/icons/hi32-action-amarok_pause.png create mode 100644 amarok/src/images/icons/hi32-action-amarok_play.png create mode 100644 amarok/src/images/icons/hi32-action-amarok_playlist.png create mode 100644 amarok/src/images/icons/hi32-action-amarok_playlist_clear.png create mode 100644 amarok/src/images/icons/hi32-action-amarok_playlist_refresh.png create mode 100644 amarok/src/images/icons/hi32-action-amarok_podcast.png create mode 100644 amarok/src/images/icons/hi32-action-amarok_podcast2.png create mode 100644 amarok/src/images/icons/hi32-action-amarok_queue.png create mode 100644 amarok/src/images/icons/hi32-action-amarok_random.png create mode 100644 amarok/src/images/icons/hi32-action-amarok_random_album.png create mode 100644 amarok/src/images/icons/hi32-action-amarok_random_no.png create mode 100644 amarok/src/images/icons/hi32-action-amarok_random_track.png create mode 100644 amarok/src/images/icons/hi32-action-amarok_redo.png create mode 100644 amarok/src/images/icons/hi32-action-amarok_refresh.png create mode 100644 amarok/src/images/icons/hi32-action-amarok_remove.png create mode 100644 amarok/src/images/icons/hi32-action-amarok_remove_from_playlist.png create mode 100644 amarok/src/images/icons/hi32-action-amarok_repeat_album.png create mode 100644 amarok/src/images/icons/hi32-action-amarok_repeat_no.png create mode 100644 amarok/src/images/icons/hi32-action-amarok_repeat_playlist.png create mode 100644 amarok/src/images/icons/hi32-action-amarok_repeat_track.png create mode 100644 amarok/src/images/icons/hi32-action-amarok_rescan.png create mode 100644 amarok/src/images/icons/hi32-action-amarok_rewind.png create mode 100644 amarok/src/images/icons/hi32-action-amarok_save.png create mode 100644 amarok/src/images/icons/hi32-action-amarok_scripts.png create mode 100644 amarok/src/images/icons/hi32-action-amarok_search.png create mode 100644 amarok/src/images/icons/hi32-action-amarok_settings_engine.png create mode 100644 amarok/src/images/icons/hi32-action-amarok_settings_general.png create mode 100644 amarok/src/images/icons/hi32-action-amarok_settings_indicator.png create mode 100644 amarok/src/images/icons/hi32-action-amarok_settings_playback.png create mode 100644 amarok/src/images/icons/hi32-action-amarok_settings_view.png create mode 100644 amarok/src/images/icons/hi32-action-amarok_stop.png create mode 100644 amarok/src/images/icons/hi32-action-amarok_track.png create mode 100644 amarok/src/images/icons/hi32-action-amarok_undo.png create mode 100644 amarok/src/images/icons/hi32-action-amarok_visualizations.png create mode 100644 amarok/src/images/icons/hi32-action-amarok_zoom.png create mode 100644 amarok/src/images/icons/hi32-action-audioscrobbler.png create mode 100644 amarok/src/images/icons/hi32-action-collection.png create mode 100644 amarok/src/images/icons/hi48-action-amarok_add_lyrics.png create mode 100644 amarok/src/images/icons/hi48-action-amarok_add_playlist.png create mode 100644 amarok/src/images/icons/hi48-action-amarok_album.png create mode 100644 amarok/src/images/icons/hi48-action-amarok_artist.png create mode 100644 amarok/src/images/icons/hi48-action-amarok_audioscrobbler.png create mode 100644 amarok/src/images/icons/hi48-action-amarok_back.png create mode 100644 amarok/src/images/icons/hi48-action-amarok_burn.png create mode 100644 amarok/src/images/icons/hi48-action-amarok_change_language.png create mode 100644 amarok/src/images/icons/hi48-action-amarok_circle.png create mode 100644 amarok/src/images/icons/hi48-action-amarok_clock.png create mode 100644 amarok/src/images/icons/hi48-action-amarok_collection.png create mode 100644 amarok/src/images/icons/hi48-action-amarok_configure.png create mode 100644 amarok/src/images/icons/hi48-action-amarok_covermanager.png create mode 100644 amarok/src/images/icons/hi48-action-amarok_device.png create mode 100644 amarok/src/images/icons/hi48-action-amarok_download.png create mode 100644 amarok/src/images/icons/hi48-action-amarok_dynamic.png create mode 100644 amarok/src/images/icons/hi48-action-amarok_edit.png create mode 100644 amarok/src/images/icons/hi48-action-amarok_editcopy.png create mode 100644 amarok/src/images/icons/hi48-action-amarok_equalizer.png create mode 100644 amarok/src/images/icons/hi48-action-amarok_external.png create mode 100644 amarok/src/images/icons/hi48-action-amarok_fastforward.png create mode 100644 amarok/src/images/icons/hi48-action-amarok_favourite_genres.png create mode 100644 amarok/src/images/icons/hi48-action-amarok_files.png create mode 100644 amarok/src/images/icons/hi48-action-amarok_files2.png create mode 100644 amarok/src/images/icons/hi48-action-amarok_info.png create mode 100644 amarok/src/images/icons/hi48-action-amarok_love.png create mode 100644 amarok/src/images/icons/hi48-action-amarok_lyrics.png create mode 100644 amarok/src/images/icons/hi48-action-amarok_magnatune.png create mode 100644 amarok/src/images/icons/hi48-action-amarok_mostplayed.png create mode 100644 amarok/src/images/icons/hi48-action-amarok_music.png create mode 100644 amarok/src/images/icons/hi48-action-amarok_next.png create mode 100644 amarok/src/images/icons/hi48-action-amarok_pause.png create mode 100644 amarok/src/images/icons/hi48-action-amarok_play.png create mode 100644 amarok/src/images/icons/hi48-action-amarok_playlist.png create mode 100644 amarok/src/images/icons/hi48-action-amarok_playlist_clear.png create mode 100644 amarok/src/images/icons/hi48-action-amarok_playlist_refresh.png create mode 100644 amarok/src/images/icons/hi48-action-amarok_podcast.png create mode 100644 amarok/src/images/icons/hi48-action-amarok_podcast2.png create mode 100644 amarok/src/images/icons/hi48-action-amarok_queue.png create mode 100644 amarok/src/images/icons/hi48-action-amarok_random.png create mode 100644 amarok/src/images/icons/hi48-action-amarok_random_album.png create mode 100644 amarok/src/images/icons/hi48-action-amarok_random_no.png create mode 100644 amarok/src/images/icons/hi48-action-amarok_random_track.png create mode 100644 amarok/src/images/icons/hi48-action-amarok_redo.png create mode 100644 amarok/src/images/icons/hi48-action-amarok_refresh.png create mode 100644 amarok/src/images/icons/hi48-action-amarok_remove.png create mode 100644 amarok/src/images/icons/hi48-action-amarok_remove_from_playlist.png create mode 100644 amarok/src/images/icons/hi48-action-amarok_repeat_album.png create mode 100644 amarok/src/images/icons/hi48-action-amarok_repeat_no.png create mode 100644 amarok/src/images/icons/hi48-action-amarok_repeat_playlist.png create mode 100644 amarok/src/images/icons/hi48-action-amarok_repeat_track.png create mode 100644 amarok/src/images/icons/hi48-action-amarok_rescan.png create mode 100644 amarok/src/images/icons/hi48-action-amarok_rewind.png create mode 100644 amarok/src/images/icons/hi48-action-amarok_save.png create mode 100644 amarok/src/images/icons/hi48-action-amarok_scripts.png create mode 100644 amarok/src/images/icons/hi48-action-amarok_search.png create mode 100644 amarok/src/images/icons/hi48-action-amarok_settings_engine.png create mode 100644 amarok/src/images/icons/hi48-action-amarok_settings_general.png create mode 100644 amarok/src/images/icons/hi48-action-amarok_settings_indicator.png create mode 100644 amarok/src/images/icons/hi48-action-amarok_settings_playback.png create mode 100644 amarok/src/images/icons/hi48-action-amarok_settings_view.png create mode 100644 amarok/src/images/icons/hi48-action-amarok_stop.png create mode 100644 amarok/src/images/icons/hi48-action-amarok_track.png create mode 100644 amarok/src/images/icons/hi48-action-amarok_undo.png create mode 100644 amarok/src/images/icons/hi48-action-amarok_visualizations.png create mode 100644 amarok/src/images/icons/hi48-action-amarok_zoom.png create mode 100644 amarok/src/images/icons/hi48-action-collection.png create mode 100644 amarok/src/images/icons/hi64-action-amarok_add_lyrics.png create mode 100644 amarok/src/images/icons/hi64-action-amarok_add_playlist.png create mode 100644 amarok/src/images/icons/hi64-action-amarok_album.png create mode 100644 amarok/src/images/icons/hi64-action-amarok_artist.png create mode 100644 amarok/src/images/icons/hi64-action-amarok_audioscrobbler.png create mode 100644 amarok/src/images/icons/hi64-action-amarok_back.png create mode 100644 amarok/src/images/icons/hi64-action-amarok_burn.png create mode 100644 amarok/src/images/icons/hi64-action-amarok_change_language.png create mode 100644 amarok/src/images/icons/hi64-action-amarok_circle.png create mode 100644 amarok/src/images/icons/hi64-action-amarok_clock.png create mode 100644 amarok/src/images/icons/hi64-action-amarok_collection.png create mode 100644 amarok/src/images/icons/hi64-action-amarok_configure.png create mode 100644 amarok/src/images/icons/hi64-action-amarok_covermanager.png create mode 100644 amarok/src/images/icons/hi64-action-amarok_device.png create mode 100644 amarok/src/images/icons/hi64-action-amarok_download.png create mode 100644 amarok/src/images/icons/hi64-action-amarok_dynamic.png create mode 100644 amarok/src/images/icons/hi64-action-amarok_edit.png create mode 100644 amarok/src/images/icons/hi64-action-amarok_editcopy.png create mode 100644 amarok/src/images/icons/hi64-action-amarok_equalizer.png create mode 100644 amarok/src/images/icons/hi64-action-amarok_external.png create mode 100644 amarok/src/images/icons/hi64-action-amarok_fastforward.png create mode 100644 amarok/src/images/icons/hi64-action-amarok_favourite_genres.png create mode 100644 amarok/src/images/icons/hi64-action-amarok_files.png create mode 100644 amarok/src/images/icons/hi64-action-amarok_files2.png create mode 100644 amarok/src/images/icons/hi64-action-amarok_info.png create mode 100644 amarok/src/images/icons/hi64-action-amarok_love.png create mode 100644 amarok/src/images/icons/hi64-action-amarok_lyrics.png create mode 100644 amarok/src/images/icons/hi64-action-amarok_magnatune.png create mode 100644 amarok/src/images/icons/hi64-action-amarok_mostplayed.png create mode 100644 amarok/src/images/icons/hi64-action-amarok_music.png create mode 100644 amarok/src/images/icons/hi64-action-amarok_next.png create mode 100644 amarok/src/images/icons/hi64-action-amarok_pause.png create mode 100644 amarok/src/images/icons/hi64-action-amarok_play.png create mode 100644 amarok/src/images/icons/hi64-action-amarok_playlist.png create mode 100644 amarok/src/images/icons/hi64-action-amarok_playlist_clear.png create mode 100644 amarok/src/images/icons/hi64-action-amarok_playlist_refresh.png create mode 100644 amarok/src/images/icons/hi64-action-amarok_podcast.png create mode 100644 amarok/src/images/icons/hi64-action-amarok_podcast2.png create mode 100644 amarok/src/images/icons/hi64-action-amarok_queue.png create mode 100644 amarok/src/images/icons/hi64-action-amarok_random.png create mode 100644 amarok/src/images/icons/hi64-action-amarok_random_album.png create mode 100644 amarok/src/images/icons/hi64-action-amarok_random_no.png create mode 100644 amarok/src/images/icons/hi64-action-amarok_random_track.png create mode 100644 amarok/src/images/icons/hi64-action-amarok_redo.png create mode 100644 amarok/src/images/icons/hi64-action-amarok_refresh.png create mode 100644 amarok/src/images/icons/hi64-action-amarok_remove.png create mode 100644 amarok/src/images/icons/hi64-action-amarok_remove_from_playlist.png create mode 100644 amarok/src/images/icons/hi64-action-amarok_repeat_album.png create mode 100644 amarok/src/images/icons/hi64-action-amarok_repeat_no.png create mode 100644 amarok/src/images/icons/hi64-action-amarok_repeat_playlist.png create mode 100644 amarok/src/images/icons/hi64-action-amarok_repeat_track.png create mode 100644 amarok/src/images/icons/hi64-action-amarok_rescan.png create mode 100644 amarok/src/images/icons/hi64-action-amarok_rewind.png create mode 100644 amarok/src/images/icons/hi64-action-amarok_save.png create mode 100644 amarok/src/images/icons/hi64-action-amarok_scripts.png create mode 100644 amarok/src/images/icons/hi64-action-amarok_search.png create mode 100644 amarok/src/images/icons/hi64-action-amarok_settings_engine.png create mode 100644 amarok/src/images/icons/hi64-action-amarok_settings_general.png create mode 100644 amarok/src/images/icons/hi64-action-amarok_settings_indicator.png create mode 100644 amarok/src/images/icons/hi64-action-amarok_settings_playback.png create mode 100644 amarok/src/images/icons/hi64-action-amarok_settings_view.png create mode 100644 amarok/src/images/icons/hi64-action-amarok_stop.png create mode 100644 amarok/src/images/icons/hi64-action-amarok_track.png create mode 100644 amarok/src/images/icons/hi64-action-amarok_undo.png create mode 100644 amarok/src/images/icons/hi64-action-amarok_visualizations.png create mode 100644 amarok/src/images/icons/hi64-action-amarok_zoom.png create mode 100644 amarok/src/images/icons/hi64-action-collection.png create mode 100644 amarok/src/images/icons/svg/crsc-action-dynamic.svg create mode 100644 amarok/src/images/icons/svg/crsc-action-player_playlist_2.svg create mode 100644 amarok/src/images/icons/svg/sources.svg create mode 100644 amarok/src/images/lastfm.png create mode 100644 amarok/src/images/loading1.png create mode 100644 amarok/src/images/loading2.png create mode 100644 amarok/src/images/menu_sidepixmap.png create mode 100644 amarok/src/images/menu_sidepixmap.xcf create mode 100644 amarok/src/images/more_albums.png create mode 100644 amarok/src/images/musicbrainz.png create mode 100644 amarok/src/images/nocover.png create mode 100644 amarok/src/images/pl_active2.png create mode 100644 amarok/src/images/pl_inactive2.png create mode 100644 amarok/src/images/player_background.jpg create mode 100644 amarok/src/images/sbinner_stars.png create mode 100644 amarok/src/images/shadow_albumcover.png create mode 100644 amarok/src/images/smallstar.png create mode 100644 amarok/src/images/sound.png create mode 100644 amarok/src/images/splash_screen.jpg create mode 100644 amarok/src/images/star.png create mode 100644 amarok/src/images/time_minus.png create mode 100644 amarok/src/images/time_plus.png create mode 100644 amarok/src/images/vol_speaker.png create mode 100644 amarok/src/images/volumeslider-gradient.png create mode 100644 amarok/src/images/volumeslider-handle.png create mode 100644 amarok/src/images/volumeslider-handle_glow.png create mode 100644 amarok/src/images/volumeslider-inset.png create mode 100644 amarok/src/images/wizard_compact.png create mode 100644 amarok/src/images/wizard_xmms.png create mode 100644 amarok/src/images/xine_logo.png create mode 100644 amarok/src/inotify/inotify-syscalls.h create mode 100644 amarok/src/inotify/inotify.h create mode 100644 amarok/src/k3bexporter.cpp create mode 100644 amarok/src/k3bexporter.h create mode 100644 amarok/src/kbookmarkhandler.cpp create mode 100644 amarok/src/kbookmarkhandler.h create mode 100644 amarok/src/konquisidebar/Makefile.am create mode 100644 amarok/src/konquisidebar/amarok.desktop create mode 100644 amarok/src/konquisidebar/universalamarok.cpp create mode 100644 amarok/src/konquisidebar/universalamarok.h create mode 100644 amarok/src/ktrm.cpp create mode 100644 amarok/src/ktrm.h create mode 100644 amarok/src/lastfm.cpp create mode 100644 amarok/src/lastfm.h create mode 100644 amarok/src/loader/Makefile.am create mode 100644 amarok/src/loader/loader.cpp create mode 100644 amarok/src/loader/loader.h create mode 100644 amarok/src/magnatunebrowser/Makefile.am create mode 100644 amarok/src/magnatunebrowser/magnatunealbumdownloader.cpp create mode 100644 amarok/src/magnatunebrowser/magnatunealbumdownloader.h create mode 100644 amarok/src/magnatunebrowser/magnatuneartistinfobox.cpp create mode 100644 amarok/src/magnatunebrowser/magnatuneartistinfobox.h create mode 100644 amarok/src/magnatunebrowser/magnatunebrowser.cpp create mode 100644 amarok/src/magnatunebrowser/magnatunebrowser.h create mode 100644 amarok/src/magnatunebrowser/magnatunedatabasehandler.cpp create mode 100644 amarok/src/magnatunebrowser/magnatunedatabasehandler.h create mode 100644 amarok/src/magnatunebrowser/magnatunedownloaddialog.cpp create mode 100644 amarok/src/magnatunebrowser/magnatunedownloaddialog.h create mode 100644 amarok/src/magnatunebrowser/magnatunedownloaddialogbase.ui create mode 100644 amarok/src/magnatunebrowser/magnatunedownloadinfo.cpp create mode 100644 amarok/src/magnatunebrowser/magnatunedownloadinfo.h create mode 100644 amarok/src/magnatunebrowser/magnatunelistview.cpp create mode 100644 amarok/src/magnatunebrowser/magnatunelistview.h create mode 100644 amarok/src/magnatunebrowser/magnatunelistviewitems.cpp create mode 100644 amarok/src/magnatunebrowser/magnatunelistviewitems.h create mode 100644 amarok/src/magnatunebrowser/magnatunepurchasedialog.cpp create mode 100644 amarok/src/magnatunebrowser/magnatunepurchasedialog.h create mode 100644 amarok/src/magnatunebrowser/magnatunepurchasedialogbase.ui create mode 100644 amarok/src/magnatunebrowser/magnatunepurchasehandler.cpp create mode 100644 amarok/src/magnatunebrowser/magnatunepurchasehandler.h create mode 100644 amarok/src/magnatunebrowser/magnatuneredownloaddialog.cpp create mode 100644 amarok/src/magnatunebrowser/magnatuneredownloaddialog.h create mode 100644 amarok/src/magnatunebrowser/magnatuneredownloaddialogbase.ui create mode 100644 amarok/src/magnatunebrowser/magnatuneredownloadhandler.cpp create mode 100644 amarok/src/magnatunebrowser/magnatuneredownloadhandler.h create mode 100644 amarok/src/magnatunebrowser/magnatunetypes.cpp create mode 100644 amarok/src/magnatunebrowser/magnatunetypes.h create mode 100644 amarok/src/magnatunebrowser/magnatunexmlparser.cpp create mode 100644 amarok/src/magnatunebrowser/magnatunexmlparser.h create mode 100644 amarok/src/main.cpp create mode 100644 amarok/src/mediabrowser.cpp create mode 100644 amarok/src/mediabrowser.h create mode 100644 amarok/src/mediadevice/Makefile.am create mode 100644 amarok/src/mediadevice/daap/Makefile.am create mode 100644 amarok/src/mediadevice/daap/addhostbase.ui create mode 100644 amarok/src/mediadevice/daap/amarok_daap-mediadevice.desktop create mode 100755 amarok/src/mediadevice/daap/amarok_daapserver.rb create mode 100644 amarok/src/mediadevice/daap/codes.rb create mode 100644 amarok/src/mediadevice/daap/daapclient.cpp create mode 100644 amarok/src/mediadevice/daap/daapclient.h create mode 100644 amarok/src/mediadevice/daap/daapreader/Makefile.am create mode 100644 amarok/src/mediadevice/daap/daapreader/authentication/Makefile.am create mode 100644 amarok/src/mediadevice/daap/daapreader/authentication/contentfetcher.cpp create mode 100644 amarok/src/mediadevice/daap/daapreader/authentication/contentfetcher.h create mode 100644 amarok/src/mediadevice/daap/daapreader/authentication/hasher.c create mode 100644 amarok/src/mediadevice/daap/daapreader/authentication/hasher.h create mode 100644 amarok/src/mediadevice/daap/daapreader/authentication/md5.c create mode 100644 amarok/src/mediadevice/daap/daapreader/authentication/md5.h create mode 100644 amarok/src/mediadevice/daap/daapreader/authentication/portability.h create mode 100644 amarok/src/mediadevice/daap/daapreader/reader.cpp create mode 100644 amarok/src/mediadevice/daap/daapreader/reader.h create mode 100644 amarok/src/mediadevice/daap/daapserver.cpp create mode 100644 amarok/src/mediadevice/daap/daapserver.h create mode 100644 amarok/src/mediadevice/daap/mongrel/Makefile.am create mode 100644 amarok/src/mediadevice/daap/mongrel/http11/Makefile.am create mode 100644 amarok/src/mediadevice/daap/mongrel/http11/ext_help.h create mode 100644 amarok/src/mediadevice/daap/mongrel/http11/http11.c create mode 100644 amarok/src/mediadevice/daap/mongrel/http11/http11.rb create mode 100644 amarok/src/mediadevice/daap/mongrel/http11/http11_parser.c create mode 100644 amarok/src/mediadevice/daap/mongrel/http11/http11_parser.h create mode 100644 amarok/src/mediadevice/daap/mongrel/http11/http11_parser.rl create mode 100644 amarok/src/mediadevice/daap/mongrel/http11/tst.h create mode 100644 amarok/src/mediadevice/daap/mongrel/http11/tst_cleanup.c create mode 100644 amarok/src/mediadevice/daap/mongrel/http11/tst_delete.c create mode 100644 amarok/src/mediadevice/daap/mongrel/http11/tst_grow_node_free_list.c create mode 100644 amarok/src/mediadevice/daap/mongrel/http11/tst_init.c create mode 100644 amarok/src/mediadevice/daap/mongrel/http11/tst_insert.c create mode 100644 amarok/src/mediadevice/daap/mongrel/http11/tst_search.c create mode 100644 amarok/src/mediadevice/daap/mongrel/lib/Makefile.am create mode 100644 amarok/src/mediadevice/daap/mongrel/lib/README create mode 100644 amarok/src/mediadevice/daap/mongrel/lib/gem_plugin.rb create mode 100644 amarok/src/mediadevice/daap/mongrel/lib/gemconfigure.rb create mode 100644 amarok/src/mediadevice/daap/mongrel/lib/mongrel.rb create mode 100644 amarok/src/mediadevice/daap/mongrel/lib/mongrel/Makefile.am create mode 100644 amarok/src/mediadevice/daap/mongrel/lib/mongrel/cgi.rb create mode 100644 amarok/src/mediadevice/daap/mongrel/lib/mongrel/command.rb create mode 100644 amarok/src/mediadevice/daap/mongrel/lib/mongrel/configurator.rb create mode 100644 amarok/src/mediadevice/daap/mongrel/lib/mongrel/debug.rb create mode 100644 amarok/src/mediadevice/daap/mongrel/lib/mongrel/handlers.rb create mode 100644 amarok/src/mediadevice/daap/mongrel/lib/mongrel/init.rb create mode 100644 amarok/src/mediadevice/daap/mongrel/lib/mongrel/mime_types.yml create mode 100644 amarok/src/mediadevice/daap/mongrel/lib/mongrel/stats.rb create mode 100644 amarok/src/mediadevice/daap/mongrel/lib/mongrel/tcphack.rb create mode 100644 amarok/src/mediadevice/daap/mongrel/lib/rbconfig/Makefile.am create mode 100644 amarok/src/mediadevice/daap/mongrel/lib/rbconfig/datadir.rb create mode 100644 amarok/src/mediadevice/daap/mongrel/lib/rubygems.rb create mode 100644 amarok/src/mediadevice/daap/mongrel/lib/rubygems/._gem_commands.rb create mode 100644 amarok/src/mediadevice/daap/mongrel/lib/rubygems/Makefile.am create mode 100644 amarok/src/mediadevice/daap/mongrel/lib/rubygems/builder.rb create mode 100644 amarok/src/mediadevice/daap/mongrel/lib/rubygems/cmd_manager.rb create mode 100644 amarok/src/mediadevice/daap/mongrel/lib/rubygems/command.rb create mode 100644 amarok/src/mediadevice/daap/mongrel/lib/rubygems/config_file.rb create mode 100644 amarok/src/mediadevice/daap/mongrel/lib/rubygems/custom_require.rb create mode 100644 amarok/src/mediadevice/daap/mongrel/lib/rubygems/dependency_list.rb create mode 100644 amarok/src/mediadevice/daap/mongrel/lib/rubygems/doc_manager.rb create mode 100644 amarok/src/mediadevice/daap/mongrel/lib/rubygems/format.rb create mode 100644 amarok/src/mediadevice/daap/mongrel/lib/rubygems/gem_commands.rb create mode 100644 amarok/src/mediadevice/daap/mongrel/lib/rubygems/gem_openssl.rb create mode 100644 amarok/src/mediadevice/daap/mongrel/lib/rubygems/gem_runner.rb create mode 100644 amarok/src/mediadevice/daap/mongrel/lib/rubygems/incremental_fetcher.rb create mode 100644 amarok/src/mediadevice/daap/mongrel/lib/rubygems/installer.rb create mode 100644 amarok/src/mediadevice/daap/mongrel/lib/rubygems/loadpath_manager.rb create mode 100644 amarok/src/mediadevice/daap/mongrel/lib/rubygems/old_format.rb create mode 100644 amarok/src/mediadevice/daap/mongrel/lib/rubygems/open-uri.rb create mode 100644 amarok/src/mediadevice/daap/mongrel/lib/rubygems/package.rb create mode 100644 amarok/src/mediadevice/daap/mongrel/lib/rubygems/remote_installer.rb create mode 100644 amarok/src/mediadevice/daap/mongrel/lib/rubygems/rubygems_version.rb create mode 100644 amarok/src/mediadevice/daap/mongrel/lib/rubygems/security.rb create mode 100644 amarok/src/mediadevice/daap/mongrel/lib/rubygems/source_index.rb create mode 100644 amarok/src/mediadevice/daap/mongrel/lib/rubygems/specification.rb create mode 100644 amarok/src/mediadevice/daap/mongrel/lib/rubygems/timer.rb create mode 100644 amarok/src/mediadevice/daap/mongrel/lib/rubygems/user_interaction.rb create mode 100644 amarok/src/mediadevice/daap/mongrel/lib/rubygems/validator.rb create mode 100644 amarok/src/mediadevice/daap/mongrel/lib/rubygems/version.rb create mode 100644 amarok/src/mediadevice/daap/proxy.cpp create mode 100644 amarok/src/mediadevice/daap/proxy.h create mode 100644 amarok/src/mediadevice/generic/Makefile.am create mode 100644 amarok/src/mediadevice/generic/amarok_generic-mediadevice.desktop create mode 100644 amarok/src/mediadevice/generic/genericmediadevice.cpp create mode 100644 amarok/src/mediadevice/generic/genericmediadevice.h create mode 100644 amarok/src/mediadevice/generic/genericmediadeviceconfigdialog.ui create mode 100644 amarok/src/mediadevice/generic/genericmediadeviceconfigdialog.ui.h create mode 100644 amarok/src/mediadevice/ifp/Makefile.am create mode 100644 amarok/src/mediadevice/ifp/amarok_ifp-mediadevice.desktop create mode 100644 amarok/src/mediadevice/ifp/ifpmediadevice.cpp create mode 100644 amarok/src/mediadevice/ifp/ifpmediadevice.h create mode 100644 amarok/src/mediadevice/ipod/Makefile.am create mode 100644 amarok/src/mediadevice/ipod/amarok_ipod-mediadevice.desktop create mode 100644 amarok/src/mediadevice/ipod/ipodmediadevice.cpp create mode 100644 amarok/src/mediadevice/ipod/ipodmediadevice.h create mode 100644 amarok/src/mediadevice/mtp/Makefile.am create mode 100644 amarok/src/mediadevice/mtp/amarok_mtp-mediadevice.desktop create mode 100644 amarok/src/mediadevice/mtp/mtpmediadevice.cpp create mode 100644 amarok/src/mediadevice/mtp/mtpmediadevice.h create mode 100644 amarok/src/mediadevice/njb/Makefile.am create mode 100644 amarok/src/mediadevice/njb/amarok_njb-mediadevice.desktop create mode 100644 amarok/src/mediadevice/njb/njbmediadevice.cpp create mode 100644 amarok/src/mediadevice/njb/njbmediadevice.h create mode 100644 amarok/src/mediadevice/njb/playlist.cpp create mode 100644 amarok/src/mediadevice/njb/playlist.h create mode 100644 amarok/src/mediadevice/njb/track.cpp create mode 100644 amarok/src/mediadevice/njb/track.h create mode 100644 amarok/src/mediadevice/riokarma/Makefile.am create mode 100644 amarok/src/mediadevice/riokarma/amarok_riokarma-mediadevice.desktop create mode 100644 amarok/src/mediadevice/riokarma/riokarmamediadevice.cpp create mode 100644 amarok/src/mediadevice/riokarma/riokarmamediadevice.h create mode 100644 amarok/src/mediadevicemanager.cpp create mode 100644 amarok/src/mediadevicemanager.h create mode 100644 amarok/src/medium.cpp create mode 100644 amarok/src/medium.h create mode 100644 amarok/src/mediumpluginmanager.cpp create mode 100644 amarok/src/mediumpluginmanager.h create mode 100644 amarok/src/metabundle.cpp create mode 100644 amarok/src/metabundle.h create mode 100644 amarok/src/metabundlesaver.cpp create mode 100644 amarok/src/metabundlesaver.h create mode 100644 amarok/src/metadata/Makefile.am create mode 100644 amarok/src/metadata/aac/Makefile.am create mode 100644 amarok/src/metadata/aac/aacfiletyperesolver.cpp create mode 100644 amarok/src/metadata/aac/aacfiletyperesolver.h create mode 100644 amarok/src/metadata/asf/Makefile.am create mode 100644 amarok/src/metadata/asf/asfattribute.cpp create mode 100644 amarok/src/metadata/asf/asfattribute.h create mode 100644 amarok/src/metadata/asf/asffile.cpp create mode 100644 amarok/src/metadata/asf/asffile.h create mode 100644 amarok/src/metadata/asf/asfproperties.cpp create mode 100644 amarok/src/metadata/asf/asfproperties.h create mode 100644 amarok/src/metadata/asf/asftag.cpp create mode 100644 amarok/src/metadata/asf/asftag.h create mode 100644 amarok/src/metadata/asf/taglib_asffiletyperesolver.cpp create mode 100644 amarok/src/metadata/asf/taglib_asffiletyperesolver.h create mode 100644 amarok/src/metadata/audible/Makefile.am create mode 100644 amarok/src/metadata/audible/audibleproperties.cpp create mode 100644 amarok/src/metadata/audible/audibleproperties.h create mode 100644 amarok/src/metadata/audible/audibletag.cpp create mode 100644 amarok/src/metadata/audible/audibletag.h create mode 100644 amarok/src/metadata/audible/taglib_audiblefile.cpp create mode 100644 amarok/src/metadata/audible/taglib_audiblefile.h create mode 100644 amarok/src/metadata/audible/taglib_audiblefiletyperesolver.cpp create mode 100644 amarok/src/metadata/audible/taglib_audiblefiletyperesolver.h create mode 100644 amarok/src/metadata/m4a/Makefile.am create mode 100644 amarok/src/metadata/m4a/boxfactory.cpp create mode 100644 amarok/src/metadata/m4a/boxfactory.h create mode 100644 amarok/src/metadata/m4a/itunesalbbox.cpp create mode 100644 amarok/src/metadata/m4a/itunesalbbox.h create mode 100644 amarok/src/metadata/m4a/itunesartbox.cpp create mode 100644 amarok/src/metadata/m4a/itunesartbox.h create mode 100644 amarok/src/metadata/m4a/itunescmtbox.cpp create mode 100644 amarok/src/metadata/m4a/itunescmtbox.h create mode 100644 amarok/src/metadata/m4a/itunescvrbox.cpp create mode 100644 amarok/src/metadata/m4a/itunescvrbox.h create mode 100644 amarok/src/metadata/m4a/itunesdatabox.cpp create mode 100644 amarok/src/metadata/m4a/itunesdatabox.h create mode 100644 amarok/src/metadata/m4a/itunesdaybox.cpp create mode 100644 amarok/src/metadata/m4a/itunesdaybox.h create mode 100644 amarok/src/metadata/m4a/itunesdiskbox.cpp create mode 100644 amarok/src/metadata/m4a/itunesdiskbox.h create mode 100644 amarok/src/metadata/m4a/itunesgenbox.cpp create mode 100644 amarok/src/metadata/m4a/itunesgenbox.h create mode 100644 amarok/src/metadata/m4a/itunesgrpbox.cpp create mode 100644 amarok/src/metadata/m4a/itunesgrpbox.h create mode 100644 amarok/src/metadata/m4a/itunesnambox.cpp create mode 100644 amarok/src/metadata/m4a/itunesnambox.h create mode 100644 amarok/src/metadata/m4a/itunestmpobox.cpp create mode 100644 amarok/src/metadata/m4a/itunestmpobox.h create mode 100644 amarok/src/metadata/m4a/itunestrknbox.cpp create mode 100644 amarok/src/metadata/m4a/itunestrknbox.h create mode 100644 amarok/src/metadata/m4a/ituneswrtbox.cpp create mode 100644 amarok/src/metadata/m4a/ituneswrtbox.h create mode 100644 amarok/src/metadata/m4a/mp4audioproperties.cpp create mode 100644 amarok/src/metadata/m4a/mp4audioproperties.h create mode 100644 amarok/src/metadata/m4a/mp4audiosampleentry.cpp create mode 100644 amarok/src/metadata/m4a/mp4audiosampleentry.h create mode 100644 amarok/src/metadata/m4a/mp4file.cpp create mode 100644 amarok/src/metadata/m4a/mp4file.h create mode 100644 amarok/src/metadata/m4a/mp4fourcc.cpp create mode 100644 amarok/src/metadata/m4a/mp4fourcc.h create mode 100644 amarok/src/metadata/m4a/mp4hdlrbox.cpp create mode 100644 amarok/src/metadata/m4a/mp4hdlrbox.h create mode 100644 amarok/src/metadata/m4a/mp4ilstbox.cpp create mode 100644 amarok/src/metadata/m4a/mp4ilstbox.h create mode 100644 amarok/src/metadata/m4a/mp4isobox.cpp create mode 100644 amarok/src/metadata/m4a/mp4isobox.h create mode 100644 amarok/src/metadata/m4a/mp4isofullbox.cpp create mode 100644 amarok/src/metadata/m4a/mp4isofullbox.h create mode 100644 amarok/src/metadata/m4a/mp4itunestag.cpp create mode 100644 amarok/src/metadata/m4a/mp4itunestag.h create mode 100644 amarok/src/metadata/m4a/mp4mdiabox.cpp create mode 100644 amarok/src/metadata/m4a/mp4mdiabox.h create mode 100644 amarok/src/metadata/m4a/mp4metabox.cpp create mode 100644 amarok/src/metadata/m4a/mp4metabox.h create mode 100644 amarok/src/metadata/m4a/mp4minfbox.cpp create mode 100644 amarok/src/metadata/m4a/mp4minfbox.h create mode 100644 amarok/src/metadata/m4a/mp4moovbox.cpp create mode 100644 amarok/src/metadata/m4a/mp4moovbox.h create mode 100644 amarok/src/metadata/m4a/mp4mvhdbox.cpp create mode 100644 amarok/src/metadata/m4a/mp4mvhdbox.h create mode 100644 amarok/src/metadata/m4a/mp4propsproxy.cpp create mode 100644 amarok/src/metadata/m4a/mp4propsproxy.h create mode 100644 amarok/src/metadata/m4a/mp4sampleentry.cpp create mode 100644 amarok/src/metadata/m4a/mp4sampleentry.h create mode 100644 amarok/src/metadata/m4a/mp4skipbox.cpp create mode 100644 amarok/src/metadata/m4a/mp4skipbox.h create mode 100644 amarok/src/metadata/m4a/mp4stblbox.cpp create mode 100644 amarok/src/metadata/m4a/mp4stblbox.h create mode 100644 amarok/src/metadata/m4a/mp4stsdbox.cpp create mode 100644 amarok/src/metadata/m4a/mp4stsdbox.h create mode 100644 amarok/src/metadata/m4a/mp4tagsproxy.cpp create mode 100644 amarok/src/metadata/m4a/mp4tagsproxy.h create mode 100644 amarok/src/metadata/m4a/mp4trakbox.cpp create mode 100644 amarok/src/metadata/m4a/mp4trakbox.h create mode 100644 amarok/src/metadata/m4a/mp4udtabox.cpp create mode 100644 amarok/src/metadata/m4a/mp4udtabox.h create mode 100644 amarok/src/metadata/m4a/taglib_mp4filetyperesolver.cpp create mode 100644 amarok/src/metadata/m4a/taglib_mp4filetyperesolver.h create mode 100644 amarok/src/metadata/mp4/Makefile.am create mode 100644 amarok/src/metadata/mp4/mp4file.cpp create mode 100644 amarok/src/metadata/mp4/mp4file.h create mode 100644 amarok/src/metadata/mp4/mp4properties.cpp create mode 100644 amarok/src/metadata/mp4/mp4properties.h create mode 100644 amarok/src/metadata/mp4/mp4tag.cpp create mode 100644 amarok/src/metadata/mp4/mp4tag.h create mode 100644 amarok/src/metadata/mp4/taglib_mp4filetyperesolver.cpp create mode 100644 amarok/src/metadata/mp4/taglib_mp4filetyperesolver.h create mode 100644 amarok/src/metadata/rmff/Makefile.am create mode 100644 amarok/src/metadata/rmff/rmff.cpp create mode 100644 amarok/src/metadata/rmff/rmff.h create mode 100644 amarok/src/metadata/rmff/taglib_realmediafile.cpp create mode 100644 amarok/src/metadata/rmff/taglib_realmediafile.h create mode 100644 amarok/src/metadata/rmff/taglib_realmediafiletyperesolver.cpp create mode 100644 amarok/src/metadata/rmff/taglib_realmediafiletyperesolver.h create mode 100644 amarok/src/metadata/speex/Makefile.am create mode 100644 amarok/src/metadata/speex/speexfile.cpp create mode 100644 amarok/src/metadata/speex/speexfile.h create mode 100644 amarok/src/metadata/speex/speexproperties.cpp create mode 100644 amarok/src/metadata/speex/speexproperties.h create mode 100644 amarok/src/metadata/speex/taglib_speexfiletyperesolver.cpp create mode 100644 amarok/src/metadata/speex/taglib_speexfiletyperesolver.h create mode 100644 amarok/src/metadata/tplugins.cpp create mode 100644 amarok/src/metadata/tplugins.h create mode 100644 amarok/src/metadata/trueaudio/Makefile.am create mode 100644 amarok/src/metadata/trueaudio/combinedtag.h create mode 100644 amarok/src/metadata/trueaudio/taglib_trueaudiofiletyperesolver.cpp create mode 100644 amarok/src/metadata/trueaudio/taglib_trueaudiofiletyperesolver.h create mode 100644 amarok/src/metadata/trueaudio/ttafile.cpp create mode 100644 amarok/src/metadata/trueaudio/ttafile.h create mode 100644 amarok/src/metadata/trueaudio/ttaproperties.cpp create mode 100644 amarok/src/metadata/trueaudio/ttaproperties.h create mode 100644 amarok/src/metadata/wav/Makefile.am create mode 100644 amarok/src/metadata/wav/wavfile.cpp create mode 100644 amarok/src/metadata/wav/wavfile.h create mode 100644 amarok/src/metadata/wav/wavfiletyperesolver.cpp create mode 100644 amarok/src/metadata/wav/wavfiletyperesolver.h create mode 100644 amarok/src/metadata/wav/wavproperties.cpp create mode 100644 amarok/src/metadata/wav/wavproperties.h create mode 100644 amarok/src/metadata/wavpack/Makefile.am create mode 100644 amarok/src/metadata/wavpack/combinedtag.h create mode 100644 amarok/src/metadata/wavpack/taglib_wavpackfiletyperesolver.cpp create mode 100644 amarok/src/metadata/wavpack/taglib_wavpackfiletyperesolver.h create mode 100644 amarok/src/metadata/wavpack/wvfile.cpp create mode 100644 amarok/src/metadata/wavpack/wvfile.h create mode 100644 amarok/src/metadata/wavpack/wvproperties.cpp create mode 100644 amarok/src/metadata/wavpack/wvproperties.h create mode 100644 amarok/src/moodbar.cpp create mode 100644 amarok/src/moodbar.h create mode 100644 amarok/src/mountpointmanager.cpp create mode 100644 amarok/src/mountpointmanager.h create mode 100644 amarok/src/multitabbar.cpp create mode 100644 amarok/src/multitabbar.h create mode 100644 amarok/src/multitabbar_p.h create mode 100644 amarok/src/mydirlister.h create mode 100644 amarok/src/mydiroperator.cpp create mode 100644 amarok/src/mydiroperator.h create mode 100644 amarok/src/newdynamic.ui create mode 100644 amarok/src/organizecollectiondialog.ui create mode 100644 amarok/src/organizecollectiondialog.ui.h create mode 100644 amarok/src/osd.cpp create mode 100644 amarok/src/osd.h create mode 100644 amarok/src/pixmapviewer.cpp create mode 100644 amarok/src/pixmapviewer.h create mode 100644 amarok/src/playerwindow.cpp create mode 100644 amarok/src/playerwindow.h create mode 100644 amarok/src/playlist.cpp create mode 100644 amarok/src/playlist.h create mode 100644 amarok/src/playlistbrowser.cpp create mode 100644 amarok/src/playlistbrowser.h create mode 100644 amarok/src/playlistbrowseritem.cpp create mode 100644 amarok/src/playlistbrowseritem.h create mode 100644 amarok/src/playlistitem.cpp create mode 100644 amarok/src/playlistitem.h create mode 100644 amarok/src/playlistloader.cpp create mode 100644 amarok/src/playlistloader.h create mode 100644 amarok/src/playlistselection.cpp create mode 100644 amarok/src/playlistselection.h create mode 100644 amarok/src/playlistwindow.cpp create mode 100644 amarok/src/playlistwindow.h create mode 100644 amarok/src/plugin/Makefile.am create mode 100644 amarok/src/plugin/plugin.cpp create mode 100644 amarok/src/plugin/plugin.h create mode 100644 amarok/src/plugin/pluginconfig.h create mode 100644 amarok/src/pluginmanager.cpp create mode 100644 amarok/src/pluginmanager.h create mode 100644 amarok/src/podcastbundle.h create mode 100644 amarok/src/podcastsettings.cpp create mode 100644 amarok/src/podcastsettings.h create mode 100644 amarok/src/podcastsettingsbase.ui create mode 100644 amarok/src/prettypopupmenu.cpp create mode 100644 amarok/src/prettypopupmenu.h create mode 100644 amarok/src/qstringx.h create mode 100644 amarok/src/queuemanager.cpp create mode 100644 amarok/src/queuemanager.h create mode 100644 amarok/src/refreshimages.cpp create mode 100644 amarok/src/refreshimages.h create mode 100644 amarok/src/scancontroller.cpp create mode 100644 amarok/src/scancontroller.h create mode 100644 amarok/src/scriptmanager.cpp create mode 100644 amarok/src/scriptmanager.h create mode 100644 amarok/src/scriptmanagerbase.ui create mode 100644 amarok/src/scripts/Makefile.am create mode 100644 amarok/src/scripts/ScriptWriting-HOWTO create mode 100644 amarok/src/scripts/alarm/COPYING create mode 100644 amarok/src/scripts/alarm/README create mode 100755 amarok/src/scripts/alarm/alarm.py create mode 100644 amarok/src/scripts/amarok-svn/INFO create mode 100644 amarok/src/scripts/amarok-svn/README create mode 100755 amarok/src/scripts/amarok-svn/amarok-bench.sh create mode 100755 amarok/src/scripts/amarok-svn/amarok-svn.sh create mode 100644 amarok/src/scripts/amarok_live/Makefile.am_ create mode 100644 amarok/src/scripts/amarok_live/README create mode 100644 amarok/src/scripts/amarok_live/amarok.live.remaster.part1.sh create mode 100644 amarok/src/scripts/amarok_live/amarok.live.remaster.part2.sh create mode 100755 amarok/src/scripts/amarok_live/amarok_live.py create mode 100755 amarok/src/scripts/amarok_live/livecd/amarok_live_scan.sh create mode 100644 amarok/src/scripts/amarok_live/standalone/amarok.live.remaster.part1.sh create mode 100644 amarok/src/scripts/amarok_live/standalone/amarok.live.remaster.part2.sh create mode 100644 amarok/src/scripts/common/Makefile.am create mode 100644 amarok/src/scripts/common/Publisher.py create mode 100644 amarok/src/scripts/common/Zeroconf.py create mode 100644 amarok/src/scripts/copy_icons.rb create mode 100644 amarok/src/scripts/databasescripts/README create mode 100644 amarok/src/scripts/databasescripts/TODO create mode 100644 amarok/src/scripts/databasescripts/backupDatabase.rb create mode 100644 amarok/src/scripts/databasescripts/databaseScripts.rb create mode 100644 amarok/src/scripts/databasescripts/redoPodcasts.rb create mode 100755 amarok/src/scripts/databasescripts/staleAlbums.rb create mode 100755 amarok/src/scripts/databasescripts/staleArtists.rb create mode 100755 amarok/src/scripts/databasescripts/staleImages.rb create mode 100755 amarok/src/scripts/databasescripts/staleStatistics.rb create mode 100644 amarok/src/scripts/embedcover/COPYING create mode 100644 amarok/src/scripts/embedcover/README create mode 100644 amarok/src/scripts/embedcover/addimage2mp3.rb create mode 100755 amarok/src/scripts/embedcover/embedcover.rb create mode 100644 amarok/src/scripts/gnome_media_keys/README create mode 100644 amarok/src/scripts/gnome_media_keys/gnome_media_keys.py create mode 100644 amarok/src/scripts/gnome_media_keys/gnome_media_keys.spec create mode 100644 amarok/src/scripts/graphequalizer/Makefile.am create mode 100644 amarok/src/scripts/graphequalizer/README create mode 100644 amarok/src/scripts/graphequalizer/eqdialog.ui create mode 100644 amarok/src/scripts/graphequalizer/eqdialog.ui.h create mode 100644 amarok/src/scripts/graphequalizer/equalizercanvasview.cpp create mode 100644 amarok/src/scripts/graphequalizer/equalizercanvasview.h create mode 100644 amarok/src/scripts/graphequalizer/equalizerdialog.cpp create mode 100644 amarok/src/scripts/graphequalizer/equalizerdialog.h create mode 100644 amarok/src/scripts/graphequalizer/main.cpp create mode 100644 amarok/src/scripts/graphequalizer/stdinreader.h create mode 100644 amarok/src/scripts/lyrics_astraweb/COPYING create mode 100644 amarok/src/scripts/lyrics_astraweb/Makefile.am create mode 100644 amarok/src/scripts/lyrics_astraweb/README create mode 100644 amarok/src/scripts/lyrics_astraweb/THIS_SCRIPT_WAS_INTENTIONALLY_DISABLED create mode 100755 amarok/src/scripts/lyrics_astraweb/lyrics_astraweb.rb create mode 100644 amarok/src/scripts/lyrics_astraweb/lyrics_astraweb.spec create mode 100644 amarok/src/scripts/lyrics_lyrc/COPYING create mode 100644 amarok/src/scripts/lyrics_lyrc/Makefile.am create mode 100644 amarok/src/scripts/lyrics_lyrc/README create mode 100755 amarok/src/scripts/lyrics_lyrc/lyrics_lyrc.rb create mode 100644 amarok/src/scripts/lyrics_lyrc/lyrics_lyrc.spec create mode 100644 amarok/src/scripts/mp3fix/COPYING create mode 100644 amarok/src/scripts/mp3fix/README create mode 100644 amarok/src/scripts/mp3fix/TODO create mode 100644 amarok/src/scripts/mp3fix/mp3fix.rb create mode 100755 amarok/src/scripts/mp3fix/mp3fixer.rb create mode 100755 amarok/src/scripts/nowplaying/amaroknowplaying.rb create mode 100644 amarok/src/scripts/playlist2html/Makefile.am create mode 100644 amarok/src/scripts/playlist2html/Playlist.py create mode 100644 amarok/src/scripts/playlist2html/PlaylistServer.py create mode 100644 amarok/src/scripts/playlist2html/PlaylistServer.spec create mode 100644 amarok/src/scripts/playlist2html/README create mode 100644 amarok/src/scripts/playlist2html/playlist2html.py create mode 100644 amarok/src/scripts/playlist2html/playlist2html.spec create mode 100755 amarok/src/scripts/rbeautify.rb create mode 100644 amarok/src/scripts/ruby_debug/Makefile.am create mode 100755 amarok/src/scripts/ruby_debug/debug.rb create mode 100644 amarok/src/scripts/score2rating/README create mode 100755 amarok/src/scripts/score2rating/score2rating.rb create mode 100644 amarok/src/scripts/score_default/COPYING create mode 100644 amarok/src/scripts/score_default/Makefile.am create mode 100644 amarok/src/scripts/score_default/README create mode 100644 amarok/src/scripts/score_default/score_default.rb create mode 100644 amarok/src/scripts/score_default/score_default.spec create mode 100644 amarok/src/scripts/score_impulsive/COPYING create mode 100644 amarok/src/scripts/score_impulsive/Makefile.am create mode 100644 amarok/src/scripts/score_impulsive/README create mode 100644 amarok/src/scripts/score_impulsive/score_impulsive.rb create mode 100644 amarok/src/scripts/score_impulsive/score_impulsive.spec create mode 100644 amarok/src/scripts/templates/Makefile.am create mode 100644 amarok/src/scripts/templates/amarok.rb create mode 100755 amarok/src/scripts/templates/python_qt_template.py create mode 100644 amarok/src/scripts/templates/ruby_qt_template.rb create mode 100644 amarok/src/scripts/webcontrol/Globals.py create mode 100644 amarok/src/scripts/webcontrol/Makefile.am create mode 100644 amarok/src/scripts/webcontrol/Playlist.py create mode 100644 amarok/src/scripts/webcontrol/README create mode 100644 amarok/src/scripts/webcontrol/RequestHandler.py create mode 100755 amarok/src/scripts/webcontrol/WebControl.py create mode 100644 amarok/src/scripts/webcontrol/WebControl.spec create mode 100644 amarok/src/scripts/webcontrol/WebPublisher.py create mode 100644 amarok/src/scripts/webcontrol/amarok_cut.png create mode 100644 amarok/src/scripts/webcontrol/controlbackground.png create mode 100644 amarok/src/scripts/webcontrol/main.css create mode 100644 amarok/src/scripts/webcontrol/main.js create mode 100644 amarok/src/scripts/webcontrol/player_end.png create mode 100644 amarok/src/scripts/webcontrol/player_pause.png create mode 100644 amarok/src/scripts/webcontrol/player_play.png create mode 100644 amarok/src/scripts/webcontrol/player_start.png create mode 100644 amarok/src/scripts/webcontrol/player_stop.png create mode 100644 amarok/src/scripts/webcontrol/smallstar.png create mode 100644 amarok/src/scripts/webcontrol/star.png create mode 100644 amarok/src/scripts/webcontrol/template.thtml create mode 100644 amarok/src/scripts/webcontrol/vol_speaker.png create mode 100644 amarok/src/scrobbler.cpp create mode 100644 amarok/src/scrobbler.h create mode 100644 amarok/src/sliderwidget.cpp create mode 100644 amarok/src/sliderwidget.h create mode 100644 amarok/src/smartplaylisteditor.cpp create mode 100644 amarok/src/smartplaylisteditor.h create mode 100644 amarok/src/socketserver.cpp create mode 100644 amarok/src/socketserver.h create mode 100644 amarok/src/sqlite/Makefile.am create mode 100644 amarok/src/sqlite/sqlite3.c create mode 100644 amarok/src/sqlite/sqlite3.h create mode 100644 amarok/src/starmanager.cpp create mode 100644 amarok/src/starmanager.h create mode 100644 amarok/src/statistics.cpp create mode 100644 amarok/src/statistics.h create mode 100644 amarok/src/statusbar/Makefile.am create mode 100644 amarok/src/statusbar/overlayWidget.cpp create mode 100644 amarok/src/statusbar/overlayWidget.h create mode 100644 amarok/src/statusbar/popupMessage.cpp create mode 100644 amarok/src/statusbar/popupMessage.h create mode 100644 amarok/src/statusbar/progressBar.cpp create mode 100644 amarok/src/statusbar/progressBar.h create mode 100644 amarok/src/statusbar/queueLabel.cpp create mode 100644 amarok/src/statusbar/queueLabel.h create mode 100644 amarok/src/statusbar/selectLabel.h create mode 100644 amarok/src/statusbar/squeezedtextlabel.cpp create mode 100644 amarok/src/statusbar/squeezedtextlabel.h create mode 100644 amarok/src/statusbar/statusBarBase.cpp create mode 100644 amarok/src/statusbar/statusBarBase.h create mode 100644 amarok/src/statusbar/statusbar.cpp create mode 100644 amarok/src/statusbar/statusbar.h create mode 100644 amarok/src/statusbar/timeLabel.h create mode 100644 amarok/src/statusbar/toggleLabel.h create mode 100644 amarok/src/systray.cpp create mode 100644 amarok/src/systray.h create mode 100644 amarok/src/tagdialog.cpp create mode 100644 amarok/src/tagdialog.h create mode 100644 amarok/src/tagdialogbase.ui create mode 100644 amarok/src/tagdialogbase.ui.h create mode 100644 amarok/src/tagguesser.cpp create mode 100644 amarok/src/tagguesser.h create mode 100644 amarok/src/tagguesserconfigdialog.ui create mode 100644 amarok/src/tagguesserconfigdialog.ui.h create mode 100644 amarok/src/tdebug.h create mode 100644 amarok/src/themes/Makefile.am create mode 100644 amarok/src/themes/example/Makefile.am create mode 100644 amarok/src/themes/example/stylesheet.css create mode 100644 amarok/src/themes/reinhardt/Makefile.am create mode 100644 amarok/src/themes/reinhardt/images/Makefile.am create mode 100644 amarok/src/themes/reinhardt/images/background.png create mode 100644 amarok/src/themes/reinhardt/images/transparency.png create mode 100644 amarok/src/themes/reinhardt/stylesheet.css create mode 100644 amarok/src/threadmanager.cpp create mode 100644 amarok/src/threadmanager.h create mode 100644 amarok/src/tooltip.cpp create mode 100644 amarok/src/tooltip.h create mode 100644 amarok/src/trackpickerdialog.cpp create mode 100644 amarok/src/trackpickerdialog.h create mode 100644 amarok/src/trackpickerdialogbase.ui create mode 100644 amarok/src/tracktooltip.cpp create mode 100644 amarok/src/tracktooltip.h create mode 100644 amarok/src/transferdialog.cpp create mode 100644 amarok/src/transferdialog.h create mode 100644 amarok/src/vis/Makefile.am create mode 100644 amarok/src/vis/libvisual/Makefile.am create mode 100644 amarok/src/vis/libvisual/libvisual.cpp create mode 100644 amarok/src/vis/libvisual/libvisual.h create mode 100644 amarok/src/xmlloader.cpp create mode 100644 amarok/src/xmlloader.h create mode 100644 amarok/src/xmlloader_p.h create mode 100644 amarok/src/xspfplaylist.cpp create mode 100644 amarok/src/xspfplaylist.h create mode 100644 config.h.in create mode 100644 configure.files create mode 100644 configure.in create mode 100644 configure.in.bot create mode 100644 configure.in.in create mode 100644 doc/Makefile.am create mode 100644 doc/amarok/Makefile.am create mode 100644 doc/amarok/add_dynamic.png create mode 100644 doc/amarok/advanced.docbook create mode 100644 doc/amarok/amarok_playlist.png create mode 100644 doc/amarok/analyzer.png create mode 100644 doc/amarok/buttons.png create mode 100644 doc/amarok/collection.png create mode 100644 doc/amarok/config.docbook create mode 100644 doc/amarok/config_appearance.png create mode 100644 doc/amarok/config_collection.png create mode 100644 doc/amarok/config_engine.png create mode 100644 doc/amarok/config_general.png create mode 100644 doc/amarok/config_osd.png create mode 100644 doc/amarok/config_playback.png create mode 100644 doc/amarok/config_scrobbler.png create mode 100644 doc/amarok/coverman.png create mode 100644 doc/amarok/dynamic_bar.png create mode 100644 doc/amarok/dynamic_settings.png create mode 100644 doc/amarok/equalizer.png create mode 100644 doc/amarok/faq.docbook create mode 100644 doc/amarok/file_browser.png create mode 100644 doc/amarok/index.docbook create mode 100644 doc/amarok/logo.png create mode 100644 doc/amarok/media_device.png create mode 100644 doc/amarok/menubar.png create mode 100644 doc/amarok/musicbrainz.png create mode 100644 doc/amarok/pl_tip1.png create mode 100644 doc/amarok/play_list.png create mode 100644 doc/amarok/player_window.png create mode 100644 doc/amarok/playlist_browser.png create mode 100644 doc/amarok/playlist_window.png create mode 100644 doc/amarok/queue_manager.png create mode 100644 doc/amarok/quick.docbook create mode 100644 doc/amarok/requirements.docbook create mode 100644 doc/amarok/rmb_menu.png create mode 100644 doc/amarok/script_manager.png create mode 100644 doc/amarok/status_bar.png create mode 100644 doc/amarok/tab_lyrics.png create mode 100644 doc/amarok/tab_music.png create mode 100644 doc/amarok/tab_wiki.png create mode 100644 doc/amarok/using.docbook create mode 100644 doc/amarok/vis_window.png create mode 100644 doc/da/Makefile.am create mode 100644 doc/da/advanced.docbook create mode 100644 doc/da/config.docbook create mode 100644 doc/da/faq.docbook create mode 100644 doc/da/index.docbook create mode 100644 doc/da/quick.docbook create mode 100644 doc/da/requirements.docbook create mode 100644 doc/da/using.docbook create mode 100644 doc/de/Makefile.am create mode 100644 doc/de/add_dynamic.png create mode 100644 doc/de/advanced.docbook create mode 100644 doc/de/amarok_playlist.png create mode 100644 doc/de/analyzer.png create mode 100644 doc/de/buttons.png create mode 100644 doc/de/collection.png create mode 100644 doc/de/config.docbook create mode 100644 doc/de/config_appearance.png create mode 100644 doc/de/config_collection.png create mode 100644 doc/de/config_engine.png create mode 100644 doc/de/config_general.png create mode 100644 doc/de/config_osd.png create mode 100644 doc/de/config_playback.png create mode 100644 doc/de/config_scrobbler.png create mode 100644 doc/de/context.png create mode 100644 doc/de/coverman.png create mode 100644 doc/de/dynamic_bar.png create mode 100644 doc/de/dynamic_settings.png create mode 100644 doc/de/equalizer.png create mode 100644 doc/de/faq.docbook create mode 100644 doc/de/file_browser.png create mode 100644 doc/de/index.docbook create mode 100644 doc/de/logo.png create mode 100644 doc/de/media_device.png create mode 100644 doc/de/menubar.png create mode 100644 doc/de/musicbrainz.png create mode 100644 doc/de/pl_browser.png create mode 100644 doc/de/pl_tip1.png create mode 100644 doc/de/play_list.png create mode 100644 doc/de/player_window.png create mode 100644 doc/de/playlist_browser.png create mode 100644 doc/de/playlist_window.png create mode 100644 doc/de/queue_manager.png create mode 100644 doc/de/quick.docbook create mode 100644 doc/de/requirements.docbook create mode 100644 doc/de/rmb_menu.png create mode 100644 doc/de/script_manager.png create mode 100644 doc/de/status_bar.png create mode 100644 doc/de/using.docbook create mode 100644 doc/de/vis_window.png create mode 100644 doc/es/Makefile.am create mode 100644 doc/es/add_dynamic.png create mode 100644 doc/es/advanced.docbook create mode 100644 doc/es/amarok_playlist.png create mode 100644 doc/es/analyzer.png create mode 100644 doc/es/browser_choice.png create mode 100644 doc/es/buttons.png create mode 100644 doc/es/collection.png create mode 100644 doc/es/config.docbook create mode 100644 doc/es/config_appearance.png create mode 100644 doc/es/config_collection.png create mode 100644 doc/es/config_engine.png create mode 100644 doc/es/config_general.png create mode 100644 doc/es/config_osd.png create mode 100644 doc/es/config_playback.png create mode 100644 doc/es/config_scrobbler.png create mode 100644 doc/es/context.png create mode 100644 doc/es/coverman.png create mode 100644 doc/es/dynamic_bar.png create mode 100644 doc/es/dynamic_mode1.png create mode 100644 doc/es/dynamic_mode2.png create mode 100644 doc/es/dynamic_settings.png create mode 100644 doc/es/equalizer.png create mode 100644 doc/es/faq.docbook create mode 100644 doc/es/file_browser.png create mode 100644 doc/es/index.docbook create mode 100644 doc/es/logo.png create mode 100644 doc/es/media_device.png create mode 100644 doc/es/menubar.png create mode 100644 doc/es/musicbrainz.png create mode 100644 doc/es/pl_browser.png create mode 100644 doc/es/pl_tip1.png create mode 100644 doc/es/play_list.png create mode 100644 doc/es/player_window.png create mode 100644 doc/es/playlist_browser.png create mode 100644 doc/es/playlist_window.png create mode 100644 doc/es/queue_manager.png create mode 100644 doc/es/quick.docbook create mode 100644 doc/es/requirements.docbook create mode 100644 doc/es/rmb_menu.png create mode 100644 doc/es/script_manager.png create mode 100644 doc/es/status_bar.png create mode 100644 doc/es/tab_lyrics.png create mode 100644 doc/es/tab_music.png create mode 100644 doc/es/tab_wiki.png create mode 100644 doc/es/using.docbook create mode 100644 doc/es/vis_window.png create mode 100644 doc/et/Makefile.am create mode 100644 doc/et/advanced.docbook create mode 100644 doc/et/config.docbook create mode 100644 doc/et/develop.docbook create mode 100644 doc/et/faq.docbook create mode 100644 doc/et/feature_guide.docbook create mode 100644 doc/et/hidden.docbook create mode 100644 doc/et/howto.docbook create mode 100644 doc/et/index.docbook create mode 100644 doc/et/plugin.docbook create mode 100644 doc/et/quick.docbook create mode 100644 doc/et/requirements.docbook create mode 100644 doc/et/using.docbook create mode 100644 doc/fr/Makefile.am create mode 100644 doc/fr/advanced.docbook create mode 100644 doc/fr/config.docbook create mode 100644 doc/fr/faq.docbook create mode 100644 doc/fr/feature_guide.docbook create mode 100644 doc/fr/index.docbook create mode 100644 doc/fr/quick.docbook create mode 100644 doc/fr/requirements.docbook create mode 100644 doc/fr/using.docbook create mode 100644 doc/it/Makefile.am create mode 100644 doc/it/add_dynamic.png create mode 100644 doc/it/advanced.docbook create mode 100644 doc/it/amarok_playlist.png create mode 100644 doc/it/analyzer.png create mode 100644 doc/it/buttons.png create mode 100644 doc/it/collection.png create mode 100644 doc/it/completo.png create mode 100644 doc/it/config.docbook create mode 100644 doc/it/config_appearance.png create mode 100644 doc/it/config_collection.png create mode 100644 doc/it/config_engine.png create mode 100644 doc/it/config_general.png create mode 100644 doc/it/config_osd.png create mode 100644 doc/it/config_playback.png create mode 100644 doc/it/config_scrobbler.png create mode 100644 doc/it/context.png create mode 100644 doc/it/coverman.png create mode 100644 doc/it/develop.docbook create mode 100644 doc/it/dynamic_bar.png create mode 100644 doc/it/dynamic_settings.png create mode 100644 doc/it/equalizer.png create mode 100644 doc/it/faq.docbook create mode 100644 doc/it/feature_guide.docbook create mode 100644 doc/it/file_browser.png create mode 100644 doc/it/hidden.docbook create mode 100644 doc/it/index.docbook create mode 100644 doc/it/media_device.png create mode 100644 doc/it/menubar.png create mode 100644 doc/it/pl_browser.png create mode 100644 doc/it/pl_tip1.png create mode 100644 doc/it/play_list.png create mode 100644 doc/it/player_window.png create mode 100644 doc/it/playlist_browser.png create mode 100644 doc/it/playlist_window.png create mode 100644 doc/it/plugin.docbook create mode 100644 doc/it/queue_manager.png create mode 100644 doc/it/quick.docbook create mode 100644 doc/it/requirements.docbook create mode 100644 doc/it/rmb_menu.png create mode 100644 doc/it/script_manager.png create mode 100644 doc/it/status_bar.png create mode 100644 doc/it/tab_lyrics.png create mode 100644 doc/it/tab_music.png create mode 100644 doc/it/tab_wiki.png create mode 100644 doc/it/using.docbook create mode 100644 doc/it/vis_window.png create mode 100644 doc/nl/Makefile.am create mode 100644 doc/nl/add_dynamic.png create mode 100644 doc/nl/advanced.docbook create mode 100644 doc/nl/amarok_playlist.png create mode 100644 doc/nl/analyzer.png create mode 100644 doc/nl/buttons.png create mode 100644 doc/nl/collection.png create mode 100644 doc/nl/config.docbook create mode 100644 doc/nl/config_appearance.png create mode 100644 doc/nl/config_collection.png create mode 100644 doc/nl/config_engine.png create mode 100644 doc/nl/config_general.png create mode 100644 doc/nl/config_osd.png create mode 100644 doc/nl/config_playback.png create mode 100644 doc/nl/config_scrobbler.png create mode 100644 doc/nl/context.png create mode 100644 doc/nl/coverman.png create mode 100644 doc/nl/develop.docbook create mode 100644 doc/nl/dynamic_bar.png create mode 100644 doc/nl/dynamic_settings.png create mode 100644 doc/nl/equalizer.png create mode 100644 doc/nl/faq.docbook create mode 100644 doc/nl/feature_guide.docbook create mode 100644 doc/nl/file_browser.png create mode 100644 doc/nl/hidden.docbook create mode 100644 doc/nl/index.docbook create mode 100644 doc/nl/logo.png create mode 100644 doc/nl/media_device.png create mode 100644 doc/nl/menubar.png create mode 100644 doc/nl/musicbrainz.png create mode 100644 doc/nl/pl_browser.png create mode 100644 doc/nl/pl_tip1.png create mode 100644 doc/nl/play_list.png create mode 100644 doc/nl/player_window.png create mode 100644 doc/nl/playlist_browser.png create mode 100644 doc/nl/playlist_window.png create mode 100644 doc/nl/plugin.docbook create mode 100644 doc/nl/queue_manager.png create mode 100644 doc/nl/quick.docbook create mode 100644 doc/nl/requirements.docbook create mode 100644 doc/nl/rmb_menu.png create mode 100644 doc/nl/script_manager.png create mode 100644 doc/nl/status_bar.png create mode 100644 doc/nl/tab_lyrics.png create mode 100644 doc/nl/tab_lyrics1.png create mode 100644 doc/nl/tab_music.png create mode 100644 doc/nl/tab_wiki.png create mode 100644 doc/nl/using.docbook create mode 100644 doc/nl/vis_window.png create mode 100644 doc/pl/Makefile.am create mode 100644 doc/pl/advanced.docbook create mode 100644 doc/pl/config.docbook create mode 100644 doc/pl/faq.docbook create mode 100644 doc/pl/index.docbook create mode 100644 doc/pl/quick.docbook create mode 100644 doc/pl/requirements.docbook create mode 100644 doc/pl/using.docbook create mode 100644 doc/pt/Makefile.am create mode 100644 doc/pt/add_dynamic.png create mode 100644 doc/pt/advanced.docbook create mode 100644 doc/pt/amarok_playlist.png create mode 100644 doc/pt/analyzer.png create mode 100644 doc/pt/buttons.png create mode 100644 doc/pt/collection.png create mode 100644 doc/pt/config.docbook create mode 100644 doc/pt/config_appearance.png create mode 100644 doc/pt/config_collection.png create mode 100644 doc/pt/config_engine.png create mode 100644 doc/pt/config_general.png create mode 100644 doc/pt/config_osd.png create mode 100644 doc/pt/config_playback.png create mode 100644 doc/pt/config_scrobbler.png create mode 100644 doc/pt/coverman.png create mode 100644 doc/pt/develop.docbook create mode 100644 doc/pt/dynamic_bar.png create mode 100644 doc/pt/dynamic_settings.png create mode 100644 doc/pt/equalizer.png create mode 100644 doc/pt/faq.docbook create mode 100644 doc/pt/feature_guide.docbook create mode 100644 doc/pt/file_browser.png create mode 100644 doc/pt/hidden.docbook create mode 100644 doc/pt/index.docbook create mode 100644 doc/pt/logo.png create mode 100644 doc/pt/media_device.png create mode 100644 doc/pt/menubar.png create mode 100644 doc/pt/musicbrainz.png create mode 100644 doc/pt/pl_tip1.png create mode 100644 doc/pt/play_list.png create mode 100644 doc/pt/player_window.png create mode 100644 doc/pt/playlist_browser.png create mode 100644 doc/pt/playlist_window.png create mode 100644 doc/pt/plugin.docbook create mode 100644 doc/pt/queue_manager.png create mode 100644 doc/pt/quick.docbook create mode 100644 doc/pt/requirements.docbook create mode 100644 doc/pt/rmb_menu.png create mode 100644 doc/pt/script_manager.png create mode 100644 doc/pt/status_bar.png create mode 100644 doc/pt/using.docbook create mode 100644 doc/pt/vis_window.png create mode 100644 doc/pt_BR/Makefile.am create mode 100644 doc/pt_BR/advanced.docbook create mode 100644 doc/pt_BR/config.docbook create mode 100644 doc/pt_BR/faq.docbook create mode 100644 doc/pt_BR/feature_guide.docbook create mode 100644 doc/pt_BR/index.docbook create mode 100644 doc/pt_BR/quick.docbook create mode 100644 doc/pt_BR/requirements.docbook create mode 100644 doc/pt_BR/using.docbook create mode 100644 doc/ru/Makefile.am create mode 100644 doc/ru/requirements.docbook create mode 100644 doc/sv/Makefile.am create mode 100644 doc/sv/add_dynamic.png create mode 100644 doc/sv/advanced.docbook create mode 100644 doc/sv/amarok_playlist.png create mode 100644 doc/sv/analyzer.png create mode 100644 doc/sv/browser_choice.png create mode 100644 doc/sv/buttons.png create mode 100644 doc/sv/collection.png create mode 100644 doc/sv/config.docbook create mode 100644 doc/sv/config_appearance.png create mode 100644 doc/sv/config_collection.png create mode 100644 doc/sv/config_colors.png create mode 100644 doc/sv/config_engine.png create mode 100644 doc/sv/config_fonts.png create mode 100644 doc/sv/config_general.png create mode 100644 doc/sv/config_mysql.png create mode 100644 doc/sv/config_osd.png create mode 100644 doc/sv/config_playback.png create mode 100644 doc/sv/config_scrobbler.png create mode 100644 doc/sv/context.png create mode 100644 doc/sv/coverman.png create mode 100644 doc/sv/develop.docbook create mode 100644 doc/sv/dynamic_bar.png create mode 100644 doc/sv/dynamic_settings.png create mode 100644 doc/sv/equalizer.png create mode 100644 doc/sv/faq.docbook create mode 100644 doc/sv/feature_guide.docbook create mode 100644 doc/sv/file_browser.png create mode 100644 doc/sv/hidden.docbook create mode 100644 doc/sv/index.docbook create mode 100644 doc/sv/media_device.png create mode 100644 doc/sv/menubar.png create mode 100644 doc/sv/pl_browser.png create mode 100644 doc/sv/pl_tip1.png create mode 100644 doc/sv/play_list.png create mode 100644 doc/sv/player_window.png create mode 100644 doc/sv/playlist_browser.png create mode 100644 doc/sv/playlist_window.png create mode 100644 doc/sv/plugin.docbook create mode 100644 doc/sv/queue_manager.png create mode 100644 doc/sv/quick.docbook create mode 100644 doc/sv/requirements.docbook create mode 100644 doc/sv/rmb_menu.png create mode 100644 doc/sv/script_manager.png create mode 100644 doc/sv/scripts_window.png create mode 100644 doc/sv/search.png create mode 100644 doc/sv/status_bar.png create mode 100644 doc/sv/streams.png create mode 100644 doc/sv/using.docbook create mode 100644 doc/sv/vis_window.png create mode 100644 doc/sv/welcome.png create mode 100644 po/Makefile.am create mode 100644 po/af/Makefile.am create mode 100644 po/af/amarok.po create mode 100644 po/ar/Makefile.am create mode 100644 po/ar/amarok.po create mode 100644 po/az/Makefile.am create mode 100644 po/az/amarok.po create mode 100644 po/be/Makefile.am create mode 100644 po/be/amarok.po create mode 100644 po/bg/Makefile.am create mode 100644 po/bg/amarok.po create mode 100644 po/bn/Makefile.am create mode 100644 po/bn/amarok.po create mode 100644 po/br/Makefile.am create mode 100644 po/br/amarok.po create mode 100644 po/ca/Makefile.am create mode 100644 po/ca/amarok.po create mode 100644 po/cs/Makefile.am create mode 100644 po/cs/amarok.po create mode 100644 po/cy/Makefile.am create mode 100644 po/cy/amarok.po create mode 100644 po/da/Makefile.am create mode 100644 po/da/amarok.po create mode 100644 po/de/Makefile.am create mode 100644 po/de/amarok.po create mode 100644 po/el/Makefile.am create mode 100644 po/el/amarok.po create mode 100644 po/en_GB/Makefile.am create mode 100644 po/en_GB/amarok.po create mode 100644 po/eo/Makefile.am create mode 100644 po/eo/amarok.po create mode 100644 po/es/Makefile.am create mode 100644 po/es/amarok.po create mode 100644 po/et/Makefile.am create mode 100644 po/et/amarok.po create mode 100644 po/eu/Makefile.am create mode 100644 po/eu/amarok.po create mode 100644 po/fa/Makefile.am create mode 100644 po/fa/amarok.po create mode 100644 po/fi/Makefile.am create mode 100644 po/fi/amarok.po create mode 100644 po/fr/Makefile.am create mode 100644 po/fr/amarok.po create mode 100644 po/ga/Makefile.am create mode 100644 po/ga/amarok.po create mode 100644 po/gl/Makefile.am create mode 100644 po/gl/amarok.po create mode 100644 po/he/Makefile.am create mode 100644 po/he/amarok.po create mode 100644 po/hi/Makefile.am create mode 100644 po/hi/amarok.po create mode 100644 po/hu/Makefile.am create mode 100644 po/hu/amarok.po create mode 100644 po/id/Makefile.am create mode 100644 po/id/amarok.po create mode 100644 po/is/Makefile.am create mode 100644 po/is/amarok.po create mode 100644 po/it/Makefile.am create mode 100644 po/it/amarok.po create mode 100644 po/ja/Makefile.am create mode 100644 po/ja/amarok.po create mode 100644 po/ka/Makefile.am create mode 100644 po/ka/amarok.po create mode 100644 po/km/Makefile.am create mode 100644 po/km/amarok.po create mode 100644 po/ko/Makefile.am create mode 100644 po/ko/amarok.po create mode 100644 po/ku/Makefile.am create mode 100644 po/ku/amarok.po create mode 100644 po/lo/Makefile.am create mode 100644 po/lo/amarok.po create mode 100644 po/lt/Makefile.am create mode 100644 po/lt/amarok.po create mode 100644 po/mk/Makefile.am create mode 100644 po/mk/amarok.po create mode 100644 po/ms/Makefile.am create mode 100644 po/ms/amarok.po create mode 100644 po/nb/Makefile.am create mode 100644 po/nb/amarok.po create mode 100644 po/nds/Makefile.am create mode 100644 po/nds/amarok.po create mode 100644 po/ne/Makefile.am create mode 100644 po/ne/amarok.po create mode 100644 po/nl/Makefile.am create mode 100644 po/nl/amarok.po create mode 100644 po/nn/Makefile.am create mode 100644 po/nn/amarok.po create mode 100644 po/pa/Makefile.am create mode 100644 po/pa/amarok.po create mode 100644 po/pl/Makefile.am create mode 100644 po/pl/amarok.po create mode 100644 po/pt/Makefile.am create mode 100644 po/pt/amarok.po create mode 100644 po/pt_BR/Makefile.am create mode 100644 po/pt_BR/amarok.po create mode 100644 po/ro/Makefile.am create mode 100644 po/ro/amarok.po create mode 100644 po/ru/Makefile.am create mode 100644 po/ru/amarok.po create mode 100644 po/rw/Makefile.am create mode 100644 po/rw/amarok.po create mode 100644 po/se/Makefile.am create mode 100644 po/se/amarok.po create mode 100644 po/sk/Makefile.am create mode 100644 po/sk/amarok.po create mode 100644 po/sl/Makefile.am create mode 100644 po/sl/amarok.po create mode 100644 po/sq/Makefile.am create mode 100644 po/sq/amarok.po create mode 100644 po/sr/Makefile.am create mode 100644 po/sr/amarok.po create mode 100644 po/sr@Latn/Makefile.am create mode 100644 po/sr@Latn/amarok.po create mode 100644 po/ss/Makefile.am create mode 100644 po/ss/amarok.po create mode 100644 po/sv/Makefile.am create mode 100644 po/sv/amarok.po create mode 100644 po/ta/Makefile.am create mode 100644 po/ta/amarok.po create mode 100644 po/tg/Makefile.am create mode 100644 po/tg/amarok.po create mode 100644 po/th/Makefile.am create mode 100644 po/th/amarok.po create mode 100644 po/tr/Makefile.am create mode 100644 po/tr/amarok.po create mode 100644 po/uk/Makefile.am create mode 100644 po/uk/amarok.po create mode 100644 po/uz/Makefile.am create mode 100644 po/uz/amarok.po create mode 100644 po/uz@cyrillic/Makefile.am create mode 100644 po/uz@cyrillic/amarok.po create mode 100644 po/zh_CN/Makefile.am create mode 100644 po/zh_CN/amarok.po create mode 100644 po/zh_TW/Makefile.am create mode 100644 po/zh_TW/amarok.po create mode 100644 subdirs diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 00000000..6a040b39 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,15 @@ +Alexandre Oliveira +Christian Muehlhaeuser +Frederik Holljen +Gábor Lehel +Ian Monroe +Jeff Mitchell +Mark Kretschmann +Martin Aumueller +Max Howell +Mike Diehl +Paul Cifarelli +Pierpaolo Di Panfilo +Roman Becker +Seb Ruiz +Stanislav Karchebny diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000..432227e4 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,51 @@ +project(extragear-multimedia) + +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}/cmake/modules ) + +# search packages used by KDE +find_package(KDE4 REQUIRED) +include (KDE4Defaults) +include (MacroLibrary) +include(MacroOptionalAddSubdirectory) +find_package(RUBY) +find_package(KdeMultimedia) +find_package(OpenGL) +find_package(Xine) +# are these two really required ? +if (APPLE) + find_package(Carbon REQUIRED) +endif (APPLE) +set(TAGLIB_MIN_VERSION "1.5") +find_package(Taglib) + +#amarok needs to be before add_definitions, since it builds +#some qt-only software +if(TAGLIB_FOUND AND RUBY_EXECUTABLE AND RUBY_INCLUDE_PATH) + macro_optional_add_subdirectory(amarok) +endif(TAGLIB_FOUND AND RUBY_EXECUTABLE AND RUBY_INCLUDE_PATH) + + +add_definitions (${QT_DEFINITIONS} ${KDE4_DEFINITIONS}) + +include_directories (${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR} ${KDE4_INCLUDES}) + +include (ConfigureChecks.cmake) + +if(TAGLIB_FOUND) +include_directories (${TAGLIB_INCLUDES}) +endif(TAGLIB_FOUND) + +if(KDEMULTIMEDIA_FOUND) + macro_optional_add_subdirectory(k3b) + macro_optional_add_subdirectory(kaudiocreator) +endif(KDEMULTIMEDIA_FOUND) +macro_optional_add_subdirectory(kaffeine) +macro_optional_add_subdirectory(kmid) +macro_optional_add_subdirectory(kplayer) +macro_optional_add_subdirectory(kmplayer) +macro_optional_add_subdirectory(doc) + +if(QT_QTOPENGL_FOUND AND OPENGL_FOUND AND XINE_FOUND) + macro_optional_add_subdirectory(kaffeinegl) +endif(QT_QTOPENGL_FOUND AND OPENGL_FOUND AND XINE_FOUND) + diff --git a/COPYING b/COPYING new file mode 100644 index 00000000..6822c1a4 --- /dev/null +++ b/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 00000000..97c97e4e --- /dev/null +++ b/ChangeLog @@ -0,0 +1,2876 @@ +Amarok ChangeLog +================ +(C) 2002-2007 the Amarok authors. + +VERSION 1.4.10 + BUGFIX: + * Fix vulnerability in the Magnatune database parsing code. Secunia + Advisory #SA31418. Thanks to Google Alerts for notifying us about this + vulnerability. + +VERSION 1.4.9 + BUGFIXES: + * The last.fm dialog did not always properly disable options when the + username was not entered. + * Fix Amazon Cover fetching by using their new web service api. + * Don't insert items into Dynamic Mode that don't exist. + * If unavailable tracks are in the Playlist and random mode is on, don't + stop those tracks if selected; continue with available tracks. + + +VERSION 1.4.8 + CHANGES: + * Optimise some database queries with the mysql backend. Patch by Alf + Eaton (BR 152749). + * Don't show the device plugin dialog when a new device is plugged in. + Apparently it's not obvious that you have to hit OK after selecting "Do + not handle" if you don't want it handled, so disabling it prevents it + from being shown repeatedly. + * Better support for iPhone/iPod Touch mounted via fuse/sshfs (libgpod 0.6.0 + or newer required). + * Only re-render the context view when visible if changing ratings, scores + or labels for songs. + + BUGFIXES: + * Last.fm metadata would not update with xine 1.1.8. (BR 150429) + * Amarok would forget podcast channel and episode settings when using the + postgresql backend. + * When adding file types with the Generic Media Device sometimes the + extensions would be prepended with & and would not save. (BR 151806) + * For improved compatibility with newer iPods, convert file extensions to + lower case during transfer. + * Replace slashes in artist name with spaces when querying Wikipedia + e.g. AC/DC, To/Die/For. (BR 150001) + * Always rebuild the dynamic mode cache when in Suggested songs mode, + so that we don't land up with stale suggestions. Patch by Jer Johnson + + * Sort albums made in the same year alphabetically in 'ascending + order'. (BR 149408) + * Statistics tool shouldn't show samplers in 'favorite albums'. + * Duplicate songs were not allowed in playlist when adding from the + collection browser. (BR 149643) + * Make sure the localUrl of a PodcastEpisode is valid after a failed + download. (BR 147351) + * Fix off-by-one error causing Smart Playlists to not load tracks with a + rating >= 4.5. (BR 148916) + * Don't enable "Configure Podcasts" at the top-level Podcasts folder if + there is nothing beneath it. (BR 146504) + * Generic Media Device could copy some non-ASCII filenames to turn to + gibberish. Thanks to David Smith for the fix. + * Fixed possible GUI freeze when Amarok was showing the dialog for + installing mp3 support. Patch by Sascha Sommer . + (BR 147126) + * Amarok could needlessely reinitialize connections to MySQL databases + after a configuration change. Combined with a bug in MySQL libraries, + this could lead to a crash. + * Pressing Preveious Track in a Dynamic Playlist could cause undefined + behavior in certain edge conditions. Now it always plays the current + track. (BR 148317) + * Immediately after loading a dynamic playlist, you couldn't drag a + track to the top of the playlist. (BR 149263) + * Fix transferring files with UTF8 names to MTP devices. Thanks to Kevin + Becker for the fix. (BR 139722) + * Display warning that iPod sysinfo could not be written in the case of + incorrect file permissions. Patch by Christian Ober-Blöbaum + . (BR 148607) + * Fix Czech character conversion to ASCII for Generic Media Device. Patch + by Matěj Laitl . (BR 149125) + + +VERSION 1.4.7 + CHANGES: + * Updated the Cool Streams. + * Improved application icon. Thanks go to Pasi Lallinaho. + * Upgraded SQLite to 3.4.1 + * SQL improvements providing optimisations on intensive queries. Patch by + Gosta . (BR 142999) + + BUGFIXES: + * Wikipedia artist lookup would freeze Amarok if the artist was not found + and the locale was not English. (BR 142764) + * Cannot limit smart playlists to more than 1000 tracks. (BR 148084) + * Fixed the formatting in the "Extended Info" pane for podcasts. + * Don't show "Not Rated" for items rated with half a star. Patch by Tuomas + Nurmi . (BR 144675) + * Copy, don't move items from Cool Streams to folders. (BR 147404) + * Sometimes folders in the playlistbrowser could be lost. (BR 147404) + * NJB devices could have tags corrupted that contained Unicode characters. + Patch by Kun Xi . (BR 147223) + * Show OSD when changing song rating via shortcut. Patch by Tuomas Nurmi + . (BR 146918) + * Show the stars indicating rating with the correct size in the OSD. Patch + Patch by Tuomas Nurmi . (BR 147059) + + +VERSION 1.4.6 + CHANGES: + * Improved icon theme, kindly provided by Landy DeField + . Big thanks! + * Playlist now sends notifications to scripts if items are added, removed, + reordered, or if the playlist is cleared. Useful for script authors. + Thanks to Miguel Angel Alvarez for the patch. + * iPod device plugin now handles RockBox devices. Thanks to Michael + Buesch for the patch. + * Organising files will only delete empty parent folders if the folder + is within the collection hierarchy. (BR 136757) + * The default cover image preview size has been increased to 130px. + * The "hide menubar" option has been removed. It's too dangerous and led + to countless support requests. + * Generic media device can now handle any KIO-compatible URL, including + obex and smb. Manage your bluetooth phone's music collection through + Amarok! + * Upgraded SQLite to 3.3.17. + * Append an album to the playlist by right-clicking on it from within + the Cover Manager. Patch by Doug Reich . + * Faster playlist handling. Patch by Ovy . (BR 142255) + * The moodbar process has been given a higher priority. (BR 136867) + * Allow for lyrics scripts to specifiy site, site_url, and add_url from + within the script. This will allow for "meta lyrics" scripts. Patch by + Sergio Pistone . (BR 141885) + * First rating star now lets you toggle between no rating, half a star, + and one full star. + + BUGFIXES: + * Uninstalling scripts would in some cases leave files behind. Patch by + Sergio Pistone . (BR 143716) + * Last.fm "Custom Station" stream works again. (BR 146020) + * Fix regression where the "Show Script Manager" button displayed on the + Lyrics tab of the Context Browser wouldn't actually show the Script + Manager. + * Don't show ratings from the previous track's rating change in the OSD on + playing the next track. + * The config dialog is now less tall and fits on widescreen displays. + * Making a dynamic playlist with the number of previously played tracks to + show set to zero and attempting to play the first track would cause a + crash. (BR 145157) + * If "Stop after current track" was used, the last track would not be + counted or rated in the user's statistics. (BR 140980) + * Generic media device wouldn't allow you to drop a folder on the + viewport, meaning you couldn't move subfolders to the top level of the + mount point. + * Made the settings dialog less tall. (BR 141250) + * Star ratings now update instantly in the Context Browser, OSD, and + Collection Browser. + * lyrc script did not work behind proxy due to a stray quote mark. Gentoo + Bug 166050. + * Fix compilation on kde-3.3 systems. + * amarok_live.py now uses popen correctly. Patch by Luke Macken + . (BR 127804) + * Make amarok_proxy.rb use HTTP/1.0 as we don't support chunked responses. + Patch by solsTiCE . (BR 141819) + * Fix Quadratic loading in Playlists. Patch by Ovy + . (BR 142255) + * Correctly set iPod model. Patch by İsmail Dönmez . + * Fix detection of vfat devices on FreeBSD. (BR 141614) + * Right-click on volume slider would change the volume. (BR 141672) + + +VERSION 1.4.5 + FEATURES: + * Added support for custom song labels. Labels can be managed + through the GUI or using new DCOP functions. (BR 89314) + * New DCOP functions to make it easier for scripts to use Amarok's + Dynamic Collection feature. + * Download songs from Shared Music (DAAP) directly into the collection. + * Fadeout for Helix engine when pressing Stop. + * Guided editing of the collection/playlist/devices filters. Patch by + Giovanni Venturi . (BR 139292) + * Added GUI options for fadeout and fadeout on exit. Both are now enabled + by default. + * Support for Speex (.spx), WavPack (.wv) and TrueAudio (.tta) files in + the collection thanks to taglib plugins by Lukáš Lalinský + . + * Search inside of lyrics, by using "/" on Context Browser. Patch by + Carles Pina i Estany . (BR 139210) + * "Automatically show context browser" feature makes a return, as per + popular request. It is however disabled by default. + * Improved keyboard navigation: Space key is now a shortcut for Play/Pause, + and cursor left/right seeks forward/backward. + * Cover images are shown in collection browser. Patch by Trever Fischer + . (BR 91044) + * Send cover art to MTP media devices if they support it. + * Elapsed time can be shown in OSD. Patch by Christian Engels + . (BR 120051) + * New redownload manager for the Magnatune.com store. Allows re-download + of any previous purchase free of charge (in any format). + * New items in the playlist are colorized, as a visual cue. + * Show rating as stars in flat collection view. Patch by Daniel Faust + . (BR 133797) + * Synchronize play count, last played time and date of modification to + iPods. Patch by Michael . (BR 136759) + * Propose list of composers in collection when editing the composer tag + from the playlist. (BR 137775) + * Greatly improved sound quality for the xine equalizer. Patch by Tobias + Knieper . (BR 127307) + * Fancy graphical volume slider for the OSD. Patch by Alexander Bechikov + . + * Shoutcast stream directory. Contributed by Adam Pigg . + * Support for %composer and %genre when guessing tags from filenames. + * Cached lyrics are now AFT-enabled, and will follow your files around as + you move and rename them. + + CHANGES: + * Added configure option to build without DAAP support. + * Album covers are now downloaded and added to album directory when + purchasing from the Magnatune.com store. (BR 136680) + * Update context browser when a change in the collection has been detected. + (BR 140588) + * Ignore leading 'The ' when sorting playlist by artist. (BR 139829) + * Smart Playlists now have 'does not start with' and 'does not end with' + options, as well as a dropdown for mount points. (BR 139552) + * Support for cue files not matching audio files' name. Patch by Dawid + Wróbel . (BR 128046) + * Script Manager now remembers if categories were open or closed. + * Restart collection scanner as long as not more than 5 % of the files + make it crash. (BR 106474) + * Ensure the first selected item in the Collection Browser stays visible + when the search field is cleared using the clear button. + * Duplicate filenames are now allowed on MTP media devices if the files are + in different folders. + * Save media device transfer queue when adding items or after transfers. + (BR 138885) + * Upgraded internal SQLite to 3.3.12. + * MTP media devices are not automatically connected on start-up. This + should solve slow loading times for those with large collections on an + MTP media device. Contributed by Mikko Seppälä. (BR 138409) + * Internationalize unknown artist/album/genre strings. Contributed by Mikko + Seppälä. (BR 138409) + * Don't assume that a device returning 0 tracks is invalid. It could just + have no tracks on. Contributed by Mikko Seppälä. (BR 138409) + * Magnatune store look now matches rest of Amarok much better. + * Album art is displayed on the Magnatune purchase dialog. + * Generic media device now has an option to force VFAT-safe filenames even + on non-VFAT filesystems. + * Double-clicked items in sidebar and urls passed on the command line + are treated equally: append them to playlist if not yet there and start + playing the first if nothing is playing. + * "Scan Changes" button was replaced with "Update Collection" menu entry. + * Consistent double-click behavior in sidebar. (BR 138125) + * Propose name of currently loaded playlist when saving current one. + * Remove support for older libmtp versions. We now require 0.0.15 or + newer. + * Deleting a playlist item on an MTP media device now results in it being + removed from the playlist. + * Magnatune store is lazy loaded to improve startup times. + * Dynamic mode logic has been rethought to provide a faster and better + user experience. + * When checking for duplicate files on a Rio Karma media device, use + track number in addition to artist, album & title. (BR 137152) + * The XMMS visualization interface has been removed. LibVisual supersedes + this feature. + * It is now possible to select the time unit for length-based smart + playlists. (BR 136841) + * Show shadowed cover images in the system tray tooltip. (BR 136589) + * Amarok won't crossfade if it was paused, and user started another + track. Patch by Tuomas Nurmi . + (BR 136428) + * Amarok now saves playlists with relative paths by default. + + BUGFIXES: + * Disable seeking in streams. (BR 140364) + * With the default theme, the playlist browser info pane would not show + the horizontal scrollbar if necessary. (BR 134221) + * Some .rm files would make Amarok crash. (BR 137695) + * Remember 'User Cover Art for Folder Icons' when organizing files. + (BR 138562) + * "Listening since..." has been changed to the more clear "First + Played..." Patch by Andrew Ash . (BR 131727) + * Fixed regression: the DEL key no longer worked in the + playlist after opening the File Browser context menu. (BR 140197) + * Smart playlists now work correctly with "is not" filters containing + numbers. Patch by Felix Rotthowe . + * Context browser would not display updated covers correctly. (BR 130518) + * The select custom cover dialog no longer starts in the wrong directory + for compilations. (BR 131776) + * Amarok's xine engine would cut off approximately the last second of an + audio file. (BR 135190) + * Cue sheet would remain enabled when switching to a stream. Patch + by Ted Percival . (BR 127683) + * Length of tracks wouldn't be shown correctly for some cue files. + Patch by Dawid Wróbel (BR 139707) + * Assume that all dots but the last in script executable files belong to + the script name. (BR 139460) + * Don't crash when quitting while initially loading the playlist. + (BR 136353) + * The same track could be queued multiple times for transferring to a + media device. (BR 129136) + * Migrate statistics for files moved from outside to the collection. + (BR 127776) + * Select All/Copy action would not copy from context browser. (BR 138635) + * Xine-engine: When a track was fading out (after pressing Stop), and you + started another track, Amarok could become unresponsive. + * Improved seeking with xine-engine. No longer jumps to 0 when you seek + too quickly. Patch by Alexander Bechikov . (BR 99808) + * Improved cover images handling for Various Artists. Patch by Tobias + Knieper . (BR 136833) + * Don't enable a mount point for devices that can't support them (mtp, + njb). + * With SQLite, the search in the collection browser was case-sensitive + with UTF-8. Patch by Stanislav Nikolov . (BR 138482) + * (Don't) Show Under Various Artists would not work when multiple albums + are selected. Patch by Tobias Knieper . + (BR 112422) + * Changed temp download location for Magnatune purchases. (BR 137912) + * Fixed potential double payment issues in the Magnatune store. + * Only synchronize already set values to media devices. (BR 138150) + * Correctly update total playlist play time when removing last.fm + streams. Patch by Modestas Vainius . (BR 134333) + * File organization jobs could not be canceled. Patch by Wenli Liu + . (BR 136527) + * Sending filenames to MTP media devices as UTF-8 caused problems, use + Latin-1 instead. + * It's now possible to delete a file from an MTP media device and + re-upload it without having to reconnect the device. + * Wikipedia links to edit sections are no longer shown. + * Metadata is read from Rio Karma media devices as UTF-8. + * Last.fm streams could be paused with DCOP or global shortcuts. + (BR 133013) + * Dynamic mode can still be used after a collection rescan. (BR 133269) + * Dynamic mode will repopulate from all available sources. (BR 137212) + * Dynamic mode no longer repeats songs often. (BR 107693) + * When transferring files to a Generic media device, having certain + characters (such as '#') in a tag field could cause a directory based on + that field to not be created. + * Editing lyrics from within the context browser no longer removes all + linebreaks. + * Read metadata from MTP media devices as UTF-8. + * Some shoutcast streams would show an empty title. (BR 127741) + * Pause would act as Play/Pause. (BR 116101) + * The same track would sometimes be shown twice in suggested songs. + (BR 129395) + * Detect VFAT partitioned devices on FreeBSD. Patch by Daniel + O'Connor . + * Favorite Tracks wouldn't be shown on Context Browser, and + Statistics Panel would be empty for SQLite users. (BR 136791) + * Volume slider in the player window would not react correctly to + the mouse wheel. (BR 136714) + * When using a proxy set by script, context browser wouldn't work + properly, and the application would crash when closing. (BR 112437) + * Proxy settings wouldn't be respected when downloading podcast + episodes. (BR 134028) + * Xine engine could hang when skipping through tracks quickly with + crossfade on. + * Fix crash when an MTP media device returned a playlist with an + invalid track ID. (BR 136552) + * The Install MP3 support script would be run regardless of what the + user answered to the shown dialog. (BR 136294) + * OSD wouldn't always show up-to-date ratings. Patch by Tuomas Nurmi + . (BR 125612) + + +VERSION 1.4.4 + FEATURES: + * Transfer .wav-files to iPods. (BR 131130) + * Xine and Helix engines now support three different crossfading modes: + always, on manual track changes only, or on automatic track changes + only. + * Manually specify local file for podcast episodes via right-click menu. + * Action menu entry for adding podcasts to Amarok. Based on .desktop files + by Harald Sitter and Fabio Bacigalupo . + * Open podcast items with external application from right-click menu. + * Synchronize listened flag for podcast between Amarok and iPods. + * Added integrated Magnatune.com music store. Includes artist and album + info and full previews of all tracks. + * Fade-out for xine-engine when pressing Stop. Patch by Tuomas Nurmi + . (BR 127316) + * Support downloading of files from an MTP device. + * Purged podcast episodes can be readded by increasing the purge number. + * Added rudimentary support for the Rio Karma. (BR 132713) + * Support creation and editing of playlists on MTP media devices. + * Undo/Redo functionality is now available over sessions. (BR 131072) + * Allow the creation of empty playlists in the playlist browser. Available + either from the Add button in the toolbar or the context menu of a + playlist folder. (BR 133543) + + CHANGES: + * Ignore leading "The " when sorting artists on media devices. (BR 136233) + * Improved handling of VFAT/ASCII files and paths when organizing the + collection and using the Generic media device. + * Enable playing audio CDs on CD insert. Patch by Will Stephenson + . (BR 136106) + * Bring Amarok main window to front when starting amarok again without + arguments. Patch by Lubos Lunak . (BR 135396) + * Don't switch to playlist browser after saving a playlist from files tab. + (BR 130189) + * Add .ape and .mpc to possible file types supported by a generic media + device. (BR 133491) + * Move button for saving current playlist from playlist browser toolbar to + playlist toolbar. (BR 129300) + * Run 'kdeeject -q devicenode' when no post-disconnect command has been + configured for media devices. + * Reduced memory usage for MTP media devices. (BR 134663) + * Faster searching on playlist and startup, due to some optimizing in + string usage. Patch by Ovidiu Gheorghioiu . + * Correctly translate media:, home:, ... style urls on KDE 3.5 and newer. + * When tracks are added to the collection and Playlist entries already + exist (as determined by the file tracking code), the corresponding + Playlist entries are updated to the new location and enabled if they + were previously disabled. + * When file tracking is updating Playlist entries, multiple entries of the + same song will now all be updated, instead of just one. + * When tracks are removed from the collection (deleted on disk or moved + outside of a collection folder) any corresponding entry in the Playlist + will be disabled. + * Dragging podcasts to to playlist will insert them in a chronological + order, so you can listen to the oldest first automatically! + * Improve application startup times dramaticaly by lazy loading podcast + episodes. + * Transferring tracks to an MTP device now shows a progress bar and + doesn't hang the rest of the UI. (only available for libmtp >= 0.0.15) + * Show a proper tag dialog when viewing information for DAAP music shares. + + BUGFIXES: + * Ipod Mode on Collection Browser would have duplicated headers. + * Multiple problems related to Amarok using wrong playlists on Dynamic Mode + fixed. + * Deleting files from generic media devices would not update the progress + bar, resulting in the progress staying at 0%. (BR 130009) + * If nothing at all existed on a generic device, the first item + transferred would incorrectly show that an error had occured during + transfer. (BR 133528) + * Synchronising a smart playlist to a device when it didn't exist before + would crash Amarok. (BR 135956) + * Proxies would not take into account certain settings in KDE's Proxy + control center modules for PAC files and more. (BR 123021) + * Generic media devices would not accept files with an extension that only + differs in case from a supported extension. (BR 135261) + * Xine-engine: Pausing during crossfade would not work properly. Patches by + Markus Kaufhold . (BR 122514 & 135285) + * Stop a running cross-fading operation before starting another one. Patch + by Markus Kaufhold . (BR 128629) + * Queuing again would dequeue. (BR 121206) + * In some cases, the Removal and Enqueue buttons in the queue manager would + have no icons. (BR 115895) + * Don't change length of position slider when navigating within a track. + (BR 122569) + * Direct copying of non-local items would result in wrong properties on + iPods. (BR 135681) + * Honor setting to show Amarok's menu in main toolbar. + * "Burn this album" would burn all albums of the same name. (BR 121963) + * Ignore double-clicks on tree item openers. (BR 125121) + * Visibility of sidebar tabs would depend on the current locale. (BR 135316) + * Ctrl-C for copying urls from the tag editor would not work when selected + with the mouse. (BR 123327) + * Check for some integral data types for improved DAAP portability. + (BR 132939) + * Take disc number into account when checking if a song is already on an + iPod. (BR 135643) + * Editing metadata in the playlist itself now matches possible alternatives + case-insensitively. (BR 135683) + * Fix loading directory in external browser in the tag editor when the path + contains parentheses. (BR 132961) + * Stop scripts using a proxy when it's disabled in KDE. Patch by Felix Geyer + . + * While playing Last.fm Streams, sometimes metadata wouldn't be updated + on track changes. Patch by Tom Kaitchuck . + * Speed patch to load playlist columns from statistic tables on population + of the playlist, makes adding to the playlist and starting up faster. + Thanks Ovy ! (BR 135324) + * Save MTP playlists when they are renamed so we don't lose changes. + * Prevent new podcastepisodes from showing up in the playlistbrowser twice + by opening it's parent before adding. (BR 134108) + * New iPods would not get initialized. + * Files that were detected as being added back to the collection would not + always be re-enabled in the Playlist. (BR 130359) + * Fix some spelling and layout issues. Part of a patch by Malcolm Parsons + . + * Correctly handle horizontal wheel events in position slider. (BR 119254) + * Don't rescan collection while transcoding. (BR 133423) + * Don't try to copy to collection from urls without kio slaves. + * Don't quit immediately if amarokrc was removed. (BR 134439) + * The DAAP client would crash Amarok under certain conditions when + kdelibs was compiled with asserts on. (BR 132851) + * Configuring the toolbar would disable the stop button. Patch by + Markus Kaufhold . (BR 132477) + * Changed tags of songs on iPods would not propagate to its database. + (BR 133842) + * Fixed playlist encoding problems. (BR 133613) + * Cover images for compilation albums can now be displayed full size in + the context browser. + * Dragging compilation albums from the collection browser or the playlist + would show multiple cover images in the tooltip. (BR 133916) + * Don't crash when calling repopulate dynamic mode from dcop. (BR 133716) + * Last.fm streams work with proxies. (BR 131137) + * Don't try to read m4a tags from apparently invalid files. (BR 133288) + * Some podcasts would insert line breaks in author/title information and + cause graphical errors. (BR 133591) + * File tracking could fail on files that were copies of each other but + with different ID3v1 or APE tags. + + +VERSION 1.4.3: + FEATURES: + * New DCOP: player trackCurrentTimeMs, returns the current track position + in milliseconds. + * Amarok File Tracking (formerly ATF) goes public! See + http://amarok.kde.org/wiki/Amarok_File_Tracking for more information. + * DAAP client now supports Zeroconf. With mDNSResponder properly setup + Amarok automatically shows local DAAP servers. + * DAAP client saves manually added computers between sessions. + + CHANGES: + * Performance with big playlists has been improved by a magnitude. This + also makes application shutdown faster. + * Remove the option to enable/disable history in dynamic mode. (BR 133076) + * Reduce the minimum available tracks to show to 0. (BR 131223) + * Change in file tracking behavior: IDs are no longer embedded into tags + but are calculated from a portion of the file data instead, letting + users with read-only music stores take advantage of it. + * Don't report "/dev/hd" style devices as new media devices. (BR 127831) + * Smart Playlists only load media from currently mounted devices. + + BUGFIXES: + * Dequeuing tracks whilst in dynamic mode might not work. (BR 133449) + * When marking podcast episodes as listened, update the channel icon if + necessary. (BR 133497) + * Don't always mark podcast channel icon as "listened" on rescan if. + (BR 133495) + * User added streams were not editable once saved. (BR 133483) + * Cover images were not displayed in some cases. (BR 133174) + * Fixed bug which prevented Amarok from creating the collection database + in rare circumstances using SQLite. (BR 133072) + * Collection scanner would only restart a maximum of 2 times instead of + 20. (fixed in SVN revision 578922) + * MTP media device support would not compile against libmtp versions >= + 0.0.12. (fixed in SVN revision 576121) + * AudioCD playback would stutter and sometimes freeze Amarok. (BR 133015) + * Dynamic Collection broke flat collection view when the Filename column + was added (BR 132874) + * DAAP client shows connection errors to the user and no longer says + "Loading" perpetually. After a failed connection, the user can now + try again. + * Don't empty media device transfer queue when canceling a transfer. + * Ctrl-C for copying urls from the tag editor would not work. (BR 123327) + * Delete covers from the filesystem when requested. + * Show context menu on right-click in empty area of media device + browser. (BR 127154) + * Sort numeric columns in flat collection view numerically. (BR 130667) + + +VERSION 1.4.2: + FEATURES: + * Handle itpc:// and pcast:// url protocols for adding podcast feeds. + (BR 128918) + * New DCOP call "collection: totalComposers" returns the number of + different composers in your collection. + * Synchronize playlists to media devices. + * Support for MTP/PlaysForSure media devices. (BR 128532) + * iPod plugin usable with iTunes phones. (BR 131487) + * Browse collection by composer. (BR 122452) + * New DCOP call "playlist: filenames" returns the filenames of the songs + currently in the playlist. Patch by Arash Abedinzadeh + + * Lyrics can be edited directly on Context Browser's Lyrics tab. + * Collection browse mode similar to that used by some portable players. + Patch by Joe Rabinoff . (BR 130586) + * BPM field. Patch by Alf B Lervåg and Aaron + VonderHaar . (BR 123142) + * Improved crossfading for xine-engine: Honours the 'Crossfade Length' + setting precisely, and uses a better mixing style profile. Patch by + Enrico Ros . + * Media and collection browser tabs now support dropping. + * Allow for deleting all the tracks on a playlist from iPods. (BR 127855) + * Ability to create custom last.fm station from the GUI. + * Ability to mark podcasts as listened. + * Show error messages when connecting to last.fm streams fails. + * A new media device implements a DAAP client. So Amarok can connect + to iTunes, Firefly Media Server etc. (BR 100513) + * Dynamic Collection: improved support for songs on removable external + harddisks, SMB and NFS shares + + CHANGES: + * Skip tracks that failed to transfer to media devices instead of stopping + transfer process. (BR 130008) + * libtunepimp 0.5.0 actually compiles successfully now. + * Lift size limit on pathnames and comments in collection databases not + managed by MySQL. (BR 130585) + * Generic media device plugin is improved. Users can configure supported + filetypes and get more control over the location of songs and podcasts + on disk (Patch by eute). + * Move composer tag to its own database table. + * Re-enable adding videos to iPods with recent libgpod-cvs. (BR 130117) + * Include Skip, Love and Ban in playlist right-click menu for last.fm + streams. + * Advanced Tag Features (ATF) deferred to 1.4.3: Public release delayed + pending some bug fixes in both Amarok and a dependency. It will be + automatically disabled the first time you run 1.4.2 if you had it enabled + from 1.4.2-beta1. (It will still be available in subversion snapshots.) + * Optionally finish transferring all queued tracks to media device after + pressing disconnect button. (BR 129716) + * It's now possible to edit scores and ratings for multiple tracks in + TagDialog. + * TagDialog won't make Amarok unresponsive while committing tags changes + to files anymore. + * Exact playtime as tooltip in statusbar. Patch by Markus Kaufhold + . (BR 130463) + * Suspend collection rescanning while organizing files. (BR 129885) + * Always use metadata from original file for transcoded files transfered + to media devices. (BR 131171) + * Enhancements to ATF/statistics to allow for better tracking of stats as + files are moved. + * Tag Editing Dialog is now ATF-enabled. + * In-line tag editing is now ATF-enabled. + * Previously, using ATF with MP3 files would wipe out existing UFID frames + from other applications. Now Amarok plays nicely and only touches its + own UFID frame. + * ATF no longer requires a restart to enable or disable it. + * ATF read-only functions are always enabled if a UID is found in the + file. Option in the configuration dialog now only controls whether new + UIDs are written to new files. + * ATF will now automatically run the rescan and clear the Playlist only on + the first time it is enabled. After that it will simply display an info + reminding users that they may need a rescan if their library has changed + since the last time it was enabled. + + BUGFIXES: + * DCOP calls to add and remove ATF tags are no longer allowed to run while + the collection is being scanned. + * Last.fm streams no longer freeze Amarok's GUI with xine-engine. + * Sometimes metadata wasn't updated with Last.fm streams. + * Update context browser on score and rating changes. (BR 132496) + * Double colons in the collection filter would lead to invalid queries. + (BR 132551) + * Handle changed semantics of MySQL 5.0.23+ (BR 132114) + * Do not try to detach() KURLs, as this would not work for non-ascii urls. + (BR 132355) + * Adding songs while at end of playlist could crash in dynamic mode. + Patch by Joe Rabinoff . (BR 128340) + * Don't update accessdate when setting songs rating or score. (BR 132274) + * Increasing or decreasing volume while muted would not correctly unmute. + (BR 132228) + * Better resize behavior in iPod collection view mode. Patch by Joe Rabinoff + (BR 132016) + * Make sure a track's compilation status is returned properly when running + with Postgresql. + * Check directory structure on iPods of unknown type in order to detect + iTunes phones. (BR 131910) + * Make 'Clear' individually translatable for playlists. (BR 131521) + * Retain column visibility for flat collection view. (BR 126685) + * Honour proxy exceptions for MusicBrainz lookups. Patch by N. Cat + . (BR 131377) + * Correctly pass links containing parentheses to external browsers. Patch + by Thomas Lindroth . (BR 131307) + * iPods would not show podcast descriptions. (BR 129824) + * Carry over rounding increments to next larger unit for fuzzy time + display. (BR 131383) + * If disabled, don't show splash screen - even on Kubuntu. (BR 125210) + * Correctly request last.fm similar artist information for artists + containing non-ASCII characters. Patch by Thomas Lindroth + . (BR 131254) + * Support non-chronologically ordered podcast feeds. (BR 119911) + * Support for libvisual 0.4.0 was fixed. Patch by Dennis Smit. + * Adding songs already on a media device to playlists would not work. + * Fix adding smart playlists to media devices. (BR 130540) + * Reverse check for mount point and device node when connecting to iPods + for better handling of device nodes pointed to by symlinks. (BR 129965) + * Make handling of filenames on iPods case-insensitive and thus fix + fix problems with too many orphaned and stale items. (BR 126431) + * Correct action of queueing current item in dynamic mode. (BR 130313) + * Double clicking in the filebrowser will append to playlist. (BR 117465) + * Fixed problems with last.fm streams containing spaces, e.g. "Hip Hop". + * When generic media devices were specified manually, transferred files + would not always get converted to VFAT-friendly names if they were on a + VFAT filesystem. + * When using ATF, tags in MP3 files would be written as ID3v2 only and + existing ID3v1 tags would be stripped, which could lead to media devices + and tagging libraries that were not ID3v2.4-aware to report that no tag + existed. Now both tags are written with identical data. + * Correct handling of filenames with special characters. (BR 132243) + + +VERSION 1.4.1: + FEATURES: + * Support for last.fm streams. (BR 111983) + * New playlist toolbar menu entry for adding streams to the playlist. + (BR 129349) + + CHANGES: + * Upgraded internal SQLite to 3.3.6. + * Inotify support disabled for now, due to stability issues. + * Tag editor is no longer modal. + * Provide warning dialog when deleting items from the playlistbrowser. + (BR 129313) + * GUI layout reverted to the classic Amarok layout. + * The Extended Info panel in the playlistbrowser is now resizeable. + + BUGFIXES: + * Pressing return in the search bar of the Collection Browser immediately + after typing a query no longer appends the wrong items to the playlist. + * Fix crash when pressing Back or Forward buttons multiple times quickly + in Artist tab. Patch by Thomas Lindroth . + * Fix problems where blanks would be added to data if SQLite was busy. + Patch by Thomas Lindroth . (BR 127608) + * Automatically refresh stream lyrics on new metadata. + * Set half star ratings on multiple selected tracks when clicking on an + item. (BR 129449) + * Only enable Show Extended Info in the Playlist Browser when information + is available. (BR 126590) + * Disable global shortcut for ratings when ratings are disabled. + (BR 129414) + * Autodetect button in Media Devices configuration dialog would not + properly signal changes, so that new devices were not always saved. + + +VERSION 1.4.1-beta1: + FEATURES: + * Much improved and completed custom icon theme by Vadim Petrunin + . + * LibVisual 0.4 supported and required. + * Support for custom scoring algorithms, via scripts. + * Creative Nomad Jukebox support (untested!). Submitted by Andres Oton + . (BR 103185) + * Inotify support. On kernels 2.6.13 and above with Inotify support + compiled in, the collection will automatically be rescanned and + updated as soon as a watched folder has changed. + + CHANGES: + * First-run wizard can no longer be restarted from the application menu. + However, it can still be invoked with "amarok --wizard". + * Astraweb lyrics script was removed for being crappy and unmaintained. If + you want to maintain it, grab it from SVN and release on kde-apps.org. + * "Append Count" option of dynamic playlists has been removed. It is + now always one. (BR 120044) + * Context browser can now play/queue specific discs of an album or + compilation. + * Automatically imported playlists go into a separate category. + * Block quitting amaroK until all on-going media device operations have + finished with a consistent state. + * Interface choice in wizard removed. + * MoodBar has been removed. The maintainer has not been updating it, and + it was causing crashes for many people. + * Usability improvements for the Script Manager, including a tree view. + * Use KMimeType for resolving file type for metadata acquisition before + falling back to extension based guessing. + * Removed the "detailed mode" in the playlist-browser. + * Also copy non-local URLs to collection when dropped onto collection + browser. + * Speed up connecting media devices with a lot of tracks to be submitted + to last.fm. + * For media without metadata, try to read metadata after transfer to + the iPod (e.g. when copying an audio CD via KIOslaves). + * Hint at starting a transcode script for transcoding while transferring + to media devices. (BR 127155) + * If a disc number is present, append it to the album's name when + organizing files. (BR 126867) + * Configure, which of fresh podcasts, newest & favorite albums are shown + in context browser home view. Patch by Patrick Muench . + (BR 127043) + * Dynamic mode no longer skips to the next song if you press play (via + dcop, for instance) while already playing a track. Instead it restarts + the current one. + * The Actions menu has been renamed the Engage menu. It's way cooler, + right? I mean, Star Trek is really cool, right? + * Multiple podcasts can be configured at once by selecting multiple channels + or by configuring the children of a folder. + + BUGFIXES: + * Allow dropping of tracks after non-existant items in the playlist. + * Make changes to the default dynamic playlists persistent. + * Send UTF-8 encoded requests to Wikipedia. Thanks to Thomas Lindroth + for the patch. (BR 127654) + * Correctly restore podcast channel title when fetching fails. + * Show error message when xine mp3 decoder isn't installed, don't just + play next track. + * Properly render and optimise playlist loading icons. + * Properly import and export XSPF playlist formats. + * Optimise addition of playlists to the playlistbrowser. + * In context browser, show localized date for podcasts. (BR 127853) + * Regression in dynamic mode caused it to skip the first track in the + playlist whenever it was started. (BR 127451) + * Stop Playing after Track: remember current track (BR 127312) + * Radio streams were broken for protocols other than HTTP. (BR 127848) + * Collection Browser would not set/unset/burn albums with ', The' in + their name. + * Prevent breakage when xine couldn't initialize the audio device. Patch + from Ilya Konstantinov . (BR 115960) + * Allow for recognition of the webdav protocol. Patch by Ilya + Konstantinov . (BR 126847) + * Setting a rating on an unplayed track would affect score generated. + Patch by Patrick Muench . (BR 127475) + * Stop tags with different capitalisation being treated as the same + when building the collection. + * Make database connections actually get closed when no longer used. + (BR 123113) + * xine engine would truncate the last seconds of a track, if no other + track followed in the playlist. + * Fixed AudioCD playback with xine-engine. Patch by Markus Kaufhold + . (BR 127388) + * If dynamic mode was turned on and then off, the previous random and + repeat modes would be forgotten. (BR 123743) + * Removing the current track through DCOP while editing a field of the + track in the playlist would cause a crash. (BR 119152) + * Make characters encoded with % (such as a forward slash, %2f) display + correctly. (BR 105266) + + +VERSION 1.4.0: + FEATURES: + * New DCOP call "player: version()". Returns the amaroK version. + * iFP has persistent settings when transferring tracks to the device. + * GStreamer-0.10 engine now supports Audio CDs. + * Context menus for entries in the statistics tool. (BR 124945) + + CHANGES: + * Composer, Disc Number and File Size columns in flat collection view. + * 'k' or 'm' suffixes for matching filesize in kibi or respectively mebi + bytes. + * Groupings when transferring files to media devices are now persistent. + (BR 127158) + * Transfer contents of smart playlists to media device without adding + them to a playlist. (BR 126997) + * Set %albumartist to Various Artists, but keep %artist as the track's + artist when organizing compilations. (BR 126936) + * Discard empty tokens surrounded by {} in custom organize file format. + (BR 124337) + * GStreamer-0.10 engine was disabled for this release (not yet stable). + * Only pick genres for Smart playlists that exist in your collection. + * VFAT plugin completely rewritten since 1.4beta3. Name is now changed to + "Generic Audio Player" to make it less needlessly technical. + * Don't limit the number of episodes shown with a new podcast, since the + user can limit the number shown afterwards by configuring the channel. + * Automatically populate the playlist with items if it is empty when a + dynamic playlist is loaded. (BR 126594) + * Unplayed/unrated tracks are no longer shown in the statistics dialog. + * Removed the option "Import Playlists". It's now always enabled. + * Show total track time in context browser (BR 126548) + * Derive filename for downloaded podcast episodes from their url in the + rss feed. (BR 125966) + * Only show albums/artists/genres with more than 3 tracks when listing + favourite albums/artists/genres. (BR 126435) + * libtunepimp 0.5 compiles successfully. + * Podcasts are automatically configured to be checked for updates. + * Show only 2 decimal places for scores in the statistics module. + * Replace 'Move to Collection' in file browser context menu by 'Organize + Files' for collection directories. (BR 125702) + * Removed the option "Show Status Bar". It's now always enabled. + * Tracks from a media device scan be submitted to last.fm immediately, + without waiting for tracks to be played in amaroK. Patch by Iain + Benson . (BR 125690) + * Any failed attempts to submit to last.fm are now automatically retried + in the background, without waiting for new tracks to be played. + * Smart playlists can be constructed using mixed ALL and ANY matches + (BR 124483) + * Configure media devices in global settings, disable media browser when + no media device is configured. + * Dynamic Playlist bar made more conspicuous. + * The Konqueror setting to show a 'delete' entry in the menu is now + respected, if the setting exists and KDE is version 3.4 or higher. + * Cover art from m4a files. Updated m4a taglib patch by Jochen Issing + and patch by Shane King + . (BR 125414) + + BUGFIXES: + * The playlist would incorrectly sort after using the queue manager in + dynamic mode. + * Sort disc numbers numerically (BR 127114) + * Smart Playlists using 'last played time' now filter correctly. + (BR 127145) + * If "Transcode Whenever Possible" was selected for transferring to media + devices, if the file was in the device's preferred format, transcoding + would not take place. Thanks to Ants Aasma for the patch. (BR 127109) + * Fix possible loss of database after changing settings. (BR 126880) + * Only include audio files when expanding directories. (BR 126765) + * Correctly handle 'Cancel' in confirmation dialog for deleting items + from media devices. (BR 126989) + * Smart-Playlist random mode was not 'sticking'. (BR 126877) + * Statusbar log files would only ever write to the first log after all + four logs had been filled. + * iFP: Don't pretend to add newly transferred files to wrong folders. + * Set a podcast as listened only when it really has been listened to. + * All tracks from a cuesheet will now submit correctly to last.fm. + (BR 114969) + * xine-engine will now correctly detect a change when only one of the + artist or album metadata changes. Patch by Kim Rasmussen + . (BR 126648) + * Less than and between criteria in a smart playlist for playcount, rating + or score of 0 now work. (BR 97046) + * Empty genres are no longer displayed in the collection browser. + (BR 126495) + * Fix regression causing drag and drop of playlist track items in the + playlistbrowser to be functionless. (BR 126387) + * Fix regression causing podcast purge property to be ignored. (BR 126194) + * Automatically convert MySql/PostgreSql passwords from 1.3 to 1.4 state. + * Popup Messages would flicker when being shown. + * Some 1.3 podcasts wouldn't get transferred to 1.4 settings. + * New podcasts didn't get a default save location. (BR 126196) + * Fixed encoding problems with lyrics scripts. + * Mark/unmark as compilation is now stored in the file tag so it is + remembered when the colection is rescanned. (BR 120428) + * Submissions from media devices are timestamped so as to be less likely + to conflict with submissions from another last.fm client. (BR 125367) + * The MySQL connection will no longer time out when idle. (BR 120198) + * Load manually configured media devices even after failed DCOP queries. + Patch by Iain Benson . (BR 125692) + * Copy/move to collection recurses into directories. (BR 125334) + * Amazon no longer tries to refetch invalid entries. (BR 125168) + * Skip hidden directories while scanning the collection. (BR 115478) + * Instead of cancelling collection organiziation operations when starting + new one append to running one. + * Correctly show & in playlist 'Burn' right-click submenu. Patch by + Laszlo Pandy . (BR 125117) + * Disable option to delete remote items in playlist right-click menu. + (BR 124745) + * Reload playlist browser podcasts when switching database engines. + * Podcast tables recreated on startup if they don't exist. + + +VERSION 1.4-beta3: + FEATURES: + * amaroK now supports multiple media devices of varying types (currently + iPods, UMS/VFAT, and iFP devices). + * Autodetection of iPods and UMS/VFAT devices (if KDE has HAL/DBUS support + compiled in). + * New DCOP call "devices: showDeviceList()" to show the Device Manager's + current device knowledge. + * amaroK now has a custom icon theme, and an option to switch back to the + system icons, if preferred (in the General settings section). + * Collection browser view is separated alphabetically. Patch by + Christian Hoenig . + * Ease navigation with track slider below playlist window by showing mood. + (BR 121715) + * Show context information for podcasts. + * Filebrowser: toolbar button to change to the directory of the currently + playing song. (BR 115479) + * Added "Play Audio CD" entry to the amaroK menu. (BR 103409) + * GStreamer-0.10 engine now supports visualizations. + * xine-engine: Show metadata for ogg vorbis streams. (BR 122505) + * Drag and drop podcast urls directly onto podcast folders for addition. + * Add media directly into directories for iRiver ifp devices. + * Button to directly edit lyrics from the context browser. (BR 123515) + * Support for SMIL playlists. (BR 121983) + * Support for WAX playlists. (BR 120980) + * Handle the Year tag when playing AudioCDs. Patch by Markus Kaufhold + . (BR 123428) + * Ignore 'The ' in artist names when sorting in the cover manager, as per + the collection browser. (BR 122858) + * Add autocompletion to the composer field in the tag dialog. (BR 123026) + + CHANGES: + * In context browser, show information about recently updated podcasts, + recently added and favourite albums when nothing is playing. + * Ratings can now have half stars: click again on the last star in the + rating to toggle it between a half and a full star. + * Improved handling of embedded cover art, utilizing the database. Patch + by Shane King . (BR 124563) + * Statistics tool has had numerous improvements. + * Optimise: Only rerender the CollectionBrowser when relevant. + * Disable detection of iPod model and thus solve g_object_get related + problems. (BR 121990) + * Don't block GUI when trying to transfer large numbers of items already + on media device. (BR 123570) + * Update playlist items when their location is changed during organizing + files. (BR 123752) + * Recursively add tracks when directories are dropped to the media browser + and the collection browser. (BR 123982) + * Visualizations now receive stereo data from amaroK. (BR 118765) + * Upgraded internal SQLite library to version 3.3.4. + * Podcast information is stored in the database. + * Improved password handling in the PostgreSQL config dialog. Patch by + Peter C. Ndikuwera . (BR 118304) + + BUGFIXES: + * Expand-By smart playlists were returning the wrong number of values. + * Fix display of media device transfer queues larger than 4 GB. (BR 125247) + * Fix duplicate detection when transferring to media device for tracks having + empty album tags. (BR 125203) + * Fix spuriously garbled collection scans. Patch by Shane King + . (BR 125114) + * Fix error with 'Back' link when browsing related artists. (BR 123227) + * Files with names containing '#' or '?' from smart playlists would not + get transferred to media device. (BR 122488) + * Stop Playing After Track option wouldn't be shown for the right tracks, + when there were queued tracks. Patch by Marcelo Penna Guerra + . (BR 124297) + * Don't submit podcast episodes to last.fm. (BR 118987) + * Accept system:/media/ urls into the playlist. (BR 120249) + * Fix leak of file descriptors with embedded cover art. Patch by Shane + King . (BR 123472) + * Stop collection folders being automatically removed. Instead, allow + user to remove non-existent folders by deselecting parent. (BR 123745) + * Stop delete key in playlist deleting last deselected item. (BR 123265) + * xine-engine: Show bitrate and samplerate for CD-Audio and WAV. Patch by + Markus Kaufhold . (BR 123625) + * Some podcasts would cause amaroK to hang. + * Check if directories still exist when showing Collection directories. + (BR 123834) + * Playlist popup menu had a visual glitch with Lipstik and (probably) + earlier versions of Plastik. + * Fixed a huge memory leak when using xine-engine with crossfading. + (BR 119230) + * Sometimes iRiver devices would crash upon disconnecting. (BR 123416) + * Adjust the Astraweb lyrics script for a layout change on the site. Patch + by Andrew Turner . (BR 123636) + * Directory selection would incorrectly highlight a directory in a + corner case. (BR 123635) + * Don't pretend to be able to uninstall default ContextBrowser themes. + (BR 123585) + * Fix preamp and frequency band scaling in the xine equalizer. Patch by + Tobias Knieper . (BR 116633) + * OSD text would not be stripped of empty lines. + * Playlist couldn't be shuffled if queued items existed. (BR 120221) + * Fixed renaming of Smart Playlists. (BR 122509) + * Fixed some bugs with PostgreSQL and Smart Playlists. Patch by Peter C. + Ndikuwera . (BR 123317) + * Escape invalid characters when transferring files to IFP devices. + (BR 123199) + * Escape newline characters when showing detailed information for podcast + items in the playlistbrowser. (BR 123109) + + +VERSION 1.4-beta2: + FEATURES: + * Equalizer for the GStreamer-0.10 engine. + * Crossfade in the helix engine! + * The build date is shown in the "About amaroK" dialog. + * Show album covers when dragging playlist items. Patch from Jonas + Hurrelmann . + + CHANGES: + * Summarize transfer failures to media devices instead of a message for each. + (BR 122491) + * Don't list the entry in the engine selection widget, when + it's not the active engine. Makes no sense to select this dummy engine. + * The aRts and GStreamer-0.8 engines have been removed for being obsolete. + * Automatically skip to the next track in the playlist when a track is + unplayable. (BR 116555) + * Don't check for collection changes on startup if Watch Folders is + disabled. (BR 116173) + + BUGFIXES: + * Handle .m4a files as audio when transferring to iPod video. (BR 122492) + * Smart playlists would not transfer to media devices. (BR 122838) + * Assume that .mp4 files are audio only when transferring to iPod. (BR 122591) + * Dereference symbolic links when transferring to iPod. (BR 123206) + * Correct domain for japanese wikipedia locale. (BR 122319) + * When deleting a downloaded podcast, the icon wouldn't be updated. + (BR 122440) + * Manage Files would create duplicates on collection. (BR 122519) + * On Statistics Dialog, Compilations would be shown with a random artist, + and dragging to playlist would add only the tracks by that artist. + (BR 122363) + * When editing current dynamic playlist, the adjusting of upcoming tracks + could be faulty. (BR 122401) + * Changing database on First-Run Wizard wouldn't work. + * When loading M3U playlists containing "." or "..", amaroK failed to + detect that the files are in the collection. Patch by Ted Percival + . (BR 121046) + * Konqueror sidebar would show garbage for people not using UTF-8 locales. + (BR 122395) + * "Open in External Browser" in the lyrics tab works now. + * Lyrc lyrics script handles tick characters correctly. + * Crash on startup when upgrading from 1.3, using MySQL. (BR 122042) + * No more crash on exit or deleting podcast. + * Handle metadata for .aac files as mpeg instead of mp4. (BR 121852) + + +VERSION 1.4-beta1: + FEATURES: + * AudioCD (CDDA) support for xine-engine, including CDDB lookup. Patch by + Alberto Griggio . (BR 121647) + * The Helix engine now supports direct alsa playback using Realplayer 10. + * New DCOP call "player: setVolumeRelative(int ticks)". + * Options for Random Mode to favor tracks with a higher rating, score, or + ones less recently played. + * Support for playing entire albums. This works just like normal, except + when choosing the next track, it'll go to the next track from the album + it finds in the playlist, or the first track of another album otherwise. + * Support for plain VFAT devices in the Media Device browser. + * You can now mousewheel over a track's queue label to change its position + in the queue. + * Added a time-filter to the CollectionBrowser. Now you can make it show + only those tracks, which have been added to your collection within the + last day, week, month or year. + * Fit to Width for the playlist columns is now optional (accessible in the + context menu for the column headers). + * On-the-fly transcoding when transferring to media devices, provided + that an appropriate transcoding script is running. + * Handle compilations as such on iPods. + * New DCOP calls "mediabrowser: ..." for interfacing with media devices. + * Multiple simultaneously connected media devices. + * Lyrics support is now scriptable. This allows to add support for any + lyrics site, and makes it possible to provide upgrades. (BR 94437) + * New DCOP call "contextbrowser: showLyrics(string)". + * New 'File Size' column in the playlist. + * Amarok now supports ASX playlist files. (BR 114051) + * New DCOP call "collection: isDirInCollection(const QString& path )". + * New DCOP call "playlist: removeByIndex(int)". (BR 119143) + * For mp3, aac/mp4, and ogg vorbis, it's possible to use Disc Number and + Composer tags. (BR 110675) (BR 90503) + * For xine-lib 1.1.1 and greater, xine engine has gapless playback. amaroK + is now "The Wall" compatible. (BR 77766) + * Option for selecting external web browser in amaroK. No longer requires + KDE-Base. (BR 106015) + * Press Enter in the Collection Browser filter to send all the visible + tracks to the playlist. + * Hold Ctrl while pressing Enter in the playlist's filter to apply to all + visible items instead of just the first, and Shift to only queue and not + play them. + * Tags can be edited inline in the playlist by clicking on a single selected + item. + * Switchable Wikipedia locale. (BR 104383) + * Initial port of GStreamer engine to GStreamer 0.10. + * Drag albums and compilations from context browser to media device and + playlist browser. + * Browse your collection and other related artists with context browser. + * Copy artwork to iPods capable of displaying it. + * Show extended podcast info on iPod. + * Optionally update playcount for items played on iPod and submit them + to last.fm and synchronize ratings between amaroK and iPod. + * Tracks can now be rated from 1-5 stars manually, in addition to the score + which amaroK calculates automatically based on your listening habits. You + can use the 'Rating' column and Win+1..5 to change the rating. + * Ability to copy items from iPod and from filebrowser to collection. + * New 'Last Played' column in the playlist, showing when the track was last + played. (Like in the Context Browser.) + * Browsers can be now accessed with keyboard shortcuts, Ctrl+1..5. + Also Ctrl+0 to close the current one, and Ctrl+Tab to switch the focus + between the playlist and the active browser. + * Downloaded podcast episodes can be deleted from the context menu. + * New DCOP call "player: osdEnabled". + * Add contents of smart amaroK playlists as playlist to media device. + * Mediabrowser support for the iRiver iFP series! + * New dcop call playlistbrowser loadPlaylist. (BR 110082) + * New Edit Track Information dialog. Lyrics can be edited there, comments + can have more than one line, some statistics and tag guessing from + filename. (BR 93982) + * Show/hide browsers via context menu. (BR 110823) + * Display disk space on media device. + * Copy standard and amaroK playlists to media device. + * Create playlist from items transferred to iPod. + * Edit dumb iPod playlists with media browser. + * Ability to read audible.com .aa file metadata and to transfer audiobooks + to iPod via file browser. + * Optionally add new podcasts to media device transfer queue on download + and remove podcasts already listened to on media device connect. + * Add podcast shows to the Podcast folder on iPods. + * Persistent media device transfer queue. + * Incremental update of media device view. + * Automatic scanning for stale and orphaned iPod items. + * Moodbar! + * configure: report not included extra features (BR 115057) + * Ability to uninstall context-browser themes. (BR 111449) + * More columns available in the Flat View of the Collection Browser. + * New Collection Scanner, running in an external process. No longer can + amaroK crash while scanning the Collection :) + * Statistics tool! + * Dragging external playlists into the playlist browser will add them. + * NMM engine now has a configure dialog. + * Collection scanner now supports WMA, MP4/AAC, and RealMedia (RA,RV,RM). + * You can now Organize Music from the Collection Browser, to move and + rename files to a logical place in your collection folders based on their + tags. + * Option to crossfade only on manual track changes. Useful for listening + to consecutive tracks on a single album. + + CHANGES: + * Dynamic Mode is now stateless, meaning there's no Dynamic Mode any more, + only loading and unloading of Dynamic Playlists. There's also now a nice + info bar above the playlist when a Dynamic Playlist is loaded. + * The major huge context menu used for hiding/showing columns in the + playlist has been replaced with a shorter one and a nice dialog. + * Elapsed time / length in the systray tooltip now updates in real time as + the song progresses. + * Tooltips in the playlist for truncated text are now shown directly above + the text, giving the effect of it being expanded to its full length. + * The option for restarting scripts automatically at startup is removed, as + it is now the default behaviour. + * Reduced memory usage for large playlists to under 30% of pre-1.4 versions. + (Measured as the difference in memory usage between an empty playlist and + loading the 'All Collection' smart playlist.) + * Import iTunes album art from directories. + * Media Devices (Apple iPod, iRiver iFP, ...) are now handled with plugins. + * New default image for albums with no cover art. + * When tabbing between cells while editing tags in the playlist, autosave + the contents of the previous tag you edited, so you don't have to + constantly go in and out of editing mode to edit lots of tags. + * When saving playlists, if there's already one with the same name, instead + of complaining about it, smartly append (2), (3), etc. to the end. + * 'Stop Playing After Track' now has a shortcut (Ctrl+Alt+V), and a global + shortcut for the currently playing track (Ctrl+Win+V). + * Various keyboard usability and focus tweaks so using amaroK with the + keyboard is nicer. + * Upgraded internal SQLite database library to version 3.2.7. + * Recoding mp3 tags has been removed due to many unjustified + complications. + * Viewing track information of remote media will show the url. + * "Update"-button is now hidden in the collection browser if "Watch + folders for changes" is enabled in the options. + * Playlist Browser now remembers which entries were open across startups. + * The tooltip and the menu from the queue icon in the statusbar now shows + the total length of the queued tracks. + * The Home tab has been merged into the Current tab, now called Music. + * New look for the current track marker in the playlist. Pimp my roK! + * When turning either random or dynamic mode on, turn the other off, + instead of completely disabling random mode when dynamic is on. + * libgpod from gtkpod replaces kio based iPod support for improved + compatibility with various iPod models. + * Podcast settings are hierarchical now, meaning you can set settings + for the category's, newly added podcasts take the settings from there parent category. + + BUGFIXES: + * Dragging text to a filter line edit would still show the "Filter + Here..." text in the background. (BR 108876) + * Don't show an empty playlist length holder in the statusbar. + * Allow for % and _ in tags, and filter them correctly. + * Do not copy files of types an iPod is not capable of playing to the + iPod. (BR 117486) + * Also take track number into account when comparing tags for checking + if a track is already present on iPod. (BR 117380) + * iPod nanos would not switch off during playing songs added with amaroK + because of their file size not being set. + * "Show Fullsize" now works for ID3 embedded cover images. (BR 114517) + * Fix possible bug when saving unencoded podcasts to strange file systems. + * OSD Preview did not update colours when toggling 'Use custom colours' + option. (BR 115965) + * Cached lyrics are not erased when rescanning. (BR 110489) + * No more "can't create amazon table" warnings. (BR 113930) + * Creating a new playlist via drag-and-drop no longer shows duplicates + of each song until amaroK is restarted. + + +VERSION 1.3.9: + FEATURES: + * Support for libtunepimp 0.4. (BR 94988) + + BUGFIXES: + * Fix leak of file descriptors with embedded cover art. Patch by Shane + King . (BR 123472) + * Playlist popup menu had a visual glitch with Lipstik and (probably) + earlier versions of Plastik. + * Fix preamp and frequency band scaling in the xine equalizer. Patch by + Tobias Knieper . (BR 116633) + * Fixed a huge memory leak when using xine-engine with crossfading. + (BR 119230) + * Fix memory leak in the helix engine when the player and playlist are + not visible. + * Stream with URLs containing "&" wouldn't be correctly saved. + (BR 121846) + * Playlist Browser would save invalid PLS Playlists. (BR 122875) + * Refresh All Podcasts wouldn't consider subfolders. (BR 122783) + * When using a folder as playlist, deleting the playlist would delete + the folder and all files inside it. (BR 122480) + * OSD was showing "No track playing" for tracks without metadata. + * Smart Playlists with playcount or score related conditions wouldn't + match all songs properly. (BR 97046) + * With enormous queues, stop menu would take a lot of time to show up. + (BR 120677) + + +VERSION 1.3.8: + BUGFIXES: + * NMM engine would crash when seeking after the playlist finished, + state Empty wasn't emitted. + * Fixed URL of the Nectarine radio stream. + * Fix crash after changing the alsa device in the helix configuration + dialog. + * When amaroK exits, send SIGTERM to running scripts. (BR 119159) + * Old error messages could be shown instead of current track lyrics. + * The equalizer in the helix engine now works properly at low sample + frequencies. + * Fixed some threading issues in loading XML playlists. + * Lyrics that are available on lyrc would be shown as "not found". + * The helix engine now includes protection so that misbehaving streams + do not cause the visualizations to leak memory. + + +VERSION 1.3.7: + CHANGES: + * In the tree view, sort tracks alphabetically first, unless one of the + categories is by album, then sort by track number first. (BR 112830) + * No longer delete Amazon covers every 90 days, instead relying on + RefreshImages to re-download covers every 80 days to comply with + the TOS of the Amazon web service. + + BUGFIXES: + * Fix weirdness when overwriting a playlist by dragging a file to the + browser. + * When using Year - Album on Collection Browser, if two albums had the + same year, the order would be pseudo-random. Patch by Xepo + . (BR 115584) + * Fix build issue on PCLinuxOS with "cpu_set undeclared". + * Fix crash in helix engine caused by improper reference counting + of the audiostreamresponse object. + * Helix engine no longer declares it is "empty" on a track change + (caused problems with context browser). + * Tag dialog doesn't delete year tags any more when editing multiple + tracks. + * amaroK would crash or hang when fetching similar artists information + from last.fm (BR 116399) + * Fix memory leak in the helix engine. (BR 116223) + * When changing the database type, the apply button wouldn't be enabled, + and it would be necessary to restart amaroK for it to work properly. + * Fix for regression in Qt 3.3.5, causing amaroK to crash when clearing + the playlist. (BR 116004) + * Zombie directories are removed automatically from the collection + scanner. (BR 115779) + * Dates wouldn't be properly loaded when editing Smart Playlists. + * Number of songs to add when using dynamic mode wouldn't be respected, + if the smartplaylist didn't have a ORDER BY statement. (BR 115860) + * Fix visibility related build problem on some distros. + + +VERSION 1.3.6: + BUGFIXES: + * Fix autoscan with PostgreSQL. (BR 111209) + * Fix problem with sequences in PostgreSQL support. (BR 115075) + * Fix potential crash at startup while accessing amazon.com. (BR 115838) + * Potential crash when loading media from the Collection. (BR 115234) + * Podcast apply to all button was faulty. + * last.fm queue wouldn't be saved to disk. Patch by John Patterson + . (BR 115212) + * Podcast download directory would only be effective next time the + application started. + * Don't crash when attempting to save an empty playlist from the Playlist + menu. + * Loading dynamic playlists with sources did not work properly. + * Fix build issue on some Linux kernel 2.4 distros. (BR 115068) + + +VERSION 1.3.5: + BUGFIXES: + * Fixed a build issue. + * Fixed potential crash at startup. (BR 114983) + + +VERSION 1.3.4: + FEATURES: + * Helix-engine supports ALSA (using RealPlayer 11). (BR 113909) + * Atom feed compatibility for podcasts. + * Statusbar messages are logged to a file, statusbar.log. (BR 99899) + * Podcast configuration now provides the ability to set the values for + all podcasts. (BR 114371) + * Downloading multiple podcasts will throw them into a queue, and + each will be downloaded sequentially. (BR 114370) + * Playlistbrowser items can be dragged into folders. + + CHANGES: + * Categories in the playlist browser are now always in the order of: + Playlists, Smart Playlists, Dynamic Playlists, Radio Streams, then + Podcasts, regardless of sorting options. (Items in the categories + are still sorted normally.) + * Reworked systray icon handling -- mostly under the hood, but it'll + now update properly - eg. when you change the cover. (BR 111014) + * Tooltip for the queue icon in the statusbar will now show the album + cover of the upcoming track. + * Totals in the collection browser will now reflect the visible items + if you set a filter. + * Podcast settings "download on request" and "stream on request" have + been merged. + * About button in script manager now uses a KAboutDialog and supports + rich text format in the README file. (BR 110961) + * After filtering the collection browser, if only a single item is left + visible, it will automatically be expanded. + * Added items for the Equalizer, Visualizations, and Queue Manager to + the context menus of the volume slider, analyzer, and statusbar queue + icon, respectively. + + BUGFIXES: + * If you queue an album from the context browser and then undo, the + queue icon in the statusbar is now updated properly (and hence + doesn't crash if you click on it). + * helix-engine no longer emits new metaData if only the bitrate of a + stream changes. (BR 114348) + * Fix amaroK attempting to destroy your computer, reach through the + monitor and violently strangle you if you attempt to exit while the + collection is being scanned. (BR 114597) (BR 114859) + * Postgresql code cleanup and fixed regression for manual collection + scanning. Autoscan still does not work. (BR 111209) + * File browser now sets to home if it was on a remote directory to prevent + annoying error messages. (BR 114498) + * Podcast settings would not add a trailing slash to podcast save + locations. (BR 114712) + * Workaround for stability issues with HyperThreading on Linux. + Added a configure check to deal with buggy GLIBC's. (BR 99199) + * xine-engine: Equalizer became inactive on trackchange when crossfading + was enabled. (BR 114492) + * Pausing a track would abort lyrics and wiki fetch jobs. (BR 114576) + * Dynamic mode did not respect repeat track mode. (BR 114585) + * The Script Manager no longer captures the script's stdout. + * Enqueuing files with amarok -e would not work for relative paths if the + working directories of the new and the running instance of amarok differ. + * Visualizations would only work when amarok was run as amarokapp. + (BR 99627) + * The number of podcasts items would be limited even when the user didn't + set it. (BR 114353) + * Switching system language wouldn't affect the root folder names on + Playlist Browser. + * On Context Browser, when showing a cached lyric, "add", "search", and + "open in external browser" buttons wouldn't work. "Open in External + Browser" is now disabled for cached lyrics. (BR 110812) + * Refreshing all podcasts when folder existed caused a crash. + * Multiple job statusbar widget was broken. (BR 114278) + * HTML in tags was getting interpreted in the context browser. + * Changing the podcast purge count could sometimes cause amaroK to hang. + * NMM-engine: Fixed crash after playing a song to the end, the trackEnd + signal was not emitted from the GUI thread. + * With Random Mode enabled and Repeat Playlist disabled, when it got to + the last track, it would play it a second time and then keep on playing + other tracks, instead of just stopping. + * Smart-Playlists were broken with PostgreSQL. Patch by Michael Landin + Hostbaek . (BR 114269) + * Collection scanner ignored files with non-ascii characters. (BR 114195) + * Don't show "Change Collection Setup"-box for non-local files. + * Fixed issue with loading playlists containing remote URL's. + * Dynamic mode history tracks would be forgotten if there was no current + track on startup. (BR 110160) + * Fixed problems with "Retrieve Similar Artists" feature in combination + with SQLite, which could lead to 100% CPU usage. (BR 104447) + * Tabbing between items and cells in the playlist while editing them now + works much nicer (goes in order and doesn't tab to invisible columns), + and you can also now use Alt+Up, Down, Left, Right to navigate between + cells as well. + * Podcast settings failed to remember the save location. (BR 114128) + * Tray icon would stop filling up and showing play/pause icon if show + player window was toggled. (BR 93711) + * If player window is toggled during playback, playlist window's caption + now correctly shows the current track's name. + * Crossfade length would be enabled in Playback options when "No + crossfading" was selected. + * If an engine does not support crossfading, "No crossfading" is now + selected in Playback options. + + +VERSION 1.3.3: + FEATURES: + * New DCOP call "contextbrowser: showHome". + * New DCOP call "contextbrowser: showCurrentTrack". + * New DCOP call "contextbrowser: showLyrics". + * New DCOP call "contextbrowser: showWiki". + * Saving a playlist will cleverly pick a default name if possible. + * Dragging an album cover into the playlist from the context browser + will append the album. + * Middle mouse button on the current track will toggle play/pause. + * Ctrl-Right click on a selection of tracks will queue all of them, not + just the track below the cursor. (BR 112841) + * CoverManager allows for downloads from Amazon Canada. (BR 113238) + * New DCOP call "playlistbrowser: addPlaylist". + * New DCOP call "playlistbrowser: scanPodcasts". Will check all podcasts + for new episodes. + * New DCOP call "playlistbrowser: addPodcast". + * New DCOP call "player: type". Returns the current track's file type. + * New DCOP call "collection: migrateFile". Updates the collection db for + changes made to filenames, keeping stats intact. + * Smartplaylist has Length property. (BR 113039) + * Added a mouse-over effect for the volume slider. + + CHANGES: + * Adding a playlistbrowser folder will automatically focus the lineedit + for renaming the item. + * Removing podcasts will delete all downloaded media. + * Playlists in the playlistbrowser can no longer be removed, only deleted. + * Removing tracks when in dynamic mode will only replace up to the minimum + upcoming tracks requirement. + * Playlist columns are automatically resized when adding or removing + columns. + * Added a warning dialog when HyperThreading is enabled. (BR 99199) + * Blacklisted GStreamer's autoaudiosink, which is really a crapsink. + * Added a context menu to the volume slider. + * When viewing covers in fullsize, the window has a maximum size, and + scrollbars are shown if necessary. The user can also scroll the cover + by dragging it. Patch by Eyal Lotem . (BR 103990) + + BUGFIXES: + * Patch fixing an almost-infinite directory-scanning problem while + building the Collection. Patch by Dirk Mueller . + * Cover Manager: Album view setting became out of sync. Patch by Michael + Pujos . (BR 113370) + * Starting the first track in the playlist when in dynamic mode would skip + it. (BR 110160) + * Position slider in player-window disappeared after 2 hours. (BR 97128) + * PlaylistBrowser duplicated items when overwriting playlists. (BR 108693) + * Podcast settings would forget about the purge items checkbox. + * The Stop button in the toolbar was always enabled at startup. + * GStreamer-Engine: Could not seek to position 00:00:00. (BR 106483) + * Don't crossfade the last track in the playlist. (BR 96478) + * If files were in the transfer queue before connecting the iPod they + would be uploaded without checking if they already exist on the device. + * Using dynamic mode's playlist shuffle would result in repeated tracks + tracks during a populate operation. + * Fixed Xine config options were disappearing on ESC key. (BR 113225) + * Fixed problems with visibility enabled compilers. Patch by Unai Garro + . (BR 113056) + * Fix regression causing dynamic mode playlist shuffle to break for + smart playlists which relied on ordering and limits. (BR 113121) + * Automatic podcast downloads did not do anything. (BR 113129) + * Playlist browser items were not properly saved on quit (with Qt 3.3.5). + Patch by Matthieu Bedouet . (BR 113020) + * amaroK could crash on startup, if on last exit sorting was enabled in + the playlist. (BR 113042) + * Adding entries to a playlist and saving it could duplicate some tracks, + if the playlist hadn't been expanded before. (BR 111579) + + +VERSION 1.3.2: + FEATURES: + * Tabs will open automatically when dragging files between tabs. + Patch by Christian Baumgart . + * Two new dcop calls which allow scripts to read many of amaroK's + configuration options. script readConfig(key) for strings, integers and + bools. script readListConfig(key) for lists. Note that these functions + aren't guaranteed to always return the latest settings (though many do). + * Added a right click menu for blank areas of the playlist, with options + to save, clear or shuffle the playlist and to "enable the dynamic + mode & repopulate". + * Playcount is shown in the tag dialog. + * New volume slider, both better looking and better working than + the old one. + * Podcasts can be saved to any location. (BR 111059) + * Added "Save as Playlist" option to the collection and file browser + context menus as well. + * Allow removing of items in the Media Device browser transfer + queue. + + CHANGES: + * Scroll wheel to switch tabs in context browser. + * Repopulate button is enabled or disabled together with dynamic mode. + * No warning dialog when starting if the directory File Browser is on + doesn't exist anymore. It just reverts to home. (BR 99208) + * Sorting on Collection Browser now shows "Unknown" items first, and + "Various Artists" last. Years are sorted descending now. + * When selecting 'Play' from the context menu on multiple items, + it'll now play the first and queue the rest. + + BUGFIXES: + * The Equalizer and QueueManager widgets were broken on window managers + other than KWin. + * "Year - Album" category in the Collection Browser didn't allow for + dragging tracks or fetching cover images. + * Xine engine no longer adds images to the playlist. + * The delete key for removing playlist items works even if the file + browser is open. (BR 100145) + * Filenames with XML entity codes were not playable in dynamic mode + and caused it to stop. (BR 108783) + * If the album or artist contained "&", cover fetching wouldn't work + properly. + * When restarting, Playlist Browser items used for playlist shuffle + wouldn't be properly marked, though they would be taken into account. + * Don't crash after changing Podcast options, or after manually deleting + its first item. + * When renaming a playlist, the "." would be removed from the filename. + Paych by Elliot Pahl . (BR 112204) + * When using next and previous on Tagdialog, after passing by a stream, + the fields would be always disabled. (BR 112060) + * Restarting track when in dynamic mode didn't work. + * Fix issues with the GStreamer engine and alsasink, and reenable it. + Patch by Vincent Tondellier . (BR 112103) + * Dynamic playlist shuffle had some incorrect smart playlist handling. + * Robustified the code for handling the '# of tracks in the playlist' + part of the statusbar, it should not ever get out of sync with + reality now. Nice side effect is you can see the track count + increase while a playlist is loading. + * "Last played - not in the last" smart playlists would only work for + sqlite. (BR 112248) + * Podcast and Dynamic subfolders are correctly restored on application + start. (BR 112162) + * Dropping tracks onto playlist browser folders will work correctly. + * Invalid podcasts are no longer discarded on quit. (BR 112116) + * Fixed playing of files that have special characters like '#' in + helix engine. + * Fixed issue where selecting multiple items after filtering the + playlist would cause all the other items 'between' them (but + invisible due to the filter) to also get selected. + + +VERSION 1.3.1: + FEATURES: + * Added 'Set as Playlist (Crop)' and 'Save as Playlist' options in the + playlist context menu. (BR 99932) + * Support for iPod shuffle devices. Patch by Guenter Schwann + . + * Media Device browser now has a connect button for connecting + your iPod after amaroK has already been started. Also includes + configurable mounting/unmounting options. + * Holding down the stop button (as opposed to just clicking it) pops + up a menu letting you stop either now, after the current track, or + after the end of the queue. + * Collection browser filter now fully supports the same Google-esque + syntax as the playlist filter, plus one extra: lyrics:"stuff to search + for" to search in cached lyrics. + * Pressing Shift+Enter after filtering the playlist will now queue + the first track. (BR 111054) + * Display short statistics in the collection browser depending on the + categorisation method. + * New DCOP call "collection: totalTracks". Returns the total number of + tracks in the collection. + * New DCOP call "collection: totalGenres". Returns the total number of + genres in the collection. + * New DCOP call "collection: totalCompilations". Returns the total number + of compilations in the collection. + * New DCOP call "collection: totalArtists". Returns the total number of + artists in the collection. + * New DCOP call "collection: totalAlbums". Returns the total number of + tracks in the collection. + * New DCOP call "collection: similarArtists(int artists)". Returns the + similar artists of the current track, results are limited by 'artists'. + * New DCOP call "playlist: repopulate". Repopulates the playlist with + tracks from dynamic mode. + * New DCOP call "player: showBrowser". Allows for showing of playlist + window browser, see the handbook for useage. + * New DCOP call "player: setLyricsByPath". Allows adding custom lyrics + for tracks. + * Add an icon in the statusbar displaying the number of queued tracks; + click on it to pop up a menu letting you jump to their locations in + the playlist. + + CHANGES: + * New "Blue Danna" splash screen. Created by Nenad Grujicic, modified by + Nathan Adolph. + * 'Stop after track' is now saved (and so remembered across amaroK + restarts). + * Ported playlist + filter-lineedit behaviour to collection browser as + well: you can move between the view and the filter with the up/down + buttons, and just typing into the view will set the filter. (BR 108656) + * Wiki Tab links use the color set for links, instead of "Selected + Background". Style Authors can use "AMAROK_LINKCOLOR" if they want that + color. (BR 111228) + * The Equalizer widget has been pimped. + * Pressing 'up' in the playlist filter will now take you to the end of + the playlist, in addition to down going to the beginning, as before. + * When jumping to the current track, it now gets centered instead of only + barely showing. + * GStreamer-engine was rewritten. The crossfading feature was removed for + now (it didn't work right with recent GStreamer versions). Improvements: + 1) Reduced CPU usage 2) Reduced latency 3) Increased stability + * No need to restart amaroK to use your iPod! + * Improved Konqueror Sidebar. + * The bundled "Shouter" AmarokScript (for radio stream serving) has been + updated and improved. + + BUGFIXES: + * amaroK wouldn't remember current track when restarting. (BR 110282) + * Some memory leaks found and fixed. + * Fix buzz and subsequent clicking when equalizer enabled in Helix and + GStreamer engines compiled with GCC 4.0.1. + * Burn option wouldn't show up for "Year - Album" items on Collection + Browser. + * Tray's tooltip would show things like 69:40 of 1:12:01. + * Wiki Tab wouldn't work for names that contained "/". (BR 111634) + * With KDE 3.4, the proper context menu wouldn't be shown for File + Browser. Patch by Christian Baumgart . + (BR 103305) + * Playcounter and Access Date wouldn't be updated properly for PostgreSQL. + Patch by Tonton . (BR 111519) + * Clicking twice on the uninstall button for the same script, would make + amaroK crash. + * Fixed an obscure crash when you emptied the playlist, had the focus on + it, and pressed up. + * No longer show dynamic info popup on application startup. Patch by + Christian Baumgart . + * Sometimes the system tray tooltip did not update on song change. + * Polishing for the collection browser and expanded item states. Patch + by Christian Baumgart . + * With xine-engine amaroK always treated remote media like radio streams. + * Selecting Classical equalizer preset prompted for name. + * Fixed konqueror sidebar compilation with kde <= 3.3 and gcc patched for + visibility. + * Konqueror sidebar can switch again between tabs. + * Fixed playing of oggs in helix engine. + * Fixed crash in helix engine when switching engines if helix/realplayer + not installed. + * Undo/Redo for the playlist was broken in some cases. + * On Collection Browser, when grouping by Genre/Artist/Year-Album it + wouldn't show the tracks. (BR 110890) + * SmartPlaylist Editor would reset "Match Any" to "Match All" when + editing. Patch by Kevin Henderson (BR 110918). + * Podcasts and playlist tracks would be sorted lexicographically + (BR 97297). + * Saved dynamic playlists were not removable. + * xine-engine: amaroK would get stuck on exit if the Equalizer was enabled + and the engine playing. (BR 110791) + * Dequeued items sometimes weren't being repainted properly. + + +VERSION 1.3: + FEATURES: + * The tyranny of deleting covers every 90 days is over. Instead, amaroK now + automatically downloads the covers every 80 days to comply with + Amazon.com requirements. + + CHANGES: + * Removed 'Apply' button from dynamic config, all config options are now + hot! (Automatically applied on alteration) + * Minimum score changed from 1 to 0. (BR 107944) + * Playlist item lengths now shown with hours when necessary. + + BUGFIXES: + * M3U playlists would be broken after editing. (BR 109774) + * When there's no artist tag, don't show tons of unrelated songs and + albums in Context Browser. (BR 110319) + * Advertisements were showing up in Lyrics Tab for some songs. + * When editing tags in Playlist Window, only try to write the new tag if + it's different from the old one. (BR 110299) + * Changes to the score in the Edit Track Information dialog should only be + applied after clicking on the "Save and Close" button. + * When only the score is changed, amaroK shouldn't complain if the file is + read-only. (BR 109054) + * Mark/Unmark as compilation wouldn't work with SQLite. (BR 109275) + * Album Covers whose name or artist contained "'" wouldn't show up when + fetched from Amazon. (BR 109700) + * Edit Track Information dialog wouldn't update collection database if + filename contained non latin1 characters. Patch by Andrey Yasniy + (BR 110030) + * SmartPlaylist category created in the PlaylistBrowser once the + collection has been built for the first time. + * Refresh the context browser as appropriate when editing tags. (BR 108884) + * Cover image shown if track has no title. + * Statusbar cancel button will terminate a podcast download. + * Don't show multiple popup messages when retrieving podcast information. + * Don't crash when adding podcasts. (BR 109982) + * Tracks with urls containg apostrophes would not cache lyrics. + * PostgreSQL compile problem (BR 110033) + + +VERSION 1.3-beta3: + FEATURES: + * New "not in the last" option for the date fields in Smart Playlists. + (BR 107725) + * New OSD tokens: %directory and %type (shows whether it's a stream, or + otherwise the extension). + * New DCOP call "player: lyrics" (BR 100306) and Lyrics Caching. (BR 97961) + * New DCOP call "player: transferDeviceFiles". Transfers queued files to + the Media Device. + * New DCOP call "player: queueForTransfer". Queues files for transfer to + the Media Device. + * Download your favourite podcasts and let amaroK manage them for you! + * 17 Equalizer presets. (BR 96302) + * xine-engine supports crossfading. Note: Your audio device must support + mixing. SBLive, dmix or ALSA 1.0.9 will do the trick. + * Shuffle the queue list in the queue manager. (BR 108861) + * The audio plugin (autodetect, ALSA, esd etc.) for xine-engine is now + configurable. + * Playlist-Browser now remembers the state and layout of its tree view. + * Show a stop icon next to the track to stop playing after. + * Miniature player window for the minimalists out there! (BR 85876) + * "Stop Playing After Track" now also works for queued tracks. + * "Open in External Browser" button for Lyrics Tab, patch from Nick + Tryon (Dhraakellian). + * Funky shadow effect for the album cover @ Context-Browser and OSD. + (BR 108334) + * Create playlists by dragging tracks onto the Playlist Category in the + PlaylistBrowser. (BR 75029) + * Show OSD when pausing and unpausing. (BR 104508) + * Make 'The' prefix of artists be transparent in the collection + browser and sort accordingly. (BR 85959) + + CHANGES: + * TagLib version 1.4 is required. + * Renamed "Track Name" column to "Filename", "Extension" to "Type". + * "Use hardware volume mixer" option has been removed. + * "Play AudioCD" gets disabled for engines that don't support KIO. + * The OSD (by default) and systray tooltip now show the same infos in + the same order as the columns in the playlist. + * xine-engine's configuration dialog has been reworked and simplified. + * xine-engine has been given the highest engine plugin rank. + * Systray tooltip now shows "elapsed time / total time" for the length. + + BUGFIXES: + * When playing, the text in the current track's columns wouldn't get + ellipsii added if the column was too short. + * Dragging 'All Collection' smart playlist made amaroK hang. + * Compilations reported incorrect number of tracks in the Context + Browser. (BR 109651) + * Track play icon remains even when stopped playing. (BR 107284) + * Sometimes valid tracks were not submitted to AudioScrobbler. (BR 100278) + * Current playlist is now being remembered when amaroK crashes. (BR 98689) + * Playlist-Browser saves its state after each change, so that no data + is lost when amaroK crashes. (BR 108814) + * Crash when trying to save Smart Playlists after creating a Collection + for the first time. + * Context menu of compilations was empty in context browser. + * Don't append albums and compilations when clicking on text in the + context browser. (BR 98797) + * xine-engine: pre-amp for the equalizer works now. (BR 104882) + * Crash when changing the number of minimum upcoming tracks right after + starting amaroK. (BR 108251) + + +VERSION 1.3-beta2: + FEATURES: + * New DCOP call "collection: scanCollectionChanges" Scans for changes made + to the collection. + * Support for "media:" URLs. Patch by Sergio Cambra + (BR 102668) + * Support for visualizations in the Helix engine. + * Queue manager to help organise your queued tracks. (BR 90594) + * Ability to create Smart Playlists based on file path. (BR 92467) + * Per track scripting via custom playlist context menu items. + * Added advanced, Google-esque syntax to the playlist filter. Lets you do + things like artist:sirenia, "pink floyd", artist:"pink floyd", or even + score:>50. When just typing words, it works as before. (BR 99312) + + CHANGES: + * Upgraded included SQLite library to version 3.2.2. + * Bumped GStreamer and GStreamer-plugins dependency to version 0.8.6. + * aKode-engine has been disabled (too buggy/incomplete). + * Repopulate upcoming tracks on demand when using dynamic mode. + * Remodel the playlist browser to incorporate dynamic mode more fully. + + BUGFIXES: + * Don't show textual URLs in Wikipedia Tab. (BR 108031) + * Don't refresh the collection view on update scans, if nothing changed. + * xine-engine: Don't pop up hundreds of error messages when something + goes wrong. Patch from John Lash (BR 101646) + * Automatic theme download with KNewStuff works now. (BR 107313) + * Clicking on "Lookup track at musicbrainz" use %2520 for spaces in URL. + (BR 107946) + * Crash when loading dynamic playlists without a collection. + * Crash when saving smart playlist without a collection. + * Do not call TagLib::MPEG::File for non-mpeg files - some FLAC files + would cause the CPU to start running in circles. (BR 107029) + * Many Helix engine improvements. + * Crash when dragging playlist items into Playlist Browser. (BR 107709) + * Improved context display when playing radio streams with xine-engine. + * Number of album tracks was incorrect when showing statistics by album. + (BR 107762) + * Massive performance speedup for the default analyzer (BlockAnalyzer). + * Dynamic mode will grab tracks from closed playlists. + * Covermanager tooltips were persistent even when window closed. Tooltips + have now been replaced with statusbar text. (BR 106976) + * Turning off dynamic mode when items were filtered only 're-enabled' the + visible items. + * Disable random mode on startup if dynamic mode is on. (BR 107311) + * The user is warned if saving tags failed. (BR 91568) + * Sub-Folders in Playlist Browser are correctly saved and restored. + * Crash after clicking on remove playlists in dynamic mode. + * Crash on Context Menu in dynamic mode. + + +VERSION 1.3-beta1: + FEATURES: + * Add Media dialog allows for multiple file selection. (BR 105903) + * The browser-sidebar has been redesigned for improved usability. + * Cue file sheet support. Patch from Martin Ehmke . + (BR 92271). + * New OSD text token, %playcount, will write the playcount. + * SmartPlaylists are editable. (BR 91036) + * PlaylistBrowser gets a makeover! + * New playlist column "Playcount" for track play counts. + * New playlist column "Extension" allows easy sorting of playlist for + compatible file types for portable media players. + * Ability to save streams to the PlaylistBrowser (BR 91075, BR 104139) + * New DCOP call "playlist: popupMessage" Displays a popup message box + in the playlist window.. + * New "year - album" - group by mode for collection browser. (BR 94845) + * New DCOP call "player: setScoreByPath(url, int)". Sets score of a track + specified by it's path. + * New DCOP call "player: setScore(int)". Sets score of the current track. + * New DCOP call "player: path()". Returns the path of the current track. + * New DCOP call "playlist: saveM3u(path, relativePaths)". + * New ScriptManager notification: "volumeChange: int". + * Tooltips for album covers in the CoverManager. (BR 103996) + * Automatic download of themes and scripts via KNewStuff. + * Different analyzers available for the playlist window. + * New DCOP call "player: enableRepeatTrack" sets repeat track on or + off. + * HelixPlayer-engine. + * 'Load' and 'Append' entries for smart playlist context menus. (BR 99213) + * Support for reading embedded images from ID3 tags. (BR 88492) + * Wikipedia tab in ContextBrowser allows for artist biography retrieval + and more, supporting 9 different languages! (BR 98050) (BR 104383) + * Show "title by artist" on playlists titlebar and taskbar. (BR 97670) + * Option to show stats in the Home tab by album. Patch from Cédric + Brégardis . + * New DCOP call "script: listRunningScripts()". Returns a list of all + currently running scripts. (BR 102649) + * New DCOP call "script: stopScript(name)". Stops a script. (BR 102649) + * New DCOP call "script: runScript(name)". Runs a script. (BR 102649) + * New form of playlist manipulation - Dynamic Mode. + * New DCOP call "player: enableRepeatPlaylist" sets repeat playlist on or + off. (BR 102754) + * Add Score widget into the tag editor. (BR 100084) + * Support for PostgreSQL as database backend. (BR 99863) + + CHANGES: + * "amarokscript" filename extension is now mandatory for script packages. + * Append Suggestions has been superceded by Dynamic Mode. + * Add a label (with shortcut) to the Playlist filter. + + BUGFIXES: + * Message box when saving of playlist failed (BR 105520) + * Avoid weird results when fetching lyrics with slow connections. + (BR 103561) (BR 101327) + * Compensate for reversed slider widget in reverse layout locales, such as + Hebrew and Arabic. Patch from Assaf Gillat . + (BR 102978) + * Playlist playMedia now works with streams. + * Context Browser is updated when current track's tags are changed. + (BR 102839) + * Clearing the playlist while playing a track does not lead to a confusing + interface anymore. (BR 103510) + + +==BEGIN KDE 3.3 DEPENDENCY== + +VERSION 1.2.4: + FEATURES: + * Queue selected tracks shortcut, Ctrl+D. (BR 83675) + + BUGFIXES: + * The first engine entry in the config dialog was always blank. + * If you filtered by more than one word in Collection Browser, adding + expandable items (eg: artists or albums) wouldn't work. (BR 100150) + * Updating the collection without any changes being made to it kept + the Update button disabled forever. + * Application freezes when switching shoutcast streams. (BR 103890) + * MusicBrainz lookup was not escaping quote characters. (BR 103740) + * Fixed crash when clicking the "clear" button in CoverManager's filter + widget. + * Update lyrics page on new radio stream metadata. (BR 99725) + * xine-engine was reporting bogus tracklengths for ogg vorbis. (BR 102547) + + +VERSION 1.2.3: + FEATURES: + * Graphequalizer script can now enable and disable the equalizer. + * New DCOP call "player: equalizerEnabled" returns whether or not + the equalizer is enabled. + * OSD notification for mute. + * Mute global shortcut, Win+M. + * Add %comment token for comment display in OSD. (BR 100944) + * View/Edit track entry into context menus of ContextBrowser and + CollectionBrowser. + * You can mark/unmark albums as compilations via CollectionBrowser's + right-click contextmenu. + * New DCOP call "collection: query(const QString& sql)". + Allows to make arbitrary queries on the Collection database. + * New DCOP call "playlist: removeCurrentTrack()". (BR 92973) + + CHANGES: + * Show "Artist - Title" for compilation discs in CollectionBrowser + and ContextBrowser. + * Upgraded internal SQLite database to 3.2.0. + * DCOP call saveCurrentPlaylist() now returns the path to current.xml. + + BUGFIXES: + * Appropriate context menu entry for changing queue status for multiple + playlist items. + * Fix regression preventing dequeuing multiple selected tracks. + * 'Show Toolbar' remembers its settings between sessions. (BR 98662) + * When doing Musicbrainz lookup from the Context browser, search for the + real track, not the whole album. + * Memleak when a radio stream stalled. (BR 102047) + * The Collection Scan finally checks for the right file modification time. + * Adding a compilation disc from ContextBrowser was broken. + * GStreamer-engine: Reduced the gap when switching to next track without + crossfading. + * GStreamer-engine: amaroK was swallowing the beginning of a track when + Fade-in was set to zero. (BR 94472) + * Use a better highlight color in the "Configure Collection" dialog. + (BR 102059) + * "Remove Duplicates / Missing" fixed. Removes dead entries correctly. + * Fix units for samplerate. (BR 101528) + * amaroK using 100% CPU on some systems. (BR 101524) + (a KHTML bug which got exposed by code in amaroK 1.2.2) + + +VERSION 1.2.2: + FEATURES: + * Context Browser CSS styles can now be installed and selected from the + appearance settings. + * Append Suggestions now has an icon in the statusbar. + * When selecting multiple files, the "View/Edit Meta Information" dialog + will show the tags that are common to all of them. (BR 100423) + * A line graph equalizer added as a script "graphequalizer." + + CHANGES: + * Add 25-track and 50-track smart-playlists. + * Update current-track icons to include greater padding. + * The contextbrowser now uses data:-URLs instead of temp image files, so + they cannot be left on disk when amaroK terminates unexpectedly, and the + Konqueror/Universal sidebar can show them when amaroK is not running. + + BUGFIXES: + * escape '&' char in contextmenu entry (BR 101276) + * Track is set as a number in the database, so shouldn't be added rounded + by quotes. (BR 101208) + * Rewrote the broken .pls playlist parser. + * Handle delay gap between songs properly with aRts engine. (BR 90404) + * Switched order of "Make playlist" and "Queue after current track" menus + to avoid playlist destruction. (BR 96164 part 1) + * Visualizations with LibVisual didn't work in some cases. (BR 99627) + * amaroK could fail to build if the whole kdeextragear-1 module was + compiled, due to conflicts with K3B on the MusicBrainz check. (BR 100906) + * Images shown on OSD where incorrect for action notifications. + * The handbook translations were not built when amaroK was installed from + the tarball. I've written a new release script in Ruby, which can + handle the new structure of kde-i18n. (BR 100498) + * GStreamer-engine can now play vorbis radio streams properly, with + full metadata support. (BR 89821) + * GStreamer-engine now uses the "decodebin" autoplugger, which fixes + the lag issues that some users had during crossfading. (BR 99570) + + +VERSION 1.2.1: + FIX: Made the Tag-Editor only operate on visible items. (BR 100268) + ADD: Database settings added to the first-run wizard. + FIX: playlist2html generates UTF-8 output now. (BR 100140) + FIX: Bitrate/length showed random values for untagged mp3 files. (BR 100200) + FIX: Crash when recoding stream MetaData without CODEC selected. (BR 100077) + CHG: Show an additional "Compilations with Artist" box in ContextBrowser. + ADD: Remember collapse-state of boxes in ContextBrowser. (BR 98664) + ADD: Display an error when unable to connect to MySQL. + ADD: Konqueror Sidebar now has full drag and drop support. + CHG: Replaced "Blue Wolf" icon with Nenad Grujicic's amaroK 1.1 + icon, due to legal issues. + ADD: Parameter "%score" shows the current song's score in OSD. + CHG: When you delete a song within amaroK, it gets removed from + the Collection automatically. + FIX: Directory column in the playlist was eating the first letter. + ADD: New DCOP call "playlist: setStopAfterCurrent(bool)". (BR 99944) + FIX: Coverfetcher: Do not crash when no cover was found. (BR 99942) + ADD: Support for amazon.co.jp cover fetching + CHG: Toolbar items reordered for optimal usability, as suggested by + Aaron "Tom Green" Seigo. + FIX: Show covers for albums containing chars '#' or '?'. (BR 96971 99780) + ADD: Help file for the playlist2html script. + ADD: New DCOP call "playlist: int getActiveIndex()". + ADD: New DCOP call "playlist: playByIndex(int)". + CHG: Upgraded internal SQLite database to 3.1.3. + FIX: Update the database after editing tags in playlist. (BR 99593) + ADD: New DCOP function "player: trackPlayCounter". (BR 99575) + ADD: .ram playlist support with code from Kaffeine. (BR 96101) + FIX: amaroK can now determine the correct track-length even for formats + unknown to TagLib. Makes it possible to seek e.g. in m4a tracks. + ADD: Can now pick from multiple Musicbrainz results. Patch from + Jonathan Halcrow . (BR 89701) + ADD: May now set a custom cover on multiple albums in the Cover-Manager. + ADD: Support relative path of tracks in writing playlists. (BR 91053) + FIX: Don't inline-edit tags for the whole playlist's selection. + FIX: Fix "Recode Tags" crash issues. (BR 95041) + ADD: "Set Custom Cover" can fetch remote images. (BR 90499) + +VERSION 1.2: + ADD: "Repeat Track" status is reflected by an icon in the playlist. + ADD: New icons from tightcode for statusbar and repeatTrack. + ADD: New Smart-Playlist "Ever Played". + CHG: Bumped GStreamer version requirement to 0.8.4. + CHG: Made it possible to use artsdsink with GStreamer again. + CHG: Don't read m3u files recursively when dropping a folder on the + playlist. No more doubled entries. + FIX: Shoutcast radio with GStreamer is improved, no more dropouts when + starting a stream. + ADD: The "Similar Artists" feature (using Audioscrobbler) can now be + switched off. (BR 95280) + FIX: Error in Shoutcast http-request, which made it impossible to play + many radio streams with GStreamer and aRts. (BR 97211, 98569) + CHG: Better default directory for selecting a custom cover. + FIX: ContextBrowser reloads after setting a custom cover. (BR 96548) + FIX: Cover-Manager's full-screen view works with Bughira (brushed metal). + ADD: Script-Manager can auto-run scripts on application startup. + ADD: aKode engine, depends on KDE 3.4. No configure check yet. + FIX: Don't add non-audio files to the Collection. + CHG: We now use the SqlLoader, which greatly improves the performance of + adding stuff to the playlist from SmartPlaylists and the Collection. + +VERSION 1.2-beta4: + ADD: It is now possible to select the right image if there are multiple + results from Amazon. Patch from Gregory Isabelli . + (BR 93287) + CHG: Reorganized the DCOP interface. We used to have all DCOP functions in the + "player" group. Now it's splitted up into several categories. Attention + script writers: Adjust your DCOP calls! + FIX: The loader is now more robust and should always find amarokapp. + CHG: The search-browser has been integrated into the file-browser. + CHG: OSD can have fake transparency and new fancy shadow. + ADD: DCOP function "shortStatusMessage", shows a temporary message on the + application's statusbar. + FIX: Frequent crashes when writing tags. (BR 95344) + FIX: CoverManager updates its status display correctly. + FIX: "isPlaying" DCOP function now works correctly. (BR 90894) + ADD: Automatic crash report generator, sends backtraces to amaroK HQ. + ADD: DCOP function "saveCurrentPlaylist". Writes the playlist to current.xml, + for scripts that need to access the playlist contents. + ADD: Playlist2html, a script for playlist exporting. (BR 96199) + ADD: Improved statusbar, with animated error notification widget. + ADD: New progress display system, can show multiple expandable progress + widgets in the statusbar. + ADD: Alarm script, starts playing music at specified alarm time. + ADD: Script-Manager for DCOP script extensions is now functional. Refer to the + amaroK Wiki for information on script writing. + ADD: Collection-Browser shows a help message in flat-mode when filter is + empty. (BR 97000) + CHG: It is possible to select the Database Engine (SQLite, MySQL) runtime, + without amaroK restart. New Database Engines can be added, they need to + inherit DbConnection and implement its' virtual methods (see + SqliteConnection and MySqlConnection). + CHG: New amaroK icon "Blue Wolf", made by Da-Flow. + FIX: Possible crash when enabling Player-Window. (BR 94668) + +VERSION 1.2-beta3: + ADD: Smart Playlists can have a random order or a score weighted random order + (BR 90861) + ADD: Show total length of selected songs in statusbar. (BR 90284) + ADD: Context-Browser now caches the tab widgets. Patch from Matias Costa + . (BR 95999) + FIX: RAND and REP buttons were always enabled at startup. (BR 95861) + ADD: Implemented "Append Suggestions" functionality. It means that when + enabled, amaroK will append a couple of suggested songs to playlist when + you play a track. This produces a continuous playlist, something similar + to listening to radio. + ADD: Implemented "Play Media..." functionality. + FIX: Playlist-Browser was appending to playlist when clicking "Load". Now it + replaces the current playlist again, as intended. + ADD: Profile for KDELIRC (Remote Controls). Patch by Dirk Ziegelmeier + . + ADD: Remove Duplicates now also removes dead entries from playlist. + FIX: Accept album-dragging from the ContextBrowser. (BR 86020) + FIX: Configure check was missing for the Konqueror Sidebar (depends on + KDE-Base). + FIX: Browser splitter was drawn incorrectly with some styles. (BR 95333) + ADD: DCOP call for relative seek. Patch by Andreas Pfaller. (BR 84989) + CHG: Bumped TagLib dependency to 1.3.1. (1.3 is too damn buggy) + FIX: CTRL-M can show the menubar again after hiding. (BR 94139) + ADD: Support for last.fm streams. + FIX: amaroK icon shows correctly in window decoration under GNOME. + ADD: Support for ID3v2 cover images. (Thanks to M. Thiesen!) (BR 88492) + ADD: DCOP calls for the status of Random Mode, Repeat Playlist and Repeat + Track. + ADD: DCOP call to return the sample rate. + ADD: DCOP call to return the track number. (BR 94825) + FIX: GStreamer-engine provides better scope synchronisation. + ADD: Save current track position and play queue on exit. (BR 90379) + FIX: Fix Directory column on playlist, show absolute directory path instead of + empty string. (BR 90361) + ADD: DCOP call to scan your collection. (BR 84621) + FIX: When an engine fails to load, respect the rank while choosing the next + engine. + +VERSION 1.2-beta2: + FIX: Classic amaroK theme looks better. + ADD: Context Browser has CSS styling. + FIX: Cover fetching improvements/fixes. + ADD: Last played: yesterday, etc. in ContextBrowser. + FIX: Big speedup for PlaylistLoader, when adding many items. + ADD: Show songs you once played, but didn't play for the longest time on + ContextBrowser's Home-page. (least played) (BR 89479) + FIX: Don't crash on song switch, when there's only one visible playlist item + and repeat-list is activated. (BR 94030) + CHG: Add and queue tracks after the current track. (BR 94121) + ADD: DCOP call to raise the equalizer configuration dialog. + ADD: Konqueror sidebar to view playing info and control amaroK. + ADD: DCOP call to clear the playlist. (BR 90149) + ADD: DCOP call to enable/disable the equalizer. + ADD: DCOP call to return the score of the currently playing track. + ADD: Audioscrobbler submit queue stored on disk. Tracks that are listened when + offline will be available for submitting later. + CHG: "Start Scan" button was renamed to "Update". Now it starts an incremental + scan instead of a full rescan. + FIX: Lyrics parsing failed for certain songs. (BR 94269) + ADD: xine-engine saves config, and implements crossfade, bug fixed too. + ADD: Player-Window can also show the BlockAnalyzer. + CHG: Run incremental scanning once a minute instead of every 30 seconds. + FIX: When collection scanning was interrupted with Cancel, incremental + scanning didn't work any longer. + CHG: Handle incremental file scanning in a thread. Now the GUI doesn't get + blocked every 30 seconds, anymore. (BR 93564) + ADD: CollectionBrowser now offers two operation modes: + The classical TreeView and a new FlatView (like the WinAmp Library). + FIX: Caching of local cover images was broken for non-unique filenames. + (BR 94068) + FIX: "Visualizations" menu entry was always disabled. + FIX: Play button was sometimes stuck in disabled state. + FIX: OSD was showing "%artist - %track" instead of "%artist - %title". + FIX: Forward command line option --engine to amarokapp. + FIX: CoverFetcher was always looking for "album - album". + +VERSION 1.2-beta1: + ADD: Full support for Audioscrobbler, including submission of tracks. + FIX: Arts engine resumes from position when session is restored. + ADD: Vorbis stream metadata support (GStreamer-engine). (BR 82378) + ADD: Cover image and lyric fetchers include filters for common extensions, + such as (Disc 1). (BR 90630) + ADD: Ability to choose from four different Amazon locales. (BR 90664) + ADD: OSD now draws gradient instead of solid colour. + ADD: 'Stop after current song' functionality. (BR 88652) + FIX: Queue function from context/collection browsers actually properly queues + tracks. (BR 90319) + ADD: MySQL database support. Patch by Andreas Mair . + Please refer to mailing list for detailed instructions. + ADD: Metadata history for streams in Context-Browser. (BR 89839) + ADD: Command line option --engine. + ADD: OSD text is now configurable, and it displays the album cover. + FIX: Remote folders are read recursively when dropped on the playlist. + FIX: Audiocd protocol in filebrowser had empty folders. + ADD: Cache system for current-track animation in playlist. Reduces CPU load + when the playlist is visible. + ADD: 10-band IIR equalizer for GStreamer and xine engines. + FIX: The background gradient effect in Context-Browser is now much faster. The + gradient also looks nicer. (BR 91276) + FIX: Password-protected streams did not work correctly. (BR 91184). Patch by + . + ADD: NMM-engine was rewritten and updated for the latest NMM release. Supports + audio and video playback. + ADD: Cover-Manager supports drag-and-drop. + ADD: Tags are now read from the Collection database if they are already + stored. This speeds up adding items to the playlist. (BR 90137) + ADD: Context-browser shows "Suggested Tracks", utilizing audioscrobbler. + FIX: Configure does no longer print "Good - Configure has finished" when a + dependency is missing. + ADD: Intelligent automatic resize for playlist columns + ADD: Shaded current-track marker in playlist. + ADD: Automatic song lyrics display. + CHG: Internal SQLite upgraded to 3.0.8. + +VERSION 1.1.1: + FIX: Crash when using GStreamer-engine on 64bit. (BR 90869) + CHG: New splash screen by Nenad Grujicic . + FIX: Crash when fetching 1 missing cover using the fetch button. (BR 90673) + REM: Unsupported option "Show Metadata in Playlist". + ADD: Menubar (optional). + FIX: GStreamer-engine now resumes playback at correct position. + ADD: iCandy for Context-Browser: Background gradient and toolbar. + CHG: Collection-Browser now has a toolbar instead of menubar. + FIX: With "Title Streaming" disabled GStreamer could not play streams. + FIX: Osssink is now the default sink for GStreamer. If sink initialization + fails, a dialog will ask to select another sink. + FIX: Pausing failed on some systems with GStreamer-engine. (BR 90417) + FIX: Never scan the same directory twice. + FIX: Disable CD-burning menu for streams. (BR 90336) + ADD: Open Cover-Manager from Context-Browser popup-menu and main menu. + FIX: Made amaroK build with --disable-amazon flag. + FIX: Docs translations were not installed correctly. (BR 90307) + FIX: GStreamer-engine refused to play some mp3 files. (BR 90317) + +VERSION 1.1: + FIX: Huge speedup for Context-Browser, makes changing tracks faster. + ADD: Progress display for Cover-Manager. + CHG: Systray animation is now optional. + CHG: Updated included sqlite to 3.0.7 (stable). + ADD: Tag editor can operate on multiple files (mass tagging). + FIX: Collection encoding broken for non-latin1 characters. (BR 89747) + ADD: Popup-menu for cover images in Context-Browser. + FIX: The first track to play is now random for random-mode. (BR 77055) + FIX: Show systray on startup. (BR 89661) + FIX: Let xine recognise tracks that have non lower-case extensions. + +VERSION 1.1-beta2: + ADD: K3B integration for burning CDs. (BR 88052) + ADD: Third category for Collection-Browser. (BR 83609) + ADD: Playlist search now supports categories. (BR 86296) + ADD: Support for MAS (Media Application Server). MAS-engine + is in experimental state. + ADD: Context-Browser shows information about radio streams. + ADD: Custom Smart Playlists with built-in editor. + ADD: Systray icon shows track progress and play status. + CHG: Imported SQLite3 and ported CollectionDB. + ADD: "Cool-Streams", a list of amaroK Squad recommended streams for + playlist-browser. + ADD: Detecting Sampler/VA discs in CollectionBrowser (shown as + "Various Artists"). (BR 81683) + ADD: Configuration GUI for xine-engine. + ADD: Next and previous track buttons for Tag-Editor. + ADD: Player-window adapts to current color scheme. + ADD: Crossfading and fade-in/out function for GStreamer-Engine. + ADD: Genre and Favorite Tracks by Artist smart playlist in the + Playlist-Browser. + ADD: IMMS-like rating system for songs. + FIX: aRts-engine has been ported to the new engine interface and is + available again (but not recommended). + FIX: Try to autodetect Sampler-Discs and show them properly in the + Contextbrowser. (BR 87182) + FIX: Multiple items can now be selected in the CoverManager. + Thanks John Hughes (BR 87584) + FIX: Various fixes for certain Artist/Album names, which had problems + with cover support. + FIX: Sorting the collection is now case-insensitive. (BR 84141) + CHG: Symlink infinite recursion check for collection scan. + FIX: Show all accessible cover images in the tooltip. (BR 87283) + FIX: Clicking an album in the ContextBrowser adds items in the correct + order, now. (BR 87733) + +VERSION 1.1-beta1: + ADD: Wizard for configuring amaroK on first startup. + CHG: Made it possible to use the next/previous buttons when amaroK is + not playing. + ADD: DCOP call to switch Random Mode on or off. (BR 84460) + ADD: DCOP call to retrieve current track's cover image. (BR 85364) + FIX: Problem with cover-saving for certain artist/album names. (BR 84171) + FIX: Show contextual information for songs, even if they are not in the + current collection instead of an ugly empty box. + ADD: GstEngine: Support for custom output plugin parameters. (BR 83949) + ADD: CoverManager - for downloading and managing album cover images. + CHG: Refactored engine plugin interface. Each engine can now provide specific + configuration GUIs. + ADD: As-you-type search for FileBrowser. + ADD: Seeking with mousewheel in playerwindow. + REM: Stream-Browser. + ADD: New meta-info dialog, with editable tags and MusicBrainz support. + ADD: Inline-tag editing auto-completion based on the Collection Database. + ADD: Deleting files physically from playlist context menu. (BR 75208) + ADD: Fadeouts for GStreamer-Engine. + ADD: New Playlist Browser, organizes multiple playlists, and offers smart + playlist functionality. + ADD: Support for redirected streams and streams with no specified port. + ADD: KIO support for GStreamer engine. Allows playing media via all + protocols supported by KIO (ftp, audiocd, fish, etc). + ADD: SearchBrowser operation can now be aborted. + ADD: Progressbar in CollectionBrowser informs about scan progress, and a + button was added for aborting the scan. (BR 83019) + ADD: Playlist sliders (volume and position) now move directly when clicked + outside of the handle. (BR 83611) + ADD: Untagged tracks now go into Collection too, listed as "unknown". + ADD: Automatic album cover fetching is back and improved. + ADD: Option for automatically switching to Context when playback is started. + CHG: Stream timeout value is now determined from KDE user settings. + ADD: Support for password-protected streams, by wef . + FIX: GStreamer engine must not allow non-audio filetypes in playlist. + ADD: Icon for "Menu" button in toolbar. Improves Usability. + +VERSION 1.0.2: + ADD: xine-engine plugin, audio only. + FIX: aRts-engine: Compatibility with newer aRts versions improved. + FIX: aRts-engine: Streams sometimes stopping shortly after playback was + started. (BR 84417) + CHG: Increased stream connect timeout to 12 seconds. + +VERSION 1.0.1: + FIX: Short dropouts after starting a stream with GStreamer. + FIX: amaroK starting invisible when systray icon is disabled. + FIX: Playlist analyzer looks freaky on some systems. (BR 83671) + FIX: Display filename in title column for wav files. (BR 83650) + FIX: Don't show crash dialog when no engine plugins are found. + FIX: Compile issue for KDE < 3.2.1 users. Sorry :( + +VERSION 1.0: + FIX: Plugin versions are validated. Prevents crashes with ancient plugins. + FIX: Configure now checks for gtk/gdk headers for the XMMSwrapper. + REM: Removed cover download feature for this release. + FIX: Do not crash if an unreadable dir is added to the collection. + FIX: Check database-sanity on startup and recreate broken tables (BR 83205). + FIX: CollectionBrowser was broken, when amaroK was running "localized". + FIX: TitleProxy hogging 100% CPU when unable to connect to server. + CHG: Bumped GStreamer requirement to 0.8.1. + ADD: Glowing player window icons. + ADD: amaroK finally remembers if it was hidden on exit. + ADD: OSDPreview now has snap to regions. + FIX: Newly shown columns in playlist can now be resized. + FIX: BR 82020: next/prev buttons disabled when they shouldn't be. + ADD: ToolbarAnalyzer remembers it's framerate, allowed fps: {50, 40, 30, 20}. + ADD: Full streaming audio support for GStreamer engine. + FIX: Don't allow user to get into a situation where there is no Menu. + ADD: Using Welcome-page power-links you can switch between XMMS and amaroK mode. + CHG: New icons and splash screen, by Roman Becker . + ADD: Allow the current GL analyzer to be detached/attached from the + main window with the 'd' key. + FIX: Filtering the collection now searches the second category, too (BR 81681). + FIX: Filter in playlist was only working for the first argument. + CHG: Collection-Monitor now processes removed dirs in a thread. + ADD: Added a switch to toggle OSD's text-shadow. (BR 82011). + ADD: More detailed track information dialog for Collection Browser. + FIX: Track length was always 0 for certain filetypes (e.g. mod, wav) (BR 82673). + FIX: Gst engine refusing to add certain filetypes to the playlist, when + the engine was idle (BR 82713). + FIX: Rare playlist redraw bug, which resulted in messed up items. + +VERSION 1.0-beta4: + ADD: CollectionDB now caches and rescales images. This binds cover art usage + in amaroK to the collection, but offers greatly improved speed for cover + retrieval and uses less memory. + FIX: Cover not shown in ContextBrowser, when song gets played for the first + time ever (BR 81241). + ADD: Cover art fetcher, downloads album cover images from amazon.com. + ADD: Configure->Playback->Device && default device option for audiosinks. + ADD: ContextBrowser now also shows your overall-favorites and the newest tracks + in your collection. Therefor I had to reset the statistics, sorry. + FIX: Decode %-encoded characters in filenames, like %2f for a slash. (BR 74576). + CHG: Songs you click in ContextBrowser will now directly start to play and won't + be added to the playlist, if they already are there. + FIX: "Start Scan" menu-entry gets disabled while scanning. (BR 81619). + FIX: Directories with non-ascii chars don't get scanned (CB) in multibyte locales. + CHG: Enhanced "Fill-Down" feature for track column (auto-increment) (BR 81194). + FIX: Closing xmms-visualizations freezes amaroK (BR 81326). + FIX: CollectionBrowser does not sort by tracknumber (BR 79600). + FIX: ContextBrowser's URLRequests need to be escaped. + FIX: Always show OSD (if enabled) on volume changes. + FIX: Filtering the collection using tokens with number(s) at the beginning + or end failed. (BR 81621). + FIX: FileBrowser didn't remember its current folder (BR 81816). + ADD: Expand/collapse items by doubleclicking in Collection (BR 81710). + FIX: Allow OSD still to be shown via shortcut when disabled (BR 80388). + FIX: Collection: live-monitoring dirs for changes works again. + FIX: Changing volume by mousewheel on systray icon works again. + ADD: Collection automatically rescans itself on startup. + ADD: "Add to Playlist" feature in CollectionBrowser, appends tracks to playlist. + ADD: Clear button for CollectionBrowser search. + FIX: Problem with invisible "Play next" marker in playlist. + FIX: Don't try to create sql-tables on every startup, but only on + sql-scheme (DATABASE_VERSION) changes. + FIX: Display splash screen on correct desktop with Xinerama. + CHG: CollectionBrowser filter now works in "search-as-you-type" mode. + FIX: Prevent TitleProxy from showing the same metadata over and over. + FIX: Compatibility bugfixes to TitleProxy, thanks to Daniel Molkentin + . I think we've now got 100% Shoutcast compatibility. + ADD: Allow changing volume by using the mousewheel anywhere on the toolbar. + FIX: Wheel-scrolling toolbar's volume slider doesn't change volume (BR 81155). + FIX: ContextBrowser is now shown in proper colors for every scheme. + CHG: Added track's physical location to the Meta Information dialog. + FIX: Show last playtime in localtime instead of UTC. + FIX: ContextBrowser not showing all items for current album. + FIX: Not all SQL queries were "string-escaped". + ADD: Added statistics database, which keeps track of how often and when you play + a specific song. + +VERSION 1.0-beta3: + ADD: Additional volume slider for playlist window. + ADD: ContextBrowser shows you images and information to the current song/artist. + It depends on the collection and is presented as an HTML widget. + CHG: Improved color handling and visual feedback in the GUI. + ADD: Global shortcut for play/pause action, as requested by multimedia-keyboard + users (BR 79541). + CHG: Small player-window can be switched off now. + FIX: CollectionBrowser out of order after scanning. + FIX: TitleProxy partly rewritten. Should be more compatible with many streams + and not be able to freeze the app any longer. + FIX: When playing a stream with title streaming activated, the track is not + marked as playing (BR 79999). + FIX: Invoking "Track Information" in Collection Browser sometimes crashed + the application (BR 80266). + FIX: In CollectionBrowser's folder setup dialog pressing cancel did not abort + (BR 80451). Thanks to Michael Pyne for patch. + ADD: Option for selecting sound output system (OSS/Alsa). Currently only + used with GStreamer engine. + CHG: Extended and updated handbook, thanks to Mike Diehl . + ADD: Context menu item "Make Playlist" in Collection Browser generates new + playlists on the fly, without the need for drag-and-drop. + CHG: Renamed several files and folders in the source code tree, resulting in + improved code accessibility. + +VERSION 1.0-beta2: + FIX: Crash on AMD64 due to assumption about pointer size. + CHG: SQLite library sourcecode now included with amaroK. + CHG: The collection-thread now inserts its data in a temporary database while + scanning, which allows us to safely use the collection in the meantime. + This is done by two concurrent sqlite-connections (thread-safe). Wrote a + new class named CollectionDB, which handles the database communication + for the collection. + ADD: URLDrag from Playlist, so you can drag and drop to xmms. Doesn't work with + the FileBrowser yet, but it will! + CHG: CollectionBrowser now fills the database inside of a thread, resulting in + improved performance. + ADD: Mini track-position slider in statusbar. + FIX: Don't try to crossfade with engines that do not support this feature. + ADD: XMMS visualization plugins can be configured with their GUI. + FIX: Collection filtering had some regressions + FIX: Loader on some systems not able to start amaroK. + FIX: Switching engines at runtime breaking volume control. + FIX: GstEngine skipping tracks directly after starting, when crossfading enabled. + CHG: Database system now works with linked tables. Saves hdd-space and cpu-time. + CHG: If you remove the current song from the playlist, we don't define the next + song anymore, but let it be randomly selected (only when random mode is on!) + CHG: Random Mode now respects the playlist filter and only picks items, which are + currently visible in the playlist. Also removed a crash situation. + CHG: Removed the search-token index. Searching now iterates through the playlist, + offering direct and specific access to the metadata. + FIX: Bug where fill-down would cause lots of extra tags to be written when a search is + in progress (BR 79482). + FIX: Defect in plugin framework code, leading to a crash on some systems + during engine plugin initialization. + FIX: Restoring current playlist on startup (BR 79436, BR 79439). + ADD: Searching the Collection with a filter. + FIX: BrowserWin's QLabels are painted white in amaroK's own color scheme. + +VERSION 1.0-beta1: + ADD: Search Browser - search stuff on your hdd + ADD: song count on playlist statusbar + ADD: support for XMMS visualization plugins + ADD: Collection Browser - a database powered music collection manager + ADD: Playlist toolbar is now configurable + ADD: toolbar analyzer in playlist window + ADD: use XML playlists internally within amaroK so tags don't have to be + loaded/reloaded all the time. Makes undo/redo much quicker. + FIX: non latin1 locale issues with loading directories and tags (thanks Leo Zhu) + ADD: clicking shuffle will sort the playlist by the nextQueue first, and + randomise the rest + ADD: Play Next can now handle several songs through a queue. The queue can be + manipulated by using the context menu or by CTRL+right clicking. + ADD: much improved gstreamer engine, now working with visualizations + CHG: GstEngine requires gstreamer-0.8 + FIX: Show move pointer instead of hand when moving preview OSD. + ADD: sorting by artist subsorts by album and track, sorting by album subsorts + by track, enjoy! + ADD: browserTabs float over the playlist when in set to not overlap + FIX: communication loader<-->amarok failing on FreeBSD + FIX: loader forgetting to close socket descriptors + FIX: FileBrowser remembers that state of its view between sessions + CHG: converted engines to plugins. they are now dynamically loaded at runtime + ADD: plugin framework + CHG: made amaroK aRts-independent. with the --without-arts configure switch + it's possible to build the app without aRts support, using only NMM or GST + ADD: Shift drag appends items to the end of the playlist. + FIX: startup notification icon staying on screen when amaroK started by loader + FIX: amaroK showing the "X" icon instead of the correct one + +VERSION 0.9: + CHG: playlistBrowser removed until next release + FIX: playerWidget font is now configurable, you need to start new track for the + scrolling marquee to get updated. Default font is used by default. + FIX: fixed several stability issues concerning stream-playback + ADD: whatsthis for all configurable options. + FIX: amaroK registering with dcop as "amarok-PID". it's back to just "amarok" now. + FIX: OSD not updating correctly when changing volume + +VERSION 0.9-beta3: + ADD: "Show Current Track" button in playlist. + ADD: Volume OSD when changing with mousewheel over trayicon. + CHG: software volume mixer uses a logarithmic function to make the scale more natural + ADD: Global shortcuts to display OSD and increase/decrease volume. + (Win+o and Win+KP_Add/KP_Subtract by default, respectively) + ADD: DCOP calls to control OSD and playback volume + ADD: ported config-GUI for audio decoders to new engine (works currently with + modplug_artsplugin) + FIX: show correct track-length when playing .mod or .sid with aRts-engine + ADD: loader application, starts and controls amaroK. it reduces the lag when handing + command line arguments to amaroK and makes the splash load faster + ADD: playlist items, which couldn't be opened / read (for some reason) will be marked + with a grey background color + ADD: pasting clipboard selection into playlist with MidButton, X11-style + CHG: refined on-screen-display with more polished look + FIX: skipping broken/non-existant tracks + CHG: If the current song is paused, the Play Button will resume, not restart it. + FIX: respect "hide playlist with main window" and playlist minimize/hide behaviour. + ADD: new OSD configuration options: bgcolor, screen position + +VERSION 0.9-beta2: + CHG: some look-and-feel polishing in the main player window + ADD: option to turn off analyzers + ADD: splash-screen shown during program startup (optional) + FIX: made stream playback with TitleProxy more stable (by using an unbuffered socket) + ADD: show stream metadata in on-screen-display + CHG: transformed "EQ" button into a togglebutton, which can also hide the effect browser + ADD: new OpenGL analyzer, contributed by Enrico Ros + FIX: FreeBSD compile fixes, contributed by Markus Brueffer + FIX: rewritten configure: checks properly for kdemultimedia presence, + and adds --without-opengl and --without-gstreamer arguments + +VERSION 0.9-beta1: + ADD: display warning when artsd is not running with realtime priority + ADD: Audioproperties are loaded as you scroll the playlist and get saved to playlist files + ADD: If trackname column is hidden, the title column will show the trackname until a title + tag can replace it. If no title tag is found the trackname stays. + CHG: Pressing "back" in Random Mode now works as expected and walks backwards + through the list of recently played songs. + ADD: TitleProxy searches for a free local port (contributed by Stefan Gehn) + CHG: Random Mode now stores the recently played songs in a buffer, which prevents + playing the same songs too often. + ADD: "Play Next" context menu option + ADD: selected aRts-effects will be remembered on next program start, including settings + FIX: sort numerical playlist columns in correct order + ADD: logarithmic fading algorithm makes crossfading smoother + ADD: Select a series of tracks, start inline tag-editing a tag and amaroK will prompt you to + edit that tag for all tracks one-by-one. Also available: fill-down. + ADD: improved crossfading: will fade out smoothly when the stop button is pressed + FIX: O(n) behavior for playlist scrolling fixed + ADD: setting to make playlist colours the KDE defaults + ADD: support for tag-editing directly in playlist + CHG: replaced old FileBrowser with the comfortable fileselector from KDevelop + CHG: analyzers now powered by a new, more flexible FFT routine + ADD: hide/show selected playlist columns + CHG: upgrade streambrowser to kderadiostation 0.5 + FIX: many streams not loading from browser and AddItem dialog + CHG: amaroK moved out of kdenonbeta. we are now member of KDE Extra Gear 1 + ADD: on-screen-display (OSD), shows an overlay with information on the currently playing track + CHG: use KMultiTabBar for browser selection + CHG: migrated settings system to KConfig XT + ADD: playlist columns for length and bitrate + ADD: merged new audio engine in. this provides a generic interface class, with multiple + backends. right now there is a backend for aRts and one for GStreamer (still rudimentary) + +==BEGIN KDE 3.2 DEPENDENCY== + +VERSION 0.8.3: + FIX: build issue + +VERSION 0.8.2: + ADD: added Hide/Show Playlist global shortcut (thanks gogo) + CHG: mousewheel over trayicon behaviour changed + CHG: search tokens can now be entered in random order + ("Presley Elvis" will find "Elvis Presley") + FIX: qt 3.1 compile issues + +VERSION 0.8.1: + FIX: compilation problem with KDE < 3.1.3 + +VERSION 0.8.0: + FIX: KDE 3.1 compatibility re-gained + ADD: hitting return in the search field of the playlist starts playback of the + first visible playlist entry (Qt >=3.2 only) + FIX: fixed crash bug in playlist searching + FIX: fixed crash bug when removing playlist-items + CHG: new layout has been adopted + ADD: added accepting files dropped onto systray icon + FIX: significant reduction in memory consumption for PlaylistItems + FIX: hardware mixer works again + CHG: replaced sliders with custom slider class, which fits better in our design + FIX: exchanged c32-app-amarok.png with the correct (active) version + FIX: amarok.desktop file. now we show up in the k-menu again. + FIX: crossfading aRts module. the fading is now much smoother than before + FIX: crossfading bug. before the fix amaroK sometimes mixed up the two xfade sources, + so it sort of faded in reverse (==crap) + ADD: tag reading in separate thread + ADD: re-added m_optCrossFade, so we don't lose the crossfade length on switching it on/off. + set default crossfade length to 2500. + CHG: "Title Streaming" on by default + CHG: integrated streambrowser into playlist window + ADD: added dcop implementation for url adding. Relevant diffs for mediacontrol are + available. + FIX: libamarokarts detection code + ADD: added long-awaited DCOP methods for manipulating the playback. This also adds + integration with kdeaddons/kicker-applets/mediacontrol. + CHG: moved DCOP handler to a separate class/file + ADD: threaded playlist insertion + FIX: removed bugs and waste code keyhandling in browser*, it mostly works as expected + now with various keypresses going to the correct places + FIX: cleaned the playlist class's public interface, also fixed some unreported bugs in + process (inconsistent recursive behavior), please keep the encapsulation, it's a + good thing (tm) + FIX: tweaked undo/redo behavior + CHG: exchanged old player icons with new ones made by + Alper Ayazoglu a.k.a. cubon + ADD: clicking on EQ button activates effect selection widget + ADD: KJanusWidget as a sidebar for filebrowser mode selection + FIX: pushing enter in lineedit goes up a level + ADD: a stream browser, can only DnD, separate window, not great yet + FIX: finally fixed the ancient "annoying-noise-when-pressing-pause" bug + FIX: should keep track of currently played item no matter what you do to the playlist, + has a nice side effect of remembering the last played song, too. + FIX: write undo for Shuffle + FIX: the expandbutton doesn't fire events when it has had its stack expanded + (behaviour a-la Winamp Classic) + FIX: crash when pressing right mouse button while stream is connecting + ADD: show bitrate for streams with icecast support + FIX: save stream names as #EXTINF in m3u files + ADD: bug report dialog + ADD: proxy for decoding shoutcast/icecast metadata (experimental!) + ADD: amaroK now in bugs.kde.org + ADD: configurable delay after each track. currently 0-10 seconds in 1 sec increments + but could easily be made to use finder increments if ppl want - piggz (www.piggz.co.uk) + ADD: viswidgetv2. it seems a lot smoother on my machine. + its quite easy to tweak the dynamics is needed. is accessible the same as the other + widgets, just click until it appears (though it looks the same as the original widget + it just acts differently) - piggz (www.piggz.co.uk) + ADD: combo with history and completion for dir/file chooser + ADD: in configure.in.in for checking the version of TagLib, if compiled from CVS, if not, + then show, that it uses bundled version of TagLib - Stormy + FIX: font dialog sizing issues + ADD: resume playback option. Using this means your track starts up again where you left it + last time you quit amaroK. Excellent feature for us developers :-) + +VERSION 0.7.0: + FIX: collection of fixes related to showing/raising/hiding the playlist + when showing/raising/hiding the mainWidget + FIX: by muesli: make playlist searches a bit faster at the expense of memory + FIX: (partial fix) bitrate/samplerate font overlap at large font sizes + change: less staccato loading of widgets + change: pause makes the analyser bars fall to zero rather than just vanish + ADD: xfade when starting tracks by doubleclick + FIX: global shortcuts can now be changed + FIX: tracks skipping randomly + change: "BrowserWin Enabled" on by default + change: "Save Playlist" on by default + change: "Show Metainfo" on by default + FIX: make loading playlist not block UI + FIX: on startup load playlist after UI is shown + change: "Software Mixer Only" on by default + FIX: make timedisplay also work for streams + FIX: volume slider adjusting + FIX: when dropping tracks to PL, order will stay the same as in FileBrowser + ADD: FileBrowser sortable by clicking on header + ADD: analyzer that distorts a bitmap + ADD: multiple analyzers now possible + ADD: "Software Mixer Only" option + Removed stale sigplay() + Cleaned a couple "deprecated" warnings + ADD: undo and redo playlist actions + FIX: rewritten config dialog and moved into separate file + ADD: started configurable colors + change: spectrum analyser bars now have dynamics, ie. they move smoothly between values + ADD: mouse wheel over systray icon changes the track, hold shift to change the volume + change: rearranged menu order for systray (quit = last) + change: moved volume slider to the right, lets see if this is better + ADD: started a font selection page in settings + FIX: Stream urls are now properly demangled/unescaped (%20 => space etc) + +VERSION 0.6.91: + FIX: ExpandButton submenu now slightly delayed + FIX: dropping items into playlist + ADD: drop-target indicator line in PlaylistWidget, providing visual feedback + ADD: tray menu + ADD: random mode + ADD: crossfading between tracks + ADD: vertical lines between columns in Playlist + ADD: alternating item colors in Playlist + ADD: column "directory" in PlaylistWidget (for Grue:) + ADD: sorting by clicking on column headers in PlaylistWidget + FIX: rewrote directory reading code in BrowserWidget.cpp. + code is now much more readable, and it also fixes a bug. + ADD: additional columns in playlist for tags + FIX: made metainfo reading algorithm faster + change: switched to TagLib for metainfo reading + ADD: button "play" in PlayerWidget.cpp is now a toggleButton + ADD: tray icon + FIX: playlist window is optionally hideable with main widget when iconified to tray + +VERSION 0.6.0: + Release :) + +VERSION 0.6.0-PRE5: + fixed: animated buttons don't get stuck anymore + fixed: invoking help + changed: MetaInfo reading now off by default. the slowdown was potentially + confusing to new users + added: documentation + fixed: cleaned up Makefile.am a bit + fixed: defined new APP_VERSION macro, since the old approach did not work + with CVS + changed: put amarok into KDE CVS (KDENONBETA) + added: applied Stormchaser's button patch. the AmarokButtons now work + in a more standard conform way. Thanks Stormchaser, blessed be :) + +VERSION 0.6.0-PRE4: + added: buttons in playlist window for play, pause, stop, next, prev. + a.k.a. stakker mode :) + removed: "load" button. this functionality is now provided by "Add item" + added: more sanity checks on pointers + fixed: when track in playlist does not exist, we now skip to the next track + fixed: all aRts references are freed correctly at program exit + fixed: effects will not be forgotten any more when EffectWidget is closed + +VERSION 0.6.0-PRE3: + fixed: crash when URLs were dropped onto filebrowser from other apps + fixed: URL dialog now accepts remote files + added: correct caption for ArtsConfigWidget + added: "amaroK Handbook" menu entry, calling KHelpCenter + changed: amarok gets installed into multimedia now + fixed: PlayObject configuration + +VERSION 0.6.0-PRE2: + changed: safety question at program exit now off by default + removed: button "sub" - it was useless + changed: clearing playlist does not stop playing anymore - for Grue ;) + fixed: potential crash at startup + added: menu option to configure PlayObject + fixed: crash when removing currently playing track + +VERSION 0.6.0-PRE1: + fixed: flicker in glowing item + fixed: another memory leak in analyzer (hopefully the last one!) + added: playlist widget can display metainfo instead of filenames + added: repeat track / repeat playlist + +VERSION 0.5.2 - 0.5.2-DEV6: + fixed: memory leak in analyzer code. + added: shortcut for copying current title to the clipboard + added: slider position can be changed by just clicking somewhere on the slider + added: icon + added: url can be entered directly above the filebrowser widget + changed: removed the "jump" widget. you can now enter a filter string + directly above the playlist widget + added: playlists (.m3u and .pls) can now directly be dragged into the playlist + added: support for .pls (audio/x-scpls) + added: amarok is now completely network-transparent. any kind of folder, + local as well as remote, can be browsed and played. + added: check for libamarokarts. amarok won't crash anymore if it's not found + added: the time display now has a mode for showing the remaining time, too + fixed: crash when clearing playlist, after playlist has played till the end. + clearing the playlist stops the playing now. + added: new gfx in playerwidget + fixed: progressbar sometimes not working, zero tracklength + fixed: font of bitrate/frequency display too big on some systems + added: command line options + added: timedisplay is now updated during seeks + added: saving window positions and size on exit + added: due to popular request, I finally changed the behaviour of the "play" + button. it's now possible to start a track on a fresh playlist without + double-clicking an item. + fixed: compile error on GCC 3.3.1 in visQueue.cpp. bugfix by thiago + added: completely rewrote drag-and-drop code. works recursively now (optionally). + plus dragging stuff from other applications into amaroK also works now. + +VERSION 0.5.1: + added a Tip of the Day at startup to explain the user interface a bit + added restarting of artsd on first program start to make sure it registers + the new mcopclasses + fixed possible compile error in viswidget.cpp + amaroK uses much less CPU now than it used to. This was mainly achieved by + using a new FFT-analyzer module, which I took from Noatuns "Winskin"-plugin, + and modified slightly to my needs. Also some other optimizations were made, + which improved the standby performance, when no song is playing. I'm still + not satisfied with overall performance, tho, but it seems that most of the + load is produced by the aRts code itself, so this will rather be difficult + to improve. + fixed crash when "next" or "previous" was pressed without a track + loaded + thanks to valgrind I was able to find and squish some serious bugs, + most of which were related to pointers. to sum it up: pointers are evil. + valgrind is great. + lots of UI-changes in the main widget. uses a background pixmap now, a + custom font and widget for the time-display, and generally looks better + fixed issues with the liquid skin. unfortunately, there seems to be no way + to display pushbuttons correctly with a black background under liquid. so, + until I find a solution for that, the expandbutton widget doesn't look quite + as cool as it used to. maybe I should ask mosfet about this.. + +VERSION 0.50: + renamed 0.15 to 0.50 + +VERSION 0.15: + playing streams now works! *yipeeee* + fixed tons of bugs in aRts playing code. i think i got it right now. + fixed loading and saving of playlists. can cope with all protocols now. + fixed a bug in EffectWidget.cpp, that gave a compile error on some systems. + Converting QString into std::string was not done correctly. Thanks to + Whitehawk Stormchaser for that one :) + changed project name to "amaroK" and built new project-file + +VERSION 0.14 (internal): + implemented use of arts-software-mixing, in case hardware-mixing + (/dev/mixer) doesn't work + fixed crash when play was pressed without selecting a file + changed the direction of the volume-slider. maximum is now at the top + added automatic saving of current playlist on exit + added previous/next track + added two radiobuttons in the playerwidget for toggling the + playlist/equalizer on and off. admitted, the equalizer doesn't yet exist, so + it's just a dummy button :P + added popup-menu for the playerwidget. opens on + right mouse button. this menu finally replaces the ugly menubar. + added some icons (from noatun) for the player-buttons instead of text + added pause function + changed most names in the source to comply with the + (unofficial?) KDE c++ coding standard (using the prefix "m_" for member + attributes and so on). This was real slave-work :/ + cleaned up code in several classes + fixed problem where subwidgets got keyboard focus and were drawn dark with + the liquid style. switched off focus completely, since it's not needed for + this type of application + +VERSION 0.13 (internal): + added cute animated pushbuttons with sub-menus + added saving playlists + added dragging items inside of playlist widget + added forward declarations in header files to reduce compile time + added saving of browserwin/splitter size + rewrote track information widget. used a html table for the text. looks much + nicer now :) + fixed sorting function + fixed jump widget. removed huge memory leaks in the widget + fixed flicker in analyzer widget + tons of bugfixes in playing code. partly rewritten. seems to be much more + stable now + +VERSION 0.12 (internal): + added ChangeLog and TODO + added grid under scope display + added saving of options, like current directory and playlist + added detection of mimetypes + added adjusting volume by mousewheel + added skipping to next track after playing + added loads of sanity/safety checks + bugfixes (tons of) in playlist code, partly rewritten + bugfixes in scope code + + +VERSION 0.1 - 0.11: + internal versions, no changelog + tried no less then 4 different sound interfaces: + mpg123, smpeg, alsaplayer, and finally aRts diff --git a/ConfigureChecks.cmake b/ConfigureChecks.cmake new file mode 100644 index 00000000..7f59dcfb --- /dev/null +++ b/ConfigureChecks.cmake @@ -0,0 +1,34 @@ +include(CheckIncludeFile) +include(CheckIncludeFiles) +include(CheckSymbolExists) +include(CheckFunctionExists) +include(CheckLibraryExists) +include(CheckPrototypeExists) +include(CheckTypeSize) +include(MacroBoolTo01) + +# The FindKDE4.cmake module sets _KDE4_PLATFORM_DEFINITIONS with +# definitions like _GNU_SOURCE that are needed on each platform. +set(CMAKE_REQUIRED_DEFINITIONS ${_KDE4_PLATFORM_DEFINITIONS}) + +#check for libz using the cmake supplied FindZLIB.cmake +macro_bool_to_01(ZLIB_FOUND HAVE_LIBZ) +macro_bool_to_01(JPEG_FOUND HAVE_LIBJPEG) +macro_bool_to_01(PNG_FOUND HAVE_LIBPNG) +macro_bool_to_01(CARBON_FOUND HAVE_CARBON) +macro_bool_to_01(NJB_FOUND HAVE_LIBNJB) +macro_bool_to_01(IFP_FOUND HAVE_IFP) +macro_bool_to_01(LIBVISUAL_FOUND HAVE_LIBVISUAL) +macro_bool_to_01(MTP_FOUND HAVE_MTP) + +#now check for dlfcn.h using the cmake supplied CHECK_include_FILE() macro +# If definitions like -D_GNU_SOURCE are needed for these checks they +# should be added to _KDE4_PLATFORM_DEFINITIONS when it is originally +# defined outside this file. Here we include these definitions in +# CMAKE_REQUIRED_DEFINITIONS so they will be included in the build of +# checks below. +set(CMAKE_REQUIRED_DEFINITIONS ${_KDE4_PLATFORM_DEFINITIONS}) +if (WIN32) + set(CMAKE_REQUIRED_LIBRARIES ${KDEWIN32_LIBRARIES} ) + set(CMAKE_REQUIRED_INCLUDES ${KDEWIN32_INCLUDES} ) +endif (WIN32) diff --git a/INSTALL b/INSTALL new file mode 100644 index 00000000..934f158f --- /dev/null +++ b/INSTALL @@ -0,0 +1,185 @@ +Installing Amarok +================= + +In order to compile and install Amarok on your system, type the following in the +base directory of the Amarok distribution: + + + % ./configure --prefix=`kde-config --prefix` + % make + % make install + + +Note: --enable-final is not supported. + + +The GNU installation instructions follow. + + +Basic Installation +================== + + These are generic installation instructions. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, a file +`config.cache' that saves the results of its tests to speed up +reconfiguring, and a file `config.log' containing compiler output +(useful mainly for debugging `configure'). + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If at some point `config.cache' +contains results you don't want to keep, you may remove or edit it. + + The file `configure.in' is used to create `configure' by a program +called `autoconf'. You only need `configure.in' if you want to change +it or regenerate `configure' using a newer version of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. If you're + using `csh' on an old version of System V, you might need to type + `sh ./configure' instead to prevent `csh' from trying to execute + `configure' itself. + + Running `configure' takes a while. While running, it prints some + messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Type `make install' to install the programs and any data files and + documentation. + + 4. You can remove the program binaries and object files from the + source code directory by typing `make clean'. + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. You can give `configure' +initial values for variables by setting them in the environment. Using +a Bourne-compatible shell, you can do that on the command line like +this: + CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure + +Or on systems that have the `env' program, you can do it like this: + env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure + +Compiling For Multiple Architectures +==================================== + + You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you must use a version of `make' that +supports the `VPATH' variable, such as GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + If you have to use a `make' that does not supports the `VPATH' +variable, you have to compile the package for one architecture at a time +in the source code directory. After you have installed the package for +one architecture, use `make distclean' before reconfiguring for another +architecture. + +Installation Names +================== + + By default, `make install' will install the package's files in +`/usr/local/bin', `/usr/local/man', etc. You can specify an +installation prefix other than `/usr/local' by giving `configure' the +option `--prefix=PATH'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +give `configure' the option `--exec-prefix=PATH', the package will use +PATH as the prefix for installing programs and libraries. +Documentation and other data files will still use the regular prefix. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + + Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Specifying the System Type +========================== + + There may be some features `configure' can not figure out +automatically, but needs to determine by the type of host the package +will run on. Usually `configure' can figure that out, but if it prints +a message saying it can not guess the host type, give it the +`--host=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name with three fields: + CPU-COMPANY-SYSTEM + +See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the host type. + + If you are building compiler tools for cross-compiling, you can also +use the `--target=TYPE' option to select the type of system they will +produce code for and the `--build=TYPE' option to select the type of +system on which you are compiling the package. + +Sharing Defaults +================ + + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Operation Controls +================== + + `configure' recognizes the following options to control how it +operates. + +`--cache-file=FILE' + Use and save the results of the tests in FILE instead of + `./config.cache'. Set FILE to `/dev/null' to disable caching, for + debugging `configure'. + +`--help' + Print a summary of the options to `configure', and exit. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`--version' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`configure' also accepts some other, not widely useful, options. + diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 00000000..ef742941 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,5 @@ +AUTOMAKE_OPTIONS = foreign 1.5 + +include admin/deps.am +include admin/Doxyfile.am +SUBDIRS=$(TOPSUBDIRS) diff --git a/Makefile.am.in b/Makefile.am.in new file mode 100644 index 00000000..49558690 --- /dev/null +++ b/Makefile.am.in @@ -0,0 +1,4 @@ +AUTOMAKE_OPTIONS = foreign 1.5 + +include admin/deps.am +include admin/Doxyfile.am diff --git a/Makefile.cvs b/Makefile.cvs new file mode 100644 index 00000000..be59a869 --- /dev/null +++ b/Makefile.cvs @@ -0,0 +1,14 @@ +all: + @echo "This Makefile is only for the CVS repository" + @echo "This will be deleted before making the distribution" + @echo "" + @if test ! -d admin; then \ + echo "Please recheckout this module!" ;\ + echo "for cvs: use checkout once and after that update again" ;\ + echo "for cvsup: checkout kde-common from cvsup and" ;\ + echo " link kde-common/admin to ./admin" ;\ + exit 1 ;\ + fi + $(MAKE) -f admin/Makefile.common cvs + +.SILENT: diff --git a/README b/README new file mode 100644 index 00000000..6d66e381 --- /dev/null +++ b/README @@ -0,0 +1,223 @@ + + Amarok - the audio player for KDE +=================================== + +There are many media players around these days, true. What's missing from most +players is a user interface that doesn't get in the way of the user. How many +buttons do you have to press for simply adding some new tracks to the playlist? +Amarok tries to be a little different, providing a simple drag and drop +interface that really makes playlist handling easy. + + + FEATURES +========== + + * Quick and simple drag and drop playlist creation + * Music library (built-in SQLite, MySQL, or PostgreSQL) + * Multiple backends supported (xine, Helix, and NMM) + * Plays all audio formats known to man + * 10 band equalizer + * Automatic cover art download using Amazon services + * The unique and powerful stylable context browser + * Automatic play-statistics generation (iRate style) + * Full lyrics download + * Learn about your music with integrated Wikipedia + * Full last.fm support + * Visualisations with libvisual + * Crossfading and gapless playback + * Fully configurable OSD for track changes + * K3B (CD-burning) integration + * Podcast support + * Access to iPod, iRiver IFP, USB Mass Storage and many other devices + * Powerful scripting interface + * Complete DCOP Access + * KDE integration + * Preview and buy albums from the Magnatune.com music store +-------------------------------------------------------------------------------- + + + DEPENDENCIES +============== + +Required + * KDE-Libs 3.3 (or newer) + http://www.kde.org + + * TagLib 1.4 (or newer) + (metadata tagging library) + http://freshmeat.net/projects/taglib + + * Ruby 1.8 + (programming language, used for scoring, lyrics, last.fm streams) + http://www.ruby-lang.org + + * One of the multimedia frameworks listed below: + +Recommended + * xine-lib 1.0.2 (or newer) + Note: xine-lib 1.1.1 is required for gapless playback. + (multimedia framework) + http://xinehq.de/ + +Optional + * RealPlayer 10 or HelixPlayer 1.0 + (multimedia framework) + http://www.real.com + (Note: only HelixPlayer is exactly RealPlayer without MP3 support) + + * KDE-Base 3.3 (or newer) + (needed for Konqueror Sidebar) + http://www.kde.org + + * MySQL 4 or 5 + (faster database support) + http://www.mysql.com + + * PostgreSQL 7.4 + (faster database support) + http://www.postgresql.org + + * OpenGL accelerated X-Server + (visualization rendering) + + * Libvisual 0.4.0 + SDL 1.2 + (visualization framework) + http://localhost.nl/~synap/libvisual/ + http://www.libsdl.org + + * ProjectM 0.96 (or newer) + (visualization plugins for Libvisual or XMMS) + http://xmms-projectm.sourceforge.net/ + + * libtunepimp 0.3 (or newer) + (automatic tagging support) + http://www.musicbrainz.org/ + + * K3B 0.11 (or newer) + (CD burning support) + http://www.k3b.org + + * libgpod 0.4.2 (or newer) + (iPod support) + Note: libgpod 0.6.0 is required for the newest Apple iPods. + http://www.gtkpod.org/libgpod.html + + * libifp 1.0.0.2 + (iRiver iFP support) + http://ifp-driver.sourceforge.net/libifp/ + + * libmp4v2 (mpeg4ip 1.5 is recommended, faad2 is less reliable) + (MP4/AAC tag reading & writing) + http://www.sf.net/projects/mpeg4ip + http://www.audiocoding.com + + * libnjb 2.2.4 (older versions may work) + (NJB mediadevice (Creative Nomad/Zen family, Dell DJ devices) + http://www.sf.net/projects/libnjb + + * libmtp 0.1.1 (or newer) + (MTP media device support AKA PlaysForSure) + http://libmtp.sourceforge.net/ + + * libkarma 0.0.5 && OMFS 0.6.1 + (Rio Karma support via USB) + http://freakysoft.de/html/libkarma/ && http://linux-karma.sf.net/ + +Please note, if compiling from source you must also install the devel versions +of these packages. +-------------------------------------------------------------------------------- + + + IMPORTANT INSTALL INSTRUCTIONS +================================ + +In order to compile and install Amarok on your system, type the following in the +base directory of the Amarok distribution: + + + % ./configure --prefix=`kde-config --prefix` + % make + % make install + + +Note: --enable-final is not guaranteed to work + + +Packages for popular distributions are available at http://amarok.kde.org +-------------------------------------------------------------------------------- + + + INSTALLATION-FAQ +================== + +Q: Can I improve Amarok's startup time? +A: Prelinking Amarok has spectacular results; however if you have binary openGL + drivers (eg Nvidia drivers), you will need to compile Amarok --without-opengl + in order to get the amarokapp binary to prelink (the amarok binary is not + important here). +-------------------------------------------------------------------------------- + + + OTHER-FAQS +============ + +For answers to problems like "Amarok won't play any MP3s!" and "My MP3s skip +and stutter!" please visit: + + http://amarok.kde.org/ +-------------------------------------------------------------------------------- + + + INFORMATION FOR PACKAGERS +=========================== + +For Amarok packages we suggest you build: + + % ./configure --disable-debug + +It is possible to build Amarok to use MySQL as the database backend. Using +MySQL makes the Amarok collection faster. + +We suggest compiling Os, there is no particular part of Amarok that would +benefit from optimisation, so the smallest binary is probably the best route. + +In order to limit the dependencies the Amarok package demands we suggest +splitting Amarok into the following packages: + + 1. Amarok + one backend + 2. xine-engine + 3. Helix-engine + 4. amarok_libvisual + 5. ipod media device + 6. ifp media device + 7. njb media device + 8. mtp media device + 9. rio karma media device + +Amarok is modular and will be fully functional as long as one of 2 or 3 is +also installed. Hence we suggest Amarok + one backend. Feel free to include the +helix, MAS and NMM engines if you can satisfy their dependencies. + +Amarok ships with two binaries: amarok and amarokapp. The amarok binary is a +wrapper designed to speed up command line argument passing. amarokapp is the +real Amarok. + +If you make packages for Amarok please let us know and we'll link to you on the +homepage (as long as you don't object). +-------------------------------------------------------------------------------- + + + CONTRIBUTING +============== + +If you wish to contribute to Amarok, you should build it from SVN and subscribe +to the amarok-devel mailing list. The IRC channel is also a place where +it's nice to be, since you can talk to other developers much easier, and +get instant notification of commits to the SVN. For instant email notification +of commits, visit http://commitfilter.kde.org/. +-------------------------------------------------------------------------------- + + +WWW : http://amarok.kde.org +MAIL: amarok@kde.org +IRC : irc.freenode.net - #amarok, #amarok.de, #amarok.es diff --git a/TODO b/TODO new file mode 100644 index 00000000..f03e5158 --- /dev/null +++ b/TODO @@ -0,0 +1,297 @@ +TODO-list for Amarok +====================== + == reported by +-->nick == assigned to + + +SHORT-TERM (URGENT): + When using "Copy files to collection" in the filebrowser, tags wont get recognised from + urls which are organised from media:/ locations. + + When playing a stream from e.g. Ampache, even if an Artist is detected, Wikipedia + will pull up "Artist - Song" instead of just Artist. + + PlaylistBrowser::slotDoubleClicked() and PlaylistBrowser::addSelectedToPlaylist() + should be merged for better code reusage. They are practically the same. + + I just deleted a tag by accident: Clicked on a file in the playlist, pressed + delete to remove the track from the playlist. I had not noticed that it + went into edit mode, though. So I clicked somewhere else, and BAM, the new + empty tag was written. My tag was gone :S IMHO we should require confirmation + before writing, e.g. only write the tag after pressing enter. + + When you start a lastfm stream, the old metadata from the last stream is + shown before the new stream starts. Doesn't look nice. + + update podcast-tuples when the feed get's corrected after a mistake by the podcaster/publisher. eg. pubdate, url, title. + + We need to check for pkg-config when configuring, otherwise we can't check for things + like libgpod. Currently its a silent error, and its very difficult to debug without + trawling through configure.in.in + + Don't reload media device plugins when saving config settings. Its annoying, slow + and all around unnecessary. It also crashes if the device is connected/loaded. + Cannot reproduce anything of this with ipods - i think i fixed that already. + + After doing "Delete downloaded podcast", the icon still indicates downloaded. + + LastFm: + - determine how hard it would be to tag songs + - display history in the playlist somehow I'm against this. + -subitems? + -just disabled? + - 'stop after current track' should work + - when you've never listenend to lastfm before, all stream links on the + website are disabled ("Download the player"). we need to do a handshake on + startup once, and then set a config item, e.g. handshaked=true. + + xine-engine: when "xine could not init any audio driver" happens, always retry + with Autodetect. This seems to be an issue with leftover amarokrc's from 1.3. + + Adding an m3u with non existant items has filesize = 16,777,216.0 TB! + + Refactor code for PlaylistBrowser::removeSelectedItems(). I can see pathological cases + where it won't work. + + Is using the score for the number, but the rating for the vis. in the context browser + confusing? (I think it is, but that might be becuase my theme doesn't use stars but a + partially-filled bar). If so it should be reverted for beta 1, I have a better solution + already planned but that won't be in time. + + The various UI and behaviour refactorings for Dynamic Mode should be finished up and + polished. Among them, add a button to turn it off in the bar-above-the-playlist, fix + drag-and-dropping of dynamic playlists, and when loading a dynamic playlist, clear the + existing playlist first, instead of making it dynamic (which has all sorts of nasty side + effects). + + Podcast Fixme's: + * Have i listened to podcast streamed? + + Loading normal (M3U) playlists from the playlist browser doesn't block the UI per se, + but it does nothing for really long time, until suddenly the whole playlist shows up, + which is strange and confusing. + + Tooltips don't work for 3rd level items in the Playlist Browser. They do how + however work (for me) for 2nd level items. Very bad, cause you can't read the + text! + + When starting a drag in the collection browser, we can block the gui if the user makes a + selection which is sql intensive (eg, select all). Instead of calculating the items + in each of those nodes, we should add the sql to the drag and allow the playlist loader + to do the work for us (which is threaded). + See SqlLoader class in playlistloader. We do the very same stuff for + Smart-Playlists already. + + The unknown album should ALWAYS be shown last. Year has a higher weighting than album + name. We should special case the Unknown album. + + When all 5 browser tabs are activated, the current tab button is drawn a few + pixels below the normal position. This has the side effect that the separator line + at the bottom of the bottom becomes invisible. + You can see the problem easily when you hide the current browser by clicking twice + the button twice. This problem AFAIK also affects 1.3-branch. + + Add DCOP functions for showing dialogs, similar to kdialog. + + More consistent playlistbrowser drag and drop actions; No dropping of default streams, set + e->accept( false ) if dropping one type onto another. + + Use KIO queues for podcast download queues. Should simplify the lot. Problems atm included + selecting multiple downloads individually, and automatic downloads occuring for more that one + podcast item. + + In EngineController::play(), add a sanity check when querying the track length + from the engine. Apparently AAC can deliver ridiculous values with GST and Helix, + like 1192479:38:49. If length is too extreme, default to the TagLib provided + value. + + Currently custom dynamic playlist selection is simply based on the titles. So its less then + ideal. Via some method, each playlist should be assigned iteratively a number paired with its + type.. Dynamic playlists and static playlists should probably have seperate numbering so + that the "next" number can be determined by looking at one XML file. + Remembering which PartyEntry was used on close should be remembered with the same method. + + Smart playlists bypass checks on whether the media is playable or not. + Or perhaps such media shouldn't be in the collection at all? This is the sort of + thing we're going to have to worry about now that we have m4a and wma tag support. + + Consider moving the GHNS provider list to a different server than amarok.kde.org, + possibly download.kde.org or similar. (When our site is down, the GHNS feature fails + to work entirely) + + Amarok fails to download any stream playlist from http://dir.xiph.org. Part of the problem is + PlaylistLoader::isPlaylistFile(const KURL&), which only looks at the filename's extension. This + fails for URLs like http://dir.xiph.org/listen.php?pid=669641&file=listen.m3u, because the filename + is "listen.php". So it must look at the whole url instead. + + On first-ever-run, if Amarok crashes building the sqlite collection, it is impossible to _ever_ + build a collection unless the user's amarokrc file is deleted! This MUST be fixed before 1.2! + ::muesli:: We could just _always_ try to create the sql tables on startup. This would fail on + ::muesli:: systems which already got the tables, but would fix the situation where tables are + ::muesli:: missing, as in this situation. Just pump the debug of these calls to /dev/null. + + Just before we load any engine plugin, write to a file, and it we don't succeed the next time + Amarok starts show a big warning saying "Gah!" and load a different one. + + If you inline set the title tag to "", prettyTitle gets inserted as the title tag. + + When the KDE color scheme is changed while Amarok is running, all toolbar buttons (those with text) + lose their texts. + + The scrobbler code should report some sort of error message rather than just saying that the + submit failed. That way when last.fm goes down we wouldn't get so many people asking why they + can't submit. It'd also be nice to be able to tell people they've got their password wrong. + + When hovering the mouse over the Repeat/Random icons in the bottom taskbar, the popups that appear + not only have inconsistent icons with the ones shown in the taskbar, but they're pixellated and ugly + as sin. Should probably be redone as SVGs. + + Xine proxy value is not saved across sessions. Problem exists in Xine-UI as well, at least for me. + Maybe an upstream bug and nothing we can fix. + +MID-TERM: + Implement a "Find My Music" button in the first-run wizard. Just a basic + thing that will look at file extensions and try to figure out where the users + music is located. Ignore /opt/, /usr etc. to avoid adding sound effects. Prompt + user with the directories at the end of the scan, allowing them to exclude some. + + The FileBrowser doesn't check for recursive symlinks. This is a KDirOperator (or the + classes it depends on) problem. + + Basically need to re-add some of the functionality I removed in re-doing dynamic + --> custom playlist creation. + 1) the default dynamic playlists need to be more insistent, the same way the + the default smart playlists are. + Implement leinir's mockup: + www.leinir.dk/temp/gallery/?image=pictures/amarok-playlist-bar-dynamic-mode.png + The idea with the look of the top is, as you can probably see from the colours + otherwise present, that it's the active window titlebar/colour/font, with the name of the + playlist being un-bolded (if the window title is bold)... + eean: Some people will identify the active window by titlebar colour alone :) + + Album covers as icons from filebrowser. + + When the player window shades, it should lose its title bar and be replaced with something more + compactish. This would probably have to be created manually. + + Make the player window scroll the text in the opposite direction if using right-2-left text + (eg hebrew or arabic) + + Use Metabar's box retraction animation to the context browser. More iCandy. Any Javascript experts around? + + Should be able to open collection browser by right clicking on stuff in contextB, so show the + artist or album that is right clicked. + + AudioCD support for gst engine (cdparanoia ! *sink). -->markey + + We should cut down on unnecessary/redundant debug output/warnings (noise). + + Perhaps a nice idea: when you drag playlistItems to a branch in the collection browser + it prompts to change the track's tags, so drag a track to the "moolaaa" album and that + track get's it's album tag changed to "moolaaa" and is inserted into the branch, so the user + know's what has happened. + + User asked for a CTRL-J itty-bitty dialog that allows you to jump to a specific part + of a track. I'll see if I can do it with tiny amount of code -->mxcl + + Don't rely on KDE timeouts to see whether a file is accessible or not, + since this really sucks for disconnected network shares. XMMS handles + this way better and it really is a problem in userland. maybe a thread + helps, which simply tries to fopen() the file. if this task hasn't been + finished in (let's say) 3 seconds, jump to the next song. XMMS even + remembers such files and their folders, so that it's not going to open + another file from that folder for the next few minutes. what about hdd + sleep-timeouts? opinions? + + Determine some behavior for the clear/shuffle/etc. buttons when a search is in action + clear -> only clear the stuff that was searched for? + shuffle -> stop the search and do normal shuffle? + ADDitionally what to do when user rearranges a set of items that are the search result? + No big deal to me as long as the behaviour is consistent. I.e. if shuffle shuffles + the search, then clear should clear the search only, and visa versa. + + Make playlist toolbar buttons toggle like the playerwidget buttons. + + Toolbar button in FileBrowser for switching recursive directory reading. + + Can't resize newly displayed columns if they are hidden at beginning of session -->mxcl + + Add an option "clean up playlist on startup". + Do you mean remove duplicates or dead entries etc. ? Cool. + + Option: don't crossfade for sequential tracks on same album. Comments? -->mxcl + Of course, there would still be the up to 150ms gap, but we can fix that some other time.. + Please clarify: is this the same as BR #75388 ? + No, this is just to make crossfading not ruin album transitions + + Add dirs to combo history when user adds a track from that dir to the playlist. + + "Show playlist" to the right click menu, redundant but necessary. + + More KTips, better KTips, somehow use the "Did you know" tip dialog (eg kmail, gideon, etc.) -->mxcl + + Option to automatically adjust column widths. + + Option to implicitly sort playlist by { track, album } (on drop only) -->mxcl + + Playmode indication button in main widget (repeat track/pl/shuffle), clickable. + + +LONG-TERM: + + Support multiple collections. This could be really powerful, and could be handled + with the collection browser. We could support non-local collections, audio cd/dvds, + NFS/Samba etc. This would also allow us to retrieve tracks from other networked pcs. + Auto-polling of added collections for a 'hot-sync' style detection of collections. + + Use more accurate interpolation for analyzers (cubic or spline) + + Tabbed playlists. + Pro: it's convenient to have several playlists + Contra: the playlist is getting cluttered enough as it is! + I already added a comment wrt this to the b.k.o bug, but here's the idea: + switch between playlists with the playlist browser -- that's what it's there for. + This avoids the clutter. The way it'd work is the currently playing playlist would + have the same fancy fading thing the current track has. The context menu for playlists + would have two seperate items, one for showing and one for playing. When just showing, + the previous playlist would continue playing while you view/edit the other one. + (If you doubleclick a song in the new playlist, it would naturally start playing that + one instead). What to do when the user doubleclicks a playlist -- show, play, or both + -- is TBD. + + Make windows magnetic / sticking together (difficult). + + Implement beat detection (thread?), interface should glow/move to + the beat, visualisations have access to beat/bpm info. + + Audio system info widget, showing all available codecs and similar info. + + Resizable playerwidget, like in Winamp3. + +IDEAS: + + Bookmarks inside of tracks (good for very long tracks), and nifty bookmark browser + + Using filelight (as a kpart maybe) for a graphical representation of the playlist. so + you could see at first glance how the altogether playing time is divided into different + albums, tunes and so on. + + +DO-NOT-IMPLEMENT (stuff that was rejected): + + Allow removing of playlist items by dragging back into browser. + I think this is really weird. When I drag something I expect that someting to be + ADDed or opened in the target. Not removed from the sender. + (RFC: is this still appropriate or would it be misleading?) + imho, it's misleading and not hid-compatible. i would rather + expect that file to be copied to the browser's current directory. + + "Hide playlist when main widget is not active" option (?? comments please: ) + Noo, we have it hiding into tray, thats enough (imagine how much flashing will be + if i drag mouse around with "focus follows mouse" on - this is the one i use all the time) + Hence it's an option, you'd not use it with focus follows mouse. But it was just an idea + anyway.. dunno if I like the sound of it anymore either. + + +BACKTRACES/DEBUG/VALGRIND: + diff --git a/acinclude.m4 b/acinclude.m4 new file mode 100644 index 00000000..6b26319f --- /dev/null +++ b/acinclude.m4 @@ -0,0 +1,11945 @@ +## -*- autoconf -*- + +dnl This file is part of the KDE libraries/packages +dnl Copyright (C) 1997 Janos Farkas (chexum@shadow.banki.hu) +dnl (C) 1997,98,99 Stephan Kulow (coolo@kde.org) + +dnl This file is free software; you can redistribute it and/or +dnl modify it under the terms of the GNU Library General Public +dnl License as published by the Free Software Foundation; either +dnl version 2 of the License, or (at your option) any later version. + +dnl This library is distributed in the hope that it will be useful, +dnl but WITHOUT ANY WARRANTY; without even the implied warranty of +dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +dnl Library General Public License for more details. + +dnl You should have received a copy of the GNU Library General Public License +dnl along with this library; see the file COPYING.LIB. If not, write to +dnl the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +dnl Boston, MA 02110-1301, USA. + +dnl IMPORTANT NOTE: +dnl Please do not modify this file unless you expect your modifications to be +dnl carried into every other module in the repository. +dnl +dnl Single-module modifications are best placed in configure.in for kdelibs +dnl and kdebase or configure.in.in if present. + +# KDE_PATH_X_DIRECT +dnl Internal subroutine of AC_PATH_X. +dnl Set ac_x_includes and/or ac_x_libraries. +AC_DEFUN([KDE_PATH_X_DIRECT], +[ +AC_REQUIRE([KDE_CHECK_LIB64]) + +if test "$ac_x_includes" = NO; then + # Guess where to find include files, by looking for this one X11 .h file. + test -z "$x_direct_test_include" && x_direct_test_include=X11/Intrinsic.h + + # First, try using that file with no special directory specified. +AC_TRY_CPP([#include <$x_direct_test_include>], +[# We can compile using X headers with no special include directory. +ac_x_includes=], +[# Look for the header file in a standard set of common directories. +# Check X11 before X11Rn because it is often a symlink to the current release. + for ac_dir in \ + /usr/X11/include \ + /usr/X11R6/include \ + /usr/X11R5/include \ + /usr/X11R4/include \ + \ + /usr/include/X11 \ + /usr/include/X11R6 \ + /usr/include/X11R5 \ + /usr/include/X11R4 \ + \ + /usr/local/X11/include \ + /usr/local/X11R6/include \ + /usr/local/X11R5/include \ + /usr/local/X11R4/include \ + \ + /usr/local/include/X11 \ + /usr/local/include/X11R6 \ + /usr/local/include/X11R5 \ + /usr/local/include/X11R4 \ + \ + /usr/X386/include \ + /usr/x386/include \ + /usr/XFree86/include/X11 \ + \ + /usr/include \ + /usr/local/include \ + /usr/unsupported/include \ + /usr/athena/include \ + /usr/local/x11r5/include \ + /usr/lpp/Xamples/include \ + \ + /usr/openwin/include \ + /usr/openwin/share/include \ + ; \ + do + if test -r "$ac_dir/$x_direct_test_include"; then + ac_x_includes=$ac_dir + break + fi + done]) +fi # $ac_x_includes = NO + +if test "$ac_x_libraries" = NO; then + # Check for the libraries. + + test -z "$x_direct_test_library" && x_direct_test_library=Xt + test -z "$x_direct_test_function" && x_direct_test_function=XtMalloc + + # See if we find them without any special options. + # Don't add to $LIBS permanently. + ac_save_LIBS="$LIBS" + LIBS="-l$x_direct_test_library $LIBS" +AC_TRY_LINK([#include ], [${x_direct_test_function}(1)], +[LIBS="$ac_save_LIBS" +# We can link X programs with no special library path. +ac_x_libraries=], +[LIBS="$ac_save_LIBS" +# First see if replacing the include by lib works. +# Check X11 before X11Rn because it is often a symlink to the current release. +for ac_dir in `echo "$ac_x_includes" | sed s/include/lib${kdelibsuff}/` \ + /usr/X11/lib${kdelibsuff} \ + /usr/X11R6/lib${kdelibsuff} \ + /usr/X11R5/lib${kdelibsuff} \ + /usr/X11R4/lib${kdelibsuff} \ + \ + /usr/lib${kdelibsuff}/X11 \ + /usr/lib${kdelibsuff}/X11R6 \ + /usr/lib${kdelibsuff}/X11R5 \ + /usr/lib${kdelibsuff}/X11R4 \ + \ + /usr/local/X11/lib${kdelibsuff} \ + /usr/local/X11R6/lib${kdelibsuff} \ + /usr/local/X11R5/lib${kdelibsuff} \ + /usr/local/X11R4/lib${kdelibsuff} \ + \ + /usr/local/lib${kdelibsuff}/X11 \ + /usr/local/lib${kdelibsuff}/X11R6 \ + /usr/local/lib${kdelibsuff}/X11R5 \ + /usr/local/lib${kdelibsuff}/X11R4 \ + \ + /usr/X386/lib${kdelibsuff} \ + /usr/x386/lib${kdelibsuff} \ + /usr/XFree86/lib${kdelibsuff}/X11 \ + \ + /usr/lib${kdelibsuff} \ + /usr/local/lib${kdelibsuff} \ + /usr/unsupported/lib${kdelibsuff} \ + /usr/athena/lib${kdelibsuff} \ + /usr/local/x11r5/lib${kdelibsuff} \ + /usr/lpp/Xamples/lib${kdelibsuff} \ + /lib/usr/lib${kdelibsuff}/X11 \ + \ + /usr/openwin/lib${kdelibsuff} \ + /usr/openwin/share/lib${kdelibsuff} \ + ; \ +do +dnl Don't even attempt the hair of trying to link an X program! + for ac_extension in a so sl; do + if test -r $ac_dir/lib${x_direct_test_library}.$ac_extension; then + ac_x_libraries=$ac_dir + break 2 + fi + done +done]) +fi # $ac_x_libraries = NO +]) + + +dnl ------------------------------------------------------------------------ +dnl Find a file (or one of more files in a list of dirs) +dnl ------------------------------------------------------------------------ +dnl +AC_DEFUN([AC_FIND_FILE], +[ +$3=NO +for i in $2; +do + for j in $1; + do + echo "configure: __oline__: $i/$j" >&AC_FD_CC + if test -r "$i/$j"; then + echo "taking that" >&AC_FD_CC + $3=$i + break 2 + fi + done +done +]) + +dnl KDE_FIND_PATH(program-name, variable-name, list-of-dirs, +dnl if-not-found, test-parameter, prepend-path) +dnl +dnl Look for program-name in list-of-dirs+$PATH. +dnl If prepend-path is set, look in $PATH+list-of-dirs instead. +dnl If found, $variable-name is set. If not, if-not-found is evaluated. +dnl test-parameter: if set, the program is executed with this arg, +dnl and only a successful exit code is required. +AC_DEFUN([KDE_FIND_PATH], +[ + AC_MSG_CHECKING([for $1]) + if test -n "$$2"; then + kde_cv_path="$$2"; + else + kde_cache=`echo $1 | sed 'y%./+-%__p_%'` + + AC_CACHE_VAL(kde_cv_path_$kde_cache, + [ + kde_cv_path="NONE" + kde_save_IFS=$IFS + IFS=':' + dirs="" + for dir in $PATH; do + dirs="$dirs $dir" + done + if test -z "$6"; then dnl Append dirs in PATH (default) + dirs="$3 $dirs" + else dnl Prepend dirs in PATH (if 6th arg is set) + dirs="$dirs $3" + fi + IFS=$kde_save_IFS + + for dir in $dirs; do + if test -x "$dir/$1"; then + if test -n "$5" + then + evalstr="$dir/$1 $5 2>&1 " + if eval $evalstr; then + kde_cv_path="$dir/$1" + break + fi + else + kde_cv_path="$dir/$1" + break + fi + fi + done + + eval "kde_cv_path_$kde_cache=$kde_cv_path" + + ]) + + eval "kde_cv_path=\"`echo '$kde_cv_path_'$kde_cache`\"" + + fi + + if test -z "$kde_cv_path" || test "$kde_cv_path" = NONE; then + AC_MSG_RESULT(not found) + $4 + else + AC_MSG_RESULT($kde_cv_path) + $2=$kde_cv_path + + fi +]) + +AC_DEFUN([KDE_MOC_ERROR_MESSAGE], +[ + AC_MSG_ERROR([No Qt meta object compiler (moc) found! +Please check whether you installed Qt correctly. +You need to have a running moc binary. +configure tried to run $ac_cv_path_moc and the test didn't +succeed. If configure shouldn't have tried this one, set +the environment variable MOC to the right one before running +configure. +]) +]) + +AC_DEFUN([KDE_UIC_ERROR_MESSAGE], +[ + AC_MSG_WARN([No Qt ui compiler (uic) found! +Please check whether you installed Qt correctly. +You need to have a running uic binary. +configure tried to run $ac_cv_path_uic and the test didn't +succeed. If configure shouldn't have tried this one, set +the environment variable UIC to the right one before running +configure. +]) +]) + + +AC_DEFUN([KDE_CHECK_UIC_FLAG], +[ + AC_MSG_CHECKING([whether uic supports -$1 ]) + kde_cache=`echo $1 | sed 'y% .=/+-%____p_%'` + AC_CACHE_VAL(kde_cv_prog_uic_$kde_cache, + [ + cat >conftest.ui < +EOT + ac_uic_testrun="$UIC_PATH -$1 $2 conftest.ui >/dev/null" + if AC_TRY_EVAL(ac_uic_testrun); then + eval "kde_cv_prog_uic_$kde_cache=yes" + else + eval "kde_cv_prog_uic_$kde_cache=no" + fi + rm -f conftest* + ]) + + if eval "test \"`echo '$kde_cv_prog_uic_'$kde_cache`\" = yes"; then + AC_MSG_RESULT([yes]) + : + $3 + else + AC_MSG_RESULT([no]) + : + $4 + fi +]) + + +dnl ------------------------------------------------------------------------ +dnl Find the meta object compiler and the ui compiler in the PATH, +dnl in $QTDIR/bin, and some more usual places +dnl ------------------------------------------------------------------------ +dnl +AC_DEFUN([AC_PATH_QT_MOC_UIC], +[ + AC_REQUIRE([KDE_CHECK_PERL]) + qt_bindirs="" + for dir in $kde_qt_dirs; do + qt_bindirs="$qt_bindirs $dir/bin $dir/src/moc" + done + qt_bindirs="$qt_bindirs /usr/bin /usr/X11R6/bin /usr/local/qt/bin" + if test ! "$ac_qt_bindir" = "NO"; then + qt_bindirs="$ac_qt_bindir $qt_bindirs" + fi + + KDE_FIND_PATH(moc, MOC, [$qt_bindirs], [KDE_MOC_ERROR_MESSAGE]) + if test -z "$UIC_NOT_NEEDED"; then + KDE_FIND_PATH(uic, UIC_PATH, [$qt_bindirs], [UIC_PATH=""]) + if test -z "$UIC_PATH" ; then + KDE_UIC_ERROR_MESSAGE + exit 1 + else + UIC=$UIC_PATH + + if test $kde_qtver = 3; then + KDE_CHECK_UIC_FLAG(L,[/nonexistent],ac_uic_supports_libpath=yes,ac_uic_supports_libpath=no) + KDE_CHECK_UIC_FLAG(nounload,,ac_uic_supports_nounload=yes,ac_uic_supports_nounload=no) + + if test x$ac_uic_supports_libpath = xyes; then + UIC="$UIC -L \$(kde_widgetdir)" + fi + if test x$ac_uic_supports_nounload = xyes; then + UIC="$UIC -nounload" + fi + fi + fi + else + UIC="echo uic not available: " + fi + + AC_SUBST(MOC) + AC_SUBST(UIC) + + UIC_TR="i18n" + if test $kde_qtver = 3; then + UIC_TR="tr2i18n" + fi + + AC_SUBST(UIC_TR) +]) + +AC_DEFUN([KDE_1_CHECK_PATHS], +[ + KDE_1_CHECK_PATH_HEADERS + + KDE_TEST_RPATH= + + if test -n "$USE_RPATH"; then + + if test -n "$kde_libraries"; then + KDE_TEST_RPATH="-R $kde_libraries" + fi + + if test -n "$qt_libraries"; then + KDE_TEST_RPATH="$KDE_TEST_RPATH -R $qt_libraries" + fi + + if test -n "$x_libraries"; then + KDE_TEST_RPATH="$KDE_TEST_RPATH -R $x_libraries" + fi + + KDE_TEST_RPATH="$KDE_TEST_RPATH $KDE_EXTRA_RPATH" + fi + +AC_MSG_CHECKING([for KDE libraries installed]) +ac_link='$LIBTOOL_SHELL --silent --mode=link ${CXX-g++} -o conftest $CXXFLAGS $all_includes $CPPFLAGS $LDFLAGS $all_libraries conftest.$ac_ext $LIBS -lkdecore $LIBQT $KDE_TEST_RPATH 1>&5' + +if AC_TRY_EVAL(ac_link) && test -s conftest; then + AC_MSG_RESULT(yes) +else + AC_MSG_ERROR([your system fails at linking a small KDE application! +Check, if your compiler is installed correctly and if you have used the +same compiler to compile Qt and kdelibs as you did use now. +For more details about this problem, look at the end of config.log.]) +fi + +if eval `KDEDIR= ./conftest 2>&5`; then + kde_result=done +else + kde_result=problems +fi + +KDEDIR= ./conftest 2> /dev/null >&5 # make an echo for config.log +kde_have_all_paths=yes + +KDE_SET_PATHS($kde_result) + +]) + +AC_DEFUN([KDE_SET_PATHS], +[ + kde_cv_all_paths="kde_have_all_paths=\"yes\" \ + kde_htmldir=\"$kde_htmldir\" \ + kde_appsdir=\"$kde_appsdir\" \ + kde_icondir=\"$kde_icondir\" \ + kde_sounddir=\"$kde_sounddir\" \ + kde_datadir=\"$kde_datadir\" \ + kde_locale=\"$kde_locale\" \ + kde_cgidir=\"$kde_cgidir\" \ + kde_confdir=\"$kde_confdir\" \ + kde_kcfgdir=\"$kde_kcfgdir\" \ + kde_mimedir=\"$kde_mimedir\" \ + kde_toolbardir=\"$kde_toolbardir\" \ + kde_wallpaperdir=\"$kde_wallpaperdir\" \ + kde_templatesdir=\"$kde_templatesdir\" \ + kde_bindir=\"$kde_bindir\" \ + kde_servicesdir=\"$kde_servicesdir\" \ + kde_servicetypesdir=\"$kde_servicetypesdir\" \ + kde_moduledir=\"$kde_moduledir\" \ + kde_styledir=\"$kde_styledir\" \ + kde_widgetdir=\"$kde_widgetdir\" \ + xdg_appsdir=\"$xdg_appsdir\" \ + xdg_menudir=\"$xdg_menudir\" \ + xdg_directorydir=\"$xdg_directorydir\" \ + kde_result=$1" +]) + +AC_DEFUN([KDE_SET_DEFAULT_PATHS], +[ +if test "$1" = "default"; then + + if test -z "$kde_htmldir"; then + kde_htmldir='\${datadir}/doc/HTML' + fi + if test -z "$kde_appsdir"; then + kde_appsdir='\${datadir}/applnk' + fi + if test -z "$kde_icondir"; then + kde_icondir='\${datadir}/icons' + fi + if test -z "$kde_sounddir"; then + kde_sounddir='\${datadir}/sounds' + fi + if test -z "$kde_datadir"; then + kde_datadir='\${datadir}/apps' + fi + if test -z "$kde_locale"; then + kde_locale='\${datadir}/locale' + fi + if test -z "$kde_cgidir"; then + kde_cgidir='\${exec_prefix}/cgi-bin' + fi + if test -z "$kde_confdir"; then + kde_confdir='\${datadir}/config' + fi + if test -z "$kde_kcfgdir"; then + kde_kcfgdir='\${datadir}/config.kcfg' + fi + if test -z "$kde_mimedir"; then + kde_mimedir='\${datadir}/mimelnk' + fi + if test -z "$kde_toolbardir"; then + kde_toolbardir='\${datadir}/toolbar' + fi + if test -z "$kde_wallpaperdir"; then + kde_wallpaperdir='\${datadir}/wallpapers' + fi + if test -z "$kde_templatesdir"; then + kde_templatesdir='\${datadir}/templates' + fi + if test -z "$kde_bindir"; then + kde_bindir='\${exec_prefix}/bin' + fi + if test -z "$kde_servicesdir"; then + kde_servicesdir='\${datadir}/services' + fi + if test -z "$kde_servicetypesdir"; then + kde_servicetypesdir='\${datadir}/servicetypes' + fi + if test -z "$kde_moduledir"; then + if test "$kde_qtver" = "2"; then + kde_moduledir='\${libdir}/kde2' + else + kde_moduledir='\${libdir}/kde3' + fi + fi + if test -z "$kde_styledir"; then + kde_styledir='\${libdir}/kde3/plugins/styles' + fi + if test -z "$kde_widgetdir"; then + kde_widgetdir='\${libdir}/kde3/plugins/designer' + fi + if test -z "$xdg_appsdir"; then + xdg_appsdir='\${datadir}/applications/kde' + fi + if test -z "$xdg_menudir"; then + xdg_menudir='\${sysconfdir}/xdg/menus' + fi + if test -z "$xdg_directorydir"; then + xdg_directorydir='\${datadir}/desktop-directories' + fi + + KDE_SET_PATHS(defaults) + +else + + if test $kde_qtver = 1; then + AC_MSG_RESULT([compiling]) + KDE_1_CHECK_PATHS + else + AC_MSG_ERROR([path checking not yet supported for KDE 2]) + fi + +fi +]) + +AC_DEFUN([KDE_CHECK_PATHS_FOR_COMPLETENESS], +[ if test -z "$kde_htmldir" || test -z "$kde_appsdir" || + test -z "$kde_icondir" || test -z "$kde_sounddir" || + test -z "$kde_datadir" || test -z "$kde_locale" || + test -z "$kde_cgidir" || test -z "$kde_confdir" || + test -z "$kde_kcfgdir" || + test -z "$kde_mimedir" || test -z "$kde_toolbardir" || + test -z "$kde_wallpaperdir" || test -z "$kde_templatesdir" || + test -z "$kde_bindir" || test -z "$kde_servicesdir" || + test -z "$kde_servicetypesdir" || test -z "$kde_moduledir" || + test -z "$kde_styledir" || test -z "kde_widgetdir" || + test -z "$xdg_appsdir" || test -z "$xdg_menudir" || test -z "$xdg_directorydir" || + test "x$kde_have_all_paths" != "xyes"; then + kde_have_all_paths=no + fi +]) + +AC_DEFUN([KDE_MISSING_PROG_ERROR], +[ + AC_MSG_ERROR([The important program $1 was not found! +Please check whether you installed KDE correctly. +]) +]) + +AC_DEFUN([KDE_MISSING_ARTS_ERROR], +[ + AC_MSG_ERROR([The important program $1 was not found! +Please check whether you installed aRts correctly or use +--without-arts to compile without aRts support (this will remove functionality). +]) +]) + +AC_DEFUN([KDE_SET_DEFAULT_BINDIRS], +[ + kde_default_bindirs="/usr/bin /usr/local/bin /opt/local/bin /usr/X11R6/bin /opt/kde/bin /opt/kde3/bin /usr/kde/bin /usr/local/kde/bin" + test -n "$KDEDIR" && kde_default_bindirs="$KDEDIR/bin $kde_default_bindirs" + if test -n "$KDEDIRS"; then + kde_save_IFS=$IFS + IFS=: + for dir in $KDEDIRS; do + kde_default_bindirs="$dir/bin $kde_default_bindirs " + done + IFS=$kde_save_IFS + fi +]) + +AC_DEFUN([KDE_SUBST_PROGRAMS], +[ + AC_ARG_WITH(arts, + AC_HELP_STRING([--without-arts],[build without aRts [default=no]]), + [build_arts=$withval], + [build_arts=yes] + ) + AM_CONDITIONAL(include_ARTS, test "$build_arts" '!=' "no") + if test "$build_arts" = "no"; then + AC_DEFINE(WITHOUT_ARTS, 1, [Defined if compiling without arts]) + fi + + KDE_SET_DEFAULT_BINDIRS + kde_default_bindirs="$exec_prefix/bin $prefix/bin $kde_libs_prefix/bin $kde_default_bindirs" + KDE_FIND_PATH(dcopidl, DCOPIDL, [$kde_default_bindirs], [KDE_MISSING_PROG_ERROR(dcopidl)]) + KDE_FIND_PATH(dcopidl2cpp, DCOPIDL2CPP, [$kde_default_bindirs], [KDE_MISSING_PROG_ERROR(dcopidl2cpp)]) + if test "$build_arts" '!=' "no"; then + KDE_FIND_PATH(mcopidl, MCOPIDL, [$kde_default_bindirs], [KDE_MISSING_ARTS_ERROR(mcopidl)]) + KDE_FIND_PATH(artsc-config, ARTSCCONFIG, [$kde_default_bindirs], [KDE_MISSING_ARTS_ERROR(artsc-config)]) + fi + KDE_FIND_PATH(meinproc, MEINPROC, [$kde_default_bindirs]) + + kde32ornewer=1 + kde33ornewer=1 + if test -n "$kde_qtver" && test "$kde_qtver" -lt 3; then + kde32ornewer= + kde33ornewer= + else + if test "$kde_qtver" = "3"; then + if test "$kde_qtsubver" -le 1; then + kde32ornewer= + fi + if test "$kde_qtsubver" -le 2; then + kde33ornewer= + fi + if test "$KDECONFIG" != "compiled"; then + if test `$KDECONFIG --version | grep KDE | sed 's/KDE: \(...\).*/\1/'` = 3.2; then + kde33ornewer= + fi + fi + fi + fi + + if test -n "$kde32ornewer"; then + KDE_FIND_PATH(kconfig_compiler, KCONFIG_COMPILER, [$kde_default_bindirs], [KDE_MISSING_PROG_ERROR(kconfig_compiler)]) + KDE_FIND_PATH(dcopidlng, DCOPIDLNG, [$kde_default_bindirs], [KDE_MISSING_PROG_ERROR(dcopidlng)]) + fi + if test -n "$kde33ornewer"; then + KDE_FIND_PATH(makekdewidgets, MAKEKDEWIDGETS, [$kde_default_bindirs], [KDE_MISSING_PROG_ERROR(makekdewidgets)]) + AC_SUBST(MAKEKDEWIDGETS) + fi + KDE_FIND_PATH(xmllint, XMLLINT, [${prefix}/bin ${exec_prefix}/bin], [XMLLINT=""]) + + if test -n "$MEINPROC" -a "$MEINPROC" != "compiled"; then + kde_sharedirs="/usr/share/kde /usr/local/share /usr/share /opt/kde3/share /opt/kde/share $prefix/share" + test -n "$KDEDIR" && kde_sharedirs="$KDEDIR/share $kde_sharedirs" + AC_FIND_FILE(apps/ksgmltools2/customization/kde-chunk.xsl, $kde_sharedirs, KDE_XSL_STYLESHEET) + if test "$KDE_XSL_STYLESHEET" = "NO"; then + KDE_XSL_STYLESHEET="" + else + KDE_XSL_STYLESHEET="$KDE_XSL_STYLESHEET/apps/ksgmltools2/customization/kde-chunk.xsl" + fi + fi + + DCOP_DEPENDENCIES='$(DCOPIDL)' + if test -n "$kde32ornewer"; then + KCFG_DEPENDENCIES='$(KCONFIG_COMPILER)' + DCOP_DEPENDENCIES='$(DCOPIDL) $(DCOPIDLNG)' + AC_SUBST(KCONFIG_COMPILER) + AC_SUBST(KCFG_DEPENDENCIES) + AC_SUBST(DCOPIDLNG) + fi + AC_SUBST(DCOPIDL) + AC_SUBST(DCOPIDL2CPP) + AC_SUBST(DCOP_DEPENDENCIES) + AC_SUBST(MCOPIDL) + AC_SUBST(ARTSCCONFIG) + AC_SUBST(MEINPROC) + AC_SUBST(KDE_XSL_STYLESHEET) + AC_SUBST(XMLLINT) +])dnl + +AC_DEFUN([AC_CREATE_KFSSTND], +[ +AC_REQUIRE([AC_CHECK_RPATH]) + +AC_MSG_CHECKING([for KDE paths]) +kde_result="" +kde_cached_paths=yes +AC_CACHE_VAL(kde_cv_all_paths, +[ + KDE_SET_DEFAULT_PATHS($1) + kde_cached_paths=no +]) +eval "$kde_cv_all_paths" +KDE_CHECK_PATHS_FOR_COMPLETENESS +if test "$kde_have_all_paths" = "no" && test "$kde_cached_paths" = "yes"; then + # wrong values were cached, may be, we can set better ones + kde_result= + kde_htmldir= kde_appsdir= kde_icondir= kde_sounddir= + kde_datadir= kde_locale= kde_cgidir= kde_confdir= kde_kcfgdir= + kde_mimedir= kde_toolbardir= kde_wallpaperdir= kde_templatesdir= + kde_bindir= kde_servicesdir= kde_servicetypesdir= kde_moduledir= + kde_have_all_paths= + kde_styledir= + kde_widgetdir= + xdg_appsdir = xdg_menudir= xdg_directorydir= + KDE_SET_DEFAULT_PATHS($1) + eval "$kde_cv_all_paths" + KDE_CHECK_PATHS_FOR_COMPLETENESS + kde_result="$kde_result (cache overridden)" +fi +if test "$kde_have_all_paths" = "no"; then + AC_MSG_ERROR([configure could not run a little KDE program to test the environment. +Since it had compiled and linked before, it must be a strange problem on your system. +Look at config.log for details. If you are not able to fix this, look at +http://www.kde.org/faq/installation.html or any www.kde.org mirror. +(If you're using an egcs version on Linux, you may update binutils!) +]) +else + rm -f conftest* + AC_MSG_RESULT($kde_result) +fi + +bindir=$kde_bindir + +KDE_SUBST_PROGRAMS + +]) + +AC_DEFUN([AC_SUBST_KFSSTND], +[ +AC_SUBST(kde_htmldir) +AC_SUBST(kde_appsdir) +AC_SUBST(kde_icondir) +AC_SUBST(kde_sounddir) +AC_SUBST(kde_datadir) +AC_SUBST(kde_locale) +AC_SUBST(kde_confdir) +AC_SUBST(kde_kcfgdir) +AC_SUBST(kde_mimedir) +AC_SUBST(kde_wallpaperdir) +AC_SUBST(kde_bindir) +dnl X Desktop Group standards +AC_SUBST(xdg_appsdir) +AC_SUBST(xdg_menudir) +AC_SUBST(xdg_directorydir) +dnl for KDE 2 +AC_SUBST(kde_templatesdir) +AC_SUBST(kde_servicesdir) +AC_SUBST(kde_servicetypesdir) +AC_SUBST(kde_moduledir) +AC_SUBST(kdeinitdir, '$(kde_moduledir)') +AC_SUBST(kde_styledir) +AC_SUBST(kde_widgetdir) +if test "$kde_qtver" = 1; then + kde_minidir="$kde_icondir/mini" +else +# for KDE 1 - this breaks KDE2 apps using minidir, but +# that's the plan ;-/ + kde_minidir="/dev/null" +fi +dnl AC_SUBST(kde_minidir) +dnl AC_SUBST(kde_cgidir) +dnl AC_SUBST(kde_toolbardir) +]) + +AC_DEFUN([KDE_MISC_TESTS], +[ + dnl Checks for libraries. + AC_CHECK_LIB(util, main, [LIBUTIL="-lutil"]) dnl for *BSD + AC_SUBST(LIBUTIL) + AC_CHECK_LIB(compat, main, [LIBCOMPAT="-lcompat"]) dnl for *BSD + AC_SUBST(LIBCOMPAT) + kde_have_crypt= + AC_CHECK_LIB(crypt, crypt, [LIBCRYPT="-lcrypt"; kde_have_crypt=yes], + AC_CHECK_LIB(c, crypt, [kde_have_crypt=yes], [ + AC_MSG_WARN([you have no crypt in either libcrypt or libc. +You should install libcrypt from another source or configure with PAM +support]) + kde_have_crypt=no + ])) + AC_SUBST(LIBCRYPT) + if test $kde_have_crypt = yes; then + AC_DEFINE_UNQUOTED(HAVE_CRYPT, 1, [Defines if your system has the crypt function]) + fi + AC_CHECK_SOCKLEN_T + AC_CHECK_LIB(dnet, dnet_ntoa, [X_EXTRA_LIBS="$X_EXTRA_LIBS -ldnet"]) + if test $ac_cv_lib_dnet_dnet_ntoa = no; then + AC_CHECK_LIB(dnet_stub, dnet_ntoa, + [X_EXTRA_LIBS="$X_EXTRA_LIBS -ldnet_stub"]) + fi + AC_CHECK_FUNC(inet_ntoa) + if test $ac_cv_func_inet_ntoa = no; then + AC_CHECK_LIB(nsl, inet_ntoa, X_EXTRA_LIBS="$X_EXTRA_LIBS -lnsl") + fi + AC_CHECK_FUNC(connect) + if test $ac_cv_func_connect = no; then + AC_CHECK_LIB(socket, connect, X_EXTRA_LIBS="-lsocket $X_EXTRA_LIBS", , + $X_EXTRA_LIBS) + fi + + AC_CHECK_FUNC(remove) + if test $ac_cv_func_remove = no; then + AC_CHECK_LIB(posix, remove, X_EXTRA_LIBS="$X_EXTRA_LIBS -lposix") + fi + + # BSDI BSD/OS 2.1 needs -lipc for XOpenDisplay. + AC_CHECK_FUNC(shmat, , + AC_CHECK_LIB(ipc, shmat, X_EXTRA_LIBS="$X_EXTRA_LIBS -lipc")) + + # more headers that need to be explicitly included on darwin + AC_CHECK_HEADERS(sys/types.h stdint.h) + + # sys/bitypes.h is needed for uint32_t and friends on Tru64 + AC_CHECK_HEADERS(sys/bitypes.h) + + # darwin requires a poll emulation library + AC_CHECK_LIB(poll, poll, LIB_POLL="-lpoll") + + # for some image handling on Mac OS X + AC_CHECK_HEADERS(Carbon/Carbon.h) + + # CoreAudio framework + AC_CHECK_HEADER(CoreAudio/CoreAudio.h, [ + AC_DEFINE(HAVE_COREAUDIO, 1, [Define if you have the CoreAudio API]) + FRAMEWORK_COREAUDIO="-Wl,-framework,CoreAudio" + ]) + + AC_CHECK_RES_INIT + AC_SUBST(LIB_POLL) + AC_SUBST(FRAMEWORK_COREAUDIO) + LIBSOCKET="$X_EXTRA_LIBS" + AC_SUBST(LIBSOCKET) + AC_SUBST(X_EXTRA_LIBS) + AC_CHECK_LIB(ucb, killpg, [LIBUCB="-lucb"]) dnl for Solaris2.4 + AC_SUBST(LIBUCB) + + case $host in dnl this *is* LynxOS specific + *-*-lynxos* ) + AC_MSG_CHECKING([LynxOS header file wrappers]) + [CFLAGS="$CFLAGS -D__NO_INCLUDE_WARN__"] + AC_MSG_RESULT(disabled) + AC_CHECK_LIB(bsd, gethostbyname, [LIBSOCKET="-lbsd"]) dnl for LynxOS + ;; + esac + + KDE_CHECK_TYPES + KDE_CHECK_LIBDL + KDE_CHECK_STRLCPY + KDE_CHECK_PIE_SUPPORT + +# darwin needs this to initialize the environment +AC_CHECK_HEADERS(crt_externs.h) +AC_CHECK_FUNC(_NSGetEnviron, [AC_DEFINE(HAVE_NSGETENVIRON, 1, [Define if your system needs _NSGetEnviron to set up the environment])]) + +AH_VERBATIM(_DARWIN_ENVIRON, +[ +#if defined(HAVE_NSGETENVIRON) && defined(HAVE_CRT_EXTERNS_H) +# include +# include +# define environ (*_NSGetEnviron()) +#endif +]) + +AH_VERBATIM(_AIX_STRINGS_H_BZERO, +[ +/* + * AIX defines FD_SET in terms of bzero, but fails to include + * that defines bzero. + */ + +#if defined(_AIX) +#include +#endif +]) + +AC_CHECK_FUNCS([vsnprintf snprintf]) + +AH_VERBATIM(_TRU64,[ +/* + * On HP-UX, the declaration of vsnprintf() is needed every time ! + */ + +#if !defined(HAVE_VSNPRINTF) || defined(hpux) +#if __STDC__ +#include +#include +#else +#include +#endif +#ifdef __cplusplus +extern "C" +#endif +int vsnprintf(char *str, size_t n, char const *fmt, va_list ap); +#ifdef __cplusplus +extern "C" +#endif +int snprintf(char *str, size_t n, char const *fmt, ...); +#endif +]) + +]) + +dnl ------------------------------------------------------------------------ +dnl Find the header files and libraries for X-Windows. Extended the +dnl macro AC_PATH_X +dnl ------------------------------------------------------------------------ +dnl +AC_DEFUN([K_PATH_X], +[ +AC_REQUIRE([KDE_MISC_TESTS])dnl +AC_REQUIRE([KDE_CHECK_LIB64]) + +AC_ARG_ENABLE( + embedded, + AC_HELP_STRING([--enable-embedded],[link to Qt-embedded, don't use X]), + kde_use_qt_emb=$enableval, + kde_use_qt_emb=no +) + +AC_ARG_ENABLE( + qtopia, + AC_HELP_STRING([--enable-qtopia],[link to Qt-embedded, link to the Qtopia Environment]), + kde_use_qt_emb_palm=$enableval, + kde_use_qt_emb_palm=no +) + +AC_ARG_ENABLE( + mac, + AC_HELP_STRING([--enable-mac],[link to Qt/Mac (don't use X)]), + kde_use_qt_mac=$enableval, + kde_use_qt_mac=no +) + +# used to disable x11-specific stuff on special platforms +AM_CONDITIONAL(include_x11, test "$kde_use_qt_emb" = "no" && test "$kde_use_qt_mac" = "no") + +if test "$kde_use_qt_emb" = "no" && test "$kde_use_qt_mac" = "no"; then + +AC_MSG_CHECKING(for X) + +AC_CACHE_VAL(kde_cv_have_x, +[# One or both of the vars are not set, and there is no cached value. +if test "{$x_includes+set}" = set || test "$x_includes" = NONE; then + kde_x_includes=NO +else + kde_x_includes=$x_includes +fi +if test "{$x_libraries+set}" = set || test "$x_libraries" = NONE; then + kde_x_libraries=NO +else + kde_x_libraries=$x_libraries +fi + +# below we use the standard autoconf calls +ac_x_libraries=$kde_x_libraries +ac_x_includes=$kde_x_includes + +KDE_PATH_X_DIRECT +dnl AC_PATH_X_XMKMF picks /usr/lib as the path for the X libraries. +dnl Unfortunately, if compiling with the N32 ABI, this is not the correct +dnl location. The correct location is /usr/lib32 or an undefined value +dnl (the linker is smart enough to pick the correct default library). +dnl Things work just fine if you use just AC_PATH_X_DIRECT. +dnl Solaris has a similar problem. AC_PATH_X_XMKMF forces x_includes to +dnl /usr/openwin/include, which doesn't work. /usr/include does work, so +dnl x_includes should be left alone. +case "$host" in +mips-sgi-irix6*) + ;; +*-*-solaris*) + ;; +*) + _AC_PATH_X_XMKMF + if test -z "$ac_x_includes"; then + ac_x_includes="." + fi + if test -z "$ac_x_libraries"; then + ac_x_libraries="/usr/lib${kdelibsuff}" + fi +esac +#from now on we use our own again + +# when the user already gave --x-includes, we ignore +# what the standard autoconf macros told us. +if test "$kde_x_includes" = NO; then + kde_x_includes=$ac_x_includes +fi + +# for --x-libraries too +if test "$kde_x_libraries" = NO; then + kde_x_libraries=$ac_x_libraries +fi + +if test "$kde_x_includes" = NO; then + AC_MSG_ERROR([Can't find X includes. Please check your installation and add the correct paths!]) +fi + +if test "$kde_x_libraries" = NO; then + AC_MSG_ERROR([Can't find X libraries. Please check your installation and add the correct paths!]) +fi + +# Record where we found X for the cache. +kde_cv_have_x="have_x=yes \ + kde_x_includes=$kde_x_includes kde_x_libraries=$kde_x_libraries" +])dnl + +eval "$kde_cv_have_x" + +if test "$have_x" != yes; then + AC_MSG_RESULT($have_x) + no_x=yes +else + AC_MSG_RESULT([libraries $kde_x_libraries, headers $kde_x_includes]) +fi + +if test -z "$kde_x_includes" || test "x$kde_x_includes" = xNONE; then + X_INCLUDES="" + x_includes="."; dnl better than nothing :- + else + x_includes=$kde_x_includes + X_INCLUDES="-I$x_includes" +fi + +if test -z "$kde_x_libraries" || test "x$kde_x_libraries" = xNONE || test "$kde_x_libraries" = "/usr/lib"; then + X_LDFLAGS="" + x_libraries="/usr/lib"; dnl better than nothing :- + else + x_libraries=$kde_x_libraries + X_LDFLAGS="-L$x_libraries" +fi +all_includes="$X_INCLUDES" +all_libraries="$X_LDFLAGS $LDFLAGS_AS_NEEDED $LDFLAGS_NEW_DTAGS" + +# Check for libraries that X11R6 Xt/Xaw programs need. +ac_save_LDFLAGS="$LDFLAGS" +LDFLAGS="$LDFLAGS $X_LDFLAGS" +# SM needs ICE to (dynamically) link under SunOS 4.x (so we have to +# check for ICE first), but we must link in the order -lSM -lICE or +# we get undefined symbols. So assume we have SM if we have ICE. +# These have to be linked with before -lX11, unlike the other +# libraries we check for below, so use a different variable. +# --interran@uluru.Stanford.EDU, kb@cs.umb.edu. +AC_CHECK_LIB(ICE, IceConnectionNumber, + [LIBSM="-lSM -lICE"], , $X_EXTRA_LIBS) +LDFLAGS="$ac_save_LDFLAGS" + +LIB_X11='-lX11 $(LIBSOCKET)' + +AC_MSG_CHECKING(for libXext) +AC_CACHE_VAL(kde_cv_have_libXext, +[ +kde_ldflags_safe="$LDFLAGS" +kde_libs_safe="$LIBS" + +LDFLAGS="$LDFLAGS $X_LDFLAGS $USER_LDFLAGS" +LIBS="-lXext -lX11 $LIBSOCKET" + +AC_TRY_LINK([ +#include +#ifdef STDC_HEADERS +# include +#endif +], +[ +printf("hello Xext\n"); +], +kde_cv_have_libXext=yes, +kde_cv_have_libXext=no +) + +LDFLAGS=$kde_ldflags_safe +LIBS=$kde_libs_safe +]) + +AC_MSG_RESULT($kde_cv_have_libXext) + +if test "$kde_cv_have_libXext" = "no"; then + AC_MSG_ERROR([We need a working libXext to proceed. Since configure +can't find it itself, we stop here assuming that make wouldn't find +them either.]) +fi + +LIB_XEXT="-lXext" +QTE_NORTTI="" + +elif test "$kde_use_qt_emb" = "yes"; then + dnl We're using QT Embedded + CPPFLAGS=-DQWS + CXXFLAGS="$CXXFLAGS -fno-rtti" + QTE_NORTTI="-fno-rtti -DQWS" + X_PRE_LIBS="" + LIB_X11="" + LIB_XEXT="" + LIB_XRENDER="" + LIBSM="" + X_INCLUDES="" + X_LDFLAGS="" + x_includes="" + x_libraries="" +elif test "$kde_use_qt_mac" = "yes"; then + dnl We're using QT/Mac (I use QT_MAC so that qglobal.h doesn't *have* to + dnl be included to get the information) --Sam + CXXFLAGS="$CXXFLAGS -DQT_MAC -no-cpp-precomp" + CFLAGS="$CFLAGS -DQT_MAC -no-cpp-precomp" + X_PRE_LIBS="" + LIB_X11="" + LIB_XEXT="" + LIB_XRENDER="" + LIBSM="" + X_INCLUDES="" + X_LDFLAGS="" + x_includes="" + x_libraries="" +fi +AC_SUBST(X_PRE_LIBS) +AC_SUBST(LIB_X11) +AC_SUBST(LIB_XRENDER) +AC_SUBST(LIBSM) +AC_SUBST(X_INCLUDES) +AC_SUBST(X_LDFLAGS) +AC_SUBST(x_includes) +AC_SUBST(x_libraries) +AC_SUBST(QTE_NORTTI) +AC_SUBST(LIB_XEXT) + +]) + +AC_DEFUN([KDE_PRINT_QT_PROGRAM], +[ +AC_REQUIRE([KDE_USE_QT]) +cat > conftest.$ac_ext < +#include +EOF +if test "$kde_qtver" = "2"; then +cat >> conftest.$ac_ext < +#include +#include +EOF + +if test $kde_qtsubver -gt 0; then +cat >> conftest.$ac_ext <> conftest.$ac_ext < +#include +#include +EOF +fi + +echo "#if ! ($kde_qt_verstring)" >> conftest.$ac_ext +cat >> conftest.$ac_ext <> conftest.$ac_ext <> conftest.$ac_ext <> conftest.$ac_ext <> conftest.$ac_ext <&AC_FD_CC + cat conftest.$ac_ext >&AC_FD_CC +fi + +rm -f conftest* +CXXFLAGS="$ac_cxxflags_safe" +LDFLAGS="$ac_ldflags_safe" +LIBS="$ac_libs_safe" + +LD_LIBRARY_PATH="$ac_LD_LIBRARY_PATH_safe" +export LD_LIBRARY_PATH +LIBRARY_PATH="$ac_LIBRARY_PATH" +export LIBRARY_PATH +AC_LANG_RESTORE +]) + +if test "$kde_cv_qt_direct" = "yes"; then + AC_MSG_RESULT(yes) + $1 +else + AC_MSG_RESULT(no) + $2 +fi +]) + +dnl ------------------------------------------------------------------------ +dnl Try to find the Qt headers and libraries. +dnl $(QT_LDFLAGS) will be -Lqtliblocation (if needed) +dnl and $(QT_INCLUDES) will be -Iqthdrlocation (if needed) +dnl ------------------------------------------------------------------------ +dnl +AC_DEFUN([AC_PATH_QT_1_3], +[ +AC_REQUIRE([K_PATH_X]) +AC_REQUIRE([KDE_USE_QT]) +AC_REQUIRE([KDE_CHECK_LIB64]) + +dnl ------------------------------------------------------------------------ +dnl Add configure flag to enable linking to MT version of Qt library. +dnl ------------------------------------------------------------------------ + +AC_ARG_ENABLE( + mt, + AC_HELP_STRING([--disable-mt],[link to non-threaded Qt (deprecated)]), + kde_use_qt_mt=$enableval, + [ + if test $kde_qtver = 3; then + kde_use_qt_mt=yes + else + kde_use_qt_mt=no + fi + ] +) + +USING_QT_MT="" + +dnl ------------------------------------------------------------------------ +dnl If we not get --disable-qt-mt then adjust some vars for the host. +dnl ------------------------------------------------------------------------ + +KDE_MT_LDFLAGS= +KDE_MT_LIBS= +if test "x$kde_use_qt_mt" = "xyes"; then + KDE_CHECK_THREADING + if test "x$kde_use_threading" = "xyes"; then + CPPFLAGS="$USE_THREADS -DQT_THREAD_SUPPORT $CPPFLAGS" + KDE_MT_LDFLAGS="$USE_THREADS" + KDE_MT_LIBS="$LIBPTHREAD" + else + kde_use_qt_mt=no + fi +fi +AC_SUBST(KDE_MT_LDFLAGS) +AC_SUBST(KDE_MT_LIBS) + +kde_qt_was_given=yes + +dnl ------------------------------------------------------------------------ +dnl If we haven't been told how to link to Qt, we work it out for ourselves. +dnl ------------------------------------------------------------------------ +if test -z "$LIBQT_GLOB"; then + if test "x$kde_use_qt_emb" = "xyes"; then + LIBQT_GLOB="libqte.*" + else + LIBQT_GLOB="libqt.*" + fi +fi + +dnl ------------------------------------------------------------ +dnl If we got --enable-embedded then adjust the Qt library name. +dnl ------------------------------------------------------------ +if test "x$kde_use_qt_emb" = "xyes"; then + qtlib="qte" +else + qtlib="qt" +fi + +kde_int_qt="-l$qtlib" + +if test -z "$LIBQPE"; then +dnl ------------------------------------------------------------ +dnl If we got --enable-palmtop then add -lqpe to the link line +dnl ------------------------------------------------------------ + if test "x$kde_use_qt_emb" = "xyes"; then + if test "x$kde_use_qt_emb_palm" = "xyes"; then + LIB_QPE="-lqpe" + else + LIB_QPE="" + fi + else + LIB_QPE="" + fi +fi + +dnl ------------------------------------------------------------------------ +dnl If we got --enable-qt-mt then adjust the Qt library name for the host. +dnl ------------------------------------------------------------------------ + +if test "x$kde_use_qt_mt" = "xyes"; then + LIBQT="-l$qtlib-mt" + kde_int_qt="-l$qtlib-mt" + LIBQT_GLOB="lib$qtlib-mt.*" + USING_QT_MT="using -mt" +else + LIBQT="-l$qtlib" +fi + +if test $kde_qtver != 1; then + + AC_REQUIRE([AC_FIND_PNG]) + AC_REQUIRE([AC_FIND_JPEG]) + LIBQT="$LIBQT $LIBPNG $LIBJPEG" +fi + +if test $kde_qtver = 3; then + AC_REQUIRE([KDE_CHECK_LIBDL]) + LIBQT="$LIBQT $LIBDL" +fi + +AC_MSG_CHECKING([for Qt]) + +if test "x$kde_use_qt_emb" != "xyes" && test "x$kde_use_qt_mac" != "xyes"; then +LIBQT="$LIBQT $X_PRE_LIBS -lXext -lX11 $LIBSM $LIBSOCKET" +fi +ac_qt_includes=NO ac_qt_libraries=NO ac_qt_bindir=NO +qt_libraries="" +qt_includes="" +AC_ARG_WITH(qt-dir, + AC_HELP_STRING([--with-qt-dir=DIR],[where the root of Qt is installed ]), + [ ac_qt_includes="$withval"/include + ac_qt_libraries="$withval"/lib${kdelibsuff} + ac_qt_bindir="$withval"/bin + ]) + +AC_ARG_WITH(qt-includes, + AC_HELP_STRING([--with-qt-includes=DIR],[where the Qt includes are. ]), + [ + ac_qt_includes="$withval" + ]) + +kde_qt_libs_given=no + +AC_ARG_WITH(qt-libraries, + AC_HELP_STRING([--with-qt-libraries=DIR],[where the Qt library is installed.]), + [ ac_qt_libraries="$withval" + kde_qt_libs_given=yes + ]) + +AC_CACHE_VAL(ac_cv_have_qt, +[#try to guess Qt locations + +qt_incdirs="" +for dir in $kde_qt_dirs; do + qt_incdirs="$qt_incdirs $dir/include $dir" +done +if test -z "$PKG_CONFIG"; then + AC_PATH_PROG(PKG_CONFIG, pkg-config, no) +fi +if test "$PKG_CONFIG" != "no" ; then + if $PKG_CONFIG --exists qt-mt ; then + qt_incdirs="$qt_incdirs `$PKG_CONFIG --variable=includedir qt-mt`" + fi +fi +qt_incdirs="$QTINC $qt_incdirs /usr/local/qt/include /usr/include/qt /usr/include /usr/X11R6/include/X11/qt /usr/X11R6/include/qt /usr/X11R6/include/qt2 /usr/include/qt3 $x_includes" +if test ! "$ac_qt_includes" = "NO"; then + qt_incdirs="$ac_qt_includes $qt_incdirs" +fi + +if test "$kde_qtver" != "1"; then + kde_qt_header=qstyle.h +else + kde_qt_header=qglobal.h +fi + +AC_FIND_FILE($kde_qt_header, $qt_incdirs, qt_incdir) +ac_qt_includes="$qt_incdir" + +qt_libdirs="" +for dir in $kde_qt_dirs; do + qt_libdirs="$qt_libdirs $dir/lib${kdelibsuff} $dir/lib $dir" +done +if test -z "$PKG_CONFIG"; then + AC_PATH_PROG(PKG_CONFIG, pkg-config, no) +fi +if test "$PKG_CONFIG" != "no" ; then + if $PKG_CONFIG --exists qt-mt ; then + qt_libdirs="$qt_incdirs `$PKG_CONFIG --variable=libdir qt-mt`" + fi +fi +qt_libdirs="$QTLIB $qt_libdirs /usr/X11R6/lib /usr/lib /usr/local/qt/lib $x_libraries" +if test ! "$ac_qt_libraries" = "NO"; then + qt_libdir=$ac_qt_libraries +else + qt_libdirs="$ac_qt_libraries $qt_libdirs" + # if the Qt was given, the chance is too big that libqt.* doesn't exist + qt_libdir=NONE + for dir in $qt_libdirs; do + try="ls -1 $dir/${LIBQT_GLOB}" + if test -n "`$try 2> /dev/null`"; then qt_libdir=$dir; break; else echo "tried $dir" >&AC_FD_CC ; fi + done +fi +for a in $qt_libdir/lib`echo ${kde_int_qt} | sed 's,^-l,,'`_incremental.*; do + if test -e "$a"; then + LIBQT="$LIBQT ${kde_int_qt}_incremental" + break + fi +done + +ac_qt_libraries="$qt_libdir" + +AC_LANG_SAVE +AC_LANG_CPLUSPLUS + +ac_cxxflags_safe="$CXXFLAGS" +ac_ldflags_safe="$LDFLAGS" +ac_libs_safe="$LIBS" + +CXXFLAGS="$CXXFLAGS -I$qt_incdir $all_includes" +LDFLAGS="$LDFLAGS -L$qt_libdir $all_libraries $USER_LDFLAGS $KDE_MT_LDFLAGS" +LIBS="$LIBS $LIBQT $KDE_MT_LIBS" + +KDE_PRINT_QT_PROGRAM + +if AC_TRY_EVAL(ac_link) && test -s conftest; then + rm -f conftest* +else + echo "configure: failed program was:" >&AC_FD_CC + cat conftest.$ac_ext >&AC_FD_CC + ac_qt_libraries="NO" +fi +rm -f conftest* +CXXFLAGS="$ac_cxxflags_safe" +LDFLAGS="$ac_ldflags_safe" +LIBS="$ac_libs_safe" + +AC_LANG_RESTORE +if test "$ac_qt_includes" = NO || test "$ac_qt_libraries" = NO; then + ac_cv_have_qt="have_qt=no" + ac_qt_notfound="" + missing_qt_mt="" + if test "$ac_qt_includes" = NO; then + if test "$ac_qt_libraries" = NO; then + ac_qt_notfound="(headers and libraries)"; + else + ac_qt_notfound="(headers)"; + fi + else + if test "x$kde_use_qt_mt" = "xyes"; then + missing_qt_mt=" +Make sure that you have compiled Qt with thread support!" + ac_qt_notfound="(library $qtlib-mt)"; + else + ac_qt_notfound="(library $qtlib)"; + fi + fi + + AC_MSG_ERROR([Qt ($kde_qt_minversion) $ac_qt_notfound not found. Please check your installation! +For more details about this problem, look at the end of config.log.$missing_qt_mt]) +else + have_qt="yes" +fi +]) + +eval "$ac_cv_have_qt" + +if test "$have_qt" != yes; then + AC_MSG_RESULT([$have_qt]); +else + ac_cv_have_qt="have_qt=yes \ + ac_qt_includes=$ac_qt_includes ac_qt_libraries=$ac_qt_libraries" + AC_MSG_RESULT([libraries $ac_qt_libraries, headers $ac_qt_includes $USING_QT_MT]) + + qt_libraries="$ac_qt_libraries" + qt_includes="$ac_qt_includes" +fi + +if test ! "$kde_qt_libs_given" = "yes" && test ! "$kde_qtver" = 3; then + KDE_CHECK_QT_DIRECT(qt_libraries= ,[]) +fi + +AC_SUBST(qt_libraries) +AC_SUBST(qt_includes) + +if test "$qt_includes" = "$x_includes" || test -z "$qt_includes"; then + QT_INCLUDES="" +else + QT_INCLUDES="-I$qt_includes" + all_includes="$QT_INCLUDES $all_includes" +fi + +if test "$qt_libraries" = "$x_libraries" || test -z "$qt_libraries"; then + QT_LDFLAGS="" +else + QT_LDFLAGS="-L$qt_libraries" + all_libraries="$QT_LDFLAGS $all_libraries" +fi +test -z "$KDE_MT_LDFLAGS" || all_libraries="$all_libraries $KDE_MT_LDFLAGS" + +AC_SUBST(QT_INCLUDES) +AC_SUBST(QT_LDFLAGS) +AC_PATH_QT_MOC_UIC + +KDE_CHECK_QT_JPEG + +if test "x$kde_use_qt_emb" != "xyes" && test "x$kde_use_qt_mac" != "xyes"; then +LIB_QT="$kde_int_qt $LIBJPEG_QT "'$(LIBZ) $(LIBPNG) -lXext $(LIB_X11) $(LIBSM)' +else +LIB_QT="$kde_int_qt $LIBJPEG_QT "'$(LIBZ) $(LIBPNG)' +fi +test -z "$KDE_MT_LIBS" || LIB_QT="$LIB_QT $KDE_MT_LIBS" +for a in $qt_libdir/lib`echo ${kde_int_qt} | sed 's,^-l,,'`_incremental.*; do + if test -e "$a"; then + LIB_QT="$LIB_QT ${kde_int_qt}_incremental" + break + fi +done + +AC_SUBST(LIB_QT) +AC_SUBST(LIB_QPE) + +AC_SUBST(kde_qtver) +]) + +AC_DEFUN([AC_PATH_QT], +[ +AC_PATH_QT_1_3 +]) + +AC_DEFUN([KDE_CHECK_UIC_PLUGINS], +[ +AC_REQUIRE([AC_PATH_QT_MOC_UIC]) + +if test x$ac_uic_supports_libpath = xyes; then + +AC_MSG_CHECKING([if UIC has KDE plugins available]) +AC_CACHE_VAL(kde_cv_uic_plugins, +[ +cat > actest.ui << EOF + +NewConnectionDialog + + + + testInput + + + + +EOF + + + +kde_cv_uic_plugins=no +kde_line="$UIC_PATH -L $kde_widgetdir" +if test x$ac_uic_supports_nounload = xyes; then + kde_line="$kde_line -nounload" +fi +kde_line="$kde_line -impl actest.h actest.ui > actest.cpp" +if AC_TRY_EVAL(kde_line); then + # if you're trying to debug this check and think it's incorrect, + # better check your installation. The check _is_ correct - your + # installation is not. + if test -f actest.cpp && grep klineedit actest.cpp > /dev/null; then + kde_cv_uic_plugins=yes + fi +fi +rm -f actest.ui actest.cpp +]) + +AC_MSG_RESULT([$kde_cv_uic_plugins]) +if test "$kde_cv_uic_plugins" != yes; then + AC_MSG_ERROR([ +you need to install kdelibs first. + +If you did install kdelibs, then the Qt version that is picked up by +this configure is not the same version you used to compile kdelibs. +The Qt Plugin installed by kdelibs is *ONLY* loadable if it is the +_same Qt version_, compiled with the _same compiler_ and the same Qt +configuration settings. +]) +fi +fi +]) + +AC_DEFUN([KDE_CHECK_FINAL], +[ + AC_ARG_ENABLE(final, + AC_HELP_STRING([--enable-final], + [build size optimized apps (experimental - needs lots of memory)]), + kde_use_final=$enableval, kde_use_final=no) + + if test "x$kde_use_final" = "xyes"; then + KDE_USE_FINAL_TRUE="" + KDE_USE_FINAL_FALSE="#" + else + KDE_USE_FINAL_TRUE="#" + KDE_USE_FINAL_FALSE="" + fi + AC_SUBST(KDE_USE_FINAL_TRUE) + AC_SUBST(KDE_USE_FINAL_FALSE) +]) + +AC_DEFUN([KDE_CHECK_CLOSURE], +[ + AC_ARG_ENABLE(closure, + AC_HELP_STRING([--enable-closure],[delay template instantiation]), + kde_use_closure=$enableval, kde_use_closure=no) + + KDE_NO_UNDEFINED="" + if test "x$kde_use_closure" = "xyes"; then + KDE_USE_CLOSURE_TRUE="" + KDE_USE_CLOSURE_FALSE="#" +# CXXFLAGS="$CXXFLAGS $REPO" + else + KDE_USE_CLOSURE_TRUE="#" + KDE_USE_CLOSURE_FALSE="" + KDE_NO_UNDEFINED="" + case $host in + *-*-linux-gnu) + KDE_CHECK_COMPILER_FLAG([Wl,--no-undefined], + [KDE_CHECK_COMPILER_FLAG([Wl,--allow-shlib-undefined], + [KDE_NO_UNDEFINED="-Wl,--no-undefined -Wl,--allow-shlib-undefined"], + [KDE_NO_UNDEFINED=""])], + [KDE_NO_UNDEFINED=""]) + ;; + esac + fi + AC_SUBST(KDE_USE_CLOSURE_TRUE) + AC_SUBST(KDE_USE_CLOSURE_FALSE) + AC_SUBST(KDE_NO_UNDEFINED) +]) + +dnl Check if the linker supports --enable-new-dtags and --as-needed +AC_DEFUN([KDE_CHECK_NEW_LDFLAGS], +[ + AC_ARG_ENABLE(new_ldflags, + AC_HELP_STRING([--enable-new-ldflags], + [enable the new linker flags]), + kde_use_new_ldflags=$enableval, + kde_use_new_ldflags=no) + + LDFLAGS_AS_NEEDED="" + LDFLAGS_NEW_DTAGS="" + if test "x$kde_use_new_ldflags" = "xyes"; then + LDFLAGS_NEW_DTAGS="" + KDE_CHECK_COMPILER_FLAG([Wl,--enable-new-dtags], + [LDFLAGS_NEW_DTAGS="-Wl,--enable-new-dtags"],) + + KDE_CHECK_COMPILER_FLAG([Wl,--as-needed], + [LDFLAGS_AS_NEEDED="-Wl,--as-needed"],) + fi + AC_SUBST(LDFLAGS_AS_NEEDED) + AC_SUBST(LDFLAGS_NEW_DTAGS) +]) + +AC_DEFUN([KDE_CHECK_NMCHECK], +[ + AC_ARG_ENABLE(nmcheck,AC_HELP_STRING([--enable-nmcheck],[enable automatic namespace cleanness check]), + kde_use_nmcheck=$enableval, kde_use_nmcheck=no) + + if test "$kde_use_nmcheck" = "yes"; then + KDE_USE_NMCHECK_TRUE="" + KDE_USE_NMCHECK_FALSE="#" + else + KDE_USE_NMCHECK_TRUE="#" + KDE_USE_NMCHECK_FALSE="" + fi + AC_SUBST(KDE_USE_NMCHECK_TRUE) + AC_SUBST(KDE_USE_NMCHECK_FALSE) +]) + +AC_DEFUN([KDE_EXPAND_MAKEVAR], [ +savex=$exec_prefix +test "x$exec_prefix" = xNONE && exec_prefix=$prefix +tmp=$$2 +while $1=`eval echo "$tmp"`; test "x$$1" != "x$tmp"; do tmp=$$1; done +exec_prefix=$savex +]) + +dnl ------------------------------------------------------------------------ +dnl Now, the same with KDE +dnl $(KDE_LDFLAGS) will be the kdeliblocation (if needed) +dnl and $(kde_includes) will be the kdehdrlocation (if needed) +dnl ------------------------------------------------------------------------ +dnl +AC_DEFUN([AC_BASE_PATH_KDE], +[ +AC_REQUIRE([KDE_CHECK_STL]) +AC_REQUIRE([AC_PATH_QT])dnl +AC_REQUIRE([KDE_CHECK_LIB64]) + +AC_CHECK_RPATH +AC_MSG_CHECKING([for KDE]) + +if test "${prefix}" != NONE; then + kde_includes=${includedir} + KDE_EXPAND_MAKEVAR(ac_kde_includes, includedir) + + kde_libraries=${libdir} + KDE_EXPAND_MAKEVAR(ac_kde_libraries, libdir) + +else + ac_kde_includes= + ac_kde_libraries= + kde_libraries="" + kde_includes="" +fi + +AC_CACHE_VAL(ac_cv_have_kde, +[#try to guess kde locations + +if test "$kde_qtver" = 1; then + kde_check_header="ksock.h" + kde_check_lib="libkdecore.la" +else + kde_check_header="ksharedptr.h" + kde_check_lib="libkio.la" +fi + +if test -z "$1"; then + +kde_incdirs="$kde_libs_prefix/include /usr/lib/kde/include /usr/local/kde/include /usr/local/include /usr/kde/include /usr/include/kde /usr/include /opt/kde3/include /opt/kde/include $x_includes $qt_includes" +test -n "$KDEDIR" && kde_incdirs="$KDEDIR/include $KDEDIR/include/kde $KDEDIR $kde_incdirs" +kde_incdirs="$ac_kde_includes $kde_incdirs" +AC_FIND_FILE($kde_check_header, $kde_incdirs, kde_incdir) +ac_kde_includes="$kde_incdir" + +if test -n "$ac_kde_includes" && test ! -r "$ac_kde_includes/$kde_check_header"; then + AC_MSG_ERROR([ +in the prefix, you've chosen, are no KDE headers installed. This will fail. +So, check this please and use another prefix!]) +fi + +kde_libdirs="$kde_libs_prefix/lib${kdelibsuff} /usr/lib/kde/lib${kdelibsuff} /usr/local/kde/lib${kdelibsuff} /usr/kde/lib${kdelibsuff} /usr/lib${kdelibsuff}/kde /usr/lib${kdelibsuff}/kde3 /usr/lib${kdelibsuff} /usr/X11R6/lib${kdelibsuff} /usr/local/lib${kdelibsuff} /opt/kde3/lib${kdelibsuff} /opt/kde/lib${kdelibsuff} /usr/X11R6/kde/lib${kdelibsuff}" +test -n "$KDEDIR" && kde_libdirs="$KDEDIR/lib${kdelibsuff} $KDEDIR $kde_libdirs" +kde_libdirs="$ac_kde_libraries $libdir $kde_libdirs" +AC_FIND_FILE($kde_check_lib, $kde_libdirs, kde_libdir) +ac_kde_libraries="$kde_libdir" + +kde_widgetdir=NO +dnl this might be somewhere else +AC_FIND_FILE("kde3/plugins/designer/kdewidgets.la", $kde_libdirs, kde_widgetdir) + +if test -n "$ac_kde_libraries" && test ! -r "$ac_kde_libraries/$kde_check_lib"; then +AC_MSG_ERROR([ +in the prefix, you've chosen, are no KDE libraries installed. This will fail. +So, check this please and use another prefix!]) +fi + +if test -n "$kde_widgetdir" && test ! -r "$kde_widgetdir/kde3/plugins/designer/kdewidgets.la"; then +AC_MSG_ERROR([ +I can't find the designer plugins. These are required and should have been installed +by kdelibs]) +fi + +if test -n "$kde_widgetdir"; then + kde_widgetdir="$kde_widgetdir/kde3/plugins/designer" +fi + + +if test "$ac_kde_includes" = NO || test "$ac_kde_libraries" = NO || test "$kde_widgetdir" = NO; then + ac_cv_have_kde="have_kde=no" +else + ac_cv_have_kde="have_kde=yes \ + ac_kde_includes=$ac_kde_includes ac_kde_libraries=$ac_kde_libraries" +fi + +else dnl test -z $1, e.g. from kdelibs + + ac_cv_have_kde="have_kde=no" + +fi +])dnl + +eval "$ac_cv_have_kde" + +if test "$have_kde" != "yes"; then + if test "${prefix}" = NONE; then + ac_kde_prefix="$ac_default_prefix" + else + ac_kde_prefix="$prefix" + fi + if test "$exec_prefix" = NONE; then + ac_kde_exec_prefix="$ac_kde_prefix" + AC_MSG_RESULT([will be installed in $ac_kde_prefix]) + else + ac_kde_exec_prefix="$exec_prefix" + AC_MSG_RESULT([will be installed in $ac_kde_prefix and $ac_kde_exec_prefix]) + fi + + kde_libraries="${libdir}" + kde_includes="${includedir}" + +else + ac_cv_have_kde="have_kde=yes \ + ac_kde_includes=$ac_kde_includes ac_kde_libraries=$ac_kde_libraries" + AC_MSG_RESULT([libraries $ac_kde_libraries, headers $ac_kde_includes]) + + kde_libraries="$ac_kde_libraries" + kde_includes="$ac_kde_includes" +fi +AC_SUBST(kde_libraries) +AC_SUBST(kde_includes) + +if test "$kde_includes" = "$x_includes" || test "$kde_includes" = "$qt_includes" || test "$kde_includes" = "/usr/include"; then + KDE_INCLUDES="" +else + KDE_INCLUDES="-I$kde_includes" + all_includes="$KDE_INCLUDES $all_includes" +fi + +KDE_DEFAULT_CXXFLAGS="-DQT_CLEAN_NAMESPACE -DQT_NO_ASCII_CAST -DQT_NO_STL -DQT_NO_COMPAT -DQT_NO_TRANSLATION" + +KDE_LDFLAGS="-L$kde_libraries" +if test ! "$kde_libraries" = "$x_libraries" && test ! "$kde_libraries" = "$qt_libraries" ; then + all_libraries="$KDE_LDFLAGS $all_libraries" +fi + +AC_SUBST(KDE_LDFLAGS) +AC_SUBST(KDE_INCLUDES) + +AC_REQUIRE([KDE_CHECK_EXTRA_LIBS]) + +all_libraries="$all_libraries $USER_LDFLAGS" +all_includes="$all_includes $USER_INCLUDES" +AC_SUBST(all_includes) +AC_SUBST(all_libraries) + +if test -z "$1"; then +KDE_CHECK_UIC_PLUGINS +fi + +ac_kde_libraries="$kde_libdir" + +AC_SUBST(AUTODIRS) + + +]) + +AC_DEFUN([KDE_CHECK_EXTRA_LIBS], +[ +AC_MSG_CHECKING(for extra includes) +AC_ARG_WITH(extra-includes,AC_HELP_STRING([--with-extra-includes=DIR],[adds non standard include paths]), + kde_use_extra_includes="$withval", + kde_use_extra_includes=NONE +) +kde_extra_includes= +if test -n "$kde_use_extra_includes" && \ + test "$kde_use_extra_includes" != "NONE"; then + + ac_save_ifs=$IFS + IFS=':' + for dir in $kde_use_extra_includes; do + kde_extra_includes="$kde_extra_includes $dir" + USER_INCLUDES="$USER_INCLUDES -I$dir" + done + IFS=$ac_save_ifs + kde_use_extra_includes="added" +else + kde_use_extra_includes="no" +fi +AC_SUBST(USER_INCLUDES) + +AC_MSG_RESULT($kde_use_extra_includes) + +kde_extra_libs= +AC_MSG_CHECKING(for extra libs) +AC_ARG_WITH(extra-libs,AC_HELP_STRING([--with-extra-libs=DIR],[adds non standard library paths]), + kde_use_extra_libs=$withval, + kde_use_extra_libs=NONE +) +if test -n "$kde_use_extra_libs" && \ + test "$kde_use_extra_libs" != "NONE"; then + + ac_save_ifs=$IFS + IFS=':' + for dir in $kde_use_extra_libs; do + kde_extra_libs="$kde_extra_libs $dir" + KDE_EXTRA_RPATH="$KDE_EXTRA_RPATH -R $dir" + USER_LDFLAGS="$USER_LDFLAGS -L$dir" + done + IFS=$ac_save_ifs + kde_use_extra_libs="added" +else + kde_use_extra_libs="no" +fi + +AC_SUBST(USER_LDFLAGS) + +AC_MSG_RESULT($kde_use_extra_libs) + +]) + +AC_DEFUN([KDE_1_CHECK_PATH_HEADERS], +[ + AC_MSG_CHECKING([for KDE headers installed]) + AC_LANG_SAVE + AC_LANG_CPLUSPLUS +cat > conftest.$ac_ext < +#endif +#include +#include "confdefs.h" +#include + +int main() { + printf("kde_htmldir=\\"%s\\"\n", KApplication::kde_htmldir().data()); + printf("kde_appsdir=\\"%s\\"\n", KApplication::kde_appsdir().data()); + printf("kde_icondir=\\"%s\\"\n", KApplication::kde_icondir().data()); + printf("kde_sounddir=\\"%s\\"\n", KApplication::kde_sounddir().data()); + printf("kde_datadir=\\"%s\\"\n", KApplication::kde_datadir().data()); + printf("kde_locale=\\"%s\\"\n", KApplication::kde_localedir().data()); + printf("kde_cgidir=\\"%s\\"\n", KApplication::kde_cgidir().data()); + printf("kde_confdir=\\"%s\\"\n", KApplication::kde_configdir().data()); + printf("kde_mimedir=\\"%s\\"\n", KApplication::kde_mimedir().data()); + printf("kde_toolbardir=\\"%s\\"\n", KApplication::kde_toolbardir().data()); + printf("kde_wallpaperdir=\\"%s\\"\n", + KApplication::kde_wallpaperdir().data()); + printf("kde_bindir=\\"%s\\"\n", KApplication::kde_bindir().data()); + printf("kde_partsdir=\\"%s\\"\n", KApplication::kde_partsdir().data()); + printf("kde_servicesdir=\\"/tmp/dummy\\"\n"); + printf("kde_servicetypesdir=\\"/tmp/dummy\\"\n"); + printf("kde_moduledir=\\"/tmp/dummy\\"\n"); + printf("kde_styledir=\\"/tmp/dummy\\"\n"); + printf("kde_widgetdir=\\"/tmp/dummy\\"\n"); + printf("xdg_appsdir=\\"/tmp/dummy\\"\n"); + printf("xdg_menudir=\\"/tmp/dummy\\"\n"); + printf("xdg_directorydir=\\"/tmp/dummy\\"\n"); + printf("kde_kcfgdir=\\"/tmp/dummy\\"\n"); + return 0; + } +EOF + + ac_save_CPPFLAGS=$CPPFLAGS + CPPFLAGS="$all_includes $CPPFLAGS" + if AC_TRY_EVAL(ac_compile); then + AC_MSG_RESULT(yes) + else + AC_MSG_ERROR([your system is not able to compile a small KDE application! +Check, if you installed the KDE header files correctly. +For more details about this problem, look at the end of config.log.]) + fi + CPPFLAGS=$ac_save_CPPFLAGS + + AC_LANG_RESTORE +]) + +AC_DEFUN([KDE_CHECK_KDEQTADDON], +[ +AC_MSG_CHECKING(for kde-qt-addon) +AC_CACHE_VAL(kde_cv_have_kdeqtaddon, +[ + kde_ldflags_safe="$LDFLAGS" + kde_libs_safe="$LIBS" + kde_cxxflags_safe="$CXXFLAGS" + + LIBS="-lkde-qt-addon $LIBQT $LIBS" + CXXFLAGS="$CXXFLAGS -I$prefix/include -I$prefix/include/kde $all_includes" + LDFLAGS="$LDFLAGS $all_libraries $USER_LDFLAGS" + + AC_TRY_LINK([ + #include + ], + [ + QDomDocument doc; + ], + kde_cv_have_kdeqtaddon=yes, + kde_cv_have_kdeqtaddon=no + ) + + LDFLAGS=$kde_ldflags_safe + LIBS=$kde_libs_safe + CXXFLAGS=$kde_cxxflags_safe +]) + +AC_MSG_RESULT($kde_cv_have_kdeqtaddon) + +if test "$kde_cv_have_kdeqtaddon" = "no"; then + AC_MSG_ERROR([Can't find libkde-qt-addon. You need to install it first. +It is a separate package (and CVS module) named kde-qt-addon.]) +fi +]) + +AC_DEFUN([KDE_CREATE_LIBS_ALIASES], +[ + AC_REQUIRE([KDE_MISC_TESTS]) + AC_REQUIRE([KDE_CHECK_LIBDL]) + AC_REQUIRE([K_PATH_X]) + +if test $kde_qtver = 3; then + case $host in + *cygwin*) lib_kded="-lkdeinit_kded" ;; + *) lib_kded="" ;; + esac + AC_SUBST(LIB_KDED, $lib_kded) + AC_SUBST(LIB_KDECORE, "-lkdecore") + AC_SUBST(LIB_KDEUI, "-lkdeui") + AC_SUBST(LIB_KIO, "-lkio") + AC_SUBST(LIB_KJS, "-lkjs") + AC_SUBST(LIB_SMB, "-lsmb") + AC_SUBST(LIB_KAB, "-lkab") + AC_SUBST(LIB_KABC, "-lkabc") + AC_SUBST(LIB_KHTML, "-lkhtml") + AC_SUBST(LIB_KSPELL, "-lkspell") + AC_SUBST(LIB_KPARTS, "-lkparts") + AC_SUBST(LIB_KDEPRINT, "-lkdeprint") + AC_SUBST(LIB_KUTILS, "-lkutils") + AC_SUBST(LIB_KDEPIM, "-lkdepim") + AC_SUBST(LIB_KIMPROXY, "-lkimproxy") + AC_SUBST(LIB_KNEWSTUFF, "-lknewstuff") + AC_SUBST(LIB_KDNSSD, "-lkdnssd") + AC_SUBST(LIB_KUNITTEST, "-lkunittest") +# these are for backward compatibility + AC_SUBST(LIB_KSYCOCA, "-lkio") + AC_SUBST(LIB_KFILE, "-lkio") +elif test $kde_qtver = 2; then + AC_SUBST(LIB_KDECORE, "-lkdecore") + AC_SUBST(LIB_KDEUI, "-lkdeui") + AC_SUBST(LIB_KIO, "-lkio") + AC_SUBST(LIB_KSYCOCA, "-lksycoca") + AC_SUBST(LIB_SMB, "-lsmb") + AC_SUBST(LIB_KFILE, "-lkfile") + AC_SUBST(LIB_KAB, "-lkab") + AC_SUBST(LIB_KHTML, "-lkhtml") + AC_SUBST(LIB_KSPELL, "-lkspell") + AC_SUBST(LIB_KPARTS, "-lkparts") + AC_SUBST(LIB_KDEPRINT, "-lkdeprint") +else + AC_SUBST(LIB_KDECORE, "-lkdecore -lXext $(LIB_QT)") + AC_SUBST(LIB_KDEUI, "-lkdeui $(LIB_KDECORE)") + AC_SUBST(LIB_KFM, "-lkfm $(LIB_KDECORE)") + AC_SUBST(LIB_KFILE, "-lkfile $(LIB_KFM) $(LIB_KDEUI)") + AC_SUBST(LIB_KAB, "-lkab $(LIB_KIMGIO) $(LIB_KDECORE)") +fi +]) + +AC_DEFUN([AC_PATH_KDE], +[ + AC_BASE_PATH_KDE + AC_ARG_ENABLE(path-check,AC_HELP_STRING([--disable-path-check],[don't try to find out, where to install]), + [ + if test "$enableval" = "no"; + then ac_use_path_checking="default" + else ac_use_path_checking="" + fi + ], + [ + if test "$kde_qtver" = 1; + then ac_use_path_checking="" + else ac_use_path_checking="default" + fi + ] + ) + + AC_CREATE_KFSSTND($ac_use_path_checking) + + AC_SUBST_KFSSTND + KDE_CREATE_LIBS_ALIASES +]) + +dnl KDE_CHECK_FUNC_EXT(, [headers], [sample-use], [C prototype], [autoheader define], [call if found]) +AC_DEFUN([KDE_CHECK_FUNC_EXT], +[ +AC_MSG_CHECKING(for $1) +AC_CACHE_VAL(kde_cv_func_$1, +[ +AC_LANG_SAVE +AC_LANG_CPLUSPLUS +save_CXXFLAGS="$CXXFLAGS" +kde_safe_LIBS="$LIBS" +LIBS="$LIBS $X_EXTRA_LIBS" +if test "$GXX" = "yes"; then +CXXFLAGS="$CXXFLAGS -pedantic-errors" +fi +AC_TRY_COMPILE([ +$2 +], +[ +$3 +], +kde_cv_func_$1=yes, +kde_cv_func_$1=no) +CXXFLAGS="$save_CXXFLAGS" +LIBS="$kde_safe_LIBS" +AC_LANG_RESTORE +]) + +AC_MSG_RESULT($kde_cv_func_$1) + +AC_MSG_CHECKING([if $1 needs custom prototype]) +AC_CACHE_VAL(kde_cv_proto_$1, +[ +if test "x$kde_cv_func_$1" = xyes; then + kde_cv_proto_$1=no +else + case "$1" in + setenv|unsetenv|usleep|random|srandom|seteuid|mkstemps|mkstemp|revoke|vsnprintf|strlcpy|strlcat) + kde_cv_proto_$1="yes - in libkdefakes" + ;; + *) + kde_cv_proto_$1=unknown + ;; + esac +fi + +if test "x$kde_cv_proto_$1" = xunknown; then + +AC_LANG_SAVE +AC_LANG_CPLUSPLUS + kde_safe_libs=$LIBS + LIBS="$LIBS $X_EXTRA_LIBS" + AC_TRY_LINK([ +$2 + +extern "C" $4; +], +[ +$3 +], +[ kde_cv_func_$1=yes + kde_cv_proto_$1=yes ], + [kde_cv_proto_$1="$1 unavailable"] +) +LIBS=$kde_safe_libs +AC_LANG_RESTORE +fi +]) +AC_MSG_RESULT($kde_cv_proto_$1) + +if test "x$kde_cv_func_$1" = xyes; then + AC_DEFINE(HAVE_$5, 1, [Define if you have $1]) + $6 +fi +if test "x$kde_cv_proto_$1" = xno; then + AC_DEFINE(HAVE_$5_PROTO, 1, + [Define if you have the $1 prototype]) +fi + +AH_VERBATIM([_HAVE_$5_PROTO], +[ +#if !defined(HAVE_$5_PROTO) +#ifdef __cplusplus +extern "C" { +#endif +$4; +#ifdef __cplusplus +} +#endif +#endif +]) +]) + +AC_DEFUN([AC_CHECK_SETENV], +[ + KDE_CHECK_FUNC_EXT(setenv, [ +#include +], + [setenv("VAR", "VALUE", 1);], + [int setenv (const char *, const char *, int)], + [SETENV]) +]) + +AC_DEFUN([AC_CHECK_UNSETENV], +[ + KDE_CHECK_FUNC_EXT(unsetenv, [ +#include +], + [unsetenv("VAR");], + [void unsetenv (const char *)], + [UNSETENV]) +]) + +AC_DEFUN([AC_CHECK_GETDOMAINNAME], +[ + KDE_CHECK_FUNC_EXT(getdomainname, [ +#include +#include +#include +], + [ +char buffer[200]; +getdomainname(buffer, 200); +], + [#include + int getdomainname (char *, size_t)], + [GETDOMAINNAME]) +]) + +AC_DEFUN([AC_CHECK_GETHOSTNAME], +[ + KDE_CHECK_FUNC_EXT(gethostname, [ +#include +#include +], + [ +char buffer[200]; +gethostname(buffer, 200); +], + [int gethostname (char *, unsigned int)], + [GETHOSTNAME]) +]) + +AC_DEFUN([AC_CHECK_USLEEP], +[ + KDE_CHECK_FUNC_EXT(usleep, [ +#include +], + [ +usleep(200); +], + [int usleep (unsigned int)], + [USLEEP]) +]) + + +AC_DEFUN([AC_CHECK_RANDOM], +[ + KDE_CHECK_FUNC_EXT(random, [ +#include +], + [ +random(); +], + [long int random(void)], + [RANDOM]) + + KDE_CHECK_FUNC_EXT(srandom, [ +#include +], + [ +srandom(27); +], + [void srandom(unsigned int)], + [SRANDOM]) + +]) + +AC_DEFUN([AC_CHECK_INITGROUPS], +[ + KDE_CHECK_FUNC_EXT(initgroups, [ +#include +#include +#include +], + [ +char buffer[200]; +initgroups(buffer, 27); +], + [int initgroups(const char *, gid_t)], + [INITGROUPS]) +]) + +AC_DEFUN([AC_CHECK_MKSTEMPS], +[ + KDE_CHECK_FUNC_EXT(mkstemps, [ +#include +#include +], + [ +mkstemps("/tmp/aaaXXXXXX", 6); +], + [int mkstemps(char *, int)], + [MKSTEMPS]) +]) + +AC_DEFUN([AC_CHECK_MKSTEMP], +[ + KDE_CHECK_FUNC_EXT(mkstemp, [ +#include +#include +], + [ +mkstemp("/tmp/aaaXXXXXX"); +], + [int mkstemp(char *)], + [MKSTEMP]) +]) + +AC_DEFUN([AC_CHECK_MKDTEMP], +[ + KDE_CHECK_FUNC_EXT(mkdtemp, [ +#include +#include +], + [ +mkdtemp("/tmp/aaaXXXXXX"); +], + [char *mkdtemp(char *)], + [MKDTEMP]) +]) + + +AC_DEFUN([AC_CHECK_RES_INIT], +[ + AC_MSG_CHECKING([if res_init needs -lresolv]) + kde_libs_safe="$LIBS" + LIBS="$LIBS $X_EXTRA_LIBS -lresolv" + AC_TRY_LINK( + [ +#include +#include +#include +#include + ], + [ + res_init(); + ], + [ + LIBRESOLV="-lresolv" + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_RES_INIT, 1, [Define if you have the res_init function]) + ], + [ AC_MSG_RESULT(no) ] + ) + LIBS=$kde_libs_safe + AC_SUBST(LIBRESOLV) + + KDE_CHECK_FUNC_EXT(res_init, + [ +#include +#include +#include +#include + ], + [res_init()], + [int res_init(void)], + [RES_INIT]) +]) + +AC_DEFUN([AC_CHECK_STRLCPY], +[ + KDE_CHECK_FUNC_EXT(strlcpy, [ +#include +], +[ char buf[20]; + strlcpy(buf, "KDE function test", sizeof(buf)); +], + [unsigned long strlcpy(char*, const char*, unsigned long)], + [STRLCPY]) +]) + +AC_DEFUN([AC_CHECK_STRLCAT], +[ + KDE_CHECK_FUNC_EXT(strlcat, [ +#include +], +[ char buf[20]; + buf[0]='\0'; + strlcat(buf, "KDE function test", sizeof(buf)); +], + [unsigned long strlcat(char*, const char*, unsigned long)], + [STRLCAT]) +]) + +AC_DEFUN([AC_CHECK_RES_QUERY], +[ + KDE_CHECK_FUNC_EXT(res_query, [ +#include +#include +#include +#include +#include +], +[ +res_query(NULL, 0, 0, NULL, 0); +], + [int res_query(const char *, int, int, unsigned char *, int)], + [RES_QUERY]) +]) + +AC_DEFUN([AC_CHECK_DN_SKIPNAME], +[ + KDE_CHECK_FUNC_EXT(dn_skipname, [ +#include +#include +#include +#include +], +[ +dn_skipname (NULL, NULL); +], + [int dn_skipname (unsigned char *, unsigned char *)], + [DN_SKIPNAME]) +]) + + +AC_DEFUN([AC_FIND_GIF], + [AC_MSG_CHECKING([for giflib]) +AC_CACHE_VAL(ac_cv_lib_gif, +[ac_save_LIBS="$LIBS" +if test "x$kde_use_qt_emb" != "xyes" && test "x$kde_use_qt_mac" != "xyes"; then +LIBS="$all_libraries -lgif -lX11 $LIBSOCKET" +else +LIBS="$all_libraries -lgif" +fi +AC_TRY_LINK(dnl +[ +#ifdef __cplusplus +extern "C" { +#endif +int GifLastError(void); +#ifdef __cplusplus +} +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +], + [return GifLastError();], + eval "ac_cv_lib_gif=yes", + eval "ac_cv_lib_gif=no") +LIBS="$ac_save_LIBS" +])dnl +if eval "test \"`echo $ac_cv_lib_gif`\" = yes"; then + AC_MSG_RESULT(yes) + AC_DEFINE_UNQUOTED(HAVE_LIBGIF, 1, [Define if you have libgif]) +else + AC_MSG_ERROR(You need giflib30. Please install the kdesupport package) +fi +]) + +AC_DEFUN([KDE_FIND_JPEG_HELPER], +[ +AC_MSG_CHECKING([for libjpeg$2]) +AC_CACHE_VAL(ac_cv_lib_jpeg_$1, +[ +ac_save_LIBS="$LIBS" +LIBS="$all_libraries $USER_LDFLAGS -ljpeg$2 -lm" +ac_save_CFLAGS="$CFLAGS" +CFLAGS="$CFLAGS $all_includes $USER_INCLUDES" +AC_TRY_LINK( +[ +#ifdef __cplusplus +extern "C" { +#endif +void jpeg_CreateDecompress(); +#ifdef __cplusplus +} +#endif +], +[jpeg_CreateDecompress();], + eval "ac_cv_lib_jpeg_$1=-ljpeg$2", + eval "ac_cv_lib_jpeg_$1=no") +LIBS="$ac_save_LIBS" +CFLAGS="$ac_save_CFLAGS" +]) + +if eval "test ! \"`echo $ac_cv_lib_jpeg_$1`\" = no"; then + LIBJPEG="$ac_cv_lib_jpeg_$1" + AC_MSG_RESULT($ac_cv_lib_jpeg_$1) +else + AC_MSG_RESULT(no) + $3 +fi + +]) + +AC_DEFUN([AC_FIND_JPEG], +[ +dnl first look for libraries +KDE_FIND_JPEG_HELPER(6b, 6b, + KDE_FIND_JPEG_HELPER(normal, [], + [ + LIBJPEG= + ] + ) +) + +dnl then search the headers (can't use simply AC_TRY_xxx, as jpeglib.h +dnl requires system dependent includes loaded before it) +jpeg_incdirs="$includedir /usr/include /usr/local/include $kde_extra_includes" +AC_FIND_FILE(jpeglib.h, $jpeg_incdirs, jpeg_incdir) +test "x$jpeg_incdir" = xNO && jpeg_incdir= + +dnl if headers _and_ libraries are missing, this is no error, and we +dnl continue with a warning (the user will get no jpeg support in khtml) +dnl if only one is missing, it means a configuration error, but we still +dnl only warn +if test -n "$jpeg_incdir" && test -n "$LIBJPEG" ; then + AC_DEFINE_UNQUOTED(HAVE_LIBJPEG, 1, [Define if you have libjpeg]) +else + if test -n "$jpeg_incdir" || test -n "$LIBJPEG" ; then + AC_MSG_WARN([ +There is an installation error in jpeg support. You seem to have only one +of either the headers _or_ the libraries installed. You may need to either +provide correct --with-extra-... options, or the development package of +libjpeg6b. You can get a source package of libjpeg from http://www.ijg.org/ +Disabling JPEG support. +]) + else + AC_MSG_WARN([libjpeg not found. disable JPEG support.]) + fi + jpeg_incdir= + LIBJPEG= +fi + +AC_SUBST(LIBJPEG) +AH_VERBATIM(_AC_CHECK_JPEG, +[/* + * jpeg.h needs HAVE_BOOLEAN, when the system uses boolean in system + * headers and I'm too lazy to write a configure test as long as only + * unixware is related + */ +#ifdef _UNIXWARE +#define HAVE_BOOLEAN +#endif +]) +]) + +AC_DEFUN([KDE_CHECK_QT_JPEG], +[ +if test -n "$LIBJPEG"; then +AC_MSG_CHECKING([if Qt needs $LIBJPEG]) +AC_CACHE_VAL(kde_cv_qt_jpeg, +[ +AC_LANG_SAVE +AC_LANG_CPLUSPLUS +ac_save_LIBS="$LIBS" +LIBS="$all_libraries $USER_LDFLAGS $LIBQT" +LIBS=`echo $LIBS | sed "s/$LIBJPEG//"` +ac_save_CXXFLAGS="$CXXFLAGS" +CXXFLAGS="$CXXFLAGS $all_includes $USER_INCLUDES" +AC_TRY_LINK( +[#include ], + [ + int argc; + char** argv; + QApplication app(argc, argv);], + eval "kde_cv_qt_jpeg=no", + eval "kde_cv_qt_jpeg=yes") +LIBS="$ac_save_LIBS" +CXXFLAGS="$ac_save_CXXFLAGS" +AC_LANG_RESTORE +fi +]) + +if eval "test ! \"`echo $kde_cv_qt_jpeg`\" = no"; then + AC_MSG_RESULT(yes) + LIBJPEG_QT='$(LIBJPEG)' +else + AC_MSG_RESULT(no) + LIBJPEG_QT= +fi + +]) + +AC_DEFUN([AC_FIND_ZLIB], +[ +AC_REQUIRE([KDE_CHECK_EXTRA_LIBS]) +AC_MSG_CHECKING([for libz]) +AC_CACHE_VAL(ac_cv_lib_z, +[ +kde_save_LIBS="$LIBS" +LIBS="$all_libraries $USER_LDFLAGS -lz $LIBSOCKET" +kde_save_CFLAGS="$CFLAGS" +CFLAGS="$CFLAGS $all_includes $USER_INCLUDES" +AC_TRY_LINK(dnl +[ +#include +#include +], +[ + char buf[42]; + gzFile f = (gzFile) 0; + /* this would segfault.. but we only link, don't run */ + (void) gzgets(f, buf, sizeof(buf)); + + return (strcmp(zlibVersion(), ZLIB_VERSION) == 0); +], + eval "ac_cv_lib_z='-lz'", + eval "ac_cv_lib_z=no") +LIBS="$kde_save_LIBS" +CFLAGS="$kde_save_CFLAGS" +])dnl +if test ! "$ac_cv_lib_z" = no; then + AC_DEFINE_UNQUOTED(HAVE_LIBZ, 1, [Define if you have libz]) + LIBZ="$ac_cv_lib_z" + AC_MSG_RESULT($ac_cv_lib_z) +else + AC_MSG_ERROR(not found. + Possibly configure picks up an outdated version + installed by XFree86. Remove it from your system. + + Check your installation and look into config.log) + LIBZ="" +fi +AC_SUBST(LIBZ) +]) + +AC_DEFUN([KDE_TRY_TIFFLIB], +[ +AC_MSG_CHECKING([for libtiff $1]) + +AC_CACHE_VAL(kde_cv_libtiff_$1, +[ +AC_LANG_SAVE +AC_LANG_CPLUSPLUS +kde_save_LIBS="$LIBS" +if test "x$kde_use_qt_emb" != "xyes" && test "x$kde_use_qt_mac" != "xyes"; then +LIBS="$all_libraries $USER_LDFLAGS -l$1 $LIBJPEG $LIBZ -lX11 $LIBSOCKET -lm" +else +LIBS="$all_libraries $USER_LDFLAGS -l$1 $LIBJPEG $LIBZ -lm" +fi +kde_save_CXXFLAGS="$CXXFLAGS" +CXXFLAGS="$CXXFLAGS $all_includes $USER_INCLUDES" + +AC_TRY_LINK(dnl +[ +#include +], + [return (TIFFOpen( "", "r") == 0); ], +[ + kde_cv_libtiff_$1="-l$1 $LIBJPEG $LIBZ" +], [ + kde_cv_libtiff_$1=no +]) + +LIBS="$kde_save_LIBS" +CXXFLAGS="$kde_save_CXXFLAGS" +AC_LANG_RESTORE +]) + +if test "$kde_cv_libtiff_$1" = "no"; then + AC_MSG_RESULT(no) + LIBTIFF="" + $3 +else + LIBTIFF="$kde_cv_libtiff_$1" + AC_MSG_RESULT(yes) + AC_DEFINE_UNQUOTED(HAVE_LIBTIFF, 1, [Define if you have libtiff]) + $2 +fi + +]) + +AC_DEFUN([AC_FIND_TIFF], +[ +AC_REQUIRE([K_PATH_X]) +AC_REQUIRE([AC_FIND_ZLIB]) +AC_REQUIRE([AC_FIND_JPEG]) +AC_REQUIRE([KDE_CHECK_EXTRA_LIBS]) + +KDE_TRY_TIFFLIB(tiff, [], + KDE_TRY_TIFFLIB(tiff34)) + +AC_SUBST(LIBTIFF) +]) + +AC_DEFUN([KDE_FIND_LIBEXR], +[ +AC_REQUIRE([KDE_CHECK_EXTRA_LIBS]) +AC_REQUIRE([AC_FIND_ZLIB]) +AC_CACHE_VAL(ac_cv_libexr, +[ + if test -z "$PKG_CONFIG"; then + AC_PATH_PROG(PKG_CONFIG, pkg-config, no) + fi + + AC_MSG_CHECKING([for OpenEXR libraries]) + + if test "$PKG_CONFIG" = "no" ; then + AC_MSG_RESULT(no) + echo "*** The pkg-config script could not be found. Make sure it is" + echo "*** in your path, or set the PKG_CONFIG environment variable" + echo "*** to the full path to pkg-config." + echo "*** Or see http://www.freedesktop.org/software/pkgconfig to get pkg-config." + else + if ! $PKG_CONFIG --exists OpenEXR ; then + AC_MSG_RESULT(no) + EXRSTATUS=no + else + if ! $PKG_CONFIG --atleast-version="1.1.1" OpenEXR ; then + AC_MSG_RESULT(no) + EXRSTATUS=old + else + kde_save_LIBS="$LIBS" + LIBS="$LIBS $all_libraries $USER_LDFLAGS `pkg-config --libs OpenEXR` $LIBZ" + AC_LANG_SAVE + AC_LANG_CPLUSPLUS + kde_save_CXXFLAGS="$CXXFLAGS" + EXR_FLAGS=`$PKG_CONFIG --cflags OpenEXR` + CXXFLAGS="$CXXFLAGS $all_includes $USER_INCLUDES $EXR_FLAGS" + + AC_TRY_LINK(dnl + [ + #include + ], + [ + using namespace Imf; + RgbaInputFile file ("dummy"); + return 0; + ], + eval "ac_cv_libexr='`pkg-config --libs OpenEXR`'", + eval "ac_cv_libexr=no" + ) + LIBS="$kde_save_LIBS" + CXXFLAGS="$kde_save_CXXFLAGS" + AC_LANG_RESTORE + ])dnl + if eval "test ! \"`echo $ac_cv_libexr`\" = no"; then + AC_DEFINE_UNQUOTED(HAVE_EXR, 1, [Define if you have OpenEXR]) + LIB_EXR="$ac_cv_libexr" + AC_MSG_RESULT($ac_cv_libexr) + else + AC_MSG_RESULT(no) + LIB_EXR="" + fi + fi + fi + fi + AC_SUBST(LIB_EXR) + AC_SUBST(EXR_FLAGS) +]) + + + +AC_DEFUN([AC_FIND_PNG], +[ +AC_REQUIRE([KDE_CHECK_EXTRA_LIBS]) +AC_REQUIRE([AC_FIND_ZLIB]) +AC_MSG_CHECKING([for libpng]) +AC_CACHE_VAL(ac_cv_lib_png, +[ +kde_save_LIBS="$LIBS" +if test "x$kde_use_qt_emb" != "xyes" && test "x$kde_use_qt_mac" != "xyes"; then +LIBS="$LIBS $all_libraries $USER_LDFLAGS -lpng $LIBZ -lm -lX11 $LIBSOCKET" +else +LIBS="$LIBS $all_libraries $USER_LDFLAGS -lpng $LIBZ -lm" +fi +kde_save_CFLAGS="$CFLAGS" +CFLAGS="$CFLAGS $all_includes $USER_INCLUDES" + +AC_TRY_LINK(dnl + [ + #include + ], + [ + png_structp png_ptr = png_create_read_struct( /* image ptr */ + PNG_LIBPNG_VER_STRING, 0, 0, 0 ); + return( png_ptr != 0 ); + ], + eval "ac_cv_lib_png='-lpng $LIBZ -lm'", + eval "ac_cv_lib_png=no" +) +LIBS="$kde_save_LIBS" +CFLAGS="$kde_save_CFLAGS" +])dnl +if eval "test ! \"`echo $ac_cv_lib_png`\" = no"; then + AC_DEFINE_UNQUOTED(HAVE_LIBPNG, 1, [Define if you have libpng]) + LIBPNG="$ac_cv_lib_png" + AC_SUBST(LIBPNG) + AC_MSG_RESULT($ac_cv_lib_png) +else + AC_MSG_RESULT(no) + LIBPNG="" + AC_SUBST(LIBPNG) +fi +]) + + +AC_DEFUN([AC_FIND_JASPER], +[ +AC_REQUIRE([KDE_CHECK_EXTRA_LIBS]) +AC_REQUIRE([AC_FIND_JPEG]) +AC_MSG_CHECKING([for jasper]) +AC_CACHE_VAL(ac_cv_jasper, +[ +kde_save_LIBS="$LIBS" +LIBS="$LIBS $all_libraries $USER_LDFLAGS -ljasper $LIBJPEG -lm" +kde_save_CFLAGS="$CFLAGS" +CFLAGS="$CFLAGS $all_includes $USER_INCLUDES" + +AC_TRY_LINK(dnl + [ + #include + ], + [ + return( jas_init() ); + ], + eval "ac_cv_jasper='-ljasper $LIBJPEG -lm'", + eval "ac_cv_jasper=no" +) +LIBS="$kde_save_LIBS" +CFLAGS="$kde_save_CFLAGS" +])dnl +if eval "test ! \"`echo $ac_cv_jasper`\" = no"; then + AC_DEFINE_UNQUOTED(HAVE_JASPER, 1, [Define if you have jasper]) + LIB_JASPER="$ac_cv_jasper" + AC_MSG_RESULT($ac_cv_jasper) +else + AC_MSG_RESULT(no) + LIB_JASPER="" +fi +AC_SUBST(LIB_JASPER) +]) + +AC_DEFUN([AC_CHECK_BOOL], +[ + AC_DEFINE_UNQUOTED(HAVE_BOOL, 1, [You _must_ have bool]) +]) + +AC_DEFUN([AC_CHECK_GNU_EXTENSIONS], +[ +AC_MSG_CHECKING(if you need GNU extensions) +AC_CACHE_VAL(ac_cv_gnu_extensions, +[ +cat > conftest.c << EOF +#include + +#ifdef __GNU_LIBRARY__ +yes +#endif +EOF + +if (eval "$ac_cpp conftest.c") 2>&5 | + egrep "yes" >/dev/null 2>&1; then + rm -rf conftest* + ac_cv_gnu_extensions=yes +else + ac_cv_gnu_extensions=no +fi +]) + +AC_MSG_RESULT($ac_cv_gnu_extensions) +if test "$ac_cv_gnu_extensions" = "yes"; then + AC_DEFINE_UNQUOTED(_GNU_SOURCE, 1, [Define if you need to use the GNU extensions]) +fi +]) + +AC_DEFUN([KDE_CHECK_COMPILER_FLAG], +[ +AC_MSG_CHECKING([whether $CXX supports -$1]) +kde_cache=`echo $1 | sed 'y% .=/+-,%____p__%'` +AC_CACHE_VAL(kde_cv_prog_cxx_$kde_cache, +[ + AC_LANG_SAVE + AC_LANG_CPLUSPLUS + save_CXXFLAGS="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS -$1" + AC_TRY_LINK([],[ return 0; ], [eval "kde_cv_prog_cxx_$kde_cache=yes"], []) + CXXFLAGS="$save_CXXFLAGS" + AC_LANG_RESTORE +]) +if eval "test \"`echo '$kde_cv_prog_cxx_'$kde_cache`\" = yes"; then + AC_MSG_RESULT(yes) + : + $2 +else + AC_MSG_RESULT(no) + : + $3 +fi +]) + +AC_DEFUN([KDE_CHECK_C_COMPILER_FLAG], +[ +AC_MSG_CHECKING([whether $CC supports -$1]) +kde_cache=`echo $1 | sed 'y% .=/+-,%____p__%'` +AC_CACHE_VAL(kde_cv_prog_cc_$kde_cache, +[ + AC_LANG_SAVE + AC_LANG_C + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS -$1" + AC_TRY_LINK([],[ return 0; ], [eval "kde_cv_prog_cc_$kde_cache=yes"], []) + CFLAGS="$save_CFLAGS" + AC_LANG_RESTORE +]) +if eval "test \"`echo '$kde_cv_prog_cc_'$kde_cache`\" = yes"; then + AC_MSG_RESULT(yes) + : + $2 +else + AC_MSG_RESULT(no) + : + $3 +fi +]) + + +dnl AC_REMOVE_FORBIDDEN removes forbidden arguments from variables +dnl use: AC_REMOVE_FORBIDDEN(CC, [-forbid -bad-option whatever]) +dnl it's all white-space separated +AC_DEFUN([AC_REMOVE_FORBIDDEN], +[ __val=$$1 + __forbid=" $2 " + if test -n "$__val"; then + __new="" + ac_save_IFS=$IFS + IFS=" " + for i in $__val; do + case "$__forbid" in + *" $i "*) AC_MSG_WARN([found forbidden $i in $1, removing it]) ;; + *) # Careful to not add spaces, where there were none, because otherwise + # libtool gets confused, if we change e.g. CXX + if test -z "$__new" ; then __new=$i ; else __new="$__new $i" ; fi ;; + esac + done + IFS=$ac_save_IFS + $1=$__new + fi +]) + + +AC_DEFUN([KDE_CHECK_FOR_BAD_COMPILER], +[ + AC_MSG_CHECKING([whether $CC is blacklisted]) + + dnl In theory we have tu run this test against $CC and $CXX + dnl in C and in C++ mode, because its perfectly legal for + dnl the user to mix compiler versions, since C has a defined + dnl ABI. + dnl + dnl For now, we assume the user is not on crack. + + AC_TRY_COMPILE([ +#ifdef __GNUC__ +#if __GNUC__ == 4 && __GNUC_MINOR__ == 0 && __GNUC_PATCHLEVEL__ == 0 +choke me +#endif +#endif +], , + kde_bad_compiler=no, + kde_bad_compiler=yes +) + + AC_MSG_RESULT($kde_bad_compiler) + +if test "$kde_bad_compiler" = "yes"; then + AC_MSG_ERROR([ + +This particular compiler version is blacklisted because it +is known to miscompile KDE. Please use a newer version, or +if that is not yet available, choose an older version. + +Please do not report a bug or bother us reporting this +configure error. We know about it, and we introduced +it by intention to avoid untraceable bugs or crashes in KDE. + +]) +fi + +]) + + +AC_DEFUN([KDE_CHECK_FOR_OPT_NOINLINE_MATCH], +[ + AC_CACHE_CHECK([whether system headers can cope with -O2 -fno-inline], + kde_cv_opt_noinline_match, + [ + kde_cv_opt_noinline_match=irrelevant + dnl if we don't use both -O2 and -fno-inline, this check is moot + if echo "$CFLAGS" | grep -e -O2 >/dev/null 2>/dev/null \ + && echo "$CFLAGS" | grep -e -fno-inline >/dev/null 2>/dev/null ; then + + ac_cflags_save="$CFLAGS" + CFLAGS="$CFLAGS -D_USE_GNU" + + AC_TRY_LINK([ + #include +], [ const char *pt, *et; + et = __extension__ ({ char __a0, __a1, __a2; (__builtin_constant_p ( ";," ) && ((size_t)(const void *)(( ";," )+ 1) - (size_t)(const void *)( ";," ) == 1) ? ((__a0 =((__const char *) ( ";," ))[0], __a0 == '\0') ? ((void) ( pt ),((void *)0) ) : ((__a1 = ((__const char *) ( ";," ))[1], __a1== '\0') ? (__extension__ (__builtin_constant_p ( __a0 ) && ( __a0 ) == '\0' ? (char *) __rawmemchr ( pt , __a0) : strchr( pt , __a0 ))) : ((__a2 = ((__const char *) ( ";," ))[2], __a2 == '\0') ? __strpbrk_c2 ( pt , __a0, __a1) :(((__const char *) ( ";," ))[3] == '\0' ? __strpbrk_c3 ( pt ,__a0, __a1, __a2): strpbrk ( pt , ";," ))))) : strpbrk ( pt , ";," )); }) ; +], + kde_cv_opt_noinline_match=yes, + kde_cv_opt_noinline_match=no + ) + + CFLAGS="$ac_cflags_save" + fi + ]) +]) + + +dnl AC_VALIDIFY_CXXFLAGS checks for forbidden flags the user may have given +AC_DEFUN([AC_VALIDIFY_CXXFLAGS], +[dnl +if test "x$kde_use_qt_emb" != "xyes"; then + AC_REMOVE_FORBIDDEN(CXX, [-fno-rtti -rpath]) + AC_REMOVE_FORBIDDEN(CXXFLAGS, [-fno-rtti -rpath]) +else + AC_REMOVE_FORBIDDEN(CXX, [-rpath]) + AC_REMOVE_FORBIDDEN(CXXFLAGS, [-rpath]) +fi +]) + +AC_DEFUN([AC_CHECK_COMPILERS], +[ + AC_ARG_ENABLE(debug, + AC_HELP_STRING([--enable-debug=ARG],[enables debug symbols (yes|no|full) [default=no]]), + [ + case $enableval in + yes) + kde_use_debug_code="yes" + kde_use_debug_define=no + ;; + full) + kde_use_debug_code="full" + kde_use_debug_define=no + ;; + *) + kde_use_debug_code="no" + kde_use_debug_define=yes + ;; + esac + ], + [kde_use_debug_code="no" + kde_use_debug_define=no + ]) + + dnl Just for configure --help + AC_ARG_ENABLE(dummyoption, + AC_HELP_STRING([--disable-debug], + [disables debug output and debug symbols [default=no]]), + [],[]) + + AC_ARG_ENABLE(strict, + AC_HELP_STRING([--enable-strict], + [compiles with strict compiler options (may not work!)]), + [ + if test $enableval = "no"; then + kde_use_strict_options="no" + else + kde_use_strict_options="yes" + fi + ], [kde_use_strict_options="no"]) + + AC_ARG_ENABLE(warnings,AC_HELP_STRING([--disable-warnings],[disables compilation with -Wall and similar]), + [ + if test $enableval = "no"; then + kde_use_warnings="no" + else + kde_use_warnings="yes" + fi + ], [kde_use_warnings="yes"]) + + dnl enable warnings for debug build + if test "$kde_use_debug_code" != "no"; then + kde_use_warnings=yes + fi + + AC_ARG_ENABLE(profile,AC_HELP_STRING([--enable-profile],[creates profiling infos [default=no]]), + [kde_use_profiling=$enableval], + [kde_use_profiling="no"] + ) + + dnl this prevents stupid AC_PROG_CC to add "-g" to the default CFLAGS + CFLAGS=" $CFLAGS" + + AC_PROG_CC + + AC_PROG_CPP + + if test "$GCC" = "yes"; then + if test "$kde_use_debug_code" != "no"; then + if test $kde_use_debug_code = "full"; then + CFLAGS="-g3 -fno-inline $CFLAGS" + else + CFLAGS="-g -O2 -fno-schedule-insns -fno-inline $CFLAGS" + fi + else + CFLAGS="-O2 $CFLAGS" + fi + fi + + if test "$kde_use_debug_define" = "yes"; then + CFLAGS="-DNDEBUG $CFLAGS" + fi + + + case "$host" in + *-*-sysv4.2uw*) CFLAGS="-D_UNIXWARE $CFLAGS";; + *-*-sysv5uw7*) CFLAGS="-D_UNIXWARE7 $CFLAGS";; + esac + + if test -z "$LDFLAGS" && test "$kde_use_debug_code" = "no" && test "$GCC" = "yes"; then + LDFLAGS="" + fi + + CXXFLAGS=" $CXXFLAGS" + + AC_PROG_CXX + + KDE_CHECK_FOR_BAD_COMPILER + + if test "$GXX" = "yes" || test "$CXX" = "KCC"; then + if test "$kde_use_debug_code" != "no"; then + if test "$CXX" = "KCC"; then + CXXFLAGS="+K0 -Wall -pedantic -W -Wpointer-arith -Wwrite-strings $CXXFLAGS" + else + if test "$kde_use_debug_code" = "full"; then + CXXFLAGS="-g3 -fno-inline $CXXFLAGS" + else + CXXFLAGS="-g -O2 -fno-schedule-insns -fno-inline $CXXFLAGS" + fi + fi + KDE_CHECK_COMPILER_FLAG(fno-builtin,[CXXFLAGS="-fno-builtin $CXXFLAGS"]) + + dnl convenience compiler flags + KDE_CHECK_COMPILER_FLAG(Woverloaded-virtual, [WOVERLOADED_VIRTUAL="-Woverloaded-virtual"], [WOVERLOADED_VRITUAL=""]) + AC_SUBST(WOVERLOADED_VIRTUAL) + else + if test "$CXX" = "KCC"; then + CXXFLAGS="+K3 $CXXFLAGS" + else + CXXFLAGS="-O2 $CXXFLAGS" + fi + fi + fi + + if test "$kde_use_debug_define" = "yes"; then + CXXFLAGS="-DNDEBUG -DNO_DEBUG $CXXFLAGS" + fi + + if test "$kde_use_profiling" = "yes"; then + KDE_CHECK_COMPILER_FLAG(pg, + [ + CFLAGS="-pg $CFLAGS" + CXXFLAGS="-pg $CXXFLAGS" + ]) + fi + + if test "$kde_use_warnings" = "yes"; then + if test "$GCC" = "yes"; then + CXXFLAGS="-Wall -W -Wpointer-arith $CXXFLAGS" + case $host in + *-*-linux-gnu) + CFLAGS="-std=iso9899:1990 -W -Wall -Wchar-subscripts -Wshadow -Wpointer-arith -Wmissing-prototypes -Wwrite-strings -D_XOPEN_SOURCE=500 -D_BSD_SOURCE $CFLAGS" + CXXFLAGS="-ansi -D_XOPEN_SOURCE=500 -D_BSD_SOURCE -Wcast-align -Wchar-subscripts $CXXFLAGS" + KDE_CHECK_COMPILER_FLAG(Wmissing-format-attribute, [CXXFLAGS="$CXXFLAGS -Wformat-security -Wmissing-format-attribute"]) + KDE_CHECK_C_COMPILER_FLAG(Wmissing-format-attribute, [CFLAGS="$CFLAGS -Wformat-security -Wmissing-format-attribute"]) + ;; + esac + KDE_CHECK_COMPILER_FLAG(Wundef,[CXXFLAGS="-Wundef $CXXFLAGS"]) + KDE_CHECK_COMPILER_FLAG(Wno-long-long,[CXXFLAGS="-Wno-long-long $CXXFLAGS"]) + dnl ### FIXME: revert for KDE 4 + KDE_CHECK_COMPILER_FLAG(Wno-non-virtual-dtor,[CXXFLAGS="$CXXFLAGS -Wno-non-virtual-dtor"]) + fi + fi + + if test "$GXX" = "yes" && test "$kde_use_strict_options" = "yes"; then + CXXFLAGS="-Wcast-qual -Wshadow -Wcast-align $CXXFLAGS" + fi + + AC_ARG_ENABLE(pch, + AC_HELP_STRING([--enable-pch], + [enables precompiled header support (currently only KCC or gcc >=3.4+unsermake) [default=no]]), + [ kde_use_pch=$enableval ],[ kde_use_pch=no ]) + + HAVE_GCC_VISIBILITY=0 + AC_SUBST([HAVE_GCC_VISIBILITY]) + + if test "$GXX" = "yes"; then + gcc_no_reorder_blocks=NO + KDE_CHECK_COMPILER_FLAG(fno-reorder-blocks,[gcc_no_reorder_blocks=YES]) + if test $kde_use_debug_code != "no" && \ + test $kde_use_debug_code != "full" && \ + test "YES" = "$gcc_no_reorder_blocks" ; then + CXXFLAGS="$CXXFLAGS -fno-reorder-blocks" + CFLAGS="$CFLAGS -fno-reorder-blocks" + fi + KDE_CHECK_COMPILER_FLAG(fno-exceptions,[CXXFLAGS="$CXXFLAGS -fno-exceptions"]) + KDE_CHECK_COMPILER_FLAG(fno-check-new, [CXXFLAGS="$CXXFLAGS -fno-check-new"]) + KDE_CHECK_COMPILER_FLAG(fno-common, [CXXFLAGS="$CXXFLAGS -fno-common"]) + KDE_CHECK_COMPILER_FLAG(fexceptions, [USE_EXCEPTIONS="-fexceptions"], USE_EXCEPTIONS= ) + ENABLE_PERMISSIVE_FLAG="-fpermissive" + + if test "$kde_use_pch" = "yes"; then + AC_MSG_CHECKING(whether gcc supports precompiling c header files) + echo >conftest.h + if $CC -x c-header conftest.h >/dev/null 2>/dev/null; then + kde_gcc_supports_pch=yes + AC_MSG_RESULT(yes) + else + kde_gcc_supports_pch=no + AC_MSG_RESULT(no) + fi + if test "$kde_gcc_supports_pch" = "yes"; then + AC_MSG_CHECKING(whether gcc supports precompiling c++ header files) + if $CXX -x c++-header conftest.h >/dev/null 2>/dev/null; then + kde_gcc_supports_pch=yes + AC_MSG_RESULT(yes) + else + kde_gcc_supports_pch=no + AC_MSG_RESULT(no) + fi + fi + rm -f conftest.h conftest.h.gch + fi + + KDE_CHECK_FOR_OPT_NOINLINE_MATCH + if test "x$kde_cv_opt_noinline_match" = "xno" ; then + CFLAGS="`echo "$CFLAGS" | sed "s/ -fno-inline//"`" + fi + fi + AM_CONDITIONAL(unsermake_enable_pch, test "$kde_use_pch" = "yes" && test "$kde_gcc_supports_pch" = "yes") + if test "$CXX" = "KCC"; then + dnl unfortunately we currently cannot disable exception support in KCC + dnl because doing so is binary incompatible and Qt by default links with exceptions :-( + dnl KDE_CHECK_COMPILER_FLAG(-no_exceptions,[CXXFLAGS="$CXXFLAGS --no_exceptions"]) + dnl KDE_CHECK_COMPILER_FLAG(-exceptions, [USE_EXCEPTIONS="--exceptions"], USE_EXCEPTIONS= ) + + if test "$kde_use_pch" = "yes"; then + dnl TODO: support --pch-dir! + KDE_CHECK_COMPILER_FLAG(-pch,[CXXFLAGS="$CXXFLAGS --pch"]) + dnl the below works (but the dir must exist), but it's + dnl useless for a whole package. + dnl The are precompiled headers for each source file, so when compiling + dnl from scratch, it doesn't make a difference, and they take up + dnl around ~5Mb _per_ sourcefile. + dnl KDE_CHECK_COMPILER_FLAG(-pch_dir /tmp, + dnl [CXXFLAGS="$CXXFLAGS --pch_dir `pwd`/pcheaders"]) + fi + dnl this flag controls inlining. by default KCC inlines in optimisation mode + dnl all implementations that are defined inside the class {} declaration. + dnl because of templates-compatibility with broken gcc compilers, this + dnl can cause excessive inlining. This flag limits it to a sane level + KDE_CHECK_COMPILER_FLAG(-inline_keyword_space_time=6,[CXXFLAGS="$CXXFLAGS --inline_keyword_space_time=6"]) + KDE_CHECK_COMPILER_FLAG(-inline_auto_space_time=2,[CXXFLAGS="$CXXFLAGS --inline_auto_space_time=2"]) + KDE_CHECK_COMPILER_FLAG(-inline_implicit_space_time=2.0,[CXXFLAGS="$CXXFLAGS --inline_implicit_space_time=2.0"]) + KDE_CHECK_COMPILER_FLAG(-inline_generated_space_time=2.0,[CXXFLAGS="$CXXFLAGS --inline_generated_space_time=2.0"]) + dnl Some source files are shared between multiple executables + dnl (or libraries) and some of those need template instantiations. + dnl In that case KCC needs to compile those sources with + dnl --one_instantiation_per_object. To make it easy for us we compile + dnl _all_ objects with that flag (--one_per is a shorthand). + KDE_CHECK_COMPILER_FLAG(-one_per, [CXXFLAGS="$CXXFLAGS --one_per"]) + fi + AC_SUBST(USE_EXCEPTIONS) + dnl obsolete macro - provided to keep things going + USE_RTTI= + AC_SUBST(USE_RTTI) + + case "$host" in + *-*-irix*) test "$GXX" = yes && CXXFLAGS="-D_LANGUAGE_C_PLUS_PLUS -D__LANGUAGE_C_PLUS_PLUS $CXXFLAGS" ;; + *-*-sysv4.2uw*) CXXFLAGS="-D_UNIXWARE $CXXFLAGS";; + *-*-sysv5uw7*) CXXFLAGS="-D_UNIXWARE7 $CXXFLAGS";; + *-*-solaris*) + if test "$GXX" = yes; then + libstdcpp=`$CXX -print-file-name=libstdc++.so` + if test ! -f $libstdcpp; then + AC_MSG_ERROR([You've compiled gcc without --enable-shared. This doesn't work with KDE. Please recompile gcc with --enable-shared to receive a libstdc++.so]) + fi + fi + ;; + esac + + AC_VALIDIFY_CXXFLAGS + + AC_PROG_CXXCPP + + if test "$GCC" = yes; then + NOOPT_CFLAGS=-O0 + fi + KDE_CHECK_COMPILER_FLAG(O0,[NOOPT_CXXFLAGS=-O0]) + + AC_ARG_ENABLE(coverage, + AC_HELP_STRING([--enable-coverage],[use gcc coverage testing]), [ + if test "$am_cv_CC_dependencies_compiler_type" = "gcc3"; then + ac_coverage_compiler="-fprofile-arcs -ftest-coverage" + ac_coverage_linker="-lgcc" + elif test "$am_cv_CC_dependencies_compiler_type" = "gcc"; then + ac_coverage_compiler="-fprofile-arcs -ftest-coverage" + ac_coverage_linker="" + else + AC_MSG_ERROR([coverage with your compiler is not supported]) + fi + CFLAGS="$CFLAGS $ac_coverage_compiler" + CXXFLAGS="$CXXFLAGS $ac_coverage_compiler" + LDFLAGS="$LDFLAGS $ac_coverage_linker" + ]) + + AC_SUBST(NOOPT_CXXFLAGS) + AC_SUBST(NOOPT_CFLAGS) + AC_SUBST(ENABLE_PERMISSIVE_FLAG) + + KDE_CHECK_NEW_LDFLAGS + KDE_CHECK_FINAL + KDE_CHECK_CLOSURE + KDE_CHECK_NMCHECK + + ifdef([AM_DEPENDENCIES], AC_REQUIRE([KDE_ADD_DEPENDENCIES]), []) +]) + +AC_DEFUN([KDE_CHECK_VISIBILITY_GCC_BUG], + [ + AC_CACHE_CHECK([for gcc -fvisibility-inlines-hidden bug], kde_cv_val_gcc_visibility_bug, + [ + AC_LANG_SAVE + AC_LANG_CPLUSPLUS + + safe_CXXFLAGS=$CXXFLAGS + safe_LDFLAGS=$LDFLAGS + CXXFLAGS="$CXXFLAGS -fPIC -fvisibility-inlines-hidden -O0" + LDFLAGS="$LDFLAGS -shared -fPIC" + + AC_TRY_LINK( + [ + /* http://gcc.gnu.org/bugzilla/show_bug.cgi?id=19664 */ + #include + int some_function( void ) __attribute__ ((visibility("default"))); + int some_function( void ) + { + std::string s("blafasel"); + return 0; + } + ], [/* elvis is alive */], + kde_cv_val_gcc_visibility_bug=no, kde_cv_val_gcc_visibility_bug=yes) + + CXXFLAGS=$safe_CXXFLAGS + LDFLAGS=$safe_LDFLAGS + AC_LANG_RESTORE + ] + ) + + if test x$kde_cv_val_gcc_visibility_bug = xno; then + CXXFLAGS="$CXXFLAGS -fvisibility-inlines-hidden" + fi + ] +) + +AC_DEFUN([KDE_ENABLE_HIDDEN_VISIBILITY], +[ + AC_BEFORE([AC_PATH_QT_1_3], [KDE_ENABLE_HIDDEN_VISIBILITY]) + + AC_MSG_CHECKING([grepping for visibility push/pop in headers]) + + if test "x$GXX" = "xyes"; then + AC_LANG_SAVE + AC_LANG_CPLUSPLUS + AC_EGREP_CPP( + [GCC visibility push], + [ #include + ], + [ + AC_MSG_RESULT(yes) + kde_stdc_visibility_patched=yes ], + [ + AC_MSG_RESULT(no) + AC_MSG_WARN([Your libstdc++ doesn't appear to be patched for + visibility support. Disabling -fvisibility=hidden]) + + kde_stdc_visibility_patched=no ]) + + AC_LANG_RESTORE + + kde_have_gcc_visibility=no + KDE_CHECK_COMPILER_FLAG(fvisibility=hidden, + [ + kde_have_gcc_visibility=yes + dnl the whole toolchain is just a mess, gcc is just too buggy + dnl to handle STL with visibility enabled. Lets reconsider + dnl when gcc 4.2 is out or when things get fixed in the compiler. + dnl Contact mueller@kde.org for details. + AC_ARG_ENABLE(gcc-hidden-visibility, + AC_HELP_STRING([--enable-gcc-hidden-visibility],[toolchain hidden visibility [default=no]]), + [kde_have_gcc_visibility=$enableval], + [kde_have_gcc_visibility=no]) + + AC_CACHE_CHECK([if Qt is patched for -fvisibility], kde_cv_val_qt_gcc_visibility_patched, + [ + AC_LANG_SAVE + AC_LANG_CPLUSPLUS + + safe_CXXFLAGS=$CXXFLAGS + CXXFLAGS="$CXXFLAGS $all_includes" + + AC_TRY_COMPILE( + [ +#include +#if Q_EXPORT - 0 != 0 +/* if this compiles, then Q_EXPORT is undefined */ +/* if Q_EXPORT is nonempty, this will break compilation */ +#endif + ], [/* elvis is alive */], + kde_cv_val_qt_gcc_visibility_patched=no, kde_cv_val_qt_gcc_visibility_patched=yes) + + CXXFLAGS=$safe_CXXFLAGS + AC_LANG_RESTORE + ] + ) + + if test x$kde_have_gcc_visibility = "xyes" && test x$kde_stdc_visibility_patched = "xyes" && test x$kde_cv_val_qt_gcc_visibility_patched = "xyes"; then + CXXFLAGS="$CXXFLAGS -fvisibility=hidden" + KDE_CHECK_VISIBILITY_GCC_BUG + HAVE_GCC_VISIBILITY=1 + AC_DEFINE_UNQUOTED(__KDE_HAVE_GCC_VISIBILITY, "$HAVE_GCC_VISIBILITY", [define to 1 if -fvisibility is supported]) + fi + ]) + fi +]) + +AC_DEFUN([KDE_ADD_DEPENDENCIES], +[ + [A]M_DEPENDENCIES(CC) + [A]M_DEPENDENCIES(CXX) +]) + +dnl just a wrapper to clean up configure.in +AC_DEFUN([KDE_PROG_LIBTOOL], +[ +AC_REQUIRE([AC_CHECK_COMPILERS]) +AC_REQUIRE([AC_ENABLE_SHARED]) +AC_REQUIRE([AC_ENABLE_STATIC]) + +AC_REQUIRE([AC_LIBTOOL_DLOPEN]) +AC_REQUIRE([KDE_CHECK_LIB64]) + +AC_OBJEXT +AC_EXEEXT + +AM_PROG_LIBTOOL +AC_LIBTOOL_CXX + +LIBTOOL_SHELL="/bin/sh ./libtool" +# LIBTOOL="$LIBTOOL --silent" +KDE_PLUGIN="-avoid-version -module -no-undefined \$(KDE_NO_UNDEFINED) \$(KDE_RPATH) \$(KDE_MT_LDFLAGS)" +AC_SUBST(KDE_PLUGIN) + +# This hack ensures that libtool creates shared libs for kunittest plugins. By default check_LTLIBRARIES makes static libs. +KDE_CHECK_PLUGIN="\$(KDE_PLUGIN) -rpath \$(libdir)" +AC_SUBST(KDE_CHECK_PLUGIN) + +# we patch configure quite some so we better keep that consistent for incremental runs +AC_SUBST(AUTOCONF,'$(SHELL) $(top_srcdir)/admin/cvs.sh configure || touch configure') +]) + +AC_DEFUN([KDE_CHECK_LIB64], +[ + AC_ARG_ENABLE(libsuffix, + AC_HELP_STRING([--enable-libsuffix], + [/lib directory suffix (64,32,none,auto[=default])]), + kdelibsuff=$enableval, kdelibsuff="auto") + + if test "$kdelibsuff" = "auto"; then + +cat > conftest.c << EOF +#include +int main() { + return 0; +} +EOF + kdelibsuff=`$CC conftest.c -o conftest.out; ldd conftest.out |sed -ne '/libc.so/{ + s,.*/lib\([[^\/]]*\)/.*,\1, + p +}'` + rm -rf conftest.* + fi + + if test "$kdelibsuff" = "no" || test "$kdelibsuff" = "none"; then + kdelibsuff= + fi + if test -z "$kdelibsuff"; then + AC_MSG_RESULT([not using lib directory suffix]) + AC_DEFINE(KDELIBSUFF, [""], Suffix for lib directories) + else + if test "$libdir" = '${exec_prefix}/lib'; then + libdir="$libdir${kdelibsuff}" + AC_SUBST([libdir], ["$libdir"]) dnl ugly hack for lib64 platforms + fi + AC_DEFINE_UNQUOTED(KDELIBSUFF, ["${kdelibsuff}"], Suffix for lib directories) + AC_MSG_RESULT([using lib directory suffix $kdelibsuff]) + fi +]) + +AC_DEFUN([KDE_CHECK_TYPES], +[ AC_CHECK_SIZEOF(int, 4)dnl + AC_CHECK_SIZEOF(short)dnl + AC_CHECK_SIZEOF(long, 4)dnl + AC_CHECK_SIZEOF(char *, 4)dnl +])dnl + +dnl Not used - kept for compat only? +AC_DEFUN([KDE_DO_IT_ALL], +[ +AC_CANONICAL_SYSTEM +AC_ARG_PROGRAM +AM_INIT_AUTOMAKE($1, $2) +AM_DISABLE_LIBRARIES +AC_PREFIX_DEFAULT(${KDEDIR:-/usr/local/kde}) +AC_CHECK_COMPILERS +KDE_PROG_LIBTOOL +AM_KDE_WITH_NLS +AC_PATH_KDE +]) + +AC_DEFUN([AC_CHECK_RPATH], +[ +AC_MSG_CHECKING(for rpath) +AC_ARG_ENABLE(rpath, + AC_HELP_STRING([--disable-rpath],[do not use the rpath feature of ld]), + USE_RPATH=$enableval, USE_RPATH=yes) + +if test -z "$KDE_RPATH" && test "$USE_RPATH" = "yes"; then + + KDE_RPATH="-R \$(libdir)" + + if test "$kde_libraries" != "$libdir"; then + KDE_RPATH="$KDE_RPATH -R \$(kde_libraries)" + fi + + if test -n "$qt_libraries"; then + KDE_RPATH="$KDE_RPATH -R \$(qt_libraries)" + fi + dnl $x_libraries is set to /usr/lib in case + if test -n "$X_LDFLAGS"; then + X_RPATH="-R \$(x_libraries)" + KDE_RPATH="$KDE_RPATH $X_RPATH" + fi + if test -n "$KDE_EXTRA_RPATH"; then + KDE_RPATH="$KDE_RPATH \$(KDE_EXTRA_RPATH)" + fi +fi +AC_SUBST(KDE_EXTRA_RPATH) +AC_SUBST(KDE_RPATH) +AC_SUBST(X_RPATH) +AC_MSG_RESULT($USE_RPATH) +]) + +dnl Check for the type of the third argument of getsockname +AC_DEFUN([AC_CHECK_SOCKLEN_T], +[ + AC_MSG_CHECKING(for socklen_t) + AC_CACHE_VAL(kde_cv_socklen_t, + [ + AC_LANG_PUSH(C++) + kde_cv_socklen_t=no + AC_TRY_COMPILE([ + #include + #include + ], + [ + socklen_t len; + getpeername(0,0,&len); + ], + [ + kde_cv_socklen_t=yes + kde_cv_socklen_t_equiv=socklen_t + ]) + AC_LANG_POP(C++) + ]) + AC_MSG_RESULT($kde_cv_socklen_t) + if test $kde_cv_socklen_t = no; then + AC_MSG_CHECKING([for socklen_t equivalent for socket functions]) + AC_CACHE_VAL(kde_cv_socklen_t_equiv, + [ + kde_cv_socklen_t_equiv=int + AC_LANG_PUSH(C++) + for t in int size_t unsigned long "unsigned long"; do + AC_TRY_COMPILE([ + #include + #include + ], + [ + $t len; + getpeername(0,0,&len); + ], + [ + kde_cv_socklen_t_equiv="$t" + break + ]) + done + AC_LANG_POP(C++) + ]) + AC_MSG_RESULT($kde_cv_socklen_t_equiv) + fi + AC_DEFINE_UNQUOTED(kde_socklen_t, $kde_cv_socklen_t_equiv, + [type to use in place of socklen_t if not defined]) + AC_DEFINE_UNQUOTED(ksize_t, $kde_cv_socklen_t_equiv, + [type to use in place of socklen_t if not defined (deprecated, use kde_socklen_t)]) +]) + +dnl This is a merge of some macros out of the gettext aclocal.m4 +dnl since we don't need anything, I took the things we need +dnl the copyright for them is: +dnl > +dnl Copyright (C) 1994, 1995, 1996, 1997, 1998 Free Software Foundation, Inc. +dnl This Makefile.in is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl This program is distributed in the hope that it will be useful, +dnl but WITHOUT ANY WARRANTY, to the extent permitted by law; without +dnl even the implied warranty of MERCHANTABILITY or FITNESS FOR A +dnl PARTICULAR PURPOSE. +dnl > +dnl for this file it is relicensed under LGPL + +AC_DEFUN([AM_KDE_WITH_NLS], + [ + dnl If we use NLS figure out what method + + AM_PATH_PROG_WITH_TEST_KDE(MSGFMT, msgfmt, + [test -n "`$ac_dir/$ac_word --version 2>&1 | grep 'GNU gettext'`"], msgfmt) + AC_PATH_PROG(GMSGFMT, gmsgfmt, $MSGFMT) + + if test -z "`$GMSGFMT --version 2>&1 | grep 'GNU gettext'`"; then + AC_MSG_RESULT([found msgfmt program is not GNU msgfmt; ignore it]) + GMSGFMT=":" + fi + MSGFMT=$GMSGFMT + AC_SUBST(GMSGFMT) + AC_SUBST(MSGFMT) + + AM_PATH_PROG_WITH_TEST_KDE(XGETTEXT, xgettext, + [test -z "`$ac_dir/$ac_word -h 2>&1 | grep '(HELP)'`"], :) + + dnl Test whether we really found GNU xgettext. + if test "$XGETTEXT" != ":"; then + dnl If it is no GNU xgettext we define it as : so that the + dnl Makefiles still can work. + if $XGETTEXT --omit-header /dev/null 2> /dev/null; then + : ; + else + AC_MSG_RESULT( + [found xgettext programs is not GNU xgettext; ignore it]) + XGETTEXT=":" + fi + fi + AC_SUBST(XGETTEXT) + + ]) + +# Search path for a program which passes the given test. +# Ulrich Drepper , 1996. + +# serial 1 +# Stephan Kulow: I appended a _KDE against name conflicts + +dnl AM_PATH_PROG_WITH_TEST_KDE(VARIABLE, PROG-TO-CHECK-FOR, +dnl TEST-PERFORMED-ON-FOUND_PROGRAM [, VALUE-IF-NOT-FOUND [, PATH]]) +AC_DEFUN([AM_PATH_PROG_WITH_TEST_KDE], +[# Extract the first word of "$2", so it can be a program name with args. +set dummy $2; ac_word=[$]2 +AC_MSG_CHECKING([for $ac_word]) +AC_CACHE_VAL(ac_cv_path_$1, +[case "[$]$1" in + /*) + ac_cv_path_$1="[$]$1" # Let the user override the test with a path. + ;; + *) + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:" + for ac_dir in ifelse([$5], , $PATH, [$5]); do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + if [$3]; then + ac_cv_path_$1="$ac_dir/$ac_word" + break + fi + fi + done + IFS="$ac_save_ifs" +dnl If no 4th arg is given, leave the cache variable unset, +dnl so AC_PATH_PROGS will keep looking. +ifelse([$4], , , [ test -z "[$]ac_cv_path_$1" && ac_cv_path_$1="$4" +])dnl + ;; +esac])dnl +$1="$ac_cv_path_$1" +if test -n "[$]$1"; then + AC_MSG_RESULT([$]$1) +else + AC_MSG_RESULT(no) +fi +AC_SUBST($1)dnl +]) + + +# Check whether LC_MESSAGES is available in . +# Ulrich Drepper , 1995. + +# serial 1 + +AC_DEFUN([AM_LC_MESSAGES], + [if test $ac_cv_header_locale_h = yes; then + AC_CACHE_CHECK([for LC_MESSAGES], am_cv_val_LC_MESSAGES, + [AC_TRY_LINK([#include ], [return LC_MESSAGES], + am_cv_val_LC_MESSAGES=yes, am_cv_val_LC_MESSAGES=no)]) + if test $am_cv_val_LC_MESSAGES = yes; then + AC_DEFINE(HAVE_LC_MESSAGES, 1, [Define if your locale.h file contains LC_MESSAGES]) + fi + fi]) + +dnl From Jim Meyering. +dnl FIXME: migrate into libit. + +AC_DEFUN([AM_FUNC_OBSTACK], +[AC_CACHE_CHECK([for obstacks], am_cv_func_obstack, + [AC_TRY_LINK([#include "obstack.h"], + [struct obstack *mem;obstack_free(mem,(char *) 0)], + am_cv_func_obstack=yes, + am_cv_func_obstack=no)]) + if test $am_cv_func_obstack = yes; then + AC_DEFINE(HAVE_OBSTACK) + else + LIBOBJS="$LIBOBJS obstack.o" + fi +]) + +dnl From Jim Meyering. Use this if you use the GNU error.[ch]. +dnl FIXME: Migrate into libit + +AC_DEFUN([AM_FUNC_ERROR_AT_LINE], +[AC_CACHE_CHECK([for error_at_line], am_cv_lib_error_at_line, + [AC_TRY_LINK([],[error_at_line(0, 0, "", 0, "");], + am_cv_lib_error_at_line=yes, + am_cv_lib_error_at_line=no)]) + if test $am_cv_lib_error_at_line = no; then + LIBOBJS="$LIBOBJS error.o" + fi + AC_SUBST(LIBOBJS)dnl +]) + +# Macro to add for using GNU gettext. +# Ulrich Drepper , 1995. + +# serial 1 +# Stephan Kulow: I put a KDE in it to avoid name conflicts + +AC_DEFUN([AM_KDE_GNU_GETTEXT], + [AC_REQUIRE([AC_PROG_MAKE_SET])dnl + AC_REQUIRE([AC_PROG_RANLIB])dnl + AC_REQUIRE([AC_HEADER_STDC])dnl + AC_REQUIRE([AC_TYPE_OFF_T])dnl + AC_REQUIRE([AC_TYPE_SIZE_T])dnl + AC_REQUIRE([AC_FUNC_ALLOCA])dnl + AC_REQUIRE([AC_FUNC_MMAP])dnl + AC_REQUIRE([AM_KDE_WITH_NLS])dnl + AC_CHECK_HEADERS([limits.h locale.h nl_types.h string.h values.h alloca.h]) + AC_CHECK_FUNCS([getcwd munmap putenv setlocale strchr strcasecmp \ +__argz_count __argz_stringify __argz_next]) + + AC_MSG_CHECKING(for stpcpy) + AC_CACHE_VAL(kde_cv_func_stpcpy, + [ + kde_safe_cxxflags=$CXXFLAGS + CXXFLAGS="-Werror" + AC_LANG_SAVE + AC_LANG_CPLUSPLUS + AC_TRY_COMPILE([ + #include + ], + [ + char buffer[200]; + stpcpy(buffer, buffer); + ], + kde_cv_func_stpcpy=yes, + kde_cv_func_stpcpy=no) + AC_LANG_RESTORE + CXXFLAGS=$kde_safe_cxxflags + ]) + AC_MSG_RESULT($kde_cv_func_stpcpy) + if eval "test \"`echo $kde_cv_func_stpcpy`\" = yes"; then + AC_DEFINE(HAVE_STPCPY, 1, [Define if you have stpcpy]) + fi + + AM_LC_MESSAGES + + if test "x$CATOBJEXT" != "x"; then + if test "x$ALL_LINGUAS" = "x"; then + LINGUAS= + else + AC_MSG_CHECKING(for catalogs to be installed) + NEW_LINGUAS= + for lang in ${LINGUAS=$ALL_LINGUAS}; do + case "$ALL_LINGUAS" in + *$lang*) NEW_LINGUAS="$NEW_LINGUAS $lang" ;; + esac + done + LINGUAS=$NEW_LINGUAS + AC_MSG_RESULT($LINGUAS) + fi + + dnl Construct list of names of catalog files to be constructed. + if test -n "$LINGUAS"; then + for lang in $LINGUAS; do CATALOGS="$CATALOGS $lang$CATOBJEXT"; done + fi + fi + + ]) + +AC_DEFUN([AC_HAVE_XPM], + [AC_REQUIRE_CPP()dnl + AC_REQUIRE([KDE_CHECK_EXTRA_LIBS]) + + test -z "$XPM_LDFLAGS" && XPM_LDFLAGS= + test -z "$XPM_INCLUDE" && XPM_INCLUDE= + + AC_ARG_WITH(xpm,AC_HELP_STRING([--without-xpm],[disable color pixmap XPM tests]), + xpm_test=$withval, xpm_test="yes") + if test "x$xpm_test" = xno; then + ac_cv_have_xpm=no + else + AC_MSG_CHECKING(for XPM) + AC_CACHE_VAL(ac_cv_have_xpm, + [ + ac_save_ldflags="$LDFLAGS" + ac_save_cflags="$CFLAGS" + if test "x$kde_use_qt_emb" != "xyes" && test "x$kde_use_qt_mac" != "xyes"; then + LDFLAGS="$LDFLAGS $X_LDFLAGS $USER_LDFLAGS $LDFLAGS $XPM_LDFLAGS $all_libraries -lXpm -lX11 -lXext $LIBZ $LIBSOCKET" + else + LDFLAGS="$LDFLAGS $X_LDFLAGS $USER_LDFLAGS $LDFLAGS $XPM_LDFLAGS $all_libraries -lXpm $LIBZ $LIBSOCKET" + fi + CFLAGS="$CFLAGS $X_INCLUDES $USER_INCLUDES" + test -n "$XPM_INCLUDE" && CFLAGS="-I$XPM_INCLUDE $CFLAGS" + AC_TRY_LINK([#include ],[], + ac_cv_have_xpm="yes",ac_cv_have_xpm="no") + LDFLAGS="$ac_save_ldflags" + CFLAGS="$ac_save_cflags" + ])dnl + + if test "$ac_cv_have_xpm" = no; then + AC_MSG_RESULT(no) + XPM_LDFLAGS="" + XPMINC="" + $2 + else + AC_DEFINE(HAVE_XPM, 1, [Define if you have XPM support]) + if test "$XPM_LDFLAGS" = ""; then + XPMLIB='-lXpm $(LIB_X11)' + else + XPMLIB="-L$XPM_LDFLAGS -lXpm "'$(LIB_X11)' + fi + if test "$XPM_INCLUDE" = ""; then + XPMINC="" + else + XPMINC="-I$XPM_INCLUDE" + fi + AC_MSG_RESULT(yes) + $1 + fi + fi + AC_SUBST(XPMINC) + AC_SUBST(XPMLIB) +]) + +AC_DEFUN([AC_HAVE_DPMS], + [AC_REQUIRE_CPP()dnl + AC_REQUIRE([KDE_CHECK_EXTRA_LIBS]) + + test -z "$DPMS_LDFLAGS" && DPMS_LDFLAGS= + test -z "$DPMS_INCLUDE" && DPMS_INCLUDE= + DPMS_LIB= + + AC_ARG_WITH(dpms,AC_HELP_STRING([--without-dpms],[disable DPMS power saving]), + dpms_test=$withval, dpms_test="yes") + if test "x$dpms_test" = xno; then + ac_cv_have_dpms=no + else + AC_MSG_CHECKING(for DPMS) + dnl Note: ac_cv_have_dpms can be no, yes, or -lXdpms. + dnl 'yes' means DPMS_LIB="", '-lXdpms' means DPMS_LIB="-lXdpms". + AC_CACHE_VAL(ac_cv_have_dpms, + [ + if test "x$kde_use_qt_emb" = "xyes" || test "x$kde_use_qt_mac" = "xyes"; then + AC_MSG_RESULT(no) + ac_cv_have_dpms="no" + else + ac_save_ldflags="$LDFLAGS" + ac_save_cflags="$CFLAGS" + ac_save_libs="$LIBS" + LDFLAGS="$LDFLAGS $DPMS_LDFLAGS $all_libraries" + LIBS="-lX11 -lXext $LIBSOCKET" + CFLAGS="$CFLAGS $X_INCLUDES" + test -n "$DPMS_INCLUDE" && CFLAGS="-I$DPMS_INCLUDE $CFLAGS" + AC_TRY_LINK([ + #include + #include + #include + #include + int foo_test_dpms() + { return DPMSSetTimeouts( 0, 0, 0, 0 ); }],[], + ac_cv_have_dpms="yes", [ + LIBS="-lXdpms $LIBS" + AC_TRY_LINK([ + #include + #include + #include + #include + int foo_test_dpms() + { return DPMSSetTimeouts( 0, 0, 0, 0 ); }],[], + [ + ac_cv_have_dpms="-lXdpms" + ],ac_cv_have_dpms="no") + ]) + LDFLAGS="$ac_save_ldflags" + CFLAGS="$ac_save_cflags" + LIBS="$ac_save_libs" + fi + ])dnl + + if test "$ac_cv_have_dpms" = no; then + AC_MSG_RESULT(no) + DPMS_LDFLAGS="" + DPMSINC="" + $2 + else + AC_DEFINE(HAVE_DPMS, 1, [Define if you have DPMS support]) + if test "$ac_cv_have_dpms" = "-lXdpms"; then + DPMS_LIB="-lXdpms" + fi + if test "$DPMS_LDFLAGS" = ""; then + DPMSLIB="$DPMS_LIB "'$(LIB_X11)' + else + DPMSLIB="$DPMS_LDFLAGS $DPMS_LIB "'$(LIB_X11)' + fi + if test "$DPMS_INCLUDE" = ""; then + DPMSINC="" + else + DPMSINC="-I$DPMS_INCLUDE" + fi + AC_MSG_RESULT(yes) + $1 + fi + fi + ac_save_cflags="$CFLAGS" + CFLAGS="$CFLAGS $X_INCLUDES" + test -n "$DPMS_INCLUDE" && CFLAGS="-I$DPMS_INCLUDE $CFLAGS" + AH_TEMPLATE(HAVE_DPMSCAPABLE_PROTO, + [Define if you have the DPMSCapable prototype in ]) + AC_CHECK_DECL(DPMSCapable, + AC_DEFINE(HAVE_DPMSCAPABLE_PROTO),, + [#include + #include ]) + AH_TEMPLATE(HAVE_DPMSINFO_PROTO, + [Define if you have the DPMSInfo prototype in ]) + AC_CHECK_DECL(DPMSInfo, + AC_DEFINE(HAVE_DPMSINFO_PROTO),, + [#include + #include ]) + CFLAGS="$ac_save_cflags" + AC_SUBST(DPMSINC) + AC_SUBST(DPMSLIB) +]) + +AC_DEFUN([AC_HAVE_GL], + [AC_REQUIRE_CPP()dnl + AC_REQUIRE([KDE_CHECK_EXTRA_LIBS]) + + test -z "$GL_LDFLAGS" && GL_LDFLAGS= + test -z "$GL_INCLUDE" && GL_INCLUDE= + + AC_ARG_WITH(gl,AC_HELP_STRING([--without-gl],[disable 3D GL modes]), + gl_test=$withval, gl_test="yes") + if test "x$kde_use_qt_emb" = "xyes"; then + # GL and Qt Embedded is a no-go for now. + ac_cv_have_gl=no + elif test "x$gl_test" = xno; then + ac_cv_have_gl=no + else + AC_MSG_CHECKING(for GL) + AC_CACHE_VAL(ac_cv_have_gl, + [ + AC_LANG_SAVE + AC_LANG_CPLUSPLUS + ac_save_ldflags=$LDFLAGS + ac_save_cxxflags=$CXXFLAGS + ac_save_libs=$LIBS + LDFLAGS="$LDFLAGS $GL_LDFLAGS $X_LDFLAGS $all_libraries" + LIBS="$LIBS -lGL -lGLU" + test "x$kde_use_qt_mac" != xyes && test "x$kde_use_qt_emb" != xyes && LIBS="$LIBS -lX11" + LIBS="$LIBS $LIB_XEXT -lm $LIBSOCKET" + CXXFLAGS="$CFLAGS $X_INCLUDES" + test -n "$GL_INCLUDE" && CFLAGS="-I$GL_INCLUDE $CFLAGS" + AC_TRY_LINK([#include +#include +], [], + ac_cv_have_gl="yes", ac_cv_have_gl="no") + AC_LANG_RESTORE + LDFLAGS=$ac_save_ldflags + CXXFLAGS=$ac_save_cxxflags + LIBS=$ac_save_libs + ])dnl + + if test "$ac_cv_have_gl" = "no"; then + AC_MSG_RESULT(no) + GL_LDFLAGS="" + GLINC="" + $2 + else + AC_DEFINE(HAVE_GL, 1, [Defines if you have GL (Mesa, OpenGL, ...)]) + if test "$GL_LDFLAGS" = ""; then + GLLIB='-lGLU -lGL $(LIB_X11)' + else + GLLIB="$GL_LDFLAGS -lGLU -lGL "'$(LIB_X11)' + fi + if test "$GL_INCLUDE" = ""; then + GLINC="" + else + GLINC="-I$GL_INCLUDE" + fi + AC_MSG_RESULT($ac_cv_have_gl) + $1 + fi + fi + AC_SUBST(GLINC) + AC_SUBST(GLLIB) +]) + + + dnl shadow password and PAM magic - maintained by ossi@kde.org + +AC_DEFUN([KDE_PAM], [ + AC_REQUIRE([KDE_CHECK_LIBDL]) + + want_pam= + AC_ARG_WITH(pam, + AC_HELP_STRING([--with-pam[=ARG]],[enable support for PAM: ARG=[yes|no|service name]]), + [ if test "x$withval" = "xyes"; then + want_pam=yes + pam_service=kde + elif test "x$withval" = "xno"; then + want_pam=no + else + want_pam=yes + pam_service=$withval + fi + ], [ pam_service=kde ]) + + use_pam= + PAMLIBS= + if test "x$want_pam" != xno; then + AC_CHECK_LIB(pam, pam_start, [ + AC_CHECK_HEADER(security/pam_appl.h, + [ pam_header=security/pam_appl.h ], + [ AC_CHECK_HEADER(pam/pam_appl.h, + [ pam_header=pam/pam_appl.h ], + [ + AC_MSG_WARN([PAM detected, but no headers found! +Make sure you have the necessary development packages installed.]) + ] + ) + ] + ) + ], , $LIBDL) + if test -z "$pam_header"; then + if test "x$want_pam" = xyes; then + AC_MSG_ERROR([--with-pam was specified, but cannot compile with PAM!]) + fi + else + AC_DEFINE(HAVE_PAM, 1, [Defines if you have PAM (Pluggable Authentication Modules)]) + PAMLIBS="$PAM_MISC_LIB -lpam $LIBDL" + use_pam=yes + + dnl darwin claims to be something special + if test "$pam_header" = "pam/pam_appl.h"; then + AC_DEFINE(HAVE_PAM_PAM_APPL_H, 1, [Define if your PAM headers are in pam/ instead of security/]) + fi + + dnl test whether struct pam_message is const (Linux) or not (Sun) + AC_MSG_CHECKING(for const pam_message) + AC_EGREP_HEADER([struct pam_message], $pam_header, + [ AC_EGREP_HEADER([const struct pam_message], $pam_header, + [AC_MSG_RESULT([const: Linux-type PAM])], + [AC_MSG_RESULT([nonconst: Sun-type PAM]) + AC_DEFINE(PAM_MESSAGE_NONCONST, 1, [Define if your PAM support takes non-const arguments (Solaris)])] + )], + [AC_MSG_RESULT([not found - assume const, Linux-type PAM])]) + fi + fi + + AC_SUBST(PAMLIBS) +]) + +dnl DEF_PAM_SERVICE(arg name, full name, define name) +AC_DEFUN([DEF_PAM_SERVICE], [ + AC_ARG_WITH($1-pam, + AC_HELP_STRING([--with-$1-pam=[val]],[override PAM service from --with-pam for $2]), + [ if test "x$use_pam" = xyes; then + $3_PAM_SERVICE=$withval + else + AC_MSG_ERROR([Cannot use use --with-$1-pam, as no PAM was detected. +You may want to enforce it by using --with-pam.]) + fi + ], + [ if test "x$use_pam" = xyes; then + $3_PAM_SERVICE="$pam_service" + fi + ]) + if test -n "$$3_PAM_SERVICE"; then + AC_MSG_RESULT([The PAM service used by $2 will be $$3_PAM_SERVICE]) + AC_DEFINE_UNQUOTED($3_PAM_SERVICE, "$$3_PAM_SERVICE", [The PAM service to be used by $2]) + fi + AC_SUBST($3_PAM_SERVICE) +]) + +AC_DEFUN([KDE_SHADOWPASSWD], [ + AC_REQUIRE([KDE_PAM]) + + AC_CHECK_LIB(shadow, getspent, + [ LIBSHADOW="-lshadow" + ac_use_shadow=yes + ], + [ dnl for UnixWare + AC_CHECK_LIB(gen, getspent, + [ LIBGEN="-lgen" + ac_use_shadow=yes + ], + [ AC_CHECK_FUNC(getspent, + [ ac_use_shadow=yes ], + [ ac_use_shadow=no ]) + ]) + ]) + AC_SUBST(LIBSHADOW) + AC_SUBST(LIBGEN) + + AC_MSG_CHECKING([for shadow passwords]) + + AC_ARG_WITH(shadow, + AC_HELP_STRING([--with-shadow],[If you want shadow password support]), + [ if test "x$withval" != "xno"; then + use_shadow=yes + else + use_shadow=no + fi + ], [ + use_shadow="$ac_use_shadow" + ]) + + if test "x$use_shadow" = xyes; then + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_SHADOW, 1, [Define if you use shadow passwords]) + else + AC_MSG_RESULT(no) + LIBSHADOW= + LIBGEN= + fi + + dnl finally make the relevant binaries setuid root, if we have shadow passwds. + dnl this still applies, if we could use it indirectly through pam. + if test "x$use_shadow" = xyes || + ( test "x$use_pam" = xyes && test "x$ac_use_shadow" = xyes ); then + case $host in + *-*-freebsd* | *-*-netbsd* | *-*-openbsd*) + SETUIDFLAGS="-m 4755 -o root";; + *) + SETUIDFLAGS="-m 4755";; + esac + fi + AC_SUBST(SETUIDFLAGS) + +]) + +AC_DEFUN([KDE_PASSWDLIBS], [ + AC_REQUIRE([KDE_MISC_TESTS]) dnl for LIBCRYPT + AC_REQUIRE([KDE_PAM]) + AC_REQUIRE([KDE_SHADOWPASSWD]) + + if test "x$use_pam" = "xyes"; then + PASSWDLIBS="$PAMLIBS" + else + PASSWDLIBS="$LIBCRYPT $LIBSHADOW $LIBGEN" + fi + + dnl FreeBSD uses a shadow-like setup, where /etc/passwd holds the users, but + dnl /etc/master.passwd holds the actual passwords. /etc/master.passwd requires + dnl root to read, so kcheckpass needs to be root (even when using pam, since pam + dnl may need to read /etc/master.passwd). + case $host in + *-*-freebsd*) + SETUIDFLAGS="-m 4755 -o root" + ;; + *) + ;; + esac + + AC_SUBST(PASSWDLIBS) +]) + +AC_DEFUN([KDE_CHECK_LIBDL], +[ +AC_CHECK_LIB(dl, dlopen, [ +LIBDL="-ldl" +ac_cv_have_dlfcn=yes +]) + +AC_CHECK_LIB(dld, shl_unload, [ +LIBDL="-ldld" +ac_cv_have_shload=yes +]) + +AC_SUBST(LIBDL) +]) + +AC_DEFUN([KDE_CHECK_DLOPEN], +[ +KDE_CHECK_LIBDL +AC_CHECK_HEADERS(dlfcn.h dl.h) +if test "$ac_cv_header_dlfcn_h" = "no"; then + ac_cv_have_dlfcn=no +fi + +if test "$ac_cv_header_dl_h" = "no"; then + ac_cv_have_shload=no +fi + +dnl XXX why change enable_dlopen? its already set by autoconf's AC_ARG_ENABLE +dnl (MM) +AC_ARG_ENABLE(dlopen, +AC_HELP_STRING([--disable-dlopen],[link statically [default=no]]), +enable_dlopen=$enableval, +enable_dlopen=yes) + +# override the user's opinion, if we know it better ;) +if test "$ac_cv_have_dlfcn" = "no" && test "$ac_cv_have_shload" = "no"; then + enable_dlopen=no +fi + +if test "$ac_cv_have_dlfcn" = "yes"; then + AC_DEFINE_UNQUOTED(HAVE_DLFCN, 1, [Define if you have dlfcn]) +fi + +if test "$ac_cv_have_shload" = "yes"; then + AC_DEFINE_UNQUOTED(HAVE_SHLOAD, 1, [Define if you have shload]) +fi + +if test "$enable_dlopen" = no ; then + test -n "$1" && eval $1 +else + test -n "$2" && eval $2 +fi + +]) + +AC_DEFUN([KDE_CHECK_DYNAMIC_LOADING], +[ +KDE_CHECK_DLOPEN(libtool_enable_shared=yes, libtool_enable_static=no) +KDE_PROG_LIBTOOL +AC_MSG_CHECKING([dynamic loading]) +eval "`egrep '^build_libtool_libs=' libtool`" +if test "$build_libtool_libs" = "yes" && test "$enable_dlopen" = "yes"; then + dynamic_loading=yes + AC_DEFINE_UNQUOTED(HAVE_DYNAMIC_LOADING) +else + dynamic_loading=no +fi +AC_MSG_RESULT($dynamic_loading) +if test "$dynamic_loading" = "yes"; then + $1 +else + $2 +fi +]) + +AC_DEFUN([KDE_ADD_INCLUDES], +[ +if test -z "$1"; then + test_include="Pix.h" +else + test_include="$1" +fi + +AC_MSG_CHECKING([for libg++ ($test_include)]) + +AC_CACHE_VAL(kde_cv_libgpp_includes, +[ +kde_cv_libgpp_includes=no + + for ac_dir in \ + \ + /usr/include/g++ \ + /usr/include \ + /usr/unsupported/include \ + /opt/include \ + $extra_include \ + ; \ + do + if test -r "$ac_dir/$test_include"; then + kde_cv_libgpp_includes=$ac_dir + break + fi + done +]) + +AC_MSG_RESULT($kde_cv_libgpp_includes) +if test "$kde_cv_libgpp_includes" != "no"; then + all_includes="-I$kde_cv_libgpp_includes $all_includes $USER_INCLUDES" +fi +]) +]) + +AC_DEFUN([KDE_CHECK_LIBPTHREAD], +[ + dnl This code is here specifically to handle the + dnl various flavors of threading library on FreeBSD + dnl 4-, 5-, and 6-, and the (weird) rules around it. + dnl There may be an environment PTHREAD_LIBS that + dnl specifies what to use; otherwise, search for it. + dnl -pthread is special cased and unsets LIBPTHREAD + dnl below if found. + LIBPTHREAD="" + + if test -n "$PTHREAD_LIBS"; then + if test "x$PTHREAD_LIBS" = "x-pthread" ; then + LIBPTHREAD="PTHREAD" + else + PTHREAD_LIBS_save="$PTHREAD_LIBS" + PTHREAD_LIBS=`echo "$PTHREAD_LIBS_save" | sed -e 's,^-l,,g'` + AC_MSG_CHECKING([for pthread_create in $PTHREAD_LIBS]) + KDE_CHECK_LIB($PTHREAD_LIBS, pthread_create, [ + LIBPTHREAD="$PTHREAD_LIBS_save"]) + PTHREAD_LIBS="$PTHREAD_LIBS_save" + fi + fi + + dnl Is this test really needed, in the face of the Tru64 test below? + if test -z "$LIBPTHREAD"; then + AC_CHECK_LIB(pthread, pthread_create, [LIBPTHREAD="-lpthread"]) + fi + + dnl This is a special Tru64 check, see BR 76171 issue #18. + if test -z "$LIBPTHREAD" ; then + AC_MSG_CHECKING([for pthread_create in -lpthread]) + kde_safe_libs=$LIBS + LIBS="$LIBS -lpthread" + AC_TRY_LINK([#include ],[(void)pthread_create(0,0,0,0);],[ + AC_MSG_RESULT(yes) + LIBPTHREAD="-lpthread"],[ + AC_MSG_RESULT(no)]) + LIBS=$kde_safe_libs + fi + + dnl Un-special-case for FreeBSD. + if test "x$LIBPTHREAD" = "xPTHREAD" ; then + LIBPTHREAD="" + fi + + AC_SUBST(LIBPTHREAD) +]) + +AC_DEFUN([KDE_CHECK_PTHREAD_OPTION], +[ + USE_THREADS="" + if test -z "$LIBPTHREAD"; then + KDE_CHECK_COMPILER_FLAG(pthread, [USE_THREADS="-D_THREAD_SAFE -pthread"]) + fi + + AH_VERBATIM(__svr_define, [ +#if defined(__SVR4) && !defined(__svr4__) +#define __svr4__ 1 +#endif +]) + case $host_os in + solaris*) + KDE_CHECK_COMPILER_FLAG(mt, [USE_THREADS="-mt"]) + CPPFLAGS="$CPPFLAGS -D_REENTRANT -D_POSIX_PTHREAD_SEMANTICS -DUSE_SOLARIS -DSVR4" + ;; + freebsd*) + CPPFLAGS="$CPPFLAGS -D_THREAD_SAFE $PTHREAD_CFLAGS" + ;; + aix*) + CPPFLAGS="$CPPFLAGS -D_THREAD_SAFE" + LIBPTHREAD="$LIBPTHREAD -lc_r" + ;; + linux*) CPPFLAGS="$CPPFLAGS -D_REENTRANT" + if test "$CXX" = "KCC"; then + CXXFLAGS="$CXXFLAGS --thread_safe" + NOOPT_CXXFLAGS="$NOOPT_CXXFLAGS --thread_safe" + fi + ;; + *) + ;; + esac + AC_SUBST(USE_THREADS) + AC_SUBST(LIBPTHREAD) +]) + +AC_DEFUN([KDE_CHECK_THREADING], +[ + AC_REQUIRE([KDE_CHECK_LIBPTHREAD]) + AC_REQUIRE([KDE_CHECK_PTHREAD_OPTION]) + dnl default is yes if libpthread is found and no if no libpthread is available + if test -z "$LIBPTHREAD"; then + if test -z "$USE_THREADS"; then + kde_check_threading_default=no + else + kde_check_threading_default=yes + fi + else + kde_check_threading_default=yes + fi + AC_ARG_ENABLE(threading,AC_HELP_STRING([--disable-threading],[disables threading even if libpthread found]), + kde_use_threading=$enableval, kde_use_threading=$kde_check_threading_default) + if test "x$kde_use_threading" = "xyes"; then + AC_DEFINE(HAVE_LIBPTHREAD, 1, [Define if you have a working libpthread (will enable threaded code)]) + fi +]) + +AC_DEFUN([KDE_TRY_LINK_PYTHON], +[ +if test "$kde_python_link_found" = no; then + +if test "$1" = normal; then + AC_MSG_CHECKING(if a Python application links) +else + AC_MSG_CHECKING(if Python depends on $2) +fi + +AC_CACHE_VAL(kde_cv_try_link_python_$1, +[ +kde_save_cflags="$CFLAGS" +CFLAGS="$CFLAGS $PYTHONINC" +kde_save_libs="$LIBS" +LIBS="$LIBS $LIBPYTHON $2 $LIBDL $LIBSOCKET" +kde_save_ldflags="$LDFLAGS" +LDFLAGS="$LDFLAGS $PYTHONLIB" + +AC_TRY_LINK( +[ +#include +],[ + PySys_SetArgv(1, 0); +], + [kde_cv_try_link_python_$1=yes], + [kde_cv_try_link_python_$1=no] +) +CFLAGS="$kde_save_cflags" +LIBS="$kde_save_libs" +LDFLAGS="$kde_save_ldflags" +]) + +if test "$kde_cv_try_link_python_$1" = "yes"; then + AC_MSG_RESULT(yes) + kde_python_link_found=yes + if test ! "$1" = normal; then + LIBPYTHON="$LIBPYTHON $2" + fi + $3 +else + AC_MSG_RESULT(no) + $4 +fi + +fi + +]) + +AC_DEFUN([KDE_CHECK_PYTHON_DIR], +[ +AC_MSG_CHECKING([for Python directory]) + +AC_CACHE_VAL(kde_cv_pythondir, +[ + if test -z "$PYTHONDIR"; then + kde_cv_pythondir=/usr/local + else + kde_cv_pythondir="$PYTHONDIR" + fi +]) + +AC_ARG_WITH(pythondir, +AC_HELP_STRING([--with-pythondir=pythondir],[use python installed in pythondir]), +[ + ac_python_dir=$withval +], ac_python_dir=$kde_cv_pythondir +) + +AC_MSG_RESULT($ac_python_dir) +]) + +AC_DEFUN([KDE_CHECK_PYTHON_INTERN], +[ +AC_REQUIRE([KDE_CHECK_LIBDL]) +AC_REQUIRE([KDE_CHECK_LIBPTHREAD]) +AC_REQUIRE([KDE_CHECK_PYTHON_DIR]) + +if test -z "$1"; then + version="1.5" +else + version="$1" +fi + +AC_MSG_CHECKING([for Python$version]) + +python_incdirs="$ac_python_dir/include /usr/include /usr/local/include/ $kde_extra_includes" +AC_FIND_FILE(Python.h, $python_incdirs, python_incdir) +if test ! -r $python_incdir/Python.h; then + AC_FIND_FILE(python$version/Python.h, $python_incdirs, python_incdir) + python_incdir=$python_incdir/python$version + if test ! -r $python_incdir/Python.h; then + python_incdir=no + fi +fi + +PYTHONINC=-I$python_incdir + +python_libdirs="$ac_python_dir/lib$kdelibsuff /usr/lib$kdelibsuff /usr/local /usr/lib$kdelibsuff $kde_extra_libs" +AC_FIND_FILE(libpython$version.so, $python_libdirs, python_libdir) +if test ! -r $python_libdir/libpython$version.so; then + AC_FIND_FILE(libpython$version.a, $python_libdirs, python_libdir) + if test ! -r $python_libdir/libpython$version.a; then + AC_FIND_FILE(python$version/config/libpython$version.a, $python_libdirs, python_libdir) + python_libdir=$python_libdir/python$version/config + if test ! -r $python_libdir/libpython$version.a; then + python_libdir=no + fi + fi +fi + +PYTHONLIB=-L$python_libdir +kde_orig_LIBPYTHON=$LIBPYTHON +if test -z "$LIBPYTHON"; then + LIBPYTHON=-lpython$version +fi + +AC_FIND_FILE(python$version/copy.py, $python_libdirs, python_moddir) +python_moddir=$python_moddir/python$version +if test ! -r $python_moddir/copy.py; then + python_moddir=no +fi + +PYTHONMODDIR=$python_moddir + +AC_MSG_RESULT(header $python_incdir library $python_libdir modules $python_moddir) + +if test x$python_incdir = xno || test x$python_libdir = xno || test x$python_moddir = xno; then + LIBPYTHON=$kde_orig_LIBPYTHON + test "x$PYTHONLIB" = "x-Lno" && PYTHONLIB="" + test "x$PYTHONINC" = "x-Ino" && PYTHONINC="" + $2 +else + dnl Note: this test is very weak + kde_python_link_found=no + KDE_TRY_LINK_PYTHON(normal) + KDE_TRY_LINK_PYTHON(m, -lm) + KDE_TRY_LINK_PYTHON(pthread, $LIBPTHREAD) + KDE_TRY_LINK_PYTHON(tcl, -ltcl) + KDE_TRY_LINK_PYTHON(db2, -ldb2) + KDE_TRY_LINK_PYTHON(m_and_thread, [$LIBPTHREAD -lm]) + KDE_TRY_LINK_PYTHON(m_and_thread_and_util, [$LIBPTHREAD -lm -lutil]) + KDE_TRY_LINK_PYTHON(m_and_thread_and_db3, [$LIBPTHREAD -lm -ldb-3 -lutil]) + KDE_TRY_LINK_PYTHON(pthread_and_db3, [$LIBPTHREAD -ldb-3]) + KDE_TRY_LINK_PYTHON(m_and_thread_and_db, [$LIBPTHREAD -lm -ldb -ltermcap -lutil]) + KDE_TRY_LINK_PYTHON(pthread_and_dl, [$LIBPTHREAD $LIBDL -lutil -lreadline -lncurses -lm]) + KDE_TRY_LINK_PYTHON(pthread_and_panel_curses, [$LIBPTHREAD $LIBDL -lm -lpanel -lcurses]) + KDE_TRY_LINK_PYTHON(m_and_thread_and_db_special, [$LIBPTHREAD -lm -ldb -lutil], [], + [AC_MSG_WARN([it seems, Python depends on another library. + Please set LIBPYTHON to '-lpython$version -lotherlib' before calling configure to fix this + and contact the authors to let them know about this problem]) + ]) + + LIBPYTHON="$LIBPYTHON $LIBDL $LIBSOCKET" + AC_SUBST(PYTHONINC) + AC_SUBST(PYTHONLIB) + AC_SUBST(LIBPYTHON) + AC_SUBST(PYTHONMODDIR) + AC_DEFINE(HAVE_PYTHON, 1, [Define if you have the development files for python]) +fi + +]) + + +AC_DEFUN([KDE_CHECK_PYTHON], +[ + KDE_CHECK_PYTHON_INTERN("2.5", + [KDE_CHECK_PYTHON_INTERN("2.4", + [KDE_CHECK_PYTHON_INTERN("2.3", + [KDE_CHECK_PYTHON_INTERN("2.2", + [KDE_CHECK_PYTHON_INTERN("2.1", + [KDE_CHECK_PYTHON_INTERN("2.0", + [KDE_CHECK_PYTHON_INTERN($1, $2) ]) + ]) + ]) + ]) + ]) + ]) +]) + +AC_DEFUN([KDE_CHECK_STL], +[ + AC_LANG_SAVE + AC_LANG_CPLUSPLUS + ac_save_CXXFLAGS="$CXXFLAGS" + CXXFLAGS="`echo $CXXFLAGS | sed s/-fno-exceptions//`" + + AC_MSG_CHECKING([if C++ programs can be compiled]) + AC_CACHE_VAL(kde_cv_stl_works, + [ + AC_TRY_COMPILE([ +#include +using namespace std; +],[ + string astring="Hallo Welt."; + astring.erase(0, 6); // now astring is "Welt" + return 0; +], kde_cv_stl_works=yes, + kde_cv_stl_works=no) +]) + + AC_MSG_RESULT($kde_cv_stl_works) + + if test "$kde_cv_stl_works" = "yes"; then + # back compatible + AC_DEFINE_UNQUOTED(HAVE_SGI_STL, 1, [Define if you have a STL implementation by SGI]) + else + AC_MSG_ERROR([Your Installation isn't able to compile simple C++ programs. +Check config.log for details - if you're using a Linux distribution you might miss +a package named similar to libstdc++-dev.]) + fi + + CXXFLAGS="$ac_save_CXXFLAGS" + AC_LANG_RESTORE +]) + +AC_DEFUN([AC_FIND_QIMGIO], + [AC_REQUIRE([AC_FIND_JPEG]) +AC_REQUIRE([KDE_CHECK_EXTRA_LIBS]) +AC_MSG_CHECKING([for qimgio]) +AC_CACHE_VAL(ac_cv_lib_qimgio, +[ +AC_LANG_SAVE +AC_LANG_CPLUSPLUS +ac_save_LIBS="$LIBS" +ac_save_CXXFLAGS="$CXXFLAGS" +LIBS="$all_libraries -lqimgio -lpng -lz $LIBJPEG $LIBQT" +CXXFLAGS="$CXXFLAGS -I$qt_incdir $all_includes" +AC_TRY_RUN(dnl +[ +#include +#include +int main() { + QString t = "hallo"; + t.fill('t'); + qInitImageIO(); +} +], + ac_cv_lib_qimgio=yes, + ac_cv_lib_qimgio=no, + ac_cv_lib_qimgio=no) +LIBS="$ac_save_LIBS" +CXXFLAGS="$ac_save_CXXFLAGS" +AC_LANG_RESTORE +])dnl +if eval "test \"`echo $ac_cv_lib_qimgio`\" = yes"; then + LIBQIMGIO="-lqimgio -lpng -lz $LIBJPEG" + AC_MSG_RESULT(yes) + AC_DEFINE_UNQUOTED(HAVE_QIMGIO, 1, [Define if you have the Qt extension qimgio available]) + AC_SUBST(LIBQIMGIO) +else + AC_MSG_RESULT(not found) +fi +]) + +AC_DEFUN([AM_DISABLE_LIBRARIES], +[ + AC_PROVIDE([AM_ENABLE_STATIC]) + AC_PROVIDE([AM_ENABLE_SHARED]) + enable_static=no + enable_shared=yes +]) + + +AC_DEFUN([AC_CHECK_UTMP_FILE], +[ + AC_MSG_CHECKING([for utmp file]) + + AC_CACHE_VAL(kde_cv_utmp_file, + [ + kde_cv_utmp_file=no + + for ac_file in \ + \ + /var/run/utmp \ + /var/adm/utmp \ + /etc/utmp \ + ; \ + do + if test -r "$ac_file"; then + kde_cv_utmp_file=$ac_file + break + fi + done + ]) + + if test "$kde_cv_utmp_file" != "no"; then + AC_DEFINE_UNQUOTED(UTMP, "$kde_cv_utmp_file", [Define the file for utmp entries]) + $1 + AC_MSG_RESULT($kde_cv_utmp_file) + else + $2 + AC_MSG_RESULT([non found]) + fi +]) + + +AC_DEFUN([KDE_CREATE_SUBDIRSLIST], +[ + +DO_NOT_COMPILE="$DO_NOT_COMPILE CVS debian bsd-port admin" +TOPSUBDIRS="" + +if test ! -s $srcdir/subdirs; then + dnl Note: Makefile.common creates subdirs, so this is just a fallback + files=`cd $srcdir && ls -1` + dirs=`for i in $files; do if test -d $i; then echo $i; fi; done` + for i in $dirs; do + echo $i >> $srcdir/subdirs + done +fi + +ac_topsubdirs= +if test -s $srcdir/inst-apps; then + ac_topsubdirs="`cat $srcdir/inst-apps`" +elif test -s $srcdir/subdirs; then + ac_topsubdirs="`cat $srcdir/subdirs`" +fi + +for i in $ac_topsubdirs; do + AC_MSG_CHECKING([if $i should be compiled]) + if test -d $srcdir/$i; then + install_it="yes" + for j in $DO_NOT_COMPILE; do + if test $i = $j; then + install_it="no" + fi + done + else + install_it="no" + fi + AC_MSG_RESULT($install_it) + vari=`echo $i | sed -e 's,[[-+.@]],_,g'` + if test $install_it = "yes"; then + TOPSUBDIRS="$TOPSUBDIRS $i" + eval "$vari""_SUBDIR_included=yes" + else + eval "$vari""_SUBDIR_included=no" + fi +done + +AC_SUBST(TOPSUBDIRS) +]) + +AC_DEFUN([KDE_CHECK_NAMESPACES], +[ +AC_MSG_CHECKING(whether C++ compiler supports namespaces) +AC_LANG_SAVE +AC_LANG_CPLUSPLUS +AC_TRY_COMPILE([ +], +[ +namespace Foo { + extern int i; + namespace Bar { + extern int i; + } +} + +int Foo::i = 0; +int Foo::Bar::i = 1; +],[ + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_NAMESPACES) +], [ +AC_MSG_RESULT(no) +]) +AC_LANG_RESTORE +]) + +dnl ------------------------------------------------------------------------ +dnl Check for S_ISSOCK macro. Doesn't exist on Unix SCO. faure@kde.org +dnl ------------------------------------------------------------------------ +dnl +AC_DEFUN([AC_CHECK_S_ISSOCK], +[ +AC_MSG_CHECKING(for S_ISSOCK) +AC_CACHE_VAL(ac_cv_have_s_issock, +[ +AC_TRY_LINK( +[ +#include +], +[ +struct stat buff; +int b = S_ISSOCK( buff.st_mode ); +], +ac_cv_have_s_issock=yes, +ac_cv_have_s_issock=no) +]) +AC_MSG_RESULT($ac_cv_have_s_issock) +if test "$ac_cv_have_s_issock" = "yes"; then + AC_DEFINE_UNQUOTED(HAVE_S_ISSOCK, 1, [Define if sys/stat.h declares S_ISSOCK.]) +fi + +AH_VERBATIM(_ISSOCK, +[ +#ifndef HAVE_S_ISSOCK +#define HAVE_S_ISSOCK +#define S_ISSOCK(mode) (1==0) +#endif +]) + +]) + +dnl ------------------------------------------------------------------------ +dnl Check for MAXPATHLEN macro, defines KDEMAXPATHLEN. faure@kde.org +dnl ------------------------------------------------------------------------ +dnl +AC_DEFUN([AC_CHECK_KDEMAXPATHLEN], +[ +AC_MSG_CHECKING(for MAXPATHLEN) +AC_CACHE_VAL(ac_cv_maxpathlen, +[ +cat > conftest.$ac_ext < +#endif +#include +#include +#ifndef MAXPATHLEN +#define MAXPATHLEN 1024 +#endif + +KDE_HELLO MAXPATHLEN + +EOF + +ac_try="$ac_cpp conftest.$ac_ext 2>/dev/null | grep '^KDE_HELLO' >conftest.out" + +if AC_TRY_EVAL(ac_try) && test -s conftest.out; then + ac_cv_maxpathlen=`sed 's#KDE_HELLO ##' conftest.out` +else + ac_cv_maxpathlen=1024 +fi + +rm conftest.* + +]) +AC_MSG_RESULT($ac_cv_maxpathlen) +AC_DEFINE_UNQUOTED(KDEMAXPATHLEN,$ac_cv_maxpathlen, [Define a safe value for MAXPATHLEN] ) +]) + +AC_DEFUN([KDE_CHECK_HEADER], +[ + kde_safe_cppflags=$CPPFLAGS + CPPFLAGS="$CPPFLAGS $all_includes" + AC_LANG_SAVE + AC_LANG_CPLUSPLUS + AC_CHECK_HEADER([$1], [$2], [$3], [$4]) + AC_LANG_RESTORE + CPPFLAGS=$kde_safe_cppflags +]) + +AC_DEFUN([KDE_CHECK_HEADERS], +[ + AH_CHECK_HEADERS([$1]) + AC_LANG_SAVE + kde_safe_cppflags=$CPPFLAGS + CPPFLAGS="$CPPFLAGS $all_includes" + AC_LANG_CPLUSPLUS + AC_CHECK_HEADERS([$1], [$2], [$3], [$4]) + CPPFLAGS=$kde_safe_cppflags + AC_LANG_RESTORE +]) + +AC_DEFUN([KDE_FAST_CONFIGURE], +[ + dnl makes configure fast (needs perl) + AC_ARG_ENABLE(fast-perl, AC_HELP_STRING([--disable-fast-perl],[disable fast Makefile generation (needs perl)]), + with_fast_perl=$enableval, with_fast_perl=yes) +]) + +AC_DEFUN([KDE_CONF_FILES], +[ + val= + if test -f $srcdir/configure.files ; then + val=`sed -e 's%^%\$(top_srcdir)/%' $srcdir/configure.files` + fi + CONF_FILES= + if test -n "$val" ; then + for i in $val ; do + CONF_FILES="$CONF_FILES $i" + done + fi + AC_SUBST(CONF_FILES) +])dnl + +dnl This sets the prefix, for arts and kdelibs +dnl Do NOT use in any other module. +dnl It only looks at --prefix, KDEDIR and falls back to /usr/local/kde +AC_DEFUN([KDE_SET_PREFIX_CORE], +[ + unset CDPATH + dnl make $KDEDIR the default for the installation + AC_PREFIX_DEFAULT(${KDEDIR:-/usr/local/kde}) + + if test "x$prefix" = "xNONE"; then + prefix=$ac_default_prefix + ac_configure_args="$ac_configure_args --prefix=$prefix" + fi + # And delete superfluous '/' to make compares easier + prefix=`echo "$prefix" | sed 's,//*,/,g' | sed -e 's,/$,,'` + exec_prefix=`echo "$exec_prefix" | sed 's,//*,/,g' | sed -e 's,/$,,'` + + kde_libs_prefix='$(prefix)' + kde_libs_htmldir='$(kde_htmldir)' + AC_SUBST(kde_libs_prefix) + AC_SUBST(kde_libs_htmldir) + KDE_FAST_CONFIGURE + KDE_CONF_FILES +]) + + +AC_DEFUN([KDE_SET_PREFIX], +[ + unset CDPATH + dnl We can't give real code to that macro, only a value. + dnl It only matters for --help, since we set the prefix in this function anyway. + AC_PREFIX_DEFAULT(${KDEDIR:-the kde prefix}) + + KDE_SET_DEFAULT_BINDIRS + if test "x$prefix" = "xNONE"; then + dnl no prefix given: look for kde-config in the PATH and deduce the prefix from it + KDE_FIND_PATH(kde-config, KDECONFIG, [$kde_default_bindirs], [KDE_MISSING_PROG_ERROR(kde-config)], [], prepend) + else + dnl prefix given: look for kde-config, preferrably in prefix, otherwise in PATH + kde_save_PATH="$PATH" + PATH="$exec_prefix/bin:$prefix/bin:$PATH" + KDE_FIND_PATH(kde-config, KDECONFIG, [$kde_default_bindirs], [KDE_MISSING_PROG_ERROR(kde-config)], [], prepend) + PATH="$kde_save_PATH" + fi + + kde_libs_prefix=`$KDECONFIG --prefix` + if test -z "$kde_libs_prefix" || test ! -x "$kde_libs_prefix"; then + AC_MSG_ERROR([$KDECONFIG --prefix outputed the non existant prefix '$kde_libs_prefix' for kdelibs. + This means it has been moved since you installed it. + This won't work. Please recompile kdelibs for the new prefix. + ]) + fi + kde_libs_htmldir=`$KDECONFIG --install html --expandvars` + + AC_MSG_CHECKING([where to install]) + if test "x$prefix" = "xNONE"; then + prefix=$kde_libs_prefix + AC_MSG_RESULT([$prefix (as returned by kde-config)]) + else + dnl --prefix was given. Compare prefixes and warn (in configure.in.bot.end) if different + given_prefix=$prefix + AC_MSG_RESULT([$prefix (as requested)]) + fi + + # And delete superfluous '/' to make compares easier + prefix=`echo "$prefix" | sed 's,//*,/,g' | sed -e 's,/$,,'` + exec_prefix=`echo "$exec_prefix" | sed 's,//*,/,g' | sed -e 's,/$,,'` + given_prefix=`echo "$given_prefix" | sed 's,//*,/,g' | sed -e 's,/$,,'` + + AC_SUBST(KDECONFIG) + AC_SUBST(kde_libs_prefix) + AC_SUBST(kde_libs_htmldir) + + KDE_FAST_CONFIGURE + KDE_CONF_FILES +]) + +pushdef([AC_PROG_INSTALL], +[ + dnl our own version, testing for a -p flag + popdef([AC_PROG_INSTALL]) + dnl as AC_PROG_INSTALL works as it works we first have + dnl to save if the user didn't specify INSTALL, as the + dnl autoconf one overwrites INSTALL and we have no chance to find + dnl out afterwards + test -n "$INSTALL" && kde_save_INSTALL_given=$INSTALL + test -n "$INSTALL_PROGRAM" && kde_save_INSTALL_PROGRAM_given=$INSTALL_PROGRAM + test -n "$INSTALL_SCRIPT" && kde_save_INSTALL_SCRIPT_given=$INSTALL_SCRIPT + AC_PROG_INSTALL + + if test -z "$kde_save_INSTALL_given" ; then + # OK, user hasn't given any INSTALL, autoconf found one for us + # now we test, if it supports the -p flag + AC_MSG_CHECKING(for -p flag to install) + rm -f confinst.$$.* > /dev/null 2>&1 + echo "Testtest" > confinst.$$.orig + ac_res=no + if ${INSTALL} -p confinst.$$.orig confinst.$$.new > /dev/null 2>&1 ; then + if test -f confinst.$$.new ; then + # OK, -p seems to do no harm to install + INSTALL="${INSTALL} -p" + ac_res=yes + fi + fi + rm -f confinst.$$.* + AC_MSG_RESULT($ac_res) + fi + dnl the following tries to resolve some signs and wonders coming up + dnl with different autoconf/automake versions + dnl e.g.: + dnl *automake 1.4 install-strip sets A_M_INSTALL_PROGRAM_FLAGS to -s + dnl and has INSTALL_PROGRAM = @INSTALL_PROGRAM@ $(A_M_INSTALL_PROGRAM_FLAGS) + dnl it header-vars.am, so there the actual INSTALL_PROGRAM gets the -s + dnl *automake 1.4a (and above) use INSTALL_STRIP_FLAG and only has + dnl INSTALL_PROGRAM = @INSTALL_PROGRAM@ there, but changes the + dnl install-@DIR@PROGRAMS targets to explicitly use that flag + dnl *autoconf 2.13 is dumb, and thinks it can use INSTALL_PROGRAM as + dnl INSTALL_SCRIPT, which breaks with automake <= 1.4 + dnl *autoconf >2.13 (since 10.Apr 1999) has not that failure + dnl *sometimes KDE does not use the install-@DIR@PROGRAM targets from + dnl automake (due to broken Makefile.am or whatever) to install programs, + dnl and so does not see the -s flag in automake > 1.4 + dnl to clean up that mess we: + dnl +set INSTALL_PROGRAM to use INSTALL_STRIP_FLAG + dnl which cleans KDE's program with automake > 1.4; + dnl +set INSTALL_SCRIPT to only use INSTALL, to clean up autoconf's problems + dnl with automake<=1.4 + dnl note that dues to this sometimes two '-s' flags are used (if KDE + dnl properly uses install-@DIR@PROGRAMS, but I don't care + dnl + dnl And to all this comes, that I even can't write in comments variable + dnl names used by automake, because it is so stupid to think I wanted to + dnl _use_ them, therefor I have written A_M_... instead of AM_ + dnl hmm, I wanted to say something ... ahh yes: Arghhh. + + if test -z "$kde_save_INSTALL_PROGRAM_given" ; then + INSTALL_PROGRAM='${INSTALL} $(INSTALL_STRIP_FLAG)' + fi + if test -z "$kde_save_INSTALL_SCRIPT_given" ; then + INSTALL_SCRIPT='${INSTALL}' + fi +])dnl + +AC_DEFUN([KDE_LANG_CPLUSPLUS], +[AC_LANG_CPLUSPLUS +ac_link='rm -rf SunWS_cache; ${CXX-g++} -o conftest${ac_exeext} $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&AC_FD_CC' +pushdef([AC_LANG_CPLUSPLUS], [popdef([AC_LANG_CPLUSPLUS]) KDE_LANG_CPLUSPLUS]) +]) + +pushdef([AC_LANG_CPLUSPLUS], +[popdef([AC_LANG_CPLUSPLUS]) +KDE_LANG_CPLUSPLUS +]) + +AC_DEFUN([KDE_CHECK_LONG_LONG], +[ +AC_MSG_CHECKING(for long long) +AC_CACHE_VAL(kde_cv_c_long_long, +[ + AC_LANG_SAVE + AC_LANG_CPLUSPLUS + AC_TRY_LINK([], [ + long long foo = 0; + foo = foo+1; + ], + kde_cv_c_long_long=yes, kde_cv_c_long_long=no) + AC_LANG_RESTORE +]) +AC_MSG_RESULT($kde_cv_c_long_long) +if test "$kde_cv_c_long_long" = yes; then + AC_DEFINE(HAVE_LONG_LONG, 1, [Define if you have long long as datatype]) +fi +]) + +AC_DEFUN([KDE_CHECK_LIB], +[ + kde_save_LDFLAGS="$LDFLAGS" + dnl AC_CHECK_LIB modifies LIBS, so save it here + kde_save_LIBS="$LIBS" + LDFLAGS="$LDFLAGS $all_libraries" + case $host_os in + aix*) LDFLAGS="-brtl $LDFLAGS" + test "$GCC" = yes && LDFLAGS="-Wl,$LDFLAGS" + ;; + esac + AC_CHECK_LIB($1, $2, $3, $4, $5) + LDFLAGS="$kde_save_LDFLAGS" + LIBS="$kde_save_LIBS" +]) + +AC_DEFUN([KDE_JAVA_PREFIX], +[ + dir=`dirname "$1"` + base=`basename "$1"` + list=`ls -1 $dir 2> /dev/null` + for entry in $list; do + if test -d $dir/$entry/bin; then + case $entry in + $base) + javadirs="$javadirs $dir/$entry/bin" + ;; + esac + elif test -d $dir/$entry/jre/bin; then + case $entry in + $base) + javadirs="$javadirs $dir/$entry/jre/bin" + ;; + esac + fi + done +]) + +dnl KDE_CHEC_JAVA_DIR(onlyjre) +AC_DEFUN([KDE_CHECK_JAVA_DIR], +[ + +AC_ARG_WITH(java, +AC_HELP_STRING([--with-java=javadir],[use java installed in javadir, --without-java disables]), +[ ac_java_dir=$withval +], ac_java_dir="" +) + +AC_MSG_CHECKING([for Java]) + +dnl at this point ac_java_dir is either a dir, 'no' to disable, or '' to say look in $PATH +if test "x$ac_java_dir" = "xno"; then + kde_java_bindir=no + kde_java_includedir=no + kde_java_libjvmdir=no + kde_java_libgcjdir=no + kde_java_libhpidir=no +else + if test "x$ac_java_dir" = "x"; then + + + dnl No option set -> collect list of candidate paths + if test -n "$JAVA_HOME"; then + KDE_JAVA_PREFIX($JAVA_HOME) + fi + KDE_JAVA_PREFIX(/usr/j2se) + KDE_JAVA_PREFIX(/usr/lib/j2se) + KDE_JAVA_PREFIX(/usr/j*dk*) + KDE_JAVA_PREFIX(/usr/lib/j*dk*) + KDE_JAVA_PREFIX(/opt/j*sdk*) + KDE_JAVA_PREFIX(/usr/lib/java*) + KDE_JAVA_PREFIX(/usr/java*) + KDE_JAVA_PREFIX(/usr/java/j*dk*) + KDE_JAVA_PREFIX(/usr/java/j*re*) + KDE_JAVA_PREFIX(/usr/lib/SunJava2*) + KDE_JAVA_PREFIX(/usr/lib/SunJava*) + KDE_JAVA_PREFIX(/usr/lib/IBMJava2*) + KDE_JAVA_PREFIX(/usr/lib/IBMJava*) + KDE_JAVA_PREFIX(/opt/java*) + + kde_cv_path="NONE" + kde_save_IFS=$IFS + IFS=':' + for dir in $PATH; do + if test -d "$dir"; then + javadirs="$javadirs $dir" + fi + done + IFS=$kde_save_IFS + jredirs= + + dnl Now javadirs contains a list of paths that exist, all ending with bin/ + for dir in $javadirs; do + dnl Check for the java executable + if test -x "$dir/java"; then + sane_path=$(cd $dir; /bin/pwd) + dnl And also check for a libjvm.so somewhere under there + dnl Since we have to go to the parent dir, /usr/bin is excluded, /usr is too big. + if test "$sane_path" != "/usr/bin"; then + libjvmdir=`find $dir/.. -name libjvm.so | sed 's,libjvm.so,,'|head -n 1` + if test ! -f $libjvmdir/libjvm.so; then continue; fi + jredirs="$jredirs $dir" + fi + fi + done + + dnl Now jredirs contains a reduced list, of paths where both java and ../**/libjvm.so was found + JAVAC= + JAVA= + kde_java_bindir=no + for dir in $jredirs; do + JAVA="$dir/java" + kde_java_bindir=$dir + if test -x "$dir/javac"; then + JAVAC="$dir/javac" + break + fi + done + + if test -n "$JAVAC"; then + dnl this substitution might not work - well, we test for jni.h below + kde_java_includedir=`echo $JAVAC | sed -e 's,bin/javac$,include/,'` + else + kde_java_includedir=no + fi + else + dnl config option set + kde_java_bindir=$ac_java_dir/bin + if test -x $ac_java_dir/bin/java && test ! -x $ac_java_dir/bin/javac; then + kde_java_includedir=no + else + kde_java_includedir=$ac_java_dir/include + fi + fi +fi + +dnl At this point kde_java_bindir and kde_java_includedir are either set or "no" +if test "x$kde_java_bindir" != "xno"; then + + dnl Look for libjvm.so + kde_java_libjvmdir=`find $kde_java_bindir/.. -name libjvm.so | sed 's,libjvm.so,,'|head -n 1` + dnl Look for libgcj.so + kde_java_libgcjdir=`find $kde_java_bindir/.. -name libgcj.so | sed 's,libgcj.so,,'|head -n 1` + dnl Look for libhpi.so and avoid green threads + kde_java_libhpidir=`find $kde_java_bindir/.. -name libhpi.so | grep -v green | sed 's,libhpi.so,,' | head -n 1` + + dnl Now check everything's fine under there + dnl the include dir is our flag for having the JDK + if test -d "$kde_java_includedir"; then + if test ! -x "$kde_java_bindir/javac"; then + AC_MSG_ERROR([javac not found under $kde_java_bindir - it seems you passed a wrong --with-java.]) + fi + if test ! -x "$kde_java_bindir/javah"; then + AC_MSG_ERROR([javah not found under $kde_java_bindir. javac was found though! Use --with-java or --without-java.]) + fi + if test ! -x "$kde_java_bindir/jar"; then + AC_MSG_ERROR([jar not found under $kde_java_bindir. javac was found though! Use --with-java or --without-java.]) + fi + if test ! -r "$kde_java_includedir/jni.h"; then + AC_MSG_ERROR([jni.h not found under $kde_java_includedir. Use --with-java or --without-java.]) + fi + + jni_includes="-I$kde_java_includedir" + dnl Strange thing, jni.h requires jni_md.h which is under genunix here.. + dnl and under linux here.. + + dnl not needed for gcj + + if test "x$kde_java_libgcjdir" = "x"; then + test -d "$kde_java_includedir/linux" && jni_includes="$jni_includes -I$kde_java_includedir/linux" + test -d "$kde_java_includedir/solaris" && jni_includes="$jni_includes -I$kde_java_includedir/solaris" + test -d "$kde_java_includedir/genunix" && jni_includes="$jni_includes -I$kde_java_includedir/genunix" + fi + + else + JAVAC= + jni_includes= + fi + + if test "x$kde_java_libgcjdir" = "x"; then + if test ! -r "$kde_java_libjvmdir/libjvm.so"; then + AC_MSG_ERROR([libjvm.so not found under $kde_java_libjvmdir. Use --without-java.]) + fi + else + if test ! -r "$kde_java_libgcjdir/libgcj.so"; then + AC_MSG_ERROR([libgcj.so not found under $kde_java_libgcjdir. Use --without-java.]) + fi + fi + + if test ! -x "$kde_java_bindir/java"; then + AC_MSG_ERROR([java not found under $kde_java_bindir. javac was found though! Use --with-java or --without-java.]) + fi + + dnl not needed for gcj compile + + if test "x$kde_java_libgcjdir" = "x"; then + if test ! -r "$kde_java_libhpidir/libhpi.so"; then + AC_MSG_ERROR([libhpi.so not found under $kde_java_libhpidir. Use --without-java.]) + fi + fi + + if test -n "$jni_includes"; then + dnl Check for JNI version + AC_LANG_SAVE + AC_LANG_CPLUSPLUS + ac_cxxflags_safe="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS $all_includes $jni_includes" + + AC_TRY_COMPILE([ + #include + ], + [ + #ifndef JNI_VERSION_1_2 + Syntax Error + #endif + ],[ kde_jni_works=yes ], + [ kde_jni_works=no ]) + + if test $kde_jni_works = no; then + AC_MSG_ERROR([Incorrect version of $kde_java_includedir/jni.h. + You need to have Java Development Kit (JDK) version 1.2. + + Use --with-java to specify another location. + Use --without-java to configure without java support. + Or download a newer JDK and try again. + See e.g. http://java.sun.com/products/jdk/1.2 ]) + fi + + CXXFLAGS="$ac_cxxflags_safe" + AC_LANG_RESTORE + + dnl All tests ok, inform and subst the variables + + JAVAC=$kde_java_bindir/javac + JAVAH=$kde_java_bindir/javah + JAR=$kde_java_bindir/jar + AC_DEFINE_UNQUOTED(PATH_JAVA, "$kde_java_bindir/java", [Define where your java executable is]) + if test "x$kde_java_libgcjdir" = "x"; then + JVMLIBS="-L$kde_java_libjvmdir -ljvm -L$kde_java_libhpidir -lhpi" + else + JVMLIBS="-L$kde_java_libgcjdir -lgcj" + fi + AC_MSG_RESULT([java JDK in $kde_java_bindir]) + + else + AC_DEFINE_UNQUOTED(PATH_JAVA, "$kde_java_bindir/java", [Define where your java executable is]) + AC_MSG_RESULT([java JRE in $kde_java_bindir]) + fi +elif test -d "/Library/Java/Home"; then + kde_java_bindir="/Library/Java/Home/bin" + jni_includes="-I/Library/Java/Home/include" + + JAVAC=$kde_java_bindir/javac + JAVAH=$kde_java_bindir/javah + JAR=$kde_java_bindir/jar + JVMLIBS="-Wl,-framework,JavaVM" + + AC_DEFINE_UNQUOTED(PATH_JAVA, "$kde_java_bindir/java", [Define where your java executable is]) + AC_MSG_RESULT([Apple Java Framework]) +else + AC_MSG_RESULT([none found]) +fi + +AC_SUBST(JAVAC) +AC_SUBST(JAVAH) +AC_SUBST(JAR) +AC_SUBST(JVMLIBS) +AC_SUBST(jni_includes) + +# for backward compat +kde_cv_java_includedir=$kde_java_includedir +kde_cv_java_bindir=$kde_java_bindir +]) + +dnl this is a redefinition of autoconf 2.5x's AC_FOREACH. +dnl When the argument list becomes big, as in KDE for AC_OUTPUT in +dnl big packages, m4_foreach is dog-slow. So use our own version of +dnl it. (matz@kde.org) +m4_define([mm_foreach], +[m4_pushdef([$1])_mm_foreach($@)m4_popdef([$1])]) +m4_define([mm_car], [[$1]]) +m4_define([mm_car2], [[$@]]) +m4_define([_mm_foreach], +[m4_if(m4_quote($2), [], [], + [m4_define([$1], mm_car($2))$3[]_mm_foreach([$1], + mm_car2(m4_shift($2)), + [$3])])]) +m4_define([AC_FOREACH], +[mm_foreach([$1], m4_split(m4_normalize([$2])), [$3])]) + +AC_DEFUN([KDE_NEED_FLEX], +[ +kde_libs_safe=$LIBS +LIBS="$LIBS $USER_LDFLAGS" +AM_PROG_LEX +LIBS=$kde_libs_safe +if test -z "$LEXLIB"; then + AC_MSG_ERROR([You need to have flex installed.]) +fi +AC_SUBST(LEXLIB) +]) + +AC_DEFUN([AC_PATH_QTOPIA], +[ + dnl TODO: use AC_CACHE_VAL + + if test -z "$1"; then + qtopia_minver_maj=1 + qtopia_minver_min=5 + qtopia_minver_pat=0 + else + qtopia_minver_maj=`echo "$1" | sed -e "s/^\(.*\)\..*\..*$/\1/"` + qtopia_minver_min=`echo "$1" | sed -e "s/^.*\.\(.*\)\..*$/\1/"` + qtopia_minver_pat=`echo "$1" | sed -e "s/^.*\..*\.\(.*\)$/\1/"` + fi + + qtopia_minver="$qtopia_minver_maj$qtopia_minver_min$qtopia_minver_pat" + qtopia_minverstr="$qtopia_minver_maj.$qtopia_minver_min.$qtopia_minver_pat" + + AC_REQUIRE([AC_PATH_QT]) + + AC_MSG_CHECKING([for Qtopia]) + + LIB_QTOPIA="-lqpe" + AC_SUBST(LIB_QTOPIA) + + kde_qtopia_dirs="$QPEDIR /opt/Qtopia" + + ac_qtopia_incdir=NO + + AC_ARG_WITH(qtopia-dir, + AC_HELP_STRING([--with-qtopia-dir=DIR],[where the root of Qtopia is installed]), + [ ac_qtopia_incdir="$withval"/include] ) + + qtopia_incdirs="" + for dir in $kde_qtopia_dirs; do + qtopia_incdirs="$qtopia_incdirs $dir/include" + done + + if test ! "$ac_qtopia_incdir" = "NO"; then + qtopia_incdirs="$ac_qtopia_incdir $qtopia_incdirs" + fi + + qtopia_incdir="" + AC_FIND_FILE(qpe/qpeapplication.h, $qtopia_incdirs, qtopia_incdir) + ac_qtopia_incdir="$qtopia_incdir" + + if test -z "$qtopia_incdir"; then + AC_MSG_ERROR([Cannot find Qtopia headers. Please check your installation.]) + fi + + qtopia_ver_maj=`cat $qtopia_incdir/qpe/version.h | sed -n -e 's,.*QPE_VERSION "\(.*\)\..*\..*".*,\1,p'`; + qtopia_ver_min=`cat $qtopia_incdir/qpe/version.h | sed -n -e 's,.*QPE_VERSION ".*\.\(.*\)\..*".*,\1,p'`; + qtopia_ver_pat=`cat $qtopia_incdir/qpe/version.h | sed -n -e 's,.*QPE_VERSION ".*\..*\.\(.*\)".*,\1,p'`; + + qtopia_ver="$qtopia_ver_maj$qtopia_ver_min$qtopia_ver_pat" + qtopia_verstr="$qtopia_ver_maj.$qtopia_ver_min.$qtopia_ver_pat" + if test "$qtopia_ver" -lt "$qtopia_minver"; then + AC_MSG_ERROR([found Qtopia version $qtopia_verstr but version $qtopia_minverstr +is required.]) + fi + + AC_LANG_SAVE + AC_LANG_CPLUSPLUS + + ac_cxxflags_safe="$CXXFLAGS" + ac_ldflags_safe="$LDFLAGS" + ac_libs_safe="$LIBS" + + CXXFLAGS="$CXXFLAGS -I$qtopia_incdir $all_includes" + LDFLAGS="$LDFLAGS $QT_LDFLAGS $all_libraries $USER_LDFLAGS $KDE_MT_LDFLAGS" + LIBS="$LIBS $LIB_QTOPIA $LIBQT" + + cat > conftest.$ac_ext < +#include + +int main( int argc, char **argv ) +{ + QPEApplication app( argc, argv ); + return 0; +} +EOF + + if AC_TRY_EVAL(ac_link) && test -s conftest; then + rm -f conftest* + else + rm -f conftest* + AC_MSG_ERROR([Cannot link small Qtopia Application. For more details look at +the end of config.log]) + fi + + CXXFLAGS="$ac_cxxflags_safe" + LDFLAGS="$ac_ldflags_safe" + LIBS="$ac_libs_safe" + + AC_LANG_RESTORE + + QTOPIA_INCLUDES="-I$qtopia_incdir" + AC_SUBST(QTOPIA_INCLUDES) + + AC_MSG_RESULT([found version $qtopia_verstr with headers at $qtopia_incdir]) +]) + + +AC_DEFUN([KDE_INIT_DOXYGEN], +[ +AC_MSG_CHECKING([for Qt docs]) +kde_qtdir= +if test "${with_qt_dir+set}" = set; then + kde_qtdir="$with_qt_dir" +fi + +AC_FIND_FILE(qsql.html, [ $kde_qtdir/doc/html $QTDIR/doc/html /usr/share/doc/packages/qt3/html /usr/lib/qt/doc /usr/lib/qt3/doc /usr/lib/qt3/doc/html /usr/doc/qt3/html /usr/doc/qt3 /usr/share/doc/qt3-doc /usr/share/qt3/doc/html /usr/X11R6/share/doc/qt/html ], QTDOCDIR) +AC_MSG_RESULT($QTDOCDIR) + +AC_SUBST(QTDOCDIR) + +KDE_FIND_PATH(dot, DOT, [], []) +if test -n "$DOT"; then + KDE_HAVE_DOT="YES" +else + KDE_HAVE_DOT="NO" +fi +AC_SUBST(KDE_HAVE_DOT) +KDE_FIND_PATH(doxygen, DOXYGEN, [], []) +AC_SUBST(DOXYGEN) + +DOXYGEN_PROJECT_NAME="$1" +DOXYGEN_PROJECT_NUMBER="$2" +AC_SUBST(DOXYGEN_PROJECT_NAME) +AC_SUBST(DOXYGEN_PROJECT_NUMBER) + +KDE_HAS_DOXYGEN=no +if test -n "$DOXYGEN" && test -x "$DOXYGEN" && test -f $QTDOCDIR/qsql.html; then + KDE_HAS_DOXYGEN=yes +fi +AC_SUBST(KDE_HAS_DOXYGEN) + +]) + + +AC_DEFUN([AC_FIND_BZIP2], +[ +AC_MSG_CHECKING([for bzDecompress in libbz2]) +AC_CACHE_VAL(ac_cv_lib_bzip2, +[ +AC_LANG_SAVE +AC_LANG_CPLUSPLUS +kde_save_LIBS="$LIBS" +LIBS="$all_libraries $USER_LDFLAGS -lbz2 $LIBSOCKET" +kde_save_CXXFLAGS="$CXXFLAGS" +CXXFLAGS="$CXXFLAGS $all_includes $USER_INCLUDES" +AC_TRY_LINK(dnl +[ +#define BZ_NO_STDIO +#include +], + [ bz_stream s; (void) bzDecompress(&s); ], + eval "ac_cv_lib_bzip2='-lbz2'", + eval "ac_cv_lib_bzip2=no") +LIBS="$kde_save_LIBS" +CXXFLAGS="$kde_save_CXXFLAGS" +AC_LANG_RESTORE +])dnl +AC_MSG_RESULT($ac_cv_lib_bzip2) + +if test ! "$ac_cv_lib_bzip2" = no; then + BZIP2DIR=bzip2 + + LIBBZ2="$ac_cv_lib_bzip2" + AC_SUBST(LIBBZ2) + +else + + cxx_shared_flag= + ld_shared_flag= + KDE_CHECK_COMPILER_FLAG(shared, [ + ld_shared_flag="-shared" + ]) + KDE_CHECK_COMPILER_FLAG(fPIC, [ + cxx_shared_flag="-fPIC" + ]) + + AC_MSG_CHECKING([for BZ2_bzDecompress in (shared) libbz2]) + AC_CACHE_VAL(ac_cv_lib_bzip2_prefix, + [ + AC_LANG_SAVE + AC_LANG_CPLUSPLUS + kde_save_LIBS="$LIBS" + LIBS="$all_libraries $USER_LDFLAGS $ld_shared_flag -lbz2 $LIBSOCKET" + kde_save_CXXFLAGS="$CXXFLAGS" + CXXFLAGS="$CFLAGS $cxx_shared_flag $all_includes $USER_INCLUDES" + + AC_TRY_LINK(dnl + [ + #define BZ_NO_STDIO + #include + ], + [ bz_stream s; (void) BZ2_bzDecompress(&s); ], + eval "ac_cv_lib_bzip2_prefix='-lbz2'", + eval "ac_cv_lib_bzip2_prefix=no") + LIBS="$kde_save_LIBS" + CXXFLAGS="$kde_save_CXXFLAGS" + AC_LANG_RESTORE + ])dnl + + AC_MSG_RESULT($ac_cv_lib_bzip2_prefix) + + if test ! "$ac_cv_lib_bzip2_prefix" = no; then + BZIP2DIR=bzip2 + + LIBBZ2="$ac_cv_lib_bzip2_prefix" + AC_SUBST(LIBBZ2) + + AC_DEFINE(NEED_BZ2_PREFIX, 1, [Define if the libbz2 functions need the BZ2_ prefix]) + dnl else, we just ignore this + fi + +fi +AM_CONDITIONAL(include_BZIP2, test -n "$BZIP2DIR") +]) + +dnl ------------------------------------------------------------------------ +dnl Try to find the SSL headers and libraries. +dnl $(SSL_LDFLAGS) will be -Lsslliblocation (if needed) +dnl and $(SSL_INCLUDES) will be -Isslhdrlocation (if needed) +dnl ------------------------------------------------------------------------ +dnl +AC_DEFUN([KDE_CHECK_SSL], +[ +LIBSSL="-lssl -lcrypto" +AC_REQUIRE([KDE_CHECK_LIB64]) + +ac_ssl_includes=NO ac_ssl_libraries=NO +ssl_libraries="" +ssl_includes="" +AC_ARG_WITH(ssl-dir, + AC_HELP_STRING([--with-ssl-dir=DIR],[where the root of OpenSSL is installed]), + [ ac_ssl_includes="$withval"/include + ac_ssl_libraries="$withval"/lib$kdelibsuff + ]) + +want_ssl=yes +AC_ARG_WITH(ssl, + AC_HELP_STRING([--without-ssl],[disable SSL checks]), + [want_ssl=$withval]) + +if test $want_ssl = yes; then + +AC_MSG_CHECKING(for OpenSSL) + +AC_CACHE_VAL(ac_cv_have_ssl, +[#try to guess OpenSSL locations + + ssl_incdirs="/usr/include /usr/local/include /usr/ssl/include /usr/local/ssl/include $prefix/include $kde_extra_includes" + ssl_incdirs="$ac_ssl_includes $ssl_incdirs" + AC_FIND_FILE(openssl/ssl.h, $ssl_incdirs, ssl_incdir) + ac_ssl_includes="$ssl_incdir" + + ssl_libdirs="/usr/lib$kdelibsuff /usr/local/lib$kdelibsuff /usr/ssl/lib$kdelibsuff /usr/local/ssl/lib$kdelibsuff $libdir $prefix/lib$kdelibsuff $exec_prefix/lib$kdelibsuff $kde_extra_libs" + if test ! "$ac_ssl_libraries" = "NO"; then + ssl_libdirs="$ac_ssl_libraries $ssl_libdirs" + fi + + test=NONE + ssl_libdir=NONE + for dir in $ssl_libdirs; do + try="ls -1 $dir/libssl*" + if test=`eval $try 2> /dev/null`; then ssl_libdir=$dir; break; else echo "tried $dir" >&AC_FD_CC ; fi + done + + ac_ssl_libraries="$ssl_libdir" + + ac_ldflags_safe="$LDFLAGS" + ac_libs_safe="$LIBS" + + LDFLAGS="$LDFLAGS -L$ssl_libdir $all_libraries" + LIBS="$LIBS $LIBSSL -lRSAglue -lrsaref" + + AC_TRY_LINK(,void RSAPrivateEncrypt(void);RSAPrivateEncrypt();, + ac_ssl_rsaref="yes" + , + ac_ssl_rsaref="no" + ) + + LDFLAGS="$ac_ldflags_safe" + LIBS="$ac_libs_safe" + + if test "$ac_ssl_includes" = NO || test "$ac_ssl_libraries" = NO; then + have_ssl=no + else + have_ssl=yes; + fi + + ]) + + eval "$ac_cv_have_ssl" + + AC_MSG_RESULT([libraries $ac_ssl_libraries, headers $ac_ssl_includes]) + + AC_MSG_CHECKING([whether OpenSSL uses rsaref]) + AC_MSG_RESULT($ac_ssl_rsaref) + + AC_MSG_CHECKING([for easter eggs]) + AC_MSG_RESULT([none found]) + +else + have_ssl=no +fi + +if test "$have_ssl" = yes; then + AC_MSG_CHECKING(for OpenSSL version) + dnl Check for SSL version + AC_CACHE_VAL(ac_cv_ssl_version, + [ + + cat >conftest.$ac_ext < +#include + int main() { + +#ifndef OPENSSL_VERSION_NUMBER + printf("ssl_version=\\"error\\"\n"); +#else + if (OPENSSL_VERSION_NUMBER < 0x00906000) + printf("ssl_version=\\"old\\"\n"); + else + printf("ssl_version=\\"ok\\"\n"); +#endif + return (0); + } +EOF + + ac_save_CPPFLAGS=$CPPFLAGS + if test "$ac_ssl_includes" != "/usr/include"; then + CPPFLAGS="$CPPFLAGS -I$ac_ssl_includes" + fi + + if AC_TRY_EVAL(ac_link); then + + if eval `./conftest 2>&5`; then + if test $ssl_version = error; then + AC_MSG_ERROR([$ssl_incdir/openssl/opensslv.h doesn't define OPENSSL_VERSION_NUMBER !]) + else + if test $ssl_version = old; then + AC_MSG_WARN([OpenSSL version too old. Upgrade to 0.9.6 at least, see http://www.openssl.org. SSL support disabled.]) + have_ssl=no + fi + fi + ac_cv_ssl_version="ssl_version=$ssl_version" + else + AC_MSG_ERROR([Your system couldn't run a small SSL test program. + Check config.log, and if you can't figure it out, send a mail to + David Faure , attaching your config.log]) + fi + + else + AC_MSG_ERROR([Your system couldn't link a small SSL test program. + Check config.log, and if you can't figure it out, send a mail to + David Faure , attaching your config.log]) + fi + CPPFLAGS=$ac_save_CPPFLAGS + + ]) + + eval "$ac_cv_ssl_version" + AC_MSG_RESULT($ssl_version) +fi + +if test "$have_ssl" != yes; then + LIBSSL=""; +else + AC_DEFINE(HAVE_SSL, 1, [If we are going to use OpenSSL]) + ac_cv_have_ssl="have_ssl=yes \ + ac_ssl_includes=$ac_ssl_includes ac_ssl_libraries=$ac_ssl_libraries ac_ssl_rsaref=$ac_ssl_rsaref" + + + ssl_libraries="$ac_ssl_libraries" + ssl_includes="$ac_ssl_includes" + + if test "$ac_ssl_rsaref" = yes; then + LIBSSL="-lssl -lcrypto -lRSAglue -lrsaref" + fi + + if test $ssl_version = "old"; then + AC_DEFINE(HAVE_OLD_SSL_API, 1, [Define if you have OpenSSL < 0.9.6]) + fi +fi + +SSL_INCLUDES= + +if test "$ssl_includes" = "/usr/include"; then + if test -f /usr/kerberos/include/krb5.h; then + SSL_INCLUDES="-I/usr/kerberos/include" + fi +elif test "$ssl_includes" != "/usr/local/include" && test -n "$ssl_includes"; then + SSL_INCLUDES="-I$ssl_includes" +fi + +if test "$ssl_libraries" = "/usr/lib" || test "$ssl_libraries" = "/usr/local/lib" || test -z "$ssl_libraries" || test "$ssl_libraries" = "NONE"; then + SSL_LDFLAGS="" +else + SSL_LDFLAGS="-L$ssl_libraries -R$ssl_libraries" +fi + +AC_SUBST(SSL_INCLUDES) +AC_SUBST(SSL_LDFLAGS) +AC_SUBST(LIBSSL) +]) + +AC_DEFUN([KDE_CHECK_STRLCPY], +[ + AC_REQUIRE([AC_CHECK_STRLCAT]) + AC_REQUIRE([AC_CHECK_STRLCPY]) + AC_CHECK_SIZEOF(size_t) + AC_CHECK_SIZEOF(unsigned long) + + AC_MSG_CHECKING([sizeof size_t == sizeof unsigned long]) + AC_TRY_COMPILE(,[ + #if SIZEOF_SIZE_T != SIZEOF_UNSIGNED_LONG + choke me + #endif + ],AC_MSG_RESULT([yes]),[ + AC_MSG_RESULT(no) + AC_MSG_ERROR([ + Apparently on your system our assumption sizeof size_t == sizeof unsigned long + does not apply. Please mail kde-devel@kde.org with a description of your system! + ]) + ]) +]) + +AC_DEFUN([KDE_CHECK_BINUTILS], +[ + AC_MSG_CHECKING([if ld supports unversioned version maps]) + + kde_save_LDFLAGS="$LDFLAGS" + LDFLAGS="$LDFLAGS -Wl,--version-script=conftest.map" + echo "{ local: extern \"C++\" { foo }; };" > conftest.map + AC_TRY_LINK([int foo;], +[ +#ifdef __INTEL_COMPILER +icc apparently does not support libtools version-info and version-script +at the same time. Dunno where the bug is, but until somebody figured out, +better disable the optional version scripts. +#endif + + foo = 42; +], kde_supports_versionmaps=yes, kde_supports_versionmaps=no) + LDFLAGS="$kde_save_LDFLAGS" + rm -f conftest.map + AM_CONDITIONAL(include_VERSION_SCRIPT, + [test "$kde_supports_versionmaps" = "yes" && test "$kde_use_debug_code" = "no"]) + + AC_MSG_RESULT($kde_supports_versionmaps) +]) + +AC_DEFUN([AM_PROG_OBJC],[ +AC_CHECK_PROGS(OBJC, gcc, gcc) +test -z "$OBJC" && AC_MSG_ERROR([no acceptable objective-c gcc found in \$PATH]) +if test "x${OBJCFLAGS-unset}" = xunset; then + OBJCFLAGS="-g -O2" +fi +AC_SUBST(OBJCFLAGS) +_AM_IF_OPTION([no-dependencies],, [_AM_DEPENDENCIES(OBJC)]) +]) + +AC_DEFUN([KDE_CHECK_PERL], +[ + KDE_FIND_PATH(perl, PERL, [$bindir $exec_prefix/bin $prefix/bin], [ + AC_MSG_ERROR([No Perl found in your $PATH. +We need perl to generate some code.]) + ]) + AC_SUBST(PERL) +]) + +AC_DEFUN([KDE_CHECK_LARGEFILE], +[ +AC_SYS_LARGEFILE +if test "$ac_cv_sys_file_offset_bits" != no; then + CPPFLAGS="$CPPFLAGS -D_FILE_OFFSET_BITS=$ac_cv_sys_file_offset_bits" +fi + +if test "x$ac_cv_sys_large_files" != "xno"; then + CPPFLAGS="$CPPFLAGS -D_LARGE_FILES=1" +fi + +]) + +dnl A small extension to PKG_CHECK_MODULES (defined in pkg.m4.in) +dnl which allows to search for libs that get installed into the KDE prefix. +dnl +dnl Syntax: KDE_PKG_CHECK_MODULES(KSTUFF, libkexif >= 0.2 glib = 1.3.4, action-if, action-not) +dnl defines KSTUFF_LIBS, KSTUFF_CFLAGS, see pkg-config man page +dnl also defines KSTUFF_PKG_ERRORS on error +AC_DEFUN([KDE_PKG_CHECK_MODULES], [ + + PKG_CONFIG_PATH="$prefix/lib${kdelibsuff}/pkgconfig:$PKG_CONFIG_PATH" + if test "$prefix" != "$kde_libs_prefix"; then + PKG_CONFIG_PATH="$kde_libs_prefix/lib${kdelibsuff}/pkgconfig:$PKG_CONFIG_PATH" + fi + export PKG_CONFIG_PATH + PKG_CHECK_MODULES([$1],[$2],[$3],[$4]) +]) + + +dnl Check for PIE support in the compiler and linker +AC_DEFUN([KDE_CHECK_PIE_SUPPORT], +[ + AC_CACHE_CHECK([for PIE support], kde_cv_val_pie_support, + [ + AC_LANG_SAVE + AC_LANG_CPLUSPLUS + safe_CXXFLAGS=$CXXFLAGS + safe_LDFLAGS=$LDFLAGS + CXXFLAGS="$CXXFLAGS -fPIE" + LDFLAGS="$LDFLAGS -pie" + + AC_TRY_LINK([int foo;], [], [kde_cv_val_pie_support=yes], [kde_cv_val_pie_support=no]) + + CXXFLAGS=$safe_CXXFLAGS + LDFLAGS=$safe_LDFLAGS + AC_LANG_RESTORE + ]) + + AC_MSG_CHECKING(if enabling -pie/fPIE support) + + AC_ARG_ENABLE(pie, + AC_HELP_STRING([--enable-pie],[platform supports PIE linking [default=detect]]), + [kde_has_pie_support=$enableval], + [kde_has_pie_support=detect]) + + if test "$kde_has_pie_support" = "detect"; then + kde_has_pie_support=$kde_cv_val_pie_support + fi + + AC_MSG_RESULT([$kde_has_pie_support]) + + KDE_USE_FPIE="" + KDE_USE_PIE="" + + AC_SUBST([KDE_USE_FPIE]) + AC_SUBST([KDE_USE_PIE]) + + if test "$kde_has_pie_support" = "yes"; then + KDE_USE_FPIE="-fPIE" + KDE_USE_PIE="-pie" + fi +]) +# libtool.m4 - Configure libtool for the host system. -*-Autoconf-*- +## Copyright 1996, 1997, 1998, 1999, 2000, 2001 +## Free Software Foundation, Inc. +## Originally by Gordon Matzigkeit , 1996 +## +## 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 program is distributed in the hope that it will be useful, but +## WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +## General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program; if not, write to the Free Software +## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +## +## As a special exception to the GNU General Public License, if you +## distribute this file as part of a program that contains a +## configuration script generated by Autoconf, you may include it under +## the same distribution terms that you use for the rest of that program. + +# serial 47 AC_PROG_LIBTOOL + + +# AC_PROVIDE_IFELSE(MACRO-NAME, IF-PROVIDED, IF-NOT-PROVIDED) +# ----------------------------------------------------------- +# If this macro is not defined by Autoconf, define it here. +m4_ifdef([AC_PROVIDE_IFELSE], + [], + [m4_define([AC_PROVIDE_IFELSE], + [m4_ifdef([AC_PROVIDE_$1], + [$2], [$3])])]) + + +# AC_PROG_LIBTOOL +# --------------- +AC_DEFUN([AC_PROG_LIBTOOL], +[AC_REQUIRE([_AC_PROG_LIBTOOL])dnl +dnl If AC_PROG_CXX has already been expanded, run AC_LIBTOOL_CXX +dnl immediately, otherwise, hook it in at the end of AC_PROG_CXX. + AC_PROVIDE_IFELSE([AC_PROG_CXX], + [AC_LIBTOOL_CXX], + [define([AC_PROG_CXX], defn([AC_PROG_CXX])[AC_LIBTOOL_CXX + ])]) +dnl And a similar setup for Fortran 77 support + AC_PROVIDE_IFELSE([AC_PROG_F77], + [AC_LIBTOOL_F77], + [define([AC_PROG_F77], defn([AC_PROG_F77])[AC_LIBTOOL_F77 +])]) + +dnl Quote A][M_PROG_GCJ so that aclocal doesn't bring it in needlessly. +dnl If either AC_PROG_GCJ or A][M_PROG_GCJ have already been expanded, run +dnl AC_LIBTOOL_GCJ immediately, otherwise, hook it in at the end of both. + AC_PROVIDE_IFELSE([AC_PROG_GCJ], + [AC_LIBTOOL_GCJ], + [AC_PROVIDE_IFELSE([A][M_PROG_GCJ], + [AC_LIBTOOL_GCJ], + [AC_PROVIDE_IFELSE([LT_AC_PROG_GCJ], + [AC_LIBTOOL_GCJ], + [ifdef([AC_PROG_GCJ], + [define([AC_PROG_GCJ], defn([AC_PROG_GCJ])[AC_LIBTOOL_GCJ])]) + ifdef([A][M_PROG_GCJ], + [define([A][M_PROG_GCJ], defn([A][M_PROG_GCJ])[AC_LIBTOOL_GCJ])]) + ifdef([LT_AC_PROG_GCJ], + [define([LT_AC_PROG_GCJ], + defn([LT_AC_PROG_GCJ])[AC_LIBTOOL_GCJ])])])]) +])])# AC_PROG_LIBTOOL + + +# _AC_PROG_LIBTOOL +# ---------------- +AC_DEFUN([_AC_PROG_LIBTOOL], +[AC_REQUIRE([AC_LIBTOOL_SETUP])dnl +AC_BEFORE([$0],[AC_LIBTOOL_CXX])dnl +AC_BEFORE([$0],[AC_LIBTOOL_F77])dnl +AC_BEFORE([$0],[AC_LIBTOOL_GCJ])dnl + +# This can be used to rebuild libtool when needed +LIBTOOL_DEPS="$ac_aux_dir/ltmain.sh" + +# Always use our own libtool. +LIBTOOL='$(SHELL) $(top_builddir)/libtool --silent' +AC_SUBST(LIBTOOL)dnl + +# Prevent multiple expansion +define([AC_PROG_LIBTOOL], []) +])# _AC_PROG_LIBTOOL + + +# AC_LIBTOOL_SETUP +# ---------------- +AC_DEFUN([AC_LIBTOOL_SETUP], +[AC_PREREQ(2.50)dnl +AC_REQUIRE([AC_ENABLE_SHARED])dnl +AC_REQUIRE([AC_ENABLE_STATIC])dnl +AC_REQUIRE([AC_ENABLE_FAST_INSTALL])dnl +AC_REQUIRE([AC_CANONICAL_HOST])dnl +AC_REQUIRE([AC_CANONICAL_BUILD])dnl +AC_REQUIRE([AC_PROG_CC])dnl +AC_REQUIRE([AC_PROG_LD])dnl +AC_REQUIRE([AC_PROG_LD_RELOAD_FLAG])dnl +AC_REQUIRE([AC_PROG_NM])dnl + +AC_REQUIRE([AC_PROG_LN_S])dnl +AC_REQUIRE([AC_DEPLIBS_CHECK_METHOD])dnl +# Autoconf 2.13's AC_OBJEXT and AC_EXEEXT macros only works for C compilers! +AC_REQUIRE([AC_OBJEXT])dnl +AC_REQUIRE([AC_EXEEXT])dnl +dnl + +AC_LIBTOOL_SYS_MAX_CMD_LEN +AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE +AC_LIBTOOL_OBJDIR + +AC_REQUIRE([_LT_AC_SYS_COMPILER])dnl +_LT_AC_PROG_ECHO_BACKSLASH + +case $host_os in +aix3*) + # AIX sometimes has problems with the GCC collect2 program. For some + # reason, if we set the COLLECT_NAMES environment variable, the problems + # vanish in a puff of smoke. + if test "X${COLLECT_NAMES+set}" != Xset; then + COLLECT_NAMES= + export COLLECT_NAMES + fi + ;; +esac + +# Sed substitution that helps us do robust quoting. It backslashifies +# metacharacters that are still active within double-quoted strings. +Xsed='sed -e s/^X//' +[sed_quote_subst='s/\([\\"\\`$\\\\]\)/\\\1/g'] + +# Same as above, but do not quote variable references. +[double_quote_subst='s/\([\\"\\`\\\\]\)/\\\1/g'] + +# Sed substitution to delay expansion of an escaped shell variable in a +# double_quote_subst'ed string. +delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g' + +# Sed substitution to avoid accidental globbing in evaled expressions +no_glob_subst='s/\*/\\\*/g' + +# Constants: +rm="rm -f" + +# Global variables: +default_ofile=libtool +can_build_shared=yes + +# All known linkers require a `.a' archive for static linking (except M$VC, +# which needs '.lib'). +libext=a +ltmain="$ac_aux_dir/ltmain.sh" +ofile="$default_ofile" +with_gnu_ld="$lt_cv_prog_gnu_ld" + +AC_CHECK_TOOL(AR, ar, false) +AC_CHECK_TOOL(RANLIB, ranlib, :) +AC_CHECK_TOOL(STRIP, strip, :) + +old_CC="$CC" +old_CFLAGS="$CFLAGS" + +# Set sane defaults for various variables +test -z "$AR" && AR=ar +test -z "$AR_FLAGS" && AR_FLAGS=cru +test -z "$AS" && AS=as +test -z "$CC" && CC=cc +test -z "$LTCC" && LTCC=$CC +test -z "$DLLTOOL" && DLLTOOL=dlltool +test -z "$LD" && LD=ld +test -z "$LN_S" && LN_S="ln -s" +test -z "$MAGIC_CMD" && MAGIC_CMD=file +test -z "$NM" && NM=nm +test -z "$SED" && SED=sed +test -z "$OBJDUMP" && OBJDUMP=objdump +test -z "$RANLIB" && RANLIB=: +test -z "$STRIP" && STRIP=: +test -z "$ac_objext" && ac_objext=o + +# Determine commands to create old-style static archives. +old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs$old_deplibs' +old_postinstall_cmds='chmod 644 $oldlib' +old_postuninstall_cmds= + +if test -n "$RANLIB"; then + case $host_os in + openbsd*) + old_postinstall_cmds="\$RANLIB -t \$oldlib~$old_postinstall_cmds" + ;; + *) + old_postinstall_cmds="\$RANLIB \$oldlib~$old_postinstall_cmds" + ;; + esac + old_archive_cmds="$old_archive_cmds~\$RANLIB \$oldlib" +fi + +# Only perform the check for file, if the check method requires it +case $deplibs_check_method in +file_magic*) + if test "$file_magic_cmd" = '$MAGIC_CMD'; then + AC_PATH_MAGIC + fi + ;; +esac + +AC_PROVIDE_IFELSE([AC_LIBTOOL_DLOPEN], enable_dlopen=yes, enable_dlopen=no) +AC_PROVIDE_IFELSE([AC_LIBTOOL_WIN32_DLL], +enable_win32_dll=yes, enable_win32_dll=no) + +AC_ARG_ENABLE([libtool-lock], + [AC_HELP_STRING([--disable-libtool-lock], + [avoid locking (might break parallel builds)])]) +test "x$enable_libtool_lock" != xno && enable_libtool_lock=yes + +AC_ARG_WITH([pic], + [AC_HELP_STRING([--with-pic], + [try to use only PIC/non-PIC objects @<:@default=use both@:>@])], + [pic_mode="$withval"], + [pic_mode=default]) +test -z "$pic_mode" && pic_mode=default + +# Use C for the default configuration in the libtool script +tagname= +AC_LIBTOOL_LANG_C_CONFIG +_LT_AC_TAGCONFIG +])# AC_LIBTOOL_SETUP + + +# _LT_AC_SYS_COMPILER +# ------------------- +AC_DEFUN([_LT_AC_SYS_COMPILER], +[AC_REQUIRE([AC_PROG_CC])dnl + +# If no C compiler was specified, use CC. +LTCC=${LTCC-"$CC"} + +# Allow CC to be a program name with arguments. +compiler=$CC +])# _LT_AC_SYS_COMPILER + + +# _LT_AC_SYS_LIBPATH_AIX +# ---------------------- +# Links a minimal program and checks the executable +# for the system default hardcoded library path. In most cases, +# this is /usr/lib:/lib, but when the MPI compilers are used +# the location of the communication and MPI libs are included too. +# If we don't find anything, use the default library path according +# to the aix ld manual. +AC_DEFUN([_LT_AC_SYS_LIBPATH_AIX], +[AC_LINK_IFELSE(AC_LANG_PROGRAM,[ +aix_libpath=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } +}'` +# Check for a 64-bit object if we didn't find anything. +if test -z "$aix_libpath"; then aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } +}'`; fi],[]) +if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi +])# _LT_AC_SYS_LIBPATH_AIX + + +# _LT_AC_SHELL_INIT(ARG) +# ---------------------- +AC_DEFUN([_LT_AC_SHELL_INIT], +[ifdef([AC_DIVERSION_NOTICE], + [AC_DIVERT_PUSH(AC_DIVERSION_NOTICE)], + [AC_DIVERT_PUSH(NOTICE)]) +$1 +AC_DIVERT_POP +])# _LT_AC_SHELL_INIT + + +# _LT_AC_PROG_ECHO_BACKSLASH +# -------------------------- +# Add some code to the start of the generated configure script which +# will find an echo command which doesn't interpret backslashes. +AC_DEFUN([_LT_AC_PROG_ECHO_BACKSLASH], +[_LT_AC_SHELL_INIT([ +# Check that we are running under the correct shell. +SHELL=${CONFIG_SHELL-/bin/sh} + +case X$ECHO in +X*--fallback-echo) + # Remove one level of quotation (which was required for Make). + ECHO=`echo "$ECHO" | sed 's,\\\\\[$]\\[$]0,'[$]0','` + ;; +esac + +echo=${ECHO-echo} +if test "X[$]1" = X--no-reexec; then + # Discard the --no-reexec flag, and continue. + shift +elif test "X[$]1" = X--fallback-echo; then + # Avoid inline document here, it may be left over + : +elif test "X`($echo '\t') 2>/dev/null`" = 'X\t' ; then + # Yippee, $echo works! + : +else + # Restart under the correct shell. + exec $SHELL "[$]0" --no-reexec ${1+"[$]@"} +fi + +if test "X[$]1" = X--fallback-echo; then + # used as fallback echo + shift + cat </dev/null && + echo_test_string="`eval $cmd`" && + (test "X$echo_test_string" = "X$echo_test_string") 2>/dev/null + then + break + fi + done +fi + +if test "X`($echo '\t') 2>/dev/null`" = 'X\t' && + echo_testing_string=`($echo "$echo_test_string") 2>/dev/null` && + test "X$echo_testing_string" = "X$echo_test_string"; then + : +else + # The Solaris, AIX, and Digital Unix default echo programs unquote + # backslashes. This makes it impossible to quote backslashes using + # echo "$something" | sed 's/\\/\\\\/g' + # + # So, first we look for a working echo in the user's PATH. + + lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR + for dir in $PATH /usr/ucb; do + IFS="$lt_save_ifs" + if (test -f $dir/echo || test -f $dir/echo$ac_exeext) && + test "X`($dir/echo '\t') 2>/dev/null`" = 'X\t' && + echo_testing_string=`($dir/echo "$echo_test_string") 2>/dev/null` && + test "X$echo_testing_string" = "X$echo_test_string"; then + echo="$dir/echo" + break + fi + done + IFS="$lt_save_ifs" + + if test "X$echo" = Xecho; then + # We didn't find a better echo, so look for alternatives. + if test "X`(print -r '\t') 2>/dev/null`" = 'X\t' && + echo_testing_string=`(print -r "$echo_test_string") 2>/dev/null` && + test "X$echo_testing_string" = "X$echo_test_string"; then + # This shell has a builtin print -r that does the trick. + echo='print -r' + elif (test -f /bin/ksh || test -f /bin/ksh$ac_exeext) && + test "X$CONFIG_SHELL" != X/bin/ksh; then + # If we have ksh, try running configure again with it. + ORIGINAL_CONFIG_SHELL=${CONFIG_SHELL-/bin/sh} + export ORIGINAL_CONFIG_SHELL + CONFIG_SHELL=/bin/ksh + export CONFIG_SHELL + exec $CONFIG_SHELL "[$]0" --no-reexec ${1+"[$]@"} + else + # Try using printf. + echo='printf %s\n' + if test "X`($echo '\t') 2>/dev/null`" = 'X\t' && + echo_testing_string=`($echo "$echo_test_string") 2>/dev/null` && + test "X$echo_testing_string" = "X$echo_test_string"; then + # Cool, printf works + : + elif echo_testing_string=`($ORIGINAL_CONFIG_SHELL "[$]0" --fallback-echo '\t') 2>/dev/null` && + test "X$echo_testing_string" = 'X\t' && + echo_testing_string=`($ORIGINAL_CONFIG_SHELL "[$]0" --fallback-echo "$echo_test_string") 2>/dev/null` && + test "X$echo_testing_string" = "X$echo_test_string"; then + CONFIG_SHELL=$ORIGINAL_CONFIG_SHELL + export CONFIG_SHELL + SHELL="$CONFIG_SHELL" + export SHELL + echo="$CONFIG_SHELL [$]0 --fallback-echo" + elif echo_testing_string=`($CONFIG_SHELL "[$]0" --fallback-echo '\t') 2>/dev/null` && + test "X$echo_testing_string" = 'X\t' && + echo_testing_string=`($CONFIG_SHELL "[$]0" --fallback-echo "$echo_test_string") 2>/dev/null` && + test "X$echo_testing_string" = "X$echo_test_string"; then + echo="$CONFIG_SHELL [$]0 --fallback-echo" + else + # maybe with a smaller string... + prev=: + + for cmd in 'echo test' 'sed 2q "[$]0"' 'sed 10q "[$]0"' 'sed 20q "[$]0"' 'sed 50q "[$]0"'; do + if (test "X$echo_test_string" = "X`eval $cmd`") 2>/dev/null + then + break + fi + prev="$cmd" + done + + if test "$prev" != 'sed 50q "[$]0"'; then + echo_test_string=`eval $prev` + export echo_test_string + exec ${ORIGINAL_CONFIG_SHELL-${CONFIG_SHELL-/bin/sh}} "[$]0" ${1+"[$]@"} + else + # Oops. We lost completely, so just stick with echo. + echo=echo + fi + fi + fi + fi +fi +fi + +# Copy echo and quote the copy suitably for passing to libtool from +# the Makefile, instead of quoting the original, which is used later. +ECHO=$echo +if test "X$ECHO" = "X$CONFIG_SHELL [$]0 --fallback-echo"; then + ECHO="$CONFIG_SHELL \\\$\[$]0 --fallback-echo" +fi + +AC_SUBST(ECHO) +])])# _LT_AC_PROG_ECHO_BACKSLASH + + +# _LT_AC_LOCK +# ----------- +AC_DEFUN([_LT_AC_LOCK], +[AC_ARG_ENABLE([libtool-lock], + [AC_HELP_STRING([--disable-libtool-lock], + [avoid locking (might break parallel builds)])]) +test "x$enable_libtool_lock" != xno && enable_libtool_lock=yes + +# Some flags need to be propagated to the compiler or linker for good +# libtool support. +case $host in +ia64-*-hpux*) + # Find out which ABI we are using. + echo 'int i;' > conftest.$ac_ext + if AC_TRY_EVAL(ac_compile); then + case `/usr/bin/file conftest.$ac_objext` in + *ELF-32*) + HPUX_IA64_MODE="32" + ;; + *ELF-64*) + HPUX_IA64_MODE="64" + ;; + esac + fi + rm -rf conftest* + ;; +*-*-irix6*) + # Find out which ABI we are using. + echo '[#]line __oline__ "configure"' > conftest.$ac_ext + if AC_TRY_EVAL(ac_compile); then + if test "$lt_cv_prog_gnu_ld" = yes; then + case `/usr/bin/file conftest.$ac_objext` in + *32-bit*) + LD="${LD-ld} -melf32bsmip" + ;; + *N32*) + LD="${LD-ld} -melf32bmipn32" + ;; + *64-bit*) + LD="${LD-ld} -melf64bmip" + ;; + esac + else + case `/usr/bin/file conftest.$ac_objext` in + *32-bit*) + LD="${LD-ld} -32" + ;; + *N32*) + LD="${LD-ld} -n32" + ;; + *64-bit*) + LD="${LD-ld} -64" + ;; + esac + fi + fi + rm -rf conftest* + ;; + +x86_64-*linux*|ppc*-*linux*|powerpc*-*linux*|s390*-*linux*|sparc*-*linux*) + # Find out which ABI we are using. + echo 'int i;' > conftest.$ac_ext + if AC_TRY_EVAL(ac_compile); then + case "`/usr/bin/file conftest.o`" in + *32-bit*) + LINUX_64_MODE="32" + case $host in + x86_64-*linux*) + LD="${LD-ld} -m elf_i386" + ;; + ppc64-*linux*) + LD="${LD-ld} -m elf32ppclinux" + ;; + s390x-*linux*) + LD="${LD-ld} -m elf_s390" + ;; + sparc64-*linux*) + LD="${LD-ld} -m elf32_sparc" + ;; + esac + ;; + *64-bit*) + LINUX_64_MODE="64" + case $host in + x86_64-*linux*) + LD="${LD-ld} -m elf_x86_64" + ;; + ppc*-*linux*|powerpc*-*linux*) + LD="${LD-ld} -m elf64ppc" + ;; + s390*-*linux*) + LD="${LD-ld} -m elf64_s390" + ;; + sparc*-*linux*) + LD="${LD-ld} -m elf64_sparc" + ;; + esac + ;; + esac + fi + rm -rf conftest* + ;; + +*-*-sco3.2v5*) + # On SCO OpenServer 5, we need -belf to get full-featured binaries. + SAVE_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS -belf" + AC_CACHE_CHECK([whether the C compiler needs -belf], lt_cv_cc_needs_belf, + [AC_LANG_PUSH(C) + AC_TRY_LINK([],[],[lt_cv_cc_needs_belf=yes],[lt_cv_cc_needs_belf=no]) + AC_LANG_POP]) + if test x"$lt_cv_cc_needs_belf" != x"yes"; then + # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf + CFLAGS="$SAVE_CFLAGS" + fi + ;; +AC_PROVIDE_IFELSE([AC_LIBTOOL_WIN32_DLL], +[*-*-cygwin* | *-*-mingw* | *-*-pw32*) + AC_CHECK_TOOL(DLLTOOL, dlltool, false) + AC_CHECK_TOOL(AS, as, false) + AC_CHECK_TOOL(OBJDUMP, objdump, false) + ;; + ]) +esac + +need_locks="$enable_libtool_lock" + +])# _LT_AC_LOCK + + +# AC_LIBTOOL_COMPILER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS, +# [OUTPUT-FILE], [ACTION-SUCCESS], [ACTION-FAILURE]) +# ---------------------------------------------------------------- +# Check whether the given compiler option works +AC_DEFUN([AC_LIBTOOL_COMPILER_OPTION], +[AC_REQUIRE([LT_AC_PROG_SED]) +AC_CACHE_CHECK([$1], [$2], + [$2=no + ifelse([$4], , [ac_outfile=conftest.$ac_objext], [ac_outfile=$4]) + printf "$lt_simple_compile_test_code" > conftest.$ac_ext + lt_compiler_flag="$3" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + # The option is referenced via a variable to avoid confusing sed. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \ + -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:__oline__: $lt_compile\"" >&AS_MESSAGE_LOG_FD) + (eval "$lt_compile" 2>conftest.err) + ac_status=$? + cat conftest.err >&AS_MESSAGE_LOG_FD + echo "$as_me:__oline__: \$? = $ac_status" >&AS_MESSAGE_LOG_FD + if (exit $ac_status) && test -s "$ac_outfile"; then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings + if test ! -s conftest.err; then + $2=yes + fi + fi + $rm conftest* +]) + +if test x"[$]$2" = xyes; then + ifelse([$5], , :, [$5]) +else + ifelse([$6], , :, [$6]) +fi +])# AC_LIBTOOL_COMPILER_OPTION + + +# AC_LIBTOOL_LINKER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS, +# [ACTION-SUCCESS], [ACTION-FAILURE]) +# ------------------------------------------------------------ +# Check whether the given compiler option works +AC_DEFUN([AC_LIBTOOL_LINKER_OPTION], +[AC_CACHE_CHECK([$1], [$2], + [$2=no + save_LDFLAGS="$LDFLAGS" + LDFLAGS="$LDFLAGS $3" + printf "$lt_simple_link_test_code" > conftest.$ac_ext + if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings + if test -s conftest.err; then + # Append any errors to the config.log. + cat conftest.err 1>&AS_MESSAGE_LOG_FD + else + $2=yes + fi + fi + $rm conftest* + LDFLAGS="$save_LDFLAGS" +]) + +if test x"[$]$2" = xyes; then + ifelse([$4], , :, [$4]) +else + ifelse([$5], , :, [$5]) +fi +])# AC_LIBTOOL_LINKER_OPTION + + +# AC_LIBTOOL_SYS_MAX_CMD_LEN +# -------------------------- +AC_DEFUN([AC_LIBTOOL_SYS_MAX_CMD_LEN], +[# find the maximum length of command line arguments +AC_MSG_CHECKING([the maximum length of command line arguments]) +AC_CACHE_VAL([lt_cv_sys_max_cmd_len], [dnl + i=0 + testring="ABCD" + + case $build_os in + msdosdjgpp*) + # On DJGPP, this test can blow up pretty badly due to problems in libc + # (any single argument exceeding 2000 bytes causes a buffer overrun + # during glob expansion). Even if it were fixed, the result of this + # check would be larger than it should be. + lt_cv_sys_max_cmd_len=12288; # 12K is about right + ;; + + gnu*) + # Under GNU Hurd, this test is not required because there is + # no limit to the length of command line arguments. + # Libtool will interpret -1 as no limit whatsoever + lt_cv_sys_max_cmd_len=-1; + ;; + + cygwin* | mingw*) + # On Win9x/ME, this test blows up -- it succeeds, but takes + # about 5 minutes as the teststring grows exponentially. + # Worse, since 9x/ME are not pre-emptively multitasking, + # you end up with a "frozen" computer, even though with patience + # the test eventually succeeds (with a max line length of 256k). + # Instead, let's just punt: use the minimum linelength reported by + # all of the supported platforms: 8192 (on NT/2K/XP). + lt_cv_sys_max_cmd_len=8192; + ;; + + *) + # If test is not a shell built-in, we'll probably end up computing a + # maximum length that is only half of the actual maximum length, but + # we can't tell. + while (test "X"`$CONFIG_SHELL [$]0 --fallback-echo "X$testring" 2>/dev/null` \ + = "XX$testring") >/dev/null 2>&1 && + new_result=`expr "X$testring" : ".*" 2>&1` && + lt_cv_sys_max_cmd_len=$new_result && + test $i != 17 # 1/2 MB should be enough + do + i=`expr $i + 1` + testring=$testring$testring + done + testring= + # Add a significant safety factor because C++ compilers can tack on massive + # amounts of additional arguments before passing them to the linker. + # It appears as though 1/2 is a usable value. + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2` + ;; + esac +]) +if test -n $lt_cv_sys_max_cmd_len ; then + AC_MSG_RESULT($lt_cv_sys_max_cmd_len) +else + AC_MSG_RESULT(none) +fi +])# AC_LIBTOOL_SYS_MAX_CMD_LEN + + +# _LT_AC_CHECK_DLFCN +# -------------------- +AC_DEFUN([_LT_AC_CHECK_DLFCN], +[AC_CHECK_HEADERS(dlfcn.h)dnl +])# _LT_AC_CHECK_DLFCN + + +# _LT_AC_TRY_DLOPEN_SELF (ACTION-IF-TRUE, ACTION-IF-TRUE-W-USCORE, +# ACTION-IF-FALSE, ACTION-IF-CROSS-COMPILING) +# ------------------------------------------------------------------ +AC_DEFUN([_LT_AC_TRY_DLOPEN_SELF], +[AC_REQUIRE([_LT_AC_CHECK_DLFCN])dnl +if test "$cross_compiling" = yes; then : + [$4] +else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext < +#endif + +#include + +#ifdef RTLD_GLOBAL +# define LT_DLGLOBAL RTLD_GLOBAL +#else +# ifdef DL_GLOBAL +# define LT_DLGLOBAL DL_GLOBAL +# else +# define LT_DLGLOBAL 0 +# endif +#endif + +/* We may have to define LT_DLLAZY_OR_NOW in the command line if we + find out it does not work in some platform. */ +#ifndef LT_DLLAZY_OR_NOW +# ifdef RTLD_LAZY +# define LT_DLLAZY_OR_NOW RTLD_LAZY +# else +# ifdef DL_LAZY +# define LT_DLLAZY_OR_NOW DL_LAZY +# else +# ifdef RTLD_NOW +# define LT_DLLAZY_OR_NOW RTLD_NOW +# else +# ifdef DL_NOW +# define LT_DLLAZY_OR_NOW DL_NOW +# else +# define LT_DLLAZY_OR_NOW 0 +# endif +# endif +# endif +# endif +#endif + +#ifdef __cplusplus +extern "C" void exit (int); +#endif + +void fnord() { int i=42;} +int main () +{ + void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); + int status = $lt_dlunknown; + + if (self) + { + if (dlsym (self,"fnord")) status = $lt_dlno_uscore; + else if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; + /* dlclose (self); */ + } + + exit (status); +}] +EOF + if AC_TRY_EVAL(ac_link) && test -s conftest${ac_exeext} 2>/dev/null; then + (./conftest; exit; ) 2>/dev/null + lt_status=$? + case x$lt_status in + x$lt_dlno_uscore) $1 ;; + x$lt_dlneed_uscore) $2 ;; + x$lt_unknown|x*) $3 ;; + esac + else : + # compilation failed + $3 + fi +fi +rm -fr conftest* +])# _LT_AC_TRY_DLOPEN_SELF + + +# AC_LIBTOOL_DLOPEN_SELF +# ------------------- +AC_DEFUN([AC_LIBTOOL_DLOPEN_SELF], +[AC_REQUIRE([_LT_AC_CHECK_DLFCN])dnl +if test "x$enable_dlopen" != xyes; then + enable_dlopen=unknown + enable_dlopen_self=unknown + enable_dlopen_self_static=unknown +else + lt_cv_dlopen=no + lt_cv_dlopen_libs= + + case $host_os in + beos*) + lt_cv_dlopen="load_add_on" + lt_cv_dlopen_libs= + lt_cv_dlopen_self=yes + ;; + + mingw* | pw32*) + lt_cv_dlopen="LoadLibrary" + lt_cv_dlopen_libs= + ;; + + cygwin*) + lt_cv_dlopen="dlopen" + lt_cv_dlopen_libs= + ;; + + darwin*) + # if libdl is installed we need to link against it + AC_CHECK_LIB([dl], [dlopen], + [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"],[ + lt_cv_dlopen="dyld" + lt_cv_dlopen_libs= + lt_cv_dlopen_self=yes + ]) + ;; + + *) + AC_CHECK_FUNC([shl_load], + [lt_cv_dlopen="shl_load"], + [AC_CHECK_LIB([dld], [shl_load], + [lt_cv_dlopen="shl_load" lt_cv_dlopen_libs="-dld"], + [AC_CHECK_FUNC([dlopen], + [lt_cv_dlopen="dlopen"], + [AC_CHECK_LIB([dl], [dlopen], + [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"], + [AC_CHECK_LIB([svld], [dlopen], + [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-lsvld"], + [AC_CHECK_LIB([dld], [dld_link], + [lt_cv_dlopen="dld_link" lt_cv_dlopen_libs="-dld"]) + ]) + ]) + ]) + ]) + ]) + ;; + esac + + if test "x$lt_cv_dlopen" != xno; then + enable_dlopen=yes + else + enable_dlopen=no + fi + + case $lt_cv_dlopen in + dlopen) + save_CPPFLAGS="$CPPFLAGS" + test "x$ac_cv_header_dlfcn_h" = xyes && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H" + + save_LDFLAGS="$LDFLAGS" + eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\" + + save_LIBS="$LIBS" + LIBS="$lt_cv_dlopen_libs $LIBS" + + AC_CACHE_CHECK([whether a program can dlopen itself], + lt_cv_dlopen_self, [dnl + _LT_AC_TRY_DLOPEN_SELF( + lt_cv_dlopen_self=yes, lt_cv_dlopen_self=yes, + lt_cv_dlopen_self=no, lt_cv_dlopen_self=cross) + ]) + + if test "x$lt_cv_dlopen_self" = xyes; then + LDFLAGS="$LDFLAGS $link_static_flag" + AC_CACHE_CHECK([whether a statically linked program can dlopen itself], + lt_cv_dlopen_self_static, [dnl + _LT_AC_TRY_DLOPEN_SELF( + lt_cv_dlopen_self_static=yes, lt_cv_dlopen_self_static=yes, + lt_cv_dlopen_self_static=no, lt_cv_dlopen_self_static=cross) + ]) + fi + + CPPFLAGS="$save_CPPFLAGS" + LDFLAGS="$save_LDFLAGS" + LIBS="$save_LIBS" + ;; + esac + + case $lt_cv_dlopen_self in + yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;; + *) enable_dlopen_self=unknown ;; + esac + + case $lt_cv_dlopen_self_static in + yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;; + *) enable_dlopen_self_static=unknown ;; + esac +fi +])# AC_LIBTOOL_DLOPEN_SELF + + +# AC_LIBTOOL_PROG_CC_C_O([TAGNAME]) +# --------------------------------- +# Check to see if options -c and -o are simultaneously supported by compiler +AC_DEFUN([AC_LIBTOOL_PROG_CC_C_O], +[AC_REQUIRE([_LT_AC_SYS_COMPILER])dnl +AC_CACHE_CHECK([if $compiler supports -c -o file.$ac_objext], + [_LT_AC_TAGVAR(lt_cv_prog_compiler_c_o, $1)], + [_LT_AC_TAGVAR(lt_cv_prog_compiler_c_o, $1)=no + $rm -r conftest 2>/dev/null + mkdir conftest + cd conftest + mkdir out + printf "$lt_simple_compile_test_code" > conftest.$ac_ext + + # According to Tom Tromey, Ian Lance Taylor reported there are C compilers + # that will create temporary files in the current directory regardless of + # the output directory. Thus, making CWD read-only will cause this test + # to fail, enabling locking or at least warning the user not to do parallel + # builds. + chmod -w . + + lt_compiler_flag="-o out/conftest2.$ac_objext" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \ + -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:__oline__: $lt_compile\"" >&AS_MESSAGE_LOG_FD) + (eval "$lt_compile" 2>out/conftest.err) + ac_status=$? + cat out/conftest.err >&AS_MESSAGE_LOG_FD + echo "$as_me:__oline__: \$? = $ac_status" >&AS_MESSAGE_LOG_FD + if (exit $ac_status) && test -s out/conftest2.$ac_objext + then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings + if test ! -s out/conftest.err; then + _LT_AC_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes + fi + fi + chmod u+w . + $rm conftest* out/* + rmdir out + cd .. + rmdir conftest + $rm conftest* +]) +])# AC_LIBTOOL_PROG_CC_C_O + + +# AC_LIBTOOL_SYS_HARD_LINK_LOCKS([TAGNAME]) +# ----------------------------------------- +# Check to see if we can do hard links to lock some files if needed +AC_DEFUN([AC_LIBTOOL_SYS_HARD_LINK_LOCKS], +[AC_REQUIRE([_LT_AC_LOCK])dnl + +hard_links="nottested" +if test "$_LT_AC_TAGVAR(lt_cv_prog_compiler_c_o, $1)" = no && test "$need_locks" != no; then + # do not overwrite the value of need_locks provided by the user + AC_MSG_CHECKING([if we can lock with hard links]) + hard_links=yes + $rm conftest* + ln conftest.a conftest.b 2>/dev/null && hard_links=no + touch conftest.a + ln conftest.a conftest.b 2>&5 || hard_links=no + ln conftest.a conftest.b 2>/dev/null && hard_links=no + AC_MSG_RESULT([$hard_links]) + if test "$hard_links" = no; then + AC_MSG_WARN([`$CC' does not support `-c -o', so `make -j' may be unsafe]) + need_locks=warn + fi +else + need_locks=no +fi +])# AC_LIBTOOL_SYS_HARD_LINK_LOCKS + + +# AC_LIBTOOL_OBJDIR +# ----------------- +AC_DEFUN([AC_LIBTOOL_OBJDIR], +[AC_CACHE_CHECK([for objdir], [lt_cv_objdir], +[rm -f .libs 2>/dev/null +mkdir .libs 2>/dev/null +if test -d .libs; then + lt_cv_objdir=.libs +else + # MS-DOS does not allow filenames that begin with a dot. + lt_cv_objdir=_libs +fi +rmdir .libs 2>/dev/null]) +objdir=$lt_cv_objdir +])# AC_LIBTOOL_OBJDIR + + +# AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH([TAGNAME]) +# ---------------------------------------------- +# Check hardcoding attributes. +AC_DEFUN([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH], +[AC_MSG_CHECKING([how to hardcode library paths into programs]) +_LT_AC_TAGVAR(hardcode_action, $1)= +if test -n "$_LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)" || \ + test -n "$_LT_AC_TAGVAR(runpath_var $1)" || \ + test "X$_LT_AC_TAGVAR(hardcode_automatic, $1)"="Xyes" ; then + + # We can hardcode non-existant directories. + if test "$_LT_AC_TAGVAR(hardcode_direct, $1)" != no && + # If the only mechanism to avoid hardcoding is shlibpath_var, we + # have to relink, otherwise we might link with an installed library + # when we should be linking with a yet-to-be-installed one + ## test "$_LT_AC_TAGVAR(hardcode_shlibpath_var, $1)" != no && + test "$_LT_AC_TAGVAR(hardcode_minus_L, $1)" != no; then + # Linking always hardcodes the temporary library directory. + _LT_AC_TAGVAR(hardcode_action, $1)=relink + else + # We can link without hardcoding, and we can hardcode nonexisting dirs. + _LT_AC_TAGVAR(hardcode_action, $1)=immediate + fi +else + # We cannot hardcode anything, or else we can only hardcode existing + # directories. + _LT_AC_TAGVAR(hardcode_action, $1)=unsupported +fi +AC_MSG_RESULT([$_LT_AC_TAGVAR(hardcode_action, $1)]) + +if test "$_LT_AC_TAGVAR(hardcode_action, $1)" = relink; then + # Fast installation is not supported + enable_fast_install=no +elif test "$shlibpath_overrides_runpath" = yes || + test "$enable_shared" = no; then + # Fast installation is not necessary + enable_fast_install=needless +fi +])# AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH + + +# AC_LIBTOOL_SYS_LIB_STRIP +# ------------------------ +AC_DEFUN([AC_LIBTOOL_SYS_LIB_STRIP], +[striplib= +old_striplib= +AC_MSG_CHECKING([whether stripping libraries is possible]) +if test -n "$STRIP" && $STRIP -V 2>&1 | grep "GNU strip" >/dev/null; then + test -z "$old_striplib" && old_striplib="$STRIP --strip-debug" + test -z "$striplib" && striplib="$STRIP --strip-unneeded" + AC_MSG_RESULT([yes]) +else +# FIXME - insert some real tests, host_os isn't really good enough + case $host_os in + darwin*) + if test -n "$STRIP" ; then + striplib="$STRIP -x" + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) +fi + ;; + *) + AC_MSG_RESULT([no]) + ;; + esac +fi +])# AC_LIBTOOL_SYS_LIB_STRIP + + +# AC_LIBTOOL_SYS_DYNAMIC_LINKER +# ----------------------------- +# PORTME Fill in your ld.so characteristics +AC_DEFUN([AC_LIBTOOL_SYS_DYNAMIC_LINKER], +[AC_MSG_CHECKING([dynamic linker characteristics]) +library_names_spec= +libname_spec='lib$name' +soname_spec= +shrext=".so" +postinstall_cmds= +postuninstall_cmds= +finish_cmds= +finish_eval= +shlibpath_var= +shlibpath_overrides_runpath=unknown +version_type=none +dynamic_linker="$host_os ld.so" +sys_lib_dlsearch_path_spec="/lib /usr/lib" +sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" +need_lib_prefix=unknown +hardcode_into_libs=no + +# when you set need_version to no, make sure it does not cause -set_version +# flags to be left without arguments +need_version=unknown + +case $host_os in +aix3*) + version_type=linux + library_names_spec='${libname}${release}${shared_ext}$versuffix $libname.a' + shlibpath_var=LIBPATH + + # AIX 3 has no versioning support, so we append a major version to the name. + soname_spec='${libname}${release}${shared_ext}$major' + ;; + +aix4* | aix5*) + version_type=linux + need_lib_prefix=no + need_version=no + hardcode_into_libs=yes + if test "$host_cpu" = ia64; then + # AIX 5 supports IA64 + library_names_spec='${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext}$versuffix $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + else + # With GCC up to 2.95.x, collect2 would create an import file + # for dependence libraries. The import file would start with + # the line `#! .'. This would cause the generated library to + # depend on `.', always an invalid library. This was fixed in + # development snapshots of GCC prior to 3.0. + case $host_os in + aix4 | aix4.[[01]] | aix4.[[01]].*) + if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' + echo ' yes ' + echo '#endif'; } | ${CC} -E - | grep yes > /dev/null; then + : + else + can_build_shared=no + fi + ;; + esac + # AIX (on Power*) has no versioning support, so currently we can not hardcode correct + # soname into executable. Probably we can add versioning support to + # collect2, so additional links can be useful in future. + if test "$aix_use_runtimelinking" = yes; then + # If using run time linking (on AIX 4.2 or later) use lib.so + # instead of lib.a to let people know that these are not + # typical AIX shared libraries. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + else + # We preserve .a as extension for shared libraries through AIX4.2 + # and later when we are not doing run time linking. + library_names_spec='${libname}${release}.a $libname.a' + soname_spec='${libname}${release}${shared_ext}$major' + fi + shlibpath_var=LIBPATH + fi + ;; + +amigaos*) + library_names_spec='$libname.ixlibrary $libname.a' + # Create ${libname}_ixlibrary.a entries in /sys/libs. + finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`$echo "X$lib" | $Xsed -e '\''s%^.*/\([[^/]]*\)\.ixlibrary$%\1%'\''`; test $rm /sys/libs/${libname}_ixlibrary.a; $show "(cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a)"; (cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a) || exit 1; done' + ;; + +beos*) + library_names_spec='${libname}${shared_ext}' + dynamic_linker="$host_os ld.so" + shlibpath_var=LIBRARY_PATH + ;; + +bsdi4*) + version_type=linux + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' + shlibpath_var=LD_LIBRARY_PATH + sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" + sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" + # the default ld.so.conf also contains /usr/contrib/lib and + # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow + # libtool to hard-code these into programs + ;; + +cygwin* | mingw* | pw32*) + version_type=windows + shrext=".dll" + need_version=no + need_lib_prefix=no + + case $GCC,$host_os in + yes,cygwin* | yes,mingw* | yes,pw32*) + library_names_spec='$libname.dll.a' + # DLL is installed to $(libdir)/../bin by postinstall_cmds + postinstall_cmds='base_file=`basename \${file}`~ + dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i;echo \$dlname'\''`~ + dldir=$destdir/`dirname \$dlpath`~ + test -d \$dldir || mkdir -p \$dldir~ + $install_prog $dir/$dlname \$dldir/$dlname' + postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ + dlpath=$dir/\$dldll~ + $rm \$dlpath' + shlibpath_overrides_runpath=yes + + case $host_os in + cygwin*) + # Cygwin DLLs use 'cyg' prefix rather than 'lib' + soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}' + sys_lib_search_path_spec="/usr/lib /lib/w32api /lib /usr/local/lib" + ;; + mingw*) + # MinGW DLLs use traditional 'lib' prefix + soname_spec='${libname}`echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}' + sys_lib_search_path_spec=`$CC -print-search-dirs | grep "^libraries:" | $SED -e "s/^libraries://" -e "s,=/,/,g"` + if echo "$sys_lib_search_path_spec" | [grep ';[c-zC-Z]:/' >/dev/null]; then + # It is most probably a Windows format PATH printed by + # mingw gcc, but we are running on Cygwin. Gcc prints its search + # path with ; separators, and with drive letters. We can handle the + # drive letters (cygwin fileutils understands them), so leave them, + # especially as we might pass files found there to a mingw objdump, + # which wouldn't understand a cygwinified path. Ahh. + sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` + else + sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` + fi + ;; + pw32*) + # pw32 DLLs use 'pw' prefix rather than 'lib' + library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' + ;; + esac + ;; + + *) + library_names_spec='${libname}`echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext} $libname.lib' + ;; + esac + dynamic_linker='Win32 ld.exe' + # FIXME: first we should search . and the directory the executable is in + shlibpath_var=PATH + ;; + +darwin* | rhapsody*) + dynamic_linker="$host_os dyld" + version_type=darwin + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${versuffix}$shared_ext ${libname}${release}${major}$shared_ext ${libname}$shared_ext' + soname_spec='${libname}${release}${major}$shared_ext' + shlibpath_overrides_runpath=yes + shlibpath_var=DYLD_LIBRARY_PATH + shrext='$(test .$module = .yes && echo .so || echo .dylib)' + # Apple's gcc prints 'gcc -print-search-dirs' doesn't operate the same. + if test "$GCC" = yes; then + sys_lib_search_path_spec=`$CC -print-search-dirs | tr "\n" "$PATH_SEPARATOR" | sed -e 's/libraries:/@libraries:/' | tr "@" "\n" | grep "^libraries:" | sed -e "s/^libraries://" -e "s,=/,/,g" -e "s,$PATH_SEPARATOR, ,g" -e "s,.*,& /lib /usr/lib /usr/local/lib,g"` + else + sys_lib_search_path_spec='/lib /usr/lib /usr/local/lib' + fi + sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' + ;; + +dgux*) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname$shared_ext' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + ;; + +freebsd1*) + dynamic_linker=no + ;; + +kfreebsd*-gnu*) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + dynamic_linker='GNU ld.so' + ;; + +freebsd*) + objformat=`test -x /usr/bin/objformat && /usr/bin/objformat || echo aout` + version_type=freebsd-$objformat + case $version_type in + freebsd-elf*) + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' + need_version=no + need_lib_prefix=no + ;; + freebsd-*) + library_names_spec='${libname}${release}${shared_ext}$versuffix $libname${shared_ext}$versuffix' + need_version=yes + ;; + esac + shlibpath_var=LD_LIBRARY_PATH + case $host_os in + freebsd2*) + shlibpath_overrides_runpath=yes + ;; + freebsd3.[01]* | freebsdelf3.[01]*) + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + *) # from 3.2 on + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + esac + ;; + +gnu*) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + hardcode_into_libs=yes + ;; + +hpux9* | hpux10* | hpux11*) + # Give a soname corresponding to the major version so that dld.sl refuses to + # link against other versions. + version_type=sunos + need_lib_prefix=no + need_version=no + case "$host_cpu" in + ia64*) + shrext='.so' + hardcode_into_libs=yes + dynamic_linker="$host_os dld.so" + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + if test "X$HPUX_IA64_MODE" = X32; then + sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" + else + sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" + fi + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + ;; + hppa*64*) + shrext='.sl' + hardcode_into_libs=yes + dynamic_linker="$host_os dld.sl" + shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH + shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + ;; + *) + shrext='.sl' + dynamic_linker="$host_os dld.sl" + shlibpath_var=SHLIB_PATH + shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + ;; + esac + # HP-UX runs *really* slowly unless shared libraries are mode 555. + postinstall_cmds='chmod 555 $lib' + ;; + +irix5* | irix6* | nonstopux*) + case $host_os in + nonstopux*) version_type=nonstopux ;; + *) + if test "$lt_cv_prog_gnu_ld" = yes; then + version_type=linux + else + version_type=irix + fi ;; + esac + need_lib_prefix=no + need_version=no + soname_spec='${libname}${release}${shared_ext}$major' + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext} $libname${shared_ext}' + case $host_os in + irix5* | nonstopux*) + libsuff= shlibsuff= + ;; + *) + case $LD in # libtool.m4 will add one of these switches to LD + *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") + libsuff= shlibsuff= libmagic=32-bit;; + *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") + libsuff=32 shlibsuff=N32 libmagic=N32;; + *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") + libsuff=64 shlibsuff=64 libmagic=64-bit;; + *) libsuff= shlibsuff= libmagic=never-match;; + esac + ;; + esac + shlibpath_var=LD_LIBRARY${shlibsuff}_PATH + shlibpath_overrides_runpath=no + sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}" + sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}" + hardcode_into_libs=yes + ;; + +# No shared lib support for Linux oldld, aout, or coff. +linux*oldld* | linux*aout* | linux*coff*) + dynamic_linker=no + ;; + +# This must be Linux ELF. +linux*) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' + libsuff= + if test "x$LINUX_64_MODE" = x64; then + # Some platforms are per default 64-bit, so there's no /lib64 + if test -d /lib64 -a ! -h /lib64; then + libsuff=64 + fi + fi + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + sys_lib_dlsearch_path_spec="/lib${libsuff} /usr/lib${libsuff}" + sys_lib_search_path_spec="/lib${libsuff} /usr/lib${libsuff} /usr/local/lib${libsuff}" + # This implies no fast_install, which is unacceptable. + # Some rework will be needed to allow for fast_install + # before this can be enabled. + hardcode_into_libs=yes + + # We used to test for /lib/ld.so.1 and disable shared libraries on + # powerpc, because MkLinux only supported shared libraries with the + # GNU dynamic linker. Since this was broken with cross compilers, + # most powerpc-linux boxes support dynamic linking these days and + # people can always --disable-shared, the test was removed, and we + # assume the GNU/Linux dynamic linker is in use. + dynamic_linker='GNU/Linux ld.so' + ;; + +netbsd*) + version_type=sunos + need_lib_prefix=no + need_version=no + if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' + dynamic_linker='NetBSD (a.out) ld.so' + else + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext} ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + dynamic_linker='NetBSD ld.elf_so' + fi + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + +newsos6) + version_type=linux + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + ;; + +nto-qnx*) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + ;; + +openbsd*) + version_type=sunos + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' + shlibpath_var=LD_LIBRARY_PATH + if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + case $host_os in + openbsd2.[[89]] | openbsd2.[[89]].*) + shlibpath_overrides_runpath=no + ;; + *) + shlibpath_overrides_runpath=yes + ;; + esac + else + shlibpath_overrides_runpath=yes + fi + ;; + +os2*) + libname_spec='$name' + shrext=".dll" + need_lib_prefix=no + library_names_spec='$libname${shared_ext} $libname.a' + dynamic_linker='OS/2 ld.exe' + shlibpath_var=LIBPATH + ;; + +osf3* | osf4* | osf5*) + version_type=osf + need_lib_prefix=no + need_version=no + soname_spec='${libname}${release}${shared_ext}$major' + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" + sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec" + ;; + +sco3.2v5*) + version_type=osf + soname_spec='${libname}${release}${shared_ext}$major' + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + ;; + +solaris*) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + # ldd complains unless libraries are executable + postinstall_cmds='chmod +x $lib' + ;; + +sunos4*) + version_type=sunos + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' + finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + if test "$with_gnu_ld" = yes; then + need_lib_prefix=no + fi + need_version=yes + ;; + +sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) + version_type=linux + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + case $host_vendor in + sni) + shlibpath_overrides_runpath=no + need_lib_prefix=no + export_dynamic_flag_spec='${wl}-Blargedynsym' + runpath_var=LD_RUN_PATH + ;; + siemens) + need_lib_prefix=no + ;; + motorola) + need_lib_prefix=no + need_version=no + shlibpath_overrides_runpath=no + sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' + ;; + esac + ;; + +sysv4*MP*) + if test -d /usr/nec ;then + version_type=linux + library_names_spec='$libname${shared_ext}.$versuffix $libname${shared_ext}.$major $libname${shared_ext}' + soname_spec='$libname${shared_ext}.$major' + shlibpath_var=LD_LIBRARY_PATH + fi + ;; + +uts4*) + version_type=linux + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + ;; + +*) + dynamic_linker=no + ;; +esac +AC_MSG_RESULT([$dynamic_linker]) +test "$dynamic_linker" = no && can_build_shared=no +])# AC_LIBTOOL_SYS_DYNAMIC_LINKER + + +# _LT_AC_TAGCONFIG +# ---------------- +AC_DEFUN([_LT_AC_TAGCONFIG], +[AC_ARG_WITH([tags], + [AC_HELP_STRING([--with-tags@<:@=TAGS@:>@], + [include additional configurations @<:@automatic@:>@])], + [tagnames="$withval"]) + +if test -f "$ltmain" && test -n "$tagnames"; then + if test ! -f "${ofile}"; then + AC_MSG_WARN([output file `$ofile' does not exist]) + fi + + if test -z "$LTCC"; then + eval "`$SHELL ${ofile} --config | grep '^LTCC='`" + if test -z "$LTCC"; then + AC_MSG_WARN([output file `$ofile' does not look like a libtool script]) + else + AC_MSG_WARN([using `LTCC=$LTCC', extracted from `$ofile']) + fi + fi + + # Extract list of available tagged configurations in $ofile. + # Note that this assumes the entire list is on one line. + available_tags=`grep "^available_tags=" "${ofile}" | $SED -e 's/available_tags=\(.*$\)/\1/' -e 's/\"//g'` + + lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," + for tagname in $tagnames; do + IFS="$lt_save_ifs" + # Check whether tagname contains only valid characters + case `$echo "X$tagname" | $Xsed -e 's:[[-_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890,/]]::g'` in + "") ;; + *) AC_MSG_ERROR([invalid tag name: $tagname]) + ;; + esac + + if grep "^# ### BEGIN LIBTOOL TAG CONFIG: $tagname$" < "${ofile}" > /dev/null + then + AC_MSG_ERROR([tag name \"$tagname\" already exists]) + fi + + # Update the list of available tags. + if test -n "$tagname"; then + echo appending configuration tag \"$tagname\" to $ofile + + case $tagname in + CXX) + if test -n "$CXX" && test "X$CXX" != "Xno"; then + AC_LIBTOOL_LANG_CXX_CONFIG + else + tagname="" + fi + ;; + + F77) + if test -n "$F77" && test "X$F77" != "Xno"; then + AC_LIBTOOL_LANG_F77_CONFIG + else + tagname="" + fi + ;; + + GCJ) + if test -n "$GCJ" && test "X$GCJ" != "Xno"; then + AC_LIBTOOL_LANG_GCJ_CONFIG + else + tagname="" + fi + ;; + + RC) + AC_LIBTOOL_LANG_RC_CONFIG + ;; + + *) + AC_MSG_ERROR([Unsupported tag name: $tagname]) + ;; + esac + + # Append the new tag name to the list of available tags. + if test -n "$tagname" ; then + available_tags="$available_tags $tagname" + fi + fi + done + IFS="$lt_save_ifs" + + # Now substitute the updated list of available tags. + if eval "sed -e 's/^available_tags=.*\$/available_tags=\"$available_tags\"/' \"$ofile\" > \"${ofile}T\""; then + mv "${ofile}T" "$ofile" + chmod +x "$ofile" + else + rm -f "${ofile}T" + AC_MSG_ERROR([unable to update list of available tagged configurations.]) + fi +fi +])# _LT_AC_TAGCONFIG + + +# AC_LIBTOOL_DLOPEN +# ----------------- +# enable checks for dlopen support +AC_DEFUN([AC_LIBTOOL_DLOPEN], + [AC_BEFORE([$0],[AC_LIBTOOL_SETUP]) +])# AC_LIBTOOL_DLOPEN + + +# AC_LIBTOOL_WIN32_DLL +# -------------------- +# declare package support for building win32 dll's +AC_DEFUN([AC_LIBTOOL_WIN32_DLL], +[AC_BEFORE([$0], [AC_LIBTOOL_SETUP]) +])# AC_LIBTOOL_WIN32_DLL + + +# AC_ENABLE_SHARED([DEFAULT]) +# --------------------------- +# implement the --enable-shared flag +# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'. +AC_DEFUN([AC_ENABLE_SHARED], +[define([AC_ENABLE_SHARED_DEFAULT], ifelse($1, no, no, yes))dnl +AC_ARG_ENABLE([shared], + [AC_HELP_STRING([--enable-shared@<:@=PKGS@:>@], + [build shared libraries @<:@default=]AC_ENABLE_SHARED_DEFAULT[@:>@])], + [p=${PACKAGE-default} + case $enableval in + yes) enable_shared=yes ;; + no) enable_shared=no ;; + *) + enable_shared=no + # Look at the argument we got. We use all the common list separators. + lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," + for pkg in $enableval; do + IFS="$lt_save_ifs" + if test "X$pkg" = "X$p"; then + enable_shared=yes + fi + done + IFS="$lt_save_ifs" + ;; + esac], + [enable_shared=]AC_ENABLE_SHARED_DEFAULT) +])# AC_ENABLE_SHARED + + +# AC_DISABLE_SHARED +# ----------------- +#- set the default shared flag to --disable-shared +AC_DEFUN([AC_DISABLE_SHARED], +[AC_BEFORE([$0],[AC_LIBTOOL_SETUP])dnl +AC_ENABLE_SHARED(no) +])# AC_DISABLE_SHARED + + +# AC_ENABLE_STATIC([DEFAULT]) +# --------------------------- +# implement the --enable-static flag +# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'. +AC_DEFUN([AC_ENABLE_STATIC], +[define([AC_ENABLE_STATIC_DEFAULT], ifelse($1, no, no, yes))dnl +AC_ARG_ENABLE([static], + [AC_HELP_STRING([--enable-static@<:@=PKGS@:>@], + [build static libraries @<:@default=]AC_ENABLE_STATIC_DEFAULT[@:>@])], + [p=${PACKAGE-default} + case $enableval in + yes) enable_static=yes ;; + no) enable_static=no ;; + *) + enable_static=no + # Look at the argument we got. We use all the common list separators. + lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," + for pkg in $enableval; do + IFS="$lt_save_ifs" + if test "X$pkg" = "X$p"; then + enable_static=yes + fi + done + IFS="$lt_save_ifs" + ;; + esac], + [enable_static=]AC_ENABLE_STATIC_DEFAULT) +])# AC_ENABLE_STATIC + + +# AC_DISABLE_STATIC +# ----------------- +# set the default static flag to --disable-static +AC_DEFUN([AC_DISABLE_STATIC], +[AC_BEFORE([$0],[AC_LIBTOOL_SETUP])dnl +AC_ENABLE_STATIC(no) +])# AC_DISABLE_STATIC + + +# AC_ENABLE_FAST_INSTALL([DEFAULT]) +# --------------------------------- +# implement the --enable-fast-install flag +# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'. +AC_DEFUN([AC_ENABLE_FAST_INSTALL], +[define([AC_ENABLE_FAST_INSTALL_DEFAULT], ifelse($1, no, no, yes))dnl +AC_ARG_ENABLE([fast-install], + [AC_HELP_STRING([--enable-fast-install@<:@=PKGS@:>@], + [optimize for fast installation @<:@default=]AC_ENABLE_FAST_INSTALL_DEFAULT[@:>@])], + [p=${PACKAGE-default} + case $enableval in + yes) enable_fast_install=yes ;; + no) enable_fast_install=no ;; + *) + enable_fast_install=no + # Look at the argument we got. We use all the common list separators. + lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," + for pkg in $enableval; do + IFS="$lt_save_ifs" + if test "X$pkg" = "X$p"; then + enable_fast_install=yes + fi + done + IFS="$lt_save_ifs" + ;; + esac], + [enable_fast_install=]AC_ENABLE_FAST_INSTALL_DEFAULT) +])# AC_ENABLE_FAST_INSTALL + + +# AC_DISABLE_FAST_INSTALL +# ----------------------- +# set the default to --disable-fast-install +AC_DEFUN([AC_DISABLE_FAST_INSTALL], +[AC_BEFORE([$0],[AC_LIBTOOL_SETUP])dnl +AC_ENABLE_FAST_INSTALL(no) +])# AC_DISABLE_FAST_INSTALL + + +# AC_LIBTOOL_PICMODE([MODE]) +# -------------------------- +# implement the --with-pic flag +# MODE is either `yes' or `no'. If omitted, it defaults to `both'. +AC_DEFUN([AC_LIBTOOL_PICMODE], +[AC_BEFORE([$0],[AC_LIBTOOL_SETUP])dnl +pic_mode=ifelse($#,1,$1,default) +])# AC_LIBTOOL_PICMODE + + +# AC_PROG_EGREP +# ------------- +# This is predefined starting with Autoconf 2.54, so this conditional +# definition can be removed once we require Autoconf 2.54 or later. +m4_ifndef([AC_PROG_EGREP], [AC_DEFUN([AC_PROG_EGREP], +[AC_CACHE_CHECK([for egrep], [ac_cv_prog_egrep], + [if echo a | (grep -E '(a|b)') >/dev/null 2>&1 + then ac_cv_prog_egrep='grep -E' + else ac_cv_prog_egrep='egrep' + fi]) + EGREP=$ac_cv_prog_egrep + AC_SUBST([EGREP]) +])]) + + +# AC_PATH_TOOL_PREFIX +# ------------------- +# find a file program which can recognise shared library +AC_DEFUN([AC_PATH_TOOL_PREFIX], +[AC_REQUIRE([AC_PROG_EGREP])dnl +AC_MSG_CHECKING([for $1]) +AC_CACHE_VAL(lt_cv_path_MAGIC_CMD, +[case $MAGIC_CMD in +[[\\/*] | ?:[\\/]*]) + lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path. + ;; +*) + lt_save_MAGIC_CMD="$MAGIC_CMD" + lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR +dnl $ac_dummy forces splitting on constant user-supplied paths. +dnl POSIX.2 word splitting is done only on the output of word expansions, +dnl not every word. This closes a longstanding sh security hole. + ac_dummy="ifelse([$2], , $PATH, [$2])" + for ac_dir in $ac_dummy; do + IFS="$lt_save_ifs" + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$1; then + lt_cv_path_MAGIC_CMD="$ac_dir/$1" + if test -n "$file_magic_test_file"; then + case $deplibs_check_method in + "file_magic "*) + file_magic_regex="`expr \"$deplibs_check_method\" : \"file_magic \(.*\)\"`" + MAGIC_CMD="$lt_cv_path_MAGIC_CMD" + if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | + $EGREP "$file_magic_regex" > /dev/null; then + : + else + cat <&2 + +*** Warning: the command libtool uses to detect shared libraries, +*** $file_magic_cmd, produces output that libtool cannot recognize. +*** The result is that libtool may fail to recognize shared libraries +*** as such. This will affect the creation of libtool libraries that +*** depend on shared libraries, but programs linked with such libtool +*** libraries will work regardless of this problem. Nevertheless, you +*** may want to report the problem to your system manager and/or to +*** bug-libtool@gnu.org + +EOF + fi ;; + esac + fi + break + fi + done + IFS="$lt_save_ifs" + MAGIC_CMD="$lt_save_MAGIC_CMD" + ;; +esac]) +MAGIC_CMD="$lt_cv_path_MAGIC_CMD" +if test -n "$MAGIC_CMD"; then + AC_MSG_RESULT($MAGIC_CMD) +else + AC_MSG_RESULT(no) +fi +])# AC_PATH_TOOL_PREFIX + + +# AC_PATH_MAGIC +# ------------- +# find a file program which can recognise a shared library +AC_DEFUN([AC_PATH_MAGIC], +[AC_PATH_TOOL_PREFIX(${ac_tool_prefix}file, /usr/bin$PATH_SEPARATOR$PATH) +if test -z "$lt_cv_path_MAGIC_CMD"; then + if test -n "$ac_tool_prefix"; then + AC_PATH_TOOL_PREFIX(file, /usr/bin$PATH_SEPARATOR$PATH) + else + MAGIC_CMD=: + fi +fi +])# AC_PATH_MAGIC + + +# AC_PROG_LD +# ---------- +# find the pathname to the GNU or non-GNU linker +AC_DEFUN([AC_PROG_LD], +[AC_ARG_WITH([gnu-ld], + [AC_HELP_STRING([--with-gnu-ld], + [assume the C compiler uses GNU ld @<:@default=no@:>@])], + [test "$withval" = no || with_gnu_ld=yes], + [with_gnu_ld=no]) +AC_REQUIRE([LT_AC_PROG_SED])dnl +AC_REQUIRE([AC_PROG_CC])dnl +AC_REQUIRE([AC_CANONICAL_HOST])dnl +AC_REQUIRE([AC_CANONICAL_BUILD])dnl +ac_prog=ld +if test "$GCC" = yes; then + # Check if gcc -print-prog-name=ld gives a path. + AC_MSG_CHECKING([for ld used by $CC]) + case $host in + *-*-mingw*) + # gcc leaves a trailing carriage return which upsets mingw + ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; + *) + ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; + esac + case $ac_prog in + # Accept absolute paths. + [[\\/]]* | ?:[[\\/]]*) + re_direlt='/[[^/]][[^/]]*/\.\./' + # Canonicalize the pathname of ld + ac_prog=`echo $ac_prog| $SED 's%\\\\%/%g'` + while echo $ac_prog | grep "$re_direlt" > /dev/null 2>&1; do + ac_prog=`echo $ac_prog| $SED "s%$re_direlt%/%"` + done + test -z "$LD" && LD="$ac_prog" + ;; + "") + # If it fails, then pretend we aren't using GCC. + ac_prog=ld + ;; + *) + # If it is relative, then search for the first ld in PATH. + with_gnu_ld=unknown + ;; + esac +elif test "$with_gnu_ld" = yes; then + AC_MSG_CHECKING([for GNU ld]) +else + AC_MSG_CHECKING([for non-GNU ld]) +fi +AC_CACHE_VAL(lt_cv_path_LD, +[if test -z "$LD"; then + lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR + for ac_dir in $PATH; do + IFS="$lt_save_ifs" + test -z "$ac_dir" && ac_dir=. + if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then + lt_cv_path_LD="$ac_dir/$ac_prog" + # Check to see if the program is GNU ld. I'd rather use --version, + # but apparently some GNU ld's only accept -v. + # Break only if it was the GNU/non-GNU ld that we prefer. + case `"$lt_cv_path_LD" -v 2>&1 &1 /dev/null; then + case $host_cpu in + i*86 ) + # Not sure whether the presence of OpenBSD here was a mistake. + # Let's accept both of them until this is cleared up. + lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD)/i[[3-9]]86 (compact )?demand paged shared library' + lt_cv_file_magic_cmd=/usr/bin/file + lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*` + ;; + esac + else + lt_cv_deplibs_check_method=pass_all + fi + ;; + +gnu*) + lt_cv_deplibs_check_method=pass_all + ;; + +hpux10.20* | hpux11*) + lt_cv_file_magic_cmd=/usr/bin/file + case "$host_cpu" in + ia64*) + lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|ELF-[[0-9]][[0-9]]) shared object file - IA64' + lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so + ;; + hppa*64*) + [lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF-[0-9][0-9]) shared object file - PA-RISC [0-9].[0-9]'] + lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl + ;; + *) + lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|PA-RISC[[0-9]].[[0-9]]) shared library' + lt_cv_file_magic_test_file=/usr/lib/libc.sl + ;; + esac + ;; + +irix5* | irix6* | nonstopux*) + case $host_os in + irix5* | nonstopux*) + # this will be overridden with pass_all, but let us keep it just in case + lt_cv_deplibs_check_method="file_magic ELF 32-bit MSB dynamic lib MIPS - version 1" + ;; + *) + case $LD in + *-32|*"-32 ") libmagic=32-bit;; + *-n32|*"-n32 ") libmagic=N32;; + *-64|*"-64 ") libmagic=64-bit;; + *) libmagic=never-match;; + esac + # this will be overridden with pass_all, but let us keep it just in case + lt_cv_deplibs_check_method="file_magic ELF ${libmagic} MSB mips-[[1234]] dynamic lib MIPS - version 1" + ;; + esac + lt_cv_file_magic_test_file=`echo /lib${libsuff}/libc.so*` + lt_cv_deplibs_check_method=pass_all + ;; + +# This must be Linux ELF. +linux*) + case $host_cpu in + alpha* | hppa* | i*86 | ia64* | m68* | mips* | powerpc* | sparc* | s390* | sh* | x86_64* ) + lt_cv_deplibs_check_method=pass_all ;; + # the debian people say, arm and glibc 2.3.1 works for them with pass_all + arm* ) + lt_cv_deplibs_check_method=pass_all ;; + *) + # glibc up to 2.1.1 does not perform some relocations on ARM + lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB (shared object|dynamic lib )' ;; + esac + lt_cv_file_magic_test_file=`echo /lib/libc.so* /lib/libc-*.so` + ;; + +netbsd*) + if echo __ELF__ | $CC -E - | grep __ELF__ > /dev/null; then + lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$' + else + lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|_pic\.a)$' + fi + ;; + +newos6*) + lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (executable|dynamic lib)' + lt_cv_file_magic_cmd=/usr/bin/file + lt_cv_file_magic_test_file=/usr/lib/libnls.so + ;; + +nto-qnx*) + lt_cv_deplibs_check_method=unknown + ;; + +openbsd*) + lt_cv_file_magic_cmd=/usr/bin/file + lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*` + if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB shared object' + else + lt_cv_deplibs_check_method='file_magic OpenBSD.* shared library' + fi + ;; + +osf3* | osf4* | osf5*) + # this will be overridden with pass_all, but let us keep it just in case + lt_cv_deplibs_check_method='file_magic COFF format alpha shared library' + lt_cv_file_magic_test_file=/shlib/libc.so + lt_cv_deplibs_check_method=pass_all + ;; + +sco3.2v5*) + lt_cv_deplibs_check_method=pass_all + ;; + +solaris*) + lt_cv_deplibs_check_method=pass_all + lt_cv_file_magic_test_file=/lib/libc.so + ;; + +sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) + case $host_vendor in + motorola) + lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (shared object|dynamic lib) M[[0-9]][[0-9]]* Version [[0-9]]' + lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*` + ;; + ncr) + lt_cv_deplibs_check_method=pass_all + ;; + sequent) + lt_cv_file_magic_cmd='/bin/file' + lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB (shared object|dynamic lib )' + ;; + sni) + lt_cv_file_magic_cmd='/bin/file' + lt_cv_deplibs_check_method="file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB dynamic lib" + lt_cv_file_magic_test_file=/lib/libc.so + ;; + siemens) + lt_cv_deplibs_check_method=pass_all + ;; + esac + ;; + +sysv5OpenUNIX8* | sysv5UnixWare7* | sysv5uw[[78]]* | unixware7* | sysv4*uw2*) + lt_cv_deplibs_check_method=pass_all + ;; +esac +]) +file_magic_cmd=$lt_cv_file_magic_cmd +deplibs_check_method=$lt_cv_deplibs_check_method +test -z "$deplibs_check_method" && deplibs_check_method=unknown +])# AC_DEPLIBS_CHECK_METHOD + + +# AC_PROG_NM +# ---------- +# find the pathname to a BSD-compatible name lister +AC_DEFUN([AC_PROG_NM], +[AC_CACHE_CHECK([for BSD-compatible nm], lt_cv_path_NM, +[if test -n "$NM"; then + # Let the user override the test. + lt_cv_path_NM="$NM" +else + lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR + for ac_dir in $PATH /usr/ccs/bin /usr/ucb /bin; do + IFS="$lt_save_ifs" + test -z "$ac_dir" && ac_dir=. + tmp_nm="$ac_dir/${ac_tool_prefix}nm" + if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext" ; then + # Check to see if the nm accepts a BSD-compat flag. + # Adding the `sed 1q' prevents false positives on HP-UX, which says: + # nm: unknown option "B" ignored + # Tru64's nm complains that /dev/null is an invalid object file + case `"$tmp_nm" -B /dev/null 2>&1 | sed '1q'` in + */dev/null* | *'Invalid file or object type'*) + lt_cv_path_NM="$tmp_nm -B" + break + ;; + *) + case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in + */dev/null*) + lt_cv_path_NM="$tmp_nm -p" + break + ;; + *) + lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but + continue # so that we can try to find one that supports BSD flags + ;; + esac + esac + fi + done + IFS="$lt_save_ifs" + test -z "$lt_cv_path_NM" && lt_cv_path_NM=nm +fi]) +NM="$lt_cv_path_NM" +])# AC_PROG_NM + + +# AC_CHECK_LIBM +# ------------- +# check for math library +AC_DEFUN([AC_CHECK_LIBM], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +LIBM= +case $host in +*-*-beos* | *-*-cygwin* | *-*-pw32* | *-*-darwin*) + # These system don't have libm, or don't need it + ;; +*-ncr-sysv4.3*) + AC_CHECK_LIB(mw, _mwvalidcheckl, LIBM="-lmw") + AC_CHECK_LIB(m, cos, LIBM="$LIBM -lm") + ;; +*) + AC_CHECK_LIB(m, cos, LIBM="-lm") + ;; +esac +])# AC_CHECK_LIBM + + +# AC_LIBLTDL_CONVENIENCE([DIRECTORY]) +# ----------------------------------- +# sets LIBLTDL to the link flags for the libltdl convenience library and +# LTDLINCL to the include flags for the libltdl header and adds +# --enable-ltdl-convenience to the configure arguments. Note that LIBLTDL +# and LTDLINCL are not AC_SUBSTed, nor is AC_CONFIG_SUBDIRS called. If +# DIRECTORY is not provided, it is assumed to be `libltdl'. LIBLTDL will +# be prefixed with '${top_builddir}/' and LTDLINCL will be prefixed with +# '${top_srcdir}/' (note the single quotes!). If your package is not +# flat and you're not using automake, define top_builddir and +# top_srcdir appropriately in the Makefiles. +AC_DEFUN([AC_LIBLTDL_CONVENIENCE], +[AC_BEFORE([$0],[AC_LIBTOOL_SETUP])dnl + case $enable_ltdl_convenience in + no) AC_MSG_ERROR([this package needs a convenience libltdl]) ;; + "") enable_ltdl_convenience=yes + ac_configure_args="$ac_configure_args --enable-ltdl-convenience" ;; + esac + LIBLTDL='${top_builddir}/'ifelse($#,1,[$1],['libltdl'])/libltdlc.la + LTDLINCL='-I${top_srcdir}/'ifelse($#,1,[$1],['libltdl']) + # For backwards non-gettext consistent compatibility... + INCLTDL="$LTDLINCL" +])# AC_LIBLTDL_CONVENIENCE + + +# AC_LIBLTDL_INSTALLABLE([DIRECTORY]) +# ----------------------------------- +# sets LIBLTDL to the link flags for the libltdl installable library and +# LTDLINCL to the include flags for the libltdl header and adds +# --enable-ltdl-install to the configure arguments. Note that LIBLTDL +# and LTDLINCL are not AC_SUBSTed, nor is AC_CONFIG_SUBDIRS called. If +# DIRECTORY is not provided and an installed libltdl is not found, it is +# assumed to be `libltdl'. LIBLTDL will be prefixed with '${top_builddir}/' +# and LTDLINCL will be prefixed with '${top_srcdir}/' (note the single +# quotes!). If your package is not flat and you're not using automake, +# define top_builddir and top_srcdir appropriately in the Makefiles. +# In the future, this macro may have to be called after AC_PROG_LIBTOOL. +AC_DEFUN([AC_LIBLTDL_INSTALLABLE], +[AC_BEFORE([$0],[AC_LIBTOOL_SETUP])dnl + AC_CHECK_LIB(ltdl, lt_dlinit, + [test x"$enable_ltdl_install" != xyes && enable_ltdl_install=no], + [if test x"$enable_ltdl_install" = xno; then + AC_MSG_WARN([libltdl not installed, but installation disabled]) + else + enable_ltdl_install=yes + fi + ]) + if test x"$enable_ltdl_install" = x"yes"; then + ac_configure_args="$ac_configure_args --enable-ltdl-install" + LIBLTDL='${top_builddir}/'ifelse($#,1,[$1],['libltdl'])/libltdl.la + LTDLINCL='-I${top_srcdir}/'ifelse($#,1,[$1],['libltdl']) + else + ac_configure_args="$ac_configure_args --enable-ltdl-install=no" + LIBLTDL="-lltdl" + LTDLINCL= + fi + # For backwards non-gettext consistent compatibility... + INCLTDL="$LTDLINCL" +])# AC_LIBLTDL_INSTALLABLE + + +# AC_LIBTOOL_CXX +# -------------- +# enable support for C++ libraries +AC_DEFUN([AC_LIBTOOL_CXX], +[AC_REQUIRE([_LT_AC_LANG_CXX]) +])# AC_LIBTOOL_CXX + + +# _LT_AC_LANG_CXX +# --------------- +AC_DEFUN([_LT_AC_LANG_CXX], +[AC_REQUIRE([AC_PROG_CXX]) +AC_REQUIRE([AC_PROG_CXXCPP]) +_LT_AC_SHELL_INIT([tagnames=${tagnames+${tagnames},}CXX]) +])# _LT_AC_LANG_CXX + + +# AC_LIBTOOL_F77 +# -------------- +# enable support for Fortran 77 libraries +AC_DEFUN([AC_LIBTOOL_F77], +[AC_REQUIRE([_LT_AC_LANG_F77]) +])# AC_LIBTOOL_F77 + + +# _LT_AC_LANG_F77 +# --------------- +AC_DEFUN([_LT_AC_LANG_F77], +[AC_REQUIRE([AC_PROG_F77]) +_LT_AC_SHELL_INIT([tagnames=${tagnames+${tagnames},}F77]) +])# _LT_AC_LANG_F77 + + +# AC_LIBTOOL_GCJ +# -------------- +# enable support for GCJ libraries +AC_DEFUN([AC_LIBTOOL_GCJ], +[AC_REQUIRE([_LT_AC_LANG_GCJ]) +])# AC_LIBTOOL_GCJ + + +# _LT_AC_LANG_GCJ +# --------------- +AC_DEFUN([_LT_AC_LANG_GCJ], +[AC_PROVIDE_IFELSE([AC_PROG_GCJ],[], + [AC_PROVIDE_IFELSE([A][M_PROG_GCJ],[], + [AC_PROVIDE_IFELSE([LT_AC_PROG_GCJ],[], + [ifdef([AC_PROG_GCJ],[AC_REQUIRE([AC_PROG_GCJ])], + [ifdef([A][M_PROG_GCJ],[AC_REQUIRE([A][M_PROG_GCJ])], + [AC_REQUIRE([A][C_PROG_GCJ_OR_A][M_PROG_GCJ])])])])])]) +_LT_AC_SHELL_INIT([tagnames=${tagnames+${tagnames},}GCJ]) +])# _LT_AC_LANG_GCJ + + +# AC_LIBTOOL_RC +# -------------- +# enable support for Windows resource files +AC_DEFUN([AC_LIBTOOL_RC], +[AC_REQUIRE([LT_AC_PROG_RC]) +_LT_AC_SHELL_INIT([tagnames=${tagnames+${tagnames},}RC]) +])# AC_LIBTOOL_RC + + +# AC_LIBTOOL_LANG_C_CONFIG +# ------------------------ +# Ensure that the configuration vars for the C compiler are +# suitably defined. Those variables are subsequently used by +# AC_LIBTOOL_CONFIG to write the compiler configuration to `libtool'. +AC_DEFUN([AC_LIBTOOL_LANG_C_CONFIG], [_LT_AC_LANG_C_CONFIG]) +AC_DEFUN([_LT_AC_LANG_C_CONFIG], +[lt_save_CC="$CC" +AC_LANG_PUSH(C) + +# Source file extension for C test sources. +ac_ext=c + +# Object file extension for compiled C test sources. +objext=o +_LT_AC_TAGVAR(objext, $1)=$objext + +# Code to be used in simple compile tests +lt_simple_compile_test_code="int some_variable = 0;\n" + +# Code to be used in simple link tests +lt_simple_link_test_code='int main(){return(0);}\n' + +_LT_AC_SYS_COMPILER + +# +# Check for any special shared library compilation flags. +# +_LT_AC_TAGVAR(lt_prog_cc_shlib, $1)= +if test "$GCC" = no; then + case $host_os in + sco3.2v5*) + _LT_AC_TAGVAR(lt_prog_cc_shlib, $1)='-belf' + ;; + esac +fi +if test -n "$_LT_AC_TAGVAR(lt_prog_cc_shlib, $1)"; then + AC_MSG_WARN([`$CC' requires `$_LT_AC_TAGVAR(lt_prog_cc_shlib, $1)' to build shared libraries]) + if echo "$old_CC $old_CFLAGS " | grep "[[ ]]$]_LT_AC_TAGVAR(lt_prog_cc_shlib, $1)[[[ ]]" >/dev/null; then : + else + AC_MSG_WARN([add `$_LT_AC_TAGVAR(lt_prog_cc_shlib, $1)' to the CC or CFLAGS env variable and reconfigure]) + _LT_AC_TAGVAR(lt_cv_prog_cc_can_build_shared, $1)=no + fi +fi + + +# +# Check to make sure the static flag actually works. +# +AC_LIBTOOL_LINKER_OPTION([if $compiler static flag $_LT_AC_TAGVAR(lt_prog_compiler_static, $1) works], + _LT_AC_TAGVAR(lt_prog_compiler_static_works, $1), + $_LT_AC_TAGVAR(lt_prog_compiler_static, $1), + [], + [_LT_AC_TAGVAR(lt_prog_compiler_static, $1)=]) + + +## CAVEAT EMPTOR: +## There is no encapsulation within the following macros, do not change +## the running order or otherwise move them around unless you know exactly +## what you are doing... +AC_LIBTOOL_PROG_COMPILER_NO_RTTI($1) +AC_LIBTOOL_PROG_COMPILER_PIC($1) +AC_LIBTOOL_PROG_CC_C_O($1) +AC_LIBTOOL_SYS_HARD_LINK_LOCKS($1) +AC_LIBTOOL_PROG_LD_SHLIBS($1) +AC_LIBTOOL_SYS_DYNAMIC_LINKER($1) +AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH($1) +AC_LIBTOOL_SYS_LIB_STRIP +AC_LIBTOOL_DLOPEN_SELF($1) + +# Report which librarie types wil actually be built +AC_MSG_CHECKING([if libtool supports shared libraries]) +AC_MSG_RESULT([$can_build_shared]) + +AC_MSG_CHECKING([whether to build shared libraries]) +test "$can_build_shared" = "no" && enable_shared=no + +# On AIX, shared libraries and static libraries use the same namespace, and +# are all built from PIC. +case "$host_os" in +aix3*) + test "$enable_shared" = yes && enable_static=no + if test -n "$RANLIB"; then + archive_cmds="$archive_cmds~\$RANLIB \$lib" + postinstall_cmds='$RANLIB $lib' + fi + ;; + +aix4*) + if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then + test "$enable_shared" = yes && enable_static=no + fi + ;; + darwin* | rhapsody*) + if test "$GCC" = yes; then + _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=no + case "$host_os" in + rhapsody* | darwin1.[[012]]) + _LT_AC_TAGVAR(allow_undefined_flag, $1)='-Wl,-undefined -Wl,suppress' + ;; + *) # Darwin 1.3 on + if test -z ${MACOSX_DEPLOYMENT_TARGET} ; then + _LT_AC_TAGVAR(allow_undefined_flag, $1)='-Wl,-flat_namespace -Wl,-undefined -Wl,suppress' + else + case ${MACOSX_DEPLOYMENT_TARGET} in + 10.[012]) + _LT_AC_TAGVAR(allow_undefined_flag, $1)='-Wl,-flat_namespace -Wl,-undefined -Wl,suppress' + ;; + 10.*) + _LT_AC_TAGVAR(allow_undefined_flag, $1)='-Wl,-undefined -Wl,dynamic_lookup' + ;; + esac + fi + ;; + esac + output_verbose_link_cmd='echo' + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -dynamiclib $allow_undefined_flag -o $lib $compiler_flags $libobjs $deplibs -install_name $rpath/$soname $verstring' + _LT_AC_TAGVAR(module_cmds, $1)='$CC $allow_undefined_flag -o $lib -bundle $compiler_flags $libobjs $deplibs' + # Don't fix this by using the ld -exported_symbols_list flag, it doesn't exist in older darwin ld's + _LT_AC_TAGVAR(archive_expsym_cmds, $1)='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC -dynamiclib $allow_undefined_flag -o $lib $compiler_flags $libobjs $deplibs -install_name $rpath/$soname $verstring~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' + _LT_AC_TAGVAR(module_expsym_cmds, $1)='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC $allow_undefined_flag -o $lib -bundle $compiler_flags $libobjs $deplibs~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' + _LT_AC_TAGVAR(hardcode_direct, $1)=no + _LT_AC_TAGVAR(hardcode_automatic, $1)=yes + _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=unsupported + _LT_AC_TAGVAR(whole_archive_flag_spec, $1)='-all_load $convenience' + _LT_AC_TAGVAR(link_all_deplibs, $1)=yes + else + _LT_AC_TAGVAR(ld_shlibs, $1)=no + fi + ;; +esac +AC_MSG_RESULT([$enable_shared]) + +AC_MSG_CHECKING([whether to build static libraries]) +# Make sure either enable_shared or enable_static is yes. +test "$enable_shared" = yes || enable_static=yes +AC_MSG_RESULT([$enable_static]) + +AC_LIBTOOL_CONFIG($1) + +AC_LANG_POP +CC="$lt_save_CC" +])# AC_LIBTOOL_LANG_C_CONFIG + + +# AC_LIBTOOL_LANG_CXX_CONFIG +# -------------------------- +# Ensure that the configuration vars for the C compiler are +# suitably defined. Those variables are subsequently used by +# AC_LIBTOOL_CONFIG to write the compiler configuration to `libtool'. +AC_DEFUN([AC_LIBTOOL_LANG_CXX_CONFIG], [_LT_AC_LANG_CXX_CONFIG(CXX)]) +AC_DEFUN([_LT_AC_LANG_CXX_CONFIG], +[AC_LANG_PUSH(C++) +AC_REQUIRE([AC_PROG_CXX]) +AC_REQUIRE([AC_PROG_CXXCPP]) + +_LT_AC_TAGVAR(archive_cmds_need_lc, $1)=no +_LT_AC_TAGVAR(allow_undefined_flag, $1)= +_LT_AC_TAGVAR(always_export_symbols, $1)=no +_LT_AC_TAGVAR(archive_expsym_cmds, $1)= +_LT_AC_TAGVAR(export_dynamic_flag_spec, $1)= +_LT_AC_TAGVAR(hardcode_direct, $1)=no +_LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)= +_LT_AC_TAGVAR(hardcode_libdir_flag_spec_ld, $1)= +_LT_AC_TAGVAR(hardcode_libdir_separator, $1)= +_LT_AC_TAGVAR(hardcode_minus_L, $1)=no +_LT_AC_TAGVAR(hardcode_automatic, $1)=no +_LT_AC_TAGVAR(module_cmds, $1)= +_LT_AC_TAGVAR(module_expsym_cmds, $1)= +_LT_AC_TAGVAR(link_all_deplibs, $1)=unknown +_LT_AC_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds +_LT_AC_TAGVAR(no_undefined_flag, $1)= +_LT_AC_TAGVAR(whole_archive_flag_spec, $1)= +_LT_AC_TAGVAR(enable_shared_with_static_runtimes, $1)=no + +# Dependencies to place before and after the object being linked: +_LT_AC_TAGVAR(predep_objects, $1)= +_LT_AC_TAGVAR(postdep_objects, $1)= +_LT_AC_TAGVAR(predeps, $1)= +_LT_AC_TAGVAR(postdeps, $1)= +_LT_AC_TAGVAR(compiler_lib_search_path, $1)= + +# Source file extension for C++ test sources. +ac_ext=cc + +# Object file extension for compiled C++ test sources. +objext=o +_LT_AC_TAGVAR(objext, $1)=$objext + +# Code to be used in simple compile tests +lt_simple_compile_test_code="int some_variable = 0;\n" + +# Code to be used in simple link tests +lt_simple_link_test_code='int main(int, char *[]) { return(0); }\n' + +# ltmain only uses $CC for tagged configurations so make sure $CC is set. +_LT_AC_SYS_COMPILER + +# Allow CC to be a program name with arguments. +lt_save_CC=$CC +lt_save_LD=$LD +lt_save_GCC=$GCC +GCC=$GXX +lt_save_with_gnu_ld=$with_gnu_ld +lt_save_path_LD=$lt_cv_path_LD +if test -n "${lt_cv_prog_gnu_ldcxx+set}"; then + lt_cv_prog_gnu_ld=$lt_cv_prog_gnu_ldcxx +else + unset lt_cv_prog_gnu_ld +fi +if test -n "${lt_cv_path_LDCXX+set}"; then + lt_cv_path_LD=$lt_cv_path_LDCXX +else + unset lt_cv_path_LD +fi +test -z "${LDCXX+set}" || LD=$LDCXX +CC=${CXX-"c++"} +compiler=$CC +_LT_AC_TAGVAR(compiler, $1)=$CC +cc_basename=`$echo X"$compiler" | $Xsed -e 's%^.*/%%'` + +# We don't want -fno-exception wen compiling C++ code, so set the +# no_builtin_flag separately +if test "$GXX" = yes; then + _LT_AC_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin' +else + _LT_AC_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)= +fi + +if test "$GXX" = yes; then + # Set up default GNU C++ configuration + + AC_PROG_LD + + # Check if GNU C++ uses GNU ld as the underlying linker, since the + # archiving commands below assume that GNU ld is being used. + if test "$with_gnu_ld" = yes; then + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $compiler_flags $predep_objects $libobjs $deplibs $postdep_objects ${wl}-soname $wl$soname -o $lib' + _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$CC -shared -nostdlib $compiler_flags $predep_objects $libobjs $deplibs $postdep_objects ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}--rpath ${wl}$libdir' + _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' + + # If archive_cmds runs LD, not CC, wlarc should be empty + # XXX I think wlarc can be eliminated in ltcf-cxx, but I need to + # investigate it a little bit more. (MM) + wlarc='${wl}' + + # ancient GNU ld didn't support --whole-archive et. al. + if eval "`$CC -print-prog-name=ld` --help 2>&1" | \ + grep 'no-whole-archive' > /dev/null; then + _LT_AC_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' + else + _LT_AC_TAGVAR(whole_archive_flag_spec, $1)= + fi + else + with_gnu_ld=no + wlarc= + + # A generic and very simple default shared library creation + # command for GNU C++ for the case where it uses the native + # linker, instead of GNU ld. If possible, this setting should + # overridden to take advantage of the native linker features on + # the platform it is being used on. + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $compiler_flags $predep_objects $libobjs $deplibs $postdep_objects -o $lib' + fi + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep "\-L"' + +else + GXX=no + with_gnu_ld=no + wlarc= +fi + +# PORTME: fill in a description of your system's C++ link characteristics +AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries]) +_LT_AC_TAGVAR(ld_shlibs, $1)=yes +case $host_os in + aix3*) + # FIXME: insert proper C++ library support + _LT_AC_TAGVAR(ld_shlibs, $1)=no + ;; + aix4* | aix5*) + if test "$host_cpu" = ia64; then + # On IA64, the linker does run time linking by default, so we don't + # have to do anything special. + aix_use_runtimelinking=no + exp_sym_flag='-Bexport' + no_entry_flag="" + else + # KDE requires run time linking. Make it the default. + aix_use_runtimelinking=yes + exp_sym_flag='-bexport' + no_entry_flag='-bnoentry' + fi + + # When large executables or shared objects are built, AIX ld can + # have problems creating the table of contents. If linking a library + # or program results in "error TOC overflow" add -mminimal-toc to + # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not + # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. + + _LT_AC_TAGVAR(archive_cmds, $1)='' + _LT_AC_TAGVAR(hardcode_direct, $1)=yes + _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=':' + _LT_AC_TAGVAR(link_all_deplibs, $1)=yes + + if test "$GXX" = yes; then + case $host_os in aix4.[012]|aix4.[012].*) + # We only want to do this on AIX 4.2 and lower, the check + # below for broken collect2 doesn't work under 4.3+ + collect2name=`${CC} -print-prog-name=collect2` + if test -f "$collect2name" && \ + strings "$collect2name" | grep resolve_lib_name >/dev/null + then + # We have reworked collect2 + _LT_AC_TAGVAR(hardcode_direct, $1)=yes + else + # We have old collect2 + _LT_AC_TAGVAR(hardcode_direct, $1)=unsupported + # It fails to find uninstalled libraries when the uninstalled + # path is not listed in the libpath. Setting hardcode_minus_L + # to unsupported forces relinking + _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_AC_TAGVAR(hardcode_libdir_separator, $1)= + fi + esac + shared_flag='-shared' + else + # not using gcc + if test "$host_cpu" = ia64; then + # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release + # chokes on -Wl,-G. The following line is correct: + shared_flag='-G' + else + if test "$aix_use_runtimelinking" = yes; then + shared_flag='-qmkshrobj ${wl}-G' + else + shared_flag='-qmkshrobj' + fi + fi + fi + + # Let the compiler handle the export list. + _LT_AC_TAGVAR(always_export_symbols, $1)=no + if test "$aix_use_runtimelinking" = yes; then + # Warning - without using the other runtime loading flags (-brtl), + # -berok will link without error, but may produce a broken library. + _LT_AC_TAGVAR(allow_undefined_flag, $1)='-berok' + # Determine the default libpath from the value encoded in an empty executable. + _LT_AC_SYS_LIBPATH_AIX + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" + + _LT_AC_TAGVAR(archive_cmds, $1)="\$CC"' -o $output_objdir/$soname $compiler_flags $libobjs $deplibs `if test "x${allow_undefined_flag}" != "x"; then echo "${wl}${allow_undefined_flag}"; else :; fi` '" $shared_flag" + _LT_AC_TAGVAR(archive_expsym_cmds, $1)="\$CC"' -o $output_objdir/$soname $compiler_flags $libobjs $deplibs `if test "x${allow_undefined_flag}" != "x"; then echo "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag" + else + if test "$host_cpu" = ia64; then + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $libdir:/usr/lib:/lib' + _LT_AC_TAGVAR(allow_undefined_flag, $1)="-z nodefs" + _LT_AC_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $compiler_flags $libobjs $deplibs ${wl}${allow_undefined_flag} '"\${wl}$no_entry_flag \${wl}$exp_sym_flag:\$export_symbols" + else + # Determine the default libpath from the value encoded in an empty executable. + _LT_AC_SYS_LIBPATH_AIX + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" + # Warning - without using the other run time loading flags, + # -berok will link without error, but may produce a broken library. + _LT_AC_TAGVAR(no_undefined_flag, $1)=' ${wl}-bernotok' + _LT_AC_TAGVAR(allow_undefined_flag, $1)=' ${wl}-berok' + # -bexpall does not export symbols beginning with underscore (_) + _LT_AC_TAGVAR(always_export_symbols, $1)=yes + # Exported symbols can be pulled into shared objects from archives + _LT_AC_TAGVAR(whole_archive_flag_spec, $1)=' ' + _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=yes + # This is similar to how AIX traditionally builds it's shared libraries. + _LT_AC_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $compiler_flags $libobjs $deplibs ${wl}-bE:$export_symbols ${wl}-bnoentry${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' + fi + fi + ;; + chorus*) + case $cc_basename in + *) + # FIXME: insert proper C++ library support + _LT_AC_TAGVAR(ld_shlibs, $1)=no + ;; + esac + ;; + + cygwin* | mingw* | pw32*) + # _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless, + # as there is no search path for DLLs. + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_AC_TAGVAR(allow_undefined_flag, $1)=no + _LT_AC_TAGVAR(always_export_symbols, $1)=no + _LT_AC_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + + if $LD --help 2>&1 | grep 'auto-import' > /dev/null; then + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $compiler_flags $predep_objects $libobjs $deplibs $postdep_objects -o $output_objdir/$soname ${wl}--image-base=0x10000000 ${wl}--out-implib,$lib' + # If the export-symbols file already is a .def file (1st line + # is EXPORTS), use it as is; otherwise, prepend... + _LT_AC_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then + cp $export_symbols $output_objdir/$soname.def; + else + echo EXPORTS > $output_objdir/$soname.def; + cat $export_symbols >> $output_objdir/$soname.def; + fi~ + $CC -shared -nostdlib $output_objdir/$soname.def $compiler_flags $predep_objects $libobjs $deplibs $postdep_objects -o $output_objdir/$soname ${wl}--image-base=0x10000000 ${wl}--out-implib,$lib' + else + _LT_AC_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + darwin* | rhapsody*) + if test "$GXX" = yes; then + _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=no + case "$host_os" in + rhapsody* | darwin1.[[012]]) + _LT_AC_TAGVAR(allow_undefined_flag, $1)='-Wl,-undefined -Wl,suppress' + ;; + *) # Darwin 1.3 on + if test -z ${MACOSX_DEPLOYMENT_TARGET} ; then + _LT_AC_TAGVAR(allow_undefined_flag, $1)='-Wl,-flat_namespace -Wl,-undefined -Wl,suppress' + else + case ${MACOSX_DEPLOYMENT_TARGET} in + 10.[012]) + _LT_AC_TAGVAR(allow_undefined_flag, $1)='-Wl,-flat_namespace -Wl,-undefined -Wl,suppress' + ;; + 10.*) + _LT_AC_TAGVAR(allow_undefined_flag, $1)='-Wl,-undefined -Wl,dynamic_lookup' + ;; + esac + fi + ;; + esac + lt_int_apple_cc_single_mod=no + output_verbose_link_cmd='echo' + if $CC -dumpspecs 2>&1 | grep 'single_module' >/dev/null ; then + lt_int_apple_cc_single_mod=yes + fi + if test "X$lt_int_apple_cc_single_mod" = Xyes ; then + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -dynamiclib -single_module $allow_undefined_flag -o $lib $compiler_flags $libobjs $deplibs -install_name $rpath/$soname $verstring' + else + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -r ${wl}-bind_at_load -keep_private_externs -nostdlib -o ${lib}-master.o $libobjs~$CC -dynamiclib $allow_undefined_flag -o $lib ${lib}-master.o $compiler_flags $deplibs -install_name $rpath/$soname $verstring' + fi + _LT_AC_TAGVAR(module_cmds, $1)='$CC ${wl}-bind_at_load $allow_undefined_flag -o $lib -bundle $compiler_flags $libobjs $deplibs' + + # Don't fix this by using the ld -exported_symbols_list flag, it doesn't exist in older darwin ld's + if test "X$lt_int_apple_cc_single_mod" = Xyes ; then + _LT_AC_TAGVAR(archive_expsym_cmds, $1)='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC -dynamiclib -single_module $allow_undefined_flag -o $lib $compiler_flags $libobjs $deplibs -install_name $rpath/$soname $verstring~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' + else + _LT_AC_TAGVAR(archive_expsym_cmds, $1)='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC -r ${wl}-bind_at_load -keep_private_externs -nostdlib -o ${lib}-master.o $libobjs~$CC -dynamiclib $allow_undefined_flag -o $lib ${lib}-master.o $compiler_flags $deplibs -install_name $rpath/$soname $verstring~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' + fi + _LT_AC_TAGVAR(module_expsym_cmds, $1)='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC $allow_undefined_flag -o $lib -bundle $compiler_flags $libobjs $deplibs~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' + _LT_AC_TAGVAR(hardcode_direct, $1)=no + _LT_AC_TAGVAR(hardcode_automatic, $1)=yes + _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=unsupported + _LT_AC_TAGVAR(whole_archive_flag_spec, $1)='-all_load $convenience' + _LT_AC_TAGVAR(link_all_deplibs, $1)=yes + else + _LT_AC_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + dgux*) + case $cc_basename in + ec++) + # FIXME: insert proper C++ library support + _LT_AC_TAGVAR(ld_shlibs, $1)=no + ;; + ghcx) + # Green Hills C++ Compiler + # FIXME: insert proper C++ library support + _LT_AC_TAGVAR(ld_shlibs, $1)=no + ;; + *) + # FIXME: insert proper C++ library support + _LT_AC_TAGVAR(ld_shlibs, $1)=no + ;; + esac + ;; + freebsd[12]*) + # C++ shared libraries reported to be fairly broken before switch to ELF + _LT_AC_TAGVAR(ld_shlibs, $1)=no + ;; + freebsd-elf*) + _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=no + ;; + freebsd* | kfreebsd*-gnu) + # FreeBSD 3 and later use GNU C++ and GNU ld with standard ELF + # conventions + _LT_AC_TAGVAR(ld_shlibs, $1)=yes + ;; + gnu*) + ;; + hpux9*) + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' + _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + _LT_AC_TAGVAR(hardcode_direct, $1)=yes + _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH, + # but as the default + # location of the library. + + case $cc_basename in + CC) + # FIXME: insert proper C++ library support + _LT_AC_TAGVAR(ld_shlibs, $1)=no + ;; + aCC) + _LT_AC_TAGVAR(archive_cmds, $1)='$rm $output_objdir/$soname~$CC -b ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $compiler_flags $predep_objects $libobjs $deplibs $postdep_objects~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | egrep "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; echo $list' + ;; + *) + if test "$GXX" = yes; then + _LT_AC_TAGVAR(archive_cmds, $1)='$rm $output_objdir/$soname~$CC -shared -nostdlib -fPIC ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $compiler_flags $predep_objects $libobjs $deplibs $postdep_objects~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' + else + # FIXME: insert proper C++ library support + _LT_AC_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + ;; + hpux10*|hpux11*) + if test $with_gnu_ld = no; then + case "$host_cpu" in + hppa*64*) + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' + _LT_AC_TAGVAR(hardcode_libdir_flag_spec_ld, $1)='+b $libdir' + _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: + ;; + ia64*) + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + ;; + *) + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' + _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + ;; + esac + fi + case "$host_cpu" in + hppa*64*) + _LT_AC_TAGVAR(hardcode_direct, $1)=no + _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + ia64*) + _LT_AC_TAGVAR(hardcode_direct, $1)=no + _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH, + # but as the default + # location of the library. + ;; + *) + _LT_AC_TAGVAR(hardcode_direct, $1)=yes + _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH, + # but as the default + # location of the library. + ;; + esac + + case $cc_basename in + CC) + # FIXME: insert proper C++ library support + _LT_AC_TAGVAR(ld_shlibs, $1)=no + ;; + aCC) + case "$host_cpu" in + hppa*64*|ia64*) + _LT_AC_TAGVAR(archive_cmds, $1)='$LD -b +h $soname -o $lib $linker_flags $libobjs $deplibs' + ;; + *) + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $compiler_flags $predep_objects $libobjs $deplibs $postdep_objects' + ;; + esac + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | grep "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; echo $list' + ;; + *) + if test "$GXX" = yes; then + if test $with_gnu_ld = no; then + case "$host_cpu" in + ia64*|hppa*64*) + _LT_AC_TAGVAR(archive_cmds, $1)='$LD -b +h $soname -o $lib $linker_flags $libobjs $deplibs' + ;; + *) + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $compiler_flags $predep_objects $libobjs $deplibs $postdep_objects' + ;; + esac + fi + else + # FIXME: insert proper C++ library support + _LT_AC_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + ;; + irix5* | irix6*) + case $cc_basename in + CC) + # SGI C++ + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -all -multigot $compiler_flags $predep_objects $libobjs $deplibs $postdep_objects -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${objdir}/so_locations -o $lib' + + # Archives containing C++ object files must be created using + # "CC -ar", where "CC" is the IRIX C++ compiler. This is + # necessary to make sure instantiated templates are included + # in the archive. + _LT_AC_TAGVAR(old_archive_cmds, $1)='$CC -ar -WR,-u -o $oldlib $oldobjs' + ;; + *) + if test "$GXX" = yes; then + if test "$with_gnu_ld" = no; then + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $compiler_flags $predep_objects $libobjs $deplibs $postdep_objects ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${objdir}/so_locations -o $lib' + else + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $compiler_flags $predep_objects $libobjs $deplibs $postdep_objects ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` -o $lib' + fi + fi + _LT_AC_TAGVAR(link_all_deplibs, $1)=yes + ;; + esac + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: + ;; + linux*) + case $cc_basename in + KCC) + # Kuck and Associates, Inc. (KAI) C++ Compiler + + # KCC will only create a shared library if the output file + # ends with ".so" (or ".sl" for HP-UX), so rename the library + # to its proper name (with version) after linking. + _LT_AC_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $compiler_flags $predep_objects $libobjs $deplibs $postdep_objects --soname $soname -o \$templib; mv \$templib $lib' + _LT_AC_TAGVAR(archive_expsym_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $compiler_flags $predep_objects $libobjs $deplibs $postdep_objects --soname $soname -o \$templib ${wl}-retain-symbols-file,$export_symbols; mv \$templib $lib' + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`$CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 | grep "ld"`; rm -f libconftest$shared_ext; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; echo $list' + + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}--rpath,$libdir' + _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' + + # Archives containing C++ object files must be created using + # "CC -Bstatic", where "CC" is the KAI C++ compiler. + _LT_AC_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs' + ;; + icpc) + # Intel C++ + with_gnu_ld=yes + _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=no + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared $compiler_flags $predep_objects $libobjs $deplibs $postdep_objects ${wl}-soname $wl$soname -o $lib' + _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $compiler_flags $predep_objects $libobjs $deplibs $postdep_objects ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' + _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' + _LT_AC_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive$convenience ${wl}--no-whole-archive' + ;; + cxx) + # Compaq C++ + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared $compiler_flags $predep_objects $libobjs $deplibs $postdep_objects ${wl}-soname $wl$soname -o $lib' + _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $compiler_flags $predep_objects $libobjs $deplibs $postdep_objects ${wl}-soname $wl$soname -o $lib ${wl}-retain-symbols-file $wl$export_symbols' + + runpath_var=LD_RUN_PATH + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' + _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep "ld"`; templist=`echo $templist | $SED "s/\(^.*ld.*\)\( .*ld .*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; echo $list' + ;; + esac + ;; + lynxos*) + # FIXME: insert proper C++ library support + _LT_AC_TAGVAR(ld_shlibs, $1)=no + ;; + m88k*) + # FIXME: insert proper C++ library support + _LT_AC_TAGVAR(ld_shlibs, $1)=no + ;; + mvs*) + case $cc_basename in + cxx) + # FIXME: insert proper C++ library support + _LT_AC_TAGVAR(ld_shlibs, $1)=no + ;; + *) + # FIXME: insert proper C++ library support + _LT_AC_TAGVAR(ld_shlibs, $1)=no + ;; + esac + ;; + netbsd*) + if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then + _LT_AC_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $predep_objects $libobjs $deplibs $postdep_objects $linker_flags' + wlarc= + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_AC_TAGVAR(hardcode_direct, $1)=yes + _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no + fi + # Workaround some broken pre-1.5 toolchains + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep conftest.$objext | $SED -e "s:-lgcc -lc -lgcc::"' + ;; + osf3*) + case $cc_basename in + KCC) + # Kuck and Associates, Inc. (KAI) C++ Compiler + + # KCC will only create a shared library if the output file + # ends with ".so" (or ".sl" for HP-UX), so rename the library + # to its proper name (with version) after linking. + _LT_AC_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $compiler_flags $predep_objects $libobjs $deplibs $postdep_objects --soname $soname -o \$templib; mv \$templib $lib' + + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' + _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: + + # Archives containing C++ object files must be created using + # "CC -Bstatic", where "CC" is the KAI C++ compiler. + _LT_AC_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs' + + ;; + RCC) + # Rational C++ 2.4.1 + # FIXME: insert proper C++ library support + _LT_AC_TAGVAR(ld_shlibs, $1)=no + ;; + cxx) + _LT_AC_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $compiler_flags $predep_objects $libobjs $deplibs $postdep_objects ${wl}-soname $soname `test -n "$verstring" && echo ${wl}-set_version $verstring` -update_registry ${objdir}/so_locations -o $lib' + + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep "ld" | grep -v "ld:"`; templist=`echo $templist | $SED "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; echo $list' + ;; + *) + if test "$GXX" = yes && test "$with_gnu_ld" = no; then + _LT_AC_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib ${allow_undefined_flag} $compiler_flags $predep_objects $libobjs $deplibs $postdep_objects ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${objdir}/so_locations -o $lib' + + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep "\-L"' + + else + # FIXME: insert proper C++ library support + _LT_AC_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + ;; + osf4* | osf5*) + case $cc_basename in + KCC) + # Kuck and Associates, Inc. (KAI) C++ Compiler + + # KCC will only create a shared library if the output file + # ends with ".so" (or ".sl" for HP-UX), so rename the library + # to its proper name (with version) after linking. + _LT_AC_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $compiler_flags $predep_objects $libobjs $deplibs $postdep_objects --soname $soname -o \$templib; mv \$templib $lib' + + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' + _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: + + # Archives containing C++ object files must be created using + # the KAI C++ compiler. + _LT_AC_TAGVAR(old_archive_cmds, $1)='$CC -o $oldlib $oldobjs' + ;; + RCC) + # Rational C++ 2.4.1 + # FIXME: insert proper C++ library support + _LT_AC_TAGVAR(ld_shlibs, $1)=no + ;; + cxx) + _LT_AC_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $compiler_flags $predep_objects $libobjs $deplibs $postdep_objects -msym -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${objdir}/so_locations -o $lib' + _LT_AC_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done~ + echo "-hidden">> $lib.exp~ + $CC -shared$allow_undefined_flag $compiler_flags $predep_objects $libobjs $deplibs $postdep_objects -msym -soname $soname -Wl,-input -Wl,$lib.exp `test -n "$verstring" && echo -set_version $verstring` -update_registry $objdir/so_locations -o $lib~ + $rm $lib.exp' + + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' + _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep "ld" | grep -v "ld:"`; templist=`echo $templist | $SED "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; echo $list' + ;; + *) + if test "$GXX" = yes && test "$with_gnu_ld" = no; then + _LT_AC_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib ${allow_undefined_flag} $compiler_flags $predep_objects $libobjs $deplibs $postdep_objects ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${objdir}/so_locations -o $lib' + + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep "\-L"' + + else + # FIXME: insert proper C++ library support + _LT_AC_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + ;; + psos*) + # FIXME: insert proper C++ library support + _LT_AC_TAGVAR(ld_shlibs, $1)=no + ;; + sco*) + _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=no + case $cc_basename in + CC) + # FIXME: insert proper C++ library support + _LT_AC_TAGVAR(ld_shlibs, $1)=no + ;; + *) + # FIXME: insert proper C++ library support + _LT_AC_TAGVAR(ld_shlibs, $1)=no + ;; + esac + ;; + sunos4*) + case $cc_basename in + CC) + # Sun C++ 4.x + # FIXME: insert proper C++ library support + _LT_AC_TAGVAR(ld_shlibs, $1)=no + ;; + lcc) + # Lucid + # FIXME: insert proper C++ library support + _LT_AC_TAGVAR(ld_shlibs, $1)=no + ;; + *) + # FIXME: insert proper C++ library support + _LT_AC_TAGVAR(ld_shlibs, $1)=no + ;; + esac + ;; + solaris*) + case $cc_basename in + CC) + # Sun C++ 4.2, 5.x and Centerline C++ + _LT_AC_TAGVAR(no_undefined_flag, $1)=' -zdefs' + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -nolib -h$soname -o $lib $compiler_flags $predep_objects $libobjs $deplibs $postdep_objects' + _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~ + $CC -G${allow_undefined_flag} -nolib ${wl}-M ${wl}$lib.exp -h$soname -o $lib $compiler_flags $predep_objects $libobjs $deplibs $postdep_objects~$rm $lib.exp' + + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no + case $host_os in + solaris2.[0-5] | solaris2.[0-5].*) ;; + *) + # The C++ compiler is used as linker so we must use $wl + # flag to pass the commands to the underlying system + # linker. + # Supported since Solaris 2.6 (maybe 2.5.1?) + _LT_AC_TAGVAR(whole_archive_flag_spec, $1)='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract' + ;; + esac + _LT_AC_TAGVAR(link_all_deplibs, $1)=yes + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`$CC -G $CFLAGS -v conftest.$objext 2>&1 | grep "\-[[LR]]"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; echo $list' + + # Archives containing C++ object files must be created using + # "CC -xar", where "CC" is the Sun C++ compiler. This is + # necessary to make sure instantiated templates are included + # in the archive. + _LT_AC_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs' + ;; + gcx) + # Green Hills C++ Compiler + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared $compiler_flags $predep_objects $libobjs $deplibs $postdep_objects ${wl}-h $wl$soname -o $lib' + + # The C++ compiler must be used to create the archive. + _LT_AC_TAGVAR(old_archive_cmds, $1)='$CC $LDFLAGS -archive -o $oldlib $oldobjs' + ;; + *) + # GNU C++ compiler with Solaris linker + if test "$GXX" = yes && test "$with_gnu_ld" = no; then + _LT_AC_TAGVAR(no_undefined_flag, $1)=' ${wl}-z ${wl}defs' + if $CC --version | grep -v '^2\.7' > /dev/null; then + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $LDFLAGS $compiler_flags $predep_objects $libobjs $deplibs $postdep_objects ${wl}-h $wl$soname -o $lib' + _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~ + $CC -shared -nostdlib ${wl}-M $wl$lib.exp -o $lib $compiler_flags $predep_objects $libobjs $deplibs $postdep_objects~$rm $lib.exp' + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd="$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep \"\-L\"" + else + # g++ 2.7 appears to require `-G' NOT `-shared' on this + # platform. + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -G -nostdlib $LDFLAGS $compiler_flags $predep_objects $libobjs $deplibs $postdep_objects ${wl}-h $wl$soname -o $lib' + _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~ + $CC -G -nostdlib ${wl}-M $wl$lib.exp -o $lib $compiler_flags $predep_objects $libobjs $deplibs $postdep_objects~$rm $lib.exp' + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd="$CC -G $CFLAGS -v conftest.$objext 2>&1 | grep \"\-L\"" + fi + + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $wl$libdir' + fi + ;; + esac + ;; + sysv5OpenUNIX8* | sysv5UnixWare7* | sysv5uw[[78]]* | unixware7*) + _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=no + ;; + tandem*) + case $cc_basename in + NCC) + # NonStop-UX NCC 3.20 + # FIXME: insert proper C++ library support + _LT_AC_TAGVAR(ld_shlibs, $1)=no + ;; + *) + # FIXME: insert proper C++ library support + _LT_AC_TAGVAR(ld_shlibs, $1)=no + ;; + esac + ;; + vxworks*) + # FIXME: insert proper C++ library support + _LT_AC_TAGVAR(ld_shlibs, $1)=no + ;; + *) + # FIXME: insert proper C++ library support + _LT_AC_TAGVAR(ld_shlibs, $1)=no + ;; +esac +AC_MSG_RESULT([$_LT_AC_TAGVAR(ld_shlibs, $1)]) +test "$_LT_AC_TAGVAR(ld_shlibs, $1)" = no && can_build_shared=no + +_LT_AC_TAGVAR(GCC, $1)="$GXX" +_LT_AC_TAGVAR(LD, $1)="$LD" + +## CAVEAT EMPTOR: +## There is no encapsulation within the following macros, do not change +## the running order or otherwise move them around unless you know exactly +## what you are doing... +AC_LIBTOOL_POSTDEP_PREDEP($1) +AC_LIBTOOL_PROG_COMPILER_PIC($1) +AC_LIBTOOL_PROG_CC_C_O($1) +AC_LIBTOOL_SYS_HARD_LINK_LOCKS($1) +AC_LIBTOOL_PROG_LD_SHLIBS($1) +AC_LIBTOOL_SYS_DYNAMIC_LINKER($1) +AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH($1) +AC_LIBTOOL_SYS_LIB_STRIP +AC_LIBTOOL_DLOPEN_SELF($1) + +AC_LIBTOOL_CONFIG($1) + +AC_LANG_POP +CC=$lt_save_CC +LDCXX=$LD +LD=$lt_save_LD +GCC=$lt_save_GCC +with_gnu_ldcxx=$with_gnu_ld +with_gnu_ld=$lt_save_with_gnu_ld +lt_cv_path_LDCXX=$lt_cv_path_LD +lt_cv_path_LD=$lt_save_path_LD +lt_cv_prog_gnu_ldcxx=$lt_cv_prog_gnu_ld +lt_cv_prog_gnu_ld=$lt_save_with_gnu_ld +])# AC_LIBTOOL_LANG_CXX_CONFIG + +# AC_LIBTOOL_POSTDEP_PREDEP([TAGNAME]) +# ------------------------ +# Figure out "hidden" library dependencies from verbose +# compiler output when linking a shared library. +# Parse the compiler output and extract the necessary +# objects, libraries and library flags. +AC_DEFUN([AC_LIBTOOL_POSTDEP_PREDEP],[ +dnl we can't use the lt_simple_compile_test_code here, +dnl because it contains code intended for an executable, +dnl not a library. It's possible we should let each +dnl tag define a new lt_????_link_test_code variable, +dnl but it's only used here... +ifelse([$1],[],[cat > conftest.$ac_ext < conftest.$ac_ext < conftest.$ac_ext < conftest.$ac_ext <> "$cfgfile" +ifelse([$1], [], +[#! $SHELL + +# `$echo "$cfgfile" | sed 's%^.*/%%'` - Provide generalized library-building support services. +# Generated automatically by $PROGRAM (GNU $PACKAGE $VERSION$TIMESTAMP) +# NOTE: Changes made to this file will be lost: look at ltmain.sh. +# +# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001 +# Free Software Foundation, Inc. +# +# This file is part of GNU Libtool: +# Originally by Gordon Matzigkeit , 1996 +# +# 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 program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# A sed program that does not truncate output. +SED=$lt_SED + +# Sed that helps us avoid accidentally triggering echo(1) options like -n. +Xsed="$SED -e s/^X//" + +# The HP-UX ksh and POSIX shell print the target directory to stdout +# if CDPATH is set. +if test "X\${CDPATH+set}" = Xset; then CDPATH=:; export CDPATH; fi + +# The names of the tagged configurations supported by this script. +available_tags= + +# ### BEGIN LIBTOOL CONFIG], +[# ### BEGIN LIBTOOL TAG CONFIG: $tagname]) + +# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`: + +# Shell to use when invoking shell scripts. +SHELL=$lt_SHELL + +# Whether or not to build shared libraries. +build_libtool_libs=$enable_shared + +# Whether or not to build static libraries. +build_old_libs=$enable_static + +# Whether or not to add -lc for building shared libraries. +build_libtool_need_lc=$_LT_AC_TAGVAR(archive_cmds_need_lc, $1) + +# Whether or not to disallow shared libs when runtime libs are static +allow_libtool_libs_with_static_runtimes=$_LT_AC_TAGVAR(enable_shared_with_static_runtimes, $1) + +# Whether or not to optimize for fast installation. +fast_install=$enable_fast_install + +# The host system. +host_alias=$host_alias +host=$host + +# An echo program that does not interpret backslashes. +echo=$lt_echo + +# The archiver. +AR=$lt_AR +AR_FLAGS=$lt_AR_FLAGS + +# A C compiler. +LTCC=$lt_LTCC + +# A language-specific compiler. +CC=$lt_[]_LT_AC_TAGVAR(compiler, $1) + +# Is the compiler the GNU C compiler? +with_gcc=$_LT_AC_TAGVAR(GCC, $1) + +# An ERE matcher. +EGREP=$lt_EGREP + +# The linker used to build libraries. +LD=$lt_[]_LT_AC_TAGVAR(LD, $1) + +# Whether we need hard or soft links. +LN_S=$lt_LN_S + +# A BSD-compatible nm program. +NM=$lt_NM + +# A symbol stripping program +STRIP=$STRIP + +# Used to examine libraries when file_magic_cmd begins "file" +MAGIC_CMD=$MAGIC_CMD + +# Used on cygwin: DLL creation program. +DLLTOOL="$DLLTOOL" + +# Used on cygwin: object dumper. +OBJDUMP="$OBJDUMP" + +# Used on cygwin: assembler. +AS="$AS" + +# The name of the directory that contains temporary libtool files. +objdir=$objdir + +# How to create reloadable object files. +reload_flag=$lt_reload_flag +reload_cmds=$lt_reload_cmds + +# How to pass a linker flag through the compiler. +wl=$lt_[]_LT_AC_TAGVAR(lt_prog_compiler_wl, $1) + +# Object file suffix (normally "o"). +objext="$ac_objext" + +# Old archive suffix (normally "a"). +libext="$libext" + +# Shared library suffix (normally ".so"). +shrext='$shrext' + +# Executable file suffix (normally ""). +exeext="$exeext" + +# Additional compiler flags for building library objects. +pic_flag=$lt_[]_LT_AC_TAGVAR(lt_prog_compiler_pic, $1) +pic_mode=$pic_mode + +# What is the maximum length of a command? +max_cmd_len=$lt_cv_sys_max_cmd_len + +# Does compiler simultaneously support -c and -o options? +compiler_c_o=$lt_[]_LT_AC_TAGVAR(lt_cv_prog_compiler_c_o, $1) + +# Must we lock files when doing compilation ? +need_locks=$lt_need_locks + +# Do we need the lib prefix for modules? +need_lib_prefix=$need_lib_prefix + +# Do we need a version for libraries? +need_version=$need_version + +# Whether dlopen is supported. +dlopen_support=$enable_dlopen + +# Whether dlopen of programs is supported. +dlopen_self=$enable_dlopen_self + +# Whether dlopen of statically linked programs is supported. +dlopen_self_static=$enable_dlopen_self_static + +# Compiler flag to prevent dynamic linking. +link_static_flag=$lt_[]_LT_AC_TAGVAR(lt_prog_compiler_static, $1) + +# Compiler flag to turn off builtin functions. +no_builtin_flag=$lt_[]_LT_AC_TAGVAR(lt_prog_compiler_no_builtin_flag, $1) + +# Compiler flag to allow reflexive dlopens. +export_dynamic_flag_spec=$lt_[]_LT_AC_TAGVAR(export_dynamic_flag_spec, $1) + +# Compiler flag to generate shared objects directly from archives. +whole_archive_flag_spec=$lt_[]_LT_AC_TAGVAR(whole_archive_flag_spec, $1) + +# Compiler flag to generate thread-safe objects. +thread_safe_flag_spec=$lt_[]_LT_AC_TAGVAR(thread_safe_flag_spec, $1) + +# Library versioning type. +version_type=$version_type + +# Format of library name prefix. +libname_spec=$lt_libname_spec + +# List of archive names. First name is the real one, the rest are links. +# The last name is the one that the linker finds with -lNAME. +library_names_spec=$lt_library_names_spec + +# The coded name of the library, if different from the real name. +soname_spec=$lt_soname_spec + +# Commands used to build and install an old-style archive. +RANLIB=$lt_RANLIB +old_archive_cmds=$lt_[]_LT_AC_TAGVAR(old_archive_cmds, $1) +old_postinstall_cmds=$lt_old_postinstall_cmds +old_postuninstall_cmds=$lt_old_postuninstall_cmds + +# Create an old-style archive from a shared archive. +old_archive_from_new_cmds=$lt_[]_LT_AC_TAGVAR(old_archive_from_new_cmds, $1) + +# Create a temporary old-style archive to link instead of a shared archive. +old_archive_from_expsyms_cmds=$lt_[]_LT_AC_TAGVAR(old_archive_from_expsyms_cmds, $1) + +# Commands used to build and install a shared archive. +archive_cmds=$lt_[]_LT_AC_TAGVAR(archive_cmds, $1) +archive_expsym_cmds=$lt_[]_LT_AC_TAGVAR(archive_expsym_cmds, $1) +postinstall_cmds=$lt_postinstall_cmds +postuninstall_cmds=$lt_postuninstall_cmds + +# Commands used to build a loadable module (assumed same as above if empty) +module_cmds=$lt_[]_LT_AC_TAGVAR(module_cmds, $1) +module_expsym_cmds=$lt_[]_LT_AC_TAGVAR(module_expsym_cmds, $1) + +# Commands to strip libraries. +old_striplib=$lt_old_striplib +striplib=$lt_striplib + +# Dependencies to place before the objects being linked to create a +# shared library. +predep_objects=$lt_[]_LT_AC_TAGVAR(predep_objects, $1) + +# Dependencies to place after the objects being linked to create a +# shared library. +postdep_objects=$lt_[]_LT_AC_TAGVAR(postdep_objects, $1) + +# Dependencies to place before the objects being linked to create a +# shared library. +predeps=$lt_[]_LT_AC_TAGVAR(predeps, $1) + +# Dependencies to place after the objects being linked to create a +# shared library. +postdeps=$lt_[]_LT_AC_TAGVAR(postdeps, $1) + +# The library search path used internally by the compiler when linking +# a shared library. +compiler_lib_search_path=$lt_[]_LT_AC_TAGVAR(compiler_lib_search_path, $1) + +# Method to check whether dependent libraries are shared objects. +deplibs_check_method=$lt_deplibs_check_method + +# Command to use when deplibs_check_method == file_magic. +file_magic_cmd=$lt_file_magic_cmd + +# Flag that allows shared libraries with undefined symbols to be built. +allow_undefined_flag=$lt_[]_LT_AC_TAGVAR(allow_undefined_flag, $1) + +# Flag that forces no undefined symbols. +no_undefined_flag=$lt_[]_LT_AC_TAGVAR(no_undefined_flag, $1) + +# Commands used to finish a libtool library installation in a directory. +finish_cmds=$lt_finish_cmds + +# Same as above, but a single script fragment to be evaled but not shown. +finish_eval=$lt_finish_eval + +# Take the output of nm and produce a listing of raw symbols and C names. +global_symbol_pipe=$lt_lt_cv_sys_global_symbol_pipe + +# Transform the output of nm in a proper C declaration +global_symbol_to_cdecl=$lt_lt_cv_sys_global_symbol_to_cdecl + +# Transform the output of nm in a C name address pair +global_symbol_to_c_name_address=$lt_lt_cv_sys_global_symbol_to_c_name_address + +# This is the shared library runtime path variable. +runpath_var=$runpath_var + +# This is the shared library path variable. +shlibpath_var=$shlibpath_var + +# Is shlibpath searched before the hard-coded library search path? +shlibpath_overrides_runpath=$shlibpath_overrides_runpath + +# How to hardcode a shared library path into an executable. +hardcode_action=$_LT_AC_TAGVAR(hardcode_action, $1) + +# Whether we should hardcode library paths into libraries. +hardcode_into_libs=$hardcode_into_libs + +# Flag to hardcode \$libdir into a binary during linking. +# This must work even if \$libdir does not exist. +hardcode_libdir_flag_spec=$lt_[]_LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1) + +# If ld is used when linking, flag to hardcode \$libdir into +# a binary during linking. This must work even if \$libdir does +# not exist. +hardcode_libdir_flag_spec_ld=$lt_[]_LT_AC_TAGVAR(hardcode_libdir_flag_spec_ld, $1) + +# Whether we need a single -rpath flag with a separated argument. +hardcode_libdir_separator=$lt_[]_LT_AC_TAGVAR(hardcode_libdir_separator, $1) + +# Set to yes if using DIR/libNAME${shared_ext} during linking hardcodes DIR into the +# resulting binary. +hardcode_direct=$_LT_AC_TAGVAR(hardcode_direct, $1) + +# Set to yes if using the -LDIR flag during linking hardcodes DIR into the +# resulting binary. +hardcode_minus_L=$_LT_AC_TAGVAR(hardcode_minus_L, $1) + +# Set to yes if using SHLIBPATH_VAR=DIR during linking hardcodes DIR into +# the resulting binary. +hardcode_shlibpath_var=$_LT_AC_TAGVAR(hardcode_shlibpath_var, $1) + +# Set to yes if building a shared library automatically hardcodes DIR into the library +# and all subsequent libraries and executables linked against it. +hardcode_automatic=$_LT_AC_TAGVAR(hardcode_automatic, $1) + +# Variables whose values should be saved in libtool wrapper scripts and +# restored at relink time. +variables_saved_for_relink="$variables_saved_for_relink" + +# Whether libtool must link a program against all its dependency libraries. +link_all_deplibs=$_LT_AC_TAGVAR(link_all_deplibs, $1) + +# Compile-time system search path for libraries +sys_lib_search_path_spec=$lt_sys_lib_search_path_spec + +# Run-time system search path for libraries +sys_lib_dlsearch_path_spec=$lt_sys_lib_dlsearch_path_spec + +# Fix the shell variable \$srcfile for the compiler. +fix_srcfile_path="$_LT_AC_TAGVAR(fix_srcfile_path, $1)" + +# Set to yes if exported symbols are required. +always_export_symbols=$_LT_AC_TAGVAR(always_export_symbols, $1) + +# The commands to list exported symbols. +export_symbols_cmds=$lt_[]_LT_AC_TAGVAR(export_symbols_cmds, $1) + +# The commands to extract the exported symbol list from a shared archive. +extract_expsyms_cmds=$lt_extract_expsyms_cmds + +# Symbols that should not be listed in the preloaded symbols. +exclude_expsyms=$lt_[]_LT_AC_TAGVAR(exclude_expsyms, $1) + +# Symbols that must always be exported. +include_expsyms=$lt_[]_LT_AC_TAGVAR(include_expsyms, $1) + +ifelse([$1],[], +[# ### END LIBTOOL CONFIG], +[# ### END LIBTOOL TAG CONFIG: $tagname]) + +__EOF__ + +ifelse([$1],[], [ + case $host_os in + aix3*) + cat <<\EOF >> "$cfgfile" + +# AIX sometimes has problems with the GCC collect2 program. For some +# reason, if we set the COLLECT_NAMES environment variable, the problems +# vanish in a puff of smoke. +if test "X${COLLECT_NAMES+set}" != Xset; then + COLLECT_NAMES= + export COLLECT_NAMES +fi +EOF + ;; + esac + + # We use sed instead of cat because bash on DJGPP gets confused if + # if finds mixed CR/LF and LF-only lines. Since sed operates in + # text mode, it properly converts lines to CR/LF. This bash problem + # is reportedly fixed, but why not run on old versions too? + sed '$q' "$ltmain" >> "$cfgfile" || (rm -f "$cfgfile"; exit 1) + + mv -f "$cfgfile" "$ofile" || \ + (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile") + chmod +x "$ofile" +]) +else + # If there is no Makefile yet, we rely on a make rule to execute + # `config.status --recheck' to rerun these tests and create the + # libtool script then. + test -f Makefile && make "$ltmain" +fi +])# AC_LIBTOOL_CONFIG + + +# AC_LIBTOOL_PROG_COMPILER_NO_RTTI([TAGNAME]) +# ------------------------------------------- +AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_NO_RTTI], +[AC_REQUIRE([_LT_AC_SYS_COMPILER])dnl + +_LT_AC_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)= + +if test "$GCC" = yes; then + _LT_AC_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin' + + AC_LIBTOOL_COMPILER_OPTION([if $compiler supports -fno-rtti -fno-exceptions], + lt_cv_prog_compiler_rtti_exceptions, + [-fno-rtti -fno-exceptions], [], + [_LT_AC_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)="$_LT_AC_TAGVAR(lt_prog_compiler_no_builtin_flag, $1) -fno-rtti -fno-exceptions"]) +fi +])# AC_LIBTOOL_PROG_COMPILER_NO_RTTI + + +# AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE +# --------------------------------- +AC_DEFUN([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE], +[AC_REQUIRE([AC_CANONICAL_HOST]) +AC_REQUIRE([AC_PROG_NM]) +AC_REQUIRE([AC_OBJEXT]) +# Check for command to grab the raw symbol name followed by C symbol from nm. +AC_MSG_CHECKING([command to parse $NM output from $compiler object]) +AC_CACHE_VAL([lt_cv_sys_global_symbol_pipe], +[ +# These are sane defaults that work on at least a few old systems. +# [They come from Ultrix. What could be older than Ultrix?!! ;)] + +# Character class describing NM global symbol codes. +symcode='[[BCDEGRST]]' + +# Regexp to match symbols that can be accessed directly from C. +sympat='\([[_A-Za-z]][[_A-Za-z0-9]]*\)' + +# Transform the above into a raw symbol and a C symbol. +symxfrm='\1 \2\3 \3' + +# Transform an extracted symbol line into a proper C declaration +lt_cv_sys_global_symbol_to_cdecl="sed -n -e 's/^. .* \(.*\)$/extern int \1;/p'" + +# Transform an extracted symbol line into symbol name and symbol address +lt_cv_sys_global_symbol_to_c_name_address="sed -n -e 's/^: \([[^ ]]*\) $/ {\\\"\1\\\", (lt_ptr) 0},/p' -e 's/^$symcode \([[^ ]]*\) \([[^ ]]*\)$/ {\"\2\", (lt_ptr) \&\2},/p'" + +# Define system-specific variables. +case $host_os in +aix*) + symcode='[[BCDT]]' + ;; +cygwin* | mingw* | pw32*) + symcode='[[ABCDGISTW]]' + ;; +hpux*) # Its linker distinguishes data from code symbols + if test "$host_cpu" = ia64; then + symcode='[[ABCDEGRST]]' + fi + lt_cv_sys_global_symbol_to_cdecl="sed -n -e 's/^T .* \(.*\)$/extern int \1();/p' -e 's/^$symcode* .* \(.*\)$/extern char \1;/p'" + lt_cv_sys_global_symbol_to_c_name_address="sed -n -e 's/^: \([[^ ]]*\) $/ {\\\"\1\\\", (lt_ptr) 0},/p' -e 's/^$symcode* \([[^ ]]*\) \([[^ ]]*\)$/ {\"\2\", (lt_ptr) \&\2},/p'" + ;; +irix* | nonstopux*) + symcode='[[BCDEGRST]]' + ;; +osf*) + symcode='[[BCDEGQRST]]' + ;; +solaris* | sysv5*) + symcode='[[BDT]]' + ;; +sysv4) + symcode='[[DFNSTU]]' + ;; +esac + +# Handle CRLF in mingw tool chain +opt_cr= +case $build_os in +mingw*) + opt_cr=`echo 'x\{0,1\}' | tr x '\015'` # option cr in regexp + ;; +esac + +# If we're using GNU nm, then use its standard symbol codes. +case `$NM -V 2>&1` in +*GNU* | *'with BFD'*) + symcode='[[ABCDGISTW]]' ;; +esac + +# Try without a prefix undercore, then with it. +for ac_symprfx in "" "_"; do + + # Write the raw and C identifiers. + lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[[ ]]\($symcode$symcode*\)[[ ]][[ ]]*\($ac_symprfx\)$sympat$opt_cr$/$symxfrm/p'" + + # Check to see that the pipe works correctly. + pipe_works=no + + rm -f conftest* + cat > conftest.$ac_ext < $nlist) && test -s "$nlist"; then + # Try sorting and uniquifying the output. + if sort "$nlist" | uniq > "$nlist"T; then + mv -f "$nlist"T "$nlist" + else + rm -f "$nlist"T + fi + + # Make sure that we snagged all the symbols we need. + if grep ' nm_test_var$' "$nlist" >/dev/null; then + if grep ' nm_test_func$' "$nlist" >/dev/null; then + cat < conftest.$ac_ext +#ifdef __cplusplus +extern "C" { +#endif + +EOF + # Now generate the symbol file. + eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | grep -v main >> conftest.$ac_ext' + + cat <> conftest.$ac_ext +#if defined (__STDC__) && __STDC__ +# define lt_ptr_t void * +#else +# define lt_ptr_t char * +# define const +#endif + +/* The mapping between symbol names and symbols. */ +const struct { + const char *name; + lt_ptr_t address; +} +lt_preloaded_symbols[[]] = +{ +EOF + $SED "s/^$symcode$symcode* \(.*\) \(.*\)$/ {\"\2\", (lt_ptr_t) \&\2},/" < "$nlist" | grep -v main >> conftest.$ac_ext + cat <<\EOF >> conftest.$ac_ext + {0, (lt_ptr_t) 0} +}; + +#ifdef __cplusplus +} +#endif +EOF + # Now try linking the two files. + mv conftest.$ac_objext conftstm.$ac_objext + lt_save_LIBS="$LIBS" + lt_save_CFLAGS="$CFLAGS" + LIBS="conftstm.$ac_objext" + CFLAGS="$CFLAGS$_LT_AC_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)" + if AC_TRY_EVAL(ac_link) && test -s conftest${ac_exeext}; then + pipe_works=yes + fi + LIBS="$lt_save_LIBS" + CFLAGS="$lt_save_CFLAGS" + else + echo "cannot find nm_test_func in $nlist" >&AS_MESSAGE_LOG_FD + fi + else + echo "cannot find nm_test_var in $nlist" >&AS_MESSAGE_LOG_FD + fi + else + echo "cannot run $lt_cv_sys_global_symbol_pipe" >&AS_MESSAGE_LOG_FD + fi + else + echo "$progname: failed program was:" >&AS_MESSAGE_LOG_FD + cat conftest.$ac_ext >&5 + fi + rm -f conftest* conftst* + + # Do not use the global_symbol_pipe unless it works. + if test "$pipe_works" = yes; then + break + else + lt_cv_sys_global_symbol_pipe= + fi +done +]) +if test -z "$lt_cv_sys_global_symbol_pipe"; then + lt_cv_sys_global_symbol_to_cdecl= +fi +if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then + AC_MSG_RESULT(failed) +else + AC_MSG_RESULT(ok) +fi +]) # AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE + + +# AC_LIBTOOL_PROG_COMPILER_PIC([TAGNAME]) +# --------------------------------------- +AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_PIC], +[_LT_AC_TAGVAR(lt_prog_compiler_wl, $1)= +_LT_AC_TAGVAR(lt_prog_compiler_pic, $1)= +_LT_AC_TAGVAR(lt_prog_compiler_static, $1)= + +AC_MSG_CHECKING([for $compiler option to produce PIC]) + ifelse([$1],[CXX],[ + # C++ specific cases for pic, static, wl, etc. + if test "$GXX" = yes; then + _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-static' + + case $host_os in + aix*) + # All AIX code is PIC. + if test "$host_cpu" = ia64; then + # AIX 5 now supports IA64 processor + _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + fi + ;; + amigaos*) + # FIXME: we need at least 68020 code to build shared libraries, but + # adding the `-m68020' flag to GCC prevents building anything better, + # like `-m68040'. + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4' + ;; + beos* | cygwin* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) + # PIC is the default for these OSes. + ;; + mingw* | os2* | pw32*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT' + ;; + darwin* | rhapsody*) + # PIC is the default on this platform + # Common symbols not allowed in MH_DYLIB files + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' + ;; + *djgpp*) + # DJGPP does not support shared libraries at all + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)= + ;; + sysv4*MP*) + if test -d /usr/nec; then + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic + fi + ;; + hpux*) + # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but + # not for PA HP-UX. + case "$host_cpu" in + hppa*64*|ia64*) + ;; + *) + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + esac + ;; + *) + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + esac + else + case $host_os in + aix4* | aix5*) + # All AIX code is PIC. + if test "$host_cpu" = ia64; then + # AIX 5 now supports IA64 processor + _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + else + _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp' + fi + ;; + chorus*) + case $cc_basename in + cxch68) + # Green Hills C++ Compiler + # _LT_AC_TAGVAR(lt_prog_compiler_static, $1)="--no_auto_instantiation -u __main -u __premain -u _abort -r $COOL_DIR/lib/libOrb.a $MVME_DIR/lib/CC/libC.a $MVME_DIR/lib/classix/libcx.s.a" + ;; + esac + ;; + dgux*) + case $cc_basename in + ec++) + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + ;; + ghcx) + # Green Hills C++ Compiler + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-pic' + ;; + *) + ;; + esac + ;; + freebsd* | kfreebsd*-gnu) + # FreeBSD uses GNU C++ + ;; + hpux9* | hpux10* | hpux11*) + case $cc_basename in + CC) + _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_AC_TAGVAR(lt_prog_compiler_static, $1)="${ac_cv_prog_cc_wl}-a ${ac_cv_prog_cc_wl}archive" + if test "$host_cpu" != ia64; then + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='+Z' + fi + ;; + aCC) + _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_AC_TAGVAR(lt_prog_compiler_static, $1)="${ac_cv_prog_cc_wl}-a ${ac_cv_prog_cc_wl}archive" + case "$host_cpu" in + hppa*64*|ia64*) + # +Z the default + ;; + *) + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='+Z' + ;; + esac + ;; + *) + ;; + esac + ;; + irix5* | irix6* | nonstopux*) + case $cc_basename in + CC) + _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + # CC pic flag -KPIC is the default. + ;; + *) + ;; + esac + ;; + linux*) + case $cc_basename in + KCC) + # KAI C++ Compiler + _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,' + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + icpc) + # Intel C++ + _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-static' + ;; + cxx) + # Compaq C++ + # Make sure the PIC flag is empty. It appears that all Alpha + # Linux and Compaq Tru64 Unix objects are PIC. + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)= + _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + *) + ;; + esac + ;; + lynxos*) + ;; + m88k*) + ;; + mvs*) + case $cc_basename in + cxx) + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-W c,exportall' + ;; + *) + ;; + esac + ;; + netbsd*) + ;; + osf3* | osf4* | osf5*) + case $cc_basename in + KCC) + _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,' + ;; + RCC) + # Rational C++ 2.4.1 + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-pic' + ;; + cxx) + # Digital/Compaq C++ + _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + # Make sure the PIC flag is empty. It appears that all Alpha + # Linux and Compaq Tru64 Unix objects are PIC. + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)= + _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + *) + ;; + esac + ;; + psos*) + ;; + sco*) + case $cc_basename in + CC) + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + *) + ;; + esac + ;; + solaris*) + case $cc_basename in + CC) + # Sun C++ 4.2, 5.x and Centerline C++ + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' + ;; + gcx) + # Green Hills C++ Compiler + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' + ;; + *) + ;; + esac + ;; + sunos4*) + case $cc_basename in + CC) + # Sun C++ 4.x + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-pic' + _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + lcc) + # Lucid + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-pic' + ;; + *) + ;; + esac + ;; + tandem*) + case $cc_basename in + NCC) + # NonStop-UX NCC 3.20 + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + ;; + *) + ;; + esac + ;; + unixware*) + ;; + vxworks*) + ;; + *) + _LT_AC_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no + ;; + esac + fi +], +[ + if test "$GCC" = yes; then + _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-static' + + case $host_os in + aix*) + # All AIX code is PIC. + if test "$host_cpu" = ia64; then + # AIX 5 now supports IA64 processor + _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + fi + ;; + + amigaos*) + # FIXME: we need at least 68020 code to build shared libraries, but + # adding the `-m68020' flag to GCC prevents building anything better, + # like `-m68040'. + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4' + ;; + + beos* | cygwin* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) + # PIC is the default for these OSes. + ;; + + mingw* | pw32* | os2*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT' + ;; + + darwin* | rhapsody*) + # PIC is the default on this platform + # Common symbols not allowed in MH_DYLIB files + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' + ;; + + msdosdjgpp*) + # Just because we use GCC doesn't mean we suddenly get shared libraries + # on systems that don't support them. + _LT_AC_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no + enable_shared=no + ;; + + sysv4*MP*) + if test -d /usr/nec; then + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic + fi + ;; + + hpux*) + # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but + # not for PA HP-UX. + case "$host_cpu" in + hppa*64*|ia64*) + # +Z the default + ;; + *) + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + esac + ;; + + *) + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + esac + else + # PORTME Check for flag to pass linker flags through the system compiler. + case $host_os in + aix*) + _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + if test "$host_cpu" = ia64; then + # AIX 5 now supports IA64 processor + _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + else + _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp' + fi + ;; + + mingw* | pw32* | os2*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT' + ;; + + hpux9* | hpux10* | hpux11*) + _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but + # not for PA HP-UX. + case "$host_cpu" in + hppa*64*|ia64*) + # +Z the default + ;; + *) + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='+Z' + ;; + esac + # Is there a better lt_prog_compiler_static that works with the bundled CC? + _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive' + ;; + + irix5* | irix6* | nonstopux*) + _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + # PIC (with -KPIC) is the default. + _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + + newsos6) + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + + linux*) + case $CC in + icc* | ecc*) + _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-static' + ;; + ccc*) + _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + # All Alpha code is PIC. + _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + esac + ;; + + osf3* | osf4* | osf5*) + _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + # All OSF/1 code is PIC. + _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + + sco3.2v5*) + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-Kpic' + _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-dn' + ;; + + solaris*) + _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + + sunos4*) + _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' + _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + + sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) + _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + + sysv4*MP*) + if test -d /usr/nec ;then + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-Kconform_pic' + _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + fi + ;; + + uts4*) + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-pic' + _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + + *) + _LT_AC_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no + ;; + esac + fi +]) +AC_MSG_RESULT([$_LT_AC_TAGVAR(lt_prog_compiler_pic, $1)]) + +# +# Check to make sure the PIC flag actually works. +# +if test -n "$_LT_AC_TAGVAR(lt_prog_compiler_pic, $1)"; then + AC_LIBTOOL_COMPILER_OPTION([if $compiler PIC flag $_LT_AC_TAGVAR(lt_prog_compiler_pic, $1) works], + _LT_AC_TAGVAR(lt_prog_compiler_pic_works, $1), + [$_LT_AC_TAGVAR(lt_prog_compiler_pic, $1)ifelse([$1],[],[ -DPIC],[ifelse([$1],[CXX],[ -DPIC],[])])], [], + [case $_LT_AC_TAGVAR(lt_prog_compiler_pic, $1) in + "" | " "*) ;; + *) _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)=" $_LT_AC_TAGVAR(lt_prog_compiler_pic, $1)" ;; + esac], + [_LT_AC_TAGVAR(lt_prog_compiler_pic, $1)= + _LT_AC_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no]) +fi +case "$host_os" in + # For platforms which do not support PIC, -DPIC is meaningless: + *djgpp*) + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)= + ;; + *) + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)="$_LT_AC_TAGVAR(lt_prog_compiler_pic, $1)ifelse([$1],[],[ -DPIC],[ifelse([$1],[CXX],[ -DPIC],[])])" + ;; +esac +]) + + +# AC_LIBTOOL_PROG_LD_SHLIBS([TAGNAME]) +# ------------------------------------ +# See if the linker supports building shared libraries. +AC_DEFUN([AC_LIBTOOL_PROG_LD_SHLIBS], +[AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries]) +ifelse([$1],[CXX],[ + _LT_AC_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + case $host_os in + aix4* | aix5*) + # If we're using GNU nm, then we don't want the "-C" option. + # -C means demangle to AIX nm, but means don't demangle with GNU nm + if $NM -V 2>&1 | grep 'GNU' > /dev/null; then + _LT_AC_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\[$]2 == "T") || (\[$]2 == "D") || (\[$]2 == "B")) && ([substr](\[$]3,1,1) != ".")) { print \[$]3 } }'\'' | sort -u > $export_symbols' + else + _LT_AC_TAGVAR(export_symbols_cmds, $1)='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\[$]2 == "T") || (\[$]2 == "D") || (\[$]2 == "B")) && ([substr](\[$]3,1,1) != ".")) { print \[$]3 } }'\'' | sort -u > $export_symbols' + fi + ;; + pw32*) + _LT_AC_TAGVAR(export_symbols_cmds, $1)="$ltdll_cmds" + ;; + cygwin* | mingw*) + _LT_AC_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGS]] /s/.* \([[^ ]]*\)/\1 DATA/'\'' | $SED -e '\''/^[[AITW]] /s/.* //'\'' | sort | uniq > $export_symbols' + ;; + *) + _LT_AC_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + ;; + esac +],[ + runpath_var= + _LT_AC_TAGVAR(allow_undefined_flag, $1)= + _LT_AC_TAGVAR(enable_shared_with_static_runtimes, $1)=no + _LT_AC_TAGVAR(archive_cmds, $1)= + _LT_AC_TAGVAR(archive_expsym_cmds, $1)= + _LT_AC_TAGVAR(old_archive_From_new_cmds, $1)= + _LT_AC_TAGVAR(old_archive_from_expsyms_cmds, $1)= + _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)= + _LT_AC_TAGVAR(whole_archive_flag_spec, $1)= + _LT_AC_TAGVAR(thread_safe_flag_spec, $1)= + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)= + _LT_AC_TAGVAR(hardcode_libdir_flag_spec_ld, $1)= + _LT_AC_TAGVAR(hardcode_libdir_separator, $1)= + _LT_AC_TAGVAR(hardcode_direct, $1)=no + _LT_AC_TAGVAR(hardcode_minus_L, $1)=no + _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=unsupported + _LT_AC_TAGVAR(link_all_deplibs, $1)=unknown + _LT_AC_TAGVAR(hardcode_automatic, $1)=no + _LT_AC_TAGVAR(module_cmds, $1)= + _LT_AC_TAGVAR(module_expsym_cmds, $1)= + _LT_AC_TAGVAR(always_export_symbols, $1)=no + _LT_AC_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + # include_expsyms should be a list of space-separated symbols to be *always* + # included in the symbol list + _LT_AC_TAGVAR(include_expsyms, $1)= + # exclude_expsyms can be an extended regexp of symbols to exclude + # it will be wrapped by ` (' and `)$', so one must not match beginning or + # end of line. Example: `a|bc|.*d.*' will exclude the symbols `a' and `bc', + # as well as any symbol that contains `d'. + _LT_AC_TAGVAR(exclude_expsyms, $1)="_GLOBAL_OFFSET_TABLE_" + # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out + # platforms (ab)use it in PIC code, but their linkers get confused if + # the symbol is explicitly referenced. Since portable code cannot + # rely on this symbol name, it's probably fine to never include it in + # preloaded symbol tables. + extract_expsyms_cmds= + + case $host_os in + cygwin* | mingw* | pw32*) + # FIXME: the MSVC++ port hasn't been tested in a loooong time + # When not using gcc, we currently assume that we are using + # Microsoft Visual C++. + if test "$GCC" != yes; then + with_gnu_ld=no + fi + ;; + openbsd*) + with_gnu_ld=no + ;; + esac + + _LT_AC_TAGVAR(ld_shlibs, $1)=yes + if test "$with_gnu_ld" = yes; then + # If archive_cmds runs LD, not CC, wlarc should be empty + wlarc='${wl}' + + # See if GNU ld supports shared libraries. + case $host_os in + aix3* | aix4* | aix5*) + # On AIX/PPC, the GNU linker is very broken + if test "$host_cpu" != ia64; then + _LT_AC_TAGVAR(ld_shlibs, $1)=no + cat <&2 + +*** Warning: the GNU linker, at least up to release 2.9.1, is reported +*** to be unable to reliably create shared libraries on AIX. +*** Therefore, libtool is disabling shared libraries support. If you +*** really care for shared libraries, you may want to modify your PATH +*** so that a non-GNU linker is found, and then restart. + +EOF + fi + ;; + + amigaos*) + _LT_AC_TAGVAR(archive_cmds, $1)='$rm $output_objdir/a2ixlibrary.data~$echo "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$echo "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$echo "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$echo "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes + + # Samuel A. Falvo II reports + # that the semantics of dynamic libraries on AmigaOS, at least up + # to version 4, is to share data among multiple programs linked + # with the same dynamic library. Since this doesn't match the + # behavior of shared libraries on other platforms, we can't use + # them. + _LT_AC_TAGVAR(ld_shlibs, $1)=no + ;; + + beos*) + if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then + _LT_AC_TAGVAR(allow_undefined_flag, $1)=unsupported + # Joseph Beckenbach says some releases of gcc + # support --undefined. This deserves some investigation. FIXME + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -nostart $compiler_flags $libobjs $deplibs ${wl}-soname $wl$soname -o $lib' + else + _LT_AC_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + cygwin* | mingw* | pw32*) + # _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless, + # as there is no search path for DLLs. + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_AC_TAGVAR(allow_undefined_flag, $1)=no + _LT_AC_TAGVAR(always_export_symbols, $1)=no + _LT_AC_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + _LT_AC_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGS]] /s/.* \([[^ ]]*\)/\1 DATA/'\'' | $SED -e '\''/^[[AITW]] /s/.* //'\'' | sort | uniq > $export_symbols' + + if $LD --help 2>&1 | grep 'auto-import' > /dev/null; then + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared $compiler_flags $libobjs $deplibs -o $output_objdir/$soname ${wl}--image-base=0x10000000 ${wl}--out-implib,$lib' + # If the export-symbols file already is a .def file (1st line + # is EXPORTS), use it as is; otherwise, prepend... + _LT_AC_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then + cp $export_symbols $output_objdir/$soname.def; + else + echo EXPORTS > $output_objdir/$soname.def; + cat $export_symbols >> $output_objdir/$soname.def; + fi~ + $CC -shared $output_objdir/$soname.def $compiler_flags $libobjs $deplibs -o $output_objdir/$soname ${wl}--image-base=0x10000000 ${wl}--out-implib,$lib' + else + ld_shlibs=no + fi + ;; + + netbsd*) + if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then + _LT_AC_TAGVAR(archive_cmds, $1)='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib' + wlarc= + else + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared $compiler_flags $libobjs $deplibs ${wl}-soname $wl$soname -o $lib' + _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $compiler_flags $libobjs $deplibs ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + fi + ;; + + solaris* | sysv5*) + if $LD -v 2>&1 | grep 'BFD 2\.8' > /dev/null; then + _LT_AC_TAGVAR(ld_shlibs, $1)=no + cat <&2 + +*** Warning: The releases 2.8.* of the GNU linker cannot reliably +*** create shared libraries on Solaris systems. Therefore, libtool +*** is disabling shared libraries support. We urge you to upgrade GNU +*** binutils to release 2.9.1 or newer. Another option is to modify +*** your PATH or compiler configuration so that the native linker is +*** used, and then restart. + +EOF + elif $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared $compiler_flags $libobjs $deplibs ${wl}-soname $wl$soname -o $lib' + _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $compiler_flags $libobjs $deplibs ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + else + _LT_AC_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + sunos4*) + _LT_AC_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags' + wlarc= + _LT_AC_TAGVAR(hardcode_direct, $1)=yes + _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + *) + if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared $compiler_flags $libobjs $deplibs ${wl}-soname $wl$soname -o $lib' + _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $compiler_flags $libobjs $deplibs ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + else + _LT_AC_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + + if test "$_LT_AC_TAGVAR(ld_shlibs, $1)" = yes; then + runpath_var=LD_RUN_PATH + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}--rpath ${wl}$libdir' + _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' + # ancient GNU ld didn't support --whole-archive et. al. + if $LD --help 2>&1 | grep 'no-whole-archive' > /dev/null; then + _LT_AC_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' + else + _LT_AC_TAGVAR(whole_archive_flag_spec, $1)= + fi + fi + else + # PORTME fill in a description of your system's linker (not GNU ld) + case $host_os in + aix3*) + _LT_AC_TAGVAR(allow_undefined_flag, $1)=unsupported + _LT_AC_TAGVAR(always_export_symbols, $1)=yes + _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname' + # Note: this linker hardcodes the directories in LIBPATH if there + # are no directories specified by -L. + _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes + if test "$GCC" = yes && test -z "$link_static_flag"; then + # Neither direct hardcoding nor static linking is supported with a + # broken collect2. + _LT_AC_TAGVAR(hardcode_direct, $1)=unsupported + fi + ;; + + aix4* | aix5*) + if test "$host_cpu" = ia64; then + # On IA64, the linker does run time linking by default, so we don't + # have to do anything special. + aix_use_runtimelinking=no + exp_sym_flag='-Bexport' + no_entry_flag="" + else + # If we're using GNU nm, then we don't want the "-C" option. + # -C means demangle to AIX nm, but means don't demangle with GNU nm + if $NM -V 2>&1 | grep 'GNU' > /dev/null; then + _LT_AC_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\[$]2 == "T") || (\[$]2 == "D") || (\[$]2 == "B")) && ([substr](\[$]3,1,1) != ".")) { print \[$]3 } }'\'' | sort -u > $export_symbols' + else + _LT_AC_TAGVAR(export_symbols_cmds, $1)='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\[$]2 == "T") || (\[$]2 == "D") || (\[$]2 == "B")) && ([substr](\[$]3,1,1) != ".")) { print \[$]3 } }'\'' | sort -u > $export_symbols' + fi + + # KDE requires run time linking. Make it the default. + aix_use_runtimelinking=yes + exp_sym_flag='-bexport' + no_entry_flag='-bnoentry' + fi + + # When large executables or shared objects are built, AIX ld can + # have problems creating the table of contents. If linking a library + # or program results in "error TOC overflow" add -mminimal-toc to + # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not + # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. + + _LT_AC_TAGVAR(archive_cmds, $1)='' + _LT_AC_TAGVAR(hardcode_direct, $1)=yes + _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=':' + _LT_AC_TAGVAR(link_all_deplibs, $1)=yes + + if test "$GCC" = yes; then + case $host_os in aix4.[012]|aix4.[012].*) + # We only want to do this on AIX 4.2 and lower, the check + # below for broken collect2 doesn't work under 4.3+ + collect2name=`${CC} -print-prog-name=collect2` + if test -f "$collect2name" && \ + strings "$collect2name" | grep resolve_lib_name >/dev/null + then + # We have reworked collect2 + _LT_AC_TAGVAR(hardcode_direct, $1)=yes + else + # We have old collect2 + _LT_AC_TAGVAR(hardcode_direct, $1)=unsupported + # It fails to find uninstalled libraries when the uninstalled + # path is not listed in the libpath. Setting hardcode_minus_L + # to unsupported forces relinking + _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_AC_TAGVAR(hardcode_libdir_separator, $1)= + fi + esac + shared_flag='-shared' + else + # not using gcc + if test "$host_cpu" = ia64; then + # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release + # chokes on -Wl,-G. The following line is correct: + shared_flag='-G' + else + if test "$aix_use_runtimelinking" = yes; then + shared_flag='-qmkshrobj ${wl}-G' + else + shared_flag='-qmkshrobj' + fi + fi + fi + + # Let the compiler handle the export list. + _LT_AC_TAGVAR(always_export_symbols, $1)=no + if test "$aix_use_runtimelinking" = yes; then + # Warning - without using the other runtime loading flags (-brtl), + # -berok will link without error, but may produce a broken library. + _LT_AC_TAGVAR(allow_undefined_flag, $1)='-berok' + # Determine the default libpath from the value encoded in an empty executable. + _LT_AC_SYS_LIBPATH_AIX + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" + _LT_AC_TAGVAR(archive_cmds, $1)="\$CC"' -o $output_objdir/$soname $compiler_flags $libobjs $deplibs `if test "x${allow_undefined_flag}" != "x"; then echo "${wl}${allow_undefined_flag}"; else :; fi` '" $shared_flag" + _LT_AC_TAGVAR(archive_expsym_cmds, $1)="\$CC"' -o $output_objdir/$soname $compiler_flags $libobjs $deplibs `if test "x${allow_undefined_flag}" != "x"; then echo "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag" + else + if test "$host_cpu" = ia64; then + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $libdir:/usr/lib:/lib' + _LT_AC_TAGVAR(allow_undefined_flag, $1)="-z nodefs" + _LT_AC_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $compiler_flags $libobjs $deplibs ${wl}${allow_undefined_flag} '"\${wl}$no_entry_flag \${wl}$exp_sym_flag:\$export_symbols" + else + # Determine the default libpath from the value encoded in an empty executable. + _LT_AC_SYS_LIBPATH_AIX + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" + # Warning - without using the other run time loading flags, + # -berok will link without error, but may produce a broken library. + _LT_AC_TAGVAR(no_undefined_flag, $1)=' ${wl}-bernotok' + _LT_AC_TAGVAR(allow_undefined_flag, $1)=' ${wl}-berok' + # -bexpall does not export symbols beginning with underscore (_) + _LT_AC_TAGVAR(always_export_symbols, $1)=yes + # Exported symbols can be pulled into shared objects from archives + _LT_AC_TAGVAR(whole_archive_flag_spec, $1)=' ' + _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=yes + # This is similar to how AIX traditionally builds it's shared libraries. + _LT_AC_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $compiler_flags $libobjs $deplibs ${wl}-bE:$export_symbols ${wl}-bnoentry${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' + fi + fi + ;; + + amigaos*) + _LT_AC_TAGVAR(archive_cmds, $1)='$rm $output_objdir/a2ixlibrary.data~$echo "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$echo "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$echo "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$echo "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes + # see comment about different semantics on the GNU ld section + _LT_AC_TAGVAR(ld_shlibs, $1)=no + ;; + + bsdi4*) + _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)=-rdynamic + ;; + + cygwin* | mingw* | pw32*) + # When not using gcc, we currently assume that we are using + # Microsoft Visual C++. + # hardcode_libdir_flag_spec is actually meaningless, as there is + # no search path for DLLs. + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' + _LT_AC_TAGVAR(allow_undefined_flag, $1)=no + # Tell ltmain to make .lib files, not .a files. + libext=lib + # Tell ltmain to make .dll files, not .so files. + shrext=".dll" + # FIXME: Setting linknames here is a bad hack. + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -o $lib $compiler_flags $libobjs `echo "$deplibs" | $SED -e '\''s/ -lc$//'\''` -link -dll~linknames=' + # The linker will automatically build a .lib file if we build a DLL. + _LT_AC_TAGVAR(old_archive_From_new_cmds, $1)='true' + # FIXME: Should let the user specify the lib program. + _LT_AC_TAGVAR(old_archive_cmds, $1)='lib /OUT:$oldlib$oldobjs$old_deplibs' + fix_srcfile_path='`cygpath -w "$srcfile"`' + _LT_AC_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + ;; + + darwin* | rhapsody*) + if test "$GXX" = yes ; then + _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=no + case "$host_os" in + rhapsody* | darwin1.[[012]]) + _LT_AC_TAGVAR(allow_undefined_flag, $1)='-Wl,-undefined -Wl,suppress' + ;; + *) # Darwin 1.3 on + if test -z ${MACOSX_DEPLOYMENT_TARGET} ; then + _LT_AC_TAGVAR(allow_undefined_flag, $1)='-Wl,-flat_namespace -Wl,-undefined -Wl,suppress' + else + case ${MACOSX_DEPLOYMENT_TARGET} in + 10.[012]) + _LT_AC_TAGVAR(allow_undefined_flag, $1)='-Wl,-flat_namespace -Wl,-undefined -Wl,suppress' + ;; + 10.*) + _LT_AC_TAGVAR(allow_undefined_flag, $1)='-Wl,-undefined -Wl,dynamic_lookup' + ;; + esac + fi + ;; + esac + lt_int_apple_cc_single_mod=no + output_verbose_link_cmd='echo' + if $CC -dumpspecs 2>&1 | grep 'single_module' >/dev/null ; then + lt_int_apple_cc_single_mod=yes + fi + if test "X$lt_int_apple_cc_single_mod" = Xyes ; then + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -dynamiclib -single_module $allow_undefined_flag -o $lib $compiler_flags $libobjs $deplibs -install_name $rpath/$soname $verstring' + else + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -r ${wl}-bind_at_load -keep_private_externs -nostdlib -o ${lib}-master.o $libobjs~$CC -dynamiclib $allow_undefined_flag -o $lib ${lib}-master.o $compiler_flags $deplibs -install_name $rpath/$soname $verstring' + fi + _LT_AC_TAGVAR(module_cmds, $1)='$CC ${wl}-bind_at_load $allow_undefined_flag -o $lib -bundle $compiler_flags $libobjs $deplibs' + # Don't fix this by using the ld -exported_symbols_list flag, it doesn't exist in older darwin ld's + if test "X$lt_int_apple_cc_single_mod" = Xyes ; then + _LT_AC_TAGVAR(archive_expsym_cmds, $1)='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC -dynamiclib -single_module $allow_undefined_flag -o $lib $compiler_flags $libobjs $deplibs -install_name $rpath/$soname $verstring~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' + else + _LT_AC_TAGVAR(archive_expsym_cmds, $1)='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC -r ${wl}-bind_at_load -keep_private_externs -nostdlib -o ${lib}-master.o $libobjs~$CC -dynamiclib $allow_undefined_flag -o $lib ${lib}-master.o $compiler_flags $deplibs -install_name $rpath/$soname $verstring~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' + fi + _LT_AC_TAGVAR(module_expsym_cmds, $1)='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC $allow_undefined_flag -o $lib -bundle $compiler_flags $libobjs $deplibs~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' + _LT_AC_TAGVAR(hardcode_direct, $1)=no + _LT_AC_TAGVAR(hardcode_automatic, $1)=yes + _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=unsupported + _LT_AC_TAGVAR(whole_archive_flag_spec, $1)='-all_load $convenience' + _LT_AC_TAGVAR(link_all_deplibs, $1)=yes + else + _LT_AC_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + dgux*) + _LT_AC_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + freebsd1*) + _LT_AC_TAGVAR(ld_shlibs, $1)=no + ;; + + # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor + # support. Future versions do this automatically, but an explicit c++rt0.o + # does not break anything, and helps significantly (at the cost of a little + # extra space). + freebsd2.2*) + _LT_AC_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o' + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_AC_TAGVAR(hardcode_direct, $1)=yes + _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + # Unfortunately, older versions of FreeBSD 2 do not have this feature. + freebsd2*) + _LT_AC_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' + _LT_AC_TAGVAR(hardcode_direct, $1)=yes + _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes + _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + # FreeBSD 3 and greater uses gcc -shared to do shared libraries. + freebsd* | kfreebsd*-gnu) + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -o $lib $compiler_flags $libobjs $deplibs' + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_AC_TAGVAR(hardcode_direct, $1)=yes + _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + hpux9*) + if test "$GCC" = yes; then + _LT_AC_TAGVAR(archive_cmds, $1)='$rm $output_objdir/$soname~$CC -shared -fPIC ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $compiler_flags $libobjs $deplibs~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' + else + _LT_AC_TAGVAR(archive_cmds, $1)='$rm $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' + fi + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' + _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_AC_TAGVAR(hardcode_direct, $1)=yes + + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes + _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + ;; + + hpux10* | hpux11*) + if test "$GCC" = yes -a "$with_gnu_ld" = no; then + case "$host_cpu" in + hppa*64*|ia64*) + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}+h ${wl}$soname -o $lib $compiler_flags $libobjs $deplibs' + ;; + *) + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $compiler_flags $libobjs $deplibs' + ;; + esac + else + case "$host_cpu" in + hppa*64*|ia64*) + _LT_AC_TAGVAR(archive_cmds, $1)='$LD -b +h $soname -o $lib $libobjs $deplibs $linker_flags' + ;; + *) + _LT_AC_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' + ;; + esac + fi + if test "$with_gnu_ld" = no; then + case "$host_cpu" in + hppa*64*) + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' + _LT_AC_TAGVAR(hardcode_libdir_flag_spec_ld, $1)='+b $libdir' + _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_AC_TAGVAR(hardcode_direct, $1)=no + _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + ia64*) + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_AC_TAGVAR(hardcode_direct, $1)=no + _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no + + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes + ;; + *) + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' + _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_AC_TAGVAR(hardcode_direct, $1)=yes + _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes + ;; + esac + fi + ;; + + irix5* | irix6* | nonstopux*) + if test "$GCC" = yes; then + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared $compiler_flags $libobjs $deplibs ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + else + _LT_AC_TAGVAR(archive_cmds, $1)='$LD -shared $libobjs $deplibs $linker_flags -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib' + _LT_AC_TAGVAR(hardcode_libdir_flag_spec_ld, $1)='-rpath $libdir' + fi + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_AC_TAGVAR(link_all_deplibs, $1)=yes + ;; + + netbsd*) + if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then + _LT_AC_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out + else + _LT_AC_TAGVAR(archive_cmds, $1)='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF + fi + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_AC_TAGVAR(hardcode_direct, $1)=yes + _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + newsos6) + _LT_AC_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_AC_TAGVAR(hardcode_direct, $1)=yes + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + openbsd*) + _LT_AC_TAGVAR(hardcode_direct, $1)=yes + _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no + if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $compiler_flags $libobjs $deplibs' + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' + _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + else + case $host_os in + openbsd[[01]].* | openbsd2.[[0-7]] | openbsd2.[[0-7]].*) + _LT_AC_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + ;; + *) + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $compiler_flags $libobjs $deplibs' + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' + ;; + esac + fi + ;; + + os2*) + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes + _LT_AC_TAGVAR(allow_undefined_flag, $1)=unsupported + _LT_AC_TAGVAR(archive_cmds, $1)='$echo "LIBRARY $libname INITINSTANCE" > $output_objdir/$libname.def~$echo "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~$echo DATA >> $output_objdir/$libname.def~$echo " SINGLE NONSHARED" >> $output_objdir/$libname.def~$echo EXPORTS >> $output_objdir/$libname.def~emxexp $libobjs >> $output_objdir/$libname.def~$CC -Zdll -Zcrtdll -o $lib $compiler_flags $libobjs $deplibs$output_objdir/$libname.def' + _LT_AC_TAGVAR(old_archive_From_new_cmds, $1)='emximp -o $output_objdir/$libname.a $output_objdir/$libname.def' + ;; + + osf3*) + if test "$GCC" = yes; then + _LT_AC_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $compiler_flags $libobjs $deplibs ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + else + _LT_AC_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' + _LT_AC_TAGVAR(archive_cmds, $1)='$LD -shared${allow_undefined_flag} $libobjs $deplibs $linker_flags -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib' + fi + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: + ;; + + osf4* | osf5*) # as osf3* with the addition of -msym flag + if test "$GCC" = yes; then + _LT_AC_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $compiler_flags $libobjs $deplibs ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + else + _LT_AC_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' + _LT_AC_TAGVAR(archive_cmds, $1)='$LD -shared${allow_undefined_flag} $libobjs $deplibs $linker_flags -msym -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib' + _LT_AC_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; echo "-hidden">> $lib.exp~ + $LD -shared${allow_undefined_flag} -input $lib.exp $linker_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${objdir}/so_locations -o $lib~$rm $lib.exp' + + # Both c and cxx compiler support -rpath directly + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' + fi + _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: + ;; + + sco3.2v5*) + _LT_AC_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Bexport' + runpath_var=LD_RUN_PATH + hardcode_runpath_var=yes + ;; + + solaris*) + _LT_AC_TAGVAR(no_undefined_flag, $1)=' -z text' + if test "$GCC" = yes; then + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h ${wl}$soname -o $lib $compiler_flags $libobjs $deplibs' + _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~ + $CC -shared ${wl}-M ${wl}$lib.exp ${wl}-h ${wl}$soname -o $lib $compiler_flags $libobjs $deplibs~$rm $lib.exp' + else + _LT_AC_TAGVAR(archive_cmds, $1)='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~ + $LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$rm $lib.exp' + fi + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no + case $host_os in + solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; + *) # Supported since Solaris 2.6 (maybe 2.5.1?) + _LT_AC_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract' ;; + esac + _LT_AC_TAGVAR(link_all_deplibs, $1)=yes + ;; + + sunos4*) + if test "x$host_vendor" = xsequent; then + # Use $CC to link under sequent, because it throws in some extra .o + # files that make .init and .fini sections work. + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h $soname -o $lib $compiler_flags $libobjs $deplibs' + else + _LT_AC_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags' + fi + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_AC_TAGVAR(hardcode_direct, $1)=yes + _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes + _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + sysv4) + case $host_vendor in + sni) + _LT_AC_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_AC_TAGVAR(hardcode_direct, $1)=yes # is this really true??? + ;; + siemens) + ## LD is ld it makes a PLAMLIB + ## CC just makes a GrossModule. + _LT_AC_TAGVAR(archive_cmds, $1)='$LD -G -o $lib $libobjs $deplibs $linker_flags' + _LT_AC_TAGVAR(reload_cmds, $1)='$CC -r -o $output$reload_objs' + _LT_AC_TAGVAR(hardcode_direct, $1)=no + ;; + motorola) + _LT_AC_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_AC_TAGVAR(hardcode_direct, $1)=no #Motorola manual says yes, but my tests say they lie + ;; + esac + runpath_var='LD_RUN_PATH' + _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + sysv4.3*) + _LT_AC_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='-Bexport' + ;; + + sysv4*MP*) + if test -d /usr/nec; then + _LT_AC_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no + runpath_var=LD_RUN_PATH + hardcode_runpath_var=yes + _LT_AC_TAGVAR(ld_shlibs, $1)=yes + fi + ;; + + sysv4.2uw2*) + _LT_AC_TAGVAR(archive_cmds, $1)='$LD -G -o $lib $libobjs $deplibs $linker_flags' + _LT_AC_TAGVAR(hardcode_direct, $1)=yes + _LT_AC_TAGVAR(hardcode_minus_L, $1)=no + _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no + hardcode_runpath_var=yes + runpath_var=LD_RUN_PATH + ;; + + sysv5OpenUNIX8* | sysv5UnixWare7* | sysv5uw[[78]]* | unixware7*) + _LT_AC_TAGVAR(no_undefined_flag, $1)='${wl}-z ${wl}text' + if test "$GCC" = yes; then + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h ${wl}$soname -o $lib $compiler_flags $libobjs $deplibs' + else + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h ${wl}$soname -o $lib $compiler_flags $libobjs $deplibs' + fi + runpath_var='LD_RUN_PATH' + _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + sysv5*) + _LT_AC_TAGVAR(no_undefined_flag, $1)=' -z text' + # $CC -shared without GNU ld will not create a library from C++ + # object files and a static libstdc++, better avoid it by now + _LT_AC_TAGVAR(archive_cmds, $1)='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~ + $LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$rm $lib.exp' + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)= + _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no + runpath_var='LD_RUN_PATH' + ;; + + uts4*) + _LT_AC_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + *) + _LT_AC_TAGVAR(ld_shlibs, $1)=no + ;; + esac + fi +]) +AC_MSG_RESULT([$_LT_AC_TAGVAR(ld_shlibs, $1)]) +test "$_LT_AC_TAGVAR(ld_shlibs, $1)" = no && can_build_shared=no + +variables_saved_for_relink="PATH $shlibpath_var $runpath_var" +if test "$GCC" = yes; then + variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" +fi + +# +# Do we need to explicitly link libc? +# +case "x$_LT_AC_TAGVAR(archive_cmds_need_lc, $1)" in +x|xyes) + # Assume -lc should be added + _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=yes + + if test "$enable_shared" = yes && test "$GCC" = yes; then + case $_LT_AC_TAGVAR(archive_cmds, $1) in + *'~'*) + # FIXME: we may have to deal with multi-command sequences. + ;; + '$CC '*) + # Test whether the compiler implicitly links with -lc since on some + # systems, -lgcc has to come before -lc. If gcc already passes -lc + # to ld, don't add -lc before -lgcc. + AC_MSG_CHECKING([whether -lc should be explicitly linked in]) + $rm conftest* + printf "$lt_simple_compile_test_code" > conftest.$ac_ext + + if AC_TRY_EVAL(ac_compile) 2>conftest.err; then + soname=conftest + lib=conftest + libobjs=conftest.$ac_objext + deplibs= + wl=$_LT_AC_TAGVAR(lt_prog_compiler_wl, $1) + compiler_flags=-v + linker_flags=-v + verstring= + output_objdir=. + libname=conftest + lt_save_allow_undefined_flag=$_LT_AC_TAGVAR(allow_undefined_flag, $1) + _LT_AC_TAGVAR(allow_undefined_flag, $1)= + if AC_TRY_EVAL(_LT_AC_TAGVAR(archive_cmds, $1) 2\>\&1 \| grep \" -lc \" \>/dev/null 2\>\&1) + then + _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=no + else + _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=yes + fi + _LT_AC_TAGVAR(allow_undefined_flag, $1)=$lt_save_allow_undefined_flag + else + cat conftest.err 1>&5 + fi + $rm conftest* + AC_MSG_RESULT([$_LT_AC_TAGVAR(archive_cmds_need_lc, $1)]) + ;; + esac + fi + ;; +esac +])# AC_LIBTOOL_PROG_LD_SHLIBS + + +# _LT_AC_FILE_LTDLL_C +# ------------------- +# Be careful that the start marker always follows a newline. +AC_DEFUN([_LT_AC_FILE_LTDLL_C], [ +# /* ltdll.c starts here */ +# #define WIN32_LEAN_AND_MEAN +# #include +# #undef WIN32_LEAN_AND_MEAN +# #include +# +# #ifndef __CYGWIN__ +# # ifdef __CYGWIN32__ +# # define __CYGWIN__ __CYGWIN32__ +# # endif +# #endif +# +# #ifdef __cplusplus +# extern "C" { +# #endif +# BOOL APIENTRY DllMain (HINSTANCE hInst, DWORD reason, LPVOID reserved); +# #ifdef __cplusplus +# } +# #endif +# +# #ifdef __CYGWIN__ +# #include +# DECLARE_CYGWIN_DLL( DllMain ); +# #endif +# HINSTANCE __hDllInstance_base; +# +# BOOL APIENTRY +# DllMain (HINSTANCE hInst, DWORD reason, LPVOID reserved) +# { +# __hDllInstance_base = hInst; +# return TRUE; +# } +# /* ltdll.c ends here */ +])# _LT_AC_FILE_LTDLL_C + + +# _LT_AC_TAGVAR(VARNAME, [TAGNAME]) +# --------------------------------- +AC_DEFUN([_LT_AC_TAGVAR], [ifelse([$2], [], [$1], [$1_$2])]) + + +# old names +AC_DEFUN([AM_PROG_LIBTOOL], [AC_PROG_LIBTOOL]) +AC_DEFUN([AM_ENABLE_SHARED], [AC_ENABLE_SHARED($@)]) +AC_DEFUN([AM_ENABLE_STATIC], [AC_ENABLE_STATIC($@)]) +AC_DEFUN([AM_DISABLE_SHARED], [AC_DISABLE_SHARED($@)]) +AC_DEFUN([AM_DISABLE_STATIC], [AC_DISABLE_STATIC($@)]) +AC_DEFUN([AM_PROG_LD], [AC_PROG_LD]) +AC_DEFUN([AM_PROG_NM], [AC_PROG_NM]) + +# This is just to silence aclocal about the macro not being used +ifelse([AC_DISABLE_FAST_INSTALL]) + +AC_DEFUN([LT_AC_PROG_GCJ], +[AC_CHECK_TOOL(GCJ, gcj, no) + test "x${GCJFLAGS+set}" = xset || GCJFLAGS="-g -O2" + AC_SUBST(GCJFLAGS) +]) + +AC_DEFUN([LT_AC_PROG_RC], +[AC_CHECK_TOOL(RC, windres, no) +]) + +############################################################ +# NOTE: This macro has been submitted for inclusion into # +# GNU Autoconf as AC_PROG_SED. When it is available in # +# a released version of Autoconf we should remove this # +# macro and use it instead. # +############################################################ +# LT_AC_PROG_SED +# -------------- +# Check for a fully-functional sed program, that truncates +# as few characters as possible. Prefer GNU sed if found. +AC_DEFUN([LT_AC_PROG_SED], +[AC_MSG_CHECKING([for a sed that does not truncate output]) +AC_CACHE_VAL(lt_cv_path_SED, +[# Loop through the user's path and test for sed and gsed. +# Then use that list of sed's as ones to test for truncation. +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for lt_ac_prog in sed gsed; do + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$lt_ac_prog$ac_exec_ext"; then + lt_ac_sed_list="$lt_ac_sed_list $as_dir/$lt_ac_prog$ac_exec_ext" + fi + done + done +done +lt_ac_max=0 +lt_ac_count=0 +# Add /usr/xpg4/bin/sed as it is typically found on Solaris +# along with /bin/sed that truncates output. +for lt_ac_sed in $lt_ac_sed_list /usr/xpg4/bin/sed; do + test ! -f $lt_ac_sed && break + cat /dev/null > conftest.in + lt_ac_count=0 + echo $ECHO_N "0123456789$ECHO_C" >conftest.in + # Check for GNU sed and select it if it is found. + if "$lt_ac_sed" --version 2>&1 < /dev/null | grep 'GNU' > /dev/null; then + lt_cv_path_SED=$lt_ac_sed + break + fi + while true; do + cat conftest.in conftest.in >conftest.tmp + mv conftest.tmp conftest.in + cp conftest.in conftest.nl + echo >>conftest.nl + $lt_ac_sed -e 's/a$//' < conftest.nl >conftest.out || break + cmp -s conftest.out conftest.nl || break + # 10000 chars as input seems more than enough + test $lt_ac_count -gt 10 && break + lt_ac_count=`expr $lt_ac_count + 1` + if test $lt_ac_count -gt $lt_ac_max; then + lt_ac_max=$lt_ac_count + lt_cv_path_SED=$lt_ac_sed + fi + done +done +]) +SED=$lt_cv_path_SED +AC_MSG_RESULT([$SED]) +]) + +dnl PKG_CHECK_MODULES(GSTUFF, gtk+-2.0 >= 1.3 glib = 1.3.4, action-if, action-not) +dnl defines GSTUFF_LIBS, GSTUFF_CFLAGS, see pkg-config man page +dnl also defines GSTUFF_PKG_ERRORS on error +AC_DEFUN([PKG_CHECK_MODULES], [ + succeeded=no + + if test -z "$PKG_CONFIG"; then + AC_PATH_PROG(PKG_CONFIG, pkg-config, no) + fi + + if test "$PKG_CONFIG" = "no" ; then + echo "*** The pkg-config script could not be found. Make sure it is" + echo "*** in your path, or set the PKG_CONFIG environment variable" + echo "*** to the full path to pkg-config." + echo "*** Or see http://www.freedesktop.org/software/pkgconfig to get pkg-config." + else + PKG_CONFIG_MIN_VERSION=0.9.0 + if $PKG_CONFIG --atleast-pkgconfig-version $PKG_CONFIG_MIN_VERSION; then + AC_MSG_CHECKING(for $2) + + if $PKG_CONFIG --exists "$2" ; then + AC_MSG_RESULT(yes) + succeeded=yes + + AC_MSG_CHECKING($1_CFLAGS) + $1_CFLAGS=`$PKG_CONFIG --cflags "$2"` + AC_MSG_RESULT($$1_CFLAGS) + + AC_MSG_CHECKING($1_LIBS) + $1_LIBS=`$PKG_CONFIG --libs "$2"` + AC_MSG_RESULT($$1_LIBS) + else + $1_CFLAGS="" + $1_LIBS="" + ## If we have a custom action on failure, don't print errors, but + ## do set a variable so people can do so. + $1_PKG_ERRORS=`$PKG_CONFIG --errors-to-stdout --print-errors "$2"` + ifelse([$4], ,echo $$1_PKG_ERRORS,) + fi + + AC_SUBST($1_CFLAGS) + AC_SUBST($1_LIBS) + else + echo "*** Your version of pkg-config is too old. You need version $PKG_CONFIG_MIN_VERSION or newer." + echo "*** See http://www.freedesktop.org/software/pkgconfig" + fi + fi + + if test $succeeded = yes; then + ifelse([$3], , :, [$3]) + else + ifelse([$4], , AC_MSG_ERROR([Library requirements ($2) not met; consider adjusting the PKG_CONFIG_PATH environment variable if your libraries are in a nonstandard prefix so pkg-config can find them.]), [$4]) + fi +]) + + diff --git a/aclocal.m4 b/aclocal.m4 new file mode 100644 index 00000000..986ff00c --- /dev/null +++ b/aclocal.m4 @@ -0,0 +1,881 @@ +# generated automatically by aclocal 1.10.1 -*- Autoconf -*- + +# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, +# 2005, 2006, 2007, 2008 Free Software Foundation, Inc. +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +m4_ifndef([AC_AUTOCONF_VERSION], + [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl +m4_if(AC_AUTOCONF_VERSION, [2.61],, +[m4_warning([this file was generated for autoconf 2.61. +You have another version of autoconf. It may work, but is not guaranteed to. +If you have problems, you may need to regenerate the build system entirely. +To do so, use the procedure documented by the package, typically `autoreconf'.])]) + +# Copyright (C) 2002, 2003, 2005, 2006, 2007 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_AUTOMAKE_VERSION(VERSION) +# ---------------------------- +# Automake X.Y traces this macro to ensure aclocal.m4 has been +# generated from the m4 files accompanying Automake X.Y. +# (This private macro should not be called outside this file.) +AC_DEFUN([AM_AUTOMAKE_VERSION], +[am__api_version='1.10' +dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to +dnl require some minimum version. Point them to the right macro. +m4_if([$1], [1.10.1], [], + [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl +]) + +# _AM_AUTOCONF_VERSION(VERSION) +# ----------------------------- +# aclocal traces this macro to find the Autoconf version. +# This is a private macro too. Using m4_define simplifies +# the logic in aclocal, which can simply ignore this definition. +m4_define([_AM_AUTOCONF_VERSION], []) + +# AM_SET_CURRENT_AUTOMAKE_VERSION +# ------------------------------- +# Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced. +# This function is AC_REQUIREd by AC_INIT_AUTOMAKE. +AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], +[AM_AUTOMAKE_VERSION([1.10.1])dnl +m4_ifndef([AC_AUTOCONF_VERSION], + [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl +_AM_AUTOCONF_VERSION(AC_AUTOCONF_VERSION)]) + +# AM_AUX_DIR_EXPAND -*- Autoconf -*- + +# Copyright (C) 2001, 2003, 2005 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets +# $ac_aux_dir to `$srcdir/foo'. In other projects, it is set to +# `$srcdir', `$srcdir/..', or `$srcdir/../..'. +# +# Of course, Automake must honor this variable whenever it calls a +# tool from the auxiliary directory. The problem is that $srcdir (and +# therefore $ac_aux_dir as well) can be either absolute or relative, +# depending on how configure is run. This is pretty annoying, since +# it makes $ac_aux_dir quite unusable in subdirectories: in the top +# source directory, any form will work fine, but in subdirectories a +# relative path needs to be adjusted first. +# +# $ac_aux_dir/missing +# fails when called from a subdirectory if $ac_aux_dir is relative +# $top_srcdir/$ac_aux_dir/missing +# fails if $ac_aux_dir is absolute, +# fails when called from a subdirectory in a VPATH build with +# a relative $ac_aux_dir +# +# The reason of the latter failure is that $top_srcdir and $ac_aux_dir +# are both prefixed by $srcdir. In an in-source build this is usually +# harmless because $srcdir is `.', but things will broke when you +# start a VPATH build or use an absolute $srcdir. +# +# So we could use something similar to $top_srcdir/$ac_aux_dir/missing, +# iff we strip the leading $srcdir from $ac_aux_dir. That would be: +# am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"` +# and then we would define $MISSING as +# MISSING="\${SHELL} $am_aux_dir/missing" +# This will work as long as MISSING is not called from configure, because +# unfortunately $(top_srcdir) has no meaning in configure. +# However there are other variables, like CC, which are often used in +# configure, and could therefore not use this "fixed" $ac_aux_dir. +# +# Another solution, used here, is to always expand $ac_aux_dir to an +# absolute PATH. The drawback is that using absolute paths prevent a +# configured tree to be moved without reconfiguration. + +AC_DEFUN([AM_AUX_DIR_EXPAND], +[dnl Rely on autoconf to set up CDPATH properly. +AC_PREREQ([2.50])dnl +# expand $ac_aux_dir to an absolute path +am_aux_dir=`cd $ac_aux_dir && pwd` +]) + +# AM_CONDITIONAL -*- Autoconf -*- + +# Copyright (C) 1997, 2000, 2001, 2003, 2004, 2005, 2006 +# Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 8 + +# AM_CONDITIONAL(NAME, SHELL-CONDITION) +# ------------------------------------- +# Define a conditional. +AC_DEFUN([AM_CONDITIONAL], +[AC_PREREQ(2.52)dnl + ifelse([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])], + [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl +AC_SUBST([$1_TRUE])dnl +AC_SUBST([$1_FALSE])dnl +_AM_SUBST_NOTMAKE([$1_TRUE])dnl +_AM_SUBST_NOTMAKE([$1_FALSE])dnl +if $2; then + $1_TRUE= + $1_FALSE='#' +else + $1_TRUE='#' + $1_FALSE= +fi +AC_CONFIG_COMMANDS_PRE( +[if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then + AC_MSG_ERROR([[conditional "$1" was never defined. +Usually this means the macro was only invoked conditionally.]]) +fi])]) + +# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 +# Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 9 + +# There are a few dirty hacks below to avoid letting `AC_PROG_CC' be +# written in clear, in which case automake, when reading aclocal.m4, +# will think it sees a *use*, and therefore will trigger all it's +# C support machinery. Also note that it means that autoscan, seeing +# CC etc. in the Makefile, will ask for an AC_PROG_CC use... + + +# _AM_DEPENDENCIES(NAME) +# ---------------------- +# See how the compiler implements dependency checking. +# NAME is "CC", "CXX", "GCJ", or "OBJC". +# We try a few techniques and use that to set a single cache variable. +# +# We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was +# modified to invoke _AM_DEPENDENCIES(CC); we would have a circular +# dependency, and given that the user is not expected to run this macro, +# just rely on AC_PROG_CC. +AC_DEFUN([_AM_DEPENDENCIES], +[AC_REQUIRE([AM_SET_DEPDIR])dnl +AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl +AC_REQUIRE([AM_MAKE_INCLUDE])dnl +AC_REQUIRE([AM_DEP_TRACK])dnl + +ifelse([$1], CC, [depcc="$CC" am_compiler_list=], + [$1], CXX, [depcc="$CXX" am_compiler_list=], + [$1], OBJC, [depcc="$OBJC" am_compiler_list='gcc3 gcc'], + [$1], UPC, [depcc="$UPC" am_compiler_list=], + [$1], GCJ, [depcc="$GCJ" am_compiler_list='gcc3 gcc'], + [depcc="$$1" am_compiler_list=]) + +AC_CACHE_CHECK([dependency style of $depcc], + [am_cv_$1_dependencies_compiler_type], +[if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named `D' -- because `-MD' means `put the output + # in D'. + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + # We will build objects and dependencies in a subdirectory because + # it helps to detect inapplicable dependency modes. For instance + # both Tru64's cc and ICC support -MD to output dependencies as a + # side effect of compilation, but ICC will put the dependencies in + # the current directory while Tru64 will put them in the object + # directory. + mkdir sub + + am_cv_$1_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp` + fi + for depmode in $am_compiler_list; do + # Setup a source with many dependencies, because some compilers + # like to wrap large dependency lists on column 80 (with \), and + # we should not choose a depcomp mode which is confused by this. + # + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + : > sub/conftest.c + for i in 1 2 3 4 5 6; do + echo '#include "conftst'$i'.h"' >> sub/conftest.c + # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with + # Solaris 8's {/usr,}/bin/sh. + touch sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + + case $depmode in + nosideeffect) + # after this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + none) break ;; + esac + # We check with `-c' and `-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle `-M -o', and we need to detect this. + if depmode=$depmode \ + source=sub/conftest.c object=sub/conftest.${OBJEXT-o} \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c -o sub/conftest.${OBJEXT-o} sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftest.${OBJEXT-o} sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # or remarks (even with -Werror). So we grep stderr for any message + # that says an option was ignored or not supported. + # When given -MP, icc 7.0 and 7.1 complain thusly: + # icc: Command line warning: ignoring option '-M'; no argument required + # The diagnosis changed in icc 8.0: + # icc: Command line remark: option '-MP' not supported + if (grep 'ignoring option' conftest.err || + grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else + am_cv_$1_dependencies_compiler_type=$depmode + break + fi + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_$1_dependencies_compiler_type=none +fi +]) +AC_SUBST([$1DEPMODE], [depmode=$am_cv_$1_dependencies_compiler_type]) +AM_CONDITIONAL([am__fastdep$1], [ + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_$1_dependencies_compiler_type" = gcc3]) +]) + + +# AM_SET_DEPDIR +# ------------- +# Choose a directory name for dependency files. +# This macro is AC_REQUIREd in _AM_DEPENDENCIES +AC_DEFUN([AM_SET_DEPDIR], +[AC_REQUIRE([AM_SET_LEADING_DOT])dnl +AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl +]) + + +# AM_DEP_TRACK +# ------------ +AC_DEFUN([AM_DEP_TRACK], +[AC_ARG_ENABLE(dependency-tracking, +[ --disable-dependency-tracking speeds up one-time build + --enable-dependency-tracking do not reject slow dependency extractors]) +if test "x$enable_dependency_tracking" != xno; then + am_depcomp="$ac_aux_dir/depcomp" + AMDEPBACKSLASH='\' +fi +AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno]) +AC_SUBST([AMDEPBACKSLASH])dnl +_AM_SUBST_NOTMAKE([AMDEPBACKSLASH])dnl +]) + +# Generate code to set up dependency tracking. -*- Autoconf -*- + +# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 +# Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +#serial 3 + +# _AM_OUTPUT_DEPENDENCY_COMMANDS +# ------------------------------ +AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS], +[for mf in $CONFIG_FILES; do + # Strip MF so we end up with the name of the file. + mf=`echo "$mf" | sed -e 's/:.*$//'` + # Check whether this is an Automake generated Makefile or not. + # We used to match only the files named `Makefile.in', but + # some people rename them; so instead we look at the file content. + # Grep'ing the first line is not enough: some people post-process + # each Makefile.in and add a new line on top of each file to say so. + # Grep'ing the whole file is not good either: AIX grep has a line + # limit of 2048, but all sed's we know have understand at least 4000. + if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then + dirpart=`AS_DIRNAME("$mf")` + else + continue + fi + # Extract the definition of DEPDIR, am__include, and am__quote + # from the Makefile without running `make'. + DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` + test -z "$DEPDIR" && continue + am__include=`sed -n 's/^am__include = //p' < "$mf"` + test -z "am__include" && continue + am__quote=`sed -n 's/^am__quote = //p' < "$mf"` + # When using ansi2knr, U may be empty or an underscore; expand it + U=`sed -n 's/^U = //p' < "$mf"` + # Find all dependency output files, they are included files with + # $(DEPDIR) in their names. We invoke sed twice because it is the + # simplest approach to changing $(DEPDIR) to its actual value in the + # expansion. + for file in `sed -n " + s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ + sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do + # Make sure the directory exists. + test -f "$dirpart/$file" && continue + fdir=`AS_DIRNAME(["$file"])` + AS_MKDIR_P([$dirpart/$fdir]) + # echo "creating $dirpart/$file" + echo '# dummy' > "$dirpart/$file" + done +done +])# _AM_OUTPUT_DEPENDENCY_COMMANDS + + +# AM_OUTPUT_DEPENDENCY_COMMANDS +# ----------------------------- +# This macro should only be invoked once -- use via AC_REQUIRE. +# +# This code is only required when automatic dependency tracking +# is enabled. FIXME. This creates each `.P' file that we will +# need in order to bootstrap the dependency handling code. +AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS], +[AC_CONFIG_COMMANDS([depfiles], + [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS], + [AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"]) +]) + +# Copyright (C) 1996, 1997, 2000, 2001, 2003, 2005 +# Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 8 + +# AM_CONFIG_HEADER is obsolete. It has been replaced by AC_CONFIG_HEADERS. +AU_DEFUN([AM_CONFIG_HEADER], [AC_CONFIG_HEADERS($@)]) + +# Do all the work for Automake. -*- Autoconf -*- + +# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, +# 2005, 2006, 2008 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 13 + +# This macro actually does too much. Some checks are only needed if +# your package does certain things. But this isn't really a big deal. + +# AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE]) +# AM_INIT_AUTOMAKE([OPTIONS]) +# ----------------------------------------------- +# The call with PACKAGE and VERSION arguments is the old style +# call (pre autoconf-2.50), which is being phased out. PACKAGE +# and VERSION should now be passed to AC_INIT and removed from +# the call to AM_INIT_AUTOMAKE. +# We support both call styles for the transition. After +# the next Automake release, Autoconf can make the AC_INIT +# arguments mandatory, and then we can depend on a new Autoconf +# release and drop the old call support. +AC_DEFUN([AM_INIT_AUTOMAKE], +[AC_PREREQ([2.60])dnl +dnl Autoconf wants to disallow AM_ names. We explicitly allow +dnl the ones we care about. +m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl +AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl +AC_REQUIRE([AC_PROG_INSTALL])dnl +if test "`cd $srcdir && pwd`" != "`pwd`"; then + # Use -I$(srcdir) only when $(srcdir) != ., so that make's output + # is not polluted with repeated "-I." + AC_SUBST([am__isrc], [' -I$(srcdir)'])_AM_SUBST_NOTMAKE([am__isrc])dnl + # test to see if srcdir already configured + if test -f $srcdir/config.status; then + AC_MSG_ERROR([source directory already configured; run "make distclean" there first]) + fi +fi + +# test whether we have cygpath +if test -z "$CYGPATH_W"; then + if (cygpath --version) >/dev/null 2>/dev/null; then + CYGPATH_W='cygpath -w' + else + CYGPATH_W=echo + fi +fi +AC_SUBST([CYGPATH_W]) + +# Define the identity of the package. +dnl Distinguish between old-style and new-style calls. +m4_ifval([$2], +[m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl + AC_SUBST([PACKAGE], [$1])dnl + AC_SUBST([VERSION], [$2])], +[_AM_SET_OPTIONS([$1])dnl +dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT. +m4_if(m4_ifdef([AC_PACKAGE_NAME], 1)m4_ifdef([AC_PACKAGE_VERSION], 1), 11,, + [m4_fatal([AC_INIT should be called with package and version arguments])])dnl + AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl + AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl + +_AM_IF_OPTION([no-define],, +[AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE", [Name of package]) + AC_DEFINE_UNQUOTED(VERSION, "$VERSION", [Version number of package])])dnl + +# Some tools Automake needs. +AC_REQUIRE([AM_SANITY_CHECK])dnl +AC_REQUIRE([AC_ARG_PROGRAM])dnl +AM_MISSING_PROG(ACLOCAL, aclocal-${am__api_version}) +AM_MISSING_PROG(AUTOCONF, autoconf) +AM_MISSING_PROG(AUTOMAKE, automake-${am__api_version}) +AM_MISSING_PROG(AUTOHEADER, autoheader) +AM_MISSING_PROG(MAKEINFO, makeinfo) +AM_PROG_INSTALL_SH +AM_PROG_INSTALL_STRIP +AC_REQUIRE([AM_PROG_MKDIR_P])dnl +# We need awk for the "check" target. The system "awk" is bad on +# some platforms. +AC_REQUIRE([AC_PROG_AWK])dnl +AC_REQUIRE([AC_PROG_MAKE_SET])dnl +AC_REQUIRE([AM_SET_LEADING_DOT])dnl +_AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])], + [_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])], + [_AM_PROG_TAR([v7])])]) +_AM_IF_OPTION([no-dependencies],, +[AC_PROVIDE_IFELSE([AC_PROG_CC], + [_AM_DEPENDENCIES(CC)], + [define([AC_PROG_CC], + defn([AC_PROG_CC])[_AM_DEPENDENCIES(CC)])])dnl +AC_PROVIDE_IFELSE([AC_PROG_CXX], + [_AM_DEPENDENCIES(CXX)], + [define([AC_PROG_CXX], + defn([AC_PROG_CXX])[_AM_DEPENDENCIES(CXX)])])dnl +AC_PROVIDE_IFELSE([AC_PROG_OBJC], + [_AM_DEPENDENCIES(OBJC)], + [define([AC_PROG_OBJC], + defn([AC_PROG_OBJC])[_AM_DEPENDENCIES(OBJC)])])dnl +]) +]) + + +# When config.status generates a header, we must update the stamp-h file. +# This file resides in the same directory as the config header +# that is generated. The stamp files are numbered to have different names. + +# Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the +# loop where config.status creates the headers, so we can generate +# our stamp files there. +AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK], +[# Compute $1's index in $config_headers. +_am_arg=$1 +_am_stamp_count=1 +for _am_header in $config_headers :; do + case $_am_header in + $_am_arg | $_am_arg:* ) + break ;; + * ) + _am_stamp_count=`expr $_am_stamp_count + 1` ;; + esac +done +echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count]) + +# Copyright (C) 2001, 2003, 2005 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_PROG_INSTALL_SH +# ------------------ +# Define $install_sh. +AC_DEFUN([AM_PROG_INSTALL_SH], +[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +install_sh=${install_sh-"\$(SHELL) $am_aux_dir/install-sh"} +AC_SUBST(install_sh)]) + +# Copyright (C) 2003, 2005 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 2 + +# Check whether the underlying file-system supports filenames +# with a leading dot. For instance MS-DOS doesn't. +AC_DEFUN([AM_SET_LEADING_DOT], +[rm -rf .tst 2>/dev/null +mkdir .tst 2>/dev/null +if test -d .tst; then + am__leading_dot=. +else + am__leading_dot=_ +fi +rmdir .tst 2>/dev/null +AC_SUBST([am__leading_dot])]) + +# Check to see how 'make' treats includes. -*- Autoconf -*- + +# Copyright (C) 2001, 2002, 2003, 2005 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 3 + +# AM_MAKE_INCLUDE() +# ----------------- +# Check to see how make treats includes. +AC_DEFUN([AM_MAKE_INCLUDE], +[am_make=${MAKE-make} +cat > confinc << 'END' +am__doit: + @echo done +.PHONY: am__doit +END +# If we don't find an include directive, just comment out the code. +AC_MSG_CHECKING([for style of include used by $am_make]) +am__include="#" +am__quote= +_am_result=none +# First try GNU make style include. +echo "include confinc" > confmf +# We grep out `Entering directory' and `Leaving directory' +# messages which can occur if `w' ends up in MAKEFLAGS. +# In particular we don't look at `^make:' because GNU make might +# be invoked under some other name (usually "gmake"), in which +# case it prints its new name instead of `make'. +if test "`$am_make -s -f confmf 2> /dev/null | grep -v 'ing directory'`" = "done"; then + am__include=include + am__quote= + _am_result=GNU +fi +# Now try BSD make style include. +if test "$am__include" = "#"; then + echo '.include "confinc"' > confmf + if test "`$am_make -s -f confmf 2> /dev/null`" = "done"; then + am__include=.include + am__quote="\"" + _am_result=BSD + fi +fi +AC_SUBST([am__include]) +AC_SUBST([am__quote]) +AC_MSG_RESULT([$_am_result]) +rm -f confinc confmf +]) + +# Fake the existence of programs that GNU maintainers use. -*- Autoconf -*- + +# Copyright (C) 1997, 1999, 2000, 2001, 2003, 2004, 2005 +# Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 5 + +# AM_MISSING_PROG(NAME, PROGRAM) +# ------------------------------ +AC_DEFUN([AM_MISSING_PROG], +[AC_REQUIRE([AM_MISSING_HAS_RUN]) +$1=${$1-"${am_missing_run}$2"} +AC_SUBST($1)]) + + +# AM_MISSING_HAS_RUN +# ------------------ +# Define MISSING if not defined so far and test if it supports --run. +# If it does, set am_missing_run to use it, otherwise, to nothing. +AC_DEFUN([AM_MISSING_HAS_RUN], +[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +AC_REQUIRE_AUX_FILE([missing])dnl +test x"${MISSING+set}" = xset || MISSING="\${SHELL} $am_aux_dir/missing" +# Use eval to expand $SHELL +if eval "$MISSING --run true"; then + am_missing_run="$MISSING --run " +else + am_missing_run= + AC_MSG_WARN([`missing' script is too old or missing]) +fi +]) + +# Copyright (C) 2003, 2004, 2005, 2006 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_PROG_MKDIR_P +# --------------- +# Check for `mkdir -p'. +AC_DEFUN([AM_PROG_MKDIR_P], +[AC_PREREQ([2.60])dnl +AC_REQUIRE([AC_PROG_MKDIR_P])dnl +dnl Automake 1.8 to 1.9.6 used to define mkdir_p. We now use MKDIR_P, +dnl while keeping a definition of mkdir_p for backward compatibility. +dnl @MKDIR_P@ is magic: AC_OUTPUT adjusts its value for each Makefile. +dnl However we cannot define mkdir_p as $(MKDIR_P) for the sake of +dnl Makefile.ins that do not define MKDIR_P, so we do our own +dnl adjustment using top_builddir (which is defined more often than +dnl MKDIR_P). +AC_SUBST([mkdir_p], ["$MKDIR_P"])dnl +case $mkdir_p in + [[\\/$]]* | ?:[[\\/]]*) ;; + */*) mkdir_p="\$(top_builddir)/$mkdir_p" ;; +esac +]) + +# Helper functions for option handling. -*- Autoconf -*- + +# Copyright (C) 2001, 2002, 2003, 2005 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 3 + +# _AM_MANGLE_OPTION(NAME) +# ----------------------- +AC_DEFUN([_AM_MANGLE_OPTION], +[[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])]) + +# _AM_SET_OPTION(NAME) +# ------------------------------ +# Set option NAME. Presently that only means defining a flag for this option. +AC_DEFUN([_AM_SET_OPTION], +[m4_define(_AM_MANGLE_OPTION([$1]), 1)]) + +# _AM_SET_OPTIONS(OPTIONS) +# ---------------------------------- +# OPTIONS is a space-separated list of Automake options. +AC_DEFUN([_AM_SET_OPTIONS], +[AC_FOREACH([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])]) + +# _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET]) +# ------------------------------------------- +# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. +AC_DEFUN([_AM_IF_OPTION], +[m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])]) + +# Check to make sure that the build environment is sane. -*- Autoconf -*- + +# Copyright (C) 1996, 1997, 2000, 2001, 2003, 2005 +# Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 4 + +# AM_SANITY_CHECK +# --------------- +AC_DEFUN([AM_SANITY_CHECK], +[AC_MSG_CHECKING([whether build environment is sane]) +# Just in case +sleep 1 +echo timestamp > conftest.file +# Do `set' in a subshell so we don't clobber the current shell's +# arguments. Must try -L first in case configure is actually a +# symlink; some systems play weird games with the mod time of symlinks +# (eg FreeBSD returns the mod time of the symlink's containing +# directory). +if ( + set X `ls -Lt $srcdir/configure conftest.file 2> /dev/null` + if test "$[*]" = "X"; then + # -L didn't work. + set X `ls -t $srcdir/configure conftest.file` + fi + rm -f conftest.file + if test "$[*]" != "X $srcdir/configure conftest.file" \ + && test "$[*]" != "X conftest.file $srcdir/configure"; then + + # If neither matched, then we have a broken ls. This can happen + # if, for instance, CONFIG_SHELL is bash and it inherits a + # broken ls alias from the environment. This has actually + # happened. Such a system could not be considered "sane". + AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken +alias in your environment]) + fi + + test "$[2]" = conftest.file + ) +then + # Ok. + : +else + AC_MSG_ERROR([newly created file is older than distributed files! +Check your system clock]) +fi +AC_MSG_RESULT(yes)]) + +# Copyright (C) 2001, 2003, 2005 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_PROG_INSTALL_STRIP +# --------------------- +# One issue with vendor `install' (even GNU) is that you can't +# specify the program used to strip binaries. This is especially +# annoying in cross-compiling environments, where the build's strip +# is unlikely to handle the host's binaries. +# Fortunately install-sh will honor a STRIPPROG variable, so we +# always use install-sh in `make install-strip', and initialize +# STRIPPROG with the value of the STRIP variable (set by the user). +AC_DEFUN([AM_PROG_INSTALL_STRIP], +[AC_REQUIRE([AM_PROG_INSTALL_SH])dnl +# Installed binaries are usually stripped using `strip' when the user +# run `make install-strip'. However `strip' might not be the right +# tool to use in cross-compilation environments, therefore Automake +# will honor the `STRIP' environment variable to overrule this program. +dnl Don't test for $cross_compiling = yes, because it might be `maybe'. +if test "$cross_compiling" != no; then + AC_CHECK_TOOL([STRIP], [strip], :) +fi +INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" +AC_SUBST([INSTALL_STRIP_PROGRAM])]) + +# Copyright (C) 2006 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# _AM_SUBST_NOTMAKE(VARIABLE) +# --------------------------- +# Prevent Automake from outputting VARIABLE = @VARIABLE@ in Makefile.in. +# This macro is traced by Automake. +AC_DEFUN([_AM_SUBST_NOTMAKE]) + +# Check how to create a tarball. -*- Autoconf -*- + +# Copyright (C) 2004, 2005 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 2 + +# _AM_PROG_TAR(FORMAT) +# -------------------- +# Check how to create a tarball in format FORMAT. +# FORMAT should be one of `v7', `ustar', or `pax'. +# +# Substitute a variable $(am__tar) that is a command +# writing to stdout a FORMAT-tarball containing the directory +# $tardir. +# tardir=directory && $(am__tar) > result.tar +# +# Substitute a variable $(am__untar) that extract such +# a tarball read from stdin. +# $(am__untar) < result.tar +AC_DEFUN([_AM_PROG_TAR], +[# Always define AMTAR for backward compatibility. +AM_MISSING_PROG([AMTAR], [tar]) +m4_if([$1], [v7], + [am__tar='${AMTAR} chof - "$$tardir"'; am__untar='${AMTAR} xf -'], + [m4_case([$1], [ustar],, [pax],, + [m4_fatal([Unknown tar format])]) +AC_MSG_CHECKING([how to create a $1 tar archive]) +# Loop over all known methods to create a tar archive until one works. +_am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none' +_am_tools=${am_cv_prog_tar_$1-$_am_tools} +# Do not fold the above two line into one, because Tru64 sh and +# Solaris sh will not grok spaces in the rhs of `-'. +for _am_tool in $_am_tools +do + case $_am_tool in + gnutar) + for _am_tar in tar gnutar gtar; + do + AM_RUN_LOG([$_am_tar --version]) && break + done + am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"' + am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"' + am__untar="$_am_tar -xf -" + ;; + plaintar) + # Must skip GNU tar: if it does not support --format= it doesn't create + # ustar tarball either. + (tar --version) >/dev/null 2>&1 && continue + am__tar='tar chf - "$$tardir"' + am__tar_='tar chf - "$tardir"' + am__untar='tar xf -' + ;; + pax) + am__tar='pax -L -x $1 -w "$$tardir"' + am__tar_='pax -L -x $1 -w "$tardir"' + am__untar='pax -r' + ;; + cpio) + am__tar='find "$$tardir" -print | cpio -o -H $1 -L' + am__tar_='find "$tardir" -print | cpio -o -H $1 -L' + am__untar='cpio -i -H $1 -d' + ;; + none) + am__tar=false + am__tar_=false + am__untar=false + ;; + esac + + # If the value was cached, stop now. We just wanted to have am__tar + # and am__untar set. + test -n "${am_cv_prog_tar_$1}" && break + + # tar/untar a dummy directory, and stop if the command works + rm -rf conftest.dir + mkdir conftest.dir + echo GrepMe > conftest.dir/file + AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar]) + rm -rf conftest.dir + if test -s conftest.tar; then + AM_RUN_LOG([$am__untar /dev/null 2>&1 && break + fi +done +rm -rf conftest.dir + +AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool]) +AC_MSG_RESULT([$am_cv_prog_tar_$1])]) +AC_SUBST([am__tar]) +AC_SUBST([am__untar]) +]) # _AM_PROG_TAR + +m4_include([acinclude.m4]) diff --git a/amarok/COPYING-DOCS b/amarok/COPYING-DOCS new file mode 100644 index 00000000..4a0fe1c8 --- /dev/null +++ b/amarok/COPYING-DOCS @@ -0,0 +1,397 @@ + GNU Free Documentation License + Version 1.2, November 2002 + + + Copyright (C) 2000,2001,2002 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + +0. PREAMBLE + +The purpose of this License is to make a manual, textbook, or other +functional and useful document "free" in the sense of freedom: to +assure everyone the effective freedom to copy and redistribute it, +with or without modifying it, either commercially or noncommercially. +Secondarily, this License preserves for the author and publisher a way +to get credit for their work, while not being considered responsible +for modifications made by others. + +This License is a kind of "copyleft", which means that derivative +works of the document must themselves be free in the same sense. It +complements the GNU General Public License, which is a copyleft +license designed for free software. + +We have designed this License in order to use it for manuals for free +software, because free software needs free documentation: a free +program should come with manuals providing the same freedoms that the +software does. But this License is not limited to software manuals; +it can be used for any textual work, regardless of subject matter or +whether it is published as a printed book. We recommend this License +principally for works whose purpose is instruction or reference. + + +1. APPLICABILITY AND DEFINITIONS + +This License applies to any manual or other work, in any medium, that +contains a notice placed by the copyright holder saying it can be +distributed under the terms of this License. Such a notice grants a +world-wide, royalty-free license, unlimited in duration, to use that +work under the conditions stated herein. The "Document", below, +refers to any such manual or work. Any member of the public is a +licensee, and is addressed as "you". You accept the license if you +copy, modify or distribute the work in a way requiring permission +under copyright law. + +A "Modified Version" of the Document means any work containing the +Document or a portion of it, either copied verbatim, or with +modifications and/or translated into another language. + +A "Secondary Section" is a named appendix or a front-matter section of +the Document that deals exclusively with the relationship of the +publishers or authors of the Document to the Document's overall subject +(or to related matters) and contains nothing that could fall directly +within that overall subject. (Thus, if the Document is in part a +textbook of mathematics, a Secondary Section may not explain any +mathematics.) The relationship could be a matter of historical +connection with the subject or with related matters, or of legal, +commercial, philosophical, ethical or political position regarding +them. + +The "Invariant Sections" are certain Secondary Sections whose titles +are designated, as being those of Invariant Sections, in the notice +that says that the Document is released under this License. If a +section does not fit the above definition of Secondary then it is not +allowed to be designated as Invariant. The Document may contain zero +Invariant Sections. If the Document does not identify any Invariant +Sections then there are none. + +The "Cover Texts" are certain short passages of text that are listed, +as Front-Cover Texts or Back-Cover Texts, in the notice that says that +the Document is released under this License. A Front-Cover Text may +be at most 5 words, and a Back-Cover Text may be at most 25 words. + +A "Transparent" copy of the Document means a machine-readable copy, +represented in a format whose specification is available to the +general public, that is suitable for revising the document +straightforwardly with generic text editors or (for images composed of +pixels) generic paint programs or (for drawings) some widely available +drawing editor, and that is suitable for input to text formatters or +for automatic translation to a variety of formats suitable for input +to text formatters. A copy made in an otherwise Transparent file +format whose markup, or absence of markup, has been arranged to thwart +or discourage subsequent modification by readers is not Transparent. +An image format is not Transparent if used for any substantial amount +of text. A copy that is not "Transparent" is called "Opaque". + +Examples of suitable formats for Transparent copies include plain +ASCII without markup, Texinfo input format, LaTeX input format, SGML +or XML using a publicly available DTD, and standard-conforming simple +HTML, PostScript or PDF designed for human modification. Examples of +transparent image formats include PNG, XCF and JPG. Opaque formats +include proprietary formats that can be read and edited only by +proprietary word processors, SGML or XML for which the DTD and/or +processing tools are not generally available, and the +machine-generated HTML, PostScript or PDF produced by some word +processors for output purposes only. + +The "Title Page" means, for a printed book, the title page itself, +plus such following pages as are needed to hold, legibly, the material +this License requires to appear in the title page. For works in +formats which do not have any title page as such, "Title Page" means +the text near the most prominent appearance of the work's title, +preceding the beginning of the body of the text. + +A section "Entitled XYZ" means a named subunit of the Document whose +title either is precisely XYZ or contains XYZ in parentheses following +text that translates XYZ in another language. (Here XYZ stands for a +specific section name mentioned below, such as "Acknowledgements", +"Dedications", "Endorsements", or "History".) To "Preserve the Title" +of such a section when you modify the Document means that it remains a +section "Entitled XYZ" according to this definition. + +The Document may include Warranty Disclaimers next to the notice which +states that this License applies to the Document. These Warranty +Disclaimers are considered to be included by reference in this +License, but only as regards disclaiming warranties: any other +implication that these Warranty Disclaimers may have is void and has +no effect on the meaning of this License. + + +2. VERBATIM COPYING + +You may copy and distribute the Document in any medium, either +commercially or noncommercially, provided that this License, the +copyright notices, and the license notice saying this License applies +to the Document are reproduced in all copies, and that you add no other +conditions whatsoever to those of this License. You may not use +technical measures to obstruct or control the reading or further +copying of the copies you make or distribute. However, you may accept +compensation in exchange for copies. If you distribute a large enough +number of copies you must also follow the conditions in section 3. + +You may also lend copies, under the same conditions stated above, and +you may publicly display copies. + + +3. COPYING IN QUANTITY + +If you publish printed copies (or copies in media that commonly have +printed covers) of the Document, numbering more than 100, and the +Document's license notice requires Cover Texts, you must enclose the +copies in covers that carry, clearly and legibly, all these Cover +Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on +the back cover. Both covers must also clearly and legibly identify +you as the publisher of these copies. The front cover must present +the full title with all words of the title equally prominent and +visible. You may add other material on the covers in addition. +Copying with changes limited to the covers, as long as they preserve +the title of the Document and satisfy these conditions, can be treated +as verbatim copying in other respects. + +If the required texts for either cover are too voluminous to fit +legibly, you should put the first ones listed (as many as fit +reasonably) on the actual cover, and continue the rest onto adjacent +pages. + +If you publish or distribute Opaque copies of the Document numbering +more than 100, you must either include a machine-readable Transparent +copy along with each Opaque copy, or state in or with each Opaque copy +a computer-network location from which the general network-using +public has access to download using public-standard network protocols +a complete Transparent copy of the Document, free of added material. +If you use the latter option, you must take reasonably prudent steps, +when you begin distribution of Opaque copies in quantity, to ensure +that this Transparent copy will remain thus accessible at the stated +location until at least one year after the last time you distribute an +Opaque copy (directly or through your agents or retailers) of that +edition to the public. + +It is requested, but not required, that you contact the authors of the +Document well before redistributing any large number of copies, to give +them a chance to provide you with an updated version of the Document. + + +4. MODIFICATIONS + +You may copy and distribute a Modified Version of the Document under +the conditions of sections 2 and 3 above, provided that you release +the Modified Version under precisely this License, with the Modified +Version filling the role of the Document, thus licensing distribution +and modification of the Modified Version to whoever possesses a copy +of it. In addition, you must do these things in the Modified Version: + +A. Use in the Title Page (and on the covers, if any) a title distinct + from that of the Document, and from those of previous versions + (which should, if there were any, be listed in the History section + of the Document). You may use the same title as a previous version + if the original publisher of that version gives permission. +B. List on the Title Page, as authors, one or more persons or entities + responsible for authorship of the modifications in the Modified + Version, together with at least five of the principal authors of the + Document (all of its principal authors, if it has fewer than five), + unless they release you from this requirement. +C. State on the Title page the name of the publisher of the + Modified Version, as the publisher. +D. Preserve all the copyright notices of the Document. +E. Add an appropriate copyright notice for your modifications + adjacent to the other copyright notices. +F. Include, immediately after the copyright notices, a license notice + giving the public permission to use the Modified Version under the + terms of this License, in the form shown in the Addendum below. +G. Preserve in that license notice the full lists of Invariant Sections + and required Cover Texts given in the Document's license notice. +H. Include an unaltered copy of this License. +I. Preserve the section Entitled "History", Preserve its Title, and add + to it an item stating at least the title, year, new authors, and + publisher of the Modified Version as given on the Title Page. If + there is no section Entitled "History" in the Document, create one + stating the title, year, authors, and publisher of the Document as + given on its Title Page, then add an item describing the Modified + Version as stated in the previous sentence. +J. Preserve the network location, if any, given in the Document for + public access to a Transparent copy of the Document, and likewise + the network locations given in the Document for previous versions + it was based on. These may be placed in the "History" section. + You may omit a network location for a work that was published at + least four years before the Document itself, or if the original + publisher of the version it refers to gives permission. +K. For any section Entitled "Acknowledgements" or "Dedications", + Preserve the Title of the section, and preserve in the section all + the substance and tone of each of the contributor acknowledgements + and/or dedications given therein. +L. Preserve all the Invariant Sections of the Document, + unaltered in their text and in their titles. Section numbers + or the equivalent are not considered part of the section titles. +M. Delete any section Entitled "Endorsements". Such a section + may not be included in the Modified Version. +N. Do not retitle any existing section to be Entitled "Endorsements" + or to conflict in title with any Invariant Section. +O. Preserve any Warranty Disclaimers. + +If the Modified Version includes new front-matter sections or +appendices that qualify as Secondary Sections and contain no material +copied from the Document, you may at your option designate some or all +of these sections as invariant. To do this, add their titles to the +list of Invariant Sections in the Modified Version's license notice. +These titles must be distinct from any other section titles. + +You may add a section Entitled "Endorsements", provided it contains +nothing but endorsements of your Modified Version by various +parties--for example, statements of peer review or that the text has +been approved by an organization as the authoritative definition of a +standard. + +You may add a passage of up to five words as a Front-Cover Text, and a +passage of up to 25 words as a Back-Cover Text, to the end of the list +of Cover Texts in the Modified Version. Only one passage of +Front-Cover Text and one of Back-Cover Text may be added by (or +through arrangements made by) any one entity. If the Document already +includes a cover text for the same cover, previously added by you or +by arrangement made by the same entity you are acting on behalf of, +you may not add another; but you may replace the old one, on explicit +permission from the previous publisher that added the old one. + +The author(s) and publisher(s) of the Document do not by this License +give permission to use their names for publicity for or to assert or +imply endorsement of any Modified Version. + + +5. COMBINING DOCUMENTS + +You may combine the Document with other documents released under this +License, under the terms defined in section 4 above for modified +versions, provided that you include in the combination all of the +Invariant Sections of all of the original documents, unmodified, and +list them all as Invariant Sections of your combined work in its +license notice, and that you preserve all their Warranty Disclaimers. + +The combined work need only contain one copy of this License, and +multiple identical Invariant Sections may be replaced with a single +copy. If there are multiple Invariant Sections with the same name but +different contents, make the title of each such section unique by +adding at the end of it, in parentheses, the name of the original +author or publisher of that section if known, or else a unique number. +Make the same adjustment to the section titles in the list of +Invariant Sections in the license notice of the combined work. + +In the combination, you must combine any sections Entitled "History" +in the various original documents, forming one section Entitled +"History"; likewise combine any sections Entitled "Acknowledgements", +and any sections Entitled "Dedications". You must delete all sections +Entitled "Endorsements". + + +6. COLLECTIONS OF DOCUMENTS + +You may make a collection consisting of the Document and other documents +released under this License, and replace the individual copies of this +License in the various documents with a single copy that is included in +the collection, provided that you follow the rules of this License for +verbatim copying of each of the documents in all other respects. + +You may extract a single document from such a collection, and distribute +it individually under this License, provided you insert a copy of this +License into the extracted document, and follow this License in all +other respects regarding verbatim copying of that document. + + +7. AGGREGATION WITH INDEPENDENT WORKS + +A compilation of the Document or its derivatives with other separate +and independent documents or works, in or on a volume of a storage or +distribution medium, is called an "aggregate" if the copyright +resulting from the compilation is not used to limit the legal rights +of the compilation's users beyond what the individual works permit. +When the Document is included in an aggregate, this License does not +apply to the other works in the aggregate which are not themselves +derivative works of the Document. + +If the Cover Text requirement of section 3 is applicable to these +copies of the Document, then if the Document is less than one half of +the entire aggregate, the Document's Cover Texts may be placed on +covers that bracket the Document within the aggregate, or the +electronic equivalent of covers if the Document is in electronic form. +Otherwise they must appear on printed covers that bracket the whole +aggregate. + + +8. TRANSLATION + +Translation is considered a kind of modification, so you may +distribute translations of the Document under the terms of section 4. +Replacing Invariant Sections with translations requires special +permission from their copyright holders, but you may include +translations of some or all Invariant Sections in addition to the +original versions of these Invariant Sections. You may include a +translation of this License, and all the license notices in the +Document, and any Warranty Disclaimers, provided that you also include +the original English version of this License and the original versions +of those notices and disclaimers. In case of a disagreement between +the translation and the original version of this License or a notice +or disclaimer, the original version will prevail. + +If a section in the Document is Entitled "Acknowledgements", +"Dedications", or "History", the requirement (section 4) to Preserve +its Title (section 1) will typically require changing the actual +title. + + +9. TERMINATION + +You may not copy, modify, sublicense, or distribute the Document except +as expressly provided for under this License. Any other attempt to +copy, modify, sublicense or distribute the Document is void, and will +automatically terminate your rights under this License. However, +parties who have received copies, or rights, from you under this +License will not have their licenses terminated so long as such +parties remain in full compliance. + + +10. FUTURE REVISIONS OF THIS LICENSE + +The Free Software Foundation may publish new, revised versions +of the GNU Free Documentation License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. See +http://www.gnu.org/copyleft/. + +Each version of the License is given a distinguishing version number. +If the Document specifies that a particular numbered version of this +License "or any later version" applies to it, you have the option of +following the terms and conditions either of that specified version or +of any later version that has been published (not as a draft) by the +Free Software Foundation. If the Document does not specify a version +number of this License, you may choose any version ever published (not +as a draft) by the Free Software Foundation. + + +ADDENDUM: How to use this License for your documents + +To use this License in a document you have written, include a copy of +the License in the document and put the following copyright and +license notices just after the title page: + + Copyright (c) YEAR YOUR NAME. + Permission is granted to copy, distribute and/or modify this document + under the terms of the GNU Free Documentation License, Version 1.2 + or any later version published by the Free Software Foundation; + with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. + A copy of the license is included in the section entitled "GNU + Free Documentation License". + +If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts, +replace the "with...Texts." line with this: + + with the Invariant Sections being LIST THEIR TITLES, with the + Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST. + +If you have Invariant Sections without Cover Texts, or some other +combination of the three, merge those two alternatives to suit the +situation. + +If your document contains nontrivial examples of program code, we +recommend releasing these examples in parallel under your choice of +free software license, such as the GNU General Public License, +to permit their use in free software. diff --git a/amarok/COPYING.LIB b/amarok/COPYING.LIB new file mode 100644 index 00000000..2676d08a --- /dev/null +++ b/amarok/COPYING.LIB @@ -0,0 +1,481 @@ + GNU LIBRARY GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1991 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the library GPL. It is + numbered 2 because it goes with version 2 of the ordinary GPL.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Library General Public License, applies to some +specially designated Free Software Foundation software, and to any +other libraries whose authors decide to use it. You can use it for +your libraries, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if +you distribute copies of the library, or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link a program with the library, you must provide +complete object files to the recipients so that they can relink them +with the library, after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + Our method of protecting your rights has two steps: (1) copyright +the library, and (2) offer you this license which gives you legal +permission to copy, distribute and/or modify the library. + + Also, for each distributor's protection, we want to make certain +that everyone understands that there is no warranty for this free +library. If the library is modified by someone else and passed on, we +want its recipients to know that what they have is not the original +version, so that any problems introduced by others will not reflect on +the original authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that companies distributing free +software will individually obtain patent licenses, thus in effect +transforming the program into proprietary software. To prevent this, +we have made it clear that any patent must be licensed for everyone's +free use or not licensed at all. + + Most GNU software, including some libraries, is covered by the ordinary +GNU General Public License, which was designed for utility programs. This +license, the GNU Library General Public License, applies to certain +designated libraries. This license is quite different from the ordinary +one; be sure to read it in full, and don't assume that anything in it is +the same as in the ordinary license. + + The reason we have a separate public license for some libraries is that +they blur the distinction we usually make between modifying or adding to a +program and simply using it. Linking a program with a library, without +changing the library, is in some sense simply using the library, and is +analogous to running a utility program or application program. However, in +a textual and legal sense, the linked executable is a combined work, a +derivative of the original library, and the ordinary General Public License +treats it as such. + + Because of this blurred distinction, using the ordinary General +Public License for libraries did not effectively promote software +sharing, because most developers did not use the libraries. We +concluded that weaker conditions might promote sharing better. + + However, unrestricted linking of non-free programs would deprive the +users of those programs of all benefit from the free status of the +libraries themselves. This Library General Public License is intended to +permit developers of non-free programs to use free libraries, while +preserving your freedom as a user of such programs to change the free +libraries that are incorporated in them. (We have not seen how to achieve +this as regards changes in header files, but we have achieved it as regards +changes in the actual functions of the Library.) The hope is that this +will lead to faster development of free libraries. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, while the latter only +works together with the library. + + Note that it is possible for a library to be covered by the ordinary +General Public License rather than by this special one. + + GNU LIBRARY GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library which +contains a notice placed by the copyright holder or other authorized +party saying it may be distributed under the terms of this Library +General Public License (also called "this License"). Each licensee is +addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also compile or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + c) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + d) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the source code distributed need not include anything that is normally +distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Library General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/amarok/HACKING b/amarok/HACKING new file mode 100644 index 00000000..5050ce26 --- /dev/null +++ b/amarok/HACKING @@ -0,0 +1,269 @@ +Hacking on Amarok +----------------- + +Please respect these guidelines when coding for Amarok, thanks! + +* Where this document isn't clear, refer to Amarok code. + + +This C++ FAQ is a life saver in many situations, so you want to keep it handy: + + http://www.parashift.com/c++-faq-lite/ + + +Formatting +---------- +* Spaces, not tabs +* Indentation is 4 spaces +* Lines should be limited to 90 characters +* Spaces between brackets and argument functions +* For pointer and reference variable declarations put a space between the type + and the * or & and no space before the variable name. +* For if, else, while and similar statements put the brackets on the next line, + although brackets are not needed for single statements. +* Function and class definitions have their brackets on separate lines +* A function implementation's return type is on its own line. +* amarok.h contains some helpful macros, foreach and foreachType. Use them, + they improve coding style and readability. + +Example: + + | bool + | MyClass::myMethod( QPtrList items, const QString &name ) + | { + | if( items.isEmpty() ) + | return false; + | + | foreachType( QPtrList, items ) + | { + | (*it)->setText( 0, name ); + | debug() << "Setting item name: " << name << endl; + | } + | } + +Header includes should be listed in the following order: + - Amarok includes + - KDE includes + - Qt includes + +They should also be sorted alphabetically, for ease of locating them. A small comment +if applicable is also helpful. + +Includes in a header file should be kept to the absolute minimum, as to keep compile times +low. This can be achieved by using "forward declarations" instead of includes, like +"class QListView;" Forward declarations work for pointers and const references. + +TIP: +Kate/KDevelop users can sort the headers automatically. Select the lines you want to sort, +then Tools -> Filter Selection Through Command -> "sort". + + +Example: + + | #include "amarok.h" + | #include "debug.h" + | #include "playlist.h" + | + | #include "kdialogbase.h" //baseclass + | #include "kpushbutton.h" //see function... + | + | #include "qlistviewitem.h" + | #include "qwidget.h" + + +Comments +-------- +Comment your code. Don't comment what the code does, comment on the purpose of the code. It's +good for others reading your code, and ultimately it's good for you too. + +Comments are essential when adding a strange hack, like the following example: + + | /** Due to xine-lib, we have to make KProcess close all fds, otherwise we get "device is busy" messages + | * Used by AmarokProcIO and AmarokProcess, exploiting commSetupDoneC(), a virtual method that + | * happens to be called in the forked process + | * See bug #103750 for more information. + | */ + | class AmarokProcIO : public KProcIO + | { + | public: + | virtual int commSetupDoneC() { + | const int i = KProcIO::commSetupDoneC(); + | Amarok::closeOpenFiles(KProcIO::out[0],KProcIO::in[0],KProcIO::err[0]); + | return i; + | } + | }; + + +For headers, use the Doxygen syntax. See: http://www.stack.nl/~dimitri/doxygen/ + +Example: + + | /** + | * Start playback. + | * @param offset Start playing at @p msec position. + | * @return True for success. + | */ + | virtual bool play( uint offset = 0 ) = 0; + + +Header Formatting +----------------- +General rules apply here. Please keep header function definitions aligned nicely, +if possible. It helps greatly when looking through the code. Sorted methods, +either by name or by their function (ie, group all related methods together) is +great too. + + + | #ifndef AMAROK_QUEUEMANAGER_H + | #define AMAROK_QUEUEMANAGER_H + + | class QueueList : public KListView + | { + | Q_OBJECT + | + | public: + | Queuelist( QWidget *parent, const char *name = 0 ); + | ~QueueList() {}; + | + | public slots: + | void moveSelectedUp(); + | void moveSelectedDown(); + | }; + +#endif /* AMAROK_QUEUEMANAGER_H */ + + +0 vs NULL +--------- +The use of 0 to express a null pointer is preferred over the use of NULL. +0 is not a magic value, it's the defined value of the null pointer in C++. +NULL, on the other hand, is a preprocessor directive (#define) and not only is +it more typing than '0' but preprocessor directives are less elegant. + + | SomeClass *instance = 0; + + +Const Correctness +----------------- +Try to keep your code const correct. Declare methods const if they don't mutate the object, +and use const variables. It improves safety, and also makes it easier to understand the code. + +See: http://www.parashift.com/c++-faq-lite/const-correctness.html + + +Example: + + | bool + | MyClass::isValidFile( const QString& path ) const + | { + | const bool valid = QFile::exist( path ); + | + | return valid; + | } + + +Debugging +--------- +debug.h contains some handy functions for our debug console output. +Please use them instead of kdDebug(). + +Usage: + + | #include "debug.h" + | + | debug() << "Something is happening" << endl; + | warning() << "Something bad may happen" << endl; + | error() << "Something bad did happen!" << endl; + +Additionally, there are some macros for debugging functions: + +DEBUG_BLOCK +DEBUG_FUNC_INFO +DEBUG_LINE_INFO +DEBUG_INDENT +DEBUG_UNINDENT + +AMAROK_NOTIMPLEMENTED +AMAROK_DEPRECATED + +threadweaver.h has two additional macros: +DEBUG_THREAD_FUNC_INFO outputs the memory address of the current QThread or 'none' + if its the original GUI thread. +SHOULD_BE_GUI outputs a warning message if it occurs in a thread that isn't in + the original "GUI Thread", otherwise it is silent. Useful for documenting + functions and to prevent problems in the future. + + +Usage of Amarok::config() +------------------------- +We provide this method for convenience, but it is important to use it properly. By +inspection, we can see that we may produce very obscure bugs in the wrong case: + + | KConfig + | *config( const QString &group ) + | { + | //Slightly more useful config() that allows setting the group simultaneously + | kapp->config()->setGroup( group ); + | return kapp->config(); + | } + +Take the following example: + + | void + | f1() + | { + | KConfig *config = Amarok::config( "Group 2" ); + | config->writeEntry( "Group 2 Variable", true ); + | } + | + | void + | doStuff() + | { + | KConfig *config = Amarok::config( "Group 1" ); + | f1(); + | config->writeEntry( "Group 1 Variable", true ); + | } + +We would expect the following results: + + | [Group 1] + | Group 1 Variable = true + | + | [Group 2] + | Group 2 Variable = true + +However because the config group is changed before writing the entry: + | [Group 1] + | + | [Group 2] + | Group 1 Variable = true + | Group 2 Variable = true + +Which is clearly incorrect. And hard to see when your wondering why f1() is not +working. So do not store a value of Amarok::config, make it a habit to just +always call writeEntry or readEntry directly. + +Correct: +| amarok::config( "Group 1" )->writeEntry( "Group 1 Variable", true ); + + +Errors & Asserts +---------------- +*Never use assert() or fatal(). There must be a better option than crashing a user's +application (its not uncommon for end-users to have debugging enabled). + +*KMessageBox is fine to use to prompt the user, but do not use it to display errors +or informational messages. Instead, KDE::StatusBar has a few handy methods. Refer to +amarok/src/statusbar/statusBarBase.h + + +Copyright +--------- +To comply with the GPL, add your name, email address & the year to the top of any file +that you edit. If you bring in code or files from elsewhere, make sure its +GPL-compatible and to put the authors name, email & copyright year to the top of +those files. + + +Thanks, now have fun! + -- the Amarok developers diff --git a/amarok/Makefile.am b/amarok/Makefile.am new file mode 100644 index 00000000..2ba68323 --- /dev/null +++ b/amarok/Makefile.am @@ -0,0 +1,17 @@ +SUBDIRS = \ + src + +EXTRA_DIST = \ + AUTHORS \ + COPYING \ + ChangeLog \ + INSTALL \ + README \ + TODO \ + admin \ + amarok.kdevprj \ + amarok.lsm + +AUTOMAKE_OPTIONS = \ + foreign + diff --git a/amarok/VIS_PLAN b/amarok/VIS_PLAN new file mode 100644 index 00000000..f059593c --- /dev/null +++ b/amarok/VIS_PLAN @@ -0,0 +1,163 @@ +amaroK VIS_PLAN - Created: 22-11-2003 + + +Abstract +======== +In order to create a visualisation framework that is superior to anything +popular available for Linux today we should plan amaroK's thoroughly +before implementation. This document exists to map out our ideas and is +available for anyone to edit, please, if you have some good suggestion, or +experience with this kind of work, add some comments! Thanks, Max Howell + + +Formatting +========== +The document has no regimented formatting, but do try to keep the wordwrap set +the same! Feel free to recommend formatting if the document becomes +unmanageable. + + +Plan +==== +My initial thoughts were to make visualisations a separate process rather than +separate thread. The reasoning being, a crash in the vis won't crash amaroK, +threads can be unmanageable, *nix is good for new processes, we can use DCOP to +control the vis and presumable we just need to pass a handle to the relevant +arts server to the vis and leave it. Since the music playing bit is already a +separate process it makes sense to make the visualisation separate too! Also +this may mean that these visuals could be separate from amaroK and just depend +on KDE. + +I was also thinking that this means it may be possible to not use Qt to +do rendering. There are probably better libraries for high speed graphics and if +there isn't a way to make DCOP not depend on a QApplication, then maybe we could +write something. It would be sorted if we could make something that can react to +arts sound output in general. + we could use SDL for 2d and OpenGL for 3d rendering. + +I was also thinking it would be neat to embed the visuals somehow into amaroK, +so as the background in the playlist perhaps (I know, this would probably not +work well due to the need to re-render all the AA text all the time, but still +the idea is neat) This is similar to Sonique on Windows if anyone has ever used +it. + +If we make the visualisations not amaroK specific it may be possible to embed +visualisations into the Kicker, onto the desktop, etc. + +Do we want more than an FFT of the audio data? Would other data +be useful to visuals at all? Beat detection certainly would be a useful thing to +offer the visualisations, and it would certainly stop every visual implementing +its own detection. + + +Vis Process Architecture +======================== +I suggest having a separate process for each visualization. Each vis process would +only load one visualization plugin. This way we have good crash safety and it's also +possible to develop and ship plugins separately from amaroK. So we have one "vis loader +skeleton" in amaroK, which will be instanciated whenever we need a vis. This loader could +get started with the vis plugin as cli argument. It would also contain all the socket and +interface needed to communicate with amaroK. + +We should take care to keep the design as simple as possible. + + Why make visualisations plugins as well as binaries? It seems sensible to have +visualisations as separate processes and then communicate with amK through the socket. +Why have the extra layer of plugins if you're going to force each process to only load +one plugin anyway? + + +Merging Analyzer and Vis +======================== +If we decide on unifying vis and analyzer, the question arises how to get the rendered +bitmap graphic back into amaroK's playerwidget. It seems we have the following options: + +* Writing it into a shared memory buffer. amaroK must then read the buffer and bitBlt() + the pixmap into the framebuffer +* Transferring the pixmap over a Unix socket (or UDP network socket). This can amount to + several megabytes per second. After the transfer amaroK must bitBlt() it to the + framebuffer. +* Writing it directly into the framebuffer at the desired position, like a video overlay. + There might be technical problems doing so, and it will be difficult when the user + moves the window. + + + +[Comment from muesli, to be integrated into the paragraphs above] + +imho, it would be cool to offer this service via sockets. udp would fit per- +fectly, since it has to be realtime, anyways (better loose a packet and show +nothing, instead of a out-of-sync beat). e.g., you would be able to have +various of visualizations-clients connected to amarok, showing different +animations, at the same time. this would also remove the DCOP-dependency-chains +for the OpenGL clients. + +right now, i can imagine two implementation methods: + +A) transmit the frequency vector of the analyzer to the vis-client. beat-detection and +all that stuff gets done by the vis-client. + +B) basic beat-detection and music analysis gets done by amarok itself. the +results of that process are sent to the vis-clients. + + IMO it would be good to offer services like beat detection. This +way vis-writers have less to do and are more likely to write a vis since +beat-detection is already done, also it means beat-detection is likely to be better +as we will over time offer a really good implementation, and it means that if the user +has many visuals running, the beat-detection is only done once for all visualisations. + +[/Comment by muesli] + + + + +//FIXME beat detection code temporarily moved here + + /* + // Muesli's Beat Detection - A Night's Oddysee + // shift old elements + for ( uint x = 0; x < 18; ++x ) + for ( uint y = 42; y > 0; --y ) m_beatEnergy[x][y] = m_beatEnergy[x][y - 1]; + + // get current energy values + for ( uint x = 0; x < pScopeVector->size(); ++x ) m_beatEnergy[x][0] = pScopeVector->at(x); + + // compare to old elements and get averages + double beatAvg[18]; + // double beatVariance[18]; + // double beatMood[18]; + + for ( uint x = 0; x < 18; ++x ) + { + beatAvg[x] = 0; + for ( uint y = 1; y < 44; ++y ) beatAvg[x] += m_beatEnergy[x][y]; + + beatAvg[x] = beatAvg[x] / 43; + } + */ + /* for ( uint x = 0; x < 18; ++x ) + { + beatVariance[x] = 0; + for ( uint y = 0; y < 42; ++y ) beatVariance[x] += (pow((m_beatEnergy[x][y] - beatAvg[x]), 2) / 43); + } + + for ( uint x = 0; x < 18; ++x ) + beatMood[x] = (-0.0025714 * beatVariance[x]) + 1.5142857; + */ + + // do we have a beat? let's dance! + /* int total_hits = 0; + for ( uint x = 0; x < 18; ++x ) + { + double factor = cos( x * 4 ) * 18; + factor = beatAvg[x] * factor; + + if ( m_beatEnergy[x][0] > factor ) + { + total_hits++; + kdDebug() << "*CLAP* factor: " << factor << " - x: " << x << " - average energy: " << beatAvg[x] << " - current peak: " << m_beatEnergy[x][0] << endl; + } + } + + if ( total_hits > 3 ) kdDebug() << "***CLAPCLAPCLAP***" << endl; + */ diff --git a/amarok/amarok.kdevelop b/amarok/amarok.kdevelop new file mode 100644 index 00000000..beb9be09 --- /dev/null +++ b/amarok/amarok.kdevelop @@ -0,0 +1,239 @@ + + + + Mark Kretschmann + markey@web.de + KDevKDEAutoProject + C++ + + KDE + Qt + + . + false + + + + + + amarok + + + + amarok + default + src/magnatunebrowser/libmagnatunebrowser.la + true + + + \s--build=i386-linux --host=i386-linux --target=i386-linux\s + + + false + 1 + false + unsermake + + + + + 0 + false + + + + \s-O0 -g3 -Wall + + + + + false + false + + build + /usr/kde/cvs/bin/ + + + + + + + --enable-debug=full + + + + + kdevgccoptions + kdevgppoptions + kdevpgf77options + + + + + + + + + + + + + + + + + + + + + + false + false + + + false + *.o,*.lo,CVS + true + + + + + gtk + gnustep + python + php + perl + + + /home/mark/mysource/gideon/amarok-0.5.2-gideon/html/ + /home/mark/mysource/gideon/amarok-0.5.2-gideon/html/ + + + + + + + + + /usr/bin/libtool + + + true + false + false + true + + + + + + false + true + 10 + + + + + + + + + false + + + .h + .cpp + true + + + + + true + true + true + false + true + true + 100 + 100 + true + 250 + false + 0 + true + true + false + std=_GLIBCXX_STD;__gnu_cxx=std + true + false + false + false + true + true + true + false + .; + + + + + set + m_,_ + theValue + true + true + + + true + 3 + /usr/qt/3 + 3 + EmbeddedKDevDesigner + /usr/qt/3/bin/qmake + /usr/qt/3/bin/designer + + + + false + true + Vertical + + + + + + + true + 2 + + + -f + + + + -dP + -f + -C -d -P + -u3 -p + + + + true + true + true + true + -C + + + + + + + + + + + /home/ian/workspace/local/amarok/tags + + + diff --git a/amarok/configure.in.bot b/amarok/configure.in.bot new file mode 100644 index 00000000..acf9d76d --- /dev/null +++ b/amarok/configure.in.bot @@ -0,0 +1,336 @@ +# configure.in.bot +# This file is used for printing important messages at the end of configure + +echo "" + +if test x$amarok_error_notaglib = xyes; then + echo " ========================" + echo " === Amarok - ERROR ==========================================================" + echo " ========================" + echo " =" + echo " = Amarok cannot be built because, either the TagLib library is not installed," + echo " = or if relevant, the taglib-devel package is not installed." + echo " = TagLib can be obtained from: http://ktown.kde.org/~wheeler/taglib/" + echo " =" +fi + +if test x$amarok_error_taglibold = xyes; then + echo " ========================" + echo " === Amarok - ERROR ==========================================================" + echo " ========================" + echo " =" + echo " = Amarok cannot be built because your TagLib version is too old. Please obtain" + echo " = the version $TAGLIB_REQ_VERSION from:" + echo " = http://ktown.kde.org/~wheeler/taglib/" + echo " =" +fi + +if test x$amarok_error_noruby = xyes; then + echo " ==========================" + echo " === Amarok - ERROR ==========================================================" + echo " ==========================" + echo " =" + echo " = The Ruby programming language is not installed. Please obtain Ruby" + echo " = (version 1.8 or later) from http://ruby-lang.org, or install a distribution" + echo " = package. To build Amarok requires the Ruby header files as well, which some" + echo " = distributions package separately." + echo " =" +fi + +if test x$amarok_warning_xineold = xyes; then + echo " ==========================" + echo " === Amarok - WARNING ========================================================" + echo " ==========================" + echo " =" + echo " = Amarok requires xine-lib version: 1.0-rc4" + echo " = Amarok will still be built, but you must use another sound-engine." + echo " =" +fi + +if test x$PKGCONFIGFOUND != xyes; then + echo " ==========================" + echo " === Amarok - WARNING ========================================================" + echo " ==========================" + echo " =" + echo " = pkg-config could not be found, this means some optional components (eg the" + echo " = GStreamer-engine) cannot be built." + echo " = See README for help with this issue." + echo " =" +fi + +#if test x$amarok_warning_mas_notfound = xyes; then +# echo " ==========================" +# echo " === Amarok - WARNING ========================================================" +# echo " ==========================" +# echo " =" +# echo " = mas-config could not be found, this means that MAS-engine" +# echo " = cannot be built." +# echo " =" +#fi + +if test x$build_xine = xno; then + echo " ==========================" + echo " === Amarok - WARNING ========================================================" + echo " ==========================" + echo " =" + echo " = The recommended xine-engine will not be built. If you want to use the" + echo " = powerful xine multimedia framework with Amarok, please download xine-lib" + echo " = version $xine_version_min or higher from http://xinehq.de/" + echo " =" +fi + +if test x$have_gst_plugins = xno -a x$have_gst = xyes; then + echo " ==========================" + echo " === Amarok - WARNING ========================================================" + echo " ==========================" + echo " =" + echo " = No GStreamer plugins were detected!" + echo " = Without plugins you will not be able to play any media using the" + echo " = GStreamer-engine! You need at least the MP3 plugin and a sink plugin, (eg." + echo " = ALSAsink). Please refer to http://gstreamer.freedesktop.org/" + echo " = NOTE: you will still be able to play media with another engine plugin." + echo " =" +fi + +if test x$included_sqlite = xno; then + if test x$have_sqlite = xyes; then + echo " ==========================" + echo " === Amarok - WARNING ========================================================" + echo " ==========================" + echo " =" + echo " = You have passed the --without-included-sqlite option to configure, which" + echo " = means that SQLite will be dynamically linked instead of statically linked." + echo " = IMPORTANT: you must ensure the libsqlite.so library in your system is" + echo " = threadsafe!!! Amarok will not be stable otherwise." + echo " =" + else + echo " ========================" + echo " === Amarok - ERROR ==========================================================" + echo " ========================" + echo " =" + echo " = You have passed the --without-included-sqlite option to configure, but" + echo " = the development files for SQLite could not be found. Please make sure you" + echo " = have the relevant package installed or, even better, use the included" + echo " = sqlite (unless you *really* know what you're doing, of course)." + echo " =" + fi +fi + +if test x$amarok_warning_mysql_notfound = xyes; then + echo " ==========================" + echo " === Amarok - WARNING ========================================================" + echo " ==========================" + echo " =" + echo " = mysql_config could not be found, this means that support for MySql" + echo " = will be disabled." + echo " =" +fi + +if test x$amarok_warning_postgresql_notfound = xyes; then + echo " ==========================" + echo " === Amarok - WARNING ========================================================" + echo " ==========================" + echo " =" + echo " = pg_config could not be found, this means that support for Postgresql" + echo " = will be disabled." + echo " =" +fi + +if test x$no_engine = xyes; then + all_tests=bad + echo " ==================================" + echo " === AMAROK WILL NOT BE BUILT ================================================" + echo " ==================================" + echo " =" + echo " = No suitable multimedia framework was detected. You need to install at least" + echo " = the Xine or Helix framework as detailed in the Amarok README." + echo " =" +fi + +if test x$no_amarok = xyes; then + all_tests=bad + echo " ==================================" + echo " === AMAROK WILL NOT BE BUILT ================================================" + echo " ==================================" + echo " =" + echo " = Some mandatory dependencies are either not installed or not installed" + echo " = correctly. See the Amarok README for help with this issue. Further assistance" + echo " = can be found at http://amarok.kde.org or in amarok on irc.freenode.net." + echo " = You will still be able to build other modules from extragear/multimedia." + echo " =" + +else + + echo " ==========================" + echo " === Amarok - PLUGINS ========================================================" + echo " ==========================" + + + echo " =" + echo " = The following extra functionality will NOT be included:" + + +# if test x$build_akode != xyes; then +# echo " = - aKode-engine" +# fi + +# if test x$have_gst10 != xyes; then +# echo " = - GStreamer0.10-engine" +# fi + + if test x$build_xine != xyes; then + echo " = - xine-engine" + fi + + if test x$build_nmm != xyes; then + echo " = - NMM-engine" + fi + +# if test x$build_mas != xyes; then +# echo " = - MAS-engine" +# fi + + if test x$build_helix = xno; then + echo " = - Helix-engine" + fi + + if test x$build_yauap = xno; then + echo " = - yauap-engine" + fi + + if test x$build_libvisual != xyes; then + echo " = - libvisual Support" + fi + + if test x$enable_mysql != xyes; then + echo " = - MySql Support" + fi + + if test x$enable_postgresql != xyes; then + echo " = - Postgresql Support" + fi + + if test x$have_konqsidebar != xyes; then + echo " = - Konqueror Sidebar" + fi + + if test x$have_tunepimp != xyes; then + echo " = - MusicBrainz Support" + fi + + if test x$have_mp4v2 != xyes; then + echo " = - MP4/AAC Tag Write Support" + fi + + if test x$have_libgpod != xyes; then + if test x$have_libgpod_042 != xno; then + echo " = - iPod Support" + else + echo " = - iPod Support (at least libgpod 0.4.2 is required)" + fi + fi + + if test x$have_ifp != xyes; then + echo " = - iRiver iFP Support" + fi + + if test x$have_libnjb != xyes; then + echo " = - Creative Nomad Jukebox Support" + fi + + if test x$have_libmtp != xyes; then + echo " = - MTP Device Support" + fi + + if test x$have_libkarma != xyes; then + echo " = - Rio Karma Support" + fi + + if test x$have_daap != xyes; then + echo " = - DAAP Music Sharing Support" + fi + + echo " =" + echo " = The following extra functionality will be included:" + + +# if test x$build_akode = xyes; then +# echo " = + aKode-engine" +# fi + +# if test x$have_gst10 = xyes; then +# echo " = + GStreamer0.10-engine" +# fi + + if test x$build_xine = xyes; then + echo " = + xine-engine" + fi + + if test x$build_nmm = xyes; then + echo " = + NMM-engine" + fi + +# if test x$build_mas = xyes; then +# echo " = + MAS-engine" +# fi + + if test x$build_helix != xno; then + echo " = + Helix-engine" + fi + + if test x$build_yauap != xno; then + echo " = + yauap-engine" + fi + + if test x$build_libvisual = xyes; then + echo " = + libvisual Support" + fi + + if test x$enable_mysql = xyes; then + echo " = + MySql Support" + fi + + if test x$enable_postgresql = xyes; then + echo " = + Postgresql Support" + fi + + if test x$have_konqsidebar = xyes; then + echo " = + Konqueror Sidebar" + fi + + if test x$have_tunepimp = xyes; then + echo " = + MusicBrainz Support" + fi + + if test x$have_mp4v2 = xyes; then + echo " = + MP4/AAC Tag Write Support" + fi + + if test x$have_libgpod = xyes; then + echo " = + iPod Support" + fi + + if test x$have_ifp = xyes; then + echo " = + iRiver iFP Support" + fi + + if test x$have_libnjb = xyes; then + echo " = + Creative Nomad Jukebox Support" + fi + + if test x$have_libmtp = xyes; then + echo " = + MTP Device Support" + fi + + if test x$have_libkarma = xyes; then + echo " = + Rio Karma Support" + fi + + if test x$have_daap = xyes; then + echo " = + DAAP Music Sharing Support" + fi + + echo " =" +fi + +echo " ===============================================================================" diff --git a/amarok/configure.in.in b/amarok/configure.in.in new file mode 100644 index 00000000..539d07c2 --- /dev/null +++ b/amarok/configure.in.in @@ -0,0 +1,1138 @@ + +############################################################################### +# BEGIN PKG-CONFIG CHECK +############################################################################### + +AC_ARG_VAR(PKGCONFIGFOUND, [Path to pkg-config]) +AC_CHECK_PROG(PKGCONFIGFOUND, pkg-config,[yes]) + +############################################################################### +# END PKG-CONFIG CHECK +############################################################################### + + + +############################################################################### +# BEGIN TAGLIB CHECK +############################################################################### + +PKG_CHECK_MODULES([TAGLIB], [taglib >= 1.5], [taglib_15_found=yes], [PKG_CHECK_MODULES([TAGLIB], [taglib >= 1.4])]) + +AM_CONDITIONAL([TAGLIB_15_FOUND], [test "x$taglib_15_found" = "xyes"]) +AC_SUBST(TAGLIB_CFLAGS) +AC_SUBST(TAGLIB_LIBS) + +if test "x$taglib_15_found" = "xyes"; then + AC_DEFINE([TAGLIB_15], 1, [Taglib 1.5 or later found, disabling duplicate metadata plugins]) +fi + +############################################################################### +# END TAGLIB CHECK +############################################################################### + + + +############################################################################### +# BEGIN GSTREAMER-0.10 CHECK +############################################################################### + +#AC_ARG_WITH(gstreamer10, +# AC_HELP_STRING([--with-gstreamer10],[build Amarok with GStreamer 0.10-engine]), +# [build_gstreamer10=$withval], +# [build_gstreamer10=no] +#) +# +#if test "$build_gstreamer10" != "no"; then +# if test "$PKGCONFIGFOUND" = "yes" ; then +# # check for GStreamer +# dnl Now we're ready to ask for gstreamer libs and cflags +# dnl And we can also ask for the right version of gstreamer +# have_gst10=no +# +# GST10_MAJORMINOR=0.10 +# GST10_REQ=0.10.0 +# +# PKG_CHECK_MODULES(GST10, gstreamer-$GST10_MAJORMINOR >= $GST10_REQ gstreamer-base-$GST10_MAJORMINOR, +# have_gst10=yes,have_gst10=no) +# +# dnl Give error if we don't have gstreamer +# if test "x$have_gst10" = "xno"; then +# LIB_GST10="" +# CFLAGS_GST10="" +# else +# LIB_GST10=$GST10_LIBS +# CFLAGS_GST10=$GST10_CFLAGS +# AC_SUBST(LIB_GST10) +# AC_SUBST(CFLAGS_GST10) +# AC_SUBST(GST10_MAJORMINOR) +# AC_DEFINE(HAVE_GSTREAMER10, 1, [have GStreamer10]) +# fi +# fi +#fi +# +#AM_CONDITIONAL(with_gst10, [test x$have_gst10 = xyes]) + +############################################################################### +# END GSTREAMER-0.10 CHECK +############################################################################### + + + +############################################################################### +# BEGIN XINE CHECK +############################################################################### + +AC_ARG_WITH(xine, + AC_HELP_STRING([--without-xine],[build Amarok without xine-engine]), + [build_xine=$withval], + [build_xine=yes] +) + +if test "$build_xine" != "no"; then + PKG_CHECK_MODULES([XINE], [libxine >= 1.0.2], , [build_xine=no]) +fi + +AM_CONDITIONAL(with_xine, test x$build_xine = xyes) +AC_SUBST(XINE_CFLAGS) +AC_SUBST(XINE_LIBS) + +############################################################################### +# END XINE CHECK +############################################################################### + + + +############################################################################### +# BEGIN AKODE CHECK +############################################################################### + +#AC_ARG_WITH(akode, +# AC_HELP_STRING([--without-akode],[build Amarok without akode-engine]), +# [build_akode=$withval], +# [build_akode=yes] +#) +# +#if test "$build_akode" != "no"; then +# +# AC_CHECK_PROG(AKODE_CONFIG, akode-config, yes) +# +# if test x$AKODE_CONFIG = xyes ; then +# AC_DEFINE(HAVE_AKODE, 1, [have aKode]) +# CFLAGS_AKODE=[`akode-config --cflags`] +# LIBS_AKODE=[`akode-config --libs`] +# +# akode_version=`akode-config --version` +# akode_version=VERSION_TO_NUMBER(echo $akode_version) +# akode_version_min="2.0.0" +# akode_version_min=VERSION_TO_NUMBER(echo $akode_version_min) +# +# AC_MSG_CHECKING([for akode-lib version >= 2.0]) +# +# if test $akode_version -eq $akode_version_min \ +# -o $akode_version -gt $akode_version_min; then +# +# echo "yes" +# +# else +# echo "no" +# +# build_akode=no +# fi +# else +# build_akode=no +# fi +#fi +# +#AM_CONDITIONAL(with_akode, test x$build_akode = xyes) +#AC_SUBST(CFLAGS_AKODE) +#AC_SUBST(LIBS_AKODE) + +############################################################################### +# END AKODE CHECK +############################################################################### + + + +############################################################################### +# BEGIN NMM CHECK +############################################################################### + +AC_ARG_WITH(nmm, + AC_HELP_STRING([--with-nmm],[build Amarok with NMM-engine]), + [build_nmm=$withval], + [build_nmm=no] +) + +AC_ARG_WITH(nmm-dir, + AC_HELP_STRING([--with-nmm-dir],[path to the NMM [default=/usr/local]]), + [nmm_dir="$withval"], + [nmm_dir=/usr/local] +) + +if test "$build_nmm" != "no"; then + + CFLAGS_NMM="-I$nmm_dir/include" + LDFLAGS_NMM="-L$nmm_dir/lib" + AC_DEFINE(HAVE_NMM, 1, [have NMM]) +fi + +AM_CONDITIONAL(with_nmm, test x$build_nmm = xyes) +AC_SUBST(CFLAGS_NMM) +AC_SUBST(LDFLAGS_NMM) + +############################################################################### +# END NMM CHECK +############################################################################### + + + +############################################################################### +# BEGIN MAS CHECK +############################################################################### + +#AC_ARG_WITH(mas, +# AC_HELP_STRING([--with-mas],[build Amarok with MAS-engine]), +# [build_mas=$withval], +# [build_mas=no] +#) +# +# +#if test "$build_mas" != "no"; then +# +# AC_PATH_PROG(MAS_CONFIG, mas-config, no) +# +# if test $MAS_CONFIG = "no" +# then +# amarok_warning_mas_notfound=yes +# echo "amarok_warning_mas_notfound: $amarok_warning_mas_notfound" +# build_mas=no +# else +# AC_DEFINE(HAVE_MAS, 1, [have MAS]) +# +# CFLAGS_MAS=[`$MAS_CONFIG --cflags`] +# LIBS_MAS=[`$MAS_CONFIG --libs`] +# +# build_mas=yes +# fi +#fi +# +#AM_CONDITIONAL(with_mas, test x$build_mas = xyes) +#AC_SUBST(CFLAGS_MAS) +#AC_SUBST(LIBS_MAS) + +############################################################################### +# END MAS CHECK +############################################################################### + + + +############################################################################### +# BEGIN HELIX CHECK +############################################################################### + +AC_ARG_WITH(helix, + AC_HELP_STRING([--with-helix],[build Amarok with Helix-engine]), + [build_helix=$withval], + [build_helix=no] +) + +if test "$build_helix" != "no"; then + AC_MSG_CHECKING([for RealPlayer or HelixPlayer]) + + if test "$build_helix" = "yes"; then + HXPLAY=`type -p hxplay` + RPLAY=`type -p realplay` + if test "$RPLAY" != "" -a -x "$RPLAY"; then + HELIX_LINK=`readlink -f $RPLAY` + HELIX_LINK=`dirname $HELIX_LINK` + HELIX_LIBS=`unset CDPATH; cd $HELIX_LINK && pwd` + if test -e "$HELIX_LIBS/common/clntcore.so"; then + AC_MSG_RESULT([found RealPlayer in $HELIX_LIBS]) + build_helix=$HELIX_LIBS + fi + elif test "$HXPLAY" != "" -a -x "$HXPLAY"; then + HELIX_LINK=`readlink -f $HXPLAY` + HELIX_LINK=`dirname $HELIX_LINK` + HELIX_LIBS=`unset CDPATH; cd $HELIX_LINK && pwd` + if test -e "$HELIX_LIBS/common/clntcore.so"; then + AC_MSG_RESULT([found HelixPlayer in $HELIX_LIBS]) + build_helix=$HELIX_LIBS + fi + fi + else + if test -n "$build_helix" -a -d "$build_helix"; then + HELIX_LIBS=`unset CDPATH; cd $build_helix && pwd` + else + HELIX_LIBS="$build_helix" + fi + build_helix=$HELIX_LIBS + AC_MSG_RESULT([using $HELIX_LIBS]) + fi + + AC_DEFINE(HAVE_HELIX, 1, [have HELIX]) + if test "$build_helix" = "yes"; then + HELIX_LIBS="/usr/local/RealPlayer" + AC_MSG_RESULT(["not found, using default dir"]) + fi + AC_DEFINE_UNQUOTED(HELIX_LIBS, "${HELIX_LIBS}", [location of helix libs]) + if test "$PKGCONFIGFOUND" = "yes" ; then + PKG_CHECK_MODULES([ALSALIB], alsa, have_alsa=yes,have_alsa=no) + if test "$have_alsa" = "yes" ; then + AC_DEFINE(USE_HELIX_ALSA, 1, [support ALSA in the helix-engine]) + fi + fi +fi + +AM_CONDITIONAL(with_helix, test x$build_helix != xno) + +############################################################################### +# END HELIX CHECK +############################################################################### + +############################################################################### +# BEGIN yauap CHECK +############################################################################### + +AC_ARG_WITH(yauap, + AC_HELP_STRING([--with-yauap],[build Amarok with yauap-engine]), + [build_yauap=$withval], + [build_yauap=no] +) + +if test "$build_yauap" != "no"; then + if test "$PKGCONFIGFOUND" = "yes" ; then + # check for dbus-glib + have_yauap=no + + PKG_CHECK_MODULES(DBUS, dbus-1, + have_yauap=yes,have_yauap=no) + + + dnl Give error if we don't have gstreamer + if test "x$have_yauap" = "xno"; then + LIB_YAUAP="" + CFLAGS_YAUAP="" + else + LIB_YAUAP="$DBUS_LIBS -ldbus-qt-1" + CFLAGS_YAUAP="$DBUS_CFLAGS" + AC_SUBST(LIB_YAUAP) + AC_SUBST(CFLAGS_YAUAP) + AC_DEFINE(HAVE_YAUAP, 1, [have yauap]) + fi + fi +fi + +AM_CONDITIONAL(with_yauap, [test x$have_yauap = xyes]) + +############################################################################### +# END yauap CHECK +############################################################################### + + + +############################################################################### +# BEGIN stdint.h CHECK +############################################################################### + +AC_CHECK_HEADER(stdint.h) + +############################################################################### +# END stdint.h CHECK +############################################################################### + + +############################################################################### +# BEGIN fabsf CHECK +############################################################################### + +AC_CHECK_DECLS([fabsf],,,[#include ]) +if test "$ac_cv_have_decl_fabsf" = "yes"; then + AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM( + [[#include ]], + [[return (int)fabsf(1.f);]])], + [AC_DEFINE(HAVE_FABSF, 1, [have fabsf])]) +fi + +############################################################################### +# END fabsf CHECK +############################################################################### + + + + + +############################################################################### +# BEGIN INOTIFY CHECK +############################################################################### + +AC_CHECK_HEADERS(linux/inotify.h) + +if test x"$ac_cv_header_linux_inotify_h" = x"yes"; then + AC_DEFINE(HAVE_INOTIFY, 1, [have inotify]) +fi + +############################################################################### +# END INOTIFY CHECK +############################################################################### + + + +############################################################################### +# BEGIN QT OPENGL CHECK +############################################################################### + +AC_ARG_WITH(opengl, + AC_HELP_STRING([--without-opengl],[build Amarok without OpenGL support]), + [build_opengl=$withval], + [build_opengl=yes] +) + +if test "$build_opengl" != "no"; then + AC_MSG_CHECKING(for Qt with OpenGL support) + AC_CACHE_VAL(ac_cv_kde_qt_has_opengl, + [ + AC_LANG_SAVE + AC_LANG_CPLUSPLUS + + save_CXXFLAGS="$CXXFLAGS" + save_LIBS="$LIBS" + save_LDFLAGS="$LDFLAGS" + + CXXFLAGS="$CXXFLAGS -I$qt_incdir $all_includes" + LDFLAGS="$LDFLAGS -L$qt_libdir $all_libraries $USER_LDFLAGS $KDE_MT_LDFLAGS" + LIBS="$LIBS $LIBQT $KDE_MT_LIBS" + + AC_TRY_LINK([ + #include + ], + [ + (void)new QGLWidget((QWidget*)0, "qgl"); + ], + ac_cv_kde_qt_has_opengl=yes, + ac_cv_kde_qt_has_opengl=no) + + CXXFLAGS="$save_CXXFLAGS" + LIBS="$save_LIBS" + LDFLAGS="$save_LDFLAGS" + AC_LANG_RESTORE + ]) + AC_MSG_RESULT($ac_cv_kde_qt_has_opengl) + + if test x$ac_cv_kde_qt_has_opengl = xyes; then + AC_DEFINE(HAVE_QGLWIDGET, 1, [have Qt with OpenGL support]) + gl_libs="-lGL" + else + gl_libs="" + fi + + AC_SUBST(gl_libs) +fi + +############################################################################### +# END QT OPENGL CHECK +############################################################################### + + + +############################################################################### +# BEGIN SQLITE CHECK +############################################################################### + +LIB_SQLITE="" + +AC_ARG_WITH(included-sqlite, + AC_HELP_STRING([--without-included-sqlite],[build Amarok using system sqlite library]), + [included_sqlite=$withval], + [included_sqlite=yes] +) + +if test x$included_sqlite = xno; then + if test x$PKGCONFIGFOUND = xyes; then + PKG_CHECK_MODULES(SQLITE, sqlite3 >= 3.0, have_sqlite=yes,have_sqlite=no) + + if test x$have_sqlite = xyes; then + ## AC_DEFINE(HAVE_SQLITE, 1, [have SQLite database library]) + LIB_SQLITE=`pkg-config --libs sqlite3` + else + # We don't support not having sqlite anymore + DO_NOT_COMPILE="$DO_NOT_COMPILE amarok" + no_amarok=yes + fi + fi +fi + +AC_SUBST(LIB_SQLITE) +AM_CONDITIONAL(with_included_sqlite, [test x$included_sqlite = xyes]) + + +# Determine pointer size for sqlite + +KDE_CHECK_TYPES +AC_DEFINE(SQLITE_PTR_SZ, SIZEOF_CHAR_P, [Determine pointer size for SQLite]) + +############################################################################### +# END SQLITE CHECK +############################################################################### + + + +############################################################################### +# BEGIN MYSQL CHECK +############################################################################### + +AC_ARG_ENABLE(mysql, + AC_HELP_STRING([--enable-mysql],[build Amarok with MySQL support]), + [enable_mysql=$enableval], + [enable_mysql=no] +) + +if test "$enable_mysql" = "yes"; then + + AC_CHECK_PROG(MYSQL_CONFIG, mysql_config, yes) + + if test x$MYSQL_CONFIG = xyes; then + AC_DEFINE(USE_MYSQL, 1, [MySql database support enabled]) + + mysql_includes=`mysql_config --cflags` + mysql_libs=`mysql_config --libs` + else + amarok_warning_mysql_notfound=yes + enable_mysql=no + fi + +fi + +AM_CONDITIONAL(enable_mysql, test x$enable_mysql = xyes) +AC_SUBST(mysql_includes) +AC_SUBST(mysql_libs) + +############################################################################### +# END MYSQL CHECK +############################################################################### + + + +############################################################################### +# BEGIN POSTGRESQL CHECK +############################################################################### + +AC_ARG_ENABLE(postgresql, + AC_HELP_STRING([--enable-postgresql],[build Amarok with PostgreSQL support]), + [enable_postgresql=$enableval], + [enable_postgresql=no] +) + +if test "$enable_postgresql" = "yes"; then + + AC_CHECK_PROG(POSTGRESQL_CONFIG, pg_config, yes) + + if test x$POSTGRESQL_CONFIG = xyes; then + AC_DEFINE(USE_POSTGRESQL, 1, [Postgresql database support enabled]) + + postgresql_includes=-I`pg_config --includedir` + postgresql_libs="-L`pg_config --libdir` -lpq" + else + amarok_warning_postgresql_notfound=yes + enable_postgresql=no + fi + +fi + +AM_CONDITIONAL(enable_postgresql, test x$enable_postgresql = xyes) +AC_SUBST(postgresql_includes) +AC_SUBST(postgresql_libs) + +############################################################################### +# END POSTGRESQL CHECK +############################################################################### + + + +############################################################################### +# BEGIN LIBVISUAL CHECK +############################################################################### + +AC_ARG_WITH(libvisual, + AC_HELP_STRING([--without-libvisual],[build Amarok without libvisual support]), + [with_libvisual=$withval], + [with_libvisual=yes] +) + +if test "$with_libvisual" = "yes"; then + ## libvisual plugin depends on sdl + AC_CHECK_PROG(SDL_CONFIG, sdl-config, yes) + + if test x$SDL_CONFIG = xyes; then + sdl_cflags=`sdl-config --cflags` + sdl_libs=`sdl-config --libs` + fi + + AC_SUBST(sdl_cflags) + AC_SUBST(sdl_libs) + + if test x$PKGCONFIGFOUND = xyes -a x$SDL_CONFIG = xyes; then + + PKG_CHECK_MODULES(LIBVISUAL, libvisual-0.4 >= 0.4.0, [ + LIBVISUAL_LIBS=`echo $LIBVISUAL_LIBS | sed -e 's/-lpthread//' -e 's/-ldl//'` + build_libvisual="yes" + ], [build_libvisual="no"]) + + AC_SUBST(LIBVISUAL_LIBS) + AC_SUBST(LIBVISUAL_CFLAGS) + + if test x$build_libvisual = xyes; then + AC_DEFINE(HAVE_LIBVISUAL, 1, [have LIBVISUAL]) + fi + fi +fi + +AM_CONDITIONAL(with_libvisual, test x$build_libvisual = xyes) + +############################################################################### +# END LIBVISUAL CHECK +############################################################################### + + + +############################################################################### +# BEGIN TUNEPIMP CHECK +############################################################################### + +AC_ARG_WITH(musicbrainz, + AC_HELP_STRING([--without-musicbrainz],[build Amarok without MusicBrainz support]), + [with_musicbrainz=$withval], + [with_musicbrainz=yes] +) + +if test "$with_musicbrainz" = "yes"; then + AC_CHECK_HEADER(tunepimp-0.5/tp_c.h, [build_musicbrainz="yes"], + [AC_CHECK_HEADER(tunepimp/tp_c.h, [build_musicbrainz="yes"], + [build_musicbrainz="no"])]) +fi + +if test "$build_musicbrainz" = "yes"; then + AC_CHECK_LIB(tunepimp, tr_GetPUID, + AC_DEFINE(HAVE_TUNEPIMP, 5, [have MusicBrainz 0.5.x]), + AC_CHECK_LIB(tunepimp, tp_SetFileNameEncoding, + AC_DEFINE(HAVE_TUNEPIMP, 4, [have MusicBrainz 0.4.x]), + AC_DEFINE(HAVE_TUNEPIMP, 1, [have MusicBrainz]))) + LIB_TUNEPIMP="-ltunepimp" + have_tunepimp=yes +else + AC_DEFINE(HAVE_TUNEPIMP, 0, [have TunePimp]) + LIB_TUNEPIMP="" + have_tunepimp=no +fi + +AC_SUBST(LIB_TUNEPIMP) + +############################################################################### +# END TUNEPIMP CHECK +############################################################################### + + + +############################################################################### +# BEGIN AMAZON CHECK +############################################################################### + +AC_ARG_ENABLE(amazon, + AC_HELP_STRING([--disable-amazon],[disable Amazon cover download support [default=enable]]), + [enable_amazon=$enableval], + [enable_amazon=yes] +) + +if test "$enable_amazon" != "no"; then + AC_DEFINE(AMAZON_SUPPORT, 1, [Amazon cover download support enabled]) +fi + +############################################################################### +# END AMAZON CHECK +############################################################################### + + + +############################################################################### +# BEGIN SCHED_SETAFFINITY BUGGY GLIBC CHECK +############################################################################### + +AC_MSG_CHECKING([if sched_setaffinity should be enabled]) + +AC_LANG_SAVE +AC_LANG_CPLUSPLUS + +amarok_glibcsched_works=no + +AC_TRY_COMPILE([ + #include +], +[ + cpu_set_t mask; + CPU_ZERO( &mask ); + CPU_SET( 0, &mask ); + sched_setaffinity( 0, sizeof(mask), &mask ); +], + amarok_sched_3params=yes, + amarok_sched_3params=no +) + +if test "x$amarok_sched_3params" = "xyes"; then + AC_DEFINE(SCHEDAFFINITY_SUPPORT, 1, [sched_setaffinity works correctly]) + AC_DEFINE(SCHEDAFFINITY_3PARAMS, 1, [sched_setaffinity takes three params]) + amarok_glibcsched_works=yes +fi + +if test "x$amarok_sched_3params" = "xno"; then + AC_TRY_COMPILE([ + #include + ], + [ + cpu_set_t mask; + CPU_ZERO( &mask ); + CPU_SET( 0, &mask ); + sched_setaffinity( 0, &mask ); + ], + amarok_sched_2params=yes, + amarok_sched_2params=no + ) + if test "x$amarok_sched_2params" = "xyes"; then + AC_DEFINE(SCHEDAFFINITY_SUPPORT, 1, [sched_setaffinity works correctly]) + amarok_glibcsched_works=yes + fi +fi + +AC_LANG_RESTORE + +AC_MSG_RESULT($amarok_glibcsched_works) + +############################################################################### +# END SCHED_SETAFFINITY BUGGY GLIBC CHECK +############################################################################### + + + +############################################################################### +# BEGIN KDEBASE CHECK +############################################################################### + +KDE_CHECK_HEADER(konqsidebarplugin.h, have_konqsidebar=yes, have_konqsidebar=no) +KDE_CHECK_LIB(konqsidebarplugin, _init, have_konqsidebar=$have_konqsidebar, have_konqsidebar=no) + +AM_CONDITIONAL(with_konqsidebar, [test x$have_konqsidebar = xyes]) + +############################################################################### +# END KDEBASE CHECK +############################################################################### + + + +############################################################################### +# BEGIN NJB CHECK +############################################################################### +### mediabrowser.cpp can use libnjb if available + +AC_ARG_WITH(libnjb, + AC_HELP_STRING([--with-libnjb],[build Amarok with Nomad Jukebox support from libnjb]), + [build_libnjb=$withval], + [build_libnjb=yes] +) + +if test "$build_libnjb" != "no"; then + if test "$PKGCONFIGFOUND" = "yes" ; then + + # check for libnjb + have_libnjb=no + + PKG_CHECK_MODULES(LIBNJB, libnjb, have_libnjb=yes,have_libnjb=no) + if test "x$have_libnjb" != "xno"; then + AC_DEFINE(HAVE_LIBNJB, 1, [have libnjb]) + fi + fi +fi + +AM_CONDITIONAL(with_libnjb, [test x$have_libnjb = xyes]) + +############################################################################### +# END NJB CHECK +############################################################################### + + + +############################################################################### +# BEGIN MTP CHECK +############################################################################### +### mediabrowser.cpp can use libmtp if available + +AC_ARG_WITH(libmtp, + AC_HELP_STRING([--with-libmtp],[build Amarok with support for MTP devices]), + [build_libmtp=$withval], + [build_libmtp=yes] +) +if test "$build_libmtp" != "no"; then + if test "$PKGCONFIGFOUND" = "yes" ; then + PKG_CHECK_MODULES(LIBMTP, libmtp >= 0.1.1, + [ + LIBMTP_LIBS=`echo ${LIBMTP_LIBS} | sed 's/-lusb//'` + have_libmtp=yes + ], + [ + have_libmtp=no + ]) + fi + + if test "x$have_libmtp" != "xno"; then + AC_DEFINE(HAVE_LIBMTP, 1, [have libmtp]) + fi +fi + +AM_CONDITIONAL(with_libmtp, [test x$have_libmtp = xyes]) + +############################################################################### +# END MTP CHECK +############################################################################### + +############################################################################### +# BEGIN RIO KARMA CHECK +############################################################################### +### mediabrowser.cpp can use libkarma if available + +AC_ARG_WITH(libkarma, + AC_HELP_STRING([--with-libkarma],[build Amarok with Rio Karma support]), + [build_libkarma=$withval], + [build_libkarma=yes] +) + +if test "$build_libkarma" != "no"; then + AC_CHECK_HEADERS([libkarma/lkarma.h], [have_libkarma=yes], [], []) + AC_CHECK_HEADERS([usb.h], [have_usb=yes], [], []) + + if test "$have_libkarma" = "yes"; then + AC_DEFINE(HAVE_LIBKARMA, 1, [have libkarma]) + else + AC_MSG_RESULT($have_libkarma) + have_libkarma=no + fi +fi + +AM_CONDITIONAL(with_libkarma, [test x$have_libkarma = xyes]) + +############################################################################### +# END RIO KARMA CHECK +############################################################################### + +############################################################################### +# BEGIN IFP CHECK +############################################################################### +### mediabrowser.cpp can use libifp if available + +AC_ARG_WITH(ifp, + AC_HELP_STRING([--with-ifp],[build Amarok with ifp support]), + [build_ifp=$withval], + [build_ifp=yes] +) + +if test "$build_ifp" != "no"; then + + AC_CHECK_HEADERS([ifp.h], [have_ifp=yes], [], []) + AC_CHECK_HEADERS([usb.h], [have_usb=yes], [], []) + + if test "$have_ifp" = "yes"; then + AC_DEFINE(HAVE_IFP, 1, [have ifp]) + IFP_LIBS="-lifp -lusb" + else + AC_MSG_RESULT($have_ifp) + have_ifp=no + fi + +fi + +AC_SUBST(IFP_INCLUDES) +AC_SUBST(IFP_LIBS) + +AM_CONDITIONAL(with_ifp, [test x$have_ifp = xyes]) + +############################################################################### +# END IFP CHECK +############################################################################### + + + +############################################################################### +# BEGIN LIBGPOD CHECK +############################################################################### + +AC_ARG_WITH(libgpod, + AC_HELP_STRING([--with-libgpod],[build Amarok with iPod support from libgpod]), + [build_libgpod=$withval], + [build_libgpod=yes] +) + +if test "$build_libgpod" != "no"; then + if test "$PKGCONFIGFOUND" = "yes" ; then + + # check for libgpod + have_libgpod=no + + PKG_CHECK_MODULES(LIBGPOD, libgpod-1.0, have_libgpod=yes,have_libgpod=no) + + if test "x$have_libgpod" = "xyes"; then + ac_cppflags_save=$CPPFLAGS + ac_cflags_save=$CFLAGS + ac_libs_save=$LIBS + CPPFLAGS="$CPPFLAGS $LIBGPOD_INCLUDES" + CFLAGS="$CFLAGS $LIBGPOD_CFLAGS" + LIBS="$LIBS $LIBGPOD_LIBS" + + AC_CHECK_FUNCS(itdb_track_set_thumbnails, , have_libgpod_042=no) + AC_CHECK_FUNCS(itdb_get_mountpoint, , have_libgpod_042=no) + AC_CHECK_FUNCS(itdb_device_get_ipod_info, , have_libgpod_042=no) + + AC_CHECK_MEMBER(struct _Itdb_Track.movie_flag, + [AC_DEFINE(HAVE_ITDB_MOVIE_FLAG, 1, [have libgpod movie flag])], + have_libgpod_042=no, + [#include ]) + + AC_CHECK_MEMBER(struct _Itdb_Track.skip_when_shuffling, + [AC_DEFINE(HAVE_ITDB_SKIP_SHUFFLE_FLAG, 1, [have libgpod skip when shuffling flag])], + have_libgpod_042=no, + [#include ]) + + AC_CHECK_MEMBER(struct _Itdb_Track.mark_unplayed, + [AC_DEFINE(HAVE_ITDB_MARK_UNPLAYED, 1, [have libgpod mark played flag])], + have_libgpod_042=no, + [#include ]) + + AC_CHECK_MEMBER(struct _Itdb_Track.mediatype, + [AC_DEFINE(HAVE_ITDB_MEDIATYPE, 1, [have libgpod mediatype flag])], + have_libgpod_042=no, + [#include ]) + + AC_CHECK_DECL(ITDB_IPOD_MODEL_TOUCH_BLACK, + [AC_DEFINE(HAVE_LIBGPOD_060, 1, [have at least libgpod 0.6.0])], + have_libgpod_060=no, + [#include ]) + + CPPFLAGS=$ac_cppflags_save + CFLAGS=$ac_cflags_save + LIBS=$ac_libs_save + fi + + if test "x$have_libgpod_042" = "xno"; then + have_libgpod=no + AC_MSG_RESULT(Your libgpod version is too old: at least 0.4.2 is required) + fi + + if test "x$have_libgpod" != "xno"; then + AC_DEFINE(HAVE_LIBGPOD, 1, [have libgpod]) + fi + fi +fi + +AM_CONDITIONAL(with_libgpod, [test x$have_libgpod = xyes]) + +############################################################################### +# END LIBGPOD CHECK +############################################################################### + + + +############################################################################### +# BEGIN statvfs(2) CHECK +############################################################################### + +AC_CHECK_FUNCS(statvfs) + +############################################################################### +# END statvfs(2) CHECK +############################################################################### + + + +############################################################################### +# BEGIN MP4V2 CHECK +############################################################################### +# m4a/aac tag reading and writing needs libmp4v2 from faad2 or better mpeg4ip + +AC_ARG_WITH(mp4v2, + AC_HELP_STRING([--with-mp4v2],[build Amarok with M4A/AAC tag support from mp4v2/faad2]), + [have_mp4v2=$withval], + [have_mp4v2=no] +) + +AC_ARG_WITH(mp4v2-dir, + AC_HELP_STRING([--with-mp4v2-dir],[path to mp4v2 [default=/usr]]), + [mp4v2_dir="$withval"], + [mp4v2_dir=/usr] +) + +if test "$have_mp4v2" != "no"; then + AC_LANG_SAVE + AC_LANG_CPLUSPLUS + ac_cxxflags_save=$CXXFLAGS + CXXFLAGS="$CXXFLAGS -I$mp4v2_dir/include" + ac_ldflags_save=$LDFLAGS + LDFLAGS="$LDFLAGS -L$mp4v2_dir/lib" + + # not even everyone using faad2 has + if ! test -f config.h; then + echo "#include \"confdefs.h\"" > config.h + fi + ac_cppflags_save=$CPPFLAGS + CPPFLAGS="$CPPFLAGS -I." + AC_CHECK_HEADERS(systems.h) + AC_CHECK_HEADERS([mp4.h], [have_mp4_h=yes], [], + [#ifdef HAVE_SYSTEMS_H + # include + #endif + ]) + + AC_CHECK_LIB( mp4v2, MP4Read, have_mp4v2=yes, have_mp4v2=no ) + + if test "$have_mp4v2" = "yes" -a "$have_mp4_h" = "yes"; then + AC_DEFINE(HAVE_MP4V2, 1, [have mp4v2]) + MP4V2_INCLUDES="-I$mp4v2_dir/include" + MP4V2_LIBS="-L$mp4v2_dir/lib -lmp4v2" + else + have_mp4v2=no + fi + + CPPFLAGS=$ac_cppflags_save + CXXFLAGS=$ac_cxxflags_save + LDFLAGS=$ac_ldflags_save + AC_LANG_RESTORE +fi + +AC_SUBST(MP4V2_INCLUDES) +AC_SUBST(MP4V2_LIBS) + +AM_CONDITIONAL(with_mp4v2, [test x$have_mp4v2 != xno ]) + +############################################################################### +# END MP4V2 CHECK +############################################################################### + + + +############################################################################### +# BEGIN DAAP KDE 3.4 CHECK +############################################################################### + +daapsave_CXXFLAGS="$CXXFLAGS" +CXXFLAGS="$CXXFLAGS $all_includes" +AC_LANG_SAVE +AC_LANG_CPLUSPLUS + +AC_MSG_CHECKING([if KDE is at least 3.4 for DAAP support]) +AC_COMPILE_IFELSE([ +#include +#if ! ( KDE_IS_VERSION( 3, 4, 0 ) ) +#error KDE 3.4 +#endif +], + have_kde34="yes" + DNSSD_LIBS=$LIB_KDNSSD +# echo "yes" +, + have_kde34="no" + DNSSD_LIBS="" +# echo "no" +) +CXXFLAGS="$daapsave_CXXFLAGS" +AC_LANG_RESTORE +AC_SUBST(DNSSD_LIBS) +AM_CONDITIONAL(atleast_kde34, [test x$have_kde34 != xno ]) +AC_MSG_RESULT($have_kde34) + +############################################################################### +# END DAAP KDE 3.4 CHECK +############################################################################### + + + +############################################################################### +# BEGIN OPTIONAL DAAP SUPPORT +############################################################################### + +AC_ARG_WITH(daap, + AC_HELP_STRING([--without-daap],[build Amarok without support for DAAP]), + [have_daap=$withval], + [have_daap=yes] +) + +AM_CONDITIONAL(with_daap, [test x$have_daap = xyes]) + +############################################################################### +# END OPTIONAL DAAP SUPPORT +############################################################################### + + + +############################################################################### +# BEGIN DAAP TYPE CHECKS +############################################################################### + +AC_CHECK_TYPES([uint8_t, u_int8_t, uint16_t, u_int16_t, uint32_t, u_int32_t, uint64_t, u_int64_t]) + +############################################################################### +# END DAAP TYPE CHECKS +############################################################################### + + + +############################################################################### +# BEGIN DAAP MONGREL RUBY VARIABLE +############################################################################### + +AC_PATH_PROG(RUBY, ruby, no) + +ruby_includes=[`$RUBY -rrbconfig -e 'puts Config.expand( Config::MAKEFILE_CONFIG["archdir"] )'`] +ruby_ldflags=[`$RUBY -rrbconfig -e 'puts Config.expand( Config::MAKEFILE_CONFIG["LIBRUBYARG_SHARED"] )'`] + +AC_SUBST(ruby_includes) +AC_SUBST(ruby_ldflags) +OLDCFLAGS="$CFLAGS" +CFLAGS="-I$ruby_includes -Wall" +OLDCPPFLAGS="$CPPFLAGS" +CPPFLAGS="-I$ruby_includes" #no I don't know why CPPFLAGS is used +AC_CHECK_HEADERS([ruby.h], [have_ruby_h=yes], [have_ruby_h=no]) #used in ruby check below +CFLAGS="$OLDCFLAGS" +CPPFLAGS="$OLDCPPFLAGS" + +############################################################################### +# END DAAP MONGREL RUBY VARIABLE +############################################################################### + + + +############################################################################### +# BEGIN RUBY CHECK +############################################################################### +## TODO: Check version number >= 1.8 + +if test "x$RUBY" = "xno" -o "x$have_ruby_h" = "xno"; then + amarok_error_noruby=yes + DO_NOT_COMPILE="$DO_NOT_COMPILE amarok" + no_amarok=yes +fi + +############################################################################### +# END RUBY CHECK +############################################################################### + + +############################################################################### +# BEGIN DO_NOT_COMPILE CHECK +############################################################################### + +if test x$build_xine = xno -a x$build_helix = xno; then + + DO_NOT_COMPILE="$DO_NOT_COMPILE amarok" + no_engine=yes + +fi + +############################################################################### +# END DO_NOT_COMPILE CHECK +############################################################################### + diff --git a/amarok/docs/collection_redesign.xmi b/amarok/docs/collection_redesign.xmi new file mode 100644 index 00000000..ea1c9fc5 --- /dev/null +++ b/amarok/docs/collection_redesign.xmi @@ -0,0 +1,810 @@ + + + + + umbrello uml modeller http://uml.sf.net + 1.5.5 + UnicodeUTF8 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/amarok/docs/use_umbrello_to_open_xmi_files b/amarok/docs/use_umbrello_to_open_xmi_files new file mode 100644 index 00000000..e69de29b diff --git a/amarok/src/Makefile.am b/amarok/src/Makefile.am new file mode 100644 index 00000000..92d18bfc --- /dev/null +++ b/amarok/src/Makefile.am @@ -0,0 +1,272 @@ +if with_included_sqlite + SQLITE_SUBDIR = sqlite + LIB_SQLITE_LOCAL = $(top_builddir)/amarok/src/sqlite/libsqlite.la + sqlite_includes = -I$(top_srcdir)/amarok/src/sqlite +endif + +if with_konqsidebar + KONQSIDEBAR_SUBDIR = konquisidebar +endif + +KDE_OPTIONS = nofinal + +lib_LTLIBRARIES = libamarok.la + +SUBDIRS = \ + amarokcore \ + magnatunebrowser \ + $(SQLITE_SUBDIR) \ + analyzers \ + data \ + plugin \ + images \ + loader \ + scripts \ + themes \ + vis \ + metadata \ + $(KONQSIDEBAR_SUBDIR) \ + statusbar \ + . \ + engine \ + mediadevice \ + device \ + collectionscanner + + +INCLUDES = \ + -I$(top_builddir)/amarok/src/amarokcore \ + -I$(top_builddir)/amarok/src/magnatunebrowser \ + -I$(top_srcdir)/amarok/src/amarokcore \ + -I$(top_srcdir)/amarok/src/analyzers \ + -I$(top_srcdir)/amarok/src/plugin \ + -I$(top_srcdir)/amarok/src/statusbar \ + -I$(top_srcdir)/amarok/src/mediadevice \ + -I$(top_srcdir)/amarok/src/device \ + -I$(top_srcdir)/amarok/src \ + -I$(kde_includes)/arts \ + $(TAGLIB_CFLAGS) \ + $(sqlite_includes) \ + $(mysql_includes) \ + $(postgresql_includes) \ + $(EXSCALIBAR_CFLAGS) \ + $(all_includes) + +libamarok_la_SOURCES = \ + Options1.ui \ + Options1.ui.h \ + Options2.ui \ + Options4.ui \ + Options5.ui \ + Options7.ui \ + Options8.ui \ + actionclasses.cpp \ + app.cpp \ + atomicstring.cpp \ + atomicurl.cpp \ + browserbar.cpp \ + clicklineedit.cpp \ + collectionbrowser.cpp \ + collectiondb.cpp \ + columnlist.cpp \ + configdialog.cpp \ + contextbrowser.cpp \ + coverfetcher.cpp \ + covermanager.cpp \ + cuefile.cpp \ + dbsetup.ui \ + dbsetup.ui.h \ + deletedialog.cpp \ + deletedialogbase.ui \ + deviceconfiguredialog.cpp \ + devicemanager.cpp \ + directorylist.cpp \ + dynamicmode.cpp \ + enginebase.cpp \ + enginecontroller.cpp \ + engineobserver.cpp \ + equalizergraph.cpp \ + equalizerpresetmanager.cpp \ + equalizersetup.cpp \ + expression.cpp \ + fht.cpp \ + filebrowser.cpp \ + firstrunwizard.ui \ + hintlineedit.cpp \ + htmlview.cpp \ + iconloader.cpp \ + k3bexporter.cpp \ + kbookmarkhandler.cpp \ + ktrm.cpp \ + lastfm.cpp \ + mediabrowser.cpp \ + mediadevicemanager.cpp \ + medium.cpp \ + mediumpluginmanager.cpp \ + metabundle.cpp \ + metabundlesaver.cpp \ + moodbar.cpp \ + mountpointmanager.cpp \ + multitabbar.cpp \ + mydiroperator.cpp \ + newdynamic.ui \ + organizecollectiondialog.ui \ + osd.cpp \ + pixmapviewer.cpp \ + playerwindow.cpp \ + playlist.cpp \ + playlistbrowser.cpp \ + playlistbrowseritem.cpp \ + playlistitem.cpp \ + playlistloader.cpp \ + playlistselection.cpp \ + playlistwindow.cpp \ + pluginmanager.cpp \ + podcastsettings.cpp \ + podcastsettingsbase.ui \ + prettypopupmenu.cpp \ + queuemanager.cpp \ + refreshimages.cpp \ + scancontroller.cpp \ + scriptmanager.cpp \ + scriptmanagerbase.ui \ + scrobbler.cpp \ + sliderwidget.cpp \ + smartplaylisteditor.cpp \ + socketserver.cpp \ + starmanager.cpp \ + statistics.cpp \ + systray.cpp \ + tagdialog.cpp \ + tagdialogbase.ui \ + tagguesser.cpp \ + tagguesserconfigdialog.ui \ + threadmanager.cpp \ + tooltip.cpp \ + trackpickerdialog.cpp \ + trackpickerdialogbase.ui \ + tracktooltip.cpp \ + transferdialog.cpp \ + xmlloader.cpp \ + xspfplaylist.cpp \ + editfilterdialog.cpp + +libamarok_la_LIBADD = \ + $(top_builddir)/amarok/src/amarokcore/libamarokcore.la \ + $(top_builddir)/amarok/src/analyzers/libanalyzers.la \ + $(top_builddir)/amarok/src/plugin/libplugin.la \ + $(top_builddir)/amarok/src/statusbar/libstatusbar.la \ + $(top_builddir)/amarok/src/metadata/libmetadata.la \ + $(top_builddir)/amarok/src/magnatunebrowser/libmagnatunebrowser.la \ + $(LIB_QT) $(LIB_KPARTS) -lDCOP -lkdefx $(KDE_MT_LIBS) $(LIB_KFILE) $(LIB_KDEUI) $(LIB_KDECORE) $(LIB_KHTML) $(LIB_KNEWSTUFF) \ + $(TAGLIB_LIBS) $(gl_libs) $(LIB_SQLITE) $(LIB_SQLITE_LOCAL) \ + $(LIB_TUNEPIMP) \ + $(mysql_libs) \ + $(postgresql_libs) + +libamarok_la_LDFLAGS = \ + $(all_libraries) \ + $(KDE_RPATH) + +METASOURCES = \ + AUTO + +KDE_ICON = \ + AUTO + + + +bin_SCRIPTS = amarok_proxy.rb + +bin_PROGRAMS = amarokapp +# atomicstring_unittest is excluded from the build by default to avoid compile slowdowns. +# If you intend to do work in this area and want to build it, comment the line above and +# uncomment the lines below: +#bin_PROGRAMS = amarokapp atomicstring_unittest +#atomicstring_unittest_SOURCES = atomicstring_unittest.cpp atomicstring.h +#atomicstring_unittest_LDADD = atomicstring.$(OBJEXT) $(LIB_KDECORE) + + +amarokapp_SOURCES = main.cpp + +amarokapp_LDADD = \ + $(top_builddir)/amarok/src/amarokcore/libamarokcore.la \ + libamarok.la \ + $(top_builddir)/amarok/src/analyzers/libanalyzers.la \ + $(top_builddir)/amarok/src/plugin/libplugin.la \ + $(top_builddir)/amarok/src/statusbar/libstatusbar.la \ + $(top_builddir)/amarok/src/metadata/libmetadata.la \ + $(LIB_KDECORE) + $(EXSCALIBAR_LIBS) + +amarokapp_LDFLAGS = \ + $(all_libraries) \ + $(KDE_RPATH) + +rcdir = \ + $(kde_datadir)/amarok + +rc_DATA = \ + amarokui.rc + + +configdir = \ + $(kde_confdir) + +config_DATA = \ + amarokrc + + +xdg_apps_DATA = \ + amarok.desktop + +kde_servicetypes_DATA = \ + amarok_plugin.desktop \ + amarok_codecinstall.desktop + + +servicemenudir = \ + $(kde_datadir)/konqueror/servicemenus + +servicemenu_DATA = \ + amarok_addaspodcast.desktop \ + amarok_append.desktop \ + amarok_play_audiocd.desktop + + +profiledatadir = \ + $(kde_datadir)/profiles + +profiledata_DATA = \ + amarok.profile.xml + +protocoldir = \ + $(kde_servicesdir) + +protocol_DATA = \ + amarokitpc.protocol \ + amaroklastfm.protocol \ + amarokpcast.protocol + +messages: rc.cpp + $(EXTRACTRC) `find . -name "*.rc" -o -name "*.ui" -o -name "*.kcfg"` > rc.cpp + LIST=`find . -name \*.h -o -name \*.hh -o -name \*.H -o -name \*.hxx -o -name \*.hpp -o -name \*.cpp -o -name \*.cc -o -name \*.cxx -o -name \*.ecpp -o -name \*.C`; \ + if test -n "$$LIST"; then \ + $(XGETTEXT) $$LIST -o $(podir)/amarok.pot; \ + fi + +install-exec-hook: + @echo "" + @echo "==========================" + @echo "= Amarok - INSTALLED ================================" + @echo "==========================" + @echo "=" + @echo "= Type amarok to start!" + @echo "=" + @echo "= If you have problems, please consult the README;" + @echo "= if the problems continue join us on #amarok." + @echo "=" + @echo "=======================================================" + + + diff --git a/amarok/src/Options1.ui b/amarok/src/Options1.ui new file mode 100644 index 00000000..5fa3f3bf --- /dev/null +++ b/amarok/src/Options1.ui @@ -0,0 +1,700 @@ + +Options1 + + + General + + + + 0 + 0 + 431 + 556 + + + + + 3 + 3 + 0 + 0 + + + + General + + + + unnamed + + + 0 + + + + generalBox + + + 0 + + + General Options + + + + unnamed + + + + kcfg_ShowSplashscreen + + + Sho&w splash-screen on startup + + + Check to enable the splashscreen during Amarok startup. + + + Check to enable the splashscreen during Amarok startup. + + + + + kcfg_ShowTrayIcon + + + Show tray &icon + + + Check to enable the Amarok system tray icon. + + + Check to enable the Amarok system tray icon. + + + + + layout5 + + + + unnamed + + + + spacer3_2 + + + Horizontal + + + Fixed + + + + 16 + 21 + + + + + + kcfg_AnimateTrayIcon + + + false + + + &Flash tray icon when playing + + + Check to animate the Amarok system tray icon. + + + Check to animate the Amarok system tray icon. + + + + + + + kcfg_ShowPlayerWindow + + + Show player window + + + Check to enable an extra player window. + + + Check to enable an extra player window. + + + + + layout4 + + + + unnamed + + + + textLabel1 + + + Default si&ze for cover previews: + + + kcfg_CoverPreviewSize + + + Size of the cover image in the context viewer in pixels. + + + Size of the cover image in the context viewer in pixels. + + + + + kcfg_CoverPreviewSize + + + px + + + 300 + + + 50 + + + 100 + + + Size of the cover images in the context viewer in pixels. + + + Size of the cover images in the context viewer in pixels. + + + + + spacer7 + + + Horizontal + + + Expanding + + + + 30 + 20 + + + + + + + + layout7 + + + + unnamed + + + + textLabel1_2 + + + + 150 + 0 + + + + External web &browser: + + + kComboBox_browser + + + + + kComboBox_browser + + + + 7 + 0 + 0 + 0 + + + + + 150 + 0 + + + + Choose the external web browser to be used by Amarok. + + + + + spacer7_2 + + + Horizontal + + + Expanding + + + + 50 + 21 + + + + + + + + layout8 + + + + unnamed + + + + checkBox_customBrowser + + + true + + + + 150 + 0 + + + + Use &another browser: + + + + + kLineEdit_customBrowser + + + false + + + + 7 + 0 + 0 + 0 + + + + + 150 + 0 + + + + Enter filename of external web browser. + + + + + spacer7_2_3 + + + Horizontal + + + Expanding + + + + 50 + 21 + + + + + + + + + + componentsBox + + + 0 + + + Components + + + + unnamed + + + + kcfg_UseScores + + + Use &scores + + + false + + + Scores for tracks are calculated automatically, based on your listening habits. + + + + + kcfg_UseRatings + + + Use ratings + + + You can assign ratings to tracks manually, from 1 to 5 stars. + + + + + line1 + + + HLine + + + Sunken + + + Horizontal + + + + + moodbarHelpLabel + + + You need the <a href='http://amarok.kde.org/wiki/Moodbar'>moodbar package</a> installed to enable the moodbar feature. + + + + + moodFrame + + + NoFrame + + + Raised + + + 0 + + + 0 + + + + unnamed + + + 0 + + + 0 + + + + kcfg_ShowMoodbar + + + true + + + Use &moods + + + true + + + Displays a visual representation of the current track in the slider bar of the player window and a column in the playlist window. + + + Displays a visual representation of the current track in the slider bar of the player window and a column in the playlist window. + + + + + layout8_2_2 + + + + unnamed + + + + spacerMoodier + + + Horizontal + + + Fixed + + + + 16 + 21 + + + + + + kcfg_MakeMoodier + + + true + + + Make m&oodier: + + + When enabled, the hue distribution is quantised and respread evenly, giving a prettier but less meaningful output. + + + When enabled, the hue distribution is quantised and respread evenly, giving a prettier but less meaningful output. + + + + + + Happy Like a Rainbow + + + + + Angry as Hell + + + + + Frozen in the Arctic + + + + kcfg_AlterMood + + + false + + + + + spacer7_2_2_2 + + + Horizontal + + + Expanding + + + + 130 + 20 + + + + + + + + layout7_2_2 + + + + unnamed + + + + spacerWithMusic + + + Horizontal + + + Fixed + + + + 16 + 21 + + + + + + kcfg_MoodsWithMusic + + + true + + + Stor&e mood data files with music + + + Enabling this option stores Mood data files with the music files. Disabling stores them in your home folder. + + + Enabling this option stores Mood data files with the music files. Namely, the mood file for /music/file.mp3 will be /music/file.mood. Disabling stores them in your home folder. + + + + + + + + + + + groupBox2 + + + 0 + + + Playlist-Window Options + + + + unnamed + + + + kcfg_SavePlaylist + + + &Remember current playlist on exit + + + If checked, Amarok saves the current playlist on quit and restores it when restarted.<br> + + + If checked, Amarok saves the current playlist on quit and restores it when restarted. + + + + + kcfg_RelativePlaylist + + + Manually sa&ved playlists use relative path + + + If checked, Amarok uses a relative path for the tracks of manually saved playlists + + + If checked, Amarok uses a relative path for the tracks of manually saved playlists + + + + + kcfg_AutoShowContextBrowser + + + Switch to Context &Browser on track change + + + Switch to the context browser, when playing a track.<br> + + + Switch to the context browser, when playing a track. + + + + + + + spacer5 + + + Vertical + + + Expanding + + + + 20 + 30 + + + + + + + + kcfg_ShowTrayIcon + toggled(bool) + kcfg_AnimateTrayIcon + setEnabled(bool) + + + checkBox_customBrowser + toggled(bool) + kComboBox_browser + setDisabled(bool) + + + checkBox_customBrowser + toggled(bool) + kLineEdit_customBrowser + setEnabled(bool) + + + kcfg_ShowMoodbar + toggled(bool) + General + slotUpdateMoodFrame() + + + kcfg_MakeMoodier + toggled(bool) + General + slotUpdateMoodFrame() + + + + Options1.ui.h + + + slotUpdateMoodFrame() + + + init() + + + + kcombobox.h + klineedit.h + kactivelabel.h + + diff --git a/amarok/src/Options1.ui.h b/amarok/src/Options1.ui.h new file mode 100644 index 00000000..62b322ed --- /dev/null +++ b/amarok/src/Options1.ui.h @@ -0,0 +1,218 @@ +//Released under GPLv2 or later. (C) 2005 Ian Monroe +/**************************************************************************** +** ui.h extension file, included from the uic-generated form implementation. +** +** If you want to add, delete, or rename functions or slots, use +** Qt Designer to update this file, preserving your code. +** +** You should not define a constructor or destructor in this file. +** Instead, write your code in functions called init() and destroy(). +** These will automatically be called by the form's constructor and +** destructor. +*****************************************************************************/ + +#include + +#include "amarokconfig.h" +#include "moodbar.h" +#include "starmanager.h" + +#include +#include + +void Options1::init() +{ + slotUpdateMoodFrame(); + //kcfg_CustomRatingsColors->setChecked( AmarokConfig::customRatingsColors() ); + //slotUpdateRatingsFrame(); + + QStringList browsers; + browsers << "konqueror" << "firefox" << "opera" << "galeon" << "epiphany" + << "safari" << "mozilla"; + + // Remove browsers which are not actually installed + for( QStringList::Iterator it = browsers.begin(), end = browsers.end(); it != end; ) { + if( KStandardDirs::findExe( *it ).isEmpty() ) + it = browsers.erase( it ); + else + ++it; + } +#ifdef Q_WS_MAC + if ( KStandardDirs::findExe( "open" ) != QString::null ) + browsers.prepend( i18n( "Default Browser" ) ); +#else + if ( KStandardDirs::findExe( "kfmclient" ) != QString::null ) + browsers.prepend( i18n( "Default KDE Browser" ) ); +#endif + + kComboBox_browser->insertStringList( browsers ); + kLineEdit_customBrowser->setText( AmarokConfig::externalBrowser() ); + int index = browsers.findIndex( AmarokConfig::externalBrowser() ); + if( index >= 0 ) + kComboBox_browser->setCurrentItem( index ); + else if( AmarokConfig::externalBrowser() == +#ifdef Q_WS_MAC + "open" +#else + "kfmclient openURL" +#endif + ) + { + kComboBox_browser->setCurrentItem( 0 ); + } + else + { + checkBox_customBrowser->setChecked( true ); + } +} + +void Options1::slotUpdateMoodFrame() +{ + if( Moodbar::executableExists() ) + { + moodbarHelpLabel->hide(); + moodFrame->setEnabled(true); + + kcfg_MakeMoodier->setEnabled(kcfg_ShowMoodbar->isChecked()); + kcfg_AlterMood->setEnabled(kcfg_ShowMoodbar->isChecked() && kcfg_MakeMoodier->isChecked()); + kcfg_MoodsWithMusic->setEnabled(kcfg_ShowMoodbar->isChecked()); + } + + else + { + moodbarHelpLabel->show(); + kcfg_ShowMoodbar->setChecked(false); + moodFrame->setEnabled(false); + } +} + +/* +void Options1::slotUpdateRatingsFrame() +{ + kcfg_CustomRatingsColors->setEnabled( kcfg_UseRatings->isChecked() ); + bool enableStars = kcfg_UseRatings->isChecked() && kcfg_CustomRatingsColors->isChecked(); + kcfg_FixedHalfStarColor->setEnabled( enableStars ); + + AmarokConfig::setCustomRatingsColors( enableStars ); + + StarManager::instance()->reinitStars(); + ratingsFrame->setEnabled( enableStars ); + + fivestar_1->setPixmap( *StarManager::instance()->getStar( 5 ) ); + fivestar_2->setPixmap( *StarManager::instance()->getStar( 5 ) ); + fivestar_3->setPixmap( *StarManager::instance()->getStar( 5 ) ); + fivestar_4->setPixmap( *StarManager::instance()->getStar( 5 ) ); + fivestar_5->setPixmap( *StarManager::instance()->getStar( 5 ) ); + fivestar_1->setEnabled( enableStars ); + fivestar_2->setEnabled( enableStars ); + fivestar_3->setEnabled( enableStars ); + fivestar_4->setEnabled( enableStars ); + fivestar_5->setEnabled( enableStars ); + + fourstar_1->setPixmap( *StarManager::instance()->getStar( 4 ) ); + fourstar_2->setPixmap( *StarManager::instance()->getStar( 4 ) ); + fourstar_3->setPixmap( *StarManager::instance()->getStar( 4 ) ); + fourstar_4->setPixmap( *StarManager::instance()->getStar( 4 ) ); + fourstar_1->setEnabled( enableStars ); + fourstar_2->setEnabled( enableStars ); + fourstar_3->setEnabled( enableStars ); + fourstar_4->setEnabled( enableStars ); + + threestar_1->setPixmap( *StarManager::instance()->getStar( 3 ) ); + threestar_2->setPixmap( *StarManager::instance()->getStar( 3 ) ); + threestar_3->setPixmap( *StarManager::instance()->getStar( 3 ) ); + threestar_1->setEnabled( enableStars ); + threestar_2->setEnabled( enableStars ); + threestar_3->setEnabled( enableStars ); + + twostar_1->setPixmap( *StarManager::instance()->getStar( 2 ) ); + twostar_2->setPixmap( *StarManager::instance()->getStar( 2 ) ); + twostar_1->setEnabled( enableStars ); + twostar_2->setEnabled( enableStars ); + + onestar_1->setPixmap( *StarManager::instance()->getStar( 1 ) ); + onestar_1->setEnabled( enableStars ); + + halfstar->setPixmap( *StarManager::instance()->getHalfStar() ); + halfstar->setEnabled( enableStars ); +} + +void Options1::slotFixedHalfStarColor() +{ + bool checked = kcfg_FixedHalfStarColor->isChecked(); + AmarokConfig::setFixedHalfStarColor( kcfg_FixedHalfStarColor->isChecked() ); + slotUpdateRatingsFrame(); +} + +void Options1::slotPickColorHalf() +{ + QColor halfStar; + int result = KColorDialog::getColor( halfStar ); + if( result == KColorDialog::Accepted ) + { + AmarokConfig::setStarColorHalf( halfStar ); + StarManager::instance()->setHalfColor( halfStar ); + slotUpdateRatingsFrame(); + } +} + +void Options1::slotPickColorOne() +{ + QColor oneStar; + int result = KColorDialog::getColor( oneStar ); + if( result == KColorDialog::Accepted ) + { + AmarokConfig::setStarColorOne( oneStar ); + StarManager::instance()->setColor( 1, oneStar ); + slotUpdateRatingsFrame(); + } +} + +void Options1::slotPickColorTwo() +{ + QColor twoStar; + int result = KColorDialog::getColor( twoStar ); + if( result == KColorDialog::Accepted ) + { + AmarokConfig::setStarColorTwo( twoStar ); + StarManager::instance()->setColor( 2, twoStar ); + slotUpdateRatingsFrame(); + } +} + +void Options1::slotPickColorThree() +{ + QColor threeStar; + int result = KColorDialog::getColor( threeStar ); + if( result == KColorDialog::Accepted ) + { + AmarokConfig::setStarColorThree( threeStar ); + StarManager::instance()->setColor( 3, threeStar ); + slotUpdateRatingsFrame(); + } +} + +void Options1::slotPickColorFour() +{ + QColor fourStar; + int result = KColorDialog::getColor( fourStar ); + if( result == KColorDialog::Accepted ) + { + AmarokConfig::setStarColorFour( fourStar ); + StarManager::instance()->setColor( 4, fourStar ); + slotUpdateRatingsFrame(); + } +} + +void Options1::slotPickColorFive() +{ + QColor fiveStar; + int result = KColorDialog::getColor( fiveStar ); + if( result == KColorDialog::Accepted ) + { + AmarokConfig::setStarColorFive( fiveStar ); + StarManager::instance()->setColor( 5, fiveStar ); + slotUpdateRatingsFrame(); + } +} +*/ diff --git a/amarok/src/Options2.ui b/amarok/src/Options2.ui new file mode 100644 index 00000000..71f2a31e --- /dev/null +++ b/amarok/src/Options2.ui @@ -0,0 +1,768 @@ + +Options2 + + + Options2 + + + + 0 + 0 + 447 + 564 + + + + + 3 + 3 + 0 + 0 + + + + + unnamed + + + 0 + + + + groupBox6 + + + Icons + + + + unnamed + + + + kcfg_UseCustomIconTheme + + + Use custom icon &theme (requires restart) + + + Check to enable Amarok's custom icon theme.<br> + + + Check to enable Amarok's custom icon theme. + + + + + + + groupBox4 + + + Fonts + + + + unnamed + + + + kcfg_UseCustomFonts + + + &Use custom fonts + + + false + + + Check to enable custom fonts. + + + Check to enable custom fonts. + + + + + fontGroupBox + + + false + + + + 7 + 5 + 0 + 0 + + + + NoFrame + + + 0 + + + + + + true + + + + unnamed + + + + textLabel3 + + + + 1 + 5 + 0 + 0 + + + + Playlist Window: + + + The font to use in the playlist window. + + + The font to use in the playlist window. + + + + + kcfg_PlaylistWindowFont + + + + 5 + 5 + 0 + 0 + + + + + + + The font to use in the playlist window. + + + The font to use in the playlist window. + + + + + textLabel1 + + + + 1 + 5 + 0 + 0 + + + + Player Window: + + + AlignVCenter + + + The font to use in the player window. + + + The font to use in the player window. + + + + + textLabel3_2 + + + + 1 + 5 + 0 + 0 + + + + Context Sidebar: + + + The font to use in the context browser. + + + The font to use in the context browser. + + + + + kcfg_ContextBrowserFont + + + + 5 + 5 + 0 + 0 + + + + + + + The font to use in the context browser. + + + The font to use in the context browser. + + + + + kcfg_PlayerWidgetFont + + + + 5 + 5 + 0 + 0 + + + + + + + The font to use in the context browser. + + + The font to use in the context browser. + + + + + + + + + groupBox3 + + + Color Scheme + + + + unnamed + + + + radioGroup + + + NoFrame + + + + + + + unnamed + + + 0 + + + + layout2 + + + + unnamed + + + 0 + + + + kcfg_SchemeCustom + + + &Custom color scheme + + + true + + + If selected, Amarok uses the user-defined colors in the playlist. + + + If selected, Amarok uses the user-defined colors in the playlist. + + + + + customSchemeBox + + + true + + + NoFocus + + + NoFrame + + + + + + false + + + false + + + + unnamed + + + + fgLabel + + + true + + + + 4 + 5 + 0 + 0 + + + + + 0 + 0 + + + + Fo&reground: + + + AutoText + + + kcfg_PlaylistWindowFgColor + + + Selects the color to use as foreground color in the playlist. + + + + + kcfg_PlaylistWindowFgColor + + + true + + + + 5 + 0 + 2 + 0 + + + + + + + + 255 + 255 + 255 + + + + Click to select the foreground text color in the playlist window. + + + Selects the color to use as foreground (text) color in the playlist. + + + + + bgColor + + + true + + + + 4 + 5 + 0 + 0 + + + + &Background: + + + AutoText + + + kcfg_PlaylistWindowBgColor + + + Selects the color to use as background color in the playlist. + + + + + kcfg_PlaylistWindowBgColor + + + true + + + + 5 + 0 + 2 + 0 + + + + + 0 + 0 + + + + + 192 + 32767 + + + + + + + + 32 + 32 + 80 + + + + Click to select the background color in the playlist window. + + + Selects the color to use as background color in the playlist. + + + + + + + + + kcfg_SchemeKDE + + + The current &KDE color-scheme + + + 0 + + + If selected, Amarok uses the KDE standard colors in the playlist. + + + If selected, Amarok uses the KDE standard colors in the playlist. + + + + + kcfg_SchemeAmarok + + + The classic &Amarok, "funky-monkey", theme + + + 1 + + + If selected, Amarok uses the Amarok standard colors in the playlist. + + + If selected, Amarok uses the Amarok standard colors in the playlist. + + + + + + + line1 + + + HLine + + + Sunken + + + Horizontal + + + + + layout5 + + + + unnamed + + + + textLabel1_3 + + + Color for new playlist items: + + + kcfg_NewPlaylistItemsColor + + + The color that is used when fresh items are being loaded in the playlist. + + + + + kcfg_NewPlaylistItemsColor + + + + + + The color that is used when fresh items are being loaded in the playlist. + + + + + + + + + contextGroupBox + + + Context Browser Style + + + + unnamed + + + + layout6 + + + + unnamed + + + + textLabel1_2 + + + + 5 + 5 + 0 + 0 + + + + Select a style: + + + + + + + + styleComboBox + + + Select the style of the Context Browser. + + + Select the style of the Context Browser. + + + + + + + layout5 + + + + unnamed + + + + installPushButton + + + Install New Style... + + + Click to install a new Context Browser style.<br>Tip: More styles can be found on <a href='http://kde-look.org'>http://kde-look.org</a> + + + Select and install a new Context Browser style. + + + + + retrievePushButton + + + Download Styles... + + + Click to download new Context Browser styles. + + + Select and download new Context Browser styles. + + + + + spacer2 + + + Horizontal + + + Expanding + + + + 40 + 20 + + + + + + uninstallPushButton + + + Uninstall Style + + + Click to uninstall the selected Context Browser style. + + + Uninstall the selected Context Browser style. + + + + + + + + + spacer1 + + + Vertical + + + Expanding + + + + 20 + 30 + + + + + + + + kcfg_UseCustomFonts + toggled(bool) + fontGroupBox + setEnabled(bool) + + + kcfg_SchemeCustom + toggled(bool) + customSchemeBox + setEnabled(bool) + + + installPushButton + clicked() + Options2 + installPushButton_clicked() + + + retrievePushButton + clicked() + Options2 + retrievePushButton_clicked() + + + uninstallPushButton + clicked() + Options2 + uninstallPushButton_clicked() + + + styleComboBox + activated(const QString&) + Options2 + styleComboBox_activated(const QString&) + + + + Options2.ui.h + + + installPushButton_clicked() + retrievePushButton_clicked() + uninstallPushButton_clicked() + styleComboBox_activated( const QString & s ) + updateStyleComboBox() + + + init() + + + + kfontrequester.h + kfontrequester.h + kfontrequester.h + kcolorbutton.h + kcolorbutton.h + kcolorbutton.h + kcombobox.h + + diff --git a/amarok/src/Options2.ui.h b/amarok/src/Options2.ui.h new file mode 100644 index 00000000..e3f0a54f --- /dev/null +++ b/amarok/src/Options2.ui.h @@ -0,0 +1,189 @@ +/**************************************************************************** +** ui.h extension file, included from the uic-generated form implementation. +** +** If you want to add, delete, or rename functions or slots, use +** Qt Designer to update this file, preserving your code. +** +** You should not define a constructor or destructor in this file. +** Instead, write your code in functions called init() and destroy(). +** These will automatically be called by the form's constructor and +** destructor. +*****************************************************************************/ + +#include "amarok.h" +#include "amarokconfig.h" +#include "debug.h" +#include "contextbrowser.h" + +#include +#include +#include +#include // knewstuff theme fetching +#include // " +#include // " +#include // " +#include +#include +#include + +#include +#include +#include + + +//////////////////////////////////////////////////////////////////////////////// +// class AmarokThemeNewStuff +//////////////////////////////////////////////////////////////////////////////// + +/** + * GHNS Customised Download implementation. + */ +class AmarokThemeNewStuff : public KNewStuff +{ + public: + AmarokThemeNewStuff(const QString &type, QWidget *parentWidget=0) + : KNewStuff( type, parentWidget ) + {} + + bool install( const QString& fileName ) + { + KTar archive( fileName ); + + if ( !archive.open( IO_ReadOnly ) ) { + KMessageBox::sorry( 0, i18n( "Could not read this package." ) ); + return false; + } + + const QString destination = Amarok::saveLocation( "themes/" ); + debug() << "copying to " << destination << endl; + const KArchiveDirectory* archiveDir = archive.directory(); + archiveDir->copyTo( destination, true ); + + return true; + } + + virtual bool createUploadFile( const QString& ) { return false; } +}; + + +//////////////////////////////////////////////////////////////////////////////// +// class Options2 +//////////////////////////////////////////////////////////////////////////////// + +void Options2::init() +{ + updateStyleComboBox(); + uninstallPushButton->setEnabled ( styleComboBox->currentText() != "Default" ); +} + + +// This method is basically lifted from ScriptManager::slotInstallScript() +void Options2::installPushButton_clicked() +{ + KFileDialog dia( QString::null, "*.tar *.tar.bz2 *.tar.gz|" + i18n( "Style Packages (*.tar, *.tar.bz2, *.tar.gz)" ), 0, 0, true ); + kapp->setTopWidget( &dia ); + dia.setCaption( kapp->makeStdCaption( i18n( "Select Style Package" ) ) ); + dia.setMode( KFile::File | KFile::ExistingOnly ); + if ( !dia.exec() ) return; + + KTar archive( dia.selectedURL().path() ); + + if ( !archive.open( IO_ReadOnly ) ) { + KMessageBox::sorry( 0, i18n( "Could not read this package." ) ); + return; + } + + const QString destination = Amarok::saveLocation( "themes/" ); + debug() << "copying to " << destination << endl; + const KArchiveDirectory* archiveDir = archive.directory(); + archiveDir->copyTo( destination, true ); + + updateStyleComboBox(); +} + + + +void Options2::retrievePushButton_clicked() +{ + // Delete KNewStuff's configuration entries. These entries reflect which styles + // are already installed. As we cannot yet keep them in sync after uninstalling + // styles, we deactivate the check marks entirely. + Amarok::config()->deleteGroup( "KNewStuffStatus" ); + + // we need this because KNewStuffGeneric's install function isn't clever enough + AmarokThemeNewStuff *kns = new AmarokThemeNewStuff( "amarok/themes", this ); + KNS::Engine *engine = new KNS::Engine( kns, "amarok/theme", this ); + KNS::DownloadDialog* d = new KNS::DownloadDialog( engine, this ); + d->setType( "amarok/theme" ); + // you have to do this by hand when providing your own Engine + KNS::ProviderLoader *p = new KNS::ProviderLoader( this ); + connect( p, SIGNAL( providersLoaded(Provider::List*) ), d, SLOT( slotProviders(Provider::List *) ) ); + p->load( "amarok/theme", "http://amarok.kde.org/knewstuff/amarokthemes-providers.xml" ); + + connect( d, SIGNAL( finished() ), d, SLOT( delayedDestruct() ) ); + connect( d, SIGNAL( finished() ), this, SLOT( updateStyleComboBox() ) ); + + // Due to kdelibs idiocy, KNS::DownloadDialog is /always/ non-modal. So we have to + // ensure that closing the settings dialog before the DownloadDialog doesn't crash. + QTimer::singleShot( 0, d, SLOT( exec() ) ); +} + + +void Options2::uninstallPushButton_clicked() +{ + const QString name = styleComboBox->currentText(); + + if ( name == "Default" ) + return; + + if( KMessageBox::warningContinueCancel( 0, + i18n( "

Are you sure you want to uninstall the theme %1?

" ).arg( name ), + i18n("Uninstall Theme"), i18n("Uninstall") ) == KMessageBox::Cancel ) + return; + + if ( name == AmarokConfig::contextBrowserStyleSheet() ) { + AmarokConfig::setContextBrowserStyleSheet( "Default" ); + ContextBrowser::instance()->reloadStyleSheet(); + } + + KURL themeDir( KURL::fromPathOrURL( Amarok::saveLocation( "themes/" ) ) ); + themeDir.addPath( name ); + + if( !KIO::NetAccess::del( themeDir, 0 ) ) { + KMessageBox::sorry( 0, i18n( "

Could not uninstall this theme.

" + "

You may not have sufficient permissions to delete the folder %1

." + ).arg( themeDir.isLocalFile() ? themeDir.path() : themeDir.url() ) ); + return; + } + + updateStyleComboBox(); +} + + +void Options2::styleComboBox_activated(const QString& s) +{ + bool disable = false; + QDir dir( Amarok::saveLocation( "themes/" ) + s ); + if( !dir.exists() ) + disable = true; + + uninstallPushButton->setEnabled ( !disable ); +} + + +void Options2::updateStyleComboBox() +{ + DEBUG_BLOCK + + styleComboBox->clear(); + + const QStringList styleList = kapp->dirs()->findAllResources("data","amarok/themes/*/stylesheet.css", false); + QStringList sortedList; + foreach (styleList) sortedList.append(QFileInfo( *it ).dir().dirName()); + sortedList.append( "Default" ); + sortedList.sort(); + foreach(sortedList) styleComboBox->insertItem(*it); + + styleComboBox->setCurrentItem(AmarokConfig::contextBrowserStyleSheet()); +} + diff --git a/amarok/src/Options4.ui b/amarok/src/Options4.ui new file mode 100644 index 00000000..f55eab85 --- /dev/null +++ b/amarok/src/Options4.ui @@ -0,0 +1,622 @@ + +Options4 + + + Options4 + + + + 0 + 0 + 455 + 447 + + + + + 3 + 3 + 0 + 0 + + + + + unnamed + + + + opt_crossfade + + + + 5 + 5 + 0 + 0 + + + + + 0 + 180 + + + + GroupBoxPanel + + + Sunken + + + &Transition + + + <b>Transition Behavior</b> +<p>During playback, when Amarok transitions between tracks, it can either proceed to the next track instantly (with configurable gap), or crossfade (with configurable fade period).</p> + + + + unnamed + + + + radioButtonNormalPlayback + + + &No crossfading + + + true + + + Enable normal track transition. You may insert a gap of silence between tracks. + + + + + layout11 + + + + unnamed + + + + trackDelayLengthLabel + + + + 4 + 1 + 0 + 0 + + + + + 150 + 0 + + + + Insert &gap: + + + kcfg_TrackDelayLength + + + + + kcfg_TrackDelayLength + + + + 3 + 0 + 0 + 0 + + + + + 160 + 0 + + + + ms + + + 10000 + + + 100 + + + 0 + + + Silence between tracks, in milliseconds. + + + + + + + kcfg_Crossfade + + + &Crossfading + + + false + + + Enable crossfading between tracks. + + + + + layout9 + + + + unnamed + + + + layout7 + + + + unnamed + + + + crossfadeLengthLabel + + + false + + + + 4 + 1 + 0 + 0 + + + + + 150 + 0 + + + + Crosso&ver duration: + + + kcfg_CrossfadeLength + + + + + crossfadeDropdownText + + + false + + + + 4 + 1 + 0 + 0 + + + + + 150 + 0 + + + + Crossfa&de: + + + kcfg_CrossfadeType + + + + + + + layout8 + + + + unnamed + + + + kcfg_CrossfadeLength + + + false + + + + 5 + 4 + 0 + 0 + + + + + 50 + 0 + + + + ms + + + 99999999 + + + 100 + + + 100 + + + 100 + + + The length of the crossfade between tracks, in milliseconds. + + + + + + Always + + + + + On Automatic Track Change Only + + + + + On Manual Track Change Only + + + + kcfg_CrossfadeType + + + false + + + + 5 + 4 + 0 + 0 + + + + + 50 + 0 + + + + false + + + Select when you want crossfading to occur + + + Select when you want crossfading to occur + + + + + + + + + spacer76 + + + Horizontal + + + Fixed + + + + 16 + 20 + + + + + + + + kcfg_FadeoutOnExit + + + Fade out on e&xit + + + If checked, Amarok will fade out the music on program exit. + + + + + kcfg_ResumePlayback + + + &Resume playback on start + + + If checked, Amarok will<br>resume playback from where you left it the previous session -- just like a tape-player. + + + + + spacer2 + + + Vertical + + + Expanding + + + + 31 + 30 + + + + + + opt_crossfade_2 + + + + 5 + 5 + 0 + 0 + + + + + 0 + 120 + + + + GroupBoxPanel + + + Sunken + + + &Fadeout + + + <b>Transition Behavior</b> +<p>During playback, when Amarok transitions between tracks, it can either proceed to the next track instantly (with configurable gap), or crossfade (with configurable fade period).</p> + + + + unnamed + + + + radioButtonNormalPlayback_2 + + + No &fadeout + + + true + + + Disable fadeout. Music will stop immediately. + + + + + spacer76_2 + + + Horizontal + + + Fixed + + + + 16 + 20 + + + + + + layout9_2 + + + + unnamed + + + + layout7_2 + + + + unnamed + + + + fadeoutLengthLabel + + + false + + + + 4 + 1 + 0 + 0 + + + + + 150 + 0 + + + + Fadeout &duration: + + + kcfg_FadeoutLength + + + + + + + layout8_2 + + + + unnamed + + + + kcfg_FadeoutLength + + + false + + + + 5 + 4 + 0 + 0 + + + + + 50 + 0 + + + + ms + + + 99999999 + + + 100 + + + 100 + + + 100 + + + The length of the fadeout, in milliseconds. + + + + + + + + + kcfg_Fadeout + + + Fade&out + + + false + + + Fade the music out when the Stop button is pressed. + + + + + + + + + radioButtonNormalPlayback + toggled(bool) + kcfg_TrackDelayLength + setEnabled(bool) + + + kcfg_Crossfade + toggled(bool) + kcfg_CrossfadeLength + setEnabled(bool) + + + radioButtonNormalPlayback + toggled(bool) + trackDelayLengthLabel + setEnabled(bool) + + + kcfg_Crossfade + toggled(bool) + crossfadeLengthLabel + setEnabled(bool) + + + kcfg_Crossfade + toggled(bool) + crossfadeDropdownText + setEnabled(bool) + + + kcfg_Crossfade + toggled(bool) + kcfg_CrossfadeType + setEnabled(bool) + + + kcfg_Fadeout + toggled(bool) + fadeoutLengthLabel + setEnabled(bool) + + + kcfg_Fadeout + toggled(bool) + kcfg_FadeoutLength + setEnabled(bool) + + + + diff --git a/amarok/src/Options5.ui b/amarok/src/Options5.ui new file mode 100644 index 00000000..50c54cc8 --- /dev/null +++ b/amarok/src/Options5.ui @@ -0,0 +1,530 @@ + +Options5 + + + Options5 + + + + 0 + 0 + 481 + 472 + + + + + unnamed + + + 0 + + + + kcfg_OsdEnabled + + + &Use On-Screen-Display + + + true + + + Check to enable the On-Screen-Display. <br>The OSD briefly displays track data when a new track is played. + + + Check to enable the On-Screen-Display. The OSD briefly displays track data when a new track is played. + + + + + layout2 + + + + unnamed + + + + spacer2 + + + Horizontal + + + Fixed + + + + 16 + 20 + + + + + + mainBox + + + NoFrame + + + + + + + unnamed + + + 0 + + + + fontBox + + + GroupBoxPanel + + + Sunken + + + 1 + + + &Font + + + false + + + + unnamed + + + + kcfg_OsdFont + + + + + + + + + + The font to use for the On-Screen Display. + + + The font to use for the On-Screen Display. + + + + + kcfg_OsdDrawShadow + + + Draw &shadow + + + + + + + groupBox9 + + + C&olors + + + + unnamed + + + + layout3 + + + + unnamed + + + 2 + + + + kcfg_OsdUseCustomColors + + + Use &custom colors + + + Check to enable custom colors for the On-Screen-Display. + + + Check to enable custom colors for the On-Screen-Display. + + + + + layout1 + + + + unnamed + + + + spacer1 + + + Horizontal + + + Fixed + + + + 16 + 20 + + + + + + colorsBox + + + false + + + NoFrame + + + + + + false + + + + unnamed + + + 0 + + + + textLabel2_2 + + + + 5 + 5 + 1 + 0 + + + + NoFrame + + + Background color: + + + The color of the OSD background. + + + + + kcfg_OsdTextColor + + + + 5 + 0 + 2 + 0 + + + + + + + + 255 + 0 + 0 + + + + Click to select the color of the OSD text. + + + The color of the OSD text. + + + + + kcfg_OsdBackgroundColor + + + + 5 + 0 + 2 + 0 + + + + + + + + 255 + 0 + 0 + + + + Click to select the background color of the OSD. + + + The color of the OSD background. + + + + + textLabel2 + + + + 5 + 5 + 1 + 0 + + + + Text color: + + + The color of the OSD text. + + + + + + + + + + + kcfg_OsdUseFakeTranslucency + + + Make the &background translucent + + + + + + + osdText + + + GroupBoxPanel + + + Display &Text + + + false + + + + unnamed + + + + kcfg_OsdUsePlaylistColumns + + + Display the same information as the columns in the playlist + + + + + kcfg_OsdText + + + + 7 + 7 + 0 + 0 + + + + 2 + + + PlainText + + + true + + + + + + + groupBox22 + + + + + + + unnamed + + + + textLabel1 + + + &Duration: + + + kcfg_OsdDuration + + + + + kcfg_OsdDuration + + + + 1 + 0 + 2 + 0 + + + + ms + + + Forever + + + UpDownArrows + + + 600000 + + + 0 + + + 1000 + + + 5000 + + + The time in milliseconds to show the OSD. The value must be between 500 ms and 10000 ms. + + + The time in milliseconds to show the OSD. The value must be between 500 ms and 10000 ms. + + + + + kcfg_OsdScreen + + + + 1 + 0 + 2 + 0 + + + + The screen that should display the OSD. + + + The screen that should display the OSD. + + + + + textLabel2_3 + + + Sc&reen: + + + kcfg_OsdScreen + + + + + + + + + + + + + kcfg_OsdUseCustomColors + toggled(bool) + colorsBox + setEnabled(bool) + + + kcfg_OsdEnabled + toggled(bool) + mainBox + setEnabled(bool) + + + kcfg_OsdUseCustomColors + toggled(bool) + Options5 + useCustomColorsToggled(bool) + + + kcfg_OsdUsePlaylistColumns + toggled(bool) + kcfg_OsdText + setDisabled(bool) + + + + qapplication.h + qdesktopwidget.h + osd.h + + + OSDPreviewWidget *m_pOSDPreview; + + + settingsChanged() + + + slotPositionChanged() + useCustomColorsToggled( bool on ) + + + init() + hideEvent( QHideEvent * ) + showEvent( QShowEvent * ) + + + + ktextedit.h + + diff --git a/amarok/src/Options5.ui.h b/amarok/src/Options5.ui.h new file mode 100644 index 00000000..00ad3023 --- /dev/null +++ b/amarok/src/Options5.ui.h @@ -0,0 +1,126 @@ +/*************************************************************************** +begin : 2004/02/25 +copyright : (C) Frederik Holljen +email : fh@ez.no +***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + + +/**************************************************************************** +** ui.h extension file, included from the uic-generated form implementation. +** +** If you wish to add, delete or rename functions or slots use +** Qt Designer which will update this file, preserving your code. Create an +** init() function in place of a constructor, and a destroy() function in +** place of a destructor. +*****************************************************************************/ + +#include "amarokconfig.h" +#include +#include "qstringx.h" +#include + + +void Options5::init() +{ + m_pOSDPreview = new OSDPreviewWidget( this ); //must be child!!! + m_pOSDPreview->setAlignment( static_cast( AmarokConfig::osdAlignment() ) ); + m_pOSDPreview->setOffset( AmarokConfig::osdYOffset() ); + + connect( m_pOSDPreview, SIGNAL( positionChanged() ), SLOT( slotPositionChanged() ) ); + + const int numScreens = QApplication::desktop()->numScreens(); + for( int i = 0; i < numScreens; i++ ) + kcfg_OsdScreen->insertItem( QString::number( i ) ); + + connect( kcfg_OsdDrawShadow, SIGNAL( toggled(bool) ), + m_pOSDPreview, SLOT( setDrawShadow(bool) ) ); + connect( kcfg_OsdTextColor, SIGNAL( changed(const QColor&) ), + m_pOSDPreview, SLOT( setTextColor(const QColor&) ) ); + connect( kcfg_OsdUseCustomColors, SIGNAL( toggled(bool) ), + this, SLOT( useCustomColorsToggled(bool) ) ); + connect( kcfg_OsdBackgroundColor, SIGNAL( changed(const QColor&) ), + m_pOSDPreview, SLOT( setBackgroundColor(const QColor&) ) ); + connect( kcfg_OsdFont, SIGNAL( fontSelected(const QFont&) ), + m_pOSDPreview, SLOT( setFont(const QFont&) ) ); + connect( kcfg_OsdScreen, SIGNAL( activated(int) ), + m_pOSDPreview, SLOT( setScreen(int) ) ); + connect( kcfg_OsdEnabled, SIGNAL( toggled(bool) ), + m_pOSDPreview, SLOT( setShown(bool) ) ); + + Amarok::QStringx text = i18n( + "

Tags Displayed in OSD

" + "You can use the following tokens:" + "
    " + "
  • Title - %1" + "
  • Album - %2" + "
  • Artist - %3" + "
  • Genre - %4" + "
  • Bitrate - %5" + "
  • Year - %6" + "
  • Track Length - %7" + "
  • Track Number - %8" + "
  • Filename - %9" + "
  • Directory - %10" + "
  • Type - %11" + "
  • Comment - %12" + "
  • Score - %13" + "
  • Playcount - %14" + "
  • Disc Number - %15" + "
  • Rating - %16" + "
  • Moodbar - %17" + "
  • Elapsed Time - %18" + "
" + "If you surround sections of text that contain a token with curly-braces, that section will be hidden if the token is empty, for example:" + "
%19
" + "Will not show Score: %score if the track has no score." ); + + QToolTip::add( kcfg_OsdText, text.args( QStringList() + // we don't translate these, it is not sensible to do so + << "%title" << "%album" << "%artist" << "%genre" << "%bitrate" + << "%year " << "%length" << "%track" << "%filename" << "%directory" + << "%type" << "%comment" << "%score" << "%playcount" << "%discnumber" + << "%rating" << "%moodbar" << "%elapsed" + << "%title {" + i18n( "Score: %1" ).arg( "%score" ) +'}' ) ); +} + +void +Options5::slotPositionChanged() +{ + kcfg_OsdScreen->blockSignals( true ); + kcfg_OsdScreen->setCurrentItem( m_pOSDPreview->screen() ); + kcfg_OsdScreen->blockSignals( false ); + + // Update button states (e.g. "Apply") + emit settingsChanged(); +} + +void +Options5::hideEvent( QHideEvent* ) +{ + m_pOSDPreview->hide(); +} + +void +Options5::showEvent( QShowEvent* ) +{ + useCustomColorsToggled( kcfg_OsdUseCustomColors->isChecked() ); + + m_pOSDPreview->setFont( kcfg_OsdFont->font() ); + m_pOSDPreview->setScreen( kcfg_OsdScreen->currentItem() ); + m_pOSDPreview->setShown( kcfg_OsdEnabled->isChecked() ); +} + +void +Options5::useCustomColorsToggled( bool on ) +{ + m_pOSDPreview->setUseCustomColors( on, kcfg_OsdTextColor->color(), kcfg_OsdBackgroundColor->color() ); +} diff --git a/amarok/src/Options7.ui b/amarok/src/Options7.ui new file mode 100644 index 00000000..56cf6c3c --- /dev/null +++ b/amarok/src/Options7.ui @@ -0,0 +1,81 @@ + +Options7 + + + Options7 + + + + 0 + 0 + 404 + 215 + + + + Collection Setup + + + + unnamed + + + 0 + + + + collectionFoldersBox + + + Collection Folders + + + + + databaseBox + + + Collection Database + + + + unnamed + + + + dbSetupFrame + + + + + + + + + DbSetup +
dbsetup.h
+ + 380 + 165 + + 1 + + 3 + 3 + 0 + 0 + + image0 +
+
+ + + 789c534e494dcbcc4b554829cdcdad8c2fcf4c29c95030e0524611cd48cd4ccf28010a1797249664262b2467241641a592324b8aa363156c15aab914146aadb90067111b1f + + + + + klineedit.h + kpushbutton.h + +
diff --git a/amarok/src/Options8.ui b/amarok/src/Options8.ui new file mode 100644 index 00000000..6ad0d1de --- /dev/null +++ b/amarok/src/Options8.ui @@ -0,0 +1,305 @@ + +Options8 + + + Options8 + + + + 0 + 0 + 425 + 418 + + + + + 1 + 1 + + + + + unnamed + + + 0 + + + 12 + + + + layout2 + + + + unnamed + + + 12 + + + + infoPixmap_2 + + + + 4 + 1 + 0 + 0 + + + + + + + AlignVCenter + + + + + kActiveLabel3 + + + + 5 + 4 + 0 + 0 + + + + + -1 + -1 + + + + Amarok can send the name of every song you play to last.fm. The system automatically matches you to people with similar musical tastes, and creates personalized recommendations. To learn more about last.fm, <A href='http://www.last.fm'>visit the homepage</A>. + + + + + + + groupBox3 + + + last.fm Profile + + + + unnamed + + + + kActiveLabel1 + + + + 5 + 5 + 0 + 0 + + + + <P>To use last.fm with Amarok, you need a <A href='http://www.last.fm:80/signup.php'>last.fm profile</A>. + + + + + layout3 + + + + unnamed + + + + kcfg_ScrobblerUsername + + + + 7 + 0 + 0 + 0 + + + + + + labelPassword + + + + 5 + 5 + 0 + 0 + + + + &Password: + + + kcfg_ScrobblerPassword + + + + + labelUsername + + + + 5 + 5 + 0 + 0 + + + + &Username: + + + kcfg_ScrobblerUsername + + + + + kcfg_ScrobblerPassword + + + + 7 + 0 + 0 + 0 + + + + Password + + + + + + + + + groupBox2 + + + false + + + last.fm Services + + + + unnamed + + + + textLabel1 + + + Once registered, Amarok can tell the last.fm service about your listening habits; your profile can then provide statistics and recommendations. A profile is not required to retrieve similar-artists for display in the Context Browser. + + + RichText + + + + + kcfg_SubmitPlayedSongs + + + false + + + + 7 + 5 + 0 + 0 + + + + Improve m&y profile by submitting the tracks I play + + + + + kcfg_RetrieveSimilarArtists + + + false + + + &Retrieve similar artists + + + + + + + kActiveLabel2 + + + + 5 + 5 + 0 + 0 + + + + Why not join the <A href='http://www.last.fm:80/group/Amarok+Users'>Amarok last.fm group</A> and share your musical tastes with other Amarok users? + + + + + spacer4 + + + Vertical + + + Expanding + + + + 20 + 30 + + + + + + + + kcfg_ScrobblerUsername + textChanged(const QString&) + Options8 + updateServices(const QString&) + + + + Options8.ui.h + + + updateServices( const QString & s ) + + + + kactivelabel.h + kactivelabel.h + klineedit.h + klineedit.h + kactivelabel.h + + diff --git a/amarok/src/Options8.ui.h b/amarok/src/Options8.ui.h new file mode 100644 index 00000000..d6341f6f --- /dev/null +++ b/amarok/src/Options8.ui.h @@ -0,0 +1,22 @@ +// (c) 2006 Seb Ruiz + +/**************************************************************************** +** ui.h extension file, included from the uic-generated form implementation. +** +** If you want to add, delete, or rename functions or slots, use +** Qt Designer to update this file, preserving your code. +** +** You should not define a constructor or destructor in this file. +** Instead, write your code in functions called init() and destroy(). +** These will automatically be called by the form's constructor and +** destructor. +*****************************************************************************/ + +void Options8::updateServices( const QString &s ) +{ + bool empty = s.isEmpty(); + groupBox2->setEnabled( !empty ); + kcfg_SubmitPlayedSongs->setEnabled( !empty ); + kcfg_RetrieveSimilarArtists->setEnabled( !empty ); +} + diff --git a/amarok/src/actionclasses.cpp b/amarok/src/actionclasses.cpp new file mode 100644 index 00000000..d2330e61 --- /dev/null +++ b/amarok/src/actionclasses.cpp @@ -0,0 +1,665 @@ +// Maintainer: Max Howell , (C) 2004 +// Copyright: See COPYING file that comes with this distribution + +#include "config.h" //HAVE_LIBVISUAL definition + +#include "actionclasses.h" +#include "amarok.h" +#include "amarokconfig.h" +#include "app.h" +#include "debug.h" +#include "collectiondb.h" +#include "covermanager.h" +#include "enginecontroller.h" +#include "k3bexporter.h" +#include "mediumpluginmanager.h" +#include "playlistwindow.h" +#include "playlist.h" +#include "socketserver.h" //Vis::Selector::showInstance() +#include "threadmanager.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Amarok +{ + bool repeatNone() { return AmarokConfig::repeat() == AmarokConfig::EnumRepeat::Off; } + bool repeatTrack() { return AmarokConfig::repeat() == AmarokConfig::EnumRepeat::Track; } + bool repeatAlbum() { return AmarokConfig::repeat() == AmarokConfig::EnumRepeat::Album; } + bool repeatPlaylist() { return AmarokConfig::repeat() == AmarokConfig::EnumRepeat::Playlist; } + bool randomOff() { return AmarokConfig::randomMode() == AmarokConfig::EnumRandomMode::Off; } + bool randomTracks() { return AmarokConfig::randomMode() == AmarokConfig::EnumRandomMode::Tracks; } + bool randomAlbums() { return AmarokConfig::randomMode() == AmarokConfig::EnumRandomMode::Albums; } + bool favorNone() { return AmarokConfig::favorTracks() == AmarokConfig::EnumFavorTracks::Off; } + bool favorScores() { return AmarokConfig::favorTracks() == AmarokConfig::EnumFavorTracks::HigherScores; } + bool favorRatings() { return AmarokConfig::favorTracks() == AmarokConfig::EnumFavorTracks::HigherRatings; } + bool favorLastPlay() { return AmarokConfig::favorTracks() == AmarokConfig::EnumFavorTracks::LessRecentlyPlayed; } + bool entireAlbums() { return repeatAlbum() || randomAlbums(); } +} + +using namespace Amarok; + +KHelpMenu *Menu::s_helpMenu = 0; + +static void +safePlug( KActionCollection *ac, const char *name, QWidget *w ) +{ + if( ac ) + { + KAction *a = ac->action( name ); + if( a ) a->plug( w ); + } +} + + +////////////////////////////////////////////////////////////////////////////////////////// +// MenuAction && Menu +// KActionMenu doesn't work very well, so we derived our own +////////////////////////////////////////////////////////////////////////////////////////// + +MenuAction::MenuAction( KActionCollection *ac ) + : KAction( i18n( "Amarok Menu" ), 0, ac, "amarok_menu" ) +{ + setShortcutConfigurable ( false ); //FIXME disabled as it doesn't work, should use QCursor::pos() +} + +int +MenuAction::plug( QWidget *w, int index ) +{ + KToolBar *bar = dynamic_cast(w); + + if( bar && kapp->authorizeKAction( name() ) ) + { + const int id = KAction::getToolButtonID(); + + addContainer( bar, id ); + connect( bar, SIGNAL( destroyed() ), SLOT( slotDestroyed() ) ); + + //TODO create menu on demand + //TODO create menu above and aligned within window + //TODO make the arrow point upwards! + bar->insertButton( QString::null, id, true, i18n( "Menu" ), index ); + bar->alignItemRight( id ); + + KToolBarButton* button = bar->getButton( id ); + button->setPopup( Amarok::Menu::instance() ); + button->setName( "toolbutton_amarok_menu" ); + button->setIcon( "amarok" ); + + return containerCount() - 1; + } + else return -1; +} + +Menu::Menu() +{ + KActionCollection *ac = Amarok::actionCollection(); + + setCheckable( true ); + + safePlug( ac, "repeat", this ); + safePlug( ac, "random_mode", this ); + + insertSeparator(); + + safePlug( ac, "playlist_playmedia", this ); + safePlug( ac, "play_audiocd", this ); + safePlug( ac, "lastfm_play", this ); + + insertSeparator(); + + insertItem( SmallIconSet( Amarok::icon( "covermanager" ) ), i18n( "C&over Manager" ), ID_SHOW_COVER_MANAGER ); + safePlug( ac, "queue_manager", this ); + insertItem( SmallIconSet( Amarok::icon( "visualizations" ) ), i18n( "&Visualizations" ), ID_SHOW_VIS_SELECTOR ); + insertItem( SmallIconSet( Amarok::icon( "equalizer" ) ), i18n( "E&qualizer" ), kapp, SLOT( slotConfigEqualizer() ), 0, ID_CONFIGURE_EQUALIZER ); + safePlug( ac, "script_manager", this ); + safePlug( ac, "statistics", this ); + + insertSeparator(); + + + safePlug( ac, "update_collection", this ); + insertItem( SmallIconSet( Amarok::icon( "rescan" ) ), i18n("&Rescan Collection"), ID_RESCAN_COLLECTION ); + setItemEnabled( ID_RESCAN_COLLECTION, !ThreadManager::instance()->isJobPending( "CollectionScanner" ) ); + +#ifndef Q_WS_MAC + insertSeparator(); + + safePlug( ac, KStdAction::name(KStdAction::ShowMenubar), this ); +#endif + + insertSeparator(); + + safePlug( ac, KStdAction::name(KStdAction::ConfigureToolbars), this ); + safePlug( ac, KStdAction::name(KStdAction::KeyBindings), this ); + safePlug( ac, "options_configure_globals", this ); //we created this one + safePlug( ac, KStdAction::name(KStdAction::Preferences), this ); + + insertSeparator(); + + insertItem( SmallIconSet("help"), i18n( "&Help" ), helpMenu( this ) ); + + insertSeparator(); + + safePlug( ac, KStdAction::name(KStdAction::Quit), this ); + + connect( this, SIGNAL( aboutToShow() ), SLOT( slotAboutToShow() ) ); + connect( this, SIGNAL( activated(int) ), SLOT( slotActivated(int) ) ); + + setItemEnabled( ID_SHOW_VIS_SELECTOR, false ); + #ifdef HAVE_LIBVISUAL + setItemEnabled( ID_SHOW_VIS_SELECTOR, true ); + #endif +} + +Menu* +Menu::instance() +{ + static Menu menu; + return &menu; +} + +KPopupMenu* +Menu::helpMenu( QWidget *parent ) //STATIC +{ + extern KAboutData aboutData; + + if ( s_helpMenu == 0 ) + s_helpMenu = new KHelpMenu( parent, &aboutData, Amarok::actionCollection() ); + + return s_helpMenu->menu(); +} + +void +Menu::slotAboutToShow() +{ + setItemEnabled( ID_CONFIGURE_EQUALIZER, EngineController::hasEngineProperty( "HasEqualizer" ) ); + setItemEnabled( ID_CONF_DECODER, EngineController::hasEngineProperty( "HasConfigure" ) ); +} + +void +Menu::slotActivated( int index ) +{ + switch( index ) + { + case ID_SHOW_COVER_MANAGER: + CoverManager::showOnce(); + break; + case ID_SHOW_VIS_SELECTOR: + Vis::Selector::instance()->show(); //doing it here means we delay creation of the widget + break; + case ID_RESCAN_COLLECTION: + CollectionDB::instance()->startScan(); + break; + } +} + +////////////////////////////////////////////////////////////////////////////////////////// +// PlayPauseAction +////////////////////////////////////////////////////////////////////////////////////////// + +PlayPauseAction::PlayPauseAction( KActionCollection *ac ) + : KToggleAction( i18n( "Play/Pause" ), 0, ac, "play_pause" ) + , EngineObserver( EngineController::instance() ) +{ + engineStateChanged( EngineController::engine()->state() ); + + connect( this, SIGNAL(activated()), EngineController::instance(), SLOT(playPause()) ); +} + +void +PlayPauseAction::engineStateChanged( Engine::State state, Engine::State /*oldState*/ ) +{ + QString text; + + switch( state ) { + case Engine::Playing: + setChecked( false ); + setIcon( Amarok::icon( "pause" ) ); + text = i18n( "Pause" ); + break; + case Engine::Paused: + setChecked( true ); + setIcon( Amarok::icon( "pause" ) ); + text = i18n( "Pause" ); + break; + case Engine::Empty: + setChecked( false ); + setIcon( Amarok::icon( "play" ) ); + text = i18n( "Play" ); + break; + case Engine::Idle: + return; + } + + //update menu texts for this special action + for( int x = 0; x < containerCount(); ++x ) { + QWidget *w = container( x ); + if( w->inherits( "QPopupMenu" ) ) + static_cast(w)->changeItem( itemId( x ), text ); + //TODO KToolBar sucks so much +// else if( w->inherits( "KToolBar" ) ) +// static_cast(w)->getButton( itemId( x ) )->setText( text ); + } +} + +////////////////////////////////////////////////////////////////////////////////////////// +// AnalyzerAction +////////////////////////////////////////////////////////////////////////////////////////// +#include "analyzerbase.h" + +AnalyzerAction::AnalyzerAction( KActionCollection *ac ) + : KAction( i18n( "Analyzer" ), 0, ac, "toolbar_analyzer" ) +{ + setShortcutConfigurable( false ); +} + +int +AnalyzerAction::plug( QWidget *w, int index ) +{ + //NOTE the analyzer will be deleted when the toolbar is deleted or cleared() + //we are not designed for unplugging() yet so there would be a leak if that happens + //but it's a rare event and unplugging is complicated. + + KToolBar *bar = dynamic_cast(w); + + if( bar && kapp->authorizeKAction( name() ) ) + { + const int id = KAction::getToolButtonID(); + + addContainer( w, id ); + connect( w, SIGNAL( destroyed() ), SLOT( slotDestroyed() ) ); + QWidget *container = new AnalyzerContainer( w ); + bar->insertWidget( id, 0, container, index ); + bar->setItemAutoSized( id, true ); + + return containerCount() - 1; + } + else return -1; +} + + +AnalyzerContainer::AnalyzerContainer( QWidget *parent ) + : QWidget( parent, "AnalyzerContainer" ) + , m_child( 0 ) +{ + QToolTip::add( this, i18n( "Click for more analyzers" ) ); + changeAnalyzer(); +} + +void +AnalyzerContainer::resizeEvent( QResizeEvent *) +{ + m_child->resize( size() ); +} + +void AnalyzerContainer::changeAnalyzer() +{ + delete m_child; + m_child = Analyzer::Factory::createPlaylistAnalyzer( this ); + m_child->setName( "ToolBarAnalyzer" ); + m_child->resize( size() ); + m_child->show(); +} + +void +AnalyzerContainer::mousePressEvent( QMouseEvent *e) +{ + if( e->button() == Qt::LeftButton ) { + AmarokConfig::setCurrentPlaylistAnalyzer( AmarokConfig::currentPlaylistAnalyzer() + 1 ); + changeAnalyzer(); + } +} + +void +AnalyzerContainer::contextMenuEvent( QContextMenuEvent *e) +{ +#if defined HAVE_LIBVISUAL + KPopupMenu menu; + menu.insertItem( SmallIconSet( Amarok::icon( "visualizations" ) ), i18n("&Visualizations"), Menu::ID_SHOW_VIS_SELECTOR ); + + if( menu.exec( mapToGlobal( e->pos() ) ) == Menu::ID_SHOW_VIS_SELECTOR ) + Menu::instance()->slotActivated( Menu::ID_SHOW_VIS_SELECTOR ); +#else + Q_UNUSED(e); +#endif +} + +////////////////////////////////////////////////////////////////////////////////////////// +// ToggleAction +////////////////////////////////////////////////////////////////////////////////////////// + +ToggleAction::ToggleAction( const QString &text, void ( *f ) ( bool ), KActionCollection* const ac, const char *name ) + : KToggleAction( text, 0, ac, name ) + , m_function( f ) +{} + +void ToggleAction::setChecked( bool b ) +{ + const bool announce = b != isChecked(); + + m_function( b ); + KToggleAction::setChecked( b ); + AmarokConfig::writeConfig(); //So we don't lose the setting when crashing + if( announce ) emit toggled( b ); //KToggleAction doesn't do this for us. How gay! +} + +void ToggleAction::setEnabled( bool b ) +{ + const bool announce = b != isEnabled(); + + if( !b ) + setChecked( false ); + KToggleAction::setEnabled( b ); + AmarokConfig::writeConfig(); //So we don't lose the setting when crashing + if( announce ) emit enabled( b ); +} + +////////////////////////////////////////////////////////////////////////////////////////// +// SelectAction +////////////////////////////////////////////////////////////////////////////////////////// + +SelectAction::SelectAction( const QString &text, void ( *f ) ( int ), KActionCollection* const ac, const char *name ) + : KSelectAction( text, 0, ac, name ) + , m_function( f ) +{ } + +void SelectAction::setCurrentItem( int n ) +{ + const bool announce = n != currentItem(); + + m_function( n ); + KSelectAction::setCurrentItem( n ); + AmarokConfig::writeConfig(); //So we don't lose the setting when crashing + if( announce ) emit activated( n ); +} + +void SelectAction::setEnabled( bool b ) +{ + const bool announce = b != isEnabled(); + + if( !b ) + setCurrentItem( 0 ); + KSelectAction::setEnabled( b ); + AmarokConfig::writeConfig(); //So we don't lose the setting when crashing + if( announce ) emit enabled( b ); +} + +void SelectAction::setIcons( QStringList icons ) +{ + m_icons = icons; + for( int i = 0, n = items().count(); i < n; ++i ) + popupMenu()->changeItem( i, kapp->iconLoader()->loadIconSet( *icons.at( i ), KIcon::Small ), popupMenu()->text( i ) ); +} + +QStringList SelectAction::icons() const { return m_icons; } + +QString SelectAction::currentIcon() const +{ + if( m_icons.count() ) + return *m_icons.at( currentItem() ); + return QString(); +} + +QString SelectAction::currentText() const { + return KSelectAction::currentText() + "

" + i18n("Click to change"); +} + +////////////////////////////////////////////////////////////////////////////////////////// +// VolumeAction +////////////////////////////////////////////////////////////////////////////////////////// + +VolumeAction::VolumeAction( KActionCollection *ac ) + : KAction( i18n( "Volume" ), 0, ac, "toolbar_volume" ) + , EngineObserver( EngineController::instance() ) + , m_slider( 0 ) //is QGuardedPtr +{} + +int +VolumeAction::plug( QWidget *w, int index ) +{ + //NOTE we only support one plugging currently + + delete static_cast( m_slider ); //just in case, remember, we only support one plugging! + + m_slider = new Amarok::VolumeSlider( w, Amarok::VOLUME_MAX ); + m_slider->setName( "ToolBarVolume" ); + m_slider->setValue( AmarokConfig::masterVolume() ); + m_slider->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Ignored ); + + QToolTip::add( m_slider, i18n( "Volume control" ) ); + + EngineController* const ec = EngineController::instance(); + connect( m_slider, SIGNAL(sliderMoved( int )), ec, SLOT(setVolume( int )) ); + connect( m_slider, SIGNAL(sliderReleased( int )), ec, SLOT(setVolume( int )) ); + + static_cast(w)->insertWidget( KAction::getToolButtonID(), 0, m_slider, index ); + + return 0; +} + +void +VolumeAction::engineVolumeChanged( int value ) +{ + if( m_slider ) m_slider->setValue( value ); +} + + + +////////////////////////////////////////////////////////////////////////////////////////// +// RandomAction +////////////////////////////////////////////////////////////////////////////////////////// +RandomAction::RandomAction( KActionCollection *ac ) : + SelectAction( i18n( "Ra&ndom" ), &AmarokConfig::setRandomMode, ac, "random_mode" ) +{ + setItems( QStringList() << i18n( "&Off" ) << i18n( "&Tracks" ) << i18n( "&Albums" ) ); + setCurrentItem( AmarokConfig::randomMode() ); + setIcons( QStringList() << Amarok::icon( "random_no" ) << Amarok::icon( "random_track" ) << Amarok::icon( "random_album" ) ); +} + +void +RandomAction::setCurrentItem( int n ) +{ + if( KAction *a = parentCollection()->action( "favor_tracks" ) ) + a->setEnabled( n ); + SelectAction::setCurrentItem( n ); +} + + +////////////////////////////////////////////////////////////////////////////////////////// +// FavorAction +////////////////////////////////////////////////////////////////////////////////////////// +FavorAction::FavorAction( KActionCollection *ac ) : + SelectAction( i18n( "&Favor" ), &AmarokConfig::setFavorTracks, ac, "favor_tracks" ) +{ + setItems( QStringList() << i18n( "Off" ) + << i18n( "Higher &Scores" ) + << i18n( "Higher &Ratings" ) + << i18n( "Not Recently &Played" ) ); + + setCurrentItem( AmarokConfig::favorTracks() ); + setEnabled( AmarokConfig::randomMode() ); +} + +////////////////////////////////////////////////////////////////////////////////////////// +// RepeatAction +////////////////////////////////////////////////////////////////////////////////////////// +RepeatAction::RepeatAction( KActionCollection *ac ) : + SelectAction( i18n( "&Repeat" ), &AmarokConfig::setRepeat, ac, "repeat" ) +{ + setItems( QStringList() << i18n( "&Off" ) << i18n( "&Track" ) + << i18n( "&Album" ) << i18n( "&Playlist" ) ); + setIcons( QStringList() << Amarok::icon( "repeat_no" ) << Amarok::icon( "repeat_track" ) << Amarok::icon( "repeat_album" ) << Amarok::icon( "repeat_playlist" ) ); + setCurrentItem( AmarokConfig::repeat() ); +} + +////////////////////////////////////////////////////////////////////////////////////////// +// BurnMenuAction +////////////////////////////////////////////////////////////////////////////////////////// +BurnMenuAction::BurnMenuAction( KActionCollection *ac ) + : KAction( i18n( "Burn" ), 0, ac, "burn_menu" ) +{} + +int +BurnMenuAction::plug( QWidget *w, int index ) +{ + KToolBar *bar = dynamic_cast(w); + + if( bar && kapp->authorizeKAction( name() ) ) + { + const int id = KAction::getToolButtonID(); + + addContainer( bar, id ); + connect( bar, SIGNAL( destroyed() ), SLOT( slotDestroyed() ) ); + + bar->insertButton( QString::null, id, true, i18n( "Burn" ), index ); + + KToolBarButton* button = bar->getButton( id ); + button->setPopup( Amarok::BurnMenu::instance() ); + button->setName( "toolbutton_burn_menu" ); + button->setIcon( "k3b" ); + + return containerCount() - 1; + } + else return -1; +} + +BurnMenu::BurnMenu() +{ + insertItem( i18n("Current Playlist"), CURRENT_PLAYLIST ); + insertItem( i18n("Selected Tracks"), SELECTED_TRACKS ); + //TODO add "album" and "all tracks by artist" + + connect( this, SIGNAL( aboutToShow() ), SLOT( slotAboutToShow() ) ); + connect( this, SIGNAL( activated(int) ), SLOT( slotActivated(int) ) ); +} + +KPopupMenu* +BurnMenu::instance() +{ + static BurnMenu menu; + return &menu; +} + +void +BurnMenu::slotAboutToShow() +{} + +void +BurnMenu::slotActivated( int index ) +{ + switch( index ) + { + case CURRENT_PLAYLIST: + K3bExporter::instance()->exportCurrentPlaylist(); + break; + + case SELECTED_TRACKS: + K3bExporter::instance()->exportSelectedTracks(); + break; + } +} + +////////////////////////////////////////////////////////////////////////////////////////// +// StopMenuAction +////////////////////////////////////////////////////////////////////////////////////////// + +StopAction::StopAction( KActionCollection *ac ) + : KAction( i18n( "Stop" ), Amarok::icon( "stop" ), 0, EngineController::instance(), SLOT( stop() ), ac, "stop" ) +{} + +int +StopAction::plug( QWidget *w, int index ) +{ + KToolBar *bar = dynamic_cast(w); + + if( bar && kapp->authorizeKAction( name() ) ) + { + const int id = KAction::getToolButtonID(); + + addContainer( bar, id ); + connect( bar, SIGNAL( destroyed() ), SLOT( slotDestroyed() ) ); + + bar->insertButton( QString::null, id, SIGNAL( clicked() ), EngineController::instance(), SLOT( stop() ), + true, i18n( "Stop" ), index ); + + KToolBarButton* button = bar->getButton( id ); + button->setDelayedPopup( Amarok::StopMenu::instance() ); + button->setName( "toolbutton_stop_menu" ); + button->setIcon( Amarok::icon( "stop" ) ); + button->setEnabled( EngineController::instance()->engine()->loaded() ); // Disable button at startup + + return containerCount() - 1; + } + else return KAction::plug( w, index ); +} + +StopMenu::StopMenu() +{ + insertTitle( i18n( "Stop" ) ); + insertItem( i18n("Now"), NOW ); + insertItem( i18n("After Current Track"), AFTER_TRACK ); + insertItem( i18n("After Queue"), AFTER_QUEUE ); + + connect( this, SIGNAL( aboutToShow() ), SLOT( slotAboutToShow() ) ); + connect( this, SIGNAL( activated(int) ), SLOT( slotActivated(int) ) ); +} + +KPopupMenu* +StopMenu::instance() +{ + static StopMenu menu; + return &menu; +} + +void +StopMenu::slotAboutToShow() +{ + Playlist *pl = Playlist::instance(); + + setItemEnabled( NOW, Amarok::actionCollection()->action( "stop" )->isEnabled() ); + + setItemEnabled( AFTER_TRACK, EngineController::engine()->loaded() ); + setItemChecked( AFTER_TRACK, pl->stopAfterMode() == Playlist::StopAfterCurrent ); + + setItemEnabled( AFTER_QUEUE, pl->nextTracks().count() ); + setItemChecked( AFTER_QUEUE, pl->stopAfterMode() == Playlist::StopAfterQueue ); +} + +void +StopMenu::slotActivated( int index ) +{ + Playlist* pl = Playlist::instance(); + const int mode = pl->stopAfterMode(); + + switch( index ) + { + case NOW: + Amarok::actionCollection()->action( "stop" )->activate(); + if( mode == Playlist::StopAfterCurrent || mode == Playlist::StopAfterQueue ) + pl->setStopAfterMode( Playlist::DoNotStop ); + break; + case AFTER_TRACK: + pl->setStopAfterMode( mode == Playlist::StopAfterCurrent + ? Playlist::DoNotStop + : Playlist::StopAfterCurrent ); + break; + case AFTER_QUEUE: + pl->setStopAfterMode( mode == Playlist::StopAfterQueue + ? Playlist::DoNotStop + : Playlist::StopAfterQueue ); + break; + } +} + + +#include "actionclasses.moc" diff --git a/amarok/src/actionclasses.h b/amarok/src/actionclasses.h new file mode 100644 index 00000000..4d51534a --- /dev/null +++ b/amarok/src/actionclasses.h @@ -0,0 +1,215 @@ +// Maintainer: Max Howell , (C) 2004 +// Copyright: See COPYING file that comes with this distribution +// +// Description: a popupmenu to control various features of Amarok +// also provides Amarok's helpMenu + +#ifndef AMAROK_ACTIONCLASSES_H +#define AMAROK_ACTIONCLASSES_H + +#include "engineobserver.h" +#include "prettypopupmenu.h" +#include "sliderwidget.h" + +#include +#include +#include + +class KActionCollection; +class KHelpMenu; + + +namespace Amarok +{ + class Menu : public PrettyPopupMenu + { + Q_OBJECT + public: + static Menu *instance(); + static KPopupMenu *helpMenu( QWidget *parent = 0 ); + + enum MenuIds { + ID_CONF_DECODER, + ID_SHOW_VIS_SELECTOR, + ID_SHOW_COVER_MANAGER, + ID_CONFIGURE_EQUALIZER, + ID_RESCAN_COLLECTION + }; + + public slots: + void slotActivated( int index ); + + private slots: + void slotAboutToShow(); + + private: + Menu(); + + static KHelpMenu *s_helpMenu; + }; + + + class MenuAction : public KAction + { + public: + MenuAction( KActionCollection* ); + virtual int plug( QWidget*, int index = -1 ); + }; + + + class PlayPauseAction : public KToggleAction, public EngineObserver + { + public: + PlayPauseAction( KActionCollection* ); + virtual void engineStateChanged( Engine::State, Engine::State = Engine::Empty ); + }; + + class AnalyzerContainer : public QWidget + { + public: + AnalyzerContainer( QWidget *parent ); + protected: + virtual void resizeEvent( QResizeEvent* ); + virtual void mousePressEvent( QMouseEvent* ); + virtual void contextMenuEvent( QContextMenuEvent* ); + private: + void changeAnalyzer(); + QWidget *m_child; + }; + + class AnalyzerAction : public KAction + { + public: + AnalyzerAction( KActionCollection* ); + virtual int plug( QWidget *, int index = -1 ); + }; + + + class VolumeAction : public KAction, public EngineObserver + { + public: + VolumeAction( KActionCollection* ); + virtual int plug( QWidget *, int index = -1 ); + private: + void engineVolumeChanged( int value ); + QGuardedPtr m_slider; + }; + + + class ToggleAction : public KToggleAction + { + public: + ToggleAction( const QString &text, void ( *f ) ( bool ), KActionCollection* const ac, const char *name ); + + virtual void setChecked( bool b ); + + virtual void setEnabled( bool b ); + + private: + void ( *m_function ) ( bool ); + }; + + class SelectAction : public KSelectAction + { + public: + SelectAction( const QString &text, void ( *f ) ( int ), KActionCollection* const ac, const char *name ); + + virtual void setCurrentItem( int n ); + + virtual void setEnabled( bool b ); + + virtual void setIcons( QStringList icons ); + + virtual QString currentText() const; + + QStringList icons() const; + + QString currentIcon() const; + + private: + void ( *m_function ) ( int ); + QStringList m_icons; + }; + + + class RandomAction : public SelectAction + { + public: + RandomAction( KActionCollection *ac ); + virtual void setCurrentItem( int n ); + }; + + class FavorAction : public SelectAction + { + public: + FavorAction( KActionCollection *ac ); + }; + + class RepeatAction : public SelectAction + { + public: + RepeatAction( KActionCollection *ac ); + }; + + class BurnMenu : public KPopupMenu + { + Q_OBJECT + + public: + enum MenuIds { + CURRENT_PLAYLIST, + SELECTED_TRACKS + }; + + static KPopupMenu *instance(); + + private slots: + void slotAboutToShow(); + void slotActivated( int index ); + + private: + BurnMenu(); + }; + + + class BurnMenuAction : public KAction + { + public: + BurnMenuAction( KActionCollection* ); + virtual int plug( QWidget*, int index = -1 ); + }; + + class StopMenu : public KPopupMenu + { + Q_OBJECT + + public: + enum MenuIds { + NOW, + AFTER_TRACK, + AFTER_QUEUE + }; + + static KPopupMenu *instance(); + + private slots: + void slotAboutToShow(); + void slotActivated( int index ); + + private: + StopMenu(); + }; + + + class StopAction : public KAction + { + public: + StopAction( KActionCollection* ); + virtual int plug( QWidget*, int index = -1 ); + }; + +} /* namespace Amarok */ + + +#endif /* AMAROK_ACTIONCLASSES_H */ + diff --git a/amarok/src/amarok.desktop b/amarok/src/amarok.desktop new file mode 100644 index 00000000..603db561 --- /dev/null +++ b/amarok/src/amarok.desktop @@ -0,0 +1,114 @@ +[Desktop Entry] +Type=Application +Version=0.9.4 +Encoding=UTF-8 +Name=Amarok +Name[bn]=আমারক +Name[el]=AmaroK +Name[ga]=AmaroK +Name[lt]=amaroK +Name[mk]=Амарок +Name[ne]=अमारोक +Name[pa]=ਅਮਰੋਕ +Name[ru]=amaroK +Name[zh_TW]=AmaroK +GenericName=Audio Player +GenericName[af]=Oudio Speler +GenericName[ar]=قارئى الصوت +GenericName[bg]=Аудио плеър +GenericName[bn]=অডিও প্লেয়ার +GenericName[br]=C'hoarier klevet +GenericName[ca]=Reproductor d'àudio +GenericName[cs]=Zvukový přehrávač +GenericName[da]=Lydafspiller +GenericName[de]=Audio-Wiedergabeprogramm +GenericName[el]=Αναπαραγωγή ήχου +GenericName[eo]=Aŭdludilo +GenericName[es]=Reproductor de audio +GenericName[et]=Helifailide mängija +GenericName[eu]=Audio-erreproduktorea +GenericName[fa]=پخش‌کنندۀ صوتی +GenericName[fi]=Musiikkisoitin +GenericName[fr]=Lecteur audio +GenericName[ga]=Seinnteoir Fuaime +GenericName[gl]=Reprodutor de Áudio +GenericName[hi]=ऑडियो प्लेयर +GenericName[hu]=Zenelejátszó +GenericName[is]=Tónlistarspilari +GenericName[it]=Lettore audio +GenericName[ja]=オーディオプレーヤ +GenericName[ka]=აუდიოდამკვრელი +GenericName[km]=កម្មវិធី​ចាក់​អូឌីយ៉ូ +GenericName[lt]=Muzikos grotuvas +GenericName[mk]=Аудио-изведувач +GenericName[mn]=Аудио-изведувач +GenericName[ms]=Pemain Audio +GenericName[nb]=Musikkavspiller +GenericName[nds]=Klangafspeler +GenericName[ne]=अडियो प्लेयर +GenericName[nl]=Audiospeler +GenericName[nn]=Lydspelar +GenericName[pa]=ਆਡੀਓ ਪਲੇਅਰ +GenericName[pl]=Odtwarzacz audio +GenericName[pt]=Leitor de Áudio +GenericName[pt_BR]=Reprodutor de Áudio +GenericName[ru]=Аудиоплеер +GenericName[se]=Jietnačuojaheaddji +GenericName[sk]=Audio prehrávač +GenericName[sl]=Predvajalnik zvoka +GenericName[sq]=Audio plejer +GenericName[sr]=Аудио плејер +GenericName[sr@Latn]=Audio plejer +GenericName[ss]=Audio plejer +GenericName[sv]=Ljudspelare +GenericName[ta]=கேட்பொலி இயக்கி +GenericName[tg]=Аудио Плейер +GenericName[th]=โปรแกรมเล่นเสียง +GenericName[tr]=Müzik Çalar +GenericName[uk]=Аудіопрогравач +GenericName[uz]=Audio-pleyer +GenericName[uz@cyrillic]=Аудио-плейер +GenericName[wa]=Djouweu d' muzike +GenericName[zh_CN]=音频播放器 +GenericName[zh_TW]=音樂播放程式 +Exec=amarok %U +Comment=Amarok - Rediscover Your Music! +Comment[bg]=Amarok - преоткрий музиката! +Comment[bn]=আমারক - আপনার সঙ্গীতের এক নতুন দিগন্ত +Comment[ca]=Amarok - Redescobreix la teva música! +Comment[cs]=Amarok - objevte svou hudbu! +Comment[da]=Amarok - Genopdag din musik! +Comment[de]=Amarok - Musik neu erleben! +Comment[el]=Amarok - Ξαναανακαλύψτε τη μουσική σας! +Comment[es]=Amarok - ¡Vuelva a descubrir su música! +Comment[et]=Amarok - naudi oma muusikat! +Comment[hu]=Amarok - fedezze fel a zenéjét +Comment[is]=Amarok - enduruppgötvaðu tónlistina þína! +Comment[it]=Amarok - Riscopri la tua musica! +Comment[ja]=Amarok - あなたの音楽を再発見 +Comment[km]=Amarok - រកឃើញ​តន្ត្រី​របស់​អ្នក​ឡើង​វិញ ! +Comment[lt]=Amarok – atraskite savo muziką iš naujo! +Comment[nds]=Amarok - Beleev Dien Musik nieg! +Comment[nl]=Amarok - Herontdek uw muziek! +Comment[nn]=Amarok – gjenoppdag musikken din! +Comment[pl]=Amarok - odkryj na nowo swoją muzykę! +Comment[pt]=Amarok - Descubra de Novo a Sua Música! +Comment[pt_BR]=Amarok - Redescubra Sua Música! +Comment[sk]=Amarok - Znova objavte hudbu! +Comment[sr]=Amarok — откријте своју музику! +Comment[sr@Latn]=Amarok — otkrijte svoju muziku! +Comment[sv]=Amarok - Återupptäck din musik! +Comment[th]=Amarok - ค้นพบมิติใหม่ในดนตรีของคุณ! +Comment[tr]=Amarok - Müziğinizi yeniden keşfedin! +Comment[uk]=Amarok - заново відчуйте свою музику! +Comment[uz]=Amarok - musiqaga yangidan nazar tashlang +Comment[uz@cyrillic]=Amarok - мусиқага янгидан назар ташланг +Comment[wa]=Amarok - Ridiscovroz vosse muzike! +Comment[zh_TW]=Amarok - 重新發現你的音樂! +Icon=amarok +X-KDE-Protocols=http +#don't add inode/directory to mimetypes it leads to misbehavior +MimeType=audio/aac;audio/mp4;audio/mpeg;audio/mpegurl;audio/vnd.rn-realaudio;audio/vorbis;audio/x-flac;audio/x-mp3;audio/x-mpegurl;audio/x-ms-wma;audio/x-musepack;audio/x-oggflac;audio/x-pn-realaudio;audio/x-scpls;audio/x-speex;audio/x-vorbis;audio/x-wav;video/x-ms-asf;audio/flac;audio/ogg; +DocPath=amarok/index.html +Terminal=false +Categories=Qt;KDE;AudioVideo;Audio;Player; diff --git a/amarok/src/amarok.h b/amarok/src/amarok.h new file mode 100644 index 00000000..3be1ae26 --- /dev/null +++ b/amarok/src/amarok.h @@ -0,0 +1,345 @@ +//See COPYING file for licensing information + +#ifndef AMAROK_H +#define AMAROK_H + +#include +#include + +#include // recursiveUrlExpand +#include //Amarok::ProcIO +#include +#include + +#include "amarok_export.h" + +class KActionCollection; +class KConfig; +class QColor; +class QDateTime; +class QEvent; +class QMutex; +class QPixmap; +class QWidget; +class DynamicMode; +class QListView; +class QListViewItem; +namespace KIO { class Job; } + +namespace Amarok +{ + const int VOLUME_MAX = 100; + const int SCOPE_SIZE = 9; //= 2**9 = 512 + const int blue = 0x202050; + const int VOLUME_SENSITIVITY = 30; //for mouse wheels + const int GUI_THREAD_ID = 0; + + extern QMutex globalDirsMutex; // defined in app.cpp + + namespace ColorScheme + { + ///eg. base of the Amarok Player-window + extern QColor Base; //Amarok::blue + ///eg. text in the Amarok Player-window + extern QColor Text; //Qt::white + ///eg. background colour for Amarok::PrettySliders + extern QColor Background; //brighter blue + ///eg. outline of slider widgets in Player-window + extern QColor Foreground; //lighter blue + ///eg. KListView alternative row color + extern QColor AltBase; //grey toned base + } + + /** The version of the playlist XML format. Increase whenever it changes backwards-incompatibly. */ + inline QString xmlVersion() { return "2.4"; } + + /** + * Convenience function to return the KApplication instance KConfig object + * pre-set to a specific group. + * @param group Will pre-set the KConfig object to this group. + */ + /* FIXME: This function can lead to very bizarre and hard to figure bugs. + While we don`t fix it properly, use it like this: amarok::config( Group )->readNumEntry( ... ) */ + KConfig *config( const QString &group = "General" ); //defined in app.cpp + + /** + * @return the KActionCollection used by Amarok + * The KActionCollection is owned by the PlaylistWindow, so you must ensure + * you don't try to use this before then, but we've taken steps to prevent + * this eventuality - you should be safe. + */ + KActionCollection *actionCollection(); //defined in app.cpp + + /** + * An event handler that handles events in a generic Amarok fashion. Mainly + * useful for drops, ie offers the Amarok popup for adding tracks to the + * playlist. You shouldn't pass every event here, ie closeEvents will not be + * handled as expected! Check the source in app.cpp if you want to see what + * it can do. + * @param recipient The object that received the event. + * @param e The event you want handled in a generic fashion. + * @return true if the event was handled. + */ + bool genericEventHandler( QWidget *recipient, QEvent *e ); //defined in app.cpp + + /** + * Invoke the external web browser set in Amarok's configuration. + * @param url The URL to be opened in the browser. + * @return True if the browser could be started. + */ + bool invokeBrowser( const QString& url ); //defined in app.cpp + + /** + * Obtain an Amarok PNG image as a QPixmap + */ + QPixmap getPNG( const QString& /*fileName*/ ); //defined in app.cpp + + /** + * Obtain an Amarok JPG image as a QPixmap + */ + QPixmap getJPG( const QString& /*fileName*/ ); //defined in app.cpp + + /** + * The mainWindow is the playlistWindow or the playerWindow depending on + * the configuration of Amarok + */ + QWidget *mainWindow(); //defined in app.cpp + + /** + * Allocate one on the stack, and it'll set the busy cursor for you until it + * is destroyed + */ + class OverrideCursor { //defined in app.cpp + public: + OverrideCursor( Qt::CursorShape cursor = Qt::WaitCursor ); + ~OverrideCursor(); + }; + + /** + * For saving files to ~/.kde/share/apps/amarok/directory + * @param directory will be created if not existing, you MUST end the string + * with '/' + */ + LIBAMAROK_EXPORT QString saveLocation( const QString &directory = QString::null ); //defined in collectionreader.cpp + + KIO::Job *trashFiles( const KURL::List &files ); //defined in app.cpp + + /** + * For recursively expanding the contents of a directory into a KURL::List + * (playlists are ignored) + */ + LIBAMAROK_EXPORT KURL::List recursiveUrlExpand( const KURL &url, int maxURLs = -1 ); //defined in playlistloader.cpp + LIBAMAROK_EXPORT KURL::List recursiveUrlExpand( const KURL::List &urls, int maxURLs = -1 ); //defined in playlistloader.cpp + + QString verboseTimeSince( const QDateTime &datetime ); //defined in contextbrowser.cpp + + QString verboseTimeSince( uint time_t ); //defined in contextbrowser.cpp + + /** + * Function that must be used when separating contextBrowser escaped urls + */ + // defined in contextbrowser.cpp + void albumArtistTrackFromUrl( QString url, QString &artist, QString &album, QString &detail ); + + /** + * @return the LOWERCASE file extension without the preceding '.', or "" if there is none + */ + inline QString extension( const QString &fileName ) + { + return fileName.contains( '.' ) ? fileName.mid( fileName.findRev( '.' ) + 1 ).lower() : ""; + } + + /** Transform url into a file url if possible */ + inline KURL mostLocalURL( const KURL &url ) + { +#if KDE_VERSION < KDE_MAKE_VERSION(3,5,0) + return url; +#else + return KIO::NetAccess::mostLocalURL( url, mainWindow() ); +#endif + } + + /** + * @return the last directory in @param fileName + */ + inline QString directory( const QString &fileName ) + { + return fileName.section( '/', 0, -2 ); + } + /** Due to xine-lib, we have to make KProcess close all fds, otherwise we get "device is busy" messages + * Used by Amarok::ProcIO and Amarok::Process, exploiting commSetupDoneC(), a virtual method that + * happens to be called in the forked process + * See bug #103750 for more information. + */ + //TODO ugly hack, fix KProcess for KDE 4.0 + void closeOpenFiles(int out, int in, int err); //defined in scriptmanager.cpp + + /** + * Returns internal code for database type, DbConnection::sqlite, DbConnection::mysql, or DbConnection::postgresql + * @param type either "SQLite", "MySQL", or "Postgresql". + */ + int databaseTypeCode( const QString type ); //defined in configdialog.cpp + + void setUseScores( bool use ); //defined in app.cpp + void setUseRatings( bool use ); + void setMoodbarPrefs( bool show, bool moodier, int alter, bool withMusic ); + + bool repeatNone(); //defined in actionclasses.cpp + bool repeatTrack(); + bool repeatAlbum(); + bool repeatPlaylist(); + bool randomOff(); + bool randomTracks(); + bool randomAlbums(); + bool favorNone(); + bool favorScores(); + bool favorRatings(); + bool favorLastPlay(); + bool entireAlbums(); //repeatAlbum() || randomAlbums() + + const DynamicMode *dynamicMode(); //defined in playlist.cpp + + QListViewItem* findItemByPath( QListView *view, QString path ); //defined in playlistbrowser.cpp + QStringList splitPath( QString path ); //defined in playlistbrowser.cpp + + /** + * Creates a copy of of the KURL instance, that doesn't have any QStrings sharing memory. + **/ + KURL detachedKURL( const KURL &url ); //defined in metabundle.cpp + + /** + * Maps the icon name to a system icon or custom Amarok icon, depending on the settings. + */ + LIBAMAROK_EXPORT QString icon( const QString& name ); //defined in iconloader.cpp + + /** + * Removes accents from the string + * @param path The original path. + * @return The cleaned up path. + */ + LIBAMAROK_EXPORT QString cleanPath( const QString &path ); //defined in app.cpp + + /** + * Replaces all non-ASCII characters with '_'. + * @param path The original path. + * @return The ASCIIfied path. + */ + LIBAMAROK_EXPORT QString asciiPath( const QString &path ); //defined in app.cpp + + /** + * Transform path into one valid on VFAT file systems + * @param path The original path. + * @return The cleaned up path. + */ + LIBAMAROK_EXPORT QString vfatPath( const QString &path ); //defined in app.cpp + + /** + * Compare both strings from left to right and remove the common part from input + * @param input the string that get's cleaned. + * @param ref a reference to compare input with. + * @return The cleaned up string. + */ + LIBAMAROK_EXPORT QString decapitateString( const QString &input, const QString &ref ); + + /* + * Transform to be usable within HTML/HTML attributes + * defined in contextbrowser.cpp + */ + LIBAMAROK_EXPORT QString escapeHTML( const QString &s ); + LIBAMAROK_EXPORT QString escapeHTMLAttr( const QString &s ); + LIBAMAROK_EXPORT QString unescapeHTMLAttr( const QString &s ); + + /* defined in scriptmanager.cpp */ + /** + * Returns the proxy that should be used for a given URL. + * @param url the url. + * @return The url of the proxy, or a empty string if no proxy should be used. + */ + QString proxyForUrl(const QString& url); + + /** + * Returns the proxy that should be used for a given protocol. + * @param protocol the protocol. + * @return The url of the proxy, or a empty string if no proxy should be used. + */ + QString proxyForProtocol(const QString& protocol); + + //////////////////////////////////////////////////////////////////////////////// + // class Amarok::ProcIO + //////////////////////////////////////////////////////////////////////////////// + /** + * Due to xine-lib, we have to make KProcess close all fds, otherwise we get "device is busy" messages + * Used by Amarok::ProcIO and AmarokProcess, exploiting commSetupDoneC(), a virtual method that + * happens to be called in the forked process + * See bug #103750 for more information. + */ + class LIBAMAROK_EXPORT ProcIO : public KProcIO { + public: + ProcIO(); // ctor sets the textcodec to UTF-8, in scriptmanager.cpp + virtual int commSetupDoneC() { + const int i = KProcIO::commSetupDoneC(); + Amarok::closeOpenFiles( KProcIO::out[0],KProcIO::in[0],KProcIO::err[0] ); + return i; + }; + }; + + //////////////////////////////////////////////////////////////////////////////// + // class Amarok::Process + //////////////////////////////////////////////////////////////////////////////// + /** Due to xine-lib, we have to make KProcess close all fds, otherwise we get "device is busy" messages + * Used by Amarok::ProcIO and Amarok::Process, exploiting commSetupDoneC(), a virtual method that + * happens to be called in the forked process + * See bug #103750 for more information. + */ + class LIBAMAROK_EXPORT Process : public KProcess { + public: + Process( QObject *parent = 0 ) : KProcess( parent ) {} + virtual int commSetupDoneC() { + const int i = KProcess::commSetupDoneC(); + Amarok::closeOpenFiles(KProcess::out[0],KProcess::in[0], KProcess::err[0]); + return i; + }; + }; + + +} + + +/** + * Use this to const-iterate over QStringLists, if you like. + * Watch out for the definition of last in the scope of your for. + * + * QStringList strings; + * foreach( strings ) + * debug() << *it << endl; + */ +#define foreach( x ) \ + for( QStringList::ConstIterator it = x.begin(), end = x.end(); it != end; ++it ) + +/** + * You can use this for lists that aren't QStringLists. + * Watch out for the definition of last in the scope of your for. + * + * BundleList bundles; + * foreachType( BundleList, bundles ) + * debug() << *it.url() << endl; + */ +#define foreachType( Type, x ) \ + for( Type::ConstIterator it = x.begin(), end = x.end(); it != end; ++it ) + +/** + * Creates iterators of type @p Type. + * Watch out for the definitions of last and end in your scope. + * + * BundleList bundles; + * for( for_iterators( BundleList, bundles ); it != end; ++it ) + * debug() << *it.url() << endl; + */ +#define for_iterators( Type, x ) \ + Type::ConstIterator it = x.begin(), end = x.end(), last = x.fromLast() + + +/// Update this when necessary +#define APP_VERSION "1.4.10" + +#endif diff --git a/amarok/src/amarok.profile.xml b/amarok/src/amarok.profile.xml new file mode 100644 index 00000000..8479499e --- /dev/null +++ b/amarok/src/amarok.profile.xml @@ -0,0 +1,60 @@ + + + + Amarok + Dirk Ziegelmeier + + + + + Increase volume + + + + Decrease volume + + + + Toggle sound muting + + + + + + Start playing + + + + Stop playing + + + + Pause playing + + + + Advance to next track + + + + Go to previous track + + + + + Relative seek + Seek inside of tracks. Use negative numbers for rewind. + + + + + + + Show OSD display + Works only if OSD is enabled + + + Toggles the playlist window + + + diff --git a/amarok/src/amarok_addaspodcast.desktop b/amarok/src/amarok_addaspodcast.desktop new file mode 100644 index 00000000..0b9e5f28 --- /dev/null +++ b/amarok/src/amarok_addaspodcast.desktop @@ -0,0 +1,50 @@ +[Desktop Entry] +Encoding=UTF-8 +ServiceTypes=text/html,text/xml,application/xml,text/rss,text/rdf +Actions=addAsPodcast; +[Desktop Action addAsPodcast] +Name=Add as Podcast to Amarok +Name[af]=Voeg as Podcast by Amarok by +Name[bg]=Добавяне като подкаст в Amarok +Name[bn]=পডকাস্ট হিসেবে আমারক-এ যোগ করো +Name[ca]=Afegeix com a Podcast a l'Amarok +Name[cs]=Přidat podcast +Name[da]=Tilføj som podradioudsendelse i Amarok +Name[de]=Als Podcast zu Amarok hinzufügen +Name[el]=Προσθήκη ως εκπομπή Pod στο Amarok +Name[eo]=Aldonu kiel Podkasto al Amarok +Name[es]=Añadir como un podcast a Amarok +Name[et]=Lisa Podcastina Amarokile +Name[fa]=افزودن به عنوان Podcast به Amarok +Name[fi]=Lisää Amarokiin podcastina +Name[fr]=Ajouter en tant que podcast dans Amarok +Name[hu]=Betevés az Amarokba podcast-ként +Name[is]=Bæta við Amarok sem Podcast +Name[it]=Aggiungi come podcast in Amarok +Name[ja]=ポッドキャストとして Amarok に追加 +Name[km]=បន្ថែម​ជា​ផតខាស់​ទៅ Amarok +Name[lt]=Įterpti Podcast į Amarok grojaraštį +Name[nb]= Legg til Amarok som podkast +Name[nds]=As Podcast na Amarok tofögen +Name[ne]=अमारोकमा पोडकास्टको रूपमा थप्नुहोस् +Name[nl]=Als Podcast aan Amarok toevoegen +Name[nn]=Legg til som podkast i Amarok +Name[pa]=ਅਮਰੋਕ 'ਚ ਪੋਸਟਕਾਸਟ ਵਾਂਗ ਸ਼ਾਮਿਲ +Name[pl]=Dodaj jako podcast do Amaroka +Name[pt]=Adicionar como 'Podcast' ao Amarok +Name[pt_BR]=Adicionar como Podcast ao Amarok +Name[se]=Lasit podkásttan Amarok:ii +Name[sk]=Pridať ako Podcast do Amarok +Name[sr]=Додај као подемисију у Amarok +Name[sr@Latn]=Dodaj kao podemisiju u Amarok +Name[sv]=Lägg till som podradiosändning i Amarok +Name[th]=เพิ่มเป็นพ็อดแคสต์ไปยัง Amarok +Name[tr]=Amarok'a Podcast olarak Ekle +Name[uk]=Додати в Amarok як подкест +Name[uz]=Amarok'ka podkast sifatida qoʻshish +Name[uz@cyrillic]=Amarok'ка подкаст сифатида қўшиш +Name[wa]=Radjouter come Podcast a Amarok +Name[zh_CN]=将播客添加到 Aamarok +Name[zh_TW]=將 Podcast 加入 Amarok +Icon=amarok +Exec=dcop amarok playlistbrowser addPodcast %u diff --git a/amarok/src/amarok_append.desktop b/amarok/src/amarok_append.desktop new file mode 100644 index 00000000..1d6dac52 --- /dev/null +++ b/amarok/src/amarok_append.desktop @@ -0,0 +1,175 @@ +[Desktop Entry] +Encoding=UTF-8 +ServiceTypes=application/asx,audio/* +Actions=appendToPlaylist;appendAndPlay;queueTrack; +X-KDE-Submenu=Amarok +X-KDE-Submenu[bn]=আমারক +X-KDE-Submenu[el]=AmaroK +X-KDE-Submenu[es]=amaroK +X-KDE-Submenu[ga]=AmaroK +X-KDE-Submenu[mk]=Амарок +X-KDE-Submenu[ne]=अमारोक +X-KDE-Submenu[pa]=ਅਮਰੋਕ +X-KDE-Submenu[ru]=amaroK + +[Desktop Action appendToPlaylist] +Name=Append to Playlist +Name[af]=Voeg agter aan speel lys by +Name[ar]=اضف الى لائحة القراءة +Name[bg]=Добавяне към списъка със записи +Name[bn]=সঙ্গীত-তালিকায় সংযোজন করো +Name[br]=Ouzhpennañ d'ar roll tonioù +Name[ca]=Afegeix a la llista de reproducció +Name[cs]=Přidat do seznamu skladeb +Name[da]=Tilføj til spilleliste +Name[de]=An Amarok-Wiedergabeliste anhängen +Name[el]=Προσθήκη στη λίστα αναπαραγωγής +Name[eo]=Aldoni al Ludlisto +Name[es]=Añadir a la lista de reproducción +Name[et]=Lisa esitusnimekirja +Name[fa]=پیوستن به فهرست پخش +Name[fi]=Lisää soittolistaan +Name[fr]=Ajouter à la liste de lecture +Name[ga]=Iarcheangail le Seinmliosta +Name[gl]=Engadir á Lista de Reprodución +Name[hu]=Hozzáadás a lejátszólistához +Name[is]=Bæta við Amarok lagalistann +Name[it]=Aggiungi alla playlist +Name[ja]=プレイリストに追加 +Name[ka]=რეპერტუარში დამატება +Name[km]=បន្ថែម​ទៅ​ខាង​ចុង​បញ្ជីចាក់ +Name[lt]=Pridėti prie Amarok grojaraščio +Name[mk]=Додај на листата +Name[nb]=Legg til spilleliste +Name[nds]=Na Afspeellist tofögen +Name[ne]=बजाउने सूचीमा थप्नुहोस् +Name[nl]=Toevoegen aan afspeellijst +Name[nn]=Legg til speleliste +Name[pa]=ਸੰਗੀਤ-ਸੂਚੀ 'ਚ ਸ਼ਾਮਲ +Name[pl]=Dołącz do listy odtwarzania +Name[pt]=Adicionar à Lista de Reprodução +Name[pt_BR]=Anexar à Lista de Músicas +Name[ru]=Добавить в список +Name[se]=Lasit čuojahanlistui +Name[sk]=Pridať do zoznamu skladieb +Name[sr]=Додај у листу нумера +Name[sr@Latn]=Dodaj u listu numera +Name[sv]=Lägg till i spellistan +Name[th]=เพิ่มเข้าไปในรายการเล่น +Name[tr]=Parça Listesine Ekle +Name[uk]=Додати до списку композицій +Name[uz]=Qoʻshiq roʻyxatiga qoʻshish +Name[uz@cyrillic]=Қўшиқ рўйхатига қўшиш +Name[wa]=Mete dins l' djivêye di léjhaedje +Name[zh_CN]=追加到播放列表 +Name[zh_TW]=加入播放清單 +Icon=amarok +Exec=amarok -e %U + +[Desktop Action appendAndPlay] +Name=Append & Play +Name[af]=Voeg agter aan by & Speel +Name[ar]=اضف و اقرأء +Name[bg]=Добавяне и изпълнение +Name[bn]=সংযোজন করো এবং চালাও +Name[br]=Ouzhpennañ ha seniñ +Name[ca]=Afegeix i reprodueix +Name[cs]=Připojit a hrát +Name[da]=Tilføj og spil +Name[de]=Anhängen und abspielen +Name[el]=Προσθήκη & αναπαραγωγή +Name[eo]=Aldoni & Ludi +Name[es]=Añadir y reproducir +Name[et]=Lisa ja esita +Name[fa]=پیوستن و پخش +Name[fi]=Lisää soittolistaan ja soita +Name[fr]=Ajouter & Écouter +Name[ga]=Iarcheangail & Seinn +Name[gl]=Engadir e Reproducir +Name[hu]=Hozzáadás és lejátszás +Name[is]=Bæta við og spila +Name[it]=Aggiungi & Riproduci +Name[ja]=追加して再生 +Name[ka]=დამატება და დაკვრა +Name[km]=បន្ថែម​ខាង​ចុង & ចាក់ +Name[lt]=Pridėti ir groti +Name[mk]=Додај и пушти +Name[nb]=Legg til og spill +Name[nds]=Tofögen un afspelen +Name[ne]=थप्नुहोस् र बजाउनुहोस् +Name[nl]=Toevoegen en afspelen +Name[nn]=Legg til og spel +Name[pa]=ਜੋੜੋ ਅਤੇ ਚਲਾਓ +Name[pl]=Dołącz i odtwórz +Name[pt]=Adicionar e Reproduzir +Name[pt_BR]=Anexar & Reproduzir +Name[ru]=Добавить и воспроизвести +Name[se]=Lasit ja čuojat +Name[sk]=Pridať a zahrať +Name[sr]=Додај и пусти +Name[sr@Latn]=Dodaj i pusti +Name[sv]=Lägg till och spela +Name[th]=เพิ่ม & เล่น +Name[tr]=Parça Listesine Ekle ve Çal +Name[uk]=Додати і програти +Name[uz]=Qoʻshish va oʻynash +Name[uz@cyrillic]=Қўшиш ва ўйнаш +Name[wa]=Mete dins l' djivêye et djouwer +Name[zh_CN]=追加并播放 +Name[zh_TW]=加入播放清單 & 播放 +Icon=amarok +Exec=dcop amarok playlist playMedia %U + +[Desktop Action queueTrack] +Name=Queue Track +Name[af]=Plaas snit in wagtou +Name[ar]= اضف المسار الى صفٌ الانتظار +Name[bg]=На опашката +Name[bn]=গান সারিবদ্ধ করো +Name[br]=L&ostañ ar roudenn +Name[ca]=Encua la peça +Name[cs]=Zařadit skladbu +Name[da]=Sæt spor i kø +Name[de]=Stück in Warteschlange einstellen +Name[el]=Εισαγωγή του κομματιού στην ουρά +Name[eo]=Vicigi Trakon +Name[es]=Encolar pista +Name[et]=Sea pala järjekorda +Name[fa]=صف کردن شیار +Name[fi]=Lisää jonoon +Name[fr]=Ajouter à la file d'attente +Name[ga]=Ciúáil Amhrán +Name[gl]=Pór Pista na Cola +Name[hu]=Szám betevése a sorba +Name[is]=Setja lag í biðröð +Name[it]=Accoda traccia +Name[ja]=トラックをキュー +Name[ka]=ჩანაწერის რიგში ჩამატება +Name[km]=ដាក់​បទ​ក្នុង​ជួរ +Name[lt]=Pridėti į eilę +Name[mk]=Стави нумера во редица +Name[nb]=Legg spor i kø +Name[nds]=Stück inregen +Name[ne]=लाम ट्रयाक +Name[nl]=Track in wachtrij plaatsen +Name[nn]=Legg spor i kø +Name[pa]=ਟਰੈਕ ਕਤਾਰ 'ਚ +Name[pl]=Wstaw utwór do kolejki +Name[pt]=Colocar a Faixa em Espera +Name[pt_BR]=Faixa para Fila +Name[ru]=Поставить в очередь +Name[se]=Bija bihtá gárgadassii +Name[sk]=Zaradiť do frontu +Name[sr]=Стави нумеру у ред +Name[sr@Latn]=Stavi numeru u red +Name[sv]=Köa spår +Name[th]=เข้าคิวเพลง +Name[tr]=Parçayı Sıraya Ekle +Name[uk]=Додати доріжку в чергу +Name[uz]=Navbatga qoʻyish +Name[uz@cyrillic]=Навбатга қўйиш +Name[wa]=Mete dins l' lisse des schoûtants +Name[zh_CN]=音轨排队 +Name[zh_TW]=把曲目放入播放佇列 +Icon=amarok +Exec=amarok --queue %U diff --git a/amarok/src/amarok_codecinstall.desktop b/amarok/src/amarok_codecinstall.desktop new file mode 100644 index 00000000..aa7c2167 --- /dev/null +++ b/amarok/src/amarok_codecinstall.desktop @@ -0,0 +1,15 @@ +[Desktop Entry] +Encoding=UTF-8 +Type=ServiceType +X-KDE-ServiceType=Amarok/CodecInstall + +# The unplayable codec +[PropertyDef::X-KDE-Amarok-codec] +Type=QString + +[PropertyDef::X-KDE-Amarok-engine] +Type=QString + +# If true, Amarok will show a warning dialog about the experimental nature of this plugin. +[PropertyDef::Exec] +Type=QString diff --git a/amarok/src/amarok_export.h b/amarok/src/amarok_export.h new file mode 100644 index 00000000..23afb772 --- /dev/null +++ b/amarok/src/amarok_export.h @@ -0,0 +1,17 @@ +// Copyright: See COPYING file that comes with this distribution + +#ifndef _AMAROK_EXPORT_H_ +#define _AMAROK_EXPORT_H_ + +#include + +#ifdef __KDE_HAVE_GCC_VISIBILITY +#define LIBAMAROK_NO_EXPORT __attribute__ ((visibility("hidden"))) +#define LIBAMAROK_EXPORT __attribute__ ((visibility("default"))) +#else +#define LIBAMAROK_NO_EXPORT +#define LIBAMAROK_EXPORT +#endif + +#endif + diff --git a/amarok/src/amarok_play_audiocd.desktop b/amarok/src/amarok_play_audiocd.desktop new file mode 100644 index 00000000..6ccd96ec --- /dev/null +++ b/amarok/src/amarok_play_audiocd.desktop @@ -0,0 +1,55 @@ +[Desktop Entry] +ServiceTypes=media/audiocd +Actions=Play; +Encoding=UTF-8 +X-KDE-Priority=TopLevel + +[Desktop Action Play] +Name=Play Audio CD with Amarok +Name[af]=Speel oudio CD met Amarok +Name[bg]=Изпълнение на компактдиск с Amarok +Name[bn]=আমারক দিয়ে অডিও সিডি চালাও +Name[ca]=Reprodueix el CD Àudio amb l'Amarok +Name[cs]=Přehrát audio CD +Name[da]=Afspil lyd-cd med Amarok +Name[de]=Audio-CD mit Amarok wiedergeben +Name[el]=Αναπαραγωγή CD ήχου με το Amarok +Name[eo]=Ludi Muzikan KD-n per Amarok +Name[es]=Reproducir CD con Amarok +Name[et]=Esita audio CD Amarokis +Name[fa]=پخش دیسک فشردۀ صوتی با Amarok +Name[fi]=Soita CD-levy Amarokilla +Name[fr]=Écouter un CD Audio avec Amarok +Name[ga]=Seinn Dlúthdhiosca Fuaime le Amarok +Name[hu]=Hang-CD lejátszása az Amarokkal +Name[is]=Spila tónlistardisk með Amarok +Name[it]=Riproduci CD audio con Amarok +Name[ja]=Amarok でオーディオ CD を再生 +Name[ka]=აუიდიო CD-ის დაკვრა Amarok-ით +Name[km]=ចាក់​ស៊ីឌី​អូឌីយ៉ូ​ជា​មួយ Amarok +Name[lt]=Groti audio CD su Amarok +Name[mk]=Свири аудиоцд со Амарок +Name[nb]=Spill lyd-CD med Amarok +Name[nds]=Audio-CD mit Amarok afspelen +Name[ne]=अमारोकसँग अडियो सीडी बजाउनुहोस् +Name[nl]=Audio-cd met Amarok afspelen +Name[nn]=Spel lyd-CD med Amarok +Name[pa]=ਅਮਰੋਕ ਨਾਲ ਆਡੀਓ CD ਚਲਾਓ +Name[pl]=Odtwórz Audio CD za pomocą Amarok +Name[pt]=Tocar o CD de Áudio com o Amarok +Name[pt_BR]=Reproduzir CD de Áudio com o Amarok +Name[se]=Čuojat jietna-CD:a Amarokain +Name[sk]=Zahrať Audio CD pomocou Amarok +Name[sr]=Пусти аудио CD помоћу Amarok-а +Name[sr@Latn]=Pusti audio CD pomoću Amarok-a +Name[sv]=Spela ljud-cd med Amarok +Name[th]=เล่นซีดีบันทึกเสียงด้วย Amarok +Name[tr]=Ses CD'sini Amarok ile Çal +Name[uk]=Грати аудіо-КД в Amarok +Name[uz]=Audio-diskni Amarok bilan tinglash +Name[uz@cyrillic]=Аудио-дискни Amarok билан тинглаш +Name[wa]=Djouwer l' CD d' muzike avou Amarok +Name[zh_CN]=用 Amarok 播放音频 CD +Name[zh_TW]=以 Amarok 播放音樂 CD +Icon=amarok +Exec=amarok --cdplay %u diff --git a/amarok/src/amarok_plugin.desktop b/amarok/src/amarok_plugin.desktop new file mode 100644 index 00000000..7924352d --- /dev/null +++ b/amarok/src/amarok_plugin.desktop @@ -0,0 +1,88 @@ +[Desktop Entry] +Encoding=UTF-8 +Type=ServiceType +X-KDE-ServiceType=Amarok/Plugin +Comment=Plugin for Amarok +Comment[af]=Inprop module vir Amarok +Comment[ar]= قابس ( برنامج مضاف الى) AmaroK +Comment[bg]=Приставка за Amarok +Comment[bn]=আমারক-এর জন্য প্লাগিন +Comment[br]=Lugent evit Amarok +Comment[ca]=Connector per l'Amarok +Comment[cs]=Modul pro AmaroK +Comment[de]=Modul für Amarok +Comment[el]=Πρόσθετο για το AmaroK +Comment[eo]=Kromaĵo por Amarok +Comment[es]=Extensión para Amarok +Comment[et]=Amaroki plugin +Comment[fa]=وصله برای amaroK +Comment[fi]=Amarok-liitännäinen +Comment[fr]=Module pour Amarok +Comment[ga]=Breiseán AmaroK +Comment[gl]=Extensión para Amarok +Comment[hu]=Bővítőmodul az Amarokhoz +Comment[is]=Íforrit fyrir Amarok +Comment[it]=Plugin per Amarok +Comment[ja]=Amarok のためのプラグイン +Comment[ka]=მოდული Amarok-ისთვის +Comment[km]=កម្មវិធី​ជំនួយ​សម្រាប់ Amarok +Comment[lt]=Amarok įskiepis +Comment[mk]=Приклучок за Амарок +Comment[nb]=Programtillegg for Amarok +Comment[nds]=Moduul för Amarok +Comment[ne]=अमारोकका लागि प्लगइन +Comment[nl]=Plugin voor Amarok +Comment[nn]=Programtillegg for Amarok +Comment[pa]=ਅਮਰੋਕ ਲਈ ਪਲੱਗਇਨ +Comment[pl]=Wtyczka Amaroka +Comment[pt]='Plugin' para o Amarok +Comment[pt_BR]=Plugin para o Amarok +Comment[ru]=Модуль amaroK +Comment[se]=Lassemoduvla Amarok:ii +Comment[sk]=Amarok modul +Comment[sr]=Прикључак за Amarok +Comment[sr@Latn]=Priključak za Amarok +Comment[sv]=Insticksprogram för Amarok +Comment[th]=โปรแกรมเสริมสำหรับ Amarok +Comment[tr]=Amarok için Eklenti +Comment[uk]=Втулок для Amarok +Comment[uz]=Amarok uchun plagin +Comment[uz@cyrillic]=Amarok учун плагин +Comment[wa]=Tchôke-divins po Amarok +Comment[zh_CN]=Amarok 插件 +Comment[zh_TW]=amaroK 插件 + +# Type of plugin, e.g. "engine". +[PropertyDef::X-KDE-Amarok-plugintype] +Type=QString + +# Internal name for identification, not translated. +[PropertyDef::X-KDE-Amarok-name] +Type=QString + +# List of authors. +[PropertyDef::X-KDE-Amarok-authors] +Type=QStringList + +# List of author's email addresses. +[PropertyDef::X-KDE-Amarok-email] +Type=QStringList + +# Priority of the plugin. When KTrader returns multiple offers, the one with the highest rank is chosen. +# Range: 0 (disabled) - 255 (highest) +[PropertyDef::X-KDE-Amarok-rank] +Type=int + +# Version of the plugin. +[PropertyDef::X-KDE-Amarok-version] +Type=int + +# Version of the framework this plugin is compatible with. +# Must be bumped after making binary incompatible changes. +[PropertyDef::X-KDE-Amarok-framework-version] +Type=int + +# If true, Amarok will show a warning dialog about the experimental nature of this plugin. +[PropertyDef::X-KDE-Amarok-experimental] +Type=bool + diff --git a/amarok/src/amarok_proxy.rb b/amarok/src/amarok_proxy.rb new file mode 100644 index 00000000..7984ffcf --- /dev/null +++ b/amarok/src/amarok_proxy.rb @@ -0,0 +1,238 @@ +#!/usr/bin/env ruby +# +# Proxy server for Last.fm and DAAP. Relays the stream from the server to localhost, and +# converts the protocol to http on the fly. +# +# (c) 2006 Paul Cifarelli +# (c) 2006 Mark Kretschmann +# (c) 2006 Michael Fellinger +# (c) 2006 Ian Monroe +# (c) 2006 Martin Ellis +# (c) 2006 Alexandre Oliveira +# (c) 2006 Tom Kaitchuck +# +# License: GNU General Public License V2 + +# Amarok listens to stderr and recognizes these magic strings, do not remove them: +# "AMAROK_PROXY: startup", "AMAROK_PROXY: SYNC" + + +require 'socket' +require "uri" +$stdout.sync = true + +class Proxy + ENDL = "\r\n" + + def initialize( port, remote_url, engine, proxy ) + @engine = engine + + myputs( "running with port: #{port} and url: #{remote_url} and engine: #{engine}" ) + + # Open the amarok-facing socket + # amarok: the server port on the localhost to which the engine will connect. + amarok = TCPServer.new( port ) + myputs( "startup" ) + + # amaroks: server socket for above. + amaroks = amarok.accept + + # uri: from amarok, identifies the source of the music + uri = URI.parse( remote_url ) + myputs("host " << uri.host << " ") + myputs( port ) + + # Now we have the source of the music, determine the HTTP request that + # needs to be made to the remote server (or remote proxy). It will + # be of the form "GET ... HTTP/1.x". It will include the + # http://hostname/ part if, and only if, we're using a remote proxy. + get = get_request( uri, !proxy.nil? ) + + #Check for proxy + begin + proxy_uri = URI.parse( proxy ) + serv = TCPSocket.new( proxy_uri.host, proxy_uri.port ) + rescue + serv = TCPSocket.new( uri.host, uri.port ) + end + + serv.sync = true + myputs( "running with port: #{uri.port} and host: #{uri.host}" ) + + # Read the GET request from the engine + amaroks_get = amaroks.readline + myputs( amaroks_get.inspect ) + + myputs( get.inspect ) + myputs( "#{amaroks_get} but sending #{get}" ) + serv.puts( get ) + + # Copy the HTTP REQUEST headers from the amarok engine to the + # remote server, and signal end of headers. + myputs( "COPY from amarok -> serv" ) + cp_to_empty_outward( amaroks, serv ) + safe_write( serv, "\r\n\r\n" ) + + # Copy the HTTP RESPONSE headers from the server back to the + # amarok engine. + myputs( "COPY from serv -> amarok" ) + cp_to_empty_inward( serv, amaroks ) + + if @engine == 'gst10-engine' + 3.times do + myputs( "gst10-engine waiting for reconnect" ) + sleep 1 + break if amaroks.eof + end + amaroks = amarok.accept + safe_write( amaroks, "HTTP/1.0 200 OK\r\n\r\n" ) + amaroks.each_line do |data| + myputs( data ) + data.chomp! + break if data.empty? + end + end + + # Now stream the music! + myputs( "Before cp_all()" ) + cp_all_inward( serv, amaroks ) + + if @engine == 'helix-engine' && amaroks.eof + myputs( "EOF Detected, reconnecting" ) + amaroks = amarok.accept + cp_all_inward( serv, amaroks ) + end + end + + def safe_write( output, data ) + begin + output.write data + rescue + myputs( "error from output.write, #{$!}" ) + myputs( $!.backtrace.inspect ) + break + end + end + + def cp_to_empty_outward( income, output ) + myputs "cp_to_empty_outward( income => #{income.inspect}, output => #{output.inspect}" + income.each_line do |data| + if data =~ /User-Agent: xine\/([0-9.]+)/ + version = $1.split(".").collect { |v| v.to_i } + myputs("Found xine user agent version #{version.join(".")}") + @xineworkaround = ( version[0] <= 1 && version[1] <= 1 && version[2] <= 2 ) + end + myputs( data ) + data.chomp! + safe_write( output, data ) + myputs( "data sent.") + return if data.empty? + end + end + + def desync (data) + if data.gsub!( "SYNC", "" ) + myputs( "SYNC" ) + end + end + + def cp_to_empty_inward( income, output ) + myputs( "cp_to_empty_inward( income => #{income.inspect}, output => #{output.inspect}" ) + income.each_line do |data| + myputs( data ) + safe_write( output, data ) + return if data.chomp == "" + end + end + + def cp_all_inward( income, output ) + myputs( "cp_all( income => #{income.inspect}, output => #{output.inspect}" ) + if self.is_a?( LastFM ) and @xineworkaround + myputs( "Using buffer fill workaround." ) + filler = Array.new( 4096, 0 ) + safe_write( output, filler ) # HACK: Fill xine's buffer so that xine_open() won't block + end + if @engine == 'helix-engine' + data = income.read( 1024 ) + else + data = income.read( 4 ) + end + desync( data ) + holdover = "" + loop do + begin + safe_write( output, data ) + rescue + myputs( "error from o.write, #{$!}" ) + break + end + newdata = income.read( 1024 ) + + data = holdover + newdata[0..-5] + holdover = newdata[-4..-1] + desync( data ) + + break if newdata == nil + end + end +end + +class LastFM < Proxy +# Last.fm protocol: +# Stream consists of pure MP3 files concatenated, with the string "SYNC" in between, which +# marks a track change. The proxy notifies Amarok on track change. + + def get_request( remote_uri, via_proxy ) + # remote_uri - the URI of the stream we want + # via_proxy - true iff we're going through another proxy + if via_proxy then + url = remote_uri.to_s + else + url = "#{remote_uri.path || '/'}?#{remote_uri.query}" + end + get = "GET #{url} HTTP/1.0" + ENDL + get += "Host: #{remote_uri.host}:#{remote_uri.port}" + ENDL + ENDL + end + +end + +class DaapProxy < Proxy + def initialize( port, remote_url, engine, hash, request_id, proxy ) + @hash = hash + @requestId = request_id + super( port, remote_url, engine, proxy ) + end + + def get_request( remote_uri, via_proxy ) + # via_proxy ignored for now + get = "GET #{remote_uri.path || '/'}?#{remote_uri.query} HTTP/1.0" + ENDL + get += "Accept: */*" + ENDL + get += "User-Agent: iTunes/4.6 (Windows; N)" + ENDL + get += "Client-DAAP-Version: 3.0" + ENDL + get += "Client-DAAP-Validation: #{@hash}" + ENDL + get += "Client-DAAP-Access-Index: 2" + ENDL + get += "Client-DAAP-Request-ID: #{@requestId}" + ENDL + get += "Host: #{remote_uri.host}:#{remote_uri.port}" + ENDL + ENDL + get + end +end + +def myputs( string ) + $stdout.puts( "AMAROK_PROXY: #{string}" ) +end + +begin + myputs( ARGV ) + if( ARGV[0] == "--lastfm" ) then + option, port, remote_url, engine, proxy = ARGV + LastFM.new( port, remote_url, engine, proxy ) + else + option, port, remote_url, engine, hash, request_id, proxy = ARGV + DaapProxy.new( port, remote_url, engine, hash, request_id, proxy ) + end +rescue + myputs( $!.to_s ) + myputs( $!.backtrace.inspect ) +end + +puts( "exiting" ) diff --git a/amarok/src/amarokcore/Makefile.am b/amarok/src/amarokcore/Makefile.am new file mode 100644 index 00000000..66e8a691 --- /dev/null +++ b/amarok/src/amarokcore/Makefile.am @@ -0,0 +1,28 @@ +INCLUDES = \ + -I$(top_builddir)/amarok/src \ + -I$(top_builddir)/amarok/src/amarokcore \ + -I$(top_srcdir)/amarok/src \ + -I$(top_srcdir)/amarok/src/engine \ + -I$(top_srcdir)/amarok/src/plugin \ + -I$(top_srcdir)/amarok/src/statusbar \ + -I$(top_srcdir)/amarok/src/mediadevice \ + $(all_includes) + +noinst_LTLIBRARIES = \ + libamarokcore.la + +noinst_HEADERS = \ + amarokconfig.h \ + amarokdcophandler.h + +libamarokcore_la_SOURCES = \ + amarokdcopiface.skel \ + amarokdcophandler.cpp \ + amarokconfig.kcfgc \ + crashhandler.cpp + +METASOURCES = \ + AUTO + +kde_kcfg_DATA = \ + amarok.kcfg diff --git a/amarok/src/amarokcore/amarok.kcfg b/amarok/src/amarokcore/amarok.kcfg new file mode 100644 index 00000000..9083595c --- /dev/null +++ b/amarok/src/amarokcore/amarok.kcfg @@ -0,0 +1,705 @@ + + + +qdir.h + + + + + + + Amarok version string, used to restart aRts in new installations. + + + + + The position of the Amarok main window when Amarok is started. + + + + If set the player window will start in minimal view + false + + + + The position of the playlist window when Amarok is started. + + + + The size of the playlist window when Amarok is started. + + + + If set, Amarok saves the current playlist on quit and restores it when restarted. + true + + + + If set, amarok follows symlinks when adding files or directories to the playlist. + true + + + + Set this to display a second time label to the left of the seek slider in the player window. + true + + + + Set this to display remaining track time instead of past track time in the player window. + false + + + + A score is a number from 0 to 100, determined automatically by Amarok based on how often you listen to a track and how much of it you listen to. + true + + + + A rating is 1 to 5 stars, set manually by you to describe how well you like a given track. + false + + + + Selects whether the user wants to use custom colors for ratings stars. + false + + + + Selects whether the user wants to define a custom color for the half-star. + true + + + + Whether to repeat the current track, the current album, or the current playlist indefinitely, or neither. + + + + + + + + + + + + + + + Off + + + + Tracks or albums with the chosen property will be more likely to be chosen in Random Mode. + + + + + + + Off + + + + If set, Amarok plays the tracks or albums in the playlist in a random order. + + + + + + Off + + + + The title of the Dynamic Mode that was most recently loaded in the playlist + Random Mix + + + + The name of the custom scoring script which was most recently loaded + + + + + Enable/Disable tray icon for Amarok. + true + + + + Enable/Disable tray icon animation. + false + + + + Makes Amarok more like XMMS and other Winamp clones with separate player and playlist windows. + false + + + + Displays a visual representation of the current track in the slider bar of the player window, the playlist window, and in a column of the playlist window. + false + + + + Enabling this option stores Mood data files with the music files. Disabling stores them in your home folder. + false + + + + When enabled, the hue distribution is quantised and respread evenly, giving a prettier but less meaningful output. + true + + + + The hues are distributed according to a colour theme, giving a customisable look. + 0 + + + + At the bottom of the playlist window, the toolbar can be used for buttons such as play and stop. + true + + + + Size of the cover previews in Contextbrowser and Covermanager + 130 + + + + Enable/Disable recursive directory adding to the playlist. + true + + + + Delay between tracks, in milliseconds. + 0 + + + + Enable/Disable the playlist window. Equal to clicking the PL button in the player window. + true + + + + The number of undo levels in the playlist. + 30 + + + + The ID of the visual analyzer to display. + -1 + + + + The ID of the visual analyzer to display in playlist window. + 0 + + + + Currently unused + 70,140 + + + + Enable/Disable splashscreen during Amarok startup. + true + + + + Automatically switches to ContextBrowser when playback is started. + false + + + + Set this to the style dir you want to use. + Default + + + + If set, Amarok's manually saved playlists will contain a relative path to each track, not an absolute path. + true + + + + If set, Organize files will overwrite any existing destination. + false + + + + If set, Organize files will group directories containing the same filetype. + false + + + + If set, Organize files will group artist starting in the same character. + true + + + + If set, Organize files will ignore The in artist names. + true + + + + If set, Organize files will replace spaces in filenames by an underscore. + false + + + + If set, Organize files will use cover art as folder icons. + true + + + + The ID of the collection folder destination for Organize files. + 0 + + + + If set, Organize files will replace characters that are not compatible with vfat filesystems (such as ':', '*' and '?'). + true + + + + If set, Organize files will replace characters that are not compatible with the 7-bit ASCII character set. + false + + + + If set, Organize files will rename files according to a custom format string. + false + + + + If the custom filename scheme is enabled, then Organize files will rename files according to this format string. + %folder/%filetype/%theartist/%album/%track_-_%title.%filetype + + + + Organize files will replace substrings matching this regular expression. + + + + + Organize files will replace matching substrings with this string. + + + + + kfmclient openURL + + + + true + + + + 0 + + + + + + + The Amarok master volume, a value between 0 (muted) and 100. + 80 + 0 + 100 + + + + Enable/Disable crossfading between track changes. + false + + + + The length of the crossfade between tracks in milliseconds. + 4000 + 400 + + + + Determines whether to crossfade always, or on automatic/manual track changes only. + 0 + + + + Enable/Disable fadeout. + true + + + + The length of the fadeout in milliseconds. + 3500 + 400 + + + + true + + + + Select the sound system used to play media. Amarok currently support aRts, GStreamer, xine, and NMM; however, their availability depends on the configuration used at compile time. + + + + When enabled, an equalizer plugin filters the audio stream. + false + + + + 0 + -100 + 100 + + + + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + + + + Zero + + + + + + + Determines which Amazon server cover images should be retrieved from. + us + + + + + + + Determines in which language the information is retrieved from Wikipedia. + en + + + + + + + Enable/Disable the On-Screen Display. + true + + + + If enabled, the OSD will display the same information and in the same order as the columns in the playlist. + true + + + + Customize the OSD display text. + %artist - %title {(%length)}\n%album + + + + The font to use for the On-Screen Display. + QFont("Arial",16) + + + + Draws a shadow around the OSD-text. + true + + + + Make the background of the OSD use fake-translucency. + true + + + + You can use custom colors for the OSD if you set this true. + false + + + + The color of the OSD text. The color is specified in RGB, a comma-separated list containing three integers between 0 and 255. + #ffff00 + + + + The color of the OSD background. The color is specified in RGB, a comma-separated list containing three integers between 0 and 255. + #1500a0 + + + + #0000ff + + + + The time in milliseconds to show the OSD. A value of 0 means never hide. The default value is 5000 ms. + 5000 + 0 + + + + The Y position of the OSD relative to the chosen screen and OSD alignment. If Top alignment is chosen the Y offset is the space between the upper part of the OSD and the top of the screen. If Bottom alignment is chosen the Y offset is the space between the bottom part of the OSD and the bottom of the screen. + 50 + 0 + 10000 + + + + The screen that should display the OSD. For single-headed environments this setting should be 0. + 0 + + + + If enabled, shows the album cover in the OSD. + true + + + + The relative position of the OSD. Possible choices are Left, Middle, Right and Center. + + + + + + + Center + + + + + + + + Enabled/Disables custom fonts. + false + + + + The font to use in the playlist window. + + + + The font to use in the player window. + + + + The font to use in the context browser. + + + + + + + If set, Amarok uses the Amarok standard colors in the playlist. + false + + + + If set, Amarok uses the KDE standard colors in the playlist. + true + + + + If set, Amarok uses the user-defined colors in the playlist. + false + + + + The color to use as foreground color in the playlist. The color is specified in RGB, a comma-separated list containing three integers between 0 and 255. + #80a0ff + + + + The color to use as background color in the playlist. The color is specified in RGB, a comma-separated list containing three integers between 0 and 255. + #000000 + + + + The color to use for a half rating star, if not the default. + + + + The color to use for a single rating star, if not the default. + + + + The color to use for two rating stars, if not the default. + + + + The color to use for three rating stars, if not the default. + + + + The color to use for four rating stars, if not the default. + + + + The color to use for five rating stars, if not the default. + + + + + + + If set, Amarok resumes playback of the last played track on startup. + false + + + + Internal: URL of the track to resume on startup. + + + + Internal: Playback position in the track to resume on startup. + + + + + + + The database engine used to store collection + SQLite + + + + true + + + + true + + + + + + + + + + The host MySql server is running on + localhost + + + + The port MySql server is listening + 3306 + + + + The database's name + amarok + + + + The user's name to use for connecting MySql + + + + The user's password + + + + The user's password + + + + + + + The host Postgresql server is running on + localhost + + + + The port Postgresql server is listening + 5432 + + + + The database's name + amarok + + + + The user's name to use for connecting Postgresql + amarok + + + + The user's password + + + + The user's password + + + + + + + Whether played songs are submitted to Audioscrobbler + false + + + + The username to use for connecting to Audioscrobbler + + + + + The password to use for connecting to Audioscrobbler + + + + + Whether similar songs are retrieved from Audioscrobbler + false + + + + + + + The type of media device. + + + + The mount point used for the media device connection. + + + + The mount command used for the media device connection. + + + + The umount command used for the media device connection. + + + + Whether podcasts shows already played are automatically deleted when media device is connected. + + + + Whether Amarok statistics should be synchronized with play count/ratings on device and whether tracks played should be submitted to last.fm. + + + + Whether to automatically try to connect media device when starting Amarok. + true + + + + + + + Music Sharing servers added by the user. + + + + + Passwords stored by hostname. + + + + diff --git a/amarok/src/amarokcore/amarokconfig.kcfgc b/amarok/src/amarokcore/amarokconfig.kcfgc new file mode 100644 index 00000000..f8a0c8fd --- /dev/null +++ b/amarok/src/amarokcore/amarokconfig.kcfgc @@ -0,0 +1,8 @@ +# Code generation options for kconfig_compiler +File=amarok.kcfg +ClassName=AmarokConfig +Singleton=true +Mutators=true +MemberVariables=private +Visibility=LIBAMAROK_EXPORT +IncludeFiles=amarok_export.h diff --git a/amarok/src/amarokcore/amarokdcophandler.cpp b/amarok/src/amarokcore/amarokdcophandler.cpp new file mode 100644 index 00000000..740f9d6f --- /dev/null +++ b/amarok/src/amarokcore/amarokdcophandler.cpp @@ -0,0 +1,1059 @@ +/*************************************************************************** + amarokdcophandler.cpp - DCOP Implementation + ------------------- + begin : Sat Oct 11 2003 + copyright : (C) 2003 by Stanislav Karchebny + (C) 2004 Christian Muehlhaeuser + (C) 2005 Ian Monroe + (C) 2005 Seb Ruiz + (C) 2006 Alexandre Oliveira + email : berkus@users.sf.net +***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#include "amarok.h" +#include "amarokconfig.h" +#include "amarokdcophandler.h" +#include "app.h" //transferCliArgs +#include "debug.h" +#include "collectiondb.h" +#include "contextbrowser.h" +#include "devicemanager.h" +#include "enginebase.h" +#include "enginecontroller.h" +#include "equalizersetup.h" +#include "htmlview.h" +#include "mediabrowser.h" +#include "mountpointmanager.h" +#include "osd.h" +#include "playlist.h" +#include "playlistbrowser.h" +#include "playlistitem.h" +#include "playlistwindow.h" +#include "scancontroller.h" +#include "scriptmanager.h" +#include "statusbar.h" +#include "lastfm.h" + +#include + +#include +#include +#include + + +namespace Amarok +{ +///////////////////////////////////////////////////////////////////////////////////// +// class DcopPlayerHandler +///////////////////////////////////////////////////////////////////////////////////// + + DcopPlayerHandler::DcopPlayerHandler() + : DCOPObject( "player" ) + , QObject( kapp ) + { + // Register with DCOP + if ( !kapp->dcopClient()->isRegistered() ) { + kapp->dcopClient()->registerAs( "amarok", false ); + kapp->dcopClient()->setDefaultObject( objId() ); + } + } + + QString DcopPlayerHandler::version() + { + return APP_VERSION; + } + + bool DcopPlayerHandler::dynamicModeStatus() + { + return Amarok::dynamicMode(); + } + + bool DcopPlayerHandler::equalizerEnabled() + { + if(EngineController::hasEngineProperty( "HasEqualizer" )) + return AmarokConfig::equalizerEnabled(); + else + return false; + } + + bool DcopPlayerHandler::osdEnabled() + { + return AmarokConfig::osdEnabled(); + } + + bool DcopPlayerHandler::isPlaying() + { + return EngineController::engine()->state() == Engine::Playing; + } + + bool DcopPlayerHandler::randomModeStatus() + { + return AmarokConfig::randomMode(); + } + + bool DcopPlayerHandler::repeatPlaylistStatus() + { + return Amarok::repeatPlaylist(); + } + + bool DcopPlayerHandler::repeatTrackStatus() + { + return Amarok::repeatTrack(); + } + + int DcopPlayerHandler::getVolume() + { + return EngineController::engine() ->volume(); + } + + int DcopPlayerHandler::sampleRate() + { + return EngineController::instance()->bundle().sampleRate(); + } + + float DcopPlayerHandler::score() + { + const MetaBundle &bundle = EngineController::instance()->bundle(); + float score = CollectionDB::instance()->getSongPercentage( bundle.url().path() ); + return score; + } + + int DcopPlayerHandler::rating() + { + const MetaBundle &bundle = EngineController::instance()->bundle(); + int rating = CollectionDB::instance()->getSongRating( bundle.url().path() ); + return rating; + } + + int DcopPlayerHandler::status() + { + // <0 - error, 0 - stopped, 1 - paused, 2 - playing + switch( EngineController::engine()->state() ) + { + case Engine::Playing: + return 2; + case Engine::Paused: + return 1; + case Engine::Empty: + case Engine::Idle: + return 0; + } + return -1; + } + + int DcopPlayerHandler::trackCurrentTime() + { + return EngineController::instance()->trackPosition() / 1000; + } + + int DcopPlayerHandler::trackCurrentTimeMs() + { + return EngineController::instance()->trackPosition(); + } + + int DcopPlayerHandler::trackPlayCounter() + { + const MetaBundle &bundle = EngineController::instance()->bundle(); + int count = CollectionDB::instance()->getPlayCount( bundle.url().path() ); + return count; + } + + int DcopPlayerHandler::trackTotalTime() + { + return EngineController::instance()->bundle().length(); + } + + QStringList DcopPlayerHandler::labels() + { + const MetaBundle &bundle = EngineController::instance()->bundle(); + return CollectionDB::instance()->getLabels( bundle.url().path(), CollectionDB::typeUser ); + } + + QString DcopPlayerHandler::album() + { + return EngineController::instance()->bundle().album(); + } + + QString DcopPlayerHandler::artist() + { + return EngineController::instance()->bundle().artist(); + } + + QString DcopPlayerHandler::bitrate() + { + return EngineController::instance()->bundle().prettyBitrate(); + } + + QString DcopPlayerHandler::comment() + { + return EngineController::instance()->bundle().comment(); + } + + QString DcopPlayerHandler::coverImage() + { + const MetaBundle &bundle = EngineController::instance()->bundle(); + QString image = CollectionDB::instance()->albumImage( bundle, 0 ); + return image; + } + + QString DcopPlayerHandler::currentTime() + { + return MetaBundle::prettyLength( EngineController::instance()->trackPosition() / 1000 ,true ); + } + + QString DcopPlayerHandler::encodedURL() + { + return EngineController::instance()->bundle().url().url(); + } + + QString DcopPlayerHandler::engine() + { + return AmarokConfig::soundSystem(); + } + + QString DcopPlayerHandler::genre() + { + return EngineController::instance()->bundle().genre(); + } + + QString DcopPlayerHandler::lyrics() + { + return CollectionDB::instance()->getLyrics( EngineController::instance()->bundle().url().path() ); + } + + QString DcopPlayerHandler::lyricsByPath( QString path ) + { + return CollectionDB::instance()->getLyrics( path ); + } + + QString DcopPlayerHandler::lastfmStation() + { + return LastFm::Controller::stationDescription(); //return QString::null if not playing + } + + QString DcopPlayerHandler::nowPlaying() + { + return EngineController::instance()->bundle().prettyTitle(); + } + + QString DcopPlayerHandler::path() + { + return EngineController::instance()->bundle().url().path(); + } + + QString DcopPlayerHandler::setContextStyle(const QString& msg) + { + AmarokConfig::setContextBrowserStyleSheet( msg ); + ContextBrowser::instance()->reloadStyleSheet(); + + if ( QFile::exists( Amarok::saveLocation( "themes/" + msg + '/' ) + "stylesheet.css" ) ) + return "Context browser theme '"+msg+"' applied."; + else + return "No such theme '"+msg+"' exists, default theme applied."; + } + + QString DcopPlayerHandler::title() + { + return EngineController::instance()->bundle().title(); + } + + QString DcopPlayerHandler::totalTime() + { + return EngineController::instance()->bundle().prettyLength(); + } + + QString DcopPlayerHandler::track() + { + if ( EngineController::instance()->bundle().track() != 0 ) + return QString::number( EngineController::instance()->bundle().track() ); + else + return QString(); + } + + QString DcopPlayerHandler::type() + { + if (EngineController::instance()->bundle().url().protocol() == "lastfm") + return QString("LastFm Stream"); + else + return EngineController::instance()->bundle().type(); + } + + QString DcopPlayerHandler::year() + { + return QString::number( EngineController::instance()->bundle().year() ); + } + + void DcopPlayerHandler::configEqualizer() + { + if(EngineController::hasEngineProperty( "HasEqualizer" )) + EqualizerSetup::instance()->show(); + EqualizerSetup::instance()->raise(); + } + + void DcopPlayerHandler::enableOSD(bool enable) + { + Amarok::OSD::instance()->setEnabled( enable ); + AmarokConfig::setOsdEnabled( enable ); + } + + void DcopPlayerHandler::enableRandomMode( bool enable ) + { + static_cast(Amarok::actionCollection()->action( "random_mode" )) + ->setCurrentItem( enable ? AmarokConfig::EnumRandomMode::Tracks : AmarokConfig::EnumRandomMode::Off ); + } + + void DcopPlayerHandler::enableRepeatPlaylist( bool enable ) + { + static_cast( Amarok::actionCollection()->action( "repeat" ) ) + ->setCurrentItem( enable ? AmarokConfig::EnumRepeat::Playlist : AmarokConfig::EnumRepeat::Off ); + } + + void DcopPlayerHandler::enableRepeatTrack( bool enable) + { + static_cast( Amarok::actionCollection()->action( "repeat" ) ) + ->setCurrentItem( enable ? AmarokConfig::EnumRepeat::Track : AmarokConfig::EnumRepeat::Off ); + } + + void DcopPlayerHandler::mediaDeviceMount() + { + if ( MediaBrowser::instance()->currentDevice() ) + MediaBrowser::instance()->currentDevice()->connectDevice(); + } + + void DcopPlayerHandler::mediaDeviceUmount() + { + if ( MediaBrowser::instance()->currentDevice() ) + MediaBrowser::instance()->currentDevice()->disconnectDevice(); + } + + void DcopPlayerHandler::mute() + { + EngineController::instance()->mute(); + } + + void DcopPlayerHandler::next() + { + EngineController::instance() ->next(); + } + + void DcopPlayerHandler::pause() + { + EngineController::instance()->pause(); + } + + void DcopPlayerHandler::play() + { + EngineController::instance() ->play(); + } + + void DcopPlayerHandler::playPause() + { + EngineController::instance() ->playPause(); + } + + void DcopPlayerHandler::prev() + { + EngineController::instance() ->previous(); + } + + void DcopPlayerHandler::queueForTransfer( KURL url ) + { + MediaBrowser::queue()->addURL( url ); + MediaBrowser::queue()->URLsAdded(); + } + + void DcopPlayerHandler::seek(int s) + { + if ( s > 0 && EngineController::engine()->state() != Engine::Empty ) + EngineController::instance()->seek( s * 1000 ); + } + + void DcopPlayerHandler::seekRelative(int s) + { + EngineController::instance() ->seekRelative( s * 1000 ); + } + + void DcopPlayerHandler::setEqualizer(int preamp, int band60, int band170, int band310, + int band600, int band1k, int band3k, int band6k, int band12k, int band14k, int band16k) + { + if( EngineController::hasEngineProperty( "HasEqualizer" ) ) { + bool instantiated = EqualizerSetup::isInstantiated(); + EqualizerSetup* eq = EqualizerSetup::instance(); + + QValueList gains; + gains << band60 << band170 << band310 << band600 << band1k + << band3k << band6k << band12k << band14k << band16k; + + eq->setBands( preamp, gains ); + if( !instantiated ) + delete eq; + } + } + + void DcopPlayerHandler::setEqualizerEnabled( bool active ) + { + EngineController::engine()->setEqualizerEnabled( active ); + AmarokConfig::setEqualizerEnabled( active ); + + if( EqualizerSetup::isInstantiated() ) + EqualizerSetup::instance()->setActive( active ); + } + + void DcopPlayerHandler::setEqualizerPreset( QString name ) + { + if( EngineController::hasEngineProperty( "HasEqualizer" ) ) { + bool instantiated = EqualizerSetup::isInstantiated(); + EqualizerSetup* eq = EqualizerSetup::instance(); + eq->setPreset( name ); + if ( !instantiated ) + delete eq; + } + } + + void DcopPlayerHandler::setLyricsByPath( const QString& url, const QString& lyrics ) + { + CollectionDB::instance()->setLyrics( url, lyrics ); + } + + void DcopPlayerHandler::setScore( float score ) + { + const QString &url = EngineController::instance()->bundle().url().path(); + CollectionDB::instance()->setSongPercentage(url, score); + } + + void DcopPlayerHandler::setScoreByPath( const QString &url, float score ) + { + CollectionDB::instance()->setSongPercentage(url, score); + } + + void DcopPlayerHandler::setBpm( float bpm ) + { + MetaBundle bundle = EngineController::instance()->bundle(); + bundle.setBpm( bpm ); + bundle.save(); + CollectionDB::instance()->updateTags( bundle.url().path(), bundle, true ); + } + + void DcopPlayerHandler::setBpmByPath( const QString &url, float bpm ) + { + MetaBundle bundle( url ); + bundle.setBpm(bpm); + bundle.save(); + CollectionDB::instance()->updateTags( bundle.url().path(), bundle, true ); + } + + void DcopPlayerHandler::setRating( int rating ) + { + const QString &url = EngineController::instance()->bundle().url().path(); + CollectionDB::instance()->setSongRating(url, rating); + } + + void DcopPlayerHandler::setRatingByPath( const QString &url, int rating ) + { + CollectionDB::instance()->setSongRating(url, rating); + } + + void DcopPlayerHandler::setVolume(int volume) + { + EngineController::instance()->setVolume(volume); + } + + void DcopPlayerHandler::setVolumeRelative(int ticks) + { + EngineController::instance()->increaseVolume(ticks); + } + + void DcopPlayerHandler::showBrowser( QString browser ) + { + if ( browser == "context" ) + PlaylistWindow::self()->showBrowser( "ContextBrowser" ); + if ( browser == "collection" ) + PlaylistWindow::self()->showBrowser( "CollectionBrowser" ); + if ( browser == "playlist" ) + PlaylistWindow::self()->showBrowser( "PlaylistBrowser" ); + if ( browser == "media" ) + PlaylistWindow::self()->showBrowser( "MediaBrowser" ); + if ( browser == "file" ) + PlaylistWindow::self()->showBrowser( "FileBrowser" ); + } + + void DcopPlayerHandler::showOSD() + { + Amarok::OSD::instance()->forceToggleOSD(); + } + + void DcopPlayerHandler::stop() + { + EngineController::instance() ->stop(); + } + + void DcopPlayerHandler::transferDeviceFiles() + { + if ( MediaBrowser::instance()->currentDevice() ) + MediaBrowser::instance()->currentDevice()->transferFiles(); + } + + void DcopPlayerHandler::volumeDown() + { + EngineController::instance()->decreaseVolume(); + } + + void DcopPlayerHandler::volumeUp() + { + EngineController::instance()->increaseVolume(); + } + + void DcopPlayerHandler::transferCliArgs( QStringList args ) + { + DEBUG_BLOCK + + //stop startup cursor animation - do not mess with this, it's carefully crafted + //NOTE I have no idea why we need to do this, I never get startup notification from + //the amarok binary anyway --mxcl + debug() << "Startup ID: " << args.first() << endl; + kapp->setStartupId( args.first().local8Bit() ); +#ifdef Q_WS_X11 + // currently X11 only + KStartupInfo::appStarted(); +#endif + args.pop_front(); + + const int argc = args.count() + 1; + char **argv = new char*[argc]; + + QStringList::ConstIterator it = args.constBegin(); + for( int i = 1; i < argc; ++i, ++it ) { + argv[i] = qstrdup( (*it).local8Bit() ); + debug() << "Extracted: " << argv[i] << endl; + } + + // required, loader doesn't add it + argv[0] = qstrdup( "amarokapp" ); + + // re-initialize KCmdLineArgs with the new arguments + App::initCliArgs( argc, argv ); + App::handleCliArgs(); + + //FIXME are we meant to leave this around? + //FIXME are we meant to allocate it all on the heap? + //NOTE we allow the memory leak because I think there are + // some very mysterious crashes due to deleting this + //delete[] argv; + } + +///////////////////////////////////////////////////////////////////////////////////// +// class DcopPlaylistHandler +///////////////////////////////////////////////////////////////////////////////////// + + DcopPlaylistHandler::DcopPlaylistHandler() + : DCOPObject( "playlist" ) + , QObject( kapp ) + {} + + int DcopPlaylistHandler::getActiveIndex() + { + return Playlist::instance()->currentTrackIndex( false ); + } + + int DcopPlaylistHandler::getTotalTrackCount() + { + return Playlist::instance()->totalTrackCount(); + } + + QString DcopPlaylistHandler::saveCurrentPlaylist() + { + Playlist::instance()->saveXML( Playlist::defaultPlaylistPath() ); + return Playlist::defaultPlaylistPath(); + } + + void DcopPlaylistHandler::addMedia(const KURL &url) + { + Playlist::instance()->appendMedia(url); + } + + void DcopPlaylistHandler::addMediaList(const KURL::List &urls) + { + Playlist::instance()->insertMedia(urls); + } + + void DcopPlaylistHandler::queueMedia(const KURL &url) + { + Playlist::instance()->insertMedia(KURL::List( url ), Playlist::Queue); + } + + void DcopPlaylistHandler::clearPlaylist() + { + Playlist::instance()->clear(); + } + + void DcopPlaylistHandler::playByIndex(int index) + { + Playlist::instance()->activateByIndex( index ); + } + + void DcopPlaylistHandler::playMedia( const KURL &url ) + { + Playlist::instance()->insertMedia( url, Playlist::DirectPlay | Playlist::Unique); + } + + void DcopPlaylistHandler::popupMessage( const QString& msg ) + { + StatusBar::instance()->longMessageThreadSafe( msg ); + } + + void DcopPlaylistHandler::removeCurrentTrack() + { + PlaylistItem* const item = Playlist::instance()->currentTrack(); + if ( item ) { + if( item->isBeingRenamed() ) + item->setDeleteAfterEditing( true ); + else + { + Playlist::instance()->removeItem( item ); + delete item; + } + } + } + + void DcopPlaylistHandler::removeByIndex( int index ) + { + PlaylistItem* const item = + static_cast( Playlist::instance()->itemAtIndex( index ) ); + + if ( item ) { + Playlist::instance()->removeItem( item ); + delete item; + } + } + + void DcopPlaylistHandler::repopulate() + { + Playlist::instance()->repopulate(); + } + + void DcopPlaylistHandler::saveM3u( const QString& path, bool relativePaths ) + { + Playlist::instance()->saveM3U( path, relativePaths ); + } + + void DcopPlaylistHandler::setStopAfterCurrent( bool on ) + { + Playlist::instance()->setStopAfterCurrent( on ); + } + + void DcopPlaylistHandler::shortStatusMessage(const QString& msg) + { + StatusBar::instance()->shortMessage( msg ); + } + + void DcopPlaylistHandler::shufflePlaylist() + { + Playlist::instance()->shuffle(); + } + + void DcopPlaylistHandler::togglePlaylist() + { + PlaylistWindow::self()->showHide(); + } + + QStringList DcopPlaylistHandler::filenames() + { + Playlist *p_inst = Playlist::instance(); + QStringList songlist; + + if (!p_inst) + return songlist; + + PlaylistItem *p_item = p_inst->firstChild(); + + while (p_item) + { + songlist.append(p_item->filename()); + p_item = p_item->nextSibling(); + } + + return songlist; + } + + QString DcopPlaylistHandler::currentTrackUniqueId() + { + if( Playlist::instance()->currentItem() ) + return Playlist::instance()->currentItem()->uniqueId(); + return QString(); + } + +///////////////////////////////////////////////////////////////////////////////////// +// class DcopPlaylistBrowserHandler +///////////////////////////////////////////////////////////////////////////////////// + + DcopPlaylistBrowserHandler::DcopPlaylistBrowserHandler() + : DCOPObject( "playlistbrowser" ) + , QObject( kapp ) + {} + + void DcopPlaylistBrowserHandler::addPodcast( const QString &url ) + { + PlaylistBrowser::instance()->addPodcast( url ); + } + + void DcopPlaylistBrowserHandler::scanPodcasts() + { + PlaylistBrowser::instance()->scanPodcasts(); + } + + void DcopPlaylistBrowserHandler::addPlaylist( const QString &url ) + { + PlaylistBrowser::instance()->addPlaylist( url ); + } + + int DcopPlaylistBrowserHandler::loadPlaylist( const QString &playlist ) + { + return PlaylistBrowser::instance()->loadPlaylist( playlist ); + } + +///////////////////////////////////////////////////////////////////////////////////// +// class DcopContextBrowserHandler +///////////////////////////////////////////////////////////////////////////////////// + + DcopContextBrowserHandler::DcopContextBrowserHandler() + : DCOPObject( "contextbrowser" ) + , QObject( kapp ) + {} + + void DcopContextBrowserHandler::showCurrentTrack() + { + ContextBrowser::instance()->showCurrentTrack(); + } + + void DcopContextBrowserHandler::showLyrics() + { + ContextBrowser::instance()->showLyrics(); + } + + void DcopContextBrowserHandler::showWiki() + { + ContextBrowser::instance()->showWikipedia(); + } + + void DcopContextBrowserHandler::showLyrics( const QCString& lyrics ) + { + ContextBrowser::instance()->lyricsResult( lyrics ); + } + +///////////////////////////////////////////////////////////////////////////////////// +// class DcopCollectionHandler +///////////////////////////////////////////////////////////////////////////////////// + + DcopCollectionHandler::DcopCollectionHandler() + : DCOPObject( "collection" ) + , QObject( kapp ) + {} + + int DcopCollectionHandler::totalAlbums() + { + QStringList albums = CollectionDB::instance()->query( "SELECT COUNT( id ) FROM album;" ); + QString total = albums[0]; + return total.toInt(); + } + + int DcopCollectionHandler::totalArtists() + { + QStringList artists = CollectionDB::instance()->query( "SELECT COUNT( id ) FROM artist;" ); + QString total = artists[0]; + return total.toInt(); + } + + int DcopCollectionHandler::totalComposers() + { + QStringList composers = CollectionDB::instance()->query( "SELECT COUNT( id ) FROM composer;" ); + QString total = composers[0]; + return total.toInt(); + } + + int DcopCollectionHandler::totalCompilations() + { + QStringList comps = CollectionDB::instance()->query( "SELECT COUNT( DISTINCT album ) FROM tags WHERE sampler = 1;" ); + QString total = comps[0]; + return total.toInt(); + } + + int DcopCollectionHandler::totalGenres() + { + QStringList genres = CollectionDB::instance()->query( "SELECT COUNT( id ) FROM genre;" ); + QString total = genres[0]; + return total.toInt(); + } + + int DcopCollectionHandler::totalTracks() + { + QStringList tracks = CollectionDB::instance()->query( "SELECT COUNT( url ) FROM tags;" ); + QString total = tracks[0]; + int final = total.toInt(); + return final; + } + + bool DcopCollectionHandler::isDirInCollection( const QString& path ) + { + return CollectionDB::instance()->isDirInCollection( path ); + } + + bool DcopCollectionHandler::moveFile( const QString &oldURL, const QString &newURL, bool overwrite ) + { + return CollectionDB::instance()->moveFile( oldURL, newURL, overwrite ); + } + + QStringList DcopCollectionHandler::query( const QString& sql ) + { + return CollectionDB::instance()->query( sql ); + } + + QStringList DcopCollectionHandler::similarArtists( int artists ) + { + return CollectionDB::instance()->similarArtists( EngineController::instance()->bundle().artist(), artists ); + } + + void DcopCollectionHandler::migrateFile( const QString &oldURL, const QString &newURL ) + { + CollectionDB::instance()->migrateFile( oldURL, newURL ); + } + + void DcopCollectionHandler::scanCollection() + { + CollectionDB::instance()->startScan(); + } + + void DcopCollectionHandler::scanCollectionChanges() + { + CollectionDB::instance()->scanModifiedDirs(); + } + + void DcopCollectionHandler::scanPause() + { + if( ScanController::instance() ) + ScanController::instance()->requestPause(); + else + debug() << "No ScanController instance available" << endl; + } + + void DcopCollectionHandler::scanUnpause() + { + if( ScanController::instance() ) + ScanController::instance()->requestUnpause(); + else + debug() << "No ScanController instance available" << endl; + } + + void DcopCollectionHandler::scannerAcknowledged() + { + DEBUG_BLOCK + if( ScanController::instance() ) + ScanController::instance()->requestAcknowledged(); + else + debug() << "No ScanController instance available" << endl; + } + + int DcopCollectionHandler::addLabels( const QString &url, const QStringList &labels ) + { + CollectionDB *db = CollectionDB::instance(); + QString uid = db->getUniqueId( url ); + int count = 0; + foreach( labels ) + { + if( db->addLabel( url, *it, uid , CollectionDB::typeUser ) ) + count++; + } + return count; + } + + void DcopCollectionHandler::removeLabels( const QString &url, const QStringList &oldLabels ) + { + CollectionDB::instance()->removeLabels( url, oldLabels, CollectionDB::typeUser ); + } + + void DcopCollectionHandler::disableAutoScoring( bool disable ) + { + CollectionDB::instance()->disableAutoScoring( disable ); + } + + int DcopCollectionHandler::deviceId( const QString &url ) + { + return MountPointManager::instance()->getIdForUrl( url ); + } + + QString DcopCollectionHandler::relativePath( const QString &url ) + { + int deviceid = deviceId( url ); + return MountPointManager::instance()->getRelativePath( deviceid, url ); + } + + QString DcopCollectionHandler::absolutePath( int deviceid, const QString &relativePath ) + { + return MountPointManager::instance()->getAbsolutePath( deviceid, relativePath ); + } + +///////////////////////////////////////////////////////////////////////////////////// +// class DcopScriptHandler +///////////////////////////////////////////////////////////////////////////////////// + + DcopScriptHandler::DcopScriptHandler() + : DCOPObject( "script" ) + , QObject( kapp ) + {} + + bool DcopScriptHandler::runScript(const QString& name) + { + return ScriptManager::instance()->runScript(name); + } + + bool DcopScriptHandler::stopScript(const QString& name) + { + return ScriptManager::instance()->stopScript(name); + } + + QStringList DcopScriptHandler::listRunningScripts() + { + return ScriptManager::instance()->listRunningScripts(); + } + + void DcopScriptHandler::addCustomMenuItem(QString submenu, QString itemTitle ) + { + Playlist::instance()->addCustomMenuItem( submenu, itemTitle ); + } + + void DcopScriptHandler::removeCustomMenuItem(QString submenu, QString itemTitle ) + { + Playlist::instance()->removeCustomMenuItem( submenu, itemTitle ); + } + + QString DcopScriptHandler::readConfig(const QString& key) + { + QString cleanKey = key; + KConfigSkeletonItem* configItem = AmarokConfig::self()->findItem(cleanKey.remove(' ')); + if (configItem) + return configItem->property().toString(); + else + return QString(); + } + + QStringList DcopScriptHandler::readListConfig(const QString& key) + { + QString cleanKey = key; + KConfigSkeletonItem* configItem = AmarokConfig::self()->findItem(cleanKey.remove(' ')); + QStringList stringList; + if(configItem) + { + QValueList variantList = configItem->property().toList(); + QValueList::Iterator it = variantList.begin(); + while(it != variantList.end()) + { + stringList << (*it).toString(); + ++it; + } + } + return stringList; + } + + QString DcopScriptHandler::proxyForUrl(const QString& url) + { + return Amarok::proxyForUrl( url ); + } + + QString DcopScriptHandler::proxyForProtocol(const QString& protocol) + { + return Amarok::proxyForProtocol( protocol ); + } + +///////////////////////////////////////////////////////////////////////////////////// +// class DcopDevicesHandler +///////////////////////////////////////////////////////////////////////////////////// + + DcopDevicesHandler::DcopDevicesHandler() + : DCOPObject( "devices" ) + , QObject( kapp ) + {} + + void DcopDevicesHandler::mediumAdded(QString name) + { + DeviceManager::instance()->mediumAdded(name); + } + + void DcopDevicesHandler::mediumRemoved(QString name) + { + DeviceManager::instance()->mediumRemoved(name); + } + + void DcopDevicesHandler::mediumChanged(QString name) + { + DeviceManager::instance()->mediumChanged(name); + } + + QStringList DcopDevicesHandler::showDeviceList() + { + return DeviceManager::instance()->getDeviceStringList(); + } + +///////////////////////////////////////////////////////////////////////////////////// +// class DcopDevicesHandler +///////////////////////////////////////////////////////////////////////////////////// + + DcopMediaBrowserHandler::DcopMediaBrowserHandler() + : DCOPObject( "mediabrowser" ) + , QObject( kapp ) + {} + + void DcopMediaBrowserHandler::deviceConnect() + { + if ( MediaBrowser::instance()->currentDevice() ) + MediaBrowser::instance()->currentDevice()->connectDevice(); + } + + void DcopMediaBrowserHandler::deviceDisconnect() + { + if ( MediaBrowser::instance()->currentDevice() ) + MediaBrowser::instance()->currentDevice()->disconnectDevice(); + } + + QStringList DcopMediaBrowserHandler::deviceList() + { + return MediaBrowser::instance()->deviceNames(); + } + + void DcopMediaBrowserHandler::deviceSwitch( QString name ) + { + MediaBrowser::instance()->deviceSwitch( name ); + } + + void DcopMediaBrowserHandler::queue( KURL url ) + { + MediaBrowser::queue()->addURL( url ); + MediaBrowser::queue()->URLsAdded(); + } + + void DcopMediaBrowserHandler::queueList( KURL::List urls ) + { + MediaBrowser::queue()->addURLs( urls ); + } + + void DcopMediaBrowserHandler::transfer() + { + if ( MediaBrowser::instance()->currentDevice() ) + MediaBrowser::instance()->currentDevice()->transferFiles(); + } + + void DcopMediaBrowserHandler::transcodingFinished( QString src, QString dest ) + { + MediaBrowser::instance()->transcodingFinished( src, dest ); + } + +} //namespace Amarok + +#include "amarokdcophandler.moc" diff --git a/amarok/src/amarokcore/amarokdcophandler.h b/amarok/src/amarokcore/amarokdcophandler.h new file mode 100644 index 00000000..98117b5f --- /dev/null +++ b/amarok/src/amarokcore/amarokdcophandler.h @@ -0,0 +1,261 @@ +/*************************************************************************** + amarokdcophandler.h - DCOP Implementation + ------------------- + begin : Sat Oct 11 2003 + copyright : (C) 2003 by Stanislav Karchebny + (C) 2005 Ian Monroe + (C) 2005 Seb Ruiz + email : berkus@users.sf.net + ***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#ifndef AMAROK_DCOP_HANDLER_H +#define AMAROK_DCOP_HANDLER_H + +#include +#include "amarokdcopiface.h" +class AmarokConfig; + +namespace Amarok +{ + +class DcopPlayerHandler : public QObject, virtual public AmarokPlayerInterface +{ + Q_OBJECT + + public: + DcopPlayerHandler(); + + public: + virtual QString version(); + virtual bool dynamicModeStatus(); + virtual bool equalizerEnabled(); + virtual bool osdEnabled(); + virtual bool isPlaying(); + virtual bool randomModeStatus(); + virtual bool repeatPlaylistStatus(); + virtual bool repeatTrackStatus(); + virtual int getVolume(); + virtual int sampleRate(); + virtual float score (); + virtual int rating (); + virtual int status(); + virtual int trackCurrentTime(); + virtual int trackCurrentTimeMs(); + virtual int trackPlayCounter(); + virtual int trackTotalTime(); + virtual QStringList labels(); + virtual QString album(); + virtual QString artist(); + virtual QString bitrate(); + virtual QString comment(); + virtual QString coverImage(); + virtual QString currentTime(); + virtual QString encodedURL(); + virtual QString engine(); + virtual QString genre(); + virtual QString lyrics(); + virtual QString lyricsByPath( QString path ); + virtual QString lastfmStation(); + virtual QString nowPlaying(); + virtual QString path(); + virtual QString setContextStyle(const QString&); + virtual QString title(); + virtual QString totalTime(); + virtual QString track(); + virtual QString type(); + virtual QString year(); + virtual void configEqualizer(); + virtual void enableOSD( bool enable ); + virtual void enableRandomMode( bool enable ); + virtual void enableRepeatPlaylist( bool enable ); + virtual void enableRepeatTrack( bool enable ); + virtual void mediaDeviceMount(); + virtual void mediaDeviceUmount(); + virtual void mute(); + virtual void next(); + virtual void pause(); + virtual void play(); + virtual void playPause(); + virtual void prev(); + virtual void queueForTransfer( KURL url ); + virtual void seek( int s ); + virtual void seekRelative( int s ); + virtual void setEqualizer(int preamp, int band60, int band170, int band310, int band600, int band1k, int band3k, int band6k, int band12k, int band14k, int band16k); + virtual void setEqualizerEnabled( bool active ); + virtual void setEqualizerPreset( QString name ); + virtual void setLyricsByPath( const QString& url, const QString& lyrics ); + virtual void setScore( float score ); + virtual void setScoreByPath( const QString &url, float score ); + virtual void setBpm( float bpm ); + virtual void setBpmByPath( const QString &url, float bpm ); + virtual void setRating( int rating ); + virtual void setRatingByPath( const QString &url, int rating ); + virtual void setVolume( int ); + virtual void setVolumeRelative( int ); + virtual void showBrowser( QString browser ); + virtual void showOSD(); + virtual void stop(); + virtual void volumeDown(); + virtual void volumeUp(); + virtual void transferDeviceFiles(); + + private: + virtual void transferCliArgs( QStringList args ); +}; + + +class DcopPlaylistHandler : public QObject, virtual public AmarokPlaylistInterface +{ + Q_OBJECT + + public: + DcopPlaylistHandler(); + + public: + virtual int getActiveIndex(); + virtual int getTotalTrackCount(); + virtual QString saveCurrentPlaylist(); + virtual void addMedia(const KURL &); + virtual void queueMedia(const KURL &); + virtual void addMediaList(const KURL::List &); + virtual void clearPlaylist(); + virtual QString currentTrackUniqueId(); + virtual void playByIndex(int); + virtual void playMedia(const KURL &); + virtual void popupMessage(const QString&); + virtual void removeCurrentTrack(); + virtual void removeByIndex(int); + virtual void repopulate(); + virtual void saveM3u(const QString& path, bool relativePaths); + virtual void setStopAfterCurrent(bool); + virtual void shortStatusMessage(const QString&); + virtual void shufflePlaylist(); + virtual void togglePlaylist(); + virtual QStringList filenames(); +}; + +class DcopPlaylistBrowserHandler : public QObject, virtual public AmarokPlaylistBrowserInterface +{ + Q_OBJECT + + public: + DcopPlaylistBrowserHandler(); + + public: + virtual void addPodcast( const QString &url ); + virtual void scanPodcasts(); + virtual void addPlaylist( const QString &url ); + virtual int loadPlaylist( const QString &playlist ); +}; + +class DcopContextBrowserHandler : public QObject, virtual public AmarokContextBrowserInterface +{ + Q_OBJECT + + public: + DcopContextBrowserHandler(); + + public: + virtual void showCurrentTrack(); + virtual void showLyrics(); + virtual void showWiki(); + virtual void showLyrics( const QCString& lyrics ); +}; + + +class DcopCollectionHandler : public QObject, virtual public AmarokCollectionInterface +{ + Q_OBJECT + + public: + DcopCollectionHandler(); + + public /* DCOP */ slots: + virtual int totalAlbums(); + virtual int totalArtists(); + virtual int totalComposers(); + virtual int totalCompilations(); + virtual int totalGenres(); + virtual int totalTracks(); + virtual bool isDirInCollection( const QString &path ); + virtual bool moveFile( const QString &oldURL, const QString &newURL, bool overwrite ); + virtual QStringList query(const QString& sql); + virtual QStringList similarArtists( int artists ); + virtual void migrateFile( const QString &oldURL, const QString &newURL ); + virtual void scanCollection(); + virtual void scanCollectionChanges(); + virtual void disableAutoScoring( bool disable ); + virtual void scanUnpause(); + virtual void scanPause(); + virtual void scannerAcknowledged(); + virtual int addLabels( const QString &url, const QStringList &labels ); + virtual void removeLabels( const QString &url, const QStringList &oldLabels ); + virtual int deviceId( const QString &url ); + virtual QString relativePath( const QString &url ); + virtual QString absolutePath( int deviceid, const QString &relativePath ); +}; + + +class DcopScriptHandler : public QObject, virtual public AmarokScriptInterface +{ + Q_OBJECT + + public: + DcopScriptHandler(); + + public /* DCOP */ slots: + virtual bool runScript(const QString&); + virtual bool stopScript(const QString&); + virtual QStringList listRunningScripts(); + virtual void addCustomMenuItem(QString submenu, QString itemTitle ); + virtual void removeCustomMenuItem(QString submenu, QString itemTitle ); + virtual QString readConfig(const QString& key); + virtual QStringList readListConfig(const QString& key); + virtual QString proxyForUrl(const QString& url); + virtual QString proxyForProtocol(const QString& protocol); +}; + +class DcopDevicesHandler : public QObject, virtual public AmarokDevicesInterface +{ + Q_OBJECT + + public: + DcopDevicesHandler(); + + public /* DCOP */ slots: + virtual void mediumAdded(QString name); + virtual void mediumRemoved(QString name); + virtual void mediumChanged(QString name); + virtual QStringList showDeviceList(); +}; + +class DcopMediaBrowserHandler : public QObject, virtual public AmarokMediaBrowserInterface +{ + Q_OBJECT + + public: + DcopMediaBrowserHandler(); + + public /* DCOP */ slots: + virtual void deviceConnect(); + virtual void deviceDisconnect(); + virtual QStringList deviceList(); + virtual void deviceSwitch( QString name ); + virtual void queue( KURL url ); + virtual void queueList( KURL::List urls ); + virtual void transfer(); + virtual void transcodingFinished( QString src, QString dest ); +}; + +} // namespace Amarok + +#endif diff --git a/amarok/src/amarokcore/amarokdcopiface.h b/amarok/src/amarokcore/amarokdcopiface.h new file mode 100644 index 00000000..2e37fa00 --- /dev/null +++ b/amarok/src/amarokcore/amarokdcopiface.h @@ -0,0 +1,247 @@ +/*************************************************************************** + amarokdcopiface.h - DCOP Interface + ------------------- + begin : Sat Oct 11 2003 + copyright : (C) 2003 by Stanislav Karchebny + (C) 2005 Ian Monroe + (C) 2005 Seb Ruiz + email : berkus@users.sf.net + ***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#ifndef AMAROK_DCOPIFACE_H +#define AMAROK_DCOPIFACE_H + +#include +#include +#include + +/////////////////////////////////////////////////////////////////////// +// WARNING! Please ask on #amarok before modifying the DCOP interface! +/////////////////////////////////////////////////////////////////////// + + +class AmarokPlayerInterface : virtual public DCOPObject +{ + K_DCOP + +k_dcop: + virtual QString version() = 0; ///< returns amarok version string + + virtual bool dynamicModeStatus() = 0; ///< Return dynamic mode status. + virtual bool equalizerEnabled() = 0; ///< Return the equalizer status. + virtual bool osdEnabled() = 0; ///< Return the OSD display status. + virtual bool isPlaying() = 0; ///< Return true if something is playing now. + virtual bool randomModeStatus() = 0; ///< Return random mode status. + virtual bool repeatPlaylistStatus() = 0; ///< Return repeat playlist status. + virtual bool repeatTrackStatus() = 0; ///< Return repeat track status. + virtual int getVolume() = 0; ///< Return volume in range 0-100%. + virtual int sampleRate() = 0; ///< Return the sample rate of the currently playing track. + virtual float score() = 0; ///< Return the score of the currently playing track. + virtual int rating() = 0; ///< Return the rating of the currently playing track. + virtual int status() = 0; ///< Return playback status: 0 - stopped, 1 - paused, 2 - playing. < 0 - error + virtual int trackCurrentTime() = 0; ///< Return current play position in seconds. + virtual int trackCurrentTimeMs() = 0; ///< Return current play position in milliseconds. + virtual int trackPlayCounter() = 0; ///< Return play counter for current song. + virtual int trackTotalTime() = 0; ///< Return track length in seconds. + virtual QStringList labels() = 0; ///< Return the labels of the currently playing track + + + /* New player API */ + virtual QString album() = 0; ///< Return the album of the currently playing track. + virtual QString artist() = 0; ///< Return the artist of the currently playing track. + virtual QString bitrate() = 0; ///< Return the bitrate of the currently playing track (XX kbps). + virtual QString comment() = 0; ///< Return the comment of the currently playing track. + virtual QString coverImage() = 0; ///< Return the encoded URL of the current track's cover image + virtual QString currentTime() = 0; ///< Return the position of the currently playing track ([h:]mm:ss format). + virtual QString encodedURL() = 0; ///< Return the encoded URL of the currently playing track. + virtual QString engine() = 0; /// * + * * + * 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. * + * * + ***************************************************************************/ + +#include "amarok.h" +#include "amarokconfig.h" +#include "crashhandler.h" + +#include //invokeMailer() +#include //kdBacktrace() +#include +#include +#include + +#include +#include +#include +#include //qVersion() + +#include //popen, fread +#include +#include //pid_t +#include //waitpid +#include +#include //write, getpid + + + +#ifndef TAGLIB_PATCH_VERSION +// seems to be wheel's style +#define TAGLIB_PATCH_VERSION 0 +#endif + + +namespace Amarok +{ + #if 0 + class CrashHandlerWidget : public KDialog { + public: + CrashHandlerWidget(); + }; + #endif + + static QString + runCommand( const QCString &command ) + { + static const uint SIZE = 40960; //40 KiB + static char stdoutBuf[ SIZE ] = {0}; + + std::cout << "Running: " << command << std::endl; + + FILE *process = ::popen( command, "r" ); + if ( process ) + { + stdoutBuf[ std::fread( static_cast( stdoutBuf ), sizeof(char), SIZE-1, process ) ] = '\0'; + ::pclose( process ); + } + return QString::fromLocal8Bit( stdoutBuf ); + } + + void + Crash::crashHandler( int /*signal*/ ) + { + // we need to fork to be able to get a + // semi-decent bt - I dunno why + const pid_t pid = ::fork(); + + if( pid < 0 ) + { + std::cout << "forking crash reporter failed\n"; + // continuing now can't do no good + _exit( 1 ); + } + else if ( pid == 0 ) + { + // we are the child process (the result of the fork) + std::cout << "Amarok is crashing...\n"; + + QString subject = APP_VERSION " "; + QString body = i18n( + "Amarok has crashed! We are terribly sorry about this :(\n\n" + "But, all is not lost! You could potentially help us fix the crash. " + "Information describing the crash is below, so just click send, " + "or if you have time, write a brief description of how the crash happened first.\n\n" + "Many thanks.\n\n" ); + body += i18n( "\n\n\n\n\n\n" + "The information below is to help the developers identify the problem, " + "please do not modify it.\n\n\n\n" ); + + + body += "======== DEBUG INFORMATION =======\n" + "Version: " APP_VERSION "\n" + "Engine: %1\n" + "Build date: " __DATE__ "\n" + "CC version: " __VERSION__ "\n" //assuming we're using GCC + "KDElibs: " KDE_VERSION_STRING "\n" + "Qt: %2\n" + "TagLib: %3.%4.%5\n" + "CPU count: %6\n"; + + QString cpucount = "unknown"; +#ifdef __linux__ + QString line; + uint cpuCount = 0; + QFile cpuinfo( "/proc/cpuinfo" ); + if ( cpuinfo.open( IO_ReadOnly ) ) { + while ( cpuinfo.readLine( line, 20000 ) != -1 ) { + if ( line.startsWith( "processor" ) ) { + ++cpuCount; + } + } + } + cpucount = QString::number( cpuCount ); +#endif + + + body = body.arg( AmarokConfig::soundSystem() ) + .arg( qVersion() ) + .arg( TAGLIB_MAJOR_VERSION ) + .arg( TAGLIB_MINOR_VERSION ) + .arg( TAGLIB_PATCH_VERSION ) + .arg( cpucount ); + + #ifdef NDEBUG + body += "NDEBUG: true"; + #endif + body += '\n'; + + /// obtain the backtrace with gdb + + KTempFile temp; + temp.setAutoDelete( true ); + + const int handle = temp.handle(); + +// QCString gdb_command_string = +// "file amarokapp\n" +// "attach " + QCString().setNum( ::getppid() ) + "\n" +// "bt\n" "echo \\n\n" +// "thread apply all bt\n"; + + const QCString gdb_batch = + "bt\n" + "echo \\n\\n\n" + "bt full\n" + "echo \\n\\n\n" + "echo ==== (gdb) thread apply all bt ====\\n\n" + "thread apply all bt\n"; + + ::write( handle, gdb_batch, gdb_batch.length() ); + ::fsync( handle ); + + // so we can read stderr too + ::dup2( fileno( stdout ), fileno( stderr ) ); + + + QCString gdb; + gdb = "gdb --nw -n --batch -x "; + gdb += temp.name().latin1(); + gdb += " amarokapp "; + gdb += QCString().setNum( ::getppid() ); + + QString bt = runCommand( gdb ); + + /// clean up + bt.remove( "(no debugging symbols found)..." ); + bt.remove( "(no debugging symbols found)\n" ); + bt.replace( QRegExp("\n{2,}"), "\n" ); //clean up multiple \n characters + bt.stripWhiteSpace(); + + /// analyze usefulness + bool useful = true; + const QString fileCommandOutput = runCommand( "file `which amarokapp`" ); + + if( fileCommandOutput.find( "not stripped", false ) == -1 ) + subject += "[___stripped]"; //same length as below + else + subject += "[NOTstripped]"; + + if( !bt.isEmpty() ) { + const int invalidFrames = bt.contains( QRegExp("\n#[0-9]+\\s+0x[0-9A-Fa-f]+ in \\?\\?") ); + const int validFrames = bt.contains( QRegExp("\n#[0-9]+\\s+0x[0-9A-Fa-f]+ in [^?]") ); + const int totalFrames = invalidFrames + validFrames; + + if( totalFrames > 0 ) { + const double validity = double(validFrames) / totalFrames; + subject += QString("[validity: %1]").arg( validity, 0, 'f', 2 ); + if( validity <= 0.5 ) useful = false; + } + subject += QString("[frames: %1]").arg( totalFrames, 3 /*padding*/ ); + + if( bt.find( QRegExp(" at \\w*\\.cpp:\\d+\n") ) >= 0 ) + subject += "[line numbers]"; + } + else + useful = false; + + subject += QString("[%1]").arg( AmarokConfig::soundSystem().remove( QRegExp("-?engine") ) ); + + std::cout << subject.latin1() << std::endl; + + + //TODO -fomit-frame-pointer buggers up the backtrace, so detect it + //TODO -O optimization can rearrange execution and stuff so show a warning for the developer + //TODO pass the CXXFLAGS used with the email + + if( useful ) { + body += "==== file `which amarokapp` =======\n"; + body += fileCommandOutput + "\n\n"; + body += "==== (gdb) bt =====================\n"; + body += bt + "\n\n"; + body += "==== kdBacktrace() ================\n"; + body += kdBacktrace(); + + //TODO startup notification + kapp->invokeMailer( + /*to*/ "amarok-backtraces@lists.sf.net", + /*cc*/ QString(), + /*bcc*/ QString(), + /*subject*/ subject, + /*body*/ body, + /*messageFile*/ QString(), + /*attachURLs*/ QStringList(), + /*startup_id*/ "" ); + } + else { + std::cout << i18n( "\nAmarok has crashed! We are terribly sorry about this :(\n\n" + "But, all is not lost! Perhaps an upgrade is already available " + "which fixes the problem. Please check your distribution's software repository.\n" ).local8Bit(); + } + + //_exit() exits immediately, otherwise this + //function is called repeatedly ad finitum + ::_exit( 255 ); + } + + else { + // we are the process that crashed + + ::alarm( 0 ); + + // wait for child to exit + ::waitpid( pid, NULL, 0 ); + ::_exit( 253 ); + } + } +} + + +#if 0 + +#include +#include +#include +#include +#include +#include +#include + +Amarok::CrashHandlerWidget::CrashHandlerWidget() +{ + QBoxLayout *layout = new QHBoxLayout( this, 18, 12 ); + + { + QBoxLayout *lay = new QVBoxLayout( layout ); + QLabel *label = new QLabel( this ); + label->setPixmap( locate( "data", "drkonqi/pics/konqi.png" ) ); + label->setFrameStyle( QFrame::Plain | QFrame::Box ); + lay->add( label ); + lay->addItem( new QSpacerItem( 3, 3, QSizePolicy::Minimum, QSizePolicy::Expanding ) ); + } + + layout = new QVBoxLayout( layout, 6 ); + + layout->add( new QLabel( /*i18n*/( + "

" "Amarok has crashed! We are terribly sorry about this :(" + "

" "However you now have an opportunity to help us fix this crash so that it doesn't " + "happen again! Click Send Email and Amarok will prepare an email that you " + "can send to us that contains information about the crash, and we'll try to fix it " + "as soon as possible." + "

" "Thanks for choosing Amarok.
" ), this ) ); + + layout = new QHBoxLayout( layout, 6 ); + + layout->addItem( new QSpacerItem( 6, 6, QSizePolicy::Expanding ) ); + layout->add( new KPushButton( KGuiItem( i18n("Send Email"), "mail_send" ), this, "email" ) ); + layout->add( new KPushButton( KStdGuiItem::close(), this, "close" ) ); + + static_cast(child("email"))->setDefault( true ); + + connect( child( "email" ), SIGNAL(clicked()), SLOT(accept()) ); + connect( child( "close" ), SIGNAL(clicked()), SLOT(reject()) ); + + setCaption( i18n("Crash Handler") ); + setFixedSize( sizeHint() ); +} +#endif diff --git a/amarok/src/amarokcore/crashhandler.h b/amarok/src/amarokcore/crashhandler.h new file mode 100644 index 00000000..f196ce08 --- /dev/null +++ b/amarok/src/amarokcore/crashhandler.h @@ -0,0 +1,32 @@ +/*************************************************************************** + * Copyright (C) 2005 Max Howell * + * * + * 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. * + * * + ***************************************************************************/ + +#ifndef CRASH_H +#define CRASH_H + +#include //for main.cpp + +namespace Amarok +{ + /** + * @author Max Howell + * @short The Amarok crash-handler + * + * I'm not entirely sure why this had to be inside a class, but it + * wouldn't work otherwise *shrug* + */ + class Crash + { + public: + static void crashHandler( int signal ); + }; +} + +#endif diff --git a/amarok/src/amarokitpc.protocol b/amarok/src/amarokitpc.protocol new file mode 100644 index 00000000..6ea5041a --- /dev/null +++ b/amarok/src/amarokitpc.protocol @@ -0,0 +1,11 @@ +[Protocol] +exec=amarok "%u" +protocol=itpc +input=none +output=none +helper=true +listing= +reading=false +writing=false +makedir=false +deleting=false diff --git a/amarok/src/amaroklastfm.protocol b/amarok/src/amaroklastfm.protocol new file mode 100644 index 00000000..1e6d65dc --- /dev/null +++ b/amarok/src/amaroklastfm.protocol @@ -0,0 +1,11 @@ +[Protocol] +exec=amarok "%u" +protocol=lastfm +input=none +output=none +helper=true +listing= +reading=false +writing=false +makedir=false +deleting=false diff --git a/amarok/src/amarokpcast.protocol b/amarok/src/amarokpcast.protocol new file mode 100644 index 00000000..46666d5f --- /dev/null +++ b/amarok/src/amarokpcast.protocol @@ -0,0 +1,11 @@ +[Protocol] +exec=amarok "%u" +protocol=pcast +input=none +output=none +helper=true +listing= +reading=false +writing=false +makedir=false +deleting=false diff --git a/amarok/src/amarokrc b/amarok/src/amarokrc new file mode 100644 index 00000000..1f0f49f1 --- /dev/null +++ b/amarok/src/amarokrc @@ -0,0 +1,14 @@ +[General] +XMLFile=amarokui.rc + +[BrowserBar] +CurrentPane=ContextBrowser + +[PlaylistColumnsLayout] +#see PlaylistWidget::setColumnWidth() for explanation for below +ColumnWidths=0,200,100,100,0,0,0,0,0,80,0 + +[KNewStuff] +TargetDir=amarok/themes +Uncompress=application/x-gzip +ProvidersUrl=http://amarok.kde.org/knewstuff/amarokthemes-providers.xml diff --git a/amarok/src/amarokui.rc b/amarok/src/amarokui.rc new file mode 100644 index 00000000..da29dced --- /dev/null +++ b/amarok/src/amarokui.rc @@ -0,0 +1,20 @@ + + +Playlist Toolbar + + + + + + + + + + + + + + + + + diff --git a/amarok/src/amarokui_xmms.rc b/amarok/src/amarokui_xmms.rc new file mode 100644 index 00000000..18105ed3 --- /dev/null +++ b/amarok/src/amarokui_xmms.rc @@ -0,0 +1,13 @@ + + +Playlist Toolbar + + + + + + + + + + diff --git a/amarok/src/analyzers/Makefile.am b/amarok/src/analyzers/Makefile.am new file mode 100644 index 00000000..232fd1c4 --- /dev/null +++ b/amarok/src/analyzers/Makefile.am @@ -0,0 +1,30 @@ +noinst_LTLIBRARIES = \ + libanalyzers.la + +METASOURCES = \ + AUTO + +INCLUDES = \ + -I$(top_builddir)/amarok/src/amarokcore \ + -I$(top_builddir)/amarok/src \ + -I$(top_srcdir)/amarok/src/amarokcore \ + -I$(top_srcdir)/amarok/src/engine \ + -I$(top_srcdir)/amarok/src/plugin \ + -I$(top_srcdir)/amarok/src \ + $(all_includes) + +libanalyzers_la_SOURCES = \ + analyzerbase.cpp \ + analyzerfactory.cpp \ + baranalyzer.cpp \ + blockanalyzer.cpp \ + glanalyzer.cpp \ + glanalyzer2.cpp \ + glanalyzer3.cpp \ + sonogram.cpp \ + turbine.cpp \ + boomanalyzer.cpp + + + + diff --git a/amarok/src/analyzers/analyzerbase.cpp b/amarok/src/analyzers/analyzerbase.cpp new file mode 100644 index 00000000..04e19e26 --- /dev/null +++ b/amarok/src/analyzers/analyzerbase.cpp @@ -0,0 +1,279 @@ +/*************************************************************************** + viswidget.cpp - description + ------------------- + begin : Die Jan 7 2003 + copyright : (C) 2003 by Max Howell + email : markey@web.de + ***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#include "analyzerbase.h" +#include //interpolate() +#include "enginecontroller.h" +#include //event() + + +// INSTRUCTIONS Base2D +// 1. do anything that depends on height() in init(), Base2D will call it before you are shown +// 2. otherwise you can use the constructor to initialise things +// 3. reimplement analyze(), and paint to canvas(), Base2D will update the widget when you return control to it +// 4. if you want to manipulate the scope, reimplement transform() +// 5. for convenience are pre-included +// TODO make an INSTRUCTIONS file +//can't mod scope in analyze you have to use transform + + +//TODO for 2D use setErasePixmap Qt function insetead of m_background + +// make the linker happy only for gcc < 4.0 +#if !( __GNUC__ > 4 || ( __GNUC__ == 4 && __GNUC_MINOR__ >= 0 ) ) +template class Analyzer::Base; +#endif + + +template +Analyzer::Base::Base( QWidget *parent, uint timeout, uint scopeSize ) + : W( parent ) + , m_timeout( timeout ) + , m_fht( new FHT(scopeSize) ) +{} + +template bool +Analyzer::Base::event( QEvent *e ) +{ + switch( e->type() ) { +/* case QEvent::Paint: + if( !canvas()->isNull() ) + bitBlt( this, 0, 0, canvas() ); + return true; //no propagate event*/ + case QEvent::Hide: + m_timer.stop(); + break; + + case QEvent::Show: + m_timer.start( timeout() ); + break; + + default: + break; + } + + return QWidget::event( e ); +} + +template void +Analyzer::Base::transform( Scope &scope ) //virtual +{ + //this is a standard transformation that should give + //an FFT scope that has bands for pretty analyzers + + //NOTE resizing here is redundant as FHT routines only calculate FHT::size() values + //scope.resize( m_fht->size() ); + + float *front = static_cast( &scope.front() ); + + float* f = new float[ m_fht->size() ]; + m_fht->copy( &f[0], front ); + m_fht->logSpectrum( front, &f[0] ); + m_fht->scale( front, 1.0 / 20 ); + + scope.resize( m_fht->size() / 2 ); //second half of values are rubbish + delete [] f; +} + +template void +Analyzer::Base::drawFrame() +{ + EngineBase *engine = EngineController::engine(); + + switch( engine->state() ) + { + case Engine::Playing: + { + const Engine::Scope &thescope = engine->scope(); + static Analyzer::Scope scope( 512 ); + int i = 0; + + // convert to mono here - our built in analyzers need mono, but we the engines provide interleaved pcm + for( uint x = 0; (int)x < m_fht->size(); ++x ) + { + scope[x] = double(thescope[i] + thescope[i+1]) / (2*(1<<15)); + i += 2; + } + + transform( scope ); + analyze( scope ); + + scope.resize( m_fht->size() ); + + break; + } + case Engine::Paused: + paused(); + break; + + default: + demo(); + } +} + +template int +Analyzer::Base::resizeExponent( int exp ) +{ + if ( exp < 3 ) + exp = 3; + else if ( exp > 9 ) + exp = 9; + + if ( exp != m_fht->sizeExp() ) { + delete m_fht; + m_fht = new FHT( exp ); + } + return exp; +} + +template int +Analyzer::Base::resizeForBands( int bands ) +{ + int exp; + if ( bands <= 8 ) + exp = 4; + else if ( bands <= 16 ) + exp = 5; + else if ( bands <= 32 ) + exp = 6; + else if ( bands <= 64 ) + exp = 7; + else if ( bands <= 128 ) + exp = 8; + else + exp = 9; + + resizeExponent( exp ); + return m_fht->size() / 2; +} + +template void +Analyzer::Base::paused() //virtual +{} + +template void +Analyzer::Base::demo() //virtual +{ + static int t = 201; //FIXME make static to namespace perhaps + + if( t > 999 ) t = 1; //0 = wasted calculations + if( t < 201 ) + { + Scope s( 32 ); + + const double dt = double(t) / 200; + for( uint i = 0; i < s.size(); ++i ) + s[i] = dt * (sin( M_PI + (i * M_PI) / s.size() ) + 1.0); + + analyze( s ); + } + else analyze( Scope( 32, 0 ) ); + + ++t; +} + + + +Analyzer::Base2D::Base2D( QWidget *parent, uint timeout, uint scopeSize ) + : Base( parent, timeout, scopeSize ) +{ + setWFlags( Qt::WNoAutoErase ); //no flicker + + connect( &m_timer, SIGNAL( timeout() ), SLOT( draw() ) ); +} + +void +Analyzer::Base2D::polish() +{ + //TODO is there much point in this anymore? + + //we use polish for initialzing (instead of ctor) + //because we need to know the widget's final size + QWidget::polish(); + + init(); //virtual +} + +void +Analyzer::Base2D::resizeEvent( QResizeEvent *e ) +{ + m_background.resize( size() ); + m_canvas.resize( size() ); + m_background.fill( backgroundColor() ); + eraseCanvas(); //this is necessary + + QWidget::resizeEvent( e ); +} + +void +Analyzer::Base2D::paletteChange( const QPalette& ) +{ + m_background.fill( backgroundColor() ); + eraseCanvas(); +} + + + +#ifdef HAVE_QGLWIDGET +Analyzer::Base3D::Base3D( QWidget *parent, uint timeout, uint scopeSize ) + : Base( parent, timeout, scopeSize ) +{ + connect( &m_timer, SIGNAL( timeout() ), SLOT( draw() ) ); +} +#endif + + +void +Analyzer::interpolate( const Scope &inVec, Scope &outVec ) //static +{ + double pos = 0.0; + const double step = (double)inVec.size() / outVec.size(); + + for ( uint i = 0; i < outVec.size(); ++i, pos += step ) + { + const double error = pos - std::floor( pos ); + const unsigned long offset = (unsigned long)pos; + + unsigned long indexLeft = offset + 0; + + if ( indexLeft >= inVec.size() ) + indexLeft = inVec.size() - 1; + + unsigned long indexRight = offset + 1; + + if ( indexRight >= inVec.size() ) + indexRight = inVec.size() - 1; + + outVec[i] = inVec[indexLeft ] * ( 1.0 - error ) + + inVec[indexRight] * error; + } +} + +void +Analyzer::initSin( Scope &v, const uint size ) //static +{ + double step = ( M_PI * 2 ) / size; + double radian = 0; + + for ( uint i = 0; i < size; i++ ) + { + v.push_back( sin( radian ) ); + radian += step; + } +} + +#include "analyzerbase.moc" diff --git a/amarok/src/analyzers/analyzerbase.h b/amarok/src/analyzers/analyzerbase.h new file mode 100644 index 00000000..ba3ecb30 --- /dev/null +++ b/amarok/src/analyzers/analyzerbase.h @@ -0,0 +1,153 @@ +// Maintainer: Max Howell , (C) 2004 +// Copyright: See COPYING file that comes with this distribution + +#ifndef ANALYZERBASE_H +#define ANALYZERBASE_H + + +#include //HAVE_QGLWIDGET + +#ifdef __FreeBSD__ +#include +#endif + +#include "fht.h" //stack allocated and convenience +#include //stack allocated and convenience +#include //stack allocated +#include //baseclass +#include //included for convenience + +#ifdef HAVE_QGLWIDGET +#include //baseclass +#ifdef Q_WS_MACX +#include //included for convenience +#include //included for convenience +#else +#include //included for convenience +#include //included for convenience +#endif +#else +//this is a workaround for compile problems due to moc +#define QGLWidget QWidget +#endif + +class QEvent; +class QPaintEvent; +class QResizeEvent; + + +namespace Analyzer { + +typedef std::vector Scope; + +template class Base : public W +{ +public: + uint timeout() const { return m_timeout; } + +protected: + Base( QWidget*, uint, uint = 7 ); + ~Base() { delete m_fht; } + + void drawFrame(); + int resizeExponent( int ); + int resizeForBands( int ); + virtual void transform( Scope& ); + virtual void analyze( const Scope& ) = 0; + virtual void paused(); + virtual void demo(); + + void changeTimeout( uint newTimeout ) + { + m_timer.changeInterval( newTimeout ); + m_timeout = newTimeout; + } + +private: + bool event( QEvent* ); + +protected: + QTimer m_timer; + uint m_timeout; + FHT *m_fht; +}; + + +class Base2D : public Base +{ +Q_OBJECT +public: + const QPixmap *background() const { return &m_background; } + const QPixmap *canvas() const { return &m_canvas; } + +private slots: + void draw() { drawFrame(); bitBlt( this, 0, 0, canvas() ); } + +protected: + Base2D( QWidget*, uint timeout, uint scopeSize = 7 ); + + virtual void init() {} + + QPixmap *background() { return &m_background; } + QPixmap *canvas() { return &m_canvas; } + void eraseCanvas() { bitBlt( canvas(), 0, 0, background() ); } + + void paintEvent( QPaintEvent* ) { if( !m_canvas.isNull() ) bitBlt( this, 0, 0, canvas() ); } + void resizeEvent( QResizeEvent* ); + void paletteChange( const class QPalette& ); + + void polish(); + +private: + QPixmap m_background; + QPixmap m_canvas; +}; + + + +//This mess is because moc generates an entry for this class despite the #if block +//1. the Q_OBJECT macro must be exposed +//2. we have to define the class +//3. we have to declare a ctor (to satisfy the inheritance) +//4. the slot must also by visible (!) +//TODO find out how to stop moc generating a metaobject for this class +class Base3D : public Base +{ +Q_OBJECT +#ifdef HAVE_QGLWIDGET +protected: + Base3D( QWidget*, uint, uint = 7 ); +private slots: + void draw() { drawFrame(); } +#else +protected: + Base3D( QWidget *w, uint i1, uint i2 ) : Base( w, i1, i2 ) {} +private slots: + void draw() {} +#endif +}; + + +class Factory +{ + //Currently this is a rather small class, its only purpose + //to ensure that making changes to analyzers will not require + //rebuilding the world! + + //eventually it would be better to make analyzers pluggable + //but I can't be arsed, nor can I see much reason to do so + //yet! +public: + static QWidget* createAnalyzer( QWidget* ); + static QWidget* createPlaylistAnalyzer( QWidget *); +}; + + +void interpolate( const Scope&, Scope& ); +void initSin( Scope&, const uint = 6000 ); + +} //END namespace Analyzer + +using Analyzer::Scope; + +#endif diff --git a/amarok/src/analyzers/analyzerfactory.cpp b/amarok/src/analyzers/analyzerfactory.cpp new file mode 100644 index 00000000..91c28b01 --- /dev/null +++ b/amarok/src/analyzers/analyzerfactory.cpp @@ -0,0 +1,127 @@ +/*************************************************************************** + analyzerfactory.cpp - description + ------------------- + begin : Fre Nov 15 2002 + copyright : (C) 2002 by Mark Kretschmann + email : markey@web.de +***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#include //for HAVE_QGLWIDGET macro + +#include "amarokcore/amarokconfig.h" +#include "analyzerbase.h" //declaration here + +#include "baranalyzer.h" +#include "boomanalyzer.h" +#include "sonogram.h" +#include "turbine.h" +#include "blockanalyzer.h" + +#ifdef HAVE_QGLWIDGET +#include "glanalyzer.h" +#include "glanalyzer2.h" +#include "glanalyzer3.h" +#endif + +#include +#include + +//separate from analyzerbase.cpp to save compile time + + +QWidget *Analyzer::Factory::createAnalyzer( QWidget *parent ) +{ + //new XmmsWrapper(); //toplevel + + QWidget *analyzer = 0; + + switch( AmarokConfig::currentAnalyzer() ) + { + case 2: + analyzer = new Sonogram( parent ); + break; + case 1: + analyzer = new TurbineAnalyzer( parent ); + break; + case 3: + analyzer = new BarAnalyzer( parent ); + break; + case 4: + analyzer = new BlockAnalyzer( parent ); + break; +#ifdef HAVE_QGLWIDGET + case 5: + analyzer = new GLAnalyzer( parent ); + break; + case 6: + analyzer = new GLAnalyzer2( parent ); + break; + case 7: + analyzer = new GLAnalyzer3( parent ); + break; + case 8: +#else + case 5: +#endif + analyzer = new QLabel( i18n( "Click for Analyzers" ), parent ); //blank analyzer to satisfy Grue + static_cast(analyzer)->setAlignment( Qt::AlignCenter ); + break; + + default: + AmarokConfig::setCurrentAnalyzer( 0 ); + case 0: + analyzer = new BoomAnalyzer( parent ); + } + + return analyzer; +} + +QWidget *Analyzer::Factory::createPlaylistAnalyzer( QWidget *parent) +{ + QWidget *analyzer = 0; + switch( AmarokConfig::currentPlaylistAnalyzer() ) + { + case 1: + analyzer = new TurbineAnalyzer( parent ); + break; + case 2: + analyzer = new Sonogram( parent ); + break; + case 3: + analyzer = new BoomAnalyzer( parent ); + break; + #ifdef HAVE_QGLWIDGET + case 4: + analyzer = new GLAnalyzer( parent ); + break; + case 5: + analyzer = new GLAnalyzer2( parent ); + break; + case 6: + analyzer = new GLAnalyzer3( parent ); + break; + case 7: + #else + case 4: + #endif + analyzer = new QLabel( i18n( "Click for Analyzers" ), parent ); //blank analyzer to satisfy Grue + static_cast(analyzer)->setAlignment( Qt::AlignCenter ); + break; + + default: + AmarokConfig::setCurrentPlaylistAnalyzer( 0 ); + case 0: + analyzer = new BlockAnalyzer( parent ); + break; + } + return analyzer; +} diff --git a/amarok/src/analyzers/baranalyzer.cpp b/amarok/src/analyzers/baranalyzer.cpp new file mode 100644 index 00000000..61af801b --- /dev/null +++ b/amarok/src/analyzers/baranalyzer.cpp @@ -0,0 +1,171 @@ +// +// +// C++ Implementation: $MODULE$ +// +// Description: +// +// +// Author: Mark Kretschmann , (C) 2003 +// +// Copyright: See COPYING file that comes with this distribution +// +// + +#include "baranalyzer.h" +#include //log10(), etc. +#include "debug.h" +#include + + +BarAnalyzer::BarAnalyzer( QWidget *parent ) + : Analyzer::Base2D( parent, 12, 8 ) + //, m_bands( BAND_COUNT ) + //, barVector( BAND_COUNT, 0 ) + //, roofVector( BAND_COUNT, 50 ) + //, roofVelocityVector( BAND_COUNT, ROOF_VELOCITY_REDUCTION_FACTOR ) +{ + //roof pixmaps don't depend on size() so we do in the ctor + m_bg = parent->paletteBackgroundColor(); + + QColor fg( 0xff, 0x50, 0x70 ); + #define m_bg backgroundColor() + + double dr = double(m_bg.red() - fg.red()) / (NUM_ROOFS-1); //-1 because we start loop below at 0 + double dg = double(m_bg.green() - fg.green()) / (NUM_ROOFS-1); + double db = double(m_bg.blue() - fg.blue()) / (NUM_ROOFS-1); + + for ( uint i = 0; i < NUM_ROOFS; ++i ) + { + m_pixRoof[i].resize( COLUMN_WIDTH, 1 ); + m_pixRoof[i].fill( QColor( fg.red()+int(dr*i), fg.green()+int(dg*i), fg.blue()+int(db*i) ) ); + } + + #undef m_bg +} + +void BarAnalyzer::resizeEvent( QResizeEvent * e ) +{ + debug() << "Baranalyzer Resized(" << width() << "x" << height() << ")" << endl; + Analyzer::Base2D::resizeEvent( e ); + init(); +} + +// METHODS ===================================================== + +void BarAnalyzer::init() +{ + const double MAX_AMPLITUDE = 1.0; + const double F = double(height() - 2) / (log10( 255 ) * MAX_AMPLITUDE ); + + setPaletteBackgroundColor(m_bg); + + BAND_COUNT = width() / 5; + MAX_DOWN = int(0 -((height() / 50))); + MAX_UP = int((height() / 25)); + + debug() << "BAND_COUNT = " << BAND_COUNT << " MAX_UP = " << MAX_UP << "MAX_DOWN = " << MAX_DOWN << endl; + + barVector.resize( BAND_COUNT, 0 ); + roofVector.resize( BAND_COUNT, height() -5 ); + roofVelocityVector.resize( BAND_COUNT, ROOF_VELOCITY_REDUCTION_FACTOR ); + m_roofMem.resize(BAND_COUNT); + m_scope.resize(BAND_COUNT); + + //generate a list of values that express amplitudes in range 0-MAX_AMP as ints from 0-height() on log scale + for ( uint x = 0; x < 256; ++x ) + { + m_lvlMapper[x] = uint( F * log10( x+1 ) ); + } + + m_pixBarGradient.resize( height()*COLUMN_WIDTH, height() ); + m_pixCompose.resize( size() ); + + QPainter p( &m_pixBarGradient ); + for ( int x=0, r=0x40, g=0x30, b=0xff, r2=255-r; + x < height(); ++x ) + { + for ( int y = x; y > 0; --y ) + { + const double fraction = (double)y / height(); + +// p.setPen( QColor( r + (int)(r2 * fraction), g, b - (int)(255 * fraction) ) ); + p.setPen( QColor( r + (int)(r2 * fraction), g, b ) ); + p.drawLine( x*COLUMN_WIDTH, height() - y, (x+1)*COLUMN_WIDTH, height() - y ); + } + } + + + setMinimumSize( QSize( BAND_COUNT * COLUMN_WIDTH, 10 ) ); +} + + +void BarAnalyzer::analyze( const Scope &s ) +{ + //start with a blank canvas + eraseCanvas(); + + //Analyzer::interpolate( s, m_bands ); + + Scope &v = m_scope; + Analyzer::interpolate( s, v ); + + for ( uint i = 0, x = 0, y2; i < v.size(); ++i, x+=COLUMN_WIDTH+1 ) + { + //assign pre[log10]'d value + y2 = uint(v[i] * 256); //256 will be optimised to a bitshift //no, it's a float + y2 = m_lvlMapper[ (y2 > 255) ? 255 : y2 ]; //lvlMapper is array of ints with values 0 to height() + + int change = y2 - barVector[i]; + + //using the best of Markey's, piggz and Max's ideas on the way to shift the bars + //we have the following: + // 1. don't adjust shift when doing small up movements + // 2. shift large upwards with a bias towards last value + // 3. fall downwards at a constant pace + + /*if ( change > MAX_UP ) //anything too much greater than 2 gives "jitter" + //add some dynamics - makes the value slightly closer to what it was last time + y2 = ( barVector[i] + MAX_UP ); + //y2 = ( barVector[i] * 2 + y2 ) / 3; + else*/ if ( change < MAX_DOWN ) + y2 = barVector[i] + MAX_DOWN; + + + if ( (int)y2 > roofVector[i] ) + { + roofVector[i] = (int)y2; + roofVelocityVector[i] = 1; + } + + //remember where we are + barVector[i] = y2; + + if ( m_roofMem[i].size() > NUM_ROOFS ) + m_roofMem[i].erase( m_roofMem[i].begin() ); + + //blt last n roofs, a.k.a motion blur + for ( uint c = 0; c < m_roofMem[i].size(); ++c ) + //bitBlt( m_pComposePixmap, x, m_roofMem[i]->at( c ), m_roofPixmaps[ c ] ); + bitBlt( canvas(), x, m_roofMem[i][c], &m_pixRoof[ NUM_ROOFS - 1 - c ] ); + + //blt the bar + bitBlt( canvas(), x, height() - y2, + gradient(), y2 * COLUMN_WIDTH, height() - y2, COLUMN_WIDTH, y2, Qt::CopyROP ); + + m_roofMem[i].push_back( height() - roofVector[i] - 2 ); + + //set roof parameters for the NEXT draw + if ( roofVelocityVector[i] != 0 ) + { + if ( roofVelocityVector[i] > 32 ) //no reason to do == 32 + roofVector[i] -= (roofVelocityVector[i] - 32) / 20; //trivial calculation + + if ( roofVector[i] < 0 ) + { + roofVector[i] = 0; //not strictly necessary + roofVelocityVector[i] = 0; + } + else ++roofVelocityVector[i]; + } + } +} diff --git a/amarok/src/analyzers/baranalyzer.h b/amarok/src/analyzers/baranalyzer.h new file mode 100644 index 00000000..f8869d5d --- /dev/null +++ b/amarok/src/analyzers/baranalyzer.h @@ -0,0 +1,57 @@ +// Maintainer: Max Howell +// Authors: Mark Kretschmann & Max Howell (C) 2003-4 +// Copyright: See COPYING file that comes with this distribution +// + +#ifndef BARANALYZER_H +#define BARANALYZER_H + +#include "analyzerbase.h" + +typedef std::vector aroofMemVec; + + +class BarAnalyzer : public Analyzer::Base2D +{ + public: + BarAnalyzer( QWidget* ); + + void init(); + virtual void analyze( const Scope& ); + //virtual void transform( Scope& ); + + /** + * Resizes the widget to a new geometry according to @p e + * @param e The resize-event + */ + void resizeEvent( QResizeEvent * e); + + uint BAND_COUNT; + int MAX_DOWN; + int MAX_UP; + static const uint ROOF_HOLD_TIME = 48; + static const int ROOF_VELOCITY_REDUCTION_FACTOR = 32; + static const uint NUM_ROOFS = 16; + static const uint COLUMN_WIDTH = 4; + + protected: + QPixmap m_pixRoof[NUM_ROOFS]; + //vector m_roofMem[BAND_COUNT]; + + //Scope m_bands; //copy of the Scope to prevent creating/destroying a Scope every iteration + uint m_lvlMapper[256]; + std::vector m_roofMem; + std::vector barVector; //positions of bars + std::vector roofVector; //positions of roofs + std::vector roofVelocityVector; //speed that roofs falls + + const QPixmap *gradient() const { return &m_pixBarGradient; } + + private: + QPixmap m_pixBarGradient; + QPixmap m_pixCompose; + Scope m_scope; //so we don't create a vector every frame + QColor m_bg; +}; + +#endif diff --git a/amarok/src/analyzers/blockanalyzer.cpp b/amarok/src/analyzers/blockanalyzer.cpp new file mode 100644 index 00000000..f58bc832 --- /dev/null +++ b/amarok/src/analyzers/blockanalyzer.cpp @@ -0,0 +1,451 @@ +// Author: Max Howell , (C) 2003-5 +// Mark Kretschmann , (C) 2005 +// Copyright: See COPYING file that comes with this distribution +// + +#define DEBUG_PREFIX "BlockAnalyzer" + +#include "config.h" //HAVE_LIBVISUAL definition + +#include "actionclasses.h" //mousePressEvent +#include "amarok.h" +#include "blockanalyzer.h" + +#include + +#include +#include //paletteChange() +#include //mousePressEvent +#include //mousePressEvent +#include //mousePressEvent + +#include //mousePressEvent +#include //paletteChange() +#include + + +static inline uint myMax( uint v1, uint v2 ) { return v1 > v2 ? v1 : v2; } + +namespace Amarok { extern KConfig *config( const QString& ); } + + +BlockAnalyzer::BlockAnalyzer( QWidget *parent ) + : Analyzer::Base2D( parent, 20, 9 ) + , m_columns( 0 ) //uint + , m_rows( 0 ) //uint + , m_y( 0 ) //uint + , m_barPixmap( 1, 1 ) //null qpixmaps cause crashes + , m_topBarPixmap( WIDTH, HEIGHT ) + , m_scope( MIN_COLUMNS ) //Scope + , m_store( 1 << 8, 0 ) //vector + , m_fade_bars( FADE_SIZE ) //vector + , m_fade_pos( 1 << 8, 50 ) //vector + , m_fade_intensity( 1 << 8, 32 ) //vector +{ + changeTimeout( Amarok::config( "General" )->readNumEntry( "Timeout", 20 ) ); + + setMinimumSize( MIN_COLUMNS*(WIDTH+1) -1, MIN_ROWS*(HEIGHT+1) -1 ); //-1 is padding, no drawing takes place there + setMaximumWidth( MAX_COLUMNS*(WIDTH+1) -1 ); + + // mxcl says null pixmaps cause crashes, so let's play it safe + for ( uint i = 0; i < FADE_SIZE; ++i ) + m_fade_bars[i].resize( 1, 1 ); +} + +BlockAnalyzer::~BlockAnalyzer() +{ + Amarok::config( "General" )->writeEntry( "Timeout", timeout() ); +} + +void +BlockAnalyzer::resizeEvent( QResizeEvent *e ) +{ + QWidget::resizeEvent( e ); + + canvas()->resize( size() ); + background()->resize( size() ); + + const uint oldRows = m_rows; + + //all is explained in analyze().. + //+1 to counter -1 in maxSizes, trust me we need this! + m_columns = myMax( uint(double(width()+1) / (WIDTH+1)), MAX_COLUMNS ); + m_rows = uint(double(height()+1) / (HEIGHT+1)); + + //this is the y-offset for drawing from the top of the widget + m_y = (height() - (m_rows * (HEIGHT+1)) + 2) / 2; + + m_scope.resize( m_columns ); + + if( m_rows != oldRows ) { + m_barPixmap.resize( WIDTH, m_rows*(HEIGHT+1) ); + + for ( uint i = 0; i < FADE_SIZE; ++i ) + m_fade_bars[i].resize( WIDTH, m_rows*(HEIGHT+1) ); + + m_yscale.resize( m_rows + 1 ); + + const uint PRE = 1, PRO = 1; //PRE and PRO allow us to restrict the range somewhat + + for( uint z = 0; z < m_rows; ++z ) + m_yscale[z] = 1 - (log10( PRE+z ) / log10( PRE+m_rows+PRO )); + + m_yscale[m_rows] = 0; + + determineStep(); + paletteChange( palette() ); + } + else if( width() > e->oldSize().width() || height() > e->oldSize().height() ) + drawBackground(); + + analyze( m_scope ); +} + +void +BlockAnalyzer::determineStep() +{ + // falltime is dependent on rowcount due to our digital resolution (ie we have boxes/blocks of pixels) + // I calculated the value 30 based on some trial and error + + const double fallTime = 30 * m_rows; + m_step = double(m_rows * timeout()) / fallTime; +} + +void +BlockAnalyzer::transform( Analyzer::Scope &s ) //pure virtual +{ + for( uint x = 0; x < s.size(); ++x ) + s[x] *= 2; + + float *front = static_cast( &s.front() ); + + m_fht->spectrum( front ); + m_fht->scale( front, 1.0 / 20 ); + + //the second half is pretty dull, so only show it if the user has a large analyzer + //by setting to m_scope.size() if large we prevent interpolation of large analyzers, this is good! + s.resize( m_scope.size() <= MAX_COLUMNS/2 ? MAX_COLUMNS/2 : m_scope.size() ); +} + +void +BlockAnalyzer::analyze( const Analyzer::Scope &s ) +{ + // y = 2 3 2 1 0 2 + // . . . . # . + // . . . # # . + // # . # # # # + // # # # # # # + // + // visual aid for how this analyzer works. + // y represents the number of blanks + // y starts from the top and increases in units of blocks + + // m_yscale looks similar to: { 0.7, 0.5, 0.25, 0.15, 0.1, 0 } + // if it contains 6 elements there are 5 rows in the analyzer + + Analyzer::interpolate( s, m_scope ); + + // Paint the background + bitBlt( canvas(), 0, 0, background() ); + + for( uint y, x = 0; x < m_scope.size(); ++x ) + { + // determine y + for( y = 0; m_scope[x] < m_yscale[y]; ++y ) + ; + + // this is opposite to what you'd think, higher than y + // means the bar is lower than y (physically) + if( (float)y > m_store[x] ) + y = int(m_store[x] += m_step); + else + m_store[x] = y; + + // if y is lower than m_fade_pos, then the bar has exceeded the height of the fadeout + // if the fadeout is quite faded now, then display the new one + if( y <= m_fade_pos[x] /*|| m_fade_intensity[x] < FADE_SIZE / 3*/ ) { + m_fade_pos[x] = y; + m_fade_intensity[x] = FADE_SIZE; + } + + if( m_fade_intensity[x] > 0 ) { + const uint offset = --m_fade_intensity[x]; + const uint y = m_y + (m_fade_pos[x] * (HEIGHT+1)); + bitBlt( canvas(), x*(WIDTH+1), y, &m_fade_bars[offset], 0, 0, WIDTH, height() - y ); + } + + if( m_fade_intensity[x] == 0 ) + m_fade_pos[x] = m_rows; + + //REMEMBER: y is a number from 0 to m_rows, 0 means all blocks are glowing, m_rows means none are + bitBlt( canvas(), x*(WIDTH+1), y*(HEIGHT+1) + m_y, bar(), 0, y*(HEIGHT+1) ); + } + + for( uint x = 0; x < m_store.size(); ++x ) + bitBlt( canvas(), x*(WIDTH+1), int(m_store[x])*(HEIGHT+1) + m_y, &m_topBarPixmap ); +} + + + + + +static inline void +adjustToLimits( int &b, int &f, uint &amount ) +{ + // with a range of 0-255 and maximum adjustment of amount, + // maximise the difference between f and b + + if( b < f ) { + if( b > 255 - f ) { + amount -= f; + f = 0; + } else { + amount -= (255 - f); + f = 255; + } + } + else { + if( f > 255 - b ) { + amount -= f; + f = 0; + } else { + amount -= (255 - f); + f = 255; + } + } +} + +/** + * Clever contrast function + * + * It will try to adjust the foreground color such that it contrasts well with the background + * It won't modify the hue of fg unless absolutely necessary + * @return the adjusted form of fg + */ +QColor +ensureContrast( const QColor &bg, const QColor &fg, uint _amount = 150 ) +{ + class OutputOnExit { + public: + OutputOnExit( const QColor &color ) : c( color ) {} + ~OutputOnExit() { int h,s,v; c.getHsv( &h, &s, &v ); } + private: + const QColor &c; + }; + + // hack so I don't have to cast everywhere + #define amount static_cast(_amount) +// #define STAMP debug() << (QValueList() << fh << fs << fv) << endl; +// #define STAMP1( string ) debug() << string << ": " << (QValueList() << fh << fs << fv) << endl; +// #define STAMP2( string, value ) debug() << string << "=" << value << ": " << (QValueList() << fh << fs << fv) << endl; + + OutputOnExit allocateOnTheStack( fg ); + + int bh, bs, bv; + int fh, fs, fv; + + bg.getHsv( bh, bs, bv ); + fg.getHsv( fh, fs, fv ); + + int dv = abs( bv - fv ); + +// STAMP2( "DV", dv ); + + // value is the best measure of contrast + // if there is enough difference in value already, return fg unchanged + if( dv > amount ) + return fg; + + int ds = abs( bs - fs ); + +// STAMP2( "DS", ds ); + + // saturation is good enough too. But not as good. TODO adapt this a little + if( ds > amount ) + return fg; + + int dh = abs( bh - fh ); + +// STAMP2( "DH", dh ); + + if( dh > 120 ) { + // a third of the colour wheel automatically guarentees contrast + // but only if the values are high enough and saturations significant enough + // to allow the colours to be visible and not be shades of grey or black + + // check the saturation for the two colours is sufficient that hue alone can + // provide sufficient contrast + if( ds > amount / 2 && (bs > 125 && fs > 125) ) +// STAMP1( "Sufficient saturation difference, and hues are compliemtary" ); + return fg; + else if( dv > amount / 2 && (bv > 125 && fv > 125) ) +// STAMP1( "Sufficient value difference, and hues are compliemtary" ); + return fg; + +// STAMP1( "Hues are complimentary but we must modify the value or saturation of the contrasting colour" ); + + //but either the colours are two desaturated, or too dark + //so we need to adjust the system, although not as much + ///_amount /= 2; + } + + if( fs < 50 && ds < 40 ) { + // low saturation on a low saturation is sad + const int tmp = 50 - fs; + fs = 50; + if( amount > tmp ) + _amount -= tmp; + else + _amount = 0; + } + + // test that there is available value to honor our contrast requirement + if( 255 - dv < amount ) + { + // we have to modify the value and saturation of fg + //adjustToLimits( bv, fv, amount ); + +// STAMP + + // see if we need to adjust the saturation + if( amount > 0 ) + adjustToLimits( bs, fs, _amount ); + +// STAMP + + // see if we need to adjust the hue + if( amount > 0 ) + fh += amount; // cycles around; + +// STAMP + + return QColor( fh, fs, fv, QColor::Hsv ); + } + +// STAMP + + if( fv > bv && bv > amount ) + return QColor( fh, fs, bv - amount, QColor::Hsv ); + +// STAMP + + if( fv < bv && fv > amount ) + return QColor( fh, fs, fv - amount, QColor::Hsv ); + +// STAMP + + if( fv > bv && (255 - fv > amount) ) + return QColor( fh, fs, fv + amount, QColor::Hsv ); + +// STAMP + + if( fv < bv && (255 - bv > amount ) ) + return QColor( fh, fs, bv + amount, QColor::Hsv ); + +// STAMP +// debug() << "Something went wrong!\n"; + + return Qt::blue; + + #undef amount +// #undef STAMP +} + +void +BlockAnalyzer::paletteChange( const QPalette& ) //virtual +{ + const QColor bg = palette().active().background(); + const QColor fg = ensureContrast( bg, KGlobalSettings::activeTitleColor() ); + + m_topBarPixmap.fill( fg ); + + const double dr = 15*double(bg.red() - fg.red()) / (m_rows*16); + const double dg = 15*double(bg.green() - fg.green()) / (m_rows*16); + const double db = 15*double(bg.blue() - fg.blue()) / (m_rows*16); + const int r = fg.red(), g = fg.green(), b = fg.blue(); + + bar()->fill( bg ); + + QPainter p( bar() ); + for( int y = 0; (uint)y < m_rows; ++y ) + //graduate the fg color + p.fillRect( 0, y*(HEIGHT+1), WIDTH, HEIGHT, QColor( r+int(dr*y), g+int(dg*y), b+int(db*y) ) ); + + { + const QColor bg = palette().active().background().dark( 112 ); + + //make a complimentary fadebar colour + //TODO dark is not always correct, dumbo! + int h,s,v; palette().active().background().dark( 150 ).getHsv( &h, &s, &v ); + const QColor fg( h + 120, s, v, QColor::Hsv ); + + const double dr = fg.red() - bg.red(); + const double dg = fg.green() - bg.green(); + const double db = fg.blue() - bg.blue(); + const int r = bg.red(), g = bg.green(), b = bg.blue(); + + // Precalculate all fade-bar pixmaps + for( uint y = 0; y < FADE_SIZE; ++y ) { + m_fade_bars[y].fill( palette().active().background() ); + QPainter f( &m_fade_bars[y] ); + for( int z = 0; (uint)z < m_rows; ++z ) { + const double Y = 1.0 - (log10( FADE_SIZE - y ) / log10( FADE_SIZE )); + f.fillRect( 0, z*(HEIGHT+1), WIDTH, HEIGHT, QColor( r+int(dr*Y), g+int(dg*Y), b+int(db*Y) ) ); + } + } + } + + drawBackground(); +} + +void +BlockAnalyzer::drawBackground() +{ + const QColor bg = palette().active().background(); + const QColor bgdark = bg.dark( 112 ); + + background()->fill( bg ); + + QPainter p( background() ); + for( int x = 0; (uint)x < m_columns; ++x ) + for( int y = 0; (uint)y < m_rows; ++y ) + p.fillRect( x*(WIDTH+1), y*(HEIGHT+1) + m_y, WIDTH, HEIGHT, bgdark ); + + setErasePixmap( *background() ); +} + +void +BlockAnalyzer::contextMenuEvent( QContextMenuEvent *e ) +{ + //this is hard to read in order to be compact, apologies.. + //the id of each menu item is the value of the attribute it represents, + //so mapping is concise. + + const uint ids[] = { 50, 33, 25, 20, 10 }; + + KPopupMenu menu; + menu.insertTitle( i18n( "Framerate" ) ); + + for( uint x = 0; x < 5; ++x ) + { + const uint v = ids[x]; + + menu.insertItem( i18n( "%1 fps" ).arg( 1000/v ), v ); + menu.setItemChecked( v, v == timeout() ); + } + +#if defined HAVE_LIBVISUAL + menu.insertSeparator(); + menu.insertItem( SmallIconSet( Amarok::icon( "visualizations" ) ), i18n("&Visualizations"), + 0 ); +#endif + + const int id = menu.exec( e->globalPos() ); + + if( id == 0 ) + Amarok::Menu::instance()->slotActivated( Amarok::Menu::ID_SHOW_VIS_SELECTOR ); + else if( id != -1 ) { + changeTimeout( id ); + determineStep(); + } +} diff --git a/amarok/src/analyzers/blockanalyzer.h b/amarok/src/analyzers/blockanalyzer.h new file mode 100644 index 00000000..a03c99e0 --- /dev/null +++ b/amarok/src/analyzers/blockanalyzer.h @@ -0,0 +1,62 @@ +// Maintainer: Max Howell , (C) 2003-5 +// Copyright: See COPYING file that comes with this distribution +// + +#ifndef BLOCKANALYZER_H +#define BLOCKANALYZER_H + +#include "analyzerbase.h" +#include + +class QResizeEvent; +class QMouseEvent; +class QPalette; + + +/** + * @author Max Howell + */ + +class BlockAnalyzer : public Analyzer::Base2D +{ +public: + BlockAnalyzer( QWidget* ); + ~BlockAnalyzer(); + + static const uint HEIGHT = 2; + static const uint WIDTH = 4; + static const uint MIN_ROWS = 3; //arbituary + static const uint MIN_COLUMNS = 32; //arbituary + static const uint MAX_COLUMNS = 256; //must be 2**n + static const uint FADE_SIZE = 90; + +protected: + virtual void transform( Scope& ); + virtual void analyze( const Scope& ); + virtual void resizeEvent( QResizeEvent* ); + virtual void contextMenuEvent( QContextMenuEvent* ); + virtual void paletteChange( const QPalette& ); + + void drawBackground(); + void determineStep(); + +private: + QPixmap* const bar() { return &m_barPixmap; } + + uint m_columns, m_rows; //number of rows and columns of blocks + uint m_y; //y-offset from top of widget + QPixmap m_barPixmap; + QPixmap m_topBarPixmap; + Scope m_scope; //so we don't create a vector every frame + std::vector m_store; //current bar heights + std::vector m_yscale; + + //FIXME why can't I namespace these? c++ issue? + std::vector m_fade_bars; + std::vector m_fade_pos; + std::vector m_fade_intensity; + + float m_step; //rows to fall per frame +}; + +#endif diff --git a/amarok/src/analyzers/boomanalyzer.cpp b/amarok/src/analyzers/boomanalyzer.cpp new file mode 100644 index 00000000..1f9d8c71 --- /dev/null +++ b/amarok/src/analyzers/boomanalyzer.cpp @@ -0,0 +1,157 @@ +// Author: Max Howell , (C) 2004 +// Copyright: See COPYING file that comes with this distribution + +#include "amarok.h" +#include "boomanalyzer.h" +#include +#include +#include +#include +#include +#include + +BoomAnalyzer::BoomAnalyzer( QWidget *parent ) + : Analyzer::Base2D( parent, 10, 9 ) + , K_barHeight( 1.271 )//1.471 + , F_peakSpeed( 1.103 )//1.122 + , F( 1.0 ) + , bar_height( BAND_COUNT, 0 ) + , peak_height( BAND_COUNT, 0 ) + , peak_speed( BAND_COUNT, 0.01 ) + , barPixmap( COLUMN_WIDTH, 50 ) +{ + QWidget *o, *box = new QWidget( this, 0, WType_TopLevel ); + QSpinBox *m; + int v; + + (new QGridLayout( box, 2, 3 ))->setAutoAdd( true ); + + v = int(K_barHeight*1000); + new QLabel( "Bar fall-rate:", box ); + o = new QSlider( 100, 2000, 100, v, Qt::Horizontal, box ); + (m = new QSpinBox( 100, 2000, 1, box ))->setValue( v ); + connect( o, SIGNAL(valueChanged(int)), SLOT(changeK_barHeight( int )) ); + connect( o, SIGNAL(valueChanged(int)), m, SLOT(setValue( int )) ); + + v = int(F_peakSpeed*1000); + new QLabel( "Peak acceleration: ", box ); + o = new QSlider( 1000, 1300, 50, v, Qt::Horizontal, box ); + (m = new QSpinBox( 1000, 1300, 1, box ))->setValue( v ); + connect( o, SIGNAL(valueChanged(int)), SLOT(changeF_peakSpeed( int )) ); + connect( o, SIGNAL(valueChanged(int)), m, SLOT(setValue( int )) ); + + //box->show(); +} + + +void +BoomAnalyzer::changeK_barHeight( int newValue ) +{ + K_barHeight = (double)newValue / 1000; +} + +void +BoomAnalyzer::changeF_peakSpeed( int newValue ) +{ + F_peakSpeed = (double)newValue / 1000; +} + +void +BoomAnalyzer::init() +{ + const uint HEIGHT = height() - 2; + const double h = 1.2 / HEIGHT; + + F = double(HEIGHT) / (log10( 256 ) * 1.1 /*<- max. amplitude*/); + + barPixmap.resize( COLUMN_WIDTH-2, HEIGHT ); + + QPainter p( &barPixmap ); + for( uint y = 0; y < HEIGHT; ++y ) + { + const double F = (double)y * h; + + p.setPen( QColor( 255 - int(229.0 * F), 255 - int(229.0 * F), 255 - int(191.0 * F) ) ); + p.drawLine( 0, y, COLUMN_WIDTH-2, y ); + } +} + +void +BoomAnalyzer::transform( Scope &s ) +{ + float *front = static_cast( &s.front() ); + + m_fht->spectrum( front ); + m_fht->scale( front, 1.0 / 60 ); + + Scope scope( 32, 0 ); + + const uint xscale[] = { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,19,24,29,36,43,52,63,76,91,108,129,153,182,216,255 }; + + for( uint j, i = 0; i < 32; i++ ) + for( j = xscale[i]; j < xscale[i + 1]; j++ ) + if ( s[j] > scope[i] ) + scope[i] = s[j]; + + s = scope; +} + +void +BoomAnalyzer::analyze( const Scope &scope ) +{ + eraseCanvas(); + + QPainter p( canvas() ); + float h; + const uint MAX_HEIGHT = height() - 1; + + for( uint i = 0, x = 0, y; i < BAND_COUNT; ++i, x += COLUMN_WIDTH+1 ) + { + h = log10( scope[i]*256.0 ) * F; + + if( h > MAX_HEIGHT ) + h = MAX_HEIGHT; + + if( h > bar_height[i] ) + { + bar_height[i] = h; + + if( h > peak_height[i] ) + { + peak_height[i] = h; + peak_speed[i] = 0.01; + } + else goto peak_handling; + } + else + { + if( bar_height[i] > 0.0 ) + { + bar_height[i] -= K_barHeight; //1.4 + if( bar_height[i] < 0.0 ) bar_height[i] = 0.0; + } + + peak_handling: + + if( peak_height[i] > 0.0 ) + { + peak_height[i] -= peak_speed[i]; + peak_speed[i] *= F_peakSpeed; //1.12 + + if( peak_height[i] < bar_height[i] ) peak_height[i] = bar_height[i]; + if( peak_height[i] < 0.0 ) peak_height[i] = 0.0; + } + } + + y = height() - uint(bar_height[i]); + bitBlt( canvas(), x+1, y, &barPixmap, 0, y ); + p.setPen( Amarok::ColorScheme::Foreground ); + p.drawRect( x, y, COLUMN_WIDTH, height() - y ); + + y = height() - uint(peak_height[i]); + p.setPen( Amarok::ColorScheme::Text ); + p.drawLine( x, y, x+COLUMN_WIDTH-1, y ); + } +} + +#include "boomanalyzer.moc" diff --git a/amarok/src/analyzers/boomanalyzer.h b/amarok/src/analyzers/boomanalyzer.h new file mode 100644 index 00000000..55c5ec3a --- /dev/null +++ b/amarok/src/analyzers/boomanalyzer.h @@ -0,0 +1,52 @@ +// Author: Max Howell , (C) 2004 +// Copyright: See COPYING file that comes with this distribution +// + +#ifndef BOOMANALYZER_H +#define BOOMANALYZER_H + +#include "analyzerbase.h" + +/** +@author Max Howell +*/ + +class BoomAnalyzer : public Analyzer::Base2D +{ +Q_OBJECT +public: + BoomAnalyzer( QWidget* ); + + virtual void init(); + virtual void transform( Scope &s ); + virtual void analyze( const Scope& ); + +public slots: + void changeK_barHeight( int ); + void changeF_peakSpeed( int ); + +protected: + static const uint COLUMN_WIDTH = 4; + static const uint BAND_COUNT = 32; + + double K_barHeight, F_peakSpeed, F; + + std::vector bar_height; + std::vector peak_height; + std::vector peak_speed; + + QPixmap barPixmap; +}; + +namespace Amarok +{ + namespace ColorScheme + { + extern QColor Base; + extern QColor Text; + extern QColor Background; + extern QColor Foreground; + } +} + +#endif diff --git a/amarok/src/analyzers/glanalyzer.cpp b/amarok/src/analyzers/glanalyzer.cpp new file mode 100644 index 00000000..ec8a6bc4 --- /dev/null +++ b/amarok/src/analyzers/glanalyzer.cpp @@ -0,0 +1,342 @@ +/*************************************************************************** + gloscope.cpp - description + ------------------- + begin : Jan 17 2004 + copyright : (C) 2004 by Adam Pigg + email : adam@piggz.co.uk + ***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#include + +#ifdef HAVE_QGLWIDGET + +#include +#include "glanalyzer.h" +#include + + +GLAnalyzer::GLAnalyzer( QWidget *parent ) + : Analyzer::Base3D(parent, 15) + , m_oldy(32, -10.0f) + , m_peaks(32) +{} + +GLAnalyzer::~GLAnalyzer() +{} + +// METHODS ===================================================== + +void GLAnalyzer::analyze( const Scope &s ) +{ + //kdDebug() << "Scope Size: " << s.size() << endl; + /* Scope t(32); + if (s.size() != 32) + { + Analyzer::interpolate(s, t); + } + else + { + t = s; + }*/ + uint offset = 0; + static float peak; + float mfactor = 0.0; + static int drawcount; + + if (s.size() == 64) + { + offset=8; + } + + glRotatef(0.25f, 0.0f, 1.0f, 0.5f); //Rotate the scene + drawFloor(); + drawcount++; + if (drawcount > 25) + { + drawcount = 0; + peak = 0.0; + } + + for ( uint i = 0; i < 32; i++ ) + { + if (s[i] > peak) + { + peak = s[i]; + } + } + + mfactor = 20 / peak; + for ( uint i = 0; i < 32; i++ ) + { + + //kdDebug() << "Scope item " << i << " value: " << s[i] << endl; + + // Calculate new horizontal position (x) depending on number of samples + x = -16.0f + i; + + // Calculating new vertical position (y) depending on the data passed by amarok + y = float(s[i+offset] * mfactor); //This make it kinda dynamically resize depending on the data + + //Some basic bounds checking + if (y > 30) + y = 30; + else if (y < 0) + y = 0; + + if((y - m_oldy[i]) < -0.6f) // Going Down Too Much + { + y = m_oldy[i] - 0.7f; + } + if (y < 0.0f) + { + y = 0.0f; + } + + m_oldy[i] = y; //Save value as last value + + //Peak Code + if (m_oldy[i] > m_peaks[i].level) + { + m_peaks[i].level = m_oldy[i]; + m_peaks[i].delay = 30; + } + + if (m_peaks[i].delay > 0) + { + m_peaks[i].delay--; + } + + if (m_peaks[i].level > 1.0f) + { + if (m_peaks[i].delay <= 0) + { + m_peaks[i].level-=0.4f; + } + } + // Draw the bar + drawBar(x,y); + drawPeak(x, m_peaks[i].level); + + } + + updateGL(); +} + +void GLAnalyzer::initializeGL() +{ + // Clear frame (next fading will be preferred to clearing) + glClearColor(0.0f, 0.0f, 0.0f, 1.0f);// Set clear color to black + glClear( GL_COLOR_BUFFER_BIT ); + + // Set the shading model + glShadeModel(GL_SMOOTH); + + // Set the polygon mode to fill + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + + // Enable depth testing for hidden line removal + glEnable(GL_DEPTH_TEST); + + // Set blend parameters for 'composting alpha' + glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); +} + +void GLAnalyzer::resizeGL( int w, int h ) +{ + glViewport( 0, 0, (GLint)w, (GLint)h ); + glMatrixMode( GL_PROJECTION ); + glLoadIdentity(); + glOrtho(-16.0f, 16.0f, -10.0f, 10.0f, -50.0f, 100.0f); + glMatrixMode( GL_MODELVIEW ); + glLoadIdentity(); +} + +void GLAnalyzer::paintGL() +{ + glMatrixMode( GL_MODELVIEW ); +#if 0 + glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); +#else + glEnable( GL_DEPTH_TEST ); + glEnable( GL_BLEND ); + glPushMatrix(); + glLoadIdentity(); + glBegin( GL_TRIANGLE_STRIP ); + glColor4f( 0.0f, 0.0f, 0.1f, 0.08f ); + glVertex2f( 20.0f, 10.0f ); + glVertex2f( -20.0f, 10.0f ); + glVertex2f( 20.0f, -10.0f ); + glVertex2f( -20.0f, -10.0f ); + glEnd(); + glPopMatrix(); + glDisable( GL_BLEND ); + glEnable( GL_DEPTH_TEST ); + glClear( GL_DEPTH_BUFFER_BIT ); +#endif + //swapBuffers(); + + glFlush(); + +} + +void GLAnalyzer::drawBar(float xPos, float height) +{ + glPushMatrix(); + + //Sets color to blue + //Set the colour depending on the height of the bar + glColor3f((height/40) + 0.5f, (height/40) + 0.625f, 1.0f); + glTranslatef(xPos, -10.0f, 0.0f); + + glScalef(1.0f, height, 3.0f); + drawCube(); + + //Set colour to full blue + //glColor3f(0.0f, 0.0f, 1.0f); + //drawFrame(); + glPopMatrix(); +} + +void GLAnalyzer::drawFloor() +{ + glPushMatrix(); + + //Sets color to amarok blue + glColor3f( 0.5f, 0.625f, 1.0f); + glTranslatef(-16.0f,-11.0f, -4.0f); + + glScalef(32.0f, 1.0f, 10.0f); + drawCube(); + + //Set colour to full blue + glColor3f(0.0f, 0.0f, 1.0f); + drawFrame(); + glPopMatrix(); +} + +void GLAnalyzer::drawPeak(float xPos, float ypos) +{ + glPushMatrix(); + + //Set the colour to red + glColor3f(1.0f, 0.0f, 0.0f); + glTranslatef(xPos, ypos - 10.0f, 0.0f); + + glScalef(1.0f, 1.0f, 3.0f); + drawCube(); + + glPopMatrix(); +} + +void GLAnalyzer::drawCube() +{ + glPushMatrix(); + glBegin(GL_POLYGON); + + //This is the top face + glVertex3f(0.0f, 1.0f, 0.0f); + glVertex3f(1.0f, 1.0f, 0.0f); + glVertex3f(1.0f, 1.0f, 1.0f); + glVertex3f(0.0f, 1.0f, 1.0f); + glVertex3f(0.0f, 1.0f, 0.0f); + + //This is the front face + glVertex3f(0.0f, 0.0f, 0.0f); + glVertex3f(1.0f, 0.0f, 0.0f); + glVertex3f(1.0f, 1.0f, 0.0f); + glVertex3f(0.0f, 1.0f, 0.0f); + glVertex3f(0.0f, 0.0f, 0.0f); + + //This is the right face + glVertex3f(1.0f, 0.0f, 0.0f); + glVertex3f(1.0f, 0.0f, 1.0f); + glVertex3f(1.0f, 1.0f, 1.0f); + glVertex3f(1.0f, 1.0f, 0.0f); + glVertex3f(1.0f, 0.0f, 0.0f); + + //This is the left face + glVertex3f(0.0f, 0.0f, 0.0f); + glVertex3f(0.0f, 0.0f, 1.0f); + glVertex3f(0.0f, 1.0f, 1.0f); + glVertex3f(0.0f, 1.0f, 0.0f); + glVertex3f(0.0f, 0.0f, 0.0f); + + //This is the bottom face + glVertex3f(0.0f, 0.0f, 0.0f); + glVertex3f(1.0f, 0.0f, 0.0f); + glVertex3f(1.0f, 0.0f, 1.0f); + glVertex3f(0.0f, 0.0f, 1.0f); + glVertex3f(0.0f, 0.0f, 0.0f); + + //This is the back face + glVertex3f(0.0f, 0.0f, 1.0f); + glVertex3f(1.0f, 0.0f, 1.0f); + glVertex3f(1.0f, 1.0f, 1.0f); + glVertex3f(0.0f, 1.0f, 1.0f); + glVertex3f(0.0f, 0.0f, 1.0f); + + glEnd(); + glPopMatrix(); +} +void GLAnalyzer::drawFrame() +{ + glPushMatrix(); + glBegin(GL_LINES); + + + //This is the top face + glVertex3f(0.0f, 1.0f, 0.0f); + glVertex3f(1.0f, 1.0f, 0.0f); + glVertex3f(1.0f, 1.0f, 1.0f); + glVertex3f(0.0f, 1.0f, 1.0f); + glVertex3f(0.0f, 1.0f, 0.0f); + + //This is the front face + glVertex3f(0.0f, 0.0f, 0.0f); + glVertex3f(1.0f, 0.0f, 0.0f); + glVertex3f(1.0f, 1.0f, 0.0f); + glVertex3f(0.0f, 1.0f, 0.0f); + glVertex3f(0.0f, 0.0f, 0.0f); + + //This is the right face + glVertex3f(1.0f, 0.0f, 0.0f); + glVertex3f(1.0f, 0.0f, 1.0f); + glVertex3f(1.0f, 1.0f, 1.0f); + glVertex3f(1.0f, 1.0f, 0.0f); + glVertex3f(1.0f, 0.0f, 0.0f); + + //This is the left face + glVertex3f(0.0f, 0.0f, 0.0f); + glVertex3f(0.0f, 0.0f, 1.0f); + glVertex3f(0.0f, 1.0f, 1.0f); + glVertex3f(0.0f, 1.0f, 0.0f); + glVertex3f(0.0f, 0.0f, 0.0f); + + //This is the bottom face + glVertex3f(0.0f, 0.0f, 0.0f); + glVertex3f(1.0f, 0.0f, 0.0f); + glVertex3f(1.0f, 0.0f, 1.0f); + glVertex3f(0.0f, 0.0f, 1.0f); + glVertex3f(0.0f, 0.0f, 0.0f); + + //This is the back face + glVertex3f(0.0f, 0.0f, 1.0f); + glVertex3f(1.0f, 0.0f, 1.0f); + glVertex3f(1.0f, 1.0f, 1.0f); + glVertex3f(0.0f, 1.0f, 1.0f); + glVertex3f(0.0f, 0.0f, 1.0f); + + glEnd(); + glPopMatrix(); +} + +#endif diff --git a/amarok/src/analyzers/glanalyzer.h b/amarok/src/analyzers/glanalyzer.h new file mode 100644 index 00000000..394c4638 --- /dev/null +++ b/amarok/src/analyzers/glanalyzer.h @@ -0,0 +1,62 @@ +/*************************************************************************** + gloscope.h - description + ------------------- + begin : Jan 17 2004 + copyright : (C) 2004 by Adam Pigg + email : adam@piggz.co.uk + ***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#ifndef GLOSCOPE_H +#define GLOSCOPE_H + +#include +#ifdef HAVE_QGLWIDGET + +#include "analyzerbase.h" + +/** + *@author piggz + */ + +typedef struct +{ + float level; + uint delay; +} +peak_tx; + +class GLAnalyzer : public Analyzer::Base3D +{ +private: + std::vector m_oldy; + std::vector m_peaks; + + void drawCube(); + void drawFrame(); + void drawBar(float xPos, float height); + void drawPeak(float xPos, float ypos); + void drawFloor(); + + GLfloat x, y; +public: + GLAnalyzer(QWidget *); + ~GLAnalyzer(); + void analyze( const Scope & ); + +protected: + void initializeGL(); + void resizeGL( int w, int h ); + void paintGL(); +}; + +#endif +#endif diff --git a/amarok/src/analyzers/glanalyzer2.cpp b/amarok/src/analyzers/glanalyzer2.cpp new file mode 100644 index 00000000..6f3d1c83 --- /dev/null +++ b/amarok/src/analyzers/glanalyzer2.cpp @@ -0,0 +1,333 @@ +/*************************************************************************** + glanalyzer2.cpp - description + ------------------- + begin : Feb 16 2004 + copyright : (C) 2004 by Enrico Ros + email : eros.kde@email.it + ***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#include + +#ifdef HAVE_QGLWIDGET + +#include +#include +#include "glanalyzer2.h" +#include +#include +#include +#include + + +GLAnalyzer2::GLAnalyzer2( QWidget *parent ): +Analyzer::Base3D(parent, 15) +{ + //initialize openGL context before managing GL calls + makeCurrent(); + loadTexture( locate("data","amarok/data/dot.png"), dotTexture ); + loadTexture( locate("data","amarok/data/wirl1.png"), w1Texture ); + loadTexture( locate("data","amarok/data/wirl2.png"), w2Texture ); + + show.paused = true; + show.pauseTimer = 0.0; + show.rotDegrees = 0.0; + frame.rotDegrees = 0.0; +} + +GLAnalyzer2::~GLAnalyzer2() +{ + freeTexture( dotTexture ); + freeTexture( w1Texture ); + freeTexture( w2Texture ); +} + +void GLAnalyzer2::initializeGL() +{ + // Set a smooth shade model + glShadeModel(GL_SMOOTH); + + // Disable depth test (all is drawn on a 2d plane) + glDisable(GL_DEPTH_TEST); + + // Set blend parameters for 'composting alpha' + glBlendFunc( GL_SRC_ALPHA, GL_ONE ); //superpose + //glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); //fade + glEnable( GL_BLEND ); + + // Clear frame with a black background + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear( GL_COLOR_BUFFER_BIT ); +} + +void GLAnalyzer2::resizeGL( int w, int h ) +{ + // Setup screen. We're going to manually do the perspective projection + glViewport( 0, 0, (GLint)w, (GLint)h ); + glMatrixMode( GL_PROJECTION ); + glLoadIdentity(); + glOrtho( -10.0f, 10.0f, -10.0f, 10.0f, -5.0f, 5.0f ); + + // Get the aspect ratio of the screen to draw 'cicular' particles + float ratio = (float)w / (float)h, + eqPixH = 60, + eqPixW = 80; + if ( ratio >= (4.0/3.0) ) { + unitX = 10.0 / (eqPixH * ratio); + unitY = 10.0 / eqPixH; + } else { + unitX = 10.0 / eqPixW; + unitY = 10.0 / (eqPixW / ratio); + } + + // Get current timestamp. + timeval tv; + gettimeofday( &tv, NULL ); + show.timeStamp = (double)tv.tv_sec + (double)tv.tv_usec/1000000.0; +} + +void GLAnalyzer2::paused() +{ + analyze( Scope() ); +} + +void GLAnalyzer2::analyze( const Scope &s ) +{ + bool haveNoData = s.empty(); + + // if we're going into pause mode, clear timers. + if ( !show.paused && haveNoData ) + show.pauseTimer = 0.0; + + // if we have got data, interpolate it (asking myself why I'm doing it here..) + if ( !(show.paused = haveNoData) ) + { + int bands = s.size(), + lowbands = bands / 4, + hibands = bands / 3, + midbands = bands - lowbands - hibands; Q_UNUSED( midbands ); + float currentEnergy = 0, + currentMeanBand = 0, + maxValue = 0; + for ( int i = 0; i < bands; i++ ) + { + float value = s[i]; + currentEnergy += value; + currentMeanBand += (float)i * value; + if ( value > maxValue ) + maxValue = value; + } + frame.silence = currentEnergy < 0.001; + if ( !frame.silence ) + { + frame.meanBand = 100.0 * currentMeanBand / (currentEnergy * bands); + currentEnergy = 100.0 * currentEnergy / (float)bands; + frame.dEnergy = currentEnergy - frame.energy; + frame.energy = currentEnergy; +// printf( "%d [%f :: %f ]\t%f \n", bands, frame.energy, frame.meanBand, maxValue ); + } else + frame.energy = 0.0; + } + + // update the frame + updateGL(); +} + +void GLAnalyzer2::paintGL() +{ + // Compute the dT since the last call to paintGL and update timings + timeval tv; + gettimeofday( &tv, NULL ); + double currentTime = (double)tv.tv_sec + (double)tv.tv_usec/1000000.0; + show.dT = currentTime - show.timeStamp; + show.timeStamp = currentTime; + + // Clear frame + glClear( GL_COLOR_BUFFER_BIT ); + + // Shitch to MODEL matrix and reset it to default + glMatrixMode( GL_MODELVIEW ); + glLoadIdentity(); + + // Fade the previous drawings. +/* glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); + glBegin( GL_TRIANGLE_STRIP ); + glColor4f( 0.0f, 0.0f, 0.0f, 0.2f ); + glVertex2f( 10.0f, 10.0f ); + glVertex2f( -10.0f, 10.0f ); + glVertex2f( 10.0f, -10.0f ); + glVertex2f( -10.0f, -10.0f ); + glEnd();*/ + + glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); + glEnable( GL_TEXTURE_2D ); + float alphaN = show.paused ? 0.2 : (frame.energy / 10.0), + alphaP = show.paused ? 1.0 : (1 - frame.energy / 20.0); + if ( alphaN > 1.0 ) + alphaN = 1.0; + if ( alphaP < 0.1 ) + alphaP = 0.1; + glBindTexture( GL_TEXTURE_2D, w2Texture ); + setTextureMatrix( show.rotDegrees, 0.707*alphaP ); + glColor4f( 1.0f, 1.0f, 1.0f, 1.0f ); + glBegin( GL_TRIANGLE_STRIP ); + glTexCoord2f( 1.0, 1.0 ); + glVertex2f( 10.0f, 10.0f ); + glTexCoord2f( 0.0, 1.0 ); + glVertex2f( -10.0f, 10.0f ); + glTexCoord2f( 1.0, 0.0 ); + glVertex2f( 10.0f, -10.0f ); + glTexCoord2f( 0.0 , 0.0 ); + glVertex2f( -10.0f, -10.0f ); + glEnd(); + glBindTexture( GL_TEXTURE_2D, w1Texture ); + setTextureMatrix( -show.rotDegrees * 2, 0.707 ); + glColor4f( 1.0f, 1.0f, 1.0f, alphaN ); + glBegin( GL_TRIANGLE_STRIP ); + glTexCoord2f( 1.0, 1.0 ); + glVertex2f( 10.0f, 10.0f ); + glTexCoord2f( 0.0, 1.0 ); + glVertex2f( -10.0f, 10.0f ); + glTexCoord2f( 1.0, 0.0 ); + glVertex2f( 10.0f, -10.0f ); + glTexCoord2f( 0.0 , 0.0 ); + glVertex2f( -10.0f, -10.0f ); + glEnd(); + setTextureMatrix( 0.0, 0.0 ); + glDisable( GL_TEXTURE_2D ); + glBlendFunc( GL_SRC_ALPHA, GL_ONE ); + + // Here begins the real draw loop + // some updates to the show + show.rotDegrees += 40.0 * show.dT; + frame.rotDegrees += 80.0 * show.dT; + + // handle the 'pause' status + if ( show.paused ) + { + if ( show.pauseTimer > 0.5 ) + { + if ( show.pauseTimer > 0.6 ) + show.pauseTimer -= 0.6; + drawFullDot( 0.0f, 0.4f, 0.8f, 1.0f ); + drawFullDot( 0.0f, 0.4f, 0.8f, 1.0f ); + } + show.pauseTimer += show.dT; + return; + } + + if ( dotTexture ) { + glEnable( GL_TEXTURE_2D ); + glBindTexture( GL_TEXTURE_2D, dotTexture ); + } else + glDisable( GL_TEXTURE_2D ); + + glLoadIdentity(); +// glRotatef( -frame.rotDegrees, 0,0,1 ); + glBegin( GL_QUADS ); +// Particle * particle = particleList.first(); +// for (; particle; particle = particleList.next()) + { + glColor4f( 0.0f, 1.0f, 0.0f, 1.0f ); + drawDot( 0, 0, kMax(10.0,(10.0 * frame.energy)) ); + glColor4f( 1.0f, 0.0f, 0.0f, 1.0f ); + drawDot( 6, 0, kMax(10.0, (5.0 * frame.energy)) ); + glColor4f( 0.0f, 0.4f, 1.0f, 1.0f ); + drawDot( -6, 0, kMax(10.0, (5.0 * frame.energy)) ); + } + glEnd(); +} + +void GLAnalyzer2::drawDot( float x, float y, float size ) +{ + float sizeX = size * unitX, + sizeY = size * unitY, + pLeft = x - sizeX, + pTop = y + sizeY, + pRight = x + sizeX, + pBottom = y - sizeY; + glTexCoord2f( 0, 0 ); // Bottom Left + glVertex2f( pLeft, pBottom ); + glTexCoord2f( 0, 1 ); // Top Left + glVertex2f( pLeft, pTop ); + glTexCoord2f( 1, 1 ); // Top Right + glVertex2f( pRight, pTop ); + glTexCoord2f( 1, 0 ); // Bottom Right + glVertex2f( pRight, pBottom ); +} + +void GLAnalyzer2::drawFullDot( float r, float g, float b, float a ) +{ + glBindTexture( GL_TEXTURE_2D, dotTexture ); + glEnable( GL_TEXTURE_2D ); + glColor4f( r, g, b, a ); + glBegin( GL_TRIANGLE_STRIP ); + glTexCoord2f( 1.0, 1.0 ); + glVertex2f( 10.0f, 10.0f ); + glTexCoord2f( 0.0, 1.0 ); + glVertex2f( -10.0f, 10.0f ); + glTexCoord2f( 1.0, 0.0 ); + glVertex2f( 10.0f, -10.0f ); + glTexCoord2f( 0.0 , 0.0 ); + glVertex2f( -10.0f, -10.0f ); + glEnd(); + glDisable( GL_TEXTURE_2D ); +} + + +void GLAnalyzer2::setTextureMatrix( float rot, float scale ) +{ + glMatrixMode( GL_TEXTURE); + glLoadIdentity(); + if ( rot != 0.0 || scale != 0.0 ) + { + glTranslatef( 0.5f, 0.5f, 0.0f ); + glRotatef( rot, 0.0f, 0.0f, 1.0f ); + glScalef( scale, scale, 1.0f ); + glTranslatef( -0.5f, -0.5f, 0.0f ); + } + glMatrixMode( GL_MODELVIEW ); +} + +bool GLAnalyzer2::loadTexture( QString fileName, GLuint& textureID ) +{ + //reset texture ID to the default EMPTY value + textureID = 0; + + //load image + QImage tmp; + if ( !tmp.load( fileName ) ) + return false; + + //convert it to suitable format (flipped RGBA) + QImage texture = QGLWidget::convertToGLFormat( tmp ); + if ( texture.isNull() ) + return false; + + //get texture number and bind loaded image to that texture + glGenTextures( 1, &textureID ); + glBindTexture( GL_TEXTURE_2D, textureID ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + glTexImage2D( GL_TEXTURE_2D, 0, 4, texture.width(), texture.height(), + 0, GL_RGBA, GL_UNSIGNED_BYTE, texture.bits() ); + return true; +} + + +void GLAnalyzer2::freeTexture( GLuint & textureID ) +{ + if ( textureID > 0 ) + glDeleteTextures( 1, &textureID ); + textureID = 0; +} + +#endif diff --git a/amarok/src/analyzers/glanalyzer2.h b/amarok/src/analyzers/glanalyzer2.h new file mode 100644 index 00000000..3aeb98ae --- /dev/null +++ b/amarok/src/analyzers/glanalyzer2.h @@ -0,0 +1,73 @@ +/*************************************************************************** + glanalyzer2.h - description + ------------------- + begin : Feb 16 2004 + copyright : (C) 2004 by Enrico Ros + email : eros.kde@email.it + ***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#ifndef GLSTARVIEW_H +#define GLSTARVIEW_H + +#include +#ifdef HAVE_QGLWIDGET + +#include "analyzerbase.h" +#include +#include + + +class GLAnalyzer2 : public Analyzer::Base3D +{ +public: + GLAnalyzer2(QWidget *); + ~GLAnalyzer2(); + void analyze( const Scope & ); + void paused(); + +protected: + void initializeGL(); + void resizeGL( int w, int h ); + void paintGL(); + +private: + struct ShowProperties { + bool paused; + double timeStamp; + double dT; + double pauseTimer; + float rotDegrees; + } show; + + struct FrameProperties { + float energy; + float dEnergy; + float meanBand; + float rotDegrees; + bool silence; + } frame; + + GLuint dotTexture; + GLuint w1Texture; + GLuint w2Texture; + float unitX, unitY; + + void drawDot( float x, float y, float size ); + void drawFullDot( float r, float g, float b, float a ); + void setTextureMatrix( float rot, float scale ); + + bool loadTexture(QString file, GLuint& textureID); + void freeTexture(GLuint& textureID); +}; + +#endif +#endif diff --git a/amarok/src/analyzers/glanalyzer3.cpp b/amarok/src/analyzers/glanalyzer3.cpp new file mode 100644 index 00000000..cc7ce4c7 --- /dev/null +++ b/amarok/src/analyzers/glanalyzer3.cpp @@ -0,0 +1,480 @@ +/*************************************************************************** + glanalyzer3.cpp - Bouncing Ballzz + ------------------- + begin : Feb 19 2004 + copyright : (C) 2004 by Enrico Ros + email : eros.kde@email.it + ***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#include + +#ifdef HAVE_QGLWIDGET + +#include +#include +#include "glanalyzer3.h" +#include +#include +#include +#include + +#ifndef HAVE_FABSF +inline float fabsf(float f) +{ + return f < 0.f ? -f : f; +} +#endif + + +class Ball +{ + public: + Ball() : x( drand48() - drand48() ), y( 1 - 2.0 * drand48() ), + z( drand48() ), vx( 0.0 ), vy( 0.0 ), vz( 0.0 ), + mass( 0.01 + drand48()/10.0 ) + //,color( (float[3]) { 0.0, drand48()*0.5, 0.7 + drand48() * 0.3 } ) + { + //this is because GCC < 3.3 can't compile the above line, we aren't sure why though + color[0] = 0.0; color[1] = drand48()*0.5; color[2] = 0.7 + drand48() * 0.3; + }; + + float x, y, z, vx, vy, vz, mass; + float color[3]; + + void updatePhysics( float dT ) + { + x += vx * dT; // position + y += vy * dT; // position + z += vz * dT; // position + if ( y < -0.8 ) vy = fabsf( vy ); + if ( y > 0.8 ) vy = -fabsf( vy ); + if ( z < 0.1 ) vz = fabsf( vz ); + if ( z > 0.9 ) vz = -fabsf( vz ); + vx += (( x > 0 ) ? 4.94 : -4.94) * dT; // G-force + vx *= (1 - 2.9 * dT); // air friction + vy *= (1 - 2.9 * dT); // air friction + vz *= (1 - 2.9 * dT); // air friction + } +}; + +class Paddle +{ + public: + Paddle( float xPos ) : onLeft( xPos < 0 ), mass( 1.0 ), + X( xPos ), x( xPos ), vx( 0.0 ) {}; + + void updatePhysics( float dT ) + { + x += vx * dT; // posision + vx += (1300 * (X - x) / mass) * dT; // elasticity + vx *= (1 - 4.0 * dT); // air friction + } + + void renderGL() + { + glBegin( GL_TRIANGLE_STRIP ); + glColor3f( 0.0f, 0.1f, 0.3f ); + glVertex3f( x, -1.0f, 0.0 ); + glVertex3f( x, 1.0f, 0.0 ); + glColor3f( 0.1f, 0.2f, 0.6f ); + glVertex3f( x, -1.0f, 1.0 ); + glVertex3f( x, 1.0f, 1.0 ); + glEnd(); + } + + void bounce( Ball * ball ) + { + if ( onLeft && ball->x < x ) + { + ball->vx = vx * mass / (mass + ball->mass) + fabsf( ball->vx ); + ball->vy = (drand48() - drand48()) * 1.8; + ball->vz = (drand48() - drand48()) * 0.9; + ball->x = x; + } + else if ( !onLeft && ball->x > x ) + { + ball->vx = vx * mass / (mass + ball->mass) - fabsf( ball->vx ); + ball->vy = (drand48() - drand48()) * 1.8; + ball->vz = (drand48() - drand48()) * 0.9; + ball->x = x; + } + } + + void impulse( float strength ) + { + if ( (onLeft && strength > vx) || (!onLeft && strength < vx) ) + vx += strength; + } + + private: + bool onLeft; + float mass, X, x, vx; +}; + + +GLAnalyzer3::GLAnalyzer3( QWidget *parent ): +Analyzer::Base3D(parent, 15) +{ + //initialize openGL context before managing GL calls + makeCurrent(); + loadTexture( locate("data","amarok/data/ball.png"), ballTexture ); + loadTexture( locate("data","amarok/data/grid.png"), gridTexture ); + + balls.setAutoDelete( true ); + leftPaddle = new Paddle( -1.0 ); + rightPaddle = new Paddle( 1.0 ); + for ( int i = 0; i < NUMBER_OF_BALLS; i++ ) + balls.append( new Ball() ); + + show.colorK = 0.0; + show.gridScrollK = 0.0; + show.gridEnergyK = 0.0; + show.camRot = 0.0; + show.camRoll = 0.0; + show.peakEnergy = 1.0; + frame.silence = true; + frame.energy = 0.0; + frame.dEnergy = 0.0; +} + +GLAnalyzer3::~GLAnalyzer3() +{ + freeTexture( ballTexture ); + freeTexture( gridTexture ); + delete leftPaddle; + delete rightPaddle; + balls.clear(); +} + +void GLAnalyzer3::initializeGL() +{ + // Set a smooth shade model + glShadeModel(GL_SMOOTH); + + // Disable depth test (all is drawn 'z-sorted') + glDisable( GL_DEPTH_TEST ); + + // Set blending function (Alpha addition) + glBlendFunc( GL_SRC_ALPHA, GL_ONE ); + + // Clear frame with a black background + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); +} + +void GLAnalyzer3::resizeGL( int w, int h ) +{ + // Setup screen. We're going to manually do the perspective projection + glViewport( 0, 0, (GLint)w, (GLint)h ); + glMatrixMode( GL_PROJECTION ); + glLoadIdentity(); + glFrustum( -0.5f, 0.5f, -0.5f, 0.5f, 0.5f, 4.5f ); + + // Get the aspect ratio of the screen to draw 'circular' particles + float ratio = (float)w / (float)h; + if ( ratio >= 1.0 ) { + unitX = 0.34 / ratio; + unitY = 0.34; + } else { + unitX = 0.34; + unitY = 0.34 * ratio; + } + + // Get current timestamp. + timeval tv; + gettimeofday( &tv, NULL ); + show.timeStamp = (double)tv.tv_sec + (double)tv.tv_usec/1000000.0; +} + +void GLAnalyzer3::paused() +{ + analyze( Scope() ); +} + +void GLAnalyzer3::analyze( const Scope &s ) +{ + // compute the dTime since the last call + timeval tv; + gettimeofday( &tv, NULL ); + double currentTime = (double)tv.tv_sec + (double)tv.tv_usec/1000000.0; + show.dT = currentTime - show.timeStamp; + show.timeStamp = currentTime; + + // compute energy integrating frame's spectrum + if ( !s.empty() ) + { + int bands = s.size(); + float currentEnergy = 0, + maxValue = 0; + // integrate spectrum -> energy + for ( int i = 0; i < bands; i++ ) + { + float value = s[i]; + currentEnergy += value; + if ( value > maxValue ) + maxValue = value; + } + currentEnergy *= 100.0 / (float)bands; + // emulate a peak detector: currentEnergy -> peakEnergy (3tau = 30 seconds) + show.peakEnergy = 1.0 + ( show.peakEnergy - 1.0 ) * exp( - show.dT / 10.0 ); + if ( currentEnergy > show.peakEnergy ) + show.peakEnergy = currentEnergy; + // check for silence + frame.silence = currentEnergy < 0.001; + // normalize frame energy against peak energy and compute frame stats + currentEnergy /= show.peakEnergy; + frame.dEnergy = currentEnergy - frame.energy; + frame.energy = currentEnergy; + } else + frame.silence = true; + + // update the frame + updateGL(); +} + +void GLAnalyzer3::paintGL() +{ + // limit max dT to 0.05 and update color and scroll constants + if ( show.dT > 0.05 ) + show.dT = 0.05; + show.colorK += show.dT * 0.4; + if ( show.colorK > 3.0 ) + show.colorK -= 3.0; + show.gridScrollK += 0.2 * show.peakEnergy * show.dT; + + // Switch to MODEL matrix and clear screen + glMatrixMode( GL_MODELVIEW ); + glLoadIdentity(); + glClear( GL_COLOR_BUFFER_BIT ); + + // Draw scrolling grid + if ( (show.gridEnergyK > 0.05) || (!frame.silence && frame.dEnergy < -0.3) ) + { + show.gridEnergyK *= exp( -show.dT / 0.1 ); + if ( -frame.dEnergy > show.gridEnergyK ) + show.gridEnergyK = -frame.dEnergy*2.0; + float gridColor[4] = { 0.0, 1.0, 0.6, show.gridEnergyK }; + drawScrollGrid( show.gridScrollK, gridColor ); + } + + // Roll camera up/down handling the beat + show.camRot += show.camRoll * show.dT; // posision + show.camRoll -= 400 * show.camRot * show.dT; // elasticity + show.camRoll *= (1 - 2.0 * show.dT); // friction + if ( !frame.silence && frame.dEnergy > 0.4 ) + show.camRoll += show.peakEnergy*2.0; + glRotatef( show.camRoll / 2.0, 1,0,0 ); + + // Translate the drawing plane + glTranslatef( 0.0f, 0.0f, -1.8f ); + + // Draw upper/lower planes and paddles + drawHFace( -1.0 ); + drawHFace( 1.0 ); + leftPaddle->renderGL(); + rightPaddle->renderGL(); + + // Draw Balls + if ( ballTexture ) { + glEnable( GL_TEXTURE_2D ); + glBindTexture( GL_TEXTURE_2D, ballTexture ); + } else + glDisable( GL_TEXTURE_2D ); + glEnable( GL_BLEND ); + Ball * ball = balls.first(); + for ( ; ball; ball = balls.next() ) + { + float color[3], + angle = show.colorK; + // Rotate the color based on 'angle' value [0,3) + if ( angle < 1.0 ) + { + color[ 0 ] = ball->color[ 0 ] * (1 - angle) + ball->color[ 1 ] * angle; + color[ 1 ] = ball->color[ 1 ] * (1 - angle) + ball->color[ 2 ] * angle; + color[ 2 ] = ball->color[ 2 ] * (1 - angle) + ball->color[ 0 ] * angle; + } + else if ( angle < 2.0 ) + { + angle -= 1.0; + color[ 0 ] = ball->color[ 1 ] * (1 - angle) + ball->color[ 2 ] * angle; + color[ 1 ] = ball->color[ 2 ] * (1 - angle) + ball->color[ 0 ] * angle; + color[ 2 ] = ball->color[ 0 ] * (1 - angle) + ball->color[ 1 ] * angle; + } + else + { + angle -= 2.0; + color[ 0 ] = ball->color[ 2 ] * (1 - angle) + ball->color[ 0 ] * angle; + color[ 1 ] = ball->color[ 0 ] * (1 - angle) + ball->color[ 1 ] * angle; + color[ 2 ] = ball->color[ 1 ] * (1 - angle) + ball->color[ 2 ] * angle; + } + // Draw the dot and update its physics also checking at bounces + glColor3fv( color ); + drawDot3s( ball->x, ball->y, ball->z, 1.0 ); + ball->updatePhysics( show.dT ); + if ( ball->x < 0 ) + leftPaddle->bounce( ball ); + else + rightPaddle->bounce( ball ); + } + glDisable( GL_BLEND ); + glDisable( GL_TEXTURE_2D ); + + // Update physics of paddles + leftPaddle->updatePhysics( show.dT ); + rightPaddle->updatePhysics( show.dT ); + if ( !frame.silence ) + { + leftPaddle->impulse( frame.energy*3.0 + frame.dEnergy*6.0 ); + rightPaddle->impulse( -frame.energy*3.0 - frame.dEnergy*6.0 ); + } +} + +void GLAnalyzer3::drawDot3s( float x, float y, float z, float size ) +{ + // Circular XY dot drawing functions + float sizeX = size * unitX, + sizeY = size * unitY, + pXm = x - sizeX, + pXM = x + sizeX, + pYm = y - sizeY, + pYM = y + sizeY; + // Draw the Dot + glBegin( GL_QUADS ); + glTexCoord2f( 0, 0 ); // Bottom Left + glVertex3f( pXm, pYm, z ); + glTexCoord2f( 0, 1 ); // Top Left + glVertex3f( pXm, pYM, z ); + glTexCoord2f( 1, 1 ); // Top Right + glVertex3f( pXM, pYM, z ); + glTexCoord2f( 1, 0 ); // Bottom Right + glVertex3f( pXM, pYm, z ); + glEnd(); + + // Shadow XZ drawing functions + float sizeZ = size / 10.0, + pZm = z - sizeZ, + pZM = z + sizeZ, + currentColor[4]; + glGetFloatv( GL_CURRENT_COLOR, currentColor ); + float alpha = currentColor[3], + topSide = (y + 1) / 4, + bottomSide = (1 - y) / 4; + // Draw the top shadow + currentColor[3] = topSide * topSide * alpha; + glColor4fv( currentColor ); + glBegin( GL_QUADS ); + glTexCoord2f( 0, 0 ); // Bottom Left + glVertex3f( pXm, 1, pZm ); + glTexCoord2f( 0, 1 ); // Top Left + glVertex3f( pXm, 1, pZM ); + glTexCoord2f( 1, 1 ); // Top Right + glVertex3f( pXM, 1, pZM ); + glTexCoord2f( 1, 0 ); // Bottom Right + glVertex3f( pXM, 1, pZm ); + glEnd(); + // Draw the bottom shadow + currentColor[3] = bottomSide * bottomSide * alpha; + glColor4fv( currentColor ); + glBegin( GL_QUADS ); + glTexCoord2f( 0, 0 ); // Bottom Left + glVertex3f( pXm, -1, pZm ); + glTexCoord2f( 0, 1 ); // Top Left + glVertex3f( pXm, -1, pZM ); + glTexCoord2f( 1, 1 ); // Top Right + glVertex3f( pXM, -1, pZM ); + glTexCoord2f( 1, 0 ); // Bottom Right + glVertex3f( pXM, -1, pZm ); + glEnd(); +} + +void GLAnalyzer3::drawHFace( float y ) +{ + glBegin( GL_TRIANGLE_STRIP ); + glColor3f( 0.0f, 0.1f, 0.2f ); + glVertex3f( -1.0f, y, 0.0 ); + glVertex3f( 1.0f, y, 0.0 ); + glColor3f( 0.1f, 0.6f, 0.5f ); + glVertex3f( -1.0f, y, 2.0 ); + glVertex3f( 1.0f, y, 2.0 ); + glEnd(); +} + +void GLAnalyzer3::drawScrollGrid( float scroll, float color[4] ) +{ + if ( !gridTexture ) + return; + glMatrixMode( GL_TEXTURE ); + glLoadIdentity(); + glTranslatef( 0.0, -scroll, 0.0 ); + glMatrixMode( GL_MODELVIEW ); + float backColor[4] = { 1.0, 1.0, 1.0, 0.0 }; + for ( int i = 0; i < 3; i++ ) + backColor[ i ] = color[ i ]; + glEnable( GL_TEXTURE_2D ); + glBindTexture( GL_TEXTURE_2D, gridTexture ); + glEnable( GL_BLEND ); + glBegin( GL_TRIANGLE_STRIP ); + glColor4fv( color ); // top face + glTexCoord2f( 0.0f, 1.0f ); + glVertex3f( -1.0f, 1.0f, -1.0f ); + glTexCoord2f( 1.0f, 1.0f ); + glVertex3f( 1.0f, 1.0f, -1.0f ); + glColor4fv( backColor ); // central points + glTexCoord2f( 0.0f, 0.0f ); + glVertex3f( -1.0f, 0.0f, -3.0f ); + glTexCoord2f( 1.0f, 0.0f ); + glVertex3f( 1.0f, 0.0f, -3.0f ); + glColor4fv( color ); // bottom face + glTexCoord2f( 0.0f, 1.0f ); + glVertex3f( -1.0f, -1.0f, -1.0f ); + glTexCoord2f( 1.0f, 1.0f ); + glVertex3f( 1.0f, -1.0f, -1.0f ); + glEnd(); + glDisable( GL_BLEND ); + glDisable( GL_TEXTURE_2D ); + glMatrixMode( GL_TEXTURE ); + glLoadIdentity(); + glMatrixMode( GL_MODELVIEW ); +} + +bool GLAnalyzer3::loadTexture( QString fileName, GLuint& textureID ) +{ + //reset texture ID to the default EMPTY value + textureID = 0; + + //load image + QImage tmp; + if ( !tmp.load( fileName ) ) + return false; + + //convert it to suitable format (flipped RGBA) + QImage texture = QGLWidget::convertToGLFormat( tmp ); + if ( texture.isNull() ) + return false; + + //get texture number and bind loaded image to that texture + glGenTextures( 1, &textureID ); + glBindTexture( GL_TEXTURE_2D, textureID ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + glTexImage2D( GL_TEXTURE_2D, 0, 4, texture.width(), texture.height(), + 0, GL_RGBA, GL_UNSIGNED_BYTE, texture.bits() ); + return true; +} + +void GLAnalyzer3::freeTexture( GLuint& textureID ) +{ + if ( textureID > 0 ) + glDeleteTextures( 1, &textureID ); + textureID = 0; +} + +#endif diff --git a/amarok/src/analyzers/glanalyzer3.h b/amarok/src/analyzers/glanalyzer3.h new file mode 100644 index 00000000..7abd7614 --- /dev/null +++ b/amarok/src/analyzers/glanalyzer3.h @@ -0,0 +1,80 @@ +/*************************************************************************** + glanalyzer3.h - description + ------------------- + begin : Feb 16 2004 + copyright : (C) 2004 by Enrico Ros + email : eros.kde@email.it + ***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#include +#ifdef HAVE_QGLWIDGET + +#ifndef GLBOUNCER_H +#define GLBOUNCER_H + +#include "analyzerbase.h" +#include +#include + +class QWidget; +class Ball; +class Paddle; + +class GLAnalyzer3 : public Analyzer::Base3D +{ +public: + GLAnalyzer3(QWidget *); + ~GLAnalyzer3(); + void analyze( const Scope & ); + void paused(); + +protected: + void initializeGL(); + void resizeGL( int w, int h ); + void paintGL(); + +private: + struct ShowProperties { + double timeStamp; + double dT; + float colorK; + float gridScrollK; + float gridEnergyK; + float camRot; + float camRoll; + float peakEnergy; + } show; + + struct FrameProperties { + bool silence; + float energy; + float dEnergy; + } frame; + + static const int NUMBER_OF_BALLS = 16; + + QPtrList balls; + Paddle * leftPaddle, * rightPaddle; + float unitX, unitY; + GLuint ballTexture; + GLuint gridTexture; + + void drawDot3s( float x, float y, float z, float size ); + void drawHFace( float y ); + void drawScrollGrid( float scroll, float color[4] ); + + bool loadTexture(QString file, GLuint& textureID); + void freeTexture(GLuint& textureID); +}; + +#endif +#endif diff --git a/amarok/src/analyzers/sonogram.cpp b/amarok/src/analyzers/sonogram.cpp new file mode 100644 index 00000000..b29563db --- /dev/null +++ b/amarok/src/analyzers/sonogram.cpp @@ -0,0 +1,91 @@ +// +// +// C++ Implementation: Sonogram +// +// Description: +// +// +// Author: Melchior FRANZ , (C) 2004 +// +// Copyright: See COPYING file that comes with this distribution +// +// + +#include +#include "sonogram.h" + +Sonogram::Sonogram(QWidget *parent) : + Analyzer::Base2D(parent, 16, 9) +{ +} + + +Sonogram::~Sonogram() +{ +} + + +void Sonogram::init() +{ + eraseCanvas(); +} + + +void Sonogram::resizeEvent(QResizeEvent *e) +{ + QWidget::resizeEvent(e); + canvas()->resize(size()); + background()->resize(size()); + +//only for gcc < 4.0 +#if !( __GNUC__ > 4 || ( __GNUC__ == 4 && __GNUC_MINOR__ >= 0 ) ) + resizeForBands(height() < 128 ? 128 : height()); +#endif + + background()->fill(backgroundColor()); + bitBlt(canvas(), 0, 0, background()); + bitBlt(this, 0, 0, background()); +} + + +void Sonogram::analyze(const Scope &s) +{ + int x = width() - 1; + QColor c; + QPainter p(canvas()); + + bitBlt(canvas(), 0, 0, canvas(), 1, 0, x, height()); + Scope::const_iterator it = s.begin(), end = s.end(); + for (int y = height() - 1; y;) { + if (it >= end || *it < .005) + c = backgroundColor(); + else if (*it < .05) + c.setHsv(95, 255, 255 - int(*it * 4000.0)); + else if (*it < 1.0) + c.setHsv(95 - int(*it * 90.0), 255, 255); + else + c = Qt::red; + + p.setPen(c); + p.drawPoint(x, y--); + + if (it < end) + ++it; + } +} + + +void Sonogram::transform(Scope &scope) +{ + float *front = static_cast(&scope.front()); + m_fht->power2(front); + m_fht->scale(front, 1.0 / 256); + scope.resize( m_fht->size() / 2 ); +} + + +void Sonogram::demo() +{ + analyze(Scope(m_fht->size(), 0)); +} + diff --git a/amarok/src/analyzers/sonogram.h b/amarok/src/analyzers/sonogram.h new file mode 100644 index 00000000..609c9b2c --- /dev/null +++ b/amarok/src/analyzers/sonogram.h @@ -0,0 +1,37 @@ +// +// +// C++ Interface: Sonogram +// +// Description: +// +// +// Author: Melchior FRANZ , (C) 2004 +// +// Copyright: See COPYING file that comes with this distribution +// +// + +#ifndef SONOGRAM_H +#define SONOGRAM_H + +#include "analyzerbase.h" + +/** +@author Melchior FRANZ +*/ + +class Sonogram : public Analyzer::Base2D +{ +public: + Sonogram(QWidget*); + ~Sonogram(); + +protected: + void init(); + void analyze(const Scope&); + void transform(Scope&); + void demo(); + void resizeEvent(QResizeEvent*); +}; + +#endif diff --git a/amarok/src/analyzers/turbine.cpp b/amarok/src/analyzers/turbine.cpp new file mode 100644 index 00000000..d2e75640 --- /dev/null +++ b/amarok/src/analyzers/turbine.cpp @@ -0,0 +1,78 @@ +// +// Amarok BarAnalyzer 3 - Jet Turbine: Symmetric version of analyzer 1 +// +// Author: Stanislav Karchebny , (C) 2003 +// Max Howell (I modified it to use boom analyzer code) +// +// Copyright: like rest of Amarok +// + +#include +#include + +#include "amarok.h" +#include "turbine.h" + +void TurbineAnalyzer::analyze( const Scope &scope ) +{ + eraseCanvas(); + + QPainter p( canvas() ); + float h; + const uint hd2 = height() / 2; + const uint MAX_HEIGHT = hd2 - 1; + + for( uint i = 0, x = 0, y; i < BAND_COUNT; ++i, x += COLUMN_WIDTH+1 ) + { + h = log10( scope[i]*256.0 ) * F * 0.5; + + if( h > MAX_HEIGHT ) + h = MAX_HEIGHT; + + if( h > bar_height[i] ) + { + bar_height[i] = h; + + if( h > peak_height[i] ) + { + peak_height[i] = h; + peak_speed[i] = 0.01; + } + else goto peak_handling; + } + else + { + if( bar_height[i] > 0.0 ) + { + bar_height[i] -= K_barHeight; //1.4 + if( bar_height[i] < 0.0 ) bar_height[i] = 0.0; + } + + peak_handling: + + if( peak_height[i] > 0.0 ) + { + peak_height[i] -= peak_speed[i]; + peak_speed[i] *= F_peakSpeed; //1.12 + + if( peak_height[i] < bar_height[i] ) peak_height[i] = bar_height[i]; + if( peak_height[i] < 0.0 ) peak_height[i] = 0.0; + } + } + + + y = hd2 - uint(bar_height[i]); + bitBlt( canvas(), x+1, y, &barPixmap, 0, y ); + bitBlt( canvas(), x+1, hd2, &barPixmap, 0, (int)bar_height[i] ); + + p.setPen( Amarok::ColorScheme::Foreground ); + p.drawRect( x, y, COLUMN_WIDTH, (int)bar_height[i]*2 ); + + const uint x2 = x+COLUMN_WIDTH-1; + p.setPen( Amarok::ColorScheme::Text ); + y = hd2 - uint(peak_height[i]); + p.drawLine( x, y, x2, y ); + y = hd2 + uint(peak_height[i]); + p.drawLine( x, y, x2, y ); + } +} diff --git a/amarok/src/analyzers/turbine.h b/amarok/src/analyzers/turbine.h new file mode 100644 index 00000000..d75c6053 --- /dev/null +++ b/amarok/src/analyzers/turbine.h @@ -0,0 +1,22 @@ +// +// Amarok BarAnalyzer 3 - Jet Turbine: Symmetric version of analyzer 1 +// +// Author: Stanislav Karchebny , (C) 2003 +// +// Copyright: like rest of Amarok +// + +#ifndef ANALYZER_TURBINE_H +#define ANALYZER_TURBINE_H + +#include "boomanalyzer.h" + +class TurbineAnalyzer : public BoomAnalyzer +{ + public: + TurbineAnalyzer( QWidget *parent ) : BoomAnalyzer( parent ) {} + + void analyze( const Scope& ); +}; + +#endif diff --git a/amarok/src/app.cpp b/amarok/src/app.cpp new file mode 100644 index 00000000..32f20f24 --- /dev/null +++ b/amarok/src/app.cpp @@ -0,0 +1,1487 @@ +/*************************************************************************** + app.cpp - description + ------------------- +begin : Mit Okt 23 14:35:18 CEST 2002 +copyright : (C) 2002 by Mark Kretschmann +email : markey@web.de +***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#include "amarok.h" +#include "amarokconfig.h" +#include "amarokdcophandler.h" +#include "app.h" +#include "atomicstring.h" +#include "config.h" +#include "configdialog.h" +#include "contextbrowser.h" +#include "collectionbrowser.h" +#include "dbsetup.h" //firstRunWizard() +#include "debug.h" +#include "devicemanager.h" +#include "mediadevicemanager.h" +#include "enginebase.h" +#include "enginecontroller.h" +#include "equalizersetup.h" +#include "firstrunwizard.h" +#include "mediabrowser.h" +#include "metabundle.h" +#include "mountpointmanager.h" +#include "osd.h" +#include "playerwindow.h" +#include "playlist.h" +#include "playlistbrowser.h" +#include "playlistwindow.h" +#include "pluginmanager.h" +#include "refreshimages.h" +#include "scriptmanager.h" +#include "scrobbler.h" +#include "statusbar.h" +#include "systray.h" +#include "threadmanager.h" +#include "tracktooltip.h" //engineNewMetaData() + +#include + +#include +#include //firstRunWizard() +#include //initCliArgs() +#include //Amarok::OverrideCursor +#include //slotConfigToolbars() +#include //initGlobalShortcuts() +#include //applyColorScheme() +#include //amarok Icon +#include //slotConfigShortcuts() +#include +#include //applySettings(), genericEventHandler() +#include //Amarok::invokeBrowser() +#include +#include //genericEventHandler() +#include +#include + +#include //genericEventHandler() +#include //applySettings() +#include +#include //applyColorScheme() +#include //applyColorScheme() +#include //QPixmap::setDefaultOptimization() +#include //genericEventHandler +#include //showHyperThreadingWarning() +#include //default tooltip for trayicon + +// For the HyperThreading fix +#ifdef __linux__ + #ifdef SCHEDAFFINITY_SUPPORT + #include + #include + #endif //SCHEDAFFINITY_SUPPORT +#endif //__linux__ + +QMutex Debug::mutex; +QMutex Amarok::globalDirsMutex; + +int App::mainThreadId = 0; + +#ifdef Q_WS_MAC +#include + +static AEEventHandlerUPP appleEventProcessorUPP = 0; + +OSStatus +appleEventProcessor(const AppleEvent *ae, AppleEvent *, long /*handlerRefCon*/) +{ + OSType aeID = typeWildCard; + OSType aeClass = typeWildCard; + AEGetAttributePtr(ae, keyEventClassAttr, typeType, 0, &aeClass, sizeof(aeClass), 0); + AEGetAttributePtr(ae, keyEventIDAttr, typeType, 0, &aeID, sizeof(aeID), 0); + + if(aeClass == kCoreEventClass) + { + if(aeID == kAEReopenApplication) + { + if( PlaylistWindow::self() ) + PlaylistWindow::self()->show(); + } + return noErr; + } + return eventNotHandledErr; +} +#endif + +LIBAMAROK_EXPORT KAboutData aboutData( "amarok", + I18N_NOOP( "Amarok" ), APP_VERSION, + I18N_NOOP( "The audio player for KDE" ), KAboutData::License_GPL, + I18N_NOOP( "(C) 2002-2003, Mark Kretschmann\n(C) 2003-2007, The Amarok Development Squad" ), + I18N_NOOP( "IRC:\nirc.freenode.net - #amarok, #amarok.de, #amarok.es\n\nFeedback:\namarok@kde.org\n\n(Build Date: " __DATE__ ")" ), + ( "http://amarok.kde.org" ) ); + +App::App() + : KApplication() + , m_pPlayerWindow( 0 ) //will be created in applySettings() +{ + DEBUG_BLOCK + +#ifdef Q_WS_MAC + // this is inspired by OpenSceneGraph: osgDB/FilePath.cpp + + // Start with the the Bundle PlugIns directory. + + // Get the main bundle first. No need to retain or release it since + // we are not keeping a reference + CFBundleRef myBundle = CFBundleGetMainBundle(); + if( myBundle ) + { + // CFBundleGetMainBundle will return a bundle ref even if + // the application isn't part of a bundle, so we need to + // check + // if the path to the bundle ends in ".app" to see if it is + // a + // proper application bundle. If it is, the plugins path is + // added + CFURLRef urlRef = CFBundleCopyBundleURL(myBundle); + if(urlRef) + { + char bundlePath[1024]; + if( CFURLGetFileSystemRepresentation( urlRef, true, (UInt8 *)bundlePath, sizeof(bundlePath) ) ) + { + QCString bp( bundlePath ); + size_t len = bp.length(); + if( len > 4 && bp.right( 4 ) == ".app" ) + { + bp.append( "/Contents/MacOS" ); + QCString path = getenv( "PATH" ); + if( path.length() > 0 ) + { + path.prepend( ":" ); + } + path.prepend( bp ); + debug() << "setting PATH=" << path << endl; + setenv("PATH", path, 1); + } + } + // docs say we are responsible for releasing CFURLRef + CFRelease(urlRef); + } + } +#endif + + QPixmap::setDefaultOptimization( QPixmap::MemoryOptim ); + + //needs to be created before the wizard + new Amarok::DcopPlayerHandler(); // Must be created first + new Amarok::DcopPlaylistHandler(); + new Amarok::DcopPlaylistBrowserHandler(); + new Amarok::DcopContextBrowserHandler(); + new Amarok::DcopCollectionHandler(); + new Amarok::DcopMediaBrowserHandler(); + new Amarok::DcopScriptHandler(); + new Amarok::DcopDevicesHandler(); + + fixHyperThreading(); + // tell AtomicString that this is the GUI thread + if ( !AtomicString::isMainThread() ) + qWarning("AtomicString was initialized from a thread other than the GUI " + "thread. This could lead to memory leaks."); + +#ifdef Q_WS_MAC + appleEventProcessorUPP = AEEventHandlerUPP(appleEventProcessor); + AEInstallEventHandler(kCoreEventClass, kAEReopenApplication, appleEventProcessorUPP, (long)this, true); +#endif + + QTimer::singleShot( 0, this, SLOT( continueInit() ) ); +} + +App::~App() +{ + DEBUG_BLOCK + + // Hiding the OSD before exit prevents crash + Amarok::OSD::instance()->hide(); + + EngineBase* const engine = EngineController::engine(); + + if ( AmarokConfig::resumePlayback() ) { + if ( engine->state() != Engine::Empty ) { + AmarokConfig::setResumeTrack( EngineController::instance()->playingURL().prettyURL() ); + AmarokConfig::setResumeTime( engine->position() ); + } + else AmarokConfig::setResumeTrack( QString::null ); //otherwise it'll play previous resume next time! + } + + EngineController::instance()->endSession(); //records final statistics + EngineController::instance()->detach( this ); + + // do even if trayicon is not shown, it is safe + Amarok::config()->writeEntry( "HiddenOnExit", mainWindow()->isHidden() ); + + CollectionDB::instance()->stopScan(); + + delete m_pPlayerWindow; //sets some XT keys + delete m_pPlaylistWindow; //sets some XT keys + + ThreadManager::deleteInstance(); //waits for jobs to finish + + // this must be deleted before the connection to the Xserver is + // severed, or we risk a crash when the QApplication is exited, + // I asked Trolltech! *smug* + delete Amarok::OSD::instance(); + + AmarokConfig::setVersion( APP_VERSION ); + AmarokConfig::writeConfig(); + + //need to unload the engine before the kapplication is destroyed + PluginManager::unload( engine ); +} + + +#include +#include + +namespace +{ + // grabbed from KsCD source, kompatctdisk.cpp + QString urlToDevice(const QString& device) + { + KURL deviceUrl(device); + if (deviceUrl.protocol() == "media" || deviceUrl.protocol() == "system") + { + DCOPRef mediamanager( "kded", "mediamanager" ); + DCOPReply reply = mediamanager.call( "properties(QString)", deviceUrl.fileName() ); + QStringList properties = reply; + + if (!reply.isValid() || properties.count() < 6) + { + debug() << "Invalid reply from mediamanager" << endl; + return QString(); + } + else + { + debug() << "Reply from mediamanager " << properties[5] << endl; + return properties[5]; + } + } + + return device; + } + +} + + +void App::handleCliArgs() //static +{ + static char cwd[PATH_MAX]; + KCmdLineArgs* const args = KCmdLineArgs::parsedArgs(); + + if ( args->isSet( "cwd" ) ) + { + strncpy(cwd, args->getOption( "cwd" ), sizeof(cwd) ); + cwd[sizeof(cwd)-1] = '\0'; + KCmdLineArgs::setCwd( cwd ); + } + + bool haveArgs = false; + if ( args->count() > 0 ) + { + haveArgs = true; + + KURL::List list; + for( int i = 0; i < args->count(); i++ ) + { + KURL url = args->url( i ); + if( url.protocol() == "itpc" || url.protocol() == "pcast" ) + PlaylistBrowser::instance()->addPodcast( url ); + else + list << url; + } + + int options = Playlist::DefaultOptions; + if( args->isSet( "queue" ) ) + options = Playlist::Queue; + else if( args->isSet( "append" ) || args->isSet( "enqueue" ) ) + options = Playlist::Append; + else if( args->isSet( "load" ) ) + options = Playlist::Replace; + + if( args->isSet( "play" ) ) + options |= Playlist::DirectPlay; + + Playlist::instance()->insertMedia( list, options ); + } + + //we shouldn't let the user specify two of these since it is pointless! + //so we prioritise, pause > stop > play > next > prev + //thus pause is the least destructive, followed by stop as brakes are the most important bit of a car(!) + //then the others seemed sensible. Feel free to modify this order, but please leave justification in the cvs log + //I considered doing some sanity checks (eg only stop if paused or playing), but decided it wasn't worth it + else if ( args->isSet( "pause" ) ) + { + haveArgs = true; + EngineController::instance()->pause(); + } + else if ( args->isSet( "stop" ) ) + { + haveArgs = true; + EngineController::instance()->stop(); + } + else if ( args->isSet( "play-pause" ) ) + { + haveArgs = true; + EngineController::instance()->playPause(); + } + else if ( args->isSet( "play" ) ) //will restart if we are playing + { + haveArgs = true; + EngineController::instance()->play(); + } + else if ( args->isSet( "next" ) ) + { + haveArgs = true; + EngineController::instance()->next(); + } + else if ( args->isSet( "previous" ) ) + { + haveArgs = true; + EngineController::instance()->previous(); + } + else if (args->isSet("cdplay")) + { + haveArgs = true; + QString device = args->getOption("cdplay"); + device = DeviceManager::instance()->convertMediaUrlToDevice( device ); + KURL::List urls; + if (EngineController::engine()->getAudioCDContents(device, urls)) { + Playlist::instance()->insertMedia( + urls, Playlist::Replace|Playlist::DirectPlay); + } else { // Default behaviour + debug() << + "Sorry, the engine doesn't support direct play from AudioCD..." + << endl; + } + } + + if ( args->isSet( "toggle-playlist-window" ) ) + { + haveArgs = true; + pApp->m_pPlaylistWindow->showHide(); + } + + static bool firstTime = true; + if( !firstTime && !haveArgs ) + pApp->m_pPlaylistWindow->activate(); + firstTime = false; + + args->clear(); //free up memory +} + + +///////////////////////////////////////////////////////////////////////////////////// +// INIT +///////////////////////////////////////////////////////////////////////////////////// + +void App::initCliArgs( int argc, char *argv[] ) //static +{ + static KCmdLineOptions options[] = + { + { "+[URL(s)]", I18N_NOOP( "Files/URLs to open" ), 0 }, + { "r", 0, 0 }, + { "previous", I18N_NOOP( "Skip backwards in playlist" ), 0 }, + { "p", 0, 0 }, + { "play", I18N_NOOP( "Start playing current playlist" ), 0 }, + { "t", 0, 0 }, + { "play-pause", I18N_NOOP( "Play if stopped, pause if playing" ), 0 }, + { "pause", I18N_NOOP( "Pause playback" ), 0 }, + { "s", 0, 0 }, + { "stop", I18N_NOOP( "Stop playback" ), 0 }, + { "f", 0, 0 }, + { "next", I18N_NOOP( "Skip forwards in playlist" ), 0 }, + { ":", I18N_NOOP("Additional options:"), 0 }, + { "a", 0, 0 }, + { "append", I18N_NOOP( "Append files/URLs to playlist" ), 0 }, + { "e", 0, 0 }, + { "enqueue", I18N_NOOP("See append, available for backwards compatability"), 0 }, + { "queue", I18N_NOOP("Queue URLs after the currently playing track"), 0 }, + { "l", 0, 0 }, + { "load", I18N_NOOP("Load URLs, replacing current playlist"), 0 }, + { "m", 0, 0 }, + { "toggle-playlist-window", I18N_NOOP("Toggle the Playlist-window"), 0 }, + { "wizard", I18N_NOOP( "Run first-run wizard" ), 0 }, + { "engine ", I18N_NOOP( "Use the engine" ), 0 }, + { "cwd ", I18N_NOOP( "Base for relative filenames/URLs" ), 0 }, + { "cdplay ", I18N_NOOP("Play an AudioCD from "), 0 }, + //FIXME: after string freeze { "cdplay ", I18N_NOOP("Play an AudioCD from or system:/media/"), 0 }, + { 0, 0, 0 } + }; + + KCmdLineArgs::reset(); + KCmdLineArgs::init( argc, argv, &::aboutData ); //calls KApplication::addCmdLineOptions() + KCmdLineArgs::addCmdLineOptions( options ); //add our own options +} + + +#include +#include +void App::initGlobalShortcuts() +{ + EngineController* const ec = EngineController::instance(); + + m_pGlobalAccel->insert( "play", i18n( "Play" ), 0, KKey("WIN+x"), 0, + ec, SLOT( play() ), true, true ); + m_pGlobalAccel->insert( "pause", i18n( "Pause" ), 0, 0, 0, + ec, SLOT( pause() ), true, true ); + m_pGlobalAccel->insert( "play_pause", i18n( "Play/Pause" ), 0, KKey("WIN+c"), 0, + ec, SLOT( playPause() ), true, true ); + m_pGlobalAccel->insert( "stop", i18n( "Stop" ), 0, KKey("WIN+v"), 0, + ec, SLOT( stop() ), true, true ); + m_pGlobalAccel->insert( "stop_after_global", i18n( "Stop Playing After Current Track" ), 0, KKey("WIN+CTRL+v"), 0, + Playlist::instance()->qscrollview(), SLOT( toggleStopAfterCurrentTrack() ), true, true ); + m_pGlobalAccel->insert( "next", i18n( "Next Track" ), 0, KKey("WIN+b"), 0, + ec, SLOT( next() ), true, true ); + m_pGlobalAccel->insert( "prev", i18n( "Previous Track" ), 0, KKey("WIN+z"), 0, + ec, SLOT( previous() ), true, true ); + m_pGlobalAccel->insert( "volup", i18n( "Increase Volume" ), 0, KKey("WIN+KP_Add"), 0, + ec, SLOT( increaseVolume() ), true, true ); + m_pGlobalAccel->insert( "voldn", i18n( "Decrease Volume" ), 0, KKey("WIN+KP_Subtract"), 0, + ec, SLOT( decreaseVolume() ), true, true ); + m_pGlobalAccel->insert( "seekforward", i18n( "Seek Forward" ), 0, KKey("WIN+Shift+KP_Add"), 0, + ec, SLOT( seekForward() ), true, true ); + m_pGlobalAccel->insert( "seekbackward", i18n( "Seek Backward" ), 0, KKey("WIN+Shift+KP_Subtract"), 0, + ec, SLOT( seekBackward() ), true, true ); + m_pGlobalAccel->insert( "playlist_add", i18n( "Add Media..." ), 0, KKey("WIN+a"), 0, + m_pPlaylistWindow, SLOT( slotAddLocation() ), true, true ); + m_pGlobalAccel->insert( "show", i18n( "Toggle Playlist Window" ), 0, KKey("WIN+p"), 0, + m_pPlaylistWindow, SLOT( showHide() ), true, true ); +#ifdef Q_WS_X11 + m_pGlobalAccel->insert( "osd", i18n( "Show OSD" ), 0, KKey("WIN+o"), 0, + Amarok::OSD::instance(), SLOT( forceToggleOSD() ), true, true ); +#endif + m_pGlobalAccel->insert( "mute", i18n( "Mute Volume" ), 0, KKey("WIN+m"), 0, + ec, SLOT( mute() ), true, true ); + + m_pGlobalAccel->insert( "rating1", i18n( "Rate Current Track: 1" ), 0, KKey("WIN+1"), 0, + this, SLOT( setRating1() ), true, true ); + m_pGlobalAccel->insert( "rating2", i18n( "Rate Current Track: 2" ), 0, KKey("WIN+2"), 0, + this, SLOT( setRating2() ), true, true ); + m_pGlobalAccel->insert( "rating3", i18n( "Rate Current Track: 3" ), 0, KKey("WIN+3"), 0, + this, SLOT( setRating3() ), true, true ); + m_pGlobalAccel->insert( "rating4", i18n( "Rate Current Track: 4" ), 0, KKey("WIN+4"), 0, + this, SLOT( setRating4() ), true, true ); + m_pGlobalAccel->insert( "rating5", i18n( "Rate Current Track: 5" ), 0, KKey("WIN+5"), 0, + this, SLOT( setRating5() ), true, true ); + + m_pGlobalAccel->setConfigGroup( "Shortcuts" ); + m_pGlobalAccel->readSettings( kapp->config() ); + m_pGlobalAccel->updateConnections(); + + //TODO fix kde accel system so that kactions find appropriate global shortcuts + // and there is only one configure shortcuts dialog + + KActionCollection* const ac = Amarok::actionCollection(); + KAccelShortcutList list( m_pGlobalAccel ); + + for( uint i = 0; i < list.count(); ++i ) + { + KAction *action = ac->action( list.name( i ).latin1() ); + + if( action ) + { + //this is a hack really, also it means there may be two calls to the slot for the shortcut + action->setShortcutConfigurable( false ); + action->setShortcut( list.shortcut( i ) ); + } + } +} + + +void App::fixHyperThreading() +{ + /** Workaround for stability issues with HyperThreading CPU's, @see BUG 99199. + * First we detect the presence of HyperThreading. If active, we bind amarokapp + * to the first CPU only (hard affinity). + * + * @see http://www-128.ibm.com/developerworks/linux/library/l-affinity.html + * @see http://www.linuxjournal.com/article/6799 + * (articles on processor affinity with the linux kernel) + */ + + DEBUG_BLOCK + + #ifdef __linux__ + QString line; + uint cpuCount = 0; + QFile cpuinfo( "/proc/cpuinfo" ); + if ( cpuinfo.open( IO_ReadOnly ) ) { + while ( cpuinfo.readLine( line, 20000 ) != -1 ) { + if ( line.startsWith( "flags" ) ) + cpuCount++; + } + } + // If multiple CPUs are listed with the HT flag, we got HyperThreading enabled + if ( cpuCount > 1 ) { + debug() << "SMP system detected. Enabling WORKAROUND.\n"; + + // If the library is new enough try and call sched_setaffinity. + #ifdef SCHEDAFFINITY_SUPPORT + cpu_set_t mask; + CPU_ZERO( &mask ); // Initializes all the bits in the mask to zero + CPU_SET( 0, &mask ); // Sets only the bit corresponding to cpu + #ifdef SCHEDAFFINITY_3PARAMS + if ( sched_setaffinity( 0, sizeof(mask), &mask ) == -1 ) + #else //SCHEDAFFINITY_3PARAMS + if ( sched_setaffinity( 0, &mask ) == -1 ) + #endif //SCHEDAFFINITY_3PARAMS + { + warning() << "sched_setaffinity() call failed with error code: " << errno << endl; + QTimer::singleShot( 0, this, SLOT( showHyperThreadingWarning() ) ); + return; + } + #else //SCHEDAFFINITY_SUPPORT + warning()<<"glibc failed checks for sched_setaffinity" << endl; + QTimer::singleShot( 0, this, SLOT( showHyperThreadingWarning() ) ); + #endif //SCHEDAFFINITY_SUPPORT + } + else { debug() << "Workaround not enabled" << endl; } + #else //__linux__ + debug() << "SCHEDAFFINITY_SUPPORT disabled since this isn't Linux" << endl; + #endif //__linux__ +} + + +void App::showHyperThreadingWarning() // SLOT +{ + const QString text = + i18n( "

You are using a system with multiple CPUs. " + "Please note that Amarok may be unstable with this " + "configuration.

" + "

If your system has hyperthreading, you can improve Amarok's stability by using the Linux kernel option 'NOHT', " + "or by disabling HyperThreading in your BIOS setup.

" + "

More information can be found in the README file. For further assistance " + "join us at #amarok on irc.freenode.net.

" ); + + KMessageBox::information( 0, text, i18n( "Warning" ), "showHyperThreadingWarning" ); +} + + +///////////////////////////////////////////////////////////////////////////////////// +// METHODS +///////////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include + +//this class is only used in this module, so I figured I may as well define it +//here and save creating another header/source file combination + +class ID3v1StringHandler : public TagLib::ID3v1::StringHandler +{ + QTextCodec *m_codec; + + virtual TagLib::String parse( const TagLib::ByteVector &data ) const + { + return QStringToTString( m_codec->toUnicode( data.data(), data.size() ) ); + } + + virtual TagLib::ByteVector render( const TagLib::String &ts ) const + { + const QCString qcs = m_codec->fromUnicode( TStringToQString(ts) ); + return TagLib::ByteVector( qcs, qcs.length() ); + } + +public: + ID3v1StringHandler( int codecIndex ) + : m_codec( QTextCodec::codecForIndex( codecIndex ) ) + { + debug() << "codec: " << m_codec << endl; + debug() << "codec-name: " << m_codec->name() << endl; + } + + ID3v1StringHandler( QTextCodec *codec ) + : m_codec( codec ) + { + debug() << "codec: " << m_codec << endl; + debug() << "codec-name: " << m_codec->name() << endl; + } +}; + +//SLOT +void App::applySettings( bool firstTime ) +{ + ///Called when the configDialog is closed with OK or Apply + + DEBUG_BLOCK + + //determine and apply colors first + applyColorScheme(); + +#ifdef Q_WS_X11 + TrackToolTip::instance()->removeFromWidget( m_pTray ); +#endif + + if( AmarokConfig::showPlayerWindow() ) + { + if( !m_pPlayerWindow ) + { + //the player Window becomes the main Window + //it is the focus for hideWithMainWindow behaviour etc. + //it gets the majestic "Amarok" caption + m_pPlaylistWindow->setCaption( kapp->makeStdCaption( i18n("Playlist") ) ); + + m_pPlayerWindow = new PlayerWidget( m_pPlaylistWindow, "PlayerWindow", firstTime && AmarokConfig::playlistWindowEnabled() ); + + //don't show PlayerWindow on firstTime, that is done below + //we need to explicately set the PL button if it's the first time + if( !firstTime ) m_pPlayerWindow->show(); + + + connect( m_pPlayerWindow, SIGNAL(playlistToggled( bool )), m_pPlaylistWindow, SLOT(showHide()) ); + +#ifdef Q_WS_X11 + //TODO get this to work! + //may work if you set no parent for the systray? + //KWin::setSystemTrayWindowFor( m_pTray->winId(), m_pPlayerWindow->winId() ); + + delete m_pTray; m_pTray = new Amarok::TrayIcon( m_pPlayerWindow ); + + //make tray icon behave properly after selecting to show or hide player window + m_pTray->engineStateChanged(EngineController::instance()->engine()->state(), EngineController::instance()->engine()->state()); + m_pTray->engineNewMetaData(EngineController::instance()->bundle(), false); +#endif + + //make player window minimal if it was last time + if( AmarokConfig::playerWindowMinimalView() ){ + m_pPlayerWindow->setMinimalView( true ); + } + } + else + //this is called in the PlayerWindow ctor, hence the else + m_pPlayerWindow->applySettings(); + + } else if( m_pPlayerWindow ) { +#ifdef Q_WS_X11 + delete m_pTray; m_pTray = new Amarok::TrayIcon( m_pPlaylistWindow ); + m_pTray->engineStateChanged(EngineController::instance()->engine()->state(), EngineController::instance()->engine()->state()); + m_pTray->engineNewMetaData(EngineController::instance()->bundle(), false); +#endif + delete m_pPlayerWindow; m_pPlayerWindow = 0; + + //Set the caption correctly. + if ( !EngineController::instance()->bundle().prettyTitle().isEmpty() ) + m_pPlaylistWindow->setCaption( i18n("Amarok - %1").arg( EngineController::instance()->bundle().veryNiceTitle() ) ); + else + m_pPlaylistWindow->setCaption( "Amarok" ); + } + + playlistWindow()->applySettings(); + Scrobbler::instance()->applySettings(); + Amarok::OSD::instance()->applySettings(); + CollectionDB::instance()->applySettings(); +#ifdef Q_WS_X11 + m_pTray->setShown( AmarokConfig::showTrayIcon() ); + TrackToolTip::instance()->addToWidget( m_pTray ); +#endif + + + //on startup we need to show the window, but only if it wasn't hidden on exit + //and always if the trayicon isn't showing + QWidget* main_window = mainWindow(); +#ifdef Q_WS_X11 + if( ( main_window && firstTime && !Amarok::config()->readBoolEntry( "HiddenOnExit", false ) ) || ( main_window && !AmarokConfig::showTrayIcon() ) ) +#endif + { + main_window->show(); + + //takes longer but feels shorter. Crazy eh? :) + kapp->eventLoop()->processEvents( QEventLoop::ExcludeUserInput ); + } + + + { // + EngineBase *engine = EngineController::engine(); + + if( firstTime || AmarokConfig::soundSystem() != + PluginManager::getService( engine )->property( "X-KDE-Amarok-name" ).toString() ) + { + //will unload engine for us first if necessary + engine = EngineController::instance()->loadEngine(); + } + + engine->setXfadeLength( AmarokConfig::crossfade() ? AmarokConfig::crossfadeLength() : 0 ); + engine->setVolume( AmarokConfig::masterVolume() ); + + engine->setEqualizerEnabled( AmarokConfig::equalizerEnabled() ); + if ( AmarokConfig::equalizerEnabled() ) + engine->setEqualizerParameters( AmarokConfig::equalizerPreamp(), AmarokConfig::equalizerGains() ); + + Amarok::actionCollection()->action("play_audiocd")->setEnabled( EngineController::hasEngineProperty( "HasKIO" ) || EngineController::hasEngineProperty("HasCDDA")); + } // + + { // + CollectionView::instance()->renderView(true); + } // + { // + ContextBrowser::instance()->renderView(); + } // + + { // delete unneeded cover images from cache + const QString size = QString::number( AmarokConfig::coverPreviewSize() ) + '@'; + const QDir cacheDir = Amarok::saveLocation( "albumcovers/cache/" ); + const QStringList obsoleteCovers = cacheDir.entryList( "*" ); + foreach( obsoleteCovers ) + if ( !(*it).startsWith( size ) && !(*it).startsWith( "50@" ) ) + QFile( cacheDir.filePath( *it ) ).remove(); + } + + //if ( !firstTime ) + // Bizarrely and ironically calling this causes crashes for + // some people! FIXME + //AmarokConfig::writeConfig(); + +} + +//SLOT +void +App::continueInit() +{ + DEBUG_BLOCK + const KCmdLineArgs* const args = KCmdLineArgs::parsedArgs(); + bool restoreSession = args->count() == 0 || args->isSet( "append" ) || args->isSet( "enqueue" ) + || Amarok::config()->readBoolEntry( "AppendAsDefault", false ); + + // Make this instance so it can start receiving signals + MoodServer::instance(); + + // Remember old folder setup, so we can detect changes after the wizard was used + //const QStringList oldCollectionFolders = MountPointManager::instance()->collectionFolders(); + + + if ( Amarok::config()->readBoolEntry( "First Run", true ) || args->isSet( "wizard" ) ) { + std::cout << "STARTUP\n" << std::flush; //hide the splashscreen + firstRunWizard(); + Amarok::config()->writeEntry( "First Run", false ); + Amarok::config()->sync(); + } + + CollectionDB::instance()->checkDatabase(); + + m_pMediaDeviceManager = MediaDeviceManager::instance(); + m_pGlobalAccel = new KGlobalAccel( this ); + m_pPlaylistWindow = new PlaylistWindow(); +#ifdef Q_WS_X11 + m_pTray = new Amarok::TrayIcon( m_pPlaylistWindow ); +#endif + m_pPlaylistWindow->init(); //creates the playlist, browsers, etc. + //init playlist window as soon as the database is guaranteed to be usable + //connect( CollectionDB::instance(), SIGNAL( databaseUpdateDone() ), m_pPlaylistWindow, SLOT( init() ) ); + initGlobalShortcuts(); + //load previous playlist in separate thread + if ( restoreSession && AmarokConfig::savePlaylist() ) + { + Playlist::instance()->restoreSession(); + //Debug::stamp(); + //p->restoreSession(); + } + if( args->isSet( "engine" ) ) { + // we correct some common errors (case issues, missing -engine off the end) + QString engine = args->getOption( "engine" ).lower(); + if( engine.startsWith( "gstreamer" ) ) engine = "gst-engine"; + if( !engine.endsWith( "engine" ) ) engine += "-engine"; + + AmarokConfig::setSoundSystem( engine ); + } + Debug::stamp(); + //create engine, show PlayerWindow, show TrayIcon etc. + applySettings( true ); + Debug::stamp(); + // Start ScriptManager. Must be created _after_ PlaylistWindow. + ScriptManager::instance(); + Debug::stamp(); + //notify loader application that we have started + std::cout << "STARTUP\n" << std::flush; + + //after this point only analyzer and temporary pixmaps will be created + QPixmap::setDefaultOptimization( QPixmap::BestOptim ); + + //do after applySettings(), or the OSD will flicker and other wierdness! + //do before restoreSession()! + EngineController::instance()->attach( this ); + + //set a default interface + engineStateChanged( Engine::Empty ); + + if ( AmarokConfig::resumePlayback() && restoreSession && !args->isSet( "stop" ) ) { + //restore session as long as the user didn't specify media to play etc. + //do this after applySettings() so OSD displays correctly + EngineController::instance()->restoreSession(); + } + + CollectionDB *collDB = CollectionDB::instance(); + //Collection scan is triggered in firstRunWizard if the colelction folder setup was changed in the wizard + + // If database version is updated, the collection needs to be rescanned. + // Works also if the collection is empty for some other reason + // (e.g. deleted collection.db) + if ( CollectionDB::instance()->isEmpty() ) + { + //connect( collDB, SIGNAL( databaseUpdateDone() ), collDB, SLOT( startScan() ) ); + collDB->startScan(); + } + else if ( AmarokConfig::monitorChanges() ) + //connect( collDB, SIGNAL( databaseUpdateDone() ), collDB, SLOT( scanModifiedDirs() ) ); + collDB->scanModifiedDirs(); + + + handleCliArgs(); +} + +void +App::applyColorScheme() +{ + QColorGroup group; + using Amarok::ColorScheme::AltBase; + int h, s, v; + QWidget* const browserBar = static_cast( playlistWindow()->child( "BrowserBar" ) ); + QWidget* const contextBrowser = static_cast( ContextBrowser::instance() ); + + if( AmarokConfig::schemeKDE() ) + { + AltBase = KGlobalSettings::alternateBackgroundColor(); + + playlistWindow()->unsetPalette(); + browserBar->unsetPalette(); + contextBrowser->unsetPalette(); + + PlayerWidget::determineAmarokColors(); + } + + else if( AmarokConfig::schemeAmarok() ) + { + group = QApplication::palette().active(); + const QColor bg( Amarok::blue ); + AltBase.setRgb( 57, 64, 98 ); + + group.setColor( QColorGroup::Text, Qt::white ); + group.setColor( QColorGroup::Link, 0xCCCCCC ); + group.setColor( QColorGroup::Base, bg ); + group.setColor( QColorGroup::Foreground, 0xd7d7ef ); + group.setColor( QColorGroup::Background, AltBase ); + + group.setColor( QColorGroup::Button, AltBase ); + group.setColor( QColorGroup::ButtonText, 0xd7d7ef ); + +// group.setColor( QColorGroup::Light, Qt::cyan /*lighter than Button color*/ ); +// group.setColor( QColorGroup::Midlight, Qt::blue /*between Button and Light*/ ); +// group.setColor( QColorGroup::Dark, Qt::green /*darker than Button*/ ); +// group.setColor( QColorGroup::Mid, Qt::red /*between Button and Dark*/ ); +// group.setColor( QColorGroup::Shadow, Qt::yellow /*a very dark color. By default, the shadow color is Qt::black*/ ); + + group.setColor( QColorGroup::Highlight, Qt::white ); + group.setColor( QColorGroup::HighlightedText, bg ); + //group.setColor( QColorGroup::BrightText, QColor( 0xff, 0x40, 0x40 ) ); //GlowColor + + AltBase.getHsv( &h, &s, &v ); + group.setColor( QColorGroup::Midlight, QColor( h, s/3, (int)(v * 1.2), QColor::Hsv ) ); //column separator in playlist + + //TODO set all colours, even button colours, that way we can change the dark, + //light, etc. colours and Amarok scheme will look much better + + using namespace Amarok::ColorScheme; + Base = Amarok::blue; + Text = Qt::white; + Background = 0x002090; + Foreground = 0x80A0FF; + + //all children() derive their palette from this + playlistWindow()->setPalette( QPalette( group, group, group ) ); + browserBar->unsetPalette(); + contextBrowser->setPalette( QPalette( group, group, group ) ); + } + + else if( AmarokConfig::schemeCustom() ) + { + // we try to be smart: this code figures out contrasting colors for + // selection and alternate background rows + group = QApplication::palette().active(); + const QColor fg( AmarokConfig::playlistWindowFgColor() ); + const QColor bg( AmarokConfig::playlistWindowBgColor() ); + + //TODO use the ensureContrast function you devised in BlockAnalyzer + + bg.hsv( &h, &s, &v ); + v += (v < 128) ? +50 : -50; + v &= 255; //ensures 0 <= v < 256 + AltBase.setHsv( h, s, v ); + + fg.hsv( &h, &s, &v ); + v += (v < 128) ? +150 : -150; + v &= 255; //ensures 0 <= v < 256 + QColor highlight( h, s, v, QColor::Hsv ); + + group.setColor( QColorGroup::Base, bg ); + group.setColor( QColorGroup::Background, bg.dark( 115 ) ); + group.setColor( QColorGroup::Text, fg ); + group.setColor( QColorGroup::Link, fg.light( 120 ) ); + group.setColor( QColorGroup::Highlight, highlight ); + group.setColor( QColorGroup::HighlightedText, Qt::white ); + group.setColor( QColorGroup::Dark, Qt::darkGray ); + + PlayerWidget::determineAmarokColors(); + + // we only colour the middle section since we only + // allow the user to choose two colours + browserBar->setPalette( QPalette( group, group, group ) ); + contextBrowser->setPalette( QPalette( group, group, group ) ); + playlistWindow()->unsetPalette(); + } + + // set the KListView alternate colours + QObjectList* const list = playlistWindow()->queryList( "KListView" ); + for( QObject *o = list->first(); o; o = list->next() ) + static_cast(o)->setAlternateBackground( AltBase ); + delete list; //heap allocated! +} + + +bool Amarok::genericEventHandler( QWidget *recipient, QEvent *e ) +{ + //this is used as a generic event handler for widgets that want to handle + //typical events in an Amarok fashion + + //to use it just pass the event eg: + // + // void Foo::barEvent( QBarEvent *e ) + // { + // Amarok::genericEventHandler( this, e ); + // } + + switch( e->type() ) + { + case QEvent::DragEnter: + #define e static_cast(e) + e->accept( KURLDrag::canDecode( e ) ); + break; + + case QEvent::Drop: + if( KURLDrag::canDecode( e ) ) + { + QPopupMenu popup; + //FIXME this isn't a good way to determine if there is a currentTrack, need playlist() function + const bool b = EngineController::engine()->loaded(); + + popup.insertItem( SmallIconSet( Amarok::icon( "add_playlist" ) ), i18n( "&Append to Playlist" ), + Playlist::Append ); + popup.insertItem( SmallIconSet( Amarok::icon( "add_playlist" ) ), i18n( "Append && &Play" ), + Playlist::DirectPlay | Playlist::Append ); + if( b ) + popup.insertItem( SmallIconSet( Amarok::icon( "fast_forward" ) ), i18n( "&Queue Track" ), + Playlist::Queue ); + popup.insertSeparator(); + popup.insertItem( i18n( "&Cancel" ), 0 ); + + const int id = popup.exec( recipient->mapToGlobal( e->pos() ) ); + KURL::List list; + KURLDrag::decode( e, list ); + + if ( id > 0 ) + Playlist::instance()->insertMedia( list, id ); + } + else return false; + #undef e + + break; + + //this like every entry in the generic event handler is used by more than one widget + //please don't remove! + case QEvent::Wheel: + { + #define e static_cast(e) + + //this behaviour happens for the systray and the player window + //to override one, override it in that class + + switch( e->state() ) + { + case Qt::ControlButton: + { + const bool up = e->delta() > 0; + + //if this seems strange to you, please bring it up on #amarok + //for discussion as we can't decide which way is best! + if( up ) EngineController::instance()->previous(); + else EngineController::instance()->next(); + break; + } + case Qt::ShiftButton: + { + EngineController::instance()->seekRelative( ( e->delta() / 120 ) * 10000 ); // 10 seconds + break; + } + default: + EngineController::instance()->increaseVolume( e->delta() / Amarok::VOLUME_SENSITIVITY ); + } + + e->accept(); + #undef e + + break; + } + + case QEvent::Close: + + //KDE policy states we should hide to tray and not quit() when the + //close window button is pushed for the main widget + + static_cast(e)->accept(); //if we don't do this the info box appears on quit()! + + if( AmarokConfig::showTrayIcon() && !e->spontaneous() && !kapp->sessionSaving() ) + { + KMessageBox::information( recipient, + i18n( "Closing the main-window will keep Amarok running in the System Tray. " + "Use Quit from the menu, or the Amarok tray-icon to exit the application." ), + i18n( "Docking in System Tray" ), "hideOnCloseInfo" ); + } + else pApp->quit(); + + break; + + default: + return false; + } + + return true; +} + + +void App::engineStateChanged( Engine::State state, Engine::State oldState ) +{ + const MetaBundle &bundle = EngineController::instance()->bundle(); + switch( state ) + { + case Engine::Empty: + if ( AmarokConfig::showPlayerWindow() ) + m_pPlaylistWindow->setCaption( kapp->makeStdCaption( i18n("Playlist") ) ); + else m_pPlaylistWindow->setCaption( "Amarok" ); + TrackToolTip::instance()->clear(); + Amarok::OSD::instance()->setImage( KIconLoader().iconPath( "amarok", -KIcon::SizeHuge ) ); + break; + + case Engine::Playing: + if ( oldState == Engine::Paused ) + Amarok::OSD::instance()->OSDWidget::show( i18n( "state, as in playing", "Play" ) ); + if ( !bundle.prettyTitle().isEmpty() ) + m_pPlaylistWindow->setCaption( i18n("Amarok - %1").arg( bundle.veryNiceTitle() ) ); + break; + + case Engine::Paused: + Amarok::OSD::instance()->OSDWidget::show( i18n("Paused") ); + break; + + case Engine::Idle: + if ( AmarokConfig::showPlayerWindow() ) + m_pPlaylistWindow->setCaption( kapp->makeStdCaption( i18n("Playlist") ) ); + else m_pPlaylistWindow->setCaption( "Amarok" ); + break; + + default: + ; + } +} + +void App::engineNewMetaData( const MetaBundle &bundle, bool /*trackChanged*/ ) +{ + Amarok::OSD::instance()->show( bundle ); + if ( !bundle.prettyTitle().isEmpty() ) + m_pPlaylistWindow->setCaption( i18n("Amarok - %1").arg( bundle.veryNiceTitle() ) ); + + TrackToolTip::instance()->setTrack( bundle ); +} + +void App::engineTrackPositionChanged( long position, bool /*userSeek*/ ) +{ + TrackToolTip::instance()->setPos( position ); +} + +void App::engineVolumeChanged( int newVolume ) +{ + Amarok::OSD::instance()->OSDWidget::volChanged( newVolume ); +} + +void App::slotConfigEqualizer() //SLOT +{ + EqualizerSetup::instance()->show(); + EqualizerSetup::instance()->raise(); +} + + +void App::slotConfigAmarok( const QCString& page ) +{ + DEBUG_THREAD_FUNC_INFO + + AmarokConfigDialog* dialog = static_cast( KConfigDialog::exists( "settings" ) ); + + if( !dialog ) + { + //KConfigDialog didn't find an instance of this dialog, so lets create it : + dialog = new AmarokConfigDialog( m_pPlaylistWindow, "settings", AmarokConfig::self() ); + + connect( dialog, SIGNAL(settingsChanged()), SLOT(applySettings()) ); + } + + //FIXME it seems that if the dialog is on a different desktop it gets lost + // what do to? detect and move it? + + if ( page.isNull() ) + dialog->showPage( AmarokConfigDialog::s_currentPage ); + else + dialog->showPageByName( page ); + + dialog->show(); + dialog->raise(); + dialog->setActiveWindow(); +} + +void App::slotConfigShortcuts() +{ + KKeyDialog::configure( Amarok::actionCollection(), m_pPlaylistWindow ); +} + +void App::slotConfigGlobalShortcuts() +{ + KKeyDialog::configure( m_pGlobalAccel, true, m_pPlaylistWindow, true ); +} + +void App::slotConfigToolBars() +{ + PlaylistWindow* const pw = playlistWindow(); + KEditToolbar dialog( pw->actionCollection(), pw->xmlFile(), true, pw ); + + dialog.showButtonApply( false ); + + if( dialog.exec() ) + { + playlistWindow()->reloadXML(); + playlistWindow()->createGUI(); + } +} + +void App::firstRunWizard() +{ + ///show firstRunWizard + DEBUG_BLOCK + + FirstRunWizard wizard; + setTopWidget( &wizard ); + KConfigDialogManager* config = new KConfigDialogManager(&wizard, AmarokConfig::self(), "wizardconfig"); + config->updateWidgets(); + // connect(config, SIGNAL(settingsChanged()), SLOT(updateSettings())); + wizard.setCaption( makeStdCaption( i18n( "First-Run Wizard" ) ) ); + + if( wizard.exec() != QDialog::Rejected ) + { + //make sure that the DB config is stored in amarokrc before calling CollectionDB's ctor + AmarokConfig::setDatabaseEngine( + QString::number( Amarok::databaseTypeCode( wizard.dbSetup7->databaseEngine->currentText() ) ) ); + config->updateSettings(); + + const QStringList oldCollectionFolders = MountPointManager::instance()->collectionFolders(); + wizard.writeCollectionConfig(); + + // If wizard is invoked at runtime, rescan collection if folder setup has changed + if ( !Amarok::config()->readBoolEntry( "First Run", true ) && + oldCollectionFolders != MountPointManager::instance()->collectionFolders() ) + CollectionDB::instance()->startScan(); + + } +} + +void App::setUseScores( bool use ) +{ + AmarokConfig::setUseScores( use ); + emit useScores( use ); +} + +void App::setUseRatings( bool use ) +{ + AmarokConfig::setUseRatings( use ); + emit useRatings( use ); +} + +void App::setMoodbarPrefs( bool show, bool moodier, int alter, bool withMusic ) +{ + AmarokConfig::setShowMoodbar( show ); + AmarokConfig::setMakeMoodier( moodier ); + AmarokConfig::setAlterMood( alter ); + AmarokConfig::setMoodsWithMusic( withMusic ); + emit moodbarPrefs( show, moodier, alter, withMusic ); +} + +KIO::Job *App::trashFiles( const KURL::List &files ) +{ +#if KDE_IS_VERSION( 3, 3, 91 ) + KIO::Job *job = KIO::trash( files, true /*show progress*/ ); + Amarok::StatusBar::instance()->newProgressOperation( job ).setDescription( i18n("Moving files to trash") ); + connect( job, SIGNAL( result( KIO::Job* ) ), this, SLOT( slotTrashResult( KIO::Job* ) ) ); + return job; +#else + KIO::Job* job = KIO::move( files, KGlobalSettings::trashPath() ); + return job; +#endif +} + +void App::setRating( int n ) +{ + if( !AmarokConfig::useRatings() ) return; + + n *= 2; + + const Engine::State s = EngineController::instance()->engine()->state(); + if( s == Engine::Playing || s == Engine::Paused || s == Engine::Idle ) + { + const QString path = EngineController::instance()->playingURL().path(); + CollectionDB::instance()->setSongRating( path, n, true ); + const int rating = CollectionDB::instance()->getSongRating( path ); + EngineController::instance()->updateBundleRating( rating ); + Amarok::OSD::instance()->OSDWidget::ratingChanged( rating ); + if( !Amarok::OSD::instance()->isShown() && !PlaylistWindow::self()->isReallyShown() ) + Amarok::OSD::instance()->forceToggleOSD(); + } + else if( PlaylistWindow::self()->isReallyShown() && Playlist::instance()->qscrollview()->hasFocus() ) + Playlist::instance()->setSelectedRatings( n ); +} + +void App::slotTrashResult( KIO::Job *job ) +{ + if( job->error() ) + job->showErrorDialog( PlaylistWindow::self() ); +} + +QWidget *App::mainWindow() const +{ + return AmarokConfig::showPlayerWindow() ? static_cast( m_pPlayerWindow ) + : static_cast( m_pPlaylistWindow ); +} + +void App::quit() +{ + emit prepareToQuit(); + if( MediaBrowser::instance()->blockQuit() ) + { + // don't quit yet, as some media devices still have to finish transferring data + QTimer::singleShot( 100, this, SLOT( quit() ) ); + return; + } + KApplication::quit(); +} + +namespace Amarok +{ + /// @see amarok.h + + QWidget *mainWindow() + { + return pApp->playlistWindow(); + } + + KActionCollection *actionCollection() + { + return pApp->playlistWindow()->actionCollection(); + } + + KConfig *config( const QString &group ) + { + //Slightly more useful config() that allows setting the group simultaneously + kapp->config()->setGroup( group ); + return kapp->config(); + } + + bool invokeBrowser( const QString& url ) + { + //URL can be in whatever forms KURL::fromPathOrURL understands - ie most. + const QString cmd = "%1 \"%2\""; + return KRun::runCommand( cmd.arg( AmarokConfig::externalBrowser(), KURL::fromPathOrURL( url ).url() ) ) > 0; + } + + namespace ColorScheme + { + QColor Base; + QColor Text; + QColor Background; + QColor Foreground; + QColor AltBase; + } + + OverrideCursor::OverrideCursor( Qt::CursorShape cursor ) + { + QApplication::setOverrideCursor( cursor == Qt::WaitCursor ? KCursor::waitCursor() : KCursor::workingCursor() ); + } + + OverrideCursor::~OverrideCursor() + { + QApplication::restoreOverrideCursor(); + } + + QString saveLocation( const QString &directory ) + { + globalDirsMutex.lock(); + QString result = KGlobal::dirs()->saveLocation( "data", QString("amarok/") + directory, true ); + globalDirsMutex.unlock(); + return result; + } + + QString cleanPath( const QString &path ) + { + QString result = path; + // german umlauts + result.replace( QChar(0x00e4), "ae" ).replace( QChar(0x00c4), "Ae" ); + result.replace( QChar(0x00f6), "oe" ).replace( QChar(0x00d6), "Oe" ); + result.replace( QChar(0x00fc), "ue" ).replace( QChar(0x00dc), "Ue" ); + result.replace( QChar(0x00df), "ss" ); + + // some strange accents + result.replace( QChar(0x00e7), "c" ).replace( QChar(0x00c7), "C" ); + result.replace( QChar(0x00fd), "y" ).replace( QChar(0x00dd), "Y" ); + result.replace( QChar(0x00f1), "n" ).replace( QChar(0x00d1), "N" ); + + // czech letters with carons + result.replace( QChar(0x0161), "s" ).replace( QChar(0x0160), "S" ); + result.replace( QChar(0x010d), "c" ).replace( QChar(0x010c), "C" ); + result.replace( QChar(0x0159), "r" ).replace( QChar(0x0158), "R" ); + result.replace( QChar(0x017e), "z" ).replace( QChar(0x017d), "Z" ); + result.replace( QChar(0x0165), "t" ).replace( QChar(0x0164), "T" ); + result.replace( QChar(0x0148), "n" ).replace( QChar(0x0147), "N" ); + result.replace( QChar(0x010f), "d" ).replace( QChar(0x010e), "D" ); + + // accented vowels + QChar a[] = { 'a', 0xe0,0xe1,0xe2,0xe3,0xe5, 0 }; + QChar A[] = { 'A', 0xc0,0xc1,0xc2,0xc3,0xc5, 0 }; + QChar e[] = { 'e', 0xe8,0xe9,0xea,0xeb,0x11b, 0 }; + QChar E[] = { 'E', 0xc8,0xc9,0xca,0xcb,0x11a, 0 }; + QChar i[] = { 'i', 0xec,0xed,0xee,0xef, 0 }; + QChar I[] = { 'I', 0xcc,0xcd,0xce,0xcf, 0 }; + QChar o[] = { 'o', 0xf2,0xf3,0xf4,0xf5,0xf8, 0 }; + QChar O[] = { 'O', 0xd2,0xd3,0xd4,0xd5,0xd8, 0 }; + QChar u[] = { 'u', 0xf9,0xfa,0xfb,0x16f, 0 }; + QChar U[] = { 'U', 0xd9,0xda,0xdb,0x16e, 0 }; + QChar nul[] = { 0 }; + QChar *replacements[] = { a, A, e, E, i, I, o, O, u, U, nul }; + + for( uint i = 0; i < result.length(); i++ ) + { + QChar c = result.ref( i ); + for( uint n = 0; replacements[n][0] != QChar(0); n++ ) + { + for( uint k=0; replacements[n][k] != QChar(0); k++ ) + { + if( replacements[n][k] == c ) + { + c = replacements[n][0]; + } + } + } + result.ref( i ) = c; + } + return result; + } + + QString asciiPath( const QString &path ) + { + QString result = path; + for( uint i = 0; i < result.length(); i++ ) + { + QChar c = result.ref( i ); + if( c > QChar(0x7f) || c == QChar(0) ) + { + c = '_'; + } + result.ref( i ) = c; + } + return result; + } + + QString vfatPath( const QString &path ) + { + QString s = path; + + for( uint i = 0; i < s.length(); i++ ) + { + QChar c = s.ref( i ); + if( c < QChar(0x20) + || c=='*' || c=='?' || c=='<' || c=='>' + || c=='|' || c=='"' || c==':' || c=='/' + || c=='\\' ) + c = '_'; + s.ref( i ) = c; + } + + uint len = s.length(); + if( len == 3 || (len > 3 && s[3] == '.') ) + { + QString l = s.left(3).lower(); + if( l=="aux" || l=="con" || l=="nul" || l=="prn" ) + s = '_' + s; + } + else if( len == 4 || (len > 4 && s[4] == '.') ) + { + QString l = s.left(3).lower(); + QString d = s.mid(3,1); + if( (l=="com" || l=="lpt") && + (d=="0" || d=="1" || d=="2" || d=="3" || d=="4" || + d=="5" || d=="6" || d=="7" || d=="8" || d=="9") ) + s = '_' + s; + } + + while( s.startsWith( "." ) ) + s = s.mid(1); + + while( s.endsWith( "." ) ) + s = s.left( s.length()-1 ); + + s = s.left(255); + len = s.length(); + if( s[len-1] == ' ' ) + s[len-1] = '_'; + + return s; + } + + QString decapitateString( const QString &input, const QString &ref ) + { + QString t = ref.upper(); + int length = t.length(); + int commonLength = 0; + while( length > 0 ) + { + if ( input.upper().startsWith( t ) ) + { + commonLength = t.length(); + t = ref.upper().left( t.length() + length/2 ); + length = length/2; + } + else + { + t = ref.upper().left( t.length() - length/2 ); + length = length/2; + } + } + QString clean = input; + if( t.endsWith( " " ) || !ref.at( t.length() ).isLetterOrNumber() ) // common part ends with a space or complete word + clean = input.right( input.length() - commonLength ).stripWhiteSpace(); + return clean; + } + + void setUseScores( bool use ) { App::instance()->setUseScores( use ); } + void setUseRatings( bool use ) { App::instance()->setUseRatings( use ); } + void setMoodbarPrefs( bool show, bool moodier, int alter, bool withMusic ) + { App::instance()->setMoodbarPrefs( show, moodier, alter, withMusic ); } + KIO::Job *trashFiles( const KURL::List &files ) { return App::instance()->trashFiles( files ); } +} + +#include "app.moc" diff --git a/amarok/src/app.h b/amarok/src/app.h new file mode 100644 index 00000000..0ed7df27 --- /dev/null +++ b/amarok/src/app.h @@ -0,0 +1,125 @@ +/*************************************************************************** + app.h - description + ------------------- + begin : Mit Okt 23 14:35:18 CEST 2002 + copyright : (C) 2002 by Mark Kretschmann + email : markey@web.de +***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#ifndef AMAROK_APP_H +#define AMAROK_APP_H + +#include +#include "amarok_export.h" +#include "engineobserver.h" //baseclass +#include //baseclass +#include + +namespace Amarok { + class TrayIcon; +} + +namespace KIO { class Job; } + +class KActionCollection; +class KConfig; +class KGlobalAccel; +class MetaBundle; +class PlayerWidget; +class Playlist; +class PlaylistWindow; +class MediaDeviceManager; + +class LIBAMAROK_EXPORT App : public KApplication, public EngineObserver +{ + Q_OBJECT + public: + App(); + ~App(); + + static App *instance() { return static_cast( qApp ); } + + static void handleCliArgs(); + static void initCliArgs( int argc, char *argv[] ); + + static int mainThreadId; + + PlaylistWindow *playlistWindow() const { return m_pPlaylistWindow; } + PlayerWidget *playerWindow() const { return m_pPlayerWindow; } + + // FRIENDS ------ + friend class PlaylistWindow; //requires access to applySettings() + + signals: + void useScores( bool use ); + void useRatings( bool use ); + void moodbarPrefs( bool show, bool moodier, int alter, bool withMusic ); + void prepareToQuit(); + protected: /* for OSD, tray, and dcop */ + void engineStateChanged( Engine::State state, Engine::State oldState = Engine::Empty ); + void engineNewMetaData( const MetaBundle &bundle, bool trackChanged ); + void engineTrackPositionChanged( long position, bool /*userSeek*/ ); + void engineVolumeChanged( int ); + + private slots: + void showHyperThreadingWarning(); + void setRating1() { setRating( 1 ); } + void setRating2() { setRating( 2 ); } + void setRating3() { setRating( 3 ); } + void setRating4() { setRating( 4 ); } + void setRating5() { setRating( 5 ); } + void continueInit(); + + + public slots: + void applySettings( bool firstTime = false ); + void slotConfigAmarok( const QCString& page = QCString() ); + void slotConfigShortcuts(); + void slotConfigGlobalShortcuts(); + void slotConfigToolBars(); + void slotConfigEqualizer(); + void setUseScores( bool use ); + void setUseRatings( bool use ); + void setMoodbarPrefs( bool show, bool moodier, int alter, bool withMusic ); + KIO::Job *trashFiles( const KURL::List &files ); + void quit(); + + private slots: + void slotTrashResult( KIO::Job *job ); + + private: + /** Workaround for HyperThreading CPU's, @see BUG 99199 */ + void fixHyperThreading(); + + void initGlobalShortcuts(); + void applyColorScheme(); + void firstRunWizard(); + + /** returns the leading window, either playerWindow or playlistWindow */ + QWidget *mainWindow() const; + + void setRating( int n ); + + // ATTRIBUTES ------ + KGlobalAccel *m_pGlobalAccel; + PlayerWidget *m_pPlayerWindow; + PlaylistWindow *m_pPlaylistWindow; +#ifdef Q_WS_X11 + Amarok::TrayIcon *m_pTray; +#endif + MediaDeviceManager *m_pMediaDeviceManager; +}; + +#define pApp static_cast(kapp) + + +#endif // AMAROK_APP_H diff --git a/amarok/src/atomicstring.cpp b/amarok/src/atomicstring.cpp new file mode 100644 index 00000000..90fb3f92 --- /dev/null +++ b/amarok/src/atomicstring.cpp @@ -0,0 +1,175 @@ +/* + Copyright (c) 2006 Gábor Lehel + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include +#ifdef HAVE_STDINT_H +#include +#endif +#include +#include +#include + +#include "atomicstring.h" + +class AtomicString::Data: public QString +{ +public: + uint refcount; + Data(): refcount( 0 ) { } + Data( const QString &s ): QString( s ), refcount( 0 ) { } +}; + +AtomicString::AtomicString(): m_string( 0 ) { } + +AtomicString::AtomicString( const AtomicString &other ) +{ + s_storeMutex.lock(); + m_string = other.m_string; + ref( m_string ); + s_storeMutex.unlock(); +} + +AtomicString::AtomicString( const QString &string ): m_string( 0 ) +{ + if( string.isEmpty() ) + return; + + Data *s = new Data( string ); // note: s is a shallow copy + s_storeMutex.lock(); + m_string = static_cast( *( s_store.insert( s ).first ) ); + ref( m_string ); + uint rc = s->refcount; + if( rc && !isMainThread()) { + // Inserted, and we are not in the main thread -- we need to make s a deep copy, + // as this copy may be refcounted by the main thread outside our locks + (QString &) (*s) = QDeepCopy( string ); + } + s_storeMutex.unlock(); + if ( !rc ) delete( s ); // already present +} + +AtomicString::~AtomicString() +{ + s_storeMutex.lock(); + deref( m_string ); + s_storeMutex.unlock(); +} + +QString AtomicString::string() const +{ + if ( !m_string ) return QString(); + // References to the stored string are only allowed to circulate in the main thread + if ( isMainThread() ) return *m_string; + else return deepCopy(); +} + +QString AtomicString::deepCopy() const +{ + if (m_string) + return QString( m_string->unicode(), m_string->length() ); + return QString(); +} + +bool AtomicString::isEmpty() const +{ + return !m_string; +} + +const QString *AtomicString::ptr() const +{ + if( m_string ) + return m_string; + return &QString::null; +} + +uint AtomicString::refcount() const +{ + if ( m_string ) { + s_storeMutex.lock(); + uint rc = m_string->refcount; + s_storeMutex.unlock(); + return rc; + } + return 0; +} + +AtomicString &AtomicString::operator=( const AtomicString &other ) +{ + if( m_string == other.m_string ) + return *this; + s_storeMutex.lock(); + deref( m_string ); + m_string = other.m_string; + ref( m_string ); + s_storeMutex.unlock(); + return *this; +} + +// needs to be called holding the lock +inline void AtomicString::deref( Data *s ) +{ + checkLazyDeletes(); // a good time to do this + if( !s ) + return; + if( !( --s->refcount ) ) + { + s_store.erase( s ); + // only the main thread is allowed to delete stored strings + if ( isMainThread() ) + delete s; + else + s_lazyDeletes.append(s); + } +} + +// needs to be called holding the lock +inline void AtomicString::ref( Data *s ) +{ + checkLazyDeletes(); // a good time to do this + if( s ) + s->refcount++; +} + +// It is not necessary to hold the store mutex here. +bool AtomicString::isMainThread() +{ + // For isMainThread(), we could use QThread::currentThread(), except the + // docs say it's unreliable. And in general QThreads don't like to be called from + // app destructors. Good old pthreads will serve us well. As for Windows, these + // two calls surely have equivalents; better yet we'll have QT4 and thread safe + // QStrings by then. + // Note that the the static local init is thread safe. + static pthread_t main_thread = pthread_self(); + return pthread_equal(pthread_self(), main_thread); +} + +// call holding the store mutex +inline void AtomicString::checkLazyDeletes() +{ + // only the main thread is allowed to delete + if ( isMainThread() ) + { + s_lazyDeletes.setAutoDelete(true); + s_lazyDeletes.clear(); + } +} + +AtomicString::set_type AtomicString::s_store; +QPtrList AtomicString::s_lazyDeletes; +QMutex AtomicString::s_storeMutex; diff --git a/amarok/src/atomicstring.h b/amarok/src/atomicstring.h new file mode 100644 index 00000000..e81ae238 --- /dev/null +++ b/amarok/src/atomicstring.h @@ -0,0 +1,191 @@ +/* + Copyright (c) 2006 Gábor Lehel + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +/** + * A thin wrapper over QString which ensures only a single copy of string data + * is stored for equivalent strings. As a side benefit, testing for equality + * is reduced to a pointer comparison. Construction is slower than a QString, + * as it must be checked for equality with existing strings. (A hash set is + * used for this purpose. According to benchmarks, Paul Hsieh's SuperFastHash + * (which is currently used -- see http://www.azillionmonkeys.com/qed/hash.html) + * can hash 5 million 256 byte strings in 1.34s on a 1.62GHz Athlon XP.) For + * other use, the overhead compared to a plain QString should be minimal. + * + * Added note: due to QString's thread unsafe refcounting, special precautions have to be + * taken to avoid memory corruption, while still maintaining some level of efficiency. + * We deepCopy strings, unless we are in the same thread that *first* used + * AtomicStrings. Also, deletions from other threads are delayed until that first thread + * calls AtomicString again. Thus, we would appear to leak memory if many AtomicStrings + * are deleted in a different thread than the main thread, and the main thread would + * never call AtomicString again. But this is unlikely since the GUI thread is the one + * manipulating AtomicStrings mostly. You can call the static method + * AtomicString::isMainString first thing in the app to make sure the GUI thread is + * identified correctly. This workaround can be removed with QT4. + * + * @author Gábor Lehel + */ + +#ifndef AMAROK_ATOMICSTRING_H +#define AMAROK_ATOMICSTRING_H + +#include "config.h" +#include +#include "amarok_export.h" + +#include +#include +#include + +class LIBAMAROK_EXPORT AtomicString +{ +public: + /** + * Constructs an empty string. + * @see isEmpty + */ + AtomicString(); + + /** + * Constructs a copy of \p string. This operation takes constant time. + * @param string the string to copy + * @see operator= + */ + AtomicString( const AtomicString &other ); + + /** + * Constructs a copy of \p string. + * @param string the string to copy + */ + AtomicString( const QString &string ); + + /** + * Destroys the string. + * Note: this isn't virtual, to halve sizeof(). + */ + ~AtomicString(); + + /** + * Makes this string a copy of \p string. This operation takes constant time. + * @param string the string to copy + */ + AtomicString &operator=( const AtomicString &other ); + + /** + * This operation takes constant time. + * @return whether this string and \p string are equivalent + */ + bool operator==( const AtomicString &other ) const { return m_string == other.m_string; } + + + bool operator<( const AtomicString &other ) const { return m_string < other.m_string; } + +// bool operator!=( const AtomicString &other ) const { return m_string != other.m_string; } + + /** + * Returns a reference to this string, avoiding copies if possible. + * + * @return the string. + */ + QString string() const; + + /** + * Implicitly casts to a QString. + * @return the string + */ + inline operator QString() const { return string(); } + + /** + * Useful for threading. + * @return a deep copy of the string + */ + QString deepCopy() const; + + /** + * For convenience. Equivalent to isNull(). + * @return whether the string is empty + * @see isNull + */ + bool isEmpty() const; + + /** + * For convenience. Equivalent to isEmpty(). + * @return whether the string is empty + * @see isEmpty + */ + inline bool isNull() const { return isEmpty(); } + + /** + * Returns the internal pointer to the string. + * Guaranteed to be equivalent for equivalent strings, and different for + * different ones. This can be useful for certain kinds of hacks, but + * shouldn't normally be used. + * + * Note: DO NOT COPY this pointer with QString() or QString=. It is not + * thread safe to do it (QString internal refcount) + * @return the internal pointer to the string + */ + const QString *ptr() const; + + /** + * For convenience, so you can do atomicstring->QStringfunction(), + * instead of atomicstring.string().QStringfunction(). The same warning + * applies as for the above ptr() function. + */ + inline const QString *operator->() const { return ptr(); } + + /** + * For debugging purposes. + * @return the number of nonempty AtomicStrings equivalent to this one + */ + uint refcount() const; + + /** + * If called first thing in the app, this makes sure that AtomicString optimizes + * string usage for the main thread. + * @return true if this thread is considered the "main thread". + */ + static bool isMainThread(); + + +private: + struct less + { + bool operator()( const QString *a, const QString *b ) const + { return *a < *b; } + }; + typedef std::set set_type; + + class Data; + friend class Data; + + void ref( Data* ); + void deref( Data* ); + + static void checkLazyDeletes(); + + Data *m_string; + + // static data + static set_type s_store; // main string store + static QPtrList s_lazyDeletes; // strings scheduled for deletion + // by main thread + static QMutex s_storeMutex; // protects the static data above +}; + +#endif diff --git a/amarok/src/atomicstring_unittest.cpp b/amarok/src/atomicstring_unittest.cpp new file mode 100644 index 00000000..1f5d93e3 --- /dev/null +++ b/amarok/src/atomicstring_unittest.cpp @@ -0,0 +1,62 @@ +// Ovidiu Gheorghioiu , (C) 2006 +// License: GNU General Public License V2 +// +// Stress-test the AtomicString class for thread safety. Run on SMP for maximum exposure. +#include +#include +#include + +#include "atomicstring.h" + +void * +Worker(void *num) { + srand( reinterpret_cast( num ) ); + QString base = "str"; + // create 5 strings, destroy them, copy them around + const int kNumStrings = 5; + AtomicString *atStrings[kNumStrings * 2]; + for( int i = 0; i < kNumStrings * 2; i++ ) atStrings[i] = NULL; + const int kIterations = 100000; + for( int i = 0; i < kIterations; i++ ) { + int k = rand() % (kNumStrings * 2); + if( atStrings[k] == NULL ) { + // the upper half are sometimes copies of the corresponding + // lower half strings + if( k >= kNumStrings && atStrings[k % kNumStrings] != NULL ) { + atStrings[k] = new AtomicString( *atStrings[k % kNumStrings] ); + } else { + atStrings[k] = new AtomicString( base + QString::number( k ) ); + } + } else { + // check the string; could be either upper or lower + QString str = atStrings[k]->string(); + if( str != base + QString::number( k ) + && str != base + QString::number( k % kNumStrings ) ) { + qFatal( "unexpected atStrings[%d]: %s", k, str.ascii() ); + } + delete atStrings[k]; + atStrings[k] = NULL; + } + } + + return NULL; +} + +int main() { + const int kWorkers = 2; + + pthread_t workers[kWorkers]; + + for( int i = 0; i < kWorkers; i++ ) { + if( pthread_create(& workers[i], NULL, + & Worker, + reinterpret_cast(i)) + != 0) + qFatal( "Could not create thread %d", i ); + } + + for( int i = 0; i < kWorkers; i++ ) { + void *thread_return; + pthread_join( workers[i], &thread_return ); + } +} diff --git a/amarok/src/atomicurl.cpp b/amarok/src/atomicurl.cpp new file mode 100644 index 00000000..620ded36 --- /dev/null +++ b/amarok/src/atomicurl.cpp @@ -0,0 +1,127 @@ +/* + Copyright (c) 2005 Gábor Lehel + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include +#include +#include "debug.h" + +#include "atomicurl.h" + +AtomicURL::AtomicURL() { } + +AtomicURL::AtomicURL( const AtomicURL &other ) +{ + m_beginning = other.m_beginning; + m_directory = other.m_directory; + m_filename = other.m_filename; + m_end = other.m_end; +} + +AtomicURL::AtomicURL( const KURL &url ) +{ + if( url.isEmpty() ) + return; + + QString s = url.protocol() + "://"; + QString host = url.host(); + if( url.hasUser() ) + { + s += url.user(); + host.prepend("@"); + } + if( url.hasPass() ) + s += ':' + url.pass(); + if( url.port() ) + host += QString(":") + QString::number( url.port() ); + + m_beginning = s + host; + m_directory = url.directory(); + m_filename = url.fileName(); + m_end = url.query(); + if( url.hasRef() ) + m_end += QString("#") + url.ref(); + if (url != this->url()) + { + debug() << "from: " << url << endl; + debug() << "to: " << this->url() << endl; + } +} + +AtomicURL::~AtomicURL() { } + +AtomicURL &AtomicURL::operator=( const AtomicURL &other ) +{ + m_beginning = other.m_beginning; + m_directory = other.m_directory; + m_filename = other.m_filename; + return *this; +} + +bool AtomicURL::operator==( const AtomicURL &other ) const +{ + return m_filename == other.m_filename + && m_directory == other.m_directory + && m_beginning == other.m_beginning + && m_end == other.m_end; +} + +QString AtomicURL::string() const +{ + return m_beginning + path() + m_end; +} + +KURL AtomicURL::url() const +{ + if( isEmpty() ) + return KURL(); + + return KURL( string(), 106 ); +} + +bool AtomicURL::isEmpty() const +{ + return m_beginning->isEmpty() + && m_directory->isEmpty() + && m_filename.isEmpty() + && m_end.isEmpty(); +} + +void AtomicURL::setPath( const QString &path ) +{ + KURL url; + url.setPath( path ); + if( m_beginning->isEmpty() ) + *this = url; + else + { + m_directory = url.directory(); + m_filename = url.fileName(); + } +} + +QString AtomicURL::path() const +{ + if( !m_filename.isEmpty() && !m_directory->endsWith("/") ) + return m_directory + '/' + m_filename; + return m_directory + m_filename; +} + +QString AtomicURL::fileName() const { return m_filename; } + +QString AtomicURL::directory() const { return m_directory; } diff --git a/amarok/src/atomicurl.h b/amarok/src/atomicurl.h new file mode 100644 index 00000000..1f45ea7f --- /dev/null +++ b/amarok/src/atomicurl.h @@ -0,0 +1,72 @@ +/* + Copyright (c) 2005 Gábor Lehel + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +/** + While having two equivalent URLs is usually rare, parts of many URLs (the directories, mostly), + are often equivalent. AtomicURL uses AtomicStrings internally for the separate parts to try and + reduce memory usage for large numbers of URLs. +**/ + +#ifndef AMAROK_ATOMICURL_H +#define AMAROK_ATOMICURL_H + +#include +#include "atomicstring.h" +#include "amarok_export.h" + +class KURL; + +class LIBAMAROK_EXPORT AtomicURL +{ + AtomicString m_beginning; + AtomicString m_directory; + QString m_filename; + QString m_end; + +public: + AtomicURL(); + + AtomicURL( const AtomicURL &other ); + + AtomicURL( const KURL &url ); + + virtual ~AtomicURL(); + + AtomicURL &operator=( const AtomicURL &other ); + + bool operator==( const AtomicURL &other ) const; + + QString string() const; + + KURL url() const; + + operator KURL() const { return url(); } + + bool isEmpty() const; + + void setPath( const QString &path ); + + QString path() const; + + QString fileName() const; + + QString directory() const; +}; + +#endif diff --git a/amarok/src/bcpp.cfg b/amarok/src/bcpp.cfg new file mode 100644 index 00000000..a580116e --- /dev/null +++ b/amarok/src/bcpp.cfg @@ -0,0 +1,7 @@ +function_spacing = 3 +use_tabs = no +indent_spacing = 4 +place_brace_on_new_line = yes +backup_file = yes +ascii_chars_only = no + diff --git a/amarok/src/browserToolBar.h b/amarok/src/browserToolBar.h new file mode 100644 index 00000000..54cafbfc --- /dev/null +++ b/amarok/src/browserToolBar.h @@ -0,0 +1,32 @@ +/*************************************************************************** + * Copyright (C) 2004, 2005 Max Howell * + * * + * 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. * + * * + ***************************************************************************/ + +#ifndef BROWSER_TOOLBAR_H +#define BROWSER_TOOLBAR_H + +#include + +namespace Browser +{ + class ToolBar : public KToolBar + { + public: + ToolBar( QWidget *parent ) + : KToolBar( parent, "NotMainToolBar" ) + { + setMovingEnabled(false); + setFlat(true); + setIconSize( 16 ); + setEnableContextMenu( false ); + } + }; +} + +#endif diff --git a/amarok/src/browserbar.cpp b/amarok/src/browserbar.cpp new file mode 100644 index 00000000..fd32be1e --- /dev/null +++ b/amarok/src/browserbar.cpp @@ -0,0 +1,408 @@ +/*************************************************************************** + * Copyright (C) 2004, 2005 Max Howell * + * * + * 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. * + * * + ***************************************************************************/ + +#include "amarok.h" +#include "amarokconfig.h" +#include "browserbar.h" +#include "debug.h" +#include "enginecontroller.h" +#include "multitabbar.h" //m_tabBar + +#include //kapp +#include +#include //multiTabBar icons +#include + +#include //for resize cursor +#include +#include //m_mapper +#include //Amarok::Splitter +#include + + +// we emulate a qsplitter, mostly for historic reasons, but there are still a few advantages +// mostly we can stop the browserbar getting resized too small so that switching browser looks wrong + + +namespace Amarok +{ + class Splitter : public QWidget { + public: + Splitter( BrowserBar *w ) : QWidget( w, "divider" ) + { + setCursor( QCursor(SplitHCursor) ); + styleChange( style() ); + } + + virtual void paintEvent( QPaintEvent* ) + { + QPainter p( this ); + parentWidget()->style().drawPrimitive( QStyle::PE_Splitter, &p, rect(), colorGroup(), QStyle::Style_Horizontal ); + } + + virtual void styleChange( QStyle& ) + { + setFixedWidth( style().pixelMetric( QStyle::PM_SplitterWidth, this ) ); + } + + virtual void mouseMoveEvent( QMouseEvent *e ) + { + static_cast(parent())->mouseMovedOverSplitter( e ); + } + }; +} + +BrowserBar* BrowserBar::s_instance = 0; + +BrowserBar::BrowserBar( QWidget *parent ) + : QWidget( parent, "BrowserBar" ) + , EngineObserver( EngineController::instance() ) + , m_playlistBox( new QVBox( this ) ) + , m_divider( new Amarok::Splitter( this ) ) + , m_browserBox( new QVBox( this ) ) + , m_currentIndex( -1 ) + , m_lastIndex( -1 ) + , m_mapper( new QSignalMapper( this ) ) +{ + m_tabManagementButton = new QPushButton( SmallIconSet(Amarok::icon( "configure" )), 0, this, "tab_managment_button" ); + connect (m_tabManagementButton, SIGNAL(clicked()), SLOT(showBrowserSelectionMenu())); + m_tabManagementButton->setIsMenuButton ( true ); //deprecated, but since I cannot add menu directly to button it is needed. + + QToolTip::add (m_tabManagementButton, i18n("Manage tabs")); + + + m_tabBar = new MultiTabBar( MultiTabBar::Vertical, this ); + + + m_tabManagementButton->setFixedWidth(m_tabBar->sizeHint().width()); + m_tabManagementButton->setFixedHeight(m_tabBar->sizeHint().width()); + + + s_instance = this; + m_pos = m_tabBar->sizeHint().width() + 5; //5 = esthetic spacing + + m_tabBar->setStyle( MultiTabBar::AMAROK ); + m_tabBar->setPosition( MultiTabBar::Left ); + m_tabBar->showActiveTabTexts( true ); + m_tabBar->setFixedWidth( m_pos ); + m_tabBar->move( 0, 25 ); + + QVBoxLayout *layout = new QVBoxLayout( m_browserBox ); + layout->addSpacing( 3 ); // aesthetics + layout->setAutoAdd( true ); + + m_browserBox->move( m_pos, 0 ); + m_browserBox->hide(); + m_divider->hide(); + m_playlistBox->setSpacing( 1 ); + + connect( m_mapper, SIGNAL(mapped( int )), SLOT(showHideBrowser( int )) ); + + + + + + //m_tabBar->appendButton( Amarok::icon( "configure" ), 1, 0, QString::null ); + +} + +BrowserBar::~BrowserBar() +{ + KConfig* const config = Amarok::config( "BrowserBar" ); + config->writeEntry( "CurrentPane", m_currentIndex != -1 ? QString(currentBrowser()->name()) : QString::null ); + config->writeEntry( "Width", m_browserBox->width() ); +} + +void +BrowserBar::makeDropProxy( const QString &name, DropProxyTarget *finalTarget ) +{ + int id = m_browserIds[name]; + MultiTabBarButton *button = m_tabBar->tab( id ); + if( button ) + button->proxyDrops( finalTarget ); +} + +int +BrowserBar::restoreWidth() +{ + const int index = indexForName( Amarok::config( "BrowserBar" )->readEntry( "CurrentPane" ) ); + const int width = Amarok::config( "BrowserBar" )->readNumEntry( "Width", browser( index )->sizeHint().width() ); + + m_browserBox->resize( width, height() ); + m_pos = m_browserBox->width() + m_tabBar->width(); + + return index; +} + +void +BrowserBar::polish() +{ + DEBUG_FUNC_INFO + + QWidget::polish(); + + uint M = 0; + foreachType( BrowserList, m_browsers ) { + const uint m = (*it)->minimumWidth(); + if (m > M) + M = m; + if (m > 250) { + warning() << "Browser is too large, mxcl says castrate the developer: " << (*it)->name() << ", " << M << endl; + M = 250; + } + } + + m_browserBox->setMinimumWidth( M ); + const int index = restoreWidth(); + + if (index != -1) + // if we did it for -1 it ruins the browserBox size + showHideBrowser( index ); +} + +void +BrowserBar::adjustWidgetSizes() +{ + //TODO set the geometry of the PlaylistWindow before + // the browsers are loaded so this isn't called twice + + const uint w = width(); + const uint h = height(); + const uint maxW = maxBrowserWidth(); + const uint p = (m_pos < maxW) ? m_pos : maxW; + const uint ppw = p + m_divider->width(); + const uint tbw = m_tabBar->width(); + + m_divider->move( p, 0 ); + + const uint offset = !m_divider->isHidden() ? ppw : tbw; + + m_browserBox->resize( p - tbw, h ); + m_playlistBox->setGeometry( offset, 0, w - offset, h ); +} + +void +BrowserBar::mouseMovedOverSplitter( QMouseEvent *e ) +{ + const uint oldPos = m_pos; + const uint newPos = mapFromGlobal( e->globalPos() ).x(); + const uint minWidth = m_tabBar->width() + m_browserBox->minimumWidth(); + const uint maxWidth = maxBrowserWidth(); + + if( newPos < minWidth ) + m_pos = minWidth; + + else if( newPos > maxWidth ) + m_pos = maxWidth; + + else + m_pos = newPos; + + if( m_pos != oldPos ) + adjustWidgetSizes(); +} + +bool +BrowserBar::event( QEvent *e ) +{ + switch( e->type() ) + { + case QEvent::LayoutHint: + //FIXME include browserholder width + setMinimumWidth( + m_tabBar->minimumWidth() + + m_divider->minimumWidth() + + m_browserBox->width() + + m_playlistBox->minimumWidth() ); + break; + + case QEvent::Resize: +// DEBUG_LINE_INFO + + m_divider->resize( 0, height() ); //Qt will set width + m_tabBar->resize( 0, height() ); //Qt will set width + + adjustWidgetSizes(); + + return true; + + default: + ; + } + + return QWidget::event( e ); +} + +void +BrowserBar::addBrowser( const QString &identifier, QWidget *widget, const QString &title, const QString& icon ) +{ + const int id = m_tabBar->tabs()->count(); // the next available id + const QString name( widget->name() ); + m_browserIds[name] = id; + QWidget *tab; + + widget->reparent( m_browserBox, QPoint() ); + widget->hide(); + + m_tabBar->appendTab( SmallIcon( icon ), id, title, identifier ); + tab = m_tabBar->tab( id ); + tab->setFocusPolicy( QWidget::NoFocus ); //FIXME you can focus on the tab, but they respond to no input! + + //we use a SignalMapper to show/hide the corresponding browser when tabs are clicked + connect( tab, SIGNAL(clicked()), m_mapper, SLOT(map()) ); + m_mapper->setMapping( tab, id ); + connect( tab, SIGNAL(initiateDrag ( int ) ), this, SLOT( showBrowser( int )) ); + + m_browsers.push_back( widget ); +} + +void +BrowserBar::removeMediaBrowser( QWidget *widget ) +{ + BrowserList::iterator it = qFind( m_browsers.begin(), m_browsers.end(), widget ); + if( it != m_browsers.end() ) + m_browsers.erase( it ); + QWidget *tab; + tab = m_tabBar->tab( m_browserIds["MediaBrowser"] ); + m_mapper->removeMappings( tab ); + m_tabBar->removeTab( m_browserIds["MediaBrowser"] ); +} + +void +BrowserBar::showHideBrowser( int index ) +{ + const int prevIndex = m_currentIndex; + + if( m_currentIndex != -1 ) { + ///first we need to hide the currentBrowser + + m_currentIndex = -1; //to prevent race condition, see CVS history + + m_browsers[prevIndex]->hide(); + m_tabBar->setTab( prevIndex, false ); + } + + if( index == prevIndex ) { + ///close the BrowserBar + + m_browserBox->hide(); + m_divider->hide(); + + adjustWidgetSizes(); + } + + else if( (uint)index < m_browsers.count() ) { + ///open up target + + QWidget* const target = m_browsers[index]; + m_currentIndex = index; + + m_divider->show(); + target->show(); + target->setFocus(); + m_browserBox->show(); + m_tabBar->setTab( index, true ); + + if( prevIndex == -1 ) { + // we need to show the browserBox + // m_pos dictates how everything will be sized in adjustWidgetSizes() + m_pos = m_browserBox->width() + m_tabBar->width(); + adjustWidgetSizes(); + } + } + + emit browserActivated( index ); +} + +void +BrowserBar::showHideVisibleBrowser( int index ) +{ + int realindex = -1; + QPtrList tabs = *m_tabBar->tabs(); + for( int i = 0, n = tabs.count(); i < n; ++i ) + { + if( tabs.at( i )->visible() ) + index--; + if( index < 0 ) + { + realindex = i; + break; + } + } + + if( realindex >= 0 ) + showHideBrowser( realindex ); +} + +QWidget* +BrowserBar::browser( const QString &name ) const +{ + foreachType( BrowserList, m_browsers ) + if( name == (*it)->name() ) + return *it; + + return 0; +} + +int +BrowserBar::visibleCount() const +{ + int num = 0; + QPtrList tabs = *m_tabBar->tabs(); + for( int i = 0, n = tabs.count(); i < n; ++i ) + { + if( tabs.at( i )->visible() ) + num++; + } + + return num; +} + +int +BrowserBar::indexForName( const QString &name ) const +{ + for( uint x = 0; x < m_browsers.count(); ++x ) + if( name == m_browsers[x]->name() ) + return x; + return -1; +} + +void +BrowserBar::showBrowserSelectionMenu() +{ + m_tabBar->showTabSelectionMenu(mapToGlobal(QPoint(m_tabManagementButton->pos().x(), m_tabManagementButton->pos().y() +m_tabManagementButton->height() ))); +} + +void +BrowserBar::engineStateChanged( Engine::State state, Engine::State oldState ) +{ + if( !AmarokConfig::autoShowContextBrowser() || m_currentIndex == -1 ) + return; + + switch( state ) { + case Engine::Playing: + + if( oldState != Engine::Paused && m_currentIndex != -1 ) { + m_lastIndex = m_currentIndex; + showBrowser( "ContextBrowser" ); + } + break; + + case Engine::Empty: + + if( m_lastIndex >= 0 ) + showBrowser( m_lastIndex ); + + default: + ; + } +} + +#include "browserbar.moc" diff --git a/amarok/src/browserbar.h b/amarok/src/browserbar.h new file mode 100644 index 00000000..ea2fcefd --- /dev/null +++ b/amarok/src/browserbar.h @@ -0,0 +1,103 @@ +/*************************************************************************** + * Copyright (C) 2004, 2005 Max Howell * + * * + * 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. * + * * + ***************************************************************************/ + +#ifndef BROWSERBAR_H +#define BROWSERBAR_H + +#include "amarok_export.h" //LIBAMAROK_EXPORT +#include "engineobserver.h" //baseclass + +#include //baseclass +#include //stack allocated +#include //stack allocated +#include + +typedef QValueVector BrowserList; +typedef QMap BrowserIdMap; + +class MultiTabBar; +class MultiTabBarTab; +class DropProxyTarget; +class KURL; +class QSignalMapper; +class QVBox; + + +class BrowserBar : public QWidget, public EngineObserver +{ + Q_OBJECT + +public: + BrowserBar( QWidget *parent ); + ~BrowserBar(); + + LIBAMAROK_EXPORT static BrowserBar* instance() { return s_instance; } + QVBox *container() const { return m_playlistBox; } + QVBox *browserBox() const { return m_browserBox; } + + QWidget *browser( const QString& ) const; + QWidget *browser( int index ) const { if( index < 0 ) index = 0; return m_browsers[index]; } + QWidget *currentBrowser() const { return m_currentIndex < 0 ? 0 : browser( m_currentIndex ); } + + int count() const { return m_browsers.count(); } + int visibleCount() const; + + void addBrowser( const QString &identifier, QWidget*, const QString&, const QString& ); + void removeMediaBrowser( QWidget *widget ); + int indexForName( const QString& ) const; + int restoreWidth(); + + /// for internal use + void mouseMovedOverSplitter( QMouseEvent* ); + void makeDropProxy( const QString &browserName, DropProxyTarget *finalTarget ); + +protected: + virtual bool event( QEvent* ); + virtual void polish(); + +protected: + virtual void engineStateChanged( Engine::State, Engine::State = Engine::Empty ); + +public slots: + void showBrowser( const QString& name ) { showBrowser( indexForName( name ) ); } + void showBrowser( int index ) { if( index != m_currentIndex ) showHideBrowser( index ); } + void showHideBrowser( int ); + void showHideVisibleBrowser( int ); + void closeCurrentBrowser() { showHideBrowser( m_currentIndex ); } + void showBrowserSelectionMenu(); + +signals: + void browserActivated( int ); + +private: + void adjustWidgetSizes(); + uint maxBrowserWidth() const { return width() * 2 / 3; } + + static const int DEFAULT_HEIGHT = 50; + + LIBAMAROK_EXPORT static BrowserBar *s_instance; + uint m_pos; ///the x-axis position of m_divider + QVBox *m_playlistBox; ///parent to playlist, playlist filter and toolbar + QWidget *m_divider; ///a qsplitter like widget + MultiTabBar *m_tabBar; + BrowserList m_browsers; + BrowserIdMap m_browserIds; + QVBox *m_browserBox; ///parent widget to the browsers + int m_currentIndex; + int m_lastIndex; + QSignalMapper *m_mapper; ///maps tab clicks to browsers + + QPushButton *m_tabManagementButton; + + + +}; + +#endif diff --git a/amarok/src/clicklineedit.cpp b/amarok/src/clicklineedit.cpp new file mode 100644 index 00000000..d9dc62f5 --- /dev/null +++ b/amarok/src/clicklineedit.cpp @@ -0,0 +1,104 @@ +/* + This file is part of libkdepim. + Copyright (c) 2004 Daniel Molkentin + based on code by Cornelius Schumacher + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + + +#include "clicklineedit.h" + +#include "qpainter.h" + + +ClickLineEdit::ClickLineEdit( const QString &msg, QWidget *parent, const char* name ) : + KLineEdit( parent, name ) +{ + mDrawClickMsg = true; + setClickMessage( msg ); +} + + +///////////////////////////////////////////////////////////////////////////////////// +// PUBLIC +///////////////////////////////////////////////////////////////////////////////////// + +void ClickLineEdit::setClickMessage( const QString &msg ) +{ + mClickMessage = msg; + repaint(); +} + + +void ClickLineEdit::setText( const QString &txt ) +{ + mDrawClickMsg = txt.isEmpty(); + repaint(); + KLineEdit::setText( txt ); +} + + +///////////////////////////////////////////////////////////////////////////////////// +// PROTECTED +///////////////////////////////////////////////////////////////////////////////////// + +//#include +void ClickLineEdit::drawContents( QPainter *p ) +{ + KLineEdit::drawContents( p ); + + if ( mDrawClickMsg == true && !hasFocus() ) { + QPen tmp = p->pen(); + p->setPen( palette().color( QPalette::Disabled, QColorGroup::Text ) ); + QRect cr = contentsRect(); + + //p->drawPixmap( 3, 3, SmallIcon("filter") ); + + // Add two pixel margin on the left side + cr.rLeft() += 3; + p->drawText( cr, AlignAuto | AlignVCenter, mClickMessage ); + p->setPen( tmp ); + } +} + +void ClickLineEdit::dropEvent( QDropEvent *ev ) +{ + mDrawClickMsg = false; + KLineEdit::dropEvent( ev ); +} + + +void ClickLineEdit::focusInEvent( QFocusEvent *ev ) +{ + if ( mDrawClickMsg == true ) { + mDrawClickMsg = false; + repaint(); + } + QLineEdit::focusInEvent( ev ); +} + + +void ClickLineEdit::focusOutEvent( QFocusEvent *ev ) +{ + if ( text().isEmpty() ) { + mDrawClickMsg = true; + repaint(); + } + QLineEdit::focusOutEvent( ev ); +} + +#include "clicklineedit.moc" diff --git a/amarok/src/clicklineedit.h b/amarok/src/clicklineedit.h new file mode 100644 index 00000000..2912c087 --- /dev/null +++ b/amarok/src/clicklineedit.h @@ -0,0 +1,59 @@ +/* + This file is part of libkdepim. + Copyright (c) 2004 Daniel Molkentin + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef CLICKLINEEDIT_H +#define CLICKLINEEDIT_H + +#include + +/** + This class provides a KLineEdit which contains a grayed-out hinting + text as long as the user didn't enter any text + + @short LineEdit with customizable "Click here" text + @author Daniel Molkentin +*/ +class ClickLineEdit : public KLineEdit +{ + Q_OBJECT + Q_PROPERTY( QString clickMessage READ clickMessage WRITE setClickMessage ) + public: + ClickLineEdit( const QString &msg, QWidget *parent, const char* name = 0 ); + + void setClickMessage( const QString &msg ); + QString clickMessage() const { return mClickMessage; } + + virtual void setText( const QString& txt ); + + protected: + virtual void drawContents( QPainter *p ); + virtual void dropEvent( QDropEvent *ev ); + virtual void focusInEvent( QFocusEvent *ev ); + virtual void focusOutEvent( QFocusEvent *ev ); + + private: + QString mClickMessage; + bool mDrawClickMsg; + +}; + +#endif // CLICKLINEEDIT_H + + diff --git a/amarok/src/collectionbrowser.cpp b/amarok/src/collectionbrowser.cpp new file mode 100644 index 00000000..57a55e0d --- /dev/null +++ b/amarok/src/collectionbrowser.cpp @@ -0,0 +1,4718 @@ +// (c) 2004 Mark Kretschmann +// (c) 2004 Christian Muehlhaeuser +// (c) 2005 GÁbor Lehel +// (c) 2005 Alexandre Pereira de Oliveira +// (c) 2005 Christan Baumgart +// (c) 2006 Joe Rabinoff +// See COPYING file for licensing information. + +#include + +#include "amarok.h" +#include "amarokconfig.h" +#include "browserbar.h" +#include "browserToolBar.h" +#include "clicklineedit.h" +#include "collectionbrowser.h" +#include "collectiondb.h" +#include "covermanager.h" +#include "debug.h" +#include "deletedialog.h" +#include "directorylist.h" +#include "editfilterdialog.h" +#include "k3bexporter.h" +#include "mediabrowser.h" +#include "metabundle.h" +#include "mountpointmanager.h" +#include "organizecollectiondialog.h" +#include "playlist.h" //insertMedia() +#include "playlistbrowser.h" +#include "starmanager.h" +#include "statusbar.h" +#include "tagdialog.h" +#include "threadmanager.h" +#include "qstringx.h" + +#include //TagLib::File::isWritable + +#include //CollectionView ctor + +#include +#include +#include +#include //infobox +#include +#include +#include +#include +#include +#include +#include +#include //QToolTip::add() +#include +#include + +#include +#include //kapp +#include +#include +#include +#include +#include +#include //renderView() +#include +#include +#include +#include //ctor +#include //dragObject() +#include +#include + +extern "C" +{ + #if KDE_VERSION < KDE_MAKE_VERSION(3,3,91) + #include //ControlMask in contentsDragMoveEvent() + #endif +} + +using namespace CollectionBrowserIds; + +namespace Amarok { extern KConfig *config( const QString& ); } + +class CoverFetcher; + +CollectionBrowser *CollectionBrowser::s_instance = 0; + +CollectionBrowser::CollectionBrowser( const char* name ) + : QVBox( 0, name ) + , m_cat1Menu( new KPopupMenu( this ) ) + , m_cat2Menu( new KPopupMenu( this ) ) + , m_cat3Menu( new KPopupMenu( this ) ) + , m_timer( new QTimer( this ) ) + , m_returnPressed( false ) +{ + s_instance = this; + + setSpacing( 4 ); + + m_toolbar = new Browser::ToolBar( this ); + + { // + KToolBarButton *button; + KToolBar* searchToolBar = new Browser::ToolBar( this ); + + button = new KToolBarButton( "locationbar_erase", 0, searchToolBar ); + m_searchEdit = new ClickLineEdit( i18n( "Enter search terms here" ), searchToolBar ); + m_searchEdit->installEventFilter( this ); + KPushButton *filterButton = new KPushButton("...", searchToolBar, "filter"); + searchToolBar->setStretchableWidget( m_searchEdit ); + + m_searchEdit->setFrame( QFrame::Sunken ); + connect( button, SIGNAL( clicked() ), SLOT( slotClearFilter() ) ); + connect( filterButton, SIGNAL( clicked() ), SLOT( slotEditFilter() ) ); + + QToolTip::add( button, i18n( "Clear search field" ) ); + QToolTip::add( m_searchEdit, i18n( "Enter space-separated terms to search in the collection" ) ); + QToolTip::add( filterButton, i18n( "Click to edit collection filter" ) ); + } // + + + // We put a little toolbar for the forward/back buttons for iPod + // navigation to the right of m_timeFilter. This toolbar is + // hidden when not in iPod browsing mode; it is shown and hidden + // in CollectionView::setViewMode(). m_ipodHbox holds m_timeFilter + // and m_ipodToolbar + m_ipodHbox = new QHBox( this ); + m_ipodHbox->setSpacing( 7 ); // looks better + + m_timeFilter = new KComboBox( m_ipodHbox, "timeFilter" ); + m_ipodHbox->setStretchFactor( m_timeFilter, 1 ); + // Allow the combobox to shrink so the iPod buttons are still visible + m_timeFilter->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed ); + m_timeFilter->insertItem( i18n( "Entire Collection" ) ); + m_timeFilter->insertItem( i18n( "Added Today" ) ); + m_timeFilter->insertItem( i18n( "Added Within One Week" ) ); + m_timeFilter->insertItem( i18n( "Added Within One Month" ) ); + m_timeFilter->insertItem( i18n( "Added Within Three Months" ) ); + m_timeFilter->insertItem( i18n( "Added Within One Year" ) ); + + + // m_ipodToolbar just holds the forward and back buttons, which are + // plugged below + m_ipodToolbar = new Browser::ToolBar( m_ipodHbox ); + m_ipodHbox->setStretchFactor( m_ipodToolbar, 0 ); + m_ipodToolbar->setIconText( KToolBar::IconOnly, false ); + + + KActionCollection* ac = new KActionCollection( this ); + + m_view = new CollectionView( this ); + m_view->installEventFilter( this ); + + m_configureAction = new KAction( i18n( "Configure Folders" ), Amarok::icon( "configure" ), 0, this, SLOT( setupDirs() ), ac, "Configure" ); + m_treeViewAction = new KRadioAction( i18n( "Tree View" ), "view_tree", 0, m_view, SLOT( setTreeMode() ), ac, "Tree View" ); + m_flatViewAction = new KRadioAction( i18n( "Flat View" ), "view_detailed", 0, m_view, SLOT( setFlatMode() ), ac, "Flat View" ); + m_ipodViewAction = new KRadioAction( i18n( "iPod View" ), Amarok::icon("device"), 0, m_view, SLOT( setIpodMode() ), ac, "iPod View" ); + m_treeViewAction->setExclusiveGroup("view mode"); + m_flatViewAction->setExclusiveGroup("view mode"); + m_ipodViewAction->setExclusiveGroup("view mode"); + switch( m_view->m_viewMode ) + { + case CollectionView::modeTreeView: + m_treeViewAction->setChecked( true ); + break; + case CollectionView::modeFlatView: + m_flatViewAction->setChecked( true ); + break; + case CollectionView::modeIpodView: + m_ipodViewAction->setChecked( true ); + break; + } + + m_showDividerAction = new KToggleAction( i18n( "Show Divider" ), "leftjust", 0, this, SLOT( toggleDivider() ), ac, "Show Divider" ); + m_showDividerAction->setChecked(m_view->m_showDivider); + + + // m_ipodIncrement and m_ipodDecrement are the actions that + // correspond to moving forward / backward in the iPod collection + // browser window; see the "For iPod-style navigation" comments below. + m_ipodDecrement = new KAction( i18n( "Browse backward" ), + QIconSet( m_view->ipodDecrementIcon(), QIconSet::Small ), + 0, m_view, SLOT( decrementDepth() ), ac, + "iPod Decrement" ); + m_ipodIncrement = new KAction( i18n( "Browse forward" ), + QIconSet( m_view->ipodIncrementIcon(), QIconSet::Small ), + 0, m_view, SLOT( incrementDepth() ), ac, + "iPod Increment" ); + m_ipodDecrement->plug( m_ipodToolbar ); + m_ipodIncrement->plug( m_ipodToolbar ); + + // Show / hide m_ipodToolbar based on the view mode + ipodToolbar( m_view->m_viewMode == CollectionView::modeIpodView ); + + + m_tagfilterMenuButton = new KActionMenu( i18n( "Group By" ), "filter", ac ); + m_tagfilterMenuButton->setDelayed( false ); + // FIXME: either both or nothing + //m_tagfilterMenuButton->setEnabled( m_view->m_viewMode == CollectionView::modeTreeView ); + //connect ( m_treeViewAction, SIGNAL ( toggled(bool) ), m_tagfilterMenuButton, SLOT( setEnabled (bool) ) ); + + layoutToolbar(); + + m_categoryMenu = m_tagfilterMenuButton->popupMenu(); + m_categoryMenu->insertItem( i18n( "Artist" ), m_view, SLOT( presetMenu( int ) ), 0, IdArtist ); + m_categoryMenu->insertItem( i18n( "Artist / Album" ), m_view, SLOT( presetMenu( int ) ), 0, IdArtistAlbum ); + m_categoryMenu->insertItem( i18n( "Artist" )+" / "+ i18n( "Year" ) + i18n( " - " ) + i18n( "Album" ), m_view, SLOT( presetMenu( int ) ), 0, IdArtistVisYearAlbum ); + m_categoryMenu->insertItem( i18n( "Album" ), m_view, SLOT( presetMenu( int ) ), 0, IdAlbum ); + m_categoryMenu->insertItem( i18n( "Genre / Artist" ), m_view, SLOT( presetMenu( int ) ), 0, IdGenreArtist ); + m_categoryMenu->insertItem( i18n( "Genre / Artist / Album" ), m_view, SLOT( presetMenu( int ) ), 0, IdGenreArtistAlbum ); + + m_categoryMenu->insertSeparator(); + + m_categoryMenu->insertItem( i18n( "&First Level" ), m_cat1Menu ); + m_categoryMenu->insertItem( i18n( "&Second Level"), m_cat2Menu ); + m_categoryMenu->insertItem( i18n( "&Third Level" ), m_cat3Menu ); + + m_cat1Menu ->insertItem( i18n( "&Album" ), m_view, SLOT( cat1Menu( int ) ), 0, IdAlbum ); + m_cat1Menu ->insertItem( i18n( "(Y&ear) - Album" ), m_view, SLOT( cat1Menu( int ) ), 0, IdVisYearAlbum); + m_cat1Menu ->insertItem( i18n( "A&rtist"), m_view, SLOT( cat1Menu( int ) ), 0, IdArtist ); + m_cat1Menu ->insertItem( i18n( "&Composer"), m_view, SLOT( cat1Menu( int ) ), 0, IdComposer ); + m_cat1Menu ->insertItem( i18n( "&Genre" ), m_view, SLOT( cat1Menu( int ) ), 0, IdGenre ); + m_cat1Menu ->insertItem( i18n( "&Year" ), m_view, SLOT( cat1Menu( int ) ), 0, IdYear ); + m_cat1Menu ->insertItem( i18n( "&Label" ), m_view, SLOT( cat1Menu( int ) ), 0, IdLabel ); + + m_cat2Menu ->insertItem( i18n( "&None" ), m_view, SLOT( cat2Menu( int ) ), 0, IdNone ); + m_cat2Menu ->insertSeparator(); + m_cat2Menu ->insertItem( i18n( "&Album" ), m_view, SLOT( cat2Menu( int ) ), 0, IdAlbum ); + m_cat2Menu ->insertItem( i18n( "(Y&ear) - Album" ), m_view, SLOT( cat2Menu( int ) ), 0, IdVisYearAlbum); + m_cat2Menu ->insertItem( i18n( "A&rtist" ), m_view, SLOT( cat2Menu( int ) ), 0, IdArtist ); + m_cat2Menu ->insertItem( i18n( "&Composer"), m_view, SLOT( cat2Menu( int ) ), 0, IdComposer ); + m_cat2Menu ->insertItem( i18n( "&Genre" ), m_view, SLOT( cat2Menu( int ) ), 0, IdGenre ); + m_cat2Menu ->insertItem( i18n( "&Year" ), m_view, SLOT( cat2Menu( int ) ), 0, IdYear ); + m_cat2Menu ->insertItem( i18n( "&Label" ), m_view, SLOT( cat2Menu( int ) ), 0, IdLabel ); + + m_cat3Menu ->insertItem( i18n( "&None" ), m_view, SLOT( cat3Menu( int ) ), 0, IdNone ); + m_cat3Menu ->insertSeparator(); + m_cat3Menu ->insertItem( i18n( "A&lbum" ), m_view, SLOT( cat3Menu( int ) ), 0, IdAlbum ); + m_cat3Menu ->insertItem( i18n( "(Y&ear) - Album" ), m_view, SLOT( cat3Menu( int ) ), 0, IdVisYearAlbum); + m_cat3Menu ->insertItem( i18n( "A&rtist" ), m_view, SLOT( cat3Menu( int ) ), 0, IdArtist ); + m_cat3Menu ->insertItem( i18n( "&Composer"), m_view, SLOT( cat3Menu( int ) ), 0, IdComposer ); + m_cat3Menu ->insertItem( i18n( "&Genre" ), m_view, SLOT( cat3Menu( int ) ), 0, IdGenre ); + m_cat3Menu ->insertItem( i18n( "&Year" ), m_view, SLOT( cat3Menu( int ) ), 0, IdYear ); + m_cat3Menu ->insertItem( i18n( "&Label" ), m_view, SLOT( cat3Menu( int ) ), 0, IdLabel ); + + m_view->cat1Menu( m_view->m_cat1, false ); + m_view->cat2Menu( m_view->m_cat2, false ); + m_view->cat3Menu( m_view->m_cat3, false ); + m_view->setViewMode( m_view->m_viewMode ); + + connect( m_timer, SIGNAL( timeout() ), SLOT( slotSetFilter() ) ); + connect( m_searchEdit, SIGNAL( textChanged( const QString& ) ), SLOT( slotSetFilterTimeout() ) ); + connect( m_timeFilter, SIGNAL( activated( int ) ), SLOT( slotSetFilter() ) ); + + setFocusProxy( m_view ); //default object to get focus +} + +void +CollectionBrowser::slotClearFilter() //SLOT +{ + m_searchEdit->clear(); + kapp->processEvents(); //Let the search bar redraw fully. + QTimer::singleShot( 0, this, SLOT( slotSetFilter() ) ); //Filter instantly + QTimer::singleShot( 0, m_view, SLOT( slotEnsureSelectedItemVisible() ) ); +} + +void +CollectionBrowser::slotSetFilterTimeout() //SLOT +{ + m_returnPressed = false; + m_timer->start( 280, true ); //stops the timer for us first +} + +void +CollectionBrowser::slotSetFilter() //SLOT +{ + m_timer->stop(); + m_view->m_dirty = true; + m_view->setFilter( m_searchEdit->text() ); + m_view->setTimeFilter( m_timeFilter->currentItem() ); + m_view->renderView(); + if ( m_returnPressed ) + appendSearchResults(); + m_returnPressed = false; +} + +void +CollectionBrowser::slotSetFilter( const QString &filter ) //SLOT +{ + m_searchEdit->setText( filter ); + kapp->processEvents(); //Let the search bar redraw fully. + QTimer::singleShot( 0, this, SLOT( slotSetFilter() ) ); //Filter instantly + QTimer::singleShot( 0, m_view, SLOT( slotEnsureSelectedItemVisible() ) ); +} + +void +CollectionBrowser::slotEditFilter() //SLOT +{ + EditFilterDialog *cod = new EditFilterDialog( this, false, m_searchEdit->text() ); + connect( cod, SIGNAL(filterChanged(const QString &)), SLOT(slotSetFilter(const QString &)) ); + if( cod->exec() ) + m_searchEdit->setText( cod->filter() ); + delete cod; +} + +void +CollectionBrowser::setupDirs() //SLOT +{ + m_view->setupDirs(); +} + +void +CollectionBrowser::toggleDivider() //SLOT +{ + m_view->setShowDivider( m_showDividerAction->isChecked() ); +} + +void +CollectionBrowser::appendSearchResults() +{ + //If we are not filtering, or the search string has changed recently, do nothing + if ( m_searchEdit->text().stripWhiteSpace().isEmpty() || m_timer->isActive() ) + return; + m_view->selectAll(); + Playlist::instance()->insertMedia( m_view->listSelected(), Playlist::Unique | Playlist::Append ); + m_view->clearSelection(); + slotClearFilter(); +} + +bool +CollectionBrowser::eventFilter( QObject *o, QEvent *e ) +{ + switch( e->type() ) + { + case 6/*QEvent::KeyPress*/: + + //there are a few keypresses that we intercept + + #define e static_cast(e) + + if( o == m_searchEdit ) //the search lineedit + { + switch( e->key() ) + { + case Key_Up: + case Key_Down: + case Key_PageDown: + case Key_PageUp: + m_view->setFocus(); + QApplication::sendEvent( m_view, e ); + return true; + + case Key_Escape: + slotClearFilter(); + return true; + + case Key_Return: + case Key_Enter: + if ( m_timer->isActive() ) + { + //Immediately filter and add results + m_timer->stop(); + m_returnPressed = true; + QTimer::singleShot( 0, this, SLOT( slotSetFilter() ) ); + } + else + { + //Add current results + appendSearchResults(); + } + return true; + + default: + return false; + } + } + + // (Joe Rabinoff) the code that was here which dealt with wrapping + // the selection around when Key_Up or Key_Down was pressed was + // moved to CollectionView::keyPressEvent(). That code also + // skips dividers. + + if( ( e->key() >= Key_0 && e->key() <= Key_Z ) || e->key() == Key_Backspace || e->key() == Key_Escape ) + { + m_searchEdit->setFocus(); + QApplication::sendEvent( m_searchEdit, e ); + return true; + } + #undef e + break; + + default: + break; + } + + return QVBox::eventFilter( o, e ); +} + +void +CollectionBrowser::layoutToolbar() +{ + if ( !m_toolbar ) return; + + m_toolbar->clear(); + + m_toolbar->setIconText( KToolBar::IconTextRight, false ); + m_tagfilterMenuButton->plug( m_toolbar ); + m_toolbar->setIconText( KToolBar::IconOnly, false ); + + m_toolbar->insertLineSeparator(); + m_treeViewAction->plug( m_toolbar ); + m_flatViewAction->plug( m_toolbar ); + m_ipodViewAction->plug( m_toolbar ); + m_toolbar->insertLineSeparator(); + + m_showDividerAction->plug( m_toolbar ); + m_configureAction->plug( m_toolbar ); + + //This would break things if the toolbar is too big, see bug #121915 + //setMinimumWidth( m_toolbar->sizeHint().width() + 2 ); //set a reasonable minWidth +} + + +// (De)activate the iPod toolbar when switching into and out of +// iPod browsing mode +void +CollectionBrowser::ipodToolbar( bool activate ) +{ + if( activate ) + m_ipodToolbar->show(); + + else + m_ipodToolbar->hide(); +} + + + +////////////////////////////////////////////////////////////////////////////////////////// +// CLASS CollectionView +////////////////////////////////////////////////////////////////////////////////////////// + +CollectionView* CollectionView::m_instance = 0; + + +CollectionView::CollectionView( CollectionBrowser* parent ) + : KListView( parent ) + , m_parent( parent ) + , m_timeFilter( 0 ) + , m_currentDepth( 0 ) + , m_ipodIncremented ( 1 ) + , m_dirty( true ) + , m_organizingFileCancelled( false ) +{ + DEBUG_FUNC_INFO + m_instance = this; + + setSelectionMode( QListView::Extended ); + setItemsMovable( false ); + setSorting( 0 ); + setShowSortIndicator( true ); + setAcceptDrops( true ); + setAllColumnsShowFocus( true ); + + // + KConfig* config = Amarok::config( "Collection Browser" ); + m_cat1 = config->readNumEntry( "Category1", IdArtist ); + m_cat2 = config->readNumEntry( "Category2", IdAlbum ); + m_cat3 = config->readNumEntry( "Category3", IdNone ); + +#define saneCat(x) (x==IdAlbum||x==IdArtist||x==IdComposer||x==IdGenre||x==IdYear \ + ||x==IdNone \ + ||x==IdArtistAlbum||x==IdGenreArtist||x==IdGenreArtistAlbum||x==IdVisYearAlbum||x==IdArtistVisYearAlbum) + + if( !saneCat(m_cat1) ) + { + m_cat1 = IdArtist; + m_cat2 = IdAlbum; + m_cat2 = IdNone; + } + if( !saneCat(m_cat2) || !saneCat(m_cat3) ) + { + m_cat2 = m_cat3 = IdNone; + } +#undef saneCat + + m_viewMode = config->readNumEntry( "ViewMode", modeTreeView ); + m_showDivider = config->readBoolEntry( "ShowDivider", true); + updateTrackDepth(); + + m_flatColumnWidths.clear(); + QStringList flatWidths = config->readListEntry( "FlatColumnWidths" ); + for( QStringList::iterator it = flatWidths.begin(); + it != flatWidths.end(); + it++ ) + m_flatColumnWidths.push_back( (*it).toInt() ); + + // + KActionCollection* ac = new KActionCollection( this ); + KStdAction::selectAll( this, SLOT( selectAll() ), ac, "collectionview_select_all" ); + + connect( CollectionDB::instance(), SIGNAL( scanStarted() ), + this, SLOT( scanStarted() ) ); + connect( CollectionDB::instance(), SIGNAL( scanDone( bool ) ), + this, SLOT( scanDone( bool ) ) ); + connect( BrowserBar::instance(), SIGNAL( browserActivated( int ) ), + this, SLOT( renderView() ) ); // renderView() checks if current tab is this + connect( CollectionDB::instance(), SIGNAL( ratingChanged( const QString&, int ) ), + this, SLOT( ratingChanged( const QString&, int ) ) ); + + connect( this, SIGNAL( expanded( QListViewItem* ) ), + this, SLOT( slotExpand( QListViewItem* ) ) ); + connect( this, SIGNAL( collapsed( QListViewItem* ) ), + this, SLOT( slotCollapse( QListViewItem* ) ) ); + connect( this, SIGNAL( returnPressed( QListViewItem* ) ), + this, SLOT( invokeItem( QListViewItem* ) ) ); + connect( this, SIGNAL( doubleClicked( QListViewItem*, const QPoint&, int ) ), + this, SLOT( invokeItem( QListViewItem*, const QPoint&, int ) ) ); + connect( this, SIGNAL( clicked( QListViewItem*, const QPoint&, int ) ), + this, SLOT( ipodItemClicked( QListViewItem*, const QPoint&, int ) ) ); + connect( this, SIGNAL( contextMenuRequested( QListViewItem*, const QPoint&, int ) ), + this, SLOT( rmbPressed( QListViewItem*, const QPoint&, int ) ) ); + connect( header(), SIGNAL( sizeChange( int, int, int ) ), + this, SLOT( triggerUpdate() ) ); + + connect( MountPointManager::instance(), SIGNAL( mediumConnected( int ) ), + this, SLOT( databaseChanged() ) ); + connect( MountPointManager::instance(), SIGNAL( mediumRemoved( int ) ), + this, SLOT( databaseChanged() ) ); +} + + +CollectionView::~CollectionView() { + DEBUG_FUNC_INFO + + KConfig* const config = Amarok::config( "Collection Browser" ); + config->writeEntry( "Category1", m_cat1 ); + config->writeEntry( "Category2", m_cat2 ); + config->writeEntry( "Category3", m_cat3 ); + config->writeEntry( "ViewMode", m_viewMode ); + config->writeEntry( "ShowDivider", m_showDivider ); + + QStringList flatWidths; + for( QValueList::iterator it = m_flatColumnWidths.begin(); + it != m_flatColumnWidths.end(); + it++ ) + flatWidths.push_back( QString::number( (*it) ) ); + config->writeEntry( "FlatColumnWidths", flatWidths ); +} + +void +CollectionView::setShowDivider( bool show ) +{ + if (show != m_showDivider) { + m_showDivider = show; + renderView(true); + } +} + + +// Reimplemented for iPod-style navigation, and to skip dividers +// Specifically, this method traps the Key_Up/Down/Left/Right events. +// When Up or Down is pressed, it skips dividers and wraps around when +// necessary. When Left or Right is pressed and we are viewing in +// iPod mode, the iPod "move forward / backward" actions are activated. +void +CollectionView::keyPressEvent( QKeyEvent *e ) +{ + typedef QListViewItemIterator It; + + // Reimplement up and down to skip dividers and to loop around. + // Some of this code used to be in CollectionBrowser::eventFilter. + // This rewritten code is more faithful to the ordinary moving + // behavior, even when looping around. (For instance, it behaves + // correctly if control-up is pressed at the top of the screen.) + // It sends fake keypress events to the parent instead of programatically + // selecting items. + if( (e->key() == Key_Up || e->key() == Key_Down ) && currentItem() ) + { + // Handle both up and down at once to avoid code duplication (it's + // a delicate piece of logic, and was hard to get right) + + QListViewItem *cur = currentItem(); + + #define nextItem (e->key() == Key_Up ? cur->itemAbove() : cur->itemBelow()) + + bool wraparound = true; + + // First skip any dividers directly above / below + do + { + KListView::keyPressEvent( e ); + if( currentItem() == cur ) // Prevent infinite loops + { + if( nextItem != 0 ) + wraparound = false; + break; + } + cur = currentItem(); + + if( cur && dynamic_cast( cur ) == 0 ) + wraparound = false; // Found an item above / below + + } while( cur != NULL + && dynamic_cast(cur) != 0 + && nextItem != 0 ); + + if( cur == 0 ) return; // Shouldn't happen + + // Wrap around if necessary, by sending a Key_Home/Key_End event. + if( wraparound ) + { + QKeyEvent e2 ( e->type(), + (e->key() == Key_Up ? Key_End : Key_Home), + 0, e->state(), + QString::null, e->isAutoRepeat(), e->count() ); + QApplication::sendEvent( this, &e2 ); + cur = currentItem(); + + // The first item may also be a divider, so keep moving + // until it's not + while ( cur != 0 + && dynamic_cast(cur) != 0 + && nextItem != 0 ) + { + KListView::keyPressEvent( e ); + if( currentItem() == cur ) // Prevent infinite loops + break; + cur = currentItem(); + } + } + + #undef nextItem + + } + + // When Right/Left is pressed in iPod view mode, activate the iPod + // "move forward/backward" action. + else if( (e->key() == Key_Left || e->key() == Key_Right) + && m_viewMode == modeIpodView ) + { + if( e->key() == Key_Right ) + m_parent->m_ipodIncrement->activate(); + + else if( e->key() == Key_Left ) + m_parent->m_ipodDecrement->activate(); + + } + + else // we don't want the event + KListView::keyPressEvent( e ); +} + + +////////////////////////////////////////////////////////////////////////////////////////// +// public slots +////////////////////////////////////////////////////////////////////////////////////////// + +void +CollectionView::renderView(bool force /* = false */) //SLOT +{ + SHOULD_BE_GUI + if(!force && !m_dirty ) + return; + + if( BrowserBar::instance()->currentBrowser() != m_parent ) + { + // the collectionbrowser is intensive for sql, so we only renderView() if the tab + // is currently active. else, wait until user focuses it. +// debug() << "current browser is not collection, aborting renderView()" << endl; + m_dirty = true; + return; + } + m_dirty = false; + + // Don't cache / restore view if we're in ipod mode and we've + // just incremented or decremented, since we'll run selectIpodItems() + // below anyway. + if( childCount() && + !(m_viewMode == modeIpodView && m_ipodIncremented > 0) ) + cacheView(); + + //clear(); + safeClear(); + + if ( m_viewMode == modeFlatView ) + { + renderFlatModeView( force ); + } + + if( m_viewMode == modeIpodView ) + { + renderIpodModeView( force ); + } + + if( m_viewMode == modeTreeView ) + { + renderTreeModeView( force ); + } + + // Don't cache or restore view when we're just going to run + // selectIpodItems() below anyway. + if( !(m_viewMode == modeIpodView && m_ipodIncremented > 0) ) + restoreView(); + + else + selectIpodItems(); +} + + +////////////////////////////////////////////////////////////////////////////////////////// +// private slots +////////////////////////////////////////////////////////////////////////////////////////// + +void +CollectionView::setupDirs() //SLOT +{ + KDialogBase dialog( this, 0, false ); + kapp->setTopWidget( &dialog ); + dialog.setCaption( kapp->makeStdCaption( i18n("Configure Collection") ) ); + + CollectionSetup *setup = new CollectionSetup( &dialog ); + dialog.setMainWidget( setup ); + dialog.showButtonApply( false ); + dialog.adjustSize(); + // Make the dialog a bit bigger, default is too small to be useful + dialog.resize( dialog.width() + 50, dialog.height() + 150 ); + + if ( dialog.exec() != QDialog::Rejected ) + { + const bool rescan = ( MountPointManager::instance()->collectionFolders() != setup->dirs() ); + setup->writeConfig(); + + if ( rescan ) + CollectionDB::instance()->startScan(); + } +} + + +void +CollectionView::scanStarted() // SLOT +{ + Amarok::actionCollection()->action("update_collection")->setEnabled( false ); +} + + +void +CollectionView::scanDone( bool changed ) //SLOT +{ + if( changed ) + { + renderView(true); + } + + Amarok::actionCollection()->action("update_collection")->setEnabled( true ); +} + +void +CollectionView::slotEnsureSelectedItemVisible() //SLOT +{ + //Scroll to make sure the first selected item is visible + + //Find the first selected item + QListViewItem *r=0; + for ( QListViewItem *i = firstChild(); i && !r; i=i->nextSibling() ) + { + if ( i->isSelected() ) + r = i; + for ( QListViewItem *j = i->firstChild(); j && !r; j=j->nextSibling() ) + { + if ( j->isSelected() ) + r = j; + for ( QListViewItem *k = j->firstChild(); k && !r; k=k->nextSibling() ) + { + if ( k->isSelected() ) + r = k; + } + } + } + if ( r ) + { + //We've found the selected item. Now let's refocus on it. + //An elaborate agorithm to try to make as much as possible of the vicinity visible + + //It looks better if things end up consistently in one place. + //So, scroll to the end so that we come at items from the bottom. + if ( lastChild() ) + ensureItemVisible( lastChild() ); + + //Create a reverse list of parents, grandparents etc. + //Later we try to make the grandparents in view, then their children etc. + //This means that the selected item has the most priority as it is done last. + QValueStack parents; + while ( r ) + { + parents.push( r ); + r = r->parent(); + } + while ( !parents.isEmpty() ) + { + //We would prefer the next item to be visible. + if ( parents.top()->nextSibling() ) + ensureItemVisible( parents.top()->nextSibling() ); + //It's even more important the actual item is visible than the next one. + ensureItemVisible( parents.top() ); + parents.pop(); + } + } +} + +void +CollectionView::slotExpand( QListViewItem* item ) //SLOT +{ + if ( !item || !item->isExpandable() ) return; + + int category = 0; + QStringList values; + + QueryBuilder qb; + bool c = false; + bool SortbyTrackFirst = false; + + //Sort by track number first if album is in one of the categories, otherwise by track name first + if ( m_cat1 == IdAlbum || + m_cat2 == IdAlbum || + m_cat3 == IdAlbum ) + SortbyTrackFirst = true; + + // initialization for year - album mode + QString tmptext; + int VisYearAlbum = -1; + int VisLabel = -1; + int q_cat1=m_cat1; + int q_cat2=m_cat2; + int q_cat3=m_cat3; + if( m_cat1 == IdVisYearAlbum || + m_cat2 == IdVisYearAlbum || + m_cat3 == IdVisYearAlbum ) + { + SortbyTrackFirst = true; + if( m_cat1 == IdVisYearAlbum ) + { + VisYearAlbum = 1; + q_cat1 = IdAlbum; + } + if( m_cat2 == IdVisYearAlbum ) + { + VisYearAlbum = 2; + q_cat2 = IdAlbum; + } + if( m_cat3 == IdVisYearAlbum ) + { + VisYearAlbum = 3; + q_cat3 = IdAlbum; + } + } + if( m_cat1 == IdLabel || + m_cat2 == IdLabel || + m_cat3 == IdLabel ) + { + if( m_cat1 == IdLabel ) + VisLabel = 1; + if( m_cat2 == IdLabel ) + VisLabel = 2; + if ( m_cat3 == IdLabel ) + VisLabel = 3; + } + + if ( translateTimeFilter( timeFilter() ) > 0 ) + qb.addFilter( QueryBuilder::tabSong, QueryBuilder::valCreateDate, QString().setNum( QDateTime::currentDateTime().toTime_t() - translateTimeFilter( timeFilter() ) ), QueryBuilder::modeGreater ); + + QString itemText; + bool isUnknown; + + if ( dynamic_cast( item ) ) + { + itemText = static_cast( item )->getSQLText( 0 ); + } + else + { + debug() << "slotExpand in CollectionView of a non-CollectionItem" << endl; + itemText = item->text( 0 ); + } + + switch ( item->depth() ) + { + case 0: + tmptext = itemText; + isUnknown = tmptext.isEmpty(); + if ( !static_cast( item )->isSampler() ) + { + if ( m_cat1 == IdArtist ) + qb.setOptions( QueryBuilder::optNoCompilations ); + if( VisYearAlbum == 1 ) + { + tmptext = item->text( 0 ); + QString year = tmptext.left( tmptext.find( i18n(" - ") ) ); + yearAlbumCalc( year, tmptext ); + qb.addMatch( QueryBuilder::tabYear, year, false, true ); + if ( isUnknown ) + tmptext = ""; + } + + qb.addMatch( q_cat1, tmptext, false, true ); + } + else + { + qb.setOptions( QueryBuilder::optOnlyCompilations ); + c = true; + } + + if ( m_cat2 == QueryBuilder::tabSong ) + { + qb.addReturnValue( q_cat2, QueryBuilder::valTitle, true ); + qb.addReturnValue( q_cat2, QueryBuilder::valURL ); + if ( c ) qb.addReturnValue( QueryBuilder::tabArtist, QueryBuilder::valName, true ); + if ( SortbyTrackFirst ) { + qb.sortBy( q_cat2, QueryBuilder::valDiscNumber ); + qb.sortBy( q_cat2, QueryBuilder::valTrack ); + } + if ( c ) qb.sortBy( QueryBuilder::tabArtist, QueryBuilder::valName ); + qb.sortBy( q_cat2, QueryBuilder::valTitle ); + if ( !SortbyTrackFirst ) { + qb.sortBy( q_cat2, QueryBuilder::valDiscNumber ); + qb.sortBy( q_cat2, QueryBuilder::valTrack ); + } + qb.sortBy( q_cat2, QueryBuilder::valURL ); + } + else + { + c = false; + qb.addReturnValue( q_cat2, QueryBuilder::valName, true ); + if( VisYearAlbum == 2 ) + { + qb.addReturnValue( QueryBuilder::tabYear, QueryBuilder::valName, true ); + qb.sortBy( QueryBuilder::tabYear, QueryBuilder::valName ); + } + qb.sortBy( q_cat2, QueryBuilder::valName ); + } + + category = m_cat2; + break; + + case 1: + tmptext = dynamic_cast( item->parent() ) ? + static_cast( item->parent() )->getSQLText( 0 ) : + item->parent()->text( 0 ); + isUnknown = tmptext.isEmpty(); + + if( !static_cast( item->parent() )->isSampler() ) + { + if ( m_cat1 == IdArtist ) + qb.setOptions( QueryBuilder::optNoCompilations ); + if( VisYearAlbum == 1 ) + { + tmptext = item->parent()->text( 0 ); + QString year = tmptext.left( tmptext.find( i18n(" - ") ) ); + yearAlbumCalc( year, tmptext ); + qb.addMatch( QueryBuilder::tabYear, year, false, true ); + if ( isUnknown ) + tmptext = ""; + } + + qb.addMatch( q_cat1, tmptext, false, true ); + } + else + { + qb.setOptions( QueryBuilder::optOnlyCompilations ); + c = true; + } + + tmptext = itemText; + isUnknown = tmptext.isEmpty(); + + if( VisYearAlbum == 2 ) + { + tmptext = item->text( 0 ); + QString year = tmptext.left( tmptext.find( i18n(" - ") ) ); + yearAlbumCalc( year, tmptext ); + qb.addMatch( QueryBuilder::tabYear, year, false, true ); + if ( isUnknown ) + tmptext = ""; + } + + qb.addMatch( q_cat2, tmptext, false, true ); + + if( m_cat3 == QueryBuilder::tabSong ) + { + qb.addReturnValue( q_cat3, QueryBuilder::valTitle, true ); + qb.addReturnValue( q_cat3, QueryBuilder::valURL ); + if ( c ) qb.addReturnValue( QueryBuilder::tabArtist, QueryBuilder::valName, true ); + if ( SortbyTrackFirst ) { + qb.sortBy( q_cat3, QueryBuilder::valDiscNumber ); + qb.sortBy( q_cat3, QueryBuilder::valTrack ); + } + if ( c ) qb.sortBy( QueryBuilder::tabArtist, QueryBuilder::valName ); + qb.sortBy( q_cat3, QueryBuilder::valTitle ); + if ( !SortbyTrackFirst ) { + qb.sortBy( q_cat3, QueryBuilder::valDiscNumber ); + qb.sortBy( q_cat3, QueryBuilder::valTrack ); + } + qb.sortBy( q_cat3, QueryBuilder::valURL ); + } + else + { + c = false; + qb.addReturnValue( q_cat3, QueryBuilder::valName, true ); + if( VisYearAlbum == 3 ) + { + qb.addReturnValue( QueryBuilder::tabYear, QueryBuilder::valName ); + qb.sortBy( QueryBuilder::tabYear, QueryBuilder::valName ); + } + qb.sortBy( q_cat3, QueryBuilder::valName ); + } + + category = m_cat3; + break; + + case 2: + tmptext = dynamic_cast ( item->parent()->parent() ) ? + static_cast( item->parent()->parent() )->getSQLText( 0 ) : + item->parent()->parent()->text( 0 ); + isUnknown = tmptext.isEmpty(); + + if ( !static_cast( item->parent()->parent() )->isSampler() ) + { + if ( m_cat1 == IdArtist ) + qb.setOptions( QueryBuilder::optNoCompilations ); + if( VisYearAlbum == 1 ) + { + tmptext = item->parent()->parent()->text( 0 ); + QString year = tmptext.left( tmptext.find( i18n(" - ") ) ); + yearAlbumCalc( year, tmptext ); + qb.addMatch( QueryBuilder::tabYear, year, false, true ); + if ( isUnknown ) + tmptext = ""; + } + + qb.addMatch( q_cat1, tmptext, false, true ); + } + else + { + qb.setOptions( QueryBuilder::optOnlyCompilations ); + c = true; + } + + tmptext = dynamic_cast( item->parent() ) ? + static_cast( item->parent() )->getSQLText( 0 ) : + item->parent()->text( 0 ); + isUnknown = tmptext.isEmpty(); + + if( VisYearAlbum == 2 ) + { + tmptext = item->parent()->text( 0 ); + QString year = tmptext.left( tmptext.find( i18n(" - ") ) ); + yearAlbumCalc( year, tmptext ); + qb.addMatch( QueryBuilder::tabYear, year, false, true ); + if ( isUnknown ) + tmptext = ""; + } + + qb.addMatch( q_cat2, tmptext, false, true ); + + tmptext = itemText; + isUnknown = tmptext.isEmpty(); + + if( VisYearAlbum == 3 ) + { + tmptext = item->text( 0 ); + QString year = tmptext.left( tmptext.find( i18n(" - ") ) ); + yearAlbumCalc( year, tmptext ); + qb.addMatch( QueryBuilder::tabYear, year, false, true ); + if ( isUnknown ) + tmptext = ""; + } + + qb.addMatch( q_cat3, tmptext, false, true ); + + qb.addReturnValue( QueryBuilder::tabSong, QueryBuilder::valTitle, true ); + qb.addReturnValue( QueryBuilder::tabSong, QueryBuilder::valURL ); + + if( c ) + qb.addReturnValue( QueryBuilder::tabArtist, QueryBuilder::valName, true ); + if ( SortbyTrackFirst ) { + qb.sortBy( QueryBuilder::tabSong, QueryBuilder::valDiscNumber ); + qb.sortBy( QueryBuilder::tabSong, QueryBuilder::valTrack ); + } + if ( c ) qb.sortBy( QueryBuilder::tabArtist, QueryBuilder::valName ); + qb.sortBy( QueryBuilder::tabSong, QueryBuilder::valTitle ); + if ( !SortbyTrackFirst ) { + qb.sortBy( QueryBuilder::tabSong, QueryBuilder::valDiscNumber ); + qb.sortBy( QueryBuilder::tabSong, QueryBuilder::valTrack ); + } + + qb.sortBy( QueryBuilder::tabSong, QueryBuilder::valURL ); + + category = IdNone; + break; + } + + qb.setGoogleFilter( q_cat1 | q_cat2 | q_cat3 | QueryBuilder::tabSong, m_filter ); + qb.setOptions( QueryBuilder::optRemoveDuplicates ); + values = qb.run(); + uint countReturnValues = qb.countReturnValues(); + + QPixmap pixmap; + bool expandable = category != IdNone; + if ( expandable ) + pixmap = iconForCategory( category ); + + //this check avoid possible problems on database errors. FIXME: Should we add some real error handling here, + //like calling a collection update or something? + if ( values.isEmpty() ) { return; } + + for ( int i = values.count() - countReturnValues; i >= 0; i -= countReturnValues ) + { + QString text; + bool unknown=false; + + if ( category == IdVisYearAlbum ) + text += ( values[ i+1 ].isEmpty() ? "?" : values[ i+1 ] ) + i18n( " - " ); + + //show "artist - title" for compilations + if ( c ) + { + if ( values[ i + 2 ].stripWhiteSpace().isEmpty() ) + { + text += i18n( "Unknown" ) + i18n( " - " ); + unknown = true; + } + else + text = values[ i + 2 ] + i18n( " - " ); + } + + if ( values[ i ].stripWhiteSpace().isEmpty() ) + { + if ( category == IdLabel ) + text += i18n( "No Label" ); + else + text += i18n( "Unknown" ); + unknown = true; + } + else + text += values[ i ]; + + CollectionItem* child = new CollectionItem( item, category, unknown ); + child->setDragEnabled( true ); + child->setDropEnabled( false ); + child->setText( 0, text ); + if ( expandable ) + child->setPixmap( 0, pixmap ); + else + child->setUrl( values[ i + 1 ] ); + child->setExpandable( expandable ); + } + + //Display the album cover for the parent item now it is expanded + if ( dynamic_cast( item ) ) + { + CollectionItem *i = static_cast( item ); + if ( i->m_cat == IdAlbum || i->m_cat == IdVisYearAlbum ) + i->setPixmap( 0, QPixmap() ); //The pixmap given is unimportant. The cover is used. + } +} + + +void +CollectionView::slotCollapse( QListViewItem* item ) //SLOT +{ + //On collapse, go back from showing the cover to showing the icon for albums + if ( dynamic_cast( item ) ) + { + CollectionItem *i = static_cast( item ); + if ( i->m_cat == IdAlbum || i->m_cat == IdVisYearAlbum ) + i->setPixmap( 0, iconForCategory( i->m_cat ) ); + } + + QListViewItem* child = item->firstChild(); + QListViewItem* childTmp; + + //delete all children + while ( child ) + { + childTmp = child; + child = child->nextSibling(); + delete childTmp; + } +} + +void +CollectionView::ratingChanged( const QString&, int ) +{ + m_dirty = true; + QTimer::singleShot( 0, CollectionView::instance(), SLOT( renderView() ) ); +} + +void +CollectionView::presetMenu( int id ) //SLOT +{ + switch ( id ) + { + case IdArtist: + cat1Menu( IdArtist, false ); + cat2Menu( IdNone, false ); + cat3Menu( IdNone, false ); + break; + case IdAlbum: + cat1Menu( IdAlbum, false ); + cat2Menu( IdNone, false ); + cat3Menu( IdNone, false ); + break; + case IdArtistAlbum: + cat1Menu( IdArtist, false ); + cat2Menu( IdAlbum, false ); + cat3Menu( IdNone, false ); + break; + case IdArtistVisYearAlbum: + cat1Menu( IdArtist, false ); + cat2Menu( IdVisYearAlbum, false ); + cat3Menu( IdNone, false ); + break; + case IdGenreArtist: + cat1Menu( IdGenre, false ); + cat2Menu( IdArtist, false ); + cat3Menu( IdNone, false ); + break; + case IdGenreArtistAlbum: + cat1Menu( IdGenre, false ); + cat2Menu( IdArtist, false ); + cat3Menu( IdAlbum, false ); + break; + } + + renderView(true); +} + + +void +CollectionView::cat1Menu( int id, bool rerender ) //SLOT +{ + m_parent->m_cat1Menu->setItemChecked( m_cat1, false ); //uncheck old item + m_parent->m_cat2Menu->setItemEnabled( m_cat1, true ); //enable old items + m_parent->m_cat3Menu->setItemEnabled( m_cat1, true ); + m_cat1 = id; + updateColumnHeader(); + resetIpodDepth(); + m_parent->m_cat1Menu->setItemChecked( m_cat1, true ); + + //prevent choosing the same category in both menus + m_parent->m_cat2Menu->setItemEnabled( id , false ); + m_parent->m_cat3Menu->setItemEnabled( id , false ); + + //if this item is checked in second menu, uncheck it + if ( m_parent->m_cat2Menu->isItemChecked( id ) ) { + m_parent->m_cat2Menu->setItemChecked( id, false ); + m_parent->m_cat2Menu->setItemChecked( IdNone, true ); + m_cat2 = IdNone; + enableCat3Menu( false ); + } + //if this item is checked in third menu, uncheck it + if ( m_parent->m_cat3Menu->isItemChecked( id ) ) { + m_parent->m_cat3Menu->setItemChecked( id, false ); + m_parent->m_cat3Menu->setItemChecked( IdNone, true ); + m_cat3 = IdNone; + } + updateTrackDepth(); + if ( rerender ) + { + renderView(true); + } +} + + +void +CollectionView::cat2Menu( int id, bool rerender ) //SLOT +{ + m_parent->m_cat2Menu->setItemChecked( m_cat2, false ); //uncheck old item + m_parent->m_cat3Menu->setItemEnabled( m_cat3, true ); //enable old item + m_cat2 = id; + m_parent->m_cat2Menu->setItemChecked( m_cat2, true ); + updateColumnHeader(); + resetIpodDepth(); + + enableCat3Menu( id != IdNone ); + + //prevent choosing the same category in both menus + m_parent->m_cat3Menu->setItemEnabled( m_cat1 , false ); + if( id != IdNone ) + m_parent->m_cat3Menu->setItemEnabled( id , false ); + + //if this item is checked in third menu, uncheck it + if ( m_parent->m_cat3Menu->isItemChecked( id ) ) { + m_parent->m_cat3Menu->setItemChecked( id, false ); + enableCat3Menu( false ); + } + updateTrackDepth(); + if ( rerender ) + { + renderView(true); + } +} + + +void +CollectionView::cat3Menu( int id, bool rerender ) //SLOT +{ + m_parent->m_cat3Menu->setItemChecked( m_cat3, false ); //uncheck old item + m_cat3 = id; + m_parent->m_cat3Menu->setItemChecked( m_cat3, true ); + updateColumnHeader(); + resetIpodDepth(); + updateTrackDepth(); + if ( rerender ) + { + renderView(true); + } +} + + +void +CollectionView::enableCat3Menu( bool enable ) +{ + m_parent->m_cat3Menu->setItemEnabled( IdAlbum, enable ); + m_parent->m_cat3Menu->setItemEnabled( IdVisYearAlbum, enable ); + m_parent->m_cat3Menu->setItemEnabled( IdArtist, enable ); + m_parent->m_cat3Menu->setItemEnabled( IdComposer, enable ); + m_parent->m_cat3Menu->setItemEnabled( IdGenre, enable ); + m_parent->m_cat3Menu->setItemEnabled( IdYear, enable ); + m_parent->m_cat3Menu->setItemEnabled( IdLabel, enable ); + + if( !enable ) { + m_parent->m_cat3Menu->setItemChecked( m_cat3, false ); + m_parent->m_cat3Menu->setItemChecked( IdNone, true ); + m_cat3 = IdNone; + } + updateTrackDepth(); +} + + +void +CollectionView::invokeItem( QListViewItem* i, const QPoint& point, int column ) //SLOT +{ + if( column == -1 ) + return; + + QPoint p = mapFromGlobal( point ); + if ( p.x() > header()->sectionPos( header()->mapToIndex( 0 ) ) + treeStepSize() * ( i->depth() + ( rootIsDecorated() ? 1 : 0) ) + itemMargin() + || p.x() < header()->sectionPos( header()->mapToIndex( 0 ) ) ) + invokeItem( i ); +} + +void +CollectionView::invokeItem( QListViewItem* item ) //SLOT +{ + if ( !item || dynamic_cast(item) ) + return; + + item->setSelected( true ); + setCurrentItem( item ); + //append and prevent doubles in playlist + if( item->isExpandable() || m_viewMode == modeIpodView ) + Playlist::instance()->insertMedia( listSelected(), Playlist::DefaultOptions ); + else + Playlist::instance()->insertMedia( static_cast( item )->url(), Playlist::DefaultOptions ); + +} + + +// This slot is here to handle clicks on the right-arrow buttons +// in iPod browsing mode +void +CollectionView::ipodItemClicked( QListViewItem *item, const QPoint&, int c ) +{ + if( item == 0 || c == 0 ) + return; + if( m_viewMode != modeIpodView ) + return; + + // The Qt manual says NOT to delete items from within this slot + QTimer::singleShot( 0, m_parent->m_ipodIncrement, SLOT( activate() ) ); +} + + +void +CollectionView::rmbPressed( QListViewItem* item, const QPoint& point, int ) //SLOT +{ + if ( dynamic_cast( item ) ) return; + + int artistLevel = -1; + + if ( item ) { + KPopupMenu menu( this ); + + int cat = 0; + if ( m_viewMode == modeTreeView ) { + switch ( item->depth() ) + { + case 0: + cat = m_cat1; + break; + case 1: + if( m_cat1 == IdArtist ) + artistLevel = 0; + cat = m_cat2; + break; + case 2: + if( m_cat1 == IdArtist ) + artistLevel = 0; + else if( m_cat2 == IdArtist ) + artistLevel = 1; + cat = m_cat3; + break; + } + } + + else if ( m_viewMode == modeIpodView ) { + int catArr[3] = {m_cat1, m_cat2, m_cat3}; + if ( m_currentDepth < trackDepth() ) + cat = catArr[m_currentDepth]; + } + + enum Actions { APPEND, QUEUE, MAKE, SAVE, MEDIA_DEVICE, BURN_ARTIST, + BURN_COMPOSER, BURN_ALBUM, BURN_CD, FETCH, INFO, + COMPILATION_SET, COMPILATION_UNSET, ORGANIZE, DELETE, TRASH, + FILE_MENU }; + + QString trueItemText = getTrueItemText( cat, item ); + KURL::List selection = listSelected(); + QStringList siblingSelection = listSelectedSiblingsOf( cat, item ); + + menu.insertItem( SmallIconSet( Amarok::icon( "files" ) ), i18n( "&Load" ), MAKE ); + menu.insertItem( SmallIconSet( Amarok::icon( "add_playlist" ) ), i18n( "&Append to Playlist" ), APPEND ); + menu.insertItem( SmallIconSet( Amarok::icon( "queue_track" ) ), selection.count() == 1 ? i18n( "&Queue Track" ) + : i18n( "&Queue Tracks" ), QUEUE ); + + if( selection.count() > 1 || item->isExpandable() ) + menu.insertItem( SmallIconSet( Amarok::icon( "save" ) ), i18n( "&Save as Playlist..." ), SAVE ); + + menu.insertSeparator(); + + if( MediaBrowser::isAvailable() ) + menu.insertItem( SmallIconSet( Amarok::icon( "device" ) ), i18n( "&Transfer to Media Device" ), MEDIA_DEVICE ); + + if( cat == IdArtist ) + { + menu.insertItem( SmallIconSet( Amarok::icon( "burn" ) ), i18n( "&Burn All Tracks by This Artist" ), BURN_ARTIST ); + menu.setItemEnabled( BURN_ARTIST, K3bExporter::isAvailable() && siblingSelection.count() == 1 ); + } + else if( cat == IdComposer ) + { + menu.insertItem( SmallIconSet( Amarok::icon( "burn" ) ), i18n( "&Burn All Tracks by This Composer" ), BURN_COMPOSER ); + menu.setItemEnabled( BURN_COMPOSER, K3bExporter::isAvailable() && siblingSelection.count() == 1 ); + } + else if( (cat == IdAlbum || cat == IdVisYearAlbum) ) + { + menu.insertItem( SmallIconSet( Amarok::icon( "burn" ) ), i18n( "&Burn This Album" ), BURN_ALBUM ); + menu.setItemEnabled( BURN_ALBUM, K3bExporter::isAvailable() && siblingSelection.count() == 1 ); + } + // !item->isExpandable() in tree mode corresponds to + // showing tracks in iPod mode + else if( !item->isExpandable() && + (m_viewMode != modeIpodView || m_currentDepth == trackDepth()) ) + { + menu.insertItem( SmallIconSet( Amarok::icon( "burn" ) ), i18n( "B&urn to CD" ), BURN_CD ); + menu.setItemEnabled( BURN_CD, K3bExporter::isAvailable() ); + } + + menu.insertSeparator(); + + KPopupMenu fileMenu; + fileMenu.insertItem( SmallIconSet( "filesaveas" ), i18n( "&Organize File..." , "&Organize %n Files..." , selection.count() ) , ORGANIZE ); + fileMenu.insertItem( SmallIconSet( Amarok::icon( "remove" ) ), i18n( "&Delete File..." , "&Delete %n Files..." , selection.count() ) , DELETE ); + menu.insertItem( SmallIconSet( Amarok::icon( "files" ) ), i18n( "Manage &Files" ), &fileMenu, FILE_MENU ); + + if( (cat == IdAlbum || cat == IdVisYearAlbum) && siblingSelection.count() == 1 ) // cover fetch isn't multiselection capable + { + menu.insertItem( SmallIconSet( Amarok::icon( "download" ) ), i18n( "&Fetch Cover From amazon.%1" ).arg( CoverManager::amazonTld() ), this, SLOT( fetchCover() ), 0, FETCH ); + #ifndef AMAZON_SUPPORT + menu.setItemEnabled( FETCH, false ); + #endif + if( trueItemText.isEmpty() ) // disable cover fetch for unknown albums + menu.setItemEnabled( FETCH, false ); + } + + if ( ( (cat == IdAlbum || cat == IdVisYearAlbum) && siblingSelection.count() > 0 ) //album + || ( !item->isExpandable() && m_viewMode == modeTreeView ) ) //or track + { + menu.insertSeparator(); + menu.insertItem( SmallIconSet( "ok" ), i18n( "Show under &Various Artists" ), COMPILATION_SET ); + menu.insertItem( SmallIconSet( "cancel" ), i18n( "&Do not Show under Various Artists" ), COMPILATION_UNSET ); + } + + menu.insertSeparator(); + + menu.insertItem( SmallIconSet( Amarok::icon( "info" ) ) + , i18n( "Edit Track &Information...", "Edit &Information for %n Tracks...", selection.count()) + , this, SLOT( showTrackInfo() ), 0, INFO ); + + switch( menu.exec( point ) ) + { + case APPEND: + Playlist::instance()->insertMedia( selection, Playlist::Append ); + break; + case MAKE: + Playlist::instance()->insertMedia( selection, Playlist::Replace ); + break; + case SAVE: + playlistFromURLs( selection ); + break; + case QUEUE: + Playlist::instance()->insertMedia( selection, Playlist::Queue ); + break; + case MEDIA_DEVICE: + MediaBrowser::queue()->addURLs( selection ); + break; + case BURN_COMPOSER: + K3bExporter::instance()->exportComposer( trueItemText ); + break; + case BURN_ARTIST: + K3bExporter::instance()->exportArtist( trueItemText ); + break; + case BURN_ALBUM: + if( artistLevel == -1 || static_cast(item)->isSampler() ) + { + K3bExporter::instance()->exportAlbum( trueItemText ); + } + else + { + QString artist; + if( item->depth() - artistLevel == 1 ) + artist = item->parent()->text( 0 ); + else if( item->depth() - artistLevel == 2 ) + artist = item->parent()->parent()->text( 0 ); + else if( item->depth() - artistLevel == 3 ) + artist = item->parent()->parent()->parent()->text( 0 ); + K3bExporter::instance()->exportAlbum( artist, trueItemText ); + } + break; + case BURN_CD: + K3bExporter::instance()->exportTracks( selection ); + break; + case COMPILATION_SET: + setCompilation( selection, true ); + break; + case COMPILATION_UNSET: + setCompilation( selection, false ); + break; + case ORGANIZE: + organizeFiles( selection, i18n( "Organize Collection Files" ), false /* do not add to collection, just move */ ); + break; + case DELETE: + if ( DeleteDialog::showTrashDialog(this, selection) ) + { + CollectionDB::instance()->removeSongs( selection ); + foreachType( KURL::List, selection ) + CollectionDB::instance()->emitFileDeleted( (*it).path() ); + } + m_dirty = true; + QTimer::singleShot( 0, CollectionView::instance(), SLOT( renderView() ) ); + break; + } + } +} + + +void +CollectionView::setViewMode( int mode, bool rerender /*=true*/ ) +{ + if( m_viewMode == modeFlatView ) + { + m_flatColumnWidths.clear(); + for ( int c = 0; c < columns(); ++c ) + m_flatColumnWidths.push_back( columnWidth( c ) ); + } + + m_viewMode = mode; + clear(); + updateColumnHeader(); + + if( m_viewMode == modeIpodView ) + { + #if KDE_VERSION >= KDE_MAKE_VERSION(3,4,0) + setShadeSortColumn( false ); + #endif + m_parent->m_ipodDecrement->setEnabled( m_currentDepth > 0 ); + m_parent->ipodToolbar( true ); + } + else + { + #if KDE_VERSION >= KDE_MAKE_VERSION(3,4,0) + setShadeSortColumn( true ); + #endif + m_parent->ipodToolbar( false ); + } + + if ( rerender ) + { + // Pretend we just incremented the view depth so that + // renderView() will call selectIpodItems() + if( m_viewMode == modeIpodView ) + m_ipodIncremented = 1; + + renderView( true ); + } +} + +void +CollectionItem::setPixmap(int column, const QPixmap & pix) +{ + //Don't show the cover if the album isn't expanded (for speed) + if ( !isOpen() ) + { + QListViewItem::setPixmap( column, pix ); + return; + } + + //Generate Album name + QString album( text( 0 ) ), artist; + if ( m_cat == IdVisYearAlbum ) + { + QString pointlessString; + CollectionView::yearAlbumCalc( pointlessString, album ); + } + else if ( m_cat != IdAlbum ) + { + QListViewItem::setPixmap( column, pix ); + return; + } + + //Now m_cat is either IdAlbum or IdVisYearAlbum, and so this is an album as required. + + //Now work out the artist + CollectionItem *p = this; + while ( p->parent() && dynamic_cast( p->parent() ) ) + { + p = static_cast( p->parent() ); + if ( IdArtist == p->m_cat ) + { + artist = p->text( 0 ); + break; + } + } + + if ( artist.isNull() ) + { + //Try to guess artist - this will only happen if you don't have an Artist category + //above the Album category in the tree + QueryBuilder qb; + qb.addReturnValue( QueryBuilder::tabArtist, QueryBuilder::valName ); + qb.addMatch( QueryBuilder::tabAlbum, QueryBuilder::valName, album ); + + QStringList values( qb.run() ); + + if ( !values.isEmpty() ) + artist = values[ 0 ]; + else + { + //Don't bother trying to create a shadow because it won't work anyway. The + //nocover image has intial transparency, so adding the shadow doesn't work. + QListViewItem::setPixmap( column, QPixmap( CollectionDB::instance()->notAvailCover( false, 50 ) ) ); + return; + } + } + + QListViewItem::setPixmap( column, QPixmap( CollectionDB::instance()->albumImage( artist, album, true, 50 ) ) ); +} + + +void +CollectionView::fetchCover() //SLOT +{ + #ifdef AMAZON_SUPPORT + CollectionItem* item = static_cast( currentItem() ); + if ( !item ) return; + + int cat = 0; + switch ( item->depth() ) + { + case 0: + cat = m_cat1; + break; + case 1: + cat = m_cat2; + break; + case 2: + cat = m_cat3; + break; + } + + QString album = item->text(0); + if( cat == IdVisYearAlbum ) + { + // we can't use findRev since an album may have " - " within it. + QString sep = i18n(" - "); + album = album.right( album.length() - sep.length() - album.find( sep ) ); + } + + // find the first artist's name + QStringList values = + CollectionDB::instance()->query( QString ( + "SELECT DISTINCT artist.name FROM artist, album, tags " + "WHERE artist.id = tags.artist AND tags.album = album.id " + "AND album.name = '%1';" ) + .arg( CollectionDB::instance()->escapeString( album ) ) ); + + if ( !values.isEmpty() ) + CollectionDB::instance()->fetchCover( this, values[0], album, false, static_cast(item) ); + #endif +} + +void +CollectionView::showTrackInfo() //SLOT +{ + DEBUG_BLOCK + KURL::List urls = listSelected(); + int selectedTracksNumber = urls.count(); + + //If we have only one, call the full dialog. Otherwise, the multiple tracks one. + if ( selectedTracksNumber == 1 ) + { + TagDialog* dialog = new TagDialog( urls.first(), instance() ); + dialog->show(); + } + else if ( selectedTracksNumber ) + { + TagDialog* dialog = new TagDialog( urls, instance() ); + dialog->show(); + } +} + +bool +CollectionView::isOrganizingFiles() const +{ + return m_organizeURLs.count() > 0; +} + +void CollectionView::cancelOrganizingFiles() +{ + // Set the indicator + m_organizingFileCancelled = true; + + // Cancel the current underlying CollectionDB::instance()->moveFile operation + CollectionDB::instance()->cancelMovingFileJob(); +} + +void +CollectionView::organizeFiles( const KURL::List &urls, const QString &caption, bool copy ) //SLOT +{ + if( m_organizingFileCancelled ) + { + QString shortMsg = i18n( "Cannot start organize operation until jobs are aborted." ); + Amarok::StatusBar::instance()->shortMessage( shortMsg, KDE::StatusBar::Sorry ); + return; + } + + if( m_organizeURLs.count() ) + { + if( copy != m_organizeCopyMode ) + { + QString shortMsg = i18n( "Cannot start organize operation of different kind while another is in progress." ); + Amarok::StatusBar::instance()->shortMessage( shortMsg, KDE::StatusBar::Sorry ); + return; + } + else + { + m_organizeURLs += Amarok::recursiveUrlExpand( urls ); + Amarok::StatusBar::instance()->incrementProgressTotalSteps( this, urls.count() ); + return; + } + } + + QStringList folders = MountPointManager::instance()->collectionFolders(); + if( folders.isEmpty() ) + { + QString longMsg = i18n( "You need to configure at least one folder for your collection for organizing your files." ); + Amarok::StatusBar::instance()->longMessage( longMsg, KDE::StatusBar::Sorry ); + return; + } + + OrganizeCollectionDialogBase base( m_parent, "OrganizeFiles", true, caption, + KDialogBase::Ok|KDialogBase::Cancel|KDialogBase::Details ); + QVBox* page = base.makeVBoxMainWidget(); + + OrganizeCollectionDialog dialog( page ); + dialog.folderCombo->insertStringList( folders, 0 ); + dialog.folderCombo->setCurrentItem( AmarokConfig::organizeDirectory() ); + dialog.overwriteCheck->setChecked( AmarokConfig::overwriteFiles() ); + dialog.filetypeCheck->setChecked( AmarokConfig::groupByFiletype() ); + dialog.initialCheck->setChecked( AmarokConfig::groupArtists() ); + dialog.spaceCheck->setChecked( AmarokConfig::replaceSpace() ); + dialog.coverCheck->setChecked( AmarokConfig::coverIcons() ); + dialog.ignoreTheCheck->setChecked( AmarokConfig::ignoreThe() ); + dialog.vfatCheck->setChecked( AmarokConfig::vfatCompatible() ); + dialog.asciiCheck->setChecked( AmarokConfig::asciiOnly() ); + dialog.customschemeCheck->setChecked( AmarokConfig::useCustomScheme() ); + dialog.formatEdit->setText( AmarokConfig::customScheme() ); + dialog.regexpEdit->setText( AmarokConfig::replacementRegexp() ); + dialog.replaceEdit->setText( AmarokConfig::replacementString() ); + connect( &base, SIGNAL(detailsClicked()), &dialog, SLOT(slotDetails()) ); + + if( dialog.customschemeCheck->isChecked() ) + { + base.setDetails( true ); + } + else + { + dialog.slotDetails(); + } + + KURL::List previewURLs = Amarok::recursiveUrlExpand( urls.first(), 1 ); + if( previewURLs.count() ) + { + dialog.setPreviewBundle( MetaBundle( previewURLs.first() ) ); + dialog.update( 0 ); + } + + base.setInitialSize( QSize( 450, 350 ) ); + + if( base.exec() == KDialogBase::Accepted ) + { + AmarokConfig::setOrganizeDirectory( dialog.folderCombo->currentItem() ); + AmarokConfig::setOverwriteFiles( dialog.overwriteCheck->isChecked() ); + AmarokConfig::setGroupByFiletype( dialog.filetypeCheck->isChecked() ); + AmarokConfig::setGroupArtists( dialog.initialCheck->isChecked() ); + AmarokConfig::setIgnoreThe( dialog.ignoreTheCheck->isChecked() ); + AmarokConfig::setReplaceSpace( dialog.spaceCheck->isChecked() ); + AmarokConfig::setCoverIcons( dialog.coverCheck->isChecked() ); + AmarokConfig::setVfatCompatible( dialog.vfatCheck->isChecked() ); + AmarokConfig::setAsciiOnly( dialog.asciiCheck->isChecked() ); + AmarokConfig::setUseCustomScheme( dialog.customschemeCheck->isChecked() ); + AmarokConfig::setCustomScheme( dialog.formatEdit->text() ); + AmarokConfig::setReplacementRegexp( dialog.regexpEdit->text() ); + AmarokConfig::setReplacementString( dialog.replaceEdit->text() ); + KURL::List skipped; + + m_organizeURLs = Amarok::recursiveUrlExpand( urls ); + m_organizeCopyMode = copy; + CollectionDB::instance()->createTables( true ); // create temp tables + Amarok::StatusBar::instance()->newProgressOperation( this ) + .setDescription( caption ) + .setAbortSlot( this, SLOT( cancelOrganizingFiles() ) ) + .setTotalSteps( m_organizeURLs.count() ); + + while( !m_organizeURLs.empty() && !m_organizingFileCancelled ) + { + KURL &src = m_organizeURLs.first(); + + if( !CollectionDB::instance()->organizeFile( src, dialog, copy ) ) + { + skipped += src; + } + + m_organizeURLs.pop_front(); + Amarok::StatusBar::instance()->incrementProgress( this ); + + if( m_organizingFileCancelled ) m_organizeURLs.clear(); + } + + CollectionDB::instance()->sanitizeCompilations(); //queryBuilder doesn't handle unknownCompilations + CollectionDB::instance()->copyTempTables(); // copy temp table contents to permanent tables + CollectionDB::instance()->dropTables( true ); // and drop them + + // and now do an incremental scan since this was disabled while organizing files + QTimer::singleShot( 0, CollectionDB::instance(), SLOT( scanMonitor() ) ); + + if( !m_organizingFileCancelled && skipped.count() > 0 ) + { + QString longMsg = i18n( "The following file could not be organized: ", + "The following %n files could not be organized: ", skipped.count() ); + bool first = true; + for( KURL::List::iterator it = skipped.begin(); + it != skipped.end(); + it++ ) + { + if( !first ) + longMsg += i18n( ", " ); + else + first = false; + longMsg += (*it).path(); + } + longMsg += i18n( "." ); + + QString shortMsg = i18n( "Sorry, one file could not be organized.", + "Sorry, %n files could not be organized.", skipped.count() ); + Amarok::StatusBar::instance()->shortLongMessage( shortMsg, longMsg, KDE::StatusBar::Sorry ); + } + else if ( m_organizingFileCancelled ) + { + Amarok::StatusBar::instance()->shortMessage( i18n( "Aborting jobs..." ) ); + m_organizingFileCancelled = false; + } + + m_dirty = true; + QTimer::singleShot( 0, CollectionView::instance(), SLOT( renderView() ) ); + Amarok::StatusBar::instance()->endProgressOperation( this ); + } +} + +////////////////////////////////////////////////////////////////////////////////////////// +// private +////////////////////////////////////////////////////////////////////////////////////////// + +void +CollectionView::contentsDragEnterEvent( QDragEnterEvent *e ) +{ + e->accept( e->source() != viewport() && e->source() != this && KURLDrag::canDecode( e ) ); +} + +void +CollectionView::contentsDragMoveEvent( QDragMoveEvent *e ) +{ + e->accept( e->source() != viewport() && e->source() != this && KURLDrag::canDecode( e ) ); +} + +void +CollectionView::contentsDropEvent( QDropEvent *e ) +{ + KURL::List list; + if( KURLDrag::decode( e, list ) ) + { + KURL::List expandedList; + int dropped = 0; + int invalid = 0; + for( KURL::List::iterator it = list.begin(); + it != list.end(); + ++it ) + { + if( (*it).isLocalFile() && QFileInfo( (*it).path() ).isDir() ) + expandedList += Amarok::recursiveUrlExpand( *it ); + else + expandedList += *it; + } + + KURL::List cleanList; + for( KURL::List::iterator it = expandedList.begin(); + it != expandedList.end(); + ++it ) + { + QString proto = (*it).protocol(); + if( !MetaBundle::isKioUrl( *it ) ) + invalid++; + else if( (*it).isLocalFile() && CollectionDB::instance()->isFileInCollection( (*it).path() ) ) + dropped++; + else + cleanList += *it; + } + + QString msg; + if( dropped > 0 ) + msg += i18n( "One file already in collection", + "%n files already in collection", dropped ); + if( invalid > 0 ) + if( msg.isEmpty() ) + msg += i18n( "One dropped file is invalid", + "%n dropped files are invalid", invalid ); + else + msg += i18n( ", one dropped file is invalid", + ", %n dropped files are invalid", invalid ); + if( !msg.isEmpty() ) + Amarok::StatusBar::instance()->shortMessage( msg ); + if( cleanList.count() > 0 ) + organizeFiles( list, i18n( "Copy Files To Collection" ), true /* copy */ ); + } +} + +void +CollectionView::dropProxyEvent( QDropEvent *e ) +{ + contentsDropEvent( e ); +} + +void +CollectionView::safeClear() +{ + bool block = signalsBlocked(); + blockSignals( true ); + clearSelection(); + + QMap *itemCoverMap = CollectionDB::instance()->getItemCoverMap(); + QMutex* itemCoverMapMutex = CollectionDB::instance()->getItemCoverMapMutex(); + QListViewItem *c = firstChild(); + QListViewItem *n; + itemCoverMapMutex->lock(); + while( c ) { + if( itemCoverMap->contains( c ) ) + itemCoverMap->erase( c ); + n = c->nextSibling(); + delete c; + c = n; + } + itemCoverMapMutex->unlock(); + blockSignals( block ); + triggerUpdate(); +} + +void +CollectionView::updateColumnHeader() +{ + // remove all columns + for ( int i = columns() - 1; i >= 0 ; --i ) + removeColumn( i ); + + if ( m_viewMode == modeFlatView ) + { + setResizeMode( QListView::NoColumn ); + + if( m_flatColumnWidths.size() == 0 ) + { + addColumn( captionForTag( Title ) ); +#define includesArtist(cat) (((cat)&IdArtist) \ + ||((cat)&IdArtistAlbum) \ + ||((cat)&IdGenreArtist) \ + ||((cat)&IdGenreArtistAlbum) \ + ||((cat)&IdArtistVisYearAlbum)) + if( includesArtist(m_cat1)||includesArtist(m_cat2)||includesArtist(m_cat3) ) + addColumn( captionForTag( Artist ) ); + else + addColumn( captionForTag( Artist ), 0 ); +#undef includesArtist + if( m_cat1&IdComposer || m_cat2&IdComposer || m_cat3&IdComposer ) + addColumn( captionForTag( Composer ) ); + else + addColumn( captionForTag( Composer ), 0 ); +#define includesAlbum(cat) (((cat)&IdAlbum) \ + ||((cat)&IdArtistAlbum) \ + ||((cat)&IdGenreArtistAlbum) \ + ||((cat)&IdVisYearAlbum) \ + ||((cat)&IdArtistVisYearAlbum)) + if( includesAlbum(m_cat1)||includesAlbum(m_cat2)||includesAlbum(m_cat3) ) + addColumn( captionForTag( Album ) ); + else + addColumn( captionForTag( Album ), 0 ); +#undef includesAlbum +#define includesGenre(cat) (((cat)&IdGenre) \ + ||((cat)&IdGenreArtist) \ + ||((cat)&IdGenreArtistAlbum)) + if( includesGenre(m_cat1)||includesGenre(m_cat2)||includesGenre(m_cat3) ) + addColumn( captionForTag( Genre ) ); + else + addColumn( captionForTag( Genre ), 0 ); +#undef includesGenre + addColumn( captionForTag( Length ),0 ); + addColumn( captionForTag( DiscNumber ), 0 ); + addColumn( captionForTag( Track ), 0 ); +#define includesYear(cat) (((cat)&IdYear) \ + ||((cat)&IdVisYearAlbum) \ + ||((cat)&IdArtistVisYearAlbum)) + if( includesYear(m_cat1)||includesYear(m_cat2)||includesYear(m_cat3) ) + addColumn( captionForTag( Year ) ); + else + addColumn( captionForTag( Year ), 0 ); +#undef includesYear + addColumn( captionForTag( Comment ), 0 ); + addColumn( captionForTag( Playcount ), 0 ); + addColumn( captionForTag( Score ), 0 ); + addColumn( captionForTag( Rating ), 0 ); + addColumn( captionForTag( Filename ), 0 ); + addColumn( captionForTag( Firstplay ), 0 ); + addColumn( captionForTag( Lastplay ), 0 ); + addColumn( captionForTag( Modified ), 0 ); + addColumn( captionForTag( Bitrate ), 0 ); + addColumn( captionForTag( Filesize ), 0 ); + addColumn( captionForTag( BPM ), 0 ); + } + else + { + for( uint tag = 0; tag < NUM_TAGS; ++tag ) { + if( tag < m_flatColumnWidths.size() ) + addColumn( captionForTag( static_cast( tag ) ), m_flatColumnWidths[tag] ); + else + addColumn( captionForTag( static_cast( tag ) ), 0 ); + } + } + + setColumnAlignment( Track, Qt::AlignCenter ); + setColumnAlignment( DiscNumber, Qt::AlignCenter ); + setColumnAlignment( Length, Qt::AlignRight ); + setColumnAlignment( Bitrate, Qt::AlignCenter ); + setColumnAlignment( Score, Qt::AlignCenter ); + setColumnAlignment( Playcount, Qt::AlignCenter ); + setColumnAlignment( BPM, Qt::AlignRight ); + + //QListView allows invisible columns to be resized, so we disable resizing for them + for ( int i = 0; i < columns(); ++i ) { + setColumnWidthMode ( i, QListView::Manual ); + if ( columnWidth( i ) == 0 ) + header()->setResizeEnabled( false, i ); + } + setRootIsDecorated( false ); + } + else if ( m_viewMode == modeTreeView ) + { + setResizeMode( QListView::LastColumn ); + + QString caption = captionForCategory( m_cat1 ); + int catArr[2] = {m_cat2, m_cat3}; + + for(int i = 0; i < 2; i++) { + if (catArr[i] != IdNone ) { + caption += " / " + captionForCategory( catArr[i] ); + } + } + addColumn( caption ); + setRootIsDecorated( true ); + } + + // The iPod columns + // When not in track mode, there are two columns: the first + // contains the text + pixmap, and the second just has the + // right-arrow pixmap. In any case we're not in tree mode. + else if ( m_viewMode == modeIpodView ) + { + QString caption; + + if( m_currentDepth == trackDepth() ) + caption = i18n( "Tracks" ); + + else + { + int catArr[3] = {m_cat1, m_cat2, m_cat3}; + caption = captionForCategory( catArr[m_currentDepth] ); + } + + addColumn( caption ); + + if( m_currentDepth != trackDepth() ) + { + QPixmap pixmap = ipodDecrementIcon(); + // This column is for the "->" buttons. The width is just + // a guess, and will be changed once an item is added. + addColumn( "", pixmap.width() + 10 ); + } + setRootIsDecorated( false ); + + // Neither column should automatically stretch, since this tends + // to add a scrollbar when it's not needed, and anyway we manually + // stretch it in viewportResizeEvent() + header()->setStretchEnabled( false, 0 ); + if( m_currentDepth != trackDepth() ) + header()->setStretchEnabled( false, 1 ); + + } + + //manage column widths + QResizeEvent rev( size(), QSize() ); + viewportResizeEvent( &rev ); + + m_parent->m_categoryMenu->setItemChecked( IdArtist, m_cat1 == IdArtist && m_cat2 == IdNone ); + m_parent->m_categoryMenu->setItemChecked( IdAlbum, m_cat1 == IdAlbum && m_cat2 == IdNone ); + m_parent->m_categoryMenu->setItemChecked( IdArtistAlbum, m_cat1 == IdArtist && m_cat2 == IdAlbum && m_cat3 == IdNone ); + m_parent->m_categoryMenu->setItemChecked( IdArtistVisYearAlbum, m_cat1 == IdArtist && m_cat2 == IdVisYearAlbum && m_cat3 == IdNone ); + m_parent->m_categoryMenu->setItemChecked( IdGenreArtist, m_cat1 == IdGenre && m_cat2 == IdArtist && m_cat3 == IdNone ); + m_parent->m_categoryMenu->setItemChecked( IdGenreArtistAlbum, m_cat1 == IdGenre && m_cat2 == IdArtist && m_cat3 == IdAlbum ); +} + + +void +CollectionView::startDrag() +{ + KURL::List urls = listSelected(); + KURLDrag* d = new KURLDrag( urls, this ); + d->setPixmap( CollectionDB::createDragPixmap(urls), + QPoint( CollectionDB::DRAGPIXMAP_OFFSET_X, + CollectionDB::DRAGPIXMAP_OFFSET_Y ) ); + d->dragCopy(); +} + +QString +CollectionView::getTrueItemText( int cat, QListViewItem* item ) const +{ + //Work out the true name of the album ( where Unknown is "" ) , and the + QString trueItemText; + if ( item == 0 ) + { + warning() << "getTrueItemText() called for empty CollectionItem" << endl; + return QString(); + } + if ( dynamic_cast( item ) ) + { + CollectionItem* collectItem = static_cast( item ); + trueItemText = collectItem->getSQLText( 0 ); + if ( cat == IdVisYearAlbum && !collectItem->isUnknown() ) + trueItemText = trueItemText.right( trueItemText.length() - trueItemText.find( i18n( " - " ) ) - i18n( " - " ).length() ); + } + else + { + trueItemText = item->text( 0 ); + warning() << "getTrueItemText() called for non-CollectionItem with text '" << trueItemText << '\'' << endl; + } + return trueItemText; +} + +QStringList +CollectionView::listSelectedSiblingsOf( int cat, QListViewItem* item ) +{ + // notice that using the nextSibling()-axis does not work in this case as this + // would only select items below the specified item. + QStringList list; + QString trueItemText; + int depth = item->depth(); + + // go to top most item + while( item && item->itemAbove() ) + { + item = item->itemAbove(); + //debug() << "walked up to item: " << getTrueItemText( cat, item ) << endl; + } + // walk down to get all selected items in same depth + while( item ) + { + if ( item->isSelected() && item->depth() == depth ) + { + trueItemText = getTrueItemText( cat, item ); + //debug() << "selected item: " << trueItemText << endl; + list << trueItemText; + } + item = item->itemBelow(); + } + return list; +} + +KURL::List +CollectionView::listSelected() +{ + //Here we determine the URLs of all selected items. We use two passes, one for the parent items, + //and another one for the children. + + KURL::List list; + QListViewItem* item; + QStringList values; + QueryBuilder qb; + + // initialization for year - album mode + QString tmptext; + bool unknownText; + int VisYearAlbum = -1; + int q_cat1=m_cat1; + int q_cat2=m_cat2; + int q_cat3=m_cat3; + if (m_cat1 == IdVisYearAlbum || m_cat2 == IdVisYearAlbum || m_cat3 == IdVisYearAlbum) + { + if (m_cat1==IdVisYearAlbum) + { + VisYearAlbum = 1; + q_cat1 = IdAlbum; + } + if (m_cat2==IdVisYearAlbum) + { + VisYearAlbum = 2; + q_cat2 = IdAlbum; + } + if (m_cat3==IdVisYearAlbum) + { + VisYearAlbum = 3; + q_cat3 = IdAlbum; + } + } + + if ( m_viewMode == modeFlatView ) + { + for ( item = firstChild(); item; item = item->nextSibling() ) + if ( item->isSelected() ) + list << static_cast( item ) ->url(); + + return list; + } + + + // The iPod selection code is written to resemble the tree mode + // selection logic, as well as what happens on an actual iPod. If + // we're in track mode, just return the list of tracks selected. + // Otherwise select all children of all currently selected items, + // sorting first by m_cat1, then m_cat2, then m_cat3. Sort by + // track first if one of the categories is Id(VisYear)Album. + // There is a difficulty with compilation albums -- if we're + // sorting by track first then we want to group compilation albums + // separately, and not with the individual artists, even if that's + // one of the categories (e.g. if the user just adds one + // compilation album, we can't sort by artist first). In other + // words, when one of the categories is Id(VisYear)Album, + // compilation albums should behave as if the artist is Various + // Artists, for sorting purposes. This is handled by making two + // separate queries, the first for the compilations, and the + // second for everything else. + + // Note that if "All" is currently selected then the other + // selections are ignored. + if ( m_viewMode == modeIpodView ) + { + // If we're already displaying tracks, just return the selected ones + if( m_currentDepth == trackDepth() ) + { + QPtrList selected = selectedItems(); + QPtrList::iterator it = selected.begin(); + while( it != selected.end() ) + { + if( dynamic_cast(*it) != 0 ) + list << dynamic_cast(*it)->url(); + ++it; + } + return list; + } + + // We're not displaying tracks. + QueryBuilder qb; + + // Do a "fake" depth incrementation to figure out the + // correct filters at the current depth + incrementDepth( false ); + + // Copy the filter list before calling decrementDepth() below + QStringList filters[3]; + for( int i = 0; i < m_currentDepth; ++i ) + filters[i] = m_ipodFilters[i]; + QStringList filterYear = m_ipodFilterYear; + + // Undo the fake depth incrementation + decrementDepth( false ); + + int catArr[3] = {m_cat1, m_cat2, m_cat3}; + int tables = 0; + bool sortByTrackFirst = false; + for(int i = 0; i < trackDepth(); ++i) + tables |= (catArr[i] == IdVisYearAlbum + ? IdAlbum + : catArr[i]); + + // Figure out if the results will be sorted by track first + // (i.e., if one of the filters is by album). If so, we need + // to search compilations first. + if( tables & IdAlbum ) + sortByTrackFirst = true; + + if( sortByTrackFirst ) + { + // Build the query, recursively sorted. First get compilation + // albums so they'll be first, not sorted by artist + buildIpodQuery( qb, trackDepth(), filters, filterYear, true, true ); + + if ( translateTimeFilter( timeFilter() ) > 0 ) + qb.addFilter( QueryBuilder::tabSong, QueryBuilder::valCreateDate, QString().setNum( QDateTime::currentDateTime().toTime_t() - translateTimeFilter( timeFilter() ) ), QueryBuilder::modeGreater ); + + qb.setOptions( QueryBuilder::optOnlyCompilations ); + qb.setGoogleFilter( tables | QueryBuilder::tabSong, m_filter ); + qb.addReturnValue( QueryBuilder::tabSong, QueryBuilder::valURL ); + + values = qb.run(); + } + + // Now build the non-compilation album query + qb.clear(); + + buildIpodQuery( qb, trackDepth(), filters, filterYear, true, false ); + + if ( translateTimeFilter( timeFilter() ) > 0 ) + qb.addFilter( QueryBuilder::tabSong, QueryBuilder::valCreateDate, QString().setNum( QDateTime::currentDateTime().toTime_t() - translateTimeFilter( timeFilter() ) ), QueryBuilder::modeGreater ); + + if( sortByTrackFirst ) + qb.setOptions( QueryBuilder::optNoCompilations ); + qb.setGoogleFilter( tables | QueryBuilder::tabSong, m_filter ); + qb.addReturnValue( QueryBuilder::tabSong, QueryBuilder::valURL ); + + values += qb.run(); + + int total = values.count() / qb.countReturnValues(); + + // This shouldn't happen + if( total == 0 ) + return list; + + + QStringList::Iterator it = values.begin(); + KURL tmp; + while ( it != values.end() ) + { + tmp.setPath( (*(it++)) ); + list << tmp; + } + + return list; + } + + + //first pass: parents + for ( item = firstChild(); item; item = item->nextSibling() ) + if ( item->isSelected() ) + { + const bool sampler = static_cast( item )->isSampler(); + qb.clear(); + if ( translateTimeFilter( timeFilter() ) > 0 ) + qb.addFilter( QueryBuilder::tabSong, QueryBuilder::valCreateDate, QString().setNum( QDateTime::currentDateTime().toTime_t() - translateTimeFilter( timeFilter() ) ), QueryBuilder::modeGreater ); + + qb.addReturnValue( QueryBuilder::tabSong, QueryBuilder::valURL ); + + tmptext = static_cast( item )->getSQLText( 0 ); + unknownText = tmptext.isEmpty(); + + if ( !sampler ) + { + if ( q_cat1 == IdArtist ) + qb.setOptions( QueryBuilder::optNoCompilations ); + if( VisYearAlbum == 1 ) + { + tmptext = item->text( 0 ); + QString year = tmptext.left( tmptext.find( i18n(" - ") ) ); + yearAlbumCalc( year, tmptext ); + qb.addMatch( QueryBuilder::tabYear, year, false, true ); + if ( unknownText ) + tmptext = ""; + } + + qb.addMatch( q_cat1, tmptext, false, true ); + } + else + qb.setOptions( QueryBuilder::optOnlyCompilations ); + + qb.setGoogleFilter( q_cat1 | q_cat2 | q_cat3 | QueryBuilder::tabSong, m_filter ); + + if( VisYearAlbum == 1 ) + qb.sortBy( QueryBuilder::tabYear, QueryBuilder::valName); + + if( !sampler ) qb.sortBy( q_cat1, QueryBuilder::valName ); + + if( VisYearAlbum == 2 ) + qb.sortBy( QueryBuilder::tabYear, QueryBuilder::valName); + + if( q_cat2 != QueryBuilder::tabSong ) + qb.sortBy( q_cat2, QueryBuilder::valName ); + + if( VisYearAlbum == 3 ) + qb.sortBy( QueryBuilder::tabYear, QueryBuilder::valName); + + if( q_cat3 != QueryBuilder::tabSong ) + qb.sortBy( q_cat3, QueryBuilder::valName ); + + qb.sortBy( QueryBuilder::tabSong, QueryBuilder::valDiscNumber ); + qb.sortBy( QueryBuilder::tabSong, QueryBuilder::valTrack ); + qb.sortBy( QueryBuilder::tabSong, QueryBuilder::valURL ); + + qb.setOptions( QueryBuilder::optRemoveDuplicates ); + values = qb.run(); + + for ( uint i = 0; i < values.count(); i++ ) + { + KURL tmp; + tmp.setPath( values[i] ); + list << tmp; + } + } + + //second pass: category 1 + if ( m_cat2 == IdNone ) + { + for ( item = firstChild(); item; item = item->nextSibling() ) + for ( QListViewItem* child = item->firstChild(); child; child = child->nextSibling() ) + if ( child->isSelected() && !child->parent()->isSelected() ) + list << static_cast( child ) ->url(); + } + else { + for ( item = firstChild(); item; item = item->nextSibling() ) + for ( QListViewItem* child = item->firstChild(); child; child = child->nextSibling() ) + if ( child->isSelected() && !child->parent()->isSelected() ) + { + const bool sampler = static_cast( item )->isSampler(); + qb.clear(); + if ( translateTimeFilter( timeFilter() ) > 0 ) + qb.addFilter( QueryBuilder::tabSong, QueryBuilder::valCreateDate, QString().setNum( QDateTime::currentDateTime().toTime_t() - translateTimeFilter( timeFilter() ) ), QueryBuilder::modeGreater ); + + qb.addReturnValue( QueryBuilder::tabSong, QueryBuilder::valURL ); + + if ( !sampler ) + { + if ( q_cat1 == IdArtist ) + qb.setOptions( QueryBuilder::optNoCompilations ); + + tmptext = static_cast( item )->getSQLText( 0 ); + unknownText = tmptext.isEmpty(); + + if( VisYearAlbum == 1 ) + { + tmptext = item->text( 0 ); + QString year = tmptext.left( tmptext.find( i18n(" - ") ) ); + yearAlbumCalc( year, tmptext ); + qb.addMatch( QueryBuilder::tabYear, year, false, true ); + if ( unknownText ) + tmptext = ""; + } + + qb.addMatch( q_cat1, tmptext, false, true ); + } + else + qb.setOptions( QueryBuilder::optOnlyCompilations ); + + + tmptext = static_cast( child )->getSQLText( 0 ); + unknownText = tmptext.isEmpty(); + + if( VisYearAlbum == 2 ) + { + tmptext = child->text( 0 ); + QString year = tmptext.left( tmptext.find( i18n(" - ") ) ); + yearAlbumCalc( year, tmptext ); + qb.addMatch( QueryBuilder::tabYear, year, false, true ); + if ( unknownText ) + tmptext = ""; + } + + qb.addMatch( q_cat2, tmptext, false, true ); + + qb.setGoogleFilter( q_cat1 | q_cat2 | q_cat3 | QueryBuilder::tabSong, m_filter ); + + if( VisYearAlbum == 1 ) + qb.sortBy( QueryBuilder::tabYear, QueryBuilder::valName); + + if( !sampler ) + qb.sortBy( q_cat1, QueryBuilder::valName ); + + if( VisYearAlbum == 2 ) + qb.sortBy( QueryBuilder::tabYear, QueryBuilder::valName); + + qb.sortBy( q_cat2, QueryBuilder::valName ); + + if( VisYearAlbum == 3 ) + qb.sortBy( QueryBuilder::tabYear, QueryBuilder::valName); + + if( q_cat3 != QueryBuilder::tabSong ) + qb.sortBy( q_cat3, QueryBuilder::valName ); + + qb.sortBy( QueryBuilder::tabSong, QueryBuilder::valDiscNumber ); + qb.sortBy( QueryBuilder::tabSong, QueryBuilder::valTrack ); + qb.sortBy( QueryBuilder::tabSong, QueryBuilder::valURL ); + + qb.setOptions( QueryBuilder::optRemoveDuplicates ); + values = qb.run(); + + for ( uint i = 0; i < values.count(); i++ ) + { + KURL tmp; + tmp.setPath( values[i] ); + list << tmp; + } + } + } + + //third pass: category 2 + for ( item = firstChild(); item; item = item->nextSibling() ) + for ( QListViewItem* child = item->firstChild(); child; child = child->nextSibling() ) + for ( QListViewItem* grandChild = child->firstChild(); grandChild; grandChild = grandChild->nextSibling() ) + if ( grandChild->isSelected() && !grandChild->isExpandable() && !child->parent()->isSelected() && !child->isSelected() ) + list << static_cast( grandChild ) ->url(); + + //category 3 + if ( m_cat3 == IdNone ) + { + for ( item = firstChild(); item; item = item->nextSibling() ) + for ( QListViewItem* child = item->firstChild(); child; child = child->nextSibling() ) + for ( QListViewItem* grandChild = child->firstChild(); grandChild; grandChild = grandChild->nextSibling() ) + for ( QListViewItem* grandChild2 = grandChild->firstChild(); grandChild2; grandChild2 = grandChild2->nextSibling() ) + if ( grandChild2->isSelected() && !child->parent()->isSelected() && !child->isSelected() && !grandChild->isSelected() ) + list << static_cast( grandChild2 ) ->url(); + } + else { + for ( item = firstChild(); item; item = item->nextSibling() ) + for ( QListViewItem* child = item->firstChild(); child; child = child->nextSibling() ) + for ( QListViewItem* grandChild = child->firstChild(); grandChild; grandChild = grandChild->nextSibling() ) + if ( grandChild->isSelected() && !grandChild->parent()->isSelected() ) + { + const bool sampler = static_cast( item )->isSampler(); + qb.clear(); + if ( translateTimeFilter( timeFilter() ) > 0 ) + qb.addFilter( QueryBuilder::tabSong, QueryBuilder::valCreateDate, QString().setNum( QDateTime::currentDateTime().toTime_t() - translateTimeFilter( timeFilter() ) ), QueryBuilder::modeGreater ); + + qb.addReturnValue( QueryBuilder::tabSong, QueryBuilder::valURL ); + + if ( !sampler ) + { + if ( q_cat1 == IdArtist ) + qb.setOptions( QueryBuilder::optNoCompilations ); + + tmptext = static_cast( item )->getSQLText( 0 ); + unknownText = tmptext.isEmpty(); + + if( VisYearAlbum == 1 ) + { + tmptext = item->text( 0 ); + QString year = tmptext.left( tmptext.find( i18n(" - ") ) ); + yearAlbumCalc( year, tmptext ); + qb.addMatch( QueryBuilder::tabYear, year, false, true ); + if ( unknownText ) + tmptext = ""; + } + + qb.addMatch( q_cat1, tmptext, false, true ); + } + else + qb.setOptions( QueryBuilder::optOnlyCompilations ); + + tmptext = static_cast( child )->getSQLText( 0 ); + unknownText = tmptext.isEmpty(); + + if( VisYearAlbum == 2 ) + { + tmptext = child->text( 0 ); + QString year = tmptext.left( tmptext.find( i18n(" - ") ) ); + yearAlbumCalc( year, tmptext ); + qb.addMatch( QueryBuilder::tabYear, year, false, true ); + if ( unknownText ) + tmptext = ""; + } + + qb.addMatch( q_cat2, tmptext, false, true ); + + tmptext = static_cast( grandChild )->getSQLText( 0 ); + unknownText = tmptext.isEmpty(); + + if( VisYearAlbum == 3 ) + { + tmptext = grandChild->text( 0 ); + QString year = tmptext.left( tmptext.find( i18n(" - ") ) ); + yearAlbumCalc( year, tmptext ); + qb.addMatch( QueryBuilder::tabYear, year, false, true ); + if ( unknownText ) + tmptext = ""; + } + + qb.addMatch( q_cat3, tmptext, false, true ); + + qb.setGoogleFilter( q_cat1 | q_cat2 | q_cat3 | QueryBuilder::tabSong, m_filter ); + + if( VisYearAlbum == 1 ) + qb.sortBy( QueryBuilder::tabYear, QueryBuilder::valName); + + if( !sampler ) + qb.sortBy( q_cat1, QueryBuilder::valName ); + + if( VisYearAlbum == 2 ) + qb.sortBy( QueryBuilder::tabYear, QueryBuilder::valName); + + qb.sortBy( q_cat2, QueryBuilder::valName ); + + if( VisYearAlbum == 3 ) + qb.sortBy( QueryBuilder::tabYear, QueryBuilder::valName); + + qb.sortBy( q_cat3, QueryBuilder::valName ); + + qb.sortBy( QueryBuilder::tabSong, QueryBuilder::valDiscNumber ); + qb.sortBy( QueryBuilder::tabSong, QueryBuilder::valTrack ); + qb.sortBy( QueryBuilder::tabSong, QueryBuilder::valURL ); + + qb.setOptions( QueryBuilder::optRemoveDuplicates ); + values = qb.run(); + + for ( uint i = 0; i < values.count(); i++ ) + { + KURL tmp; + tmp.setPath( values[i] ); + list << tmp; + } + } + } + + //category 3 + for ( item = firstChild(); item; item = item->nextSibling() ) + for ( QListViewItem* child = item->firstChild(); child; child = child->nextSibling() ) + for ( QListViewItem* grandChild = child->firstChild(); grandChild; grandChild = grandChild->nextSibling() ) + for ( QListViewItem* grandChild2 = grandChild->firstChild(); grandChild2; grandChild2 = grandChild2->nextSibling() ) + if ( grandChild2->isSelected() && !child->parent()->isSelected() && !child->isSelected() && !grandChild->isSelected() ) + list << static_cast( grandChild2 ) ->url(); + + return list; +} + + +void +CollectionView::playlistFromURLs( const KURL::List &urls ) +{ + QString suggestion; + typedef QListViewItemIterator It; + It it( this, It::Visible | It::Selected ); + if( (*it) && !(*(++it)) ) + suggestion = (*It( this, It::Visible | It::Selected ))->text( 0 ); + else + suggestion = i18n( "Untitled" ); + const QString path = PlaylistDialog::getSaveFileName( suggestion ); + + if( path.isEmpty() ) + return; + + CollectionDB* db = CollectionDB::instance(); + + QValueList titles; + QValueList lengths; + for( KURL::List::ConstIterator it = urls.constBegin(), end = urls.constEnd(); it != end; ++it ) + { + int deviceid = MountPointManager::instance()->getIdForUrl( *it ); + KURL rpath; + MountPointManager::instance()->getRelativePath( deviceid, *it, rpath ); + const QString query = QString("SELECT title, length FROM tags WHERE url = '%1' AND deviceid = %2;") + .arg( db->escapeString( rpath.path() ) ).arg( deviceid ); + debug() << "media id: " << deviceid << " rpath: " << rpath.path() << endl; + QStringList result = db->query( query ); + titles << result[0]; + lengths << result[1].toInt(); + } + + if( PlaylistBrowser::savePlaylist( path, urls, titles, lengths ) ) + PlaylistWindow::self()->showBrowser( "PlaylistBrowser" ); +} + +QPixmap +CollectionView::iconForCategory( const int cat ) const +{ + QString icon; + switch( cat ) + { + case IdAlbum: + icon = "cdrom_unmount"; + break; + case IdVisYearAlbum: + icon = "cdrom_unmount"; + break; + case IdArtist: + icon = "personal"; + break; + case IdComposer: + icon = "personal"; + break; + + case IdGenre: + icon = "kfm"; + break; + + case IdYear: + icon = "history"; + break; + + case IdLabel: + icon = "kfm"; + break; + } + + return KGlobal::iconLoader()->loadIcon( icon, KIcon::Toolbar, KIcon::SizeSmall ); +} + +QString +CollectionView::captionForCategory( const int cat ) const +{ + switch( cat ) + { + case IdAlbum: + return i18n( "Album" ); + break; + case IdVisYearAlbum: + return i18n( "Year" ) + i18n( " - " ) + i18n( "Album" ); + break; + case IdArtist: + return i18n( "Artist" ); + break; + case IdComposer: + return i18n( "Composer" ); + break; + + case IdGenre: + return i18n( "Genre" ); + break; + + case IdYear: + return i18n( "Year" ); + break; + case IdLabel: + return i18n( "Label" ); + break; + } + + return QString(); +} + + +QString +CollectionView::captionForTag( const Tag tag) const +{ + QString caption; + switch( tag ) + { + case Artist: caption = i18n( "Artist" ); break; + case Album: caption = i18n( "Album" ); break; + case Genre: caption = i18n( "Genre" ); break; + case Title: caption = i18n( "Title" ); break; + case Length: caption = i18n( "Length" ); break; + case DiscNumber:caption = i18n( "Disc Number" ); break; + case Track: caption = i18n( "Track" ); break; + case Year: caption = i18n( "Year" ); break; + case Comment: caption = i18n( "Comment" ); break; + case Composer: caption = i18n( "Composer" ); break; + case Playcount: caption = i18n( "Playcount" ); break; + case Score: caption = i18n( "Score" ); break; + case Rating: caption = i18n( "Rating" ); break; + case Filename: caption = i18n( "Filename" ); break; + case Firstplay: caption = i18n( "First Play" ); break; + case Lastplay: caption = i18n( "Last Play" ); break; + case Modified: caption = i18n( "Modified Date" ); break; + case Bitrate: caption = i18n( "Bitrate" ); break; + case Filesize: caption = i18n( "File Size" ); break; + case BPM: caption = i18n( "BPM" ); break; + default: break; + } + return caption; +} + + +////////////////////////////// +// For iPod-style navigation +////////////////////////////// + +/* + * Overview + * -------- + * + * The iPod-style navigation is a (1, 2, or) 3-tier filtering process + * (depending on the current "Group By" setting). For concreteness + * let's say the user is grouping by Genre, Artist, Album. The first + * screen he is presented with is a list of the genres, along with an + * "All genres" option (unless there is only one genre). He selects + * one or more genres, and clicks the right arrow in the toolbar. He + * is then presented with a list of albums whose genre matches one of + * the genres he has just chosen, unless he has chosen "All genres", + * in which case all albums are shown. This is repeated until he gets + * to an actual track list. If the user clicks the left arrow, he is + * taken back to the previous screen, with his previous selection + * still intact. + * + * + * Interface + * --------- + * + * The two main actions the user can perform are "browse forward" and + * "browse backward", otherwise known as increment and decrement the + * browse depth. There is a small toolbar with these two buttons + * located to the right of the time filter combobox, which is enabled + * in iPod view mode. If the user is not viewing tracks, there is + * also a small browse forward button to the right of each entry on + * his screen. If the user is viewing tracks, the browse forward + * action adds the currently selected tracks to the playlist. + * Pressing the right or left keys is an alternate way of browsing + * forward or backward. At any point the user may drag and drop, press + * return, or double-click to add the current selection to the playlist; + * the logic for this is explained in a comment in listSelected(). If + * divider mode is on, dividers are added as expected when not in track + * mode. + * + * + * Related methods + * --------------- + * + * CollectionBrowser::ipodToolbar() + * -- (de)activate the toolbar with the browse buttons + * CollectionView::keyPressEvent() + * -- handle the Left and Right keys, as well as the Up and Down keys + * to allow for wrapping around and skipping dividers + * CollectionView::renderView() + * -- logic for applying the current filter and adding the matching + * items to the screen + * CollectionView::ipodItemClicked() + * -- slot for activating the browse forward action when the right + * arrow on a listview item is clicked + * CollectionView::setViewMode() + * -- enable/disable iPod toolbar + * CollectionView::updateColumnHeader() + * CollectionView::listSelected() + * -- apply current filters and current selection to get a track + * list, and order it correctly + * CollectionView::allForCategory() + * -- return the text for the "All" item in the current category + * CollectionView::incrementDepth() + * -- save the current selection away as a filter, and remember it + * in case the user browses back so we can reselect + * CollectionView::decrementDepth() + * -- delete the current filter and reselect the saved selection + * CollectionView::resetIpodDepth() + * -- reset the iPod view mode to the first screen + * CollectionView::buildIpodQuery() + * -- adds query and sort criteria to a QueryBuilder, based on the + * saved filters + * CollectionView::selectIpodItems() + * -- if we've just browsed forward, select the All item (or the + * unique item if there's only one). If we've just browsed back, + * reselect the saved selection. + * CollectionView::ipodIncrementIcon(), CollectionView::ipodDecrementIcon() + * -- returns a QPixmap of the small version of the right, resp. left + * arrow buttons + * CollectionView::viewportResizeEvent() + * CollectionItem::compare() + * -- in iPod mode the "All" item goes first + * + */ + + +// Returns the text for the "All" filter option for the given category +// and the number of items in that category. +QString +CollectionView::allForCategory( const int cat, const int num ) const +{ + switch( cat ) + { + // The singular forms shouldn't get used + case IdAlbum: + case IdVisYearAlbum: + return i18n( "Album", "All %n Albums", num ); + break; + case IdArtist: + return i18n( "Artist", "All %n Artists", num ); + break; + case IdComposer: + return i18n( "Composer", "All %n Composers", num ); + break; + case IdGenre: + return i18n( "Genre", "All %n Genres", num ); + break; + case IdYear: + return i18n( "Year", "All %n Years", num ); + break; + case IdLabel: + return i18n( "Label", "All %n Labels", num ); + break; + } + + return QString(); +} + +// This slot is called when the "browse right" action is activated, +// and also in listSelected(). It is responsible for saving the +// current selection in two ways: first, it saves the selection as +// a list of query match criteria in m_ipodFilters and m_ipodFilterYear, +// and second, it saves the currently selected items, the current item, +// and the screen position in m_ipodSelected, m_ipodCurrent, and +// m_ipodTopItem. +// The reason there's a separate m_ipodFilterYear which is not an +// array of lists, is because if one of the categories is IdVisYearAlbum, +// we want to save the album filter separately from the year filter; +// since there's only one category that is IdVisYearAlbum, we don't need +// an array of years. +// The main reason the cache data (m_ipodSelected, etc.) is separate +// from the filter data (m_ipodFilters, etc.), apart from the fact it's +// easier to code this way, is that the filter data must be deleted +// immediately upon browsing left, whereas the cache data must not. +// If we're in track mode, this just inserts the currently selected +// tracks into the playlist. +void +CollectionView::incrementDepth( bool rerender /*= true*/ ) +{ + if( m_viewMode != modeIpodView ) + return; + if( selectedItems().count() == 0 ) + return; + + // Track mode? + if( m_currentDepth == trackDepth() ) + { + Playlist::instance()->insertMedia( listSelected(), Playlist::Unique | Playlist::Append ); + return; + } + + m_parent->m_ipodDecrement->setEnabled( true ); + + // We're not in track mode + int catArr[3] = {m_cat1, m_cat2, m_cat3}; + int cat = catArr[m_currentDepth]; + + // Clear filters and cache data at this level + m_ipodFilters[m_currentDepth].clear(); + if( cat == IdVisYearAlbum ) + m_ipodFilterYear.clear(); + + m_ipodSelected[m_currentDepth].clear(); + m_ipodCurrent[m_currentDepth] = QString::null; + m_ipodTopItem[m_currentDepth] = QString::null; + + // Save the current item + if( currentItem() ) + m_ipodCurrent[m_currentDepth] = currentItem()->text( 0 ); + + //cache viewport's top item + QListViewItem* item = itemAt( QPoint(0, 0) ); + if ( item ) + m_ipodTopItem[m_currentDepth] = item->text( 0 ); + + // Figure out the next filter, and save the current selection + QPtrList selected = selectedItems(); + QPtrList::iterator it = selected.begin(); + + while( it != selected.end() ) + { + CollectionItem *item = dynamic_cast( *it ); + ++it; + if( item == 0 ) + continue; + + // No filter if "All" is selected + if( item->isSampler() ) + { + m_ipodFilters[m_currentDepth].clear(); + if( cat == IdVisYearAlbum ) + m_ipodFilterYear.clear(); + + // If "All" is selected then don't bother saving this + // selection, since All will then be reselected automatically + // in selectIpodItems() + m_ipodSelected[m_currentDepth].clear(); + m_ipodCurrent[m_currentDepth] = QString::null; + + break; + } + + if( cat == IdVisYearAlbum ) + { + QString tmptext = item->text( 0 ); + QString year = tmptext.left( tmptext.find( i18n(" - ") ) ); + yearAlbumCalc( year, tmptext ); + if( !item->isUnknown() ) + m_ipodFilters[m_currentDepth] << tmptext; + else + m_ipodFilters[m_currentDepth] << ""; + m_ipodFilterYear << year; // May be "" + } + + else + m_ipodFilters[m_currentDepth] << item->getSQLText( 0 ); + + // Save the selection + m_ipodSelected[m_currentDepth] << item->text( 0 ); + } + + m_currentDepth++; + + if( rerender ) + { + updateColumnHeader(); + m_ipodIncremented = 1; + renderView( true ); + } +} + + +// This slot is basically responsible for undoing whatever +// incrementDepth did. Namely, it deletes the filter at +// the previous level; selectIpodItems() will then be called +// from renderView() to reselect the remembered selection. +void +CollectionView::decrementDepth ( bool rerender /*= true*/ ) +{ + if( m_viewMode != modeIpodView ) + return; + if( m_currentDepth <= 0 ) + return; + + m_currentDepth--; + m_parent->m_ipodDecrement->setEnabled( m_currentDepth > 0 ); + m_ipodFilters[m_currentDepth].clear(); + int catArr[3] = {m_cat1, m_cat2, m_cat3}; + int cat = catArr[m_currentDepth]; + if( cat == IdVisYearAlbum ) + m_ipodFilterYear.clear(); + + // Clear the selection on higher levels + for( int i = m_currentDepth + 1; i < 3; ++i ) + { + m_ipodSelected[i].clear(); + m_ipodCurrent[i] = QString::null; + m_ipodTopItem[i] = QString::null; + } + + if( rerender ) + { + m_ipodIncremented = 2; + updateColumnHeader(); + renderView( true ); + } +} + + +// This resets the ipod view mode to the first screen. +// Call updateColumnHeader() as well whenever you run this +void +CollectionView::resetIpodDepth ( void ) +{ + m_currentDepth = 0; + m_ipodFilterYear.clear(); + m_ipodFilters[0].clear(); + m_ipodFilters[1].clear(); + m_ipodFilters[2].clear(); + m_ipodIncremented = 1; + m_parent->m_ipodDecrement->setEnabled( false ); +} + + + +// This method is the querying workhorse for the iPod browsing code. +// It is used both to populate the content browser (in renderView()) +// and to generate track lists (in listSelected()). This method only +// runs qb.addMatch() and qb.sortBy() (as well as one qb.addFilter() +// below); the caller should run qb.setGoogleFilter(), +// qb.addReturnValue(), etc. +// The sorting is as follows: if recursiveSort is true (for +// listSelected()), then it sorts first by m_cat1, then by m_cat2, +// then m_cat3, then track; if recursiveSort is false (for +// renderView()), it only sorts by the category at m_currentDepth. +// Tracks are sorted by track number first if either of the two +// following conditions hold: +// (i) recursiveSort is true and one of the categories is (Year +) Album +// (ii) recursiveSort is false and only one album is selected +// This most closely mimics the behavior of the list view, as well as +// an actual iPod. +// The compilationsOnly argument does *not* set the onlyCompilations +// option (setting options is up to the caller); all it does is disable +// sorting by artist if recursiveSort is on. This is because when doing +// a compilations-only search, all tracks should behave as if the artist +// were Various Artists for sorting purposes. +void +CollectionView::buildIpodQuery ( QueryBuilder &qb, int depth, QStringList filters[3], + QStringList filterYear, bool recursiveSort /*= false*/, bool compilationsOnly /*= false*/) +{ + int catArr[3] = {m_cat1, m_cat2, m_cat3}; + int q_cat; + bool stillFiltering = (depth < trackDepth()); + bool SortbyTrackFirst = false; + + // First apply the filters from previous screens + for( int i = 0; i < depth; ++i ) + { + q_cat = catArr[i]; + + if( q_cat == IdVisYearAlbum ) + { + q_cat = IdAlbum; + + if( filters[i].count() > 0 ) + { + // This is very annoying -- we have to do an OR of queries + // of the form (album = ? AND year = ??) + QStringList::iterator album = filters[i].begin(); + QStringList::iterator year = filterYear.begin(); + + qb.beginOR(); + + while( album != filters[i].end() ) + { + qb.beginAND(); + qb.addMatch( QueryBuilder::tabAlbum, *album, false, true ); + qb.addMatch( QueryBuilder::tabYear, *year, false, true ); + qb.endAND(); + + ++album; + ++year; + } + + qb.endOR(); + } + + if( recursiveSort ) + qb.sortBy( QueryBuilder::tabYear, QueryBuilder::valName ); + + } + + else + { + if( filters[i].count() > 0 ) + qb.addMatches( q_cat, filters[i], false, true ); + } + + // Don't sort by artist if we're getting compilations + if( recursiveSort + && !(compilationsOnly && q_cat == IdArtist) ) + qb.sortBy( q_cat, QueryBuilder::valName ); + + // Sort by track first subject to the conditions described above + if( q_cat == IdAlbum && + (filters[i].count() == 1 || recursiveSort) ) + SortbyTrackFirst = true; + } + + + // Now add the non-recursive sort-by + if( stillFiltering ) // Are we showing a category? + { + q_cat = catArr[depth]; + if( q_cat == IdVisYearAlbum ) + { + q_cat = IdAlbum; + qb.sortBy( QueryBuilder::tabYear, QueryBuilder::valName ); + } + + qb.sortBy( q_cat, QueryBuilder::valName ); + + // ensure we don't get empty genres/albums/etc due to tag changes + qb.addFilter( QueryBuilder::tabSong, QString::null ); + + } + + // ... or are we showing tracks? + else + { + if ( SortbyTrackFirst ) { + qb.sortBy( QueryBuilder::tabSong, QueryBuilder::valDiscNumber ); + qb.sortBy( QueryBuilder::tabSong, QueryBuilder::valTrack ); + } + qb.sortBy( QueryBuilder::tabSong, QueryBuilder::valTitle ); + if ( !SortbyTrackFirst ) { + qb.sortBy( QueryBuilder::tabSong, QueryBuilder::valDiscNumber ); + qb.sortBy( QueryBuilder::tabSong, QueryBuilder::valTrack ); + } + qb.sortBy( QueryBuilder::tabSong, QueryBuilder::valURL ); + } + +} + + +// This method is responsible for either selecting the "All" item +// if the iPod depth has been incremented, or selecting the previously +// remembered items if the depth has been decremented, depending on +// m_ipodIncremented, m_ipodSelected, m_ipodCurrent, and m_ipodTopItem. +// Note that if the previously selected items have disappeared (due to +// a GoogleFilter being applied, e.g.) then we select the "All" item by +// default. +// Note that if there is only one item in the list then there is no +// All option, so we select the unique item. +void +CollectionView::selectIpodItems ( void ) +{ + if( m_viewMode != modeIpodView || + m_ipodIncremented == 0 ) + { + m_ipodIncremented = 0; + return; + } + + // If we've just decremented the iPod view depth then remember + // the selection and the current item last time we incremented. + // Note that a filter or something may have happened between then + // and now, so we should allow for those items no longer being + // here (in which case we pass through to the code below) + if( m_ipodIncremented == 2 ) + { + // Pedantry -- presumably we're not at track depth! + if( m_currentDepth == trackDepth() ) + { + m_ipodIncremented = 0; + return; + } + + // If there's no selection or the selected items have + // disappeared, pass through to the code below + if( m_ipodSelected[m_currentDepth].count() == 0 ) + m_ipodIncremented = 1; + + else + { + KListView::selectAll( false ); + int selected = 0; + QStringList::iterator it = m_ipodSelected[m_currentDepth].begin(); + while( it != m_ipodSelected[m_currentDepth].end() ) + { + QListViewItem *item = findItem( *it, 0 ); + ++it; + + if( !item ) + continue; + + selected++; + // If the saved currentItem has disappeared, it's more + // intuitive if the last selected item is current. + setCurrentItem( item ); + item->setSelected( true ); + setSelectionAnchor( item ); + } + + // Pass through to below + if( selected == 0 ) + m_ipodIncremented = 1; + + else + { + // Remember the current item and scroll position + if( !m_ipodTopItem[m_currentDepth].isEmpty() && + !m_ipodTopItem[m_currentDepth].isNull() ) + { + //scroll to previous viewport top item + QListViewItem* item = findItem( m_ipodTopItem[m_currentDepth], 0 ); + if ( item ) + setContentsPos( 0, itemPos( item ) ); + } + + if( !m_ipodCurrent[m_currentDepth].isEmpty() && + !m_ipodCurrent[m_currentDepth].isNull() ) + { + QListViewItem *item = findItem( m_ipodCurrent[m_currentDepth], 0); + if( item ) + setCurrentItem( item ); + } + } + } + } + + // If we've just incremented the iPod view depth (or are displaying + // the iPod window for the first time) then automatically select the + // All option (or the only element of the list) for keyboard + // navigation + if( m_ipodIncremented == 1 ) + { + KListView::selectAll( false ); + QListViewItem *item = firstChild(); + + // There will be a divider in the first slot if there is only + // one item in the list and m_showDivider is on + while( item && dynamic_cast( item ) ) + item = item->itemBelow(); + + if( item ) + { + setCurrentItem( item ); + item->setSelected( true ); + setSelectionAnchor( item ); + setContentsPos( 0, itemPos( item ) ); + } + } + + m_ipodIncremented = 0; +} + + +// Convenience methods for returning the correct (small version of) +// the browse forward / backward buttons + +QPixmap +CollectionView::ipodIncrementIcon ( void ) +{ + return SmallIcon( Amarok::icon( "fastforward" ) ); +} + +QPixmap +CollectionView::ipodDecrementIcon ( void ) +{ + return SmallIcon( Amarok::icon( "rewind" ) ); +} + +void +CollectionView::setCompilation( const KURL::List &urls, bool compilation ) +{ + //visual feedback + QApplication::setOverrideCursor( KCursor::waitCursor() ); + + //Set it in the DB. We don't need to update the view now as we do it at the end. + CollectionDB::instance()->setCompilation( urls, compilation, false ); + + foreachType( KURL::List, urls ) { + if ( !TagLib::File::isWritable( QFile::encodeName( ( *it ).path() ) ) ) + continue; + + MetaBundle mb( *it ); + + mb.setCompilation( compilation ? MetaBundle::CompilationYes : MetaBundle::CompilationNo ); + + if( mb.save() ) { + mb.updateFilesize(); + //update the collection db, since filesize might have changed + CollectionDB::instance()->updateTags( mb.url().path(), mb, false ); + } + } + //visual feedback + QApplication::restoreOverrideCursor(); + if ( !urls.isEmpty() ) renderView(true); +} + +void +CollectionView::cacheView() +{ + //free cache + m_cacheOpenItemPaths.clear(); + + //Store the current item + m_cacheCurrentItem = makeStructuredNameList( currentItem() ); + + //cache expanded/open items + if ( m_viewMode == modeTreeView ) { + QListViewItemIterator it( this ); + while ( it.current() ) { + QListViewItem *item = it.current(); + if ( item->isOpen() ) { + //construct path to item + QStringList itemPath; + for( const QListViewItem *i = item; i; i = i->parent() ) + itemPath.prepend( i->text( 0 ) ); + + m_cacheOpenItemPaths.append ( itemPath ); + } + ++it; + } + } + + //cache viewport's top item + m_cacheViewportTopItem = makeStructuredNameList( itemAt( QPoint(0, 0) ) ); +} + + +void +CollectionView::restoreView() +{ + //expand cached items + if ( m_viewMode == modeTreeView ) { + QValueList::const_iterator it; + for ( it = m_cacheOpenItemPaths.begin(); it != m_cacheOpenItemPaths.end(); ++it ) { + QListViewItem* item = findItem( (*it)[0], 0 ); + if ( item ) + item->setOpen ( true ); + + for ( uint i = 1; i < (*it).count() && item; ++i ) { + item = item->firstChild(); + while ( item ) { + if ( item->text(0) == (*it)[ i ] ) + item->setOpen ( true ); + item = item->nextSibling(); + } + } + } + } + + //scroll to previous viewport top item + QListViewItem* item = findFromStructuredNameList( m_cacheViewportTopItem ); + if ( item ) + setContentsPos( 0, itemPos(item) ); + + //Restore a selected item (all levels of the tree stored to fully specify which item) + item = findFromStructuredNameList( m_cacheCurrentItem ); + if ( item ) + { + setCurrentItem( item ); + item->setSelected( true ); + // More intuitive if shift-click selects from current selection + setSelectionAnchor( item ); + } + + //free cache + m_cacheOpenItemPaths.clear(); + m_cacheViewportTopItem = QStringList(); + m_cacheCurrentItem = QStringList(); +} + +QStringList +CollectionView::makeStructuredNameList( QListViewItem *item ) const +{ + QStringList nameList; + for ( QListViewItem *current = item; current; current = current->parent() ) + nameList.push_front( current->text( 0 ) ); + return nameList; +} + +QListViewItem* +CollectionView::findFromStructuredNameList( const QStringList &nameList ) const +{ + QListViewItem *item( firstChild() ); + bool firstTime = true; + foreach( nameList ) + { + if ( !firstTime ) + item = item->firstChild(); + else + firstTime = false; + + while ( item && item->text( 0 ) != *it ) + item = item->nextSibling(); + + if ( !item ) + { + debug() << "Could not find expected element to select: " << nameList << endl; + break; + } + } + return nameList.isEmpty() ? 0 : item; +} + + +// Small function aimed to convert Eagles, The -> The Eagles (and back again) +// TODO Internationlise +void +CollectionView::manipulateThe( QString &str, bool reverse ) +{ + if( reverse ) + { + QString begin = str.left( 3 ); + str = str.append( ", %1" ).arg( begin ); + str = str.mid( 4 ); + return; + } + + if( !endsInThe( str ) ) + return; + + QString end = str.right( 3 ); + str = str.prepend( "%1 " ).arg( end ); + + uint newLen = str.length() - end.length() - 2; + + str.truncate( newLen ); +} + +bool +CollectionView::endsInThe( const QString & text ) +{ + return text.endsWith( ", the", false ); +} + +// avoid code duplication +void +CollectionView::yearAlbumCalc( QString &year, QString &text ) +{ + if( year == "\?" ) + year = ""; + + text = text.right( text.length() - + text.find( i18n(" - ") ) - + i18n(" - ").length() ); +} + +void +CollectionView::viewportPaintEvent( QPaintEvent *e ) +{ + KListView::viewportPaintEvent( e ); + + // Superimpose bubble help for Flat-View mode: + + if ( m_viewMode == modeFlatView && childCount() == 0 ) + { + QPainter p( viewport() ); + + QSimpleRichText t( i18n( + "
" + "

Flat-View Mode

" + "To enable the Flat-View mode, please enter search terms in the search line above." + "
" ), QApplication::font() ); + + t.setWidth( width() - 50 ); + + const uint w = t.width() + 20; + const uint h = t.height() + 20; + + p.setBrush( colorGroup().background() ); + p.drawRoundRect( 15, 15, w, h, (8*200)/w, (8*200)/h ); + t.draw( &p, 20, 20, QRect(), colorGroup() ); + } +} + + +void +CollectionView::updateTrackDepth() { + bool m3 = (m_cat3 == IdNone); + bool m2 = (m_cat2 == IdNone); + bool m1 = (m_cat1 == IdNone); + if ( m3 || m2 || m1) { + //The wanted depth, is the lowest IdNone + if (m3) + m_trackDepth = 2; + if (m2) + m_trackDepth = 1; + if (m1) + m_trackDepth = 0; + } + else // If there's no IdNone, then it's 3 + m_trackDepth = 3; +} + +void +CollectionView::viewportResizeEvent( QResizeEvent* e) +{ + if( m_viewMode != modeIpodView ) + { + header()->blockSignals( true ); + + const double width = e->size().width(); + int visibleColumns = 0; + for ( int i = 0; i < columns(); ++i ) + if ( columnWidth( i ) != 0 ) + visibleColumns ++; + int correct = e->size().width() - (e->size().width() / visibleColumns) * visibleColumns; + + if( m_viewMode == modeFlatView ) + m_flatColumnWidths.clear(); + + if ( visibleColumns != 0 ) { + for ( int c = 0; c < columns(); ++c ) { + int w = columnWidth( c ) ? static_cast( width/visibleColumns ) : 0; + if ( w > 0 ) + { + w += correct; + correct = 0; + setColumnWidth( c, w ); + } + if( m_viewMode == modeFlatView ) + m_flatColumnWidths.push_back( w ); + } + } + + header()->blockSignals( false ); + } + + // iPod-mode header adjustment code + else + { + // Don't use header()->adjustHeaderSize(), since that doesn't + // do a very good job. Instead we treat the browse-forward-button + // column as rigid, and stretch the text column to exactly fit + // the width. + + int width = visibleWidth(); + int col1width = 0; + // No column 1 for tracks + if( m_currentDepth != trackDepth() ) + col1width = columnWidth(1); + setColumnWidth( 0, width - col1width ); + } + + // Needed for correct redraw of bubble help + triggerUpdate(); +} + +bool +CollectionView::eventFilter( QObject* o, QEvent* e ) +{ + if( o == header() + && e->type() == QEvent::MouseButtonPress + && static_cast( e )->button() == Qt::RightButton + && m_viewMode == modeFlatView ) + { + KPopupMenu popup; + popup.setCheckable( true ); + popup.insertTitle( i18n( "Flat View Columns" ), /*id*/ -1, /*index*/ 1 ); + + for ( int i = 0; i < columns(); ++i ) + { + popup.insertItem( captionForTag( static_cast( i ) ), i ); + popup.setItemChecked( i, ( columnWidth(i) != 0 ) ); + } + + //title column should always be shown + popup.setItemEnabled( Title, false ); + popup.setItemVisible( Score, AmarokConfig::useScores() ); + popup.setItemVisible( Rating, AmarokConfig::useRatings() ); + + const int returnID = popup.exec( static_cast(e)->globalPos() ); + + if ( returnID != -1 ) + { + if ( columnWidth( returnID ) == 0 ) { + adjustColumn( returnID ); // show column + header()->setResizeEnabled( true, returnID ); + renderView(true); + } + else { + setColumnWidth ( returnID, 0 ); // hide column + header()->setResizeEnabled( false, returnID ); + } + //manage column widths + QResizeEvent rev ( size(), QSize() ); + viewportResizeEvent( &rev ); + } + + m_flatColumnWidths.clear(); + for ( int c = 0; c < columns(); ++c ) + m_flatColumnWidths.push_back( columnWidth( c ) ); + + return true; + } + + return KListView::eventFilter( o, e ); +} + +uint CollectionView::translateTimeFilter( uint filterMode ) +{ + uint filterSecs = 0; + switch ( filterMode ) + { + case 1: + // added today + filterSecs = 60 * 60 * 24; + break; + + case 2: + // added within one week + filterSecs = 60 * 60 * 24 * 7; + break; + + case 3: + // added within one month + filterSecs = 60 * 60 * 24 * 30; + break; + + case 4: + // added within three months + filterSecs = 60 * 60 * 24 * 91; + break; + + case 5: + // added within one year + filterSecs = 60 * 60 * 24 * 365; + break; + } + + return filterSecs; +} + +void +CollectionView::renderFlatModeView( bool /*=false*/ ) +{ + QStringList values; + QueryBuilder qb; + + if ( translateTimeFilter( timeFilter() ) > 0 ) + qb.addFilter( QueryBuilder::tabSong, QueryBuilder::valCreateDate, QString().setNum( QDateTime::currentDateTime().toTime_t() - translateTimeFilter( timeFilter() ) ), QueryBuilder::modeGreater ); + + if ( translateTimeFilter( timeFilter() ) <= 0 + && (m_filter.length() < 3 || (!m_filter.contains( " " ) && m_filter.endsWith( ":" ) ) ) ) + { + // Redraw bubble help + triggerUpdate(); + return; + } + + QValueList visibleColumns; + for ( int c = 0; c < columns(); ++c ) + if ( columnWidth( c ) != 0 ) + { + visibleColumns.append( static_cast( c ) ); + } + + //always fetch URL + qb.addReturnValue( QueryBuilder::tabSong, QueryBuilder::valURL ); + //device automatically added + + int filterTables = 0; + for ( QValueList::ConstIterator it = visibleColumns.constBegin(); it != visibleColumns.constEnd(); ++it ) + { + switch ( *it ) + { + case Artist: + qb.addReturnValue( QueryBuilder::tabArtist, QueryBuilder::valName, true ); + filterTables |= QueryBuilder::tabArtist; + break; + case Composer: + qb.addReturnValue ( QueryBuilder::tabComposer, QueryBuilder::valName, true ); + filterTables |= QueryBuilder::tabComposer; + break; + case Album: + qb.addReturnValue( QueryBuilder::tabAlbum, QueryBuilder::valName, true ); + filterTables |= QueryBuilder::tabAlbum; + break; + case Genre: + qb.addReturnValue( QueryBuilder::tabGenre, QueryBuilder::valName, true ); + filterTables |= QueryBuilder::tabGenre; + break; + case Title: + qb.addReturnValue( QueryBuilder::tabSong, QueryBuilder::valTitle, true ); + filterTables |= QueryBuilder::tabSong; + break; + case Length: + qb.addReturnValue ( QueryBuilder::tabSong, QueryBuilder::valLength ); + filterTables |= QueryBuilder::tabSong; + break; + case DiscNumber: + qb.addReturnValue ( QueryBuilder::tabSong, QueryBuilder::valDiscNumber ); + filterTables |= QueryBuilder::tabSong; + break; + case Track: + qb.addReturnValue ( QueryBuilder::tabSong, QueryBuilder::valTrack ); + filterTables |= QueryBuilder::tabSong; + break; + case Year: + qb.addReturnValue ( QueryBuilder::tabYear, QueryBuilder::valName ); + filterTables |= QueryBuilder::tabYear; + break; + case Comment: + qb.addReturnValue ( QueryBuilder::tabSong, QueryBuilder::valComment ); + filterTables |= QueryBuilder::tabSong; + break; + case Playcount: + qb.addReturnValue ( QueryBuilder::tabStats, QueryBuilder::valPlayCounter ); + break; + case Score: + qb.addReturnValue( QueryBuilder::tabStats, QueryBuilder::valScore ); + break; + case Rating: + qb.addReturnValue( QueryBuilder::tabStats, QueryBuilder::valRating ); + break; + case Filename: + qb.addReturnValue ( QueryBuilder::tabSong, QueryBuilder::valRelativePath ); + break; + case Firstplay: + qb.addReturnValue ( QueryBuilder::tabStats, QueryBuilder::valCreateDate ); + break; + case Lastplay: + qb.addReturnValue ( QueryBuilder::tabStats, QueryBuilder::valAccessDate ); + break; + case Modified: + qb.addReturnValue ( QueryBuilder::tabSong, QueryBuilder::valCreateDate ); + break; + case Bitrate: + qb.addReturnValue ( QueryBuilder::tabSong, QueryBuilder::valBitrate ); + break; + case Filesize: + qb.addReturnValue ( QueryBuilder::tabSong, QueryBuilder::valFilesize ); + break; + case BPM: + qb.addReturnValue ( QueryBuilder::tabSong, QueryBuilder::valBPM ); + filterTables |= QueryBuilder::tabSong; + break; + default: + qb.addReturnValue( QueryBuilder::tabDummy, QueryBuilder::valDummy ); + break; + } + } + + qb.setGoogleFilter( filterTables, m_filter ); + qb.sortBy( QueryBuilder::tabSong, QueryBuilder::valTitle ); + qb.setOptions( QueryBuilder::optRemoveDuplicates ); + + //we leftjoin the query so it can return mysql NULL cells, i.e. for score and playcount + //this is an ugly hack - should be integrated in querybuilder itself instead. + QString leftQuery = qb.query(); + leftQuery.replace( "INNER JOIN", "LEFT JOIN" ); + values = CollectionDB::instance()->query( leftQuery ); + + //construct items + QStringList::ConstIterator it = values.constBegin(); + QStringList::ConstIterator end = values.constEnd(); + while ( it != end ) + { + CollectionItem* item = new CollectionItem( this ); + item->setDragEnabled( true ); + item->setDropEnabled( false ); + QString rpath = *it; + item->setUrl( MountPointManager::instance()->getAbsolutePath( (*++it).toInt(), rpath ) ); + ++it; + + for ( QValueList::ConstIterator it_c = visibleColumns.constBegin(); it_c != visibleColumns.constEnd(); ++it_c ) + { + switch ( *it_c ) + { + case Length: + item->setText( *it_c, MetaBundle::prettyLength( (*it).toInt(), false) ); + break; + case Bitrate: + item->setText( *it_c, MetaBundle::prettyBitrate( (*it).toInt() ) ); + break; + case Firstplay: + case Lastplay: + case Modified: + { + QDateTime time = QDateTime(); + time.setTime_t( (*it).toUInt() ); + item->setText( *it_c, time.date().toString( Qt::LocalDate ) ); + break; + } + case Playcount: + case Score: + item->setText( *it_c, (*it).isNull() ? "0" : (*it) ); + break; + case Rating: + item->setText( *it_c, (*it).isNull() ? "0" : (*it) ); + break; + case Filename: + item->setText( *it_c, KURL::fromPathOrURL( (*it).right( (*it).length() -1 ) ).filename() ); + break; + case Filesize: + item->setText( *it_c, MetaBundle::prettyFilesize( (*it).toInt() ) ); + break; + default: + item->setText( *it_c, (*it) ); + break; + } + ++it; + } + } +} + +void +CollectionView::renderTreeModeView( bool /*=false*/ ) +{ + QStringList values; + QueryBuilder qb; + + if ( translateTimeFilter( timeFilter() ) > 0 ) + qb.addFilter( QueryBuilder::tabSong, QueryBuilder::valCreateDate, QString().setNum( QDateTime::currentDateTime().toTime_t() - translateTimeFilter( timeFilter() ) ), QueryBuilder::modeGreater ); + + setSorting( 0 ); + int VisYearAlbum = -1; + int VisLabel = -1; + int q_cat1=m_cat1; + int q_cat2=m_cat2; + int q_cat3=m_cat3; + if( m_cat1 == IdVisYearAlbum || + m_cat2 == IdVisYearAlbum || + m_cat3 == IdVisYearAlbum ) + { + if( m_cat1==IdVisYearAlbum ) + { + VisYearAlbum = 1; + q_cat1 = IdAlbum; + } + if( m_cat2==IdVisYearAlbum ) + { + VisYearAlbum = 2; + q_cat2 = IdAlbum; + } + if( m_cat3==IdVisYearAlbum ) + { + VisYearAlbum = 3; + q_cat3 = IdAlbum; + } + } + if ( m_cat1 == IdLabel || + m_cat2 == IdLabel || + m_cat3 == IdLabel ) + { + if ( m_cat1 == IdLabel ) + VisLabel = 1; + else if ( m_cat2 == IdLabel ) + VisLabel = 2; + else + VisLabel = 3; + } + QPixmap pixmap = iconForCategory( m_cat1 ); + + qb.addReturnValue( q_cat1, QueryBuilder::valName, true ); + + if( VisYearAlbum == 1 ) + qb.addReturnValue( QueryBuilder::tabYear, QueryBuilder::valName, true ); + + qb.setGoogleFilter( q_cat1 | q_cat2 | q_cat3 | QueryBuilder::tabSong, m_filter ); + + if( VisYearAlbum == 1 ) + qb.sortBy( QueryBuilder::tabYear, QueryBuilder::valName ); + + qb.sortBy( q_cat1, QueryBuilder::valName ); + qb.setOptions( QueryBuilder::optRemoveDuplicates ); + + if( q_cat1 == QueryBuilder::tabArtist ) + qb.setOptions( QueryBuilder::optNoCompilations ); + + // ensure we don't get empty genres/albums/etc due to tag changes + qb.addFilter( QueryBuilder::tabSong, QString::null ); + + values = qb.run(); + + //add items to the view + + uint dividerCount = 0; + if( values.count() ) + { + //keep track of headers already added + QMap containedDivider; + + for ( QStringList::Iterator it = values.fromLast(), begin = values.begin(); true; --it ) + { + bool unknown = false; + + //For Year-Album + if ( VisYearAlbum == 1 ) + { + ( *it ) = ( *it ).isEmpty() ? "?" : ( *it ) + i18n( " - " ); + QStringList::Iterator album = it; + --album; + if ( (*album).isEmpty() ) + { + unknown = true; + ( *it ) += i18n( "Unknown" ); + } + else + ( *it ) += *album; + } + + if ( (*it).stripWhiteSpace().isEmpty() ) + { + if ( VisLabel == 1 ) + (*it) = i18n( "No Label" ); + else + (*it) = i18n( "Unknown" ); + unknown = true; + } + else if (m_showDivider) + { + //Dividers for "The Who" should be created as "W", not "T", because + //that's how we sort it + QString actualStr = *it; + if ( m_cat1 == IdArtist && actualStr.startsWith( "the ", false ) ) + manipulateThe( actualStr, true ); + + QString headerStr = DividerItem::createGroup( actualStr, m_cat1); + + if ( !containedDivider[headerStr] && !headerStr.isEmpty() ) + { + containedDivider[headerStr] = true; + (void)new DividerItem(this, headerStr, m_cat1/*, m_sortYearsInverted*/); + dividerCount++; + } + } + + CollectionItem* item = new CollectionItem( this, m_cat1, unknown ); + item->setExpandable( true ); + item->setDragEnabled( true ); + item->setDropEnabled( false ); + item->setText( 0, *it ); + item->setPixmap( 0, pixmap ); + + //The structure of the return is different if Year - Album is format + if ( VisYearAlbum == 1 ) + --it; + + if ( it == begin ) + break; + } + } + + //check if we need to add a Various Artists node + if ( q_cat1 == QueryBuilder::tabArtist ) + { + qb.clear(); + if ( translateTimeFilter( timeFilter() ) > 0 ) + qb.addFilter( QueryBuilder::tabSong, QueryBuilder::valCreateDate, + QString().setNum( QDateTime::currentDateTime().toTime_t() - + translateTimeFilter( timeFilter() ) ), QueryBuilder::modeGreater ); + + qb.addReturnValue( q_cat1, QueryBuilder::valName, true ); + qb.setGoogleFilter( q_cat1 | q_cat2 | q_cat3 | QueryBuilder::tabSong, m_filter ); + qb.setOptions( QueryBuilder::optOnlyCompilations | QueryBuilder::optRemoveDuplicates ); + qb.setLimit( 0, 1 ); + values = qb.run(); + + if ( values.count() ) + { + // KListViewItem* x = new DividerItem(this, i18n( "Various" ), m_cat1); + // x->setExpandable(false); + // x->setDropEnabled( false ); + // x->setSelectable(false); + CollectionItem* item = new CollectionItem( this, m_cat1, false, true ); + item->setExpandable( true ); + item->setDragEnabled( true ); + item->setDropEnabled( false ); + item->setText( 0, i18n( "Various Artists" ) ); + item->setPixmap( 0, pixmap ); + } + } + + //Algorithm to expand some items after a search in a pretty/useful way: + //Aim to expand all items with one child, but with a maximum limit to how many items + //should be shown in the listview ( maxRows) after which we give up. If an item has + //more than one child and we haven't reached the limit, we may end up expanding it + //later. + QValueList couldOpen; + int totalCount = childCount() - dividerCount; + const int maxRows = 20; //This seems like a fair limit for a 1024x768 screen + if ( totalCount < maxRows ) + { + //Generate initial list of top list items to look at + for ( QListViewItem* top = firstChild(); top; top = top->nextSibling() ) + { + if ( !dynamic_cast( top ) ) + continue; + couldOpen.push_back( top ); + } + //Expand suggested items and expand or enqueue their children until we run out of + //rows or have expanded everything + for ( QValueList::iterator it = couldOpen.begin(); it != couldOpen.end() && totalCount < maxRows; ++it ) + { + if ( !( *it )->isOpen() ) + ( *it )->setOpen( true ); + totalCount += ( *it )->childCount(); + if ( ( *it )->firstChild()->isExpandable() ) //Check we have not reached the bottom + { + for ( QListViewItem *j = ( *it )->firstChild(); j && totalCount < maxRows; j = j->nextSibling() ) + { + j->setOpen( true ); + if ( j->childCount() > 1 ) //More than one child - maybe later + { + j->setOpen( false ); + couldOpen.push_back( j ); + } + else + { + //Prioritize expanding its children - add it immediately next + QValueList::iterator next = it; + ++next; + couldOpen.insert( next, j ); + ++totalCount; + } + } + } + } + } + + //If the tree has only one branch, at least at the top, make the lowest child + //which has no siblings (other branches) become selected. + //Rationale: If you search for something and then clear the search bar, this way it + //will stay in view, assuming you only had one real result. + if ( childCount() - dividerCount == 1 ) + { + QListViewItem *item = firstChild(); + if ( dynamic_cast( item ) ) //Skip a divider, if present + item = item->nextSibling(); + for ( ; item ; item = item->firstChild() ) + if ( !item->isOpen() || item->childCount() > 1 ) + break; + if ( item ) + { + setCurrentItem( item ); + item->setSelected( true ); + setSelectionAnchor( item ); + } + } + + removeDuplicatedHeaders(); +} + +void +CollectionView::removeDuplicatedHeaders() +{ + /* Following code depends on the order! */ + sort(); + + QValueList toDelete; + DividerItem *current=0, *last=0; + bool empty; + QListViewItem *item; + /* If we have two consecutive headers, one of them is useless, and should be removed */ + for( item = firstChild(),empty=false; item; item=item->nextSibling() ) + { + if ( (current = dynamic_cast( item )) ) + { + if ( empty ) + { + if ( !current->text(0).at(0).isLetterOrNumber() + || ( last->text(0).at(0).isLetterOrNumber() + && current->text(0).at(0).unicode() > last->text(0).at(0).unicode() ) ) + toDelete += current; + else + { + toDelete += last; + last=current; + } + } + else + last=current; + empty=true; + } + else + empty=false; + } + + for ( QValueList::iterator it = toDelete.begin(); it != toDelete.end(); ++it ) + delete *it; +} + + +// MODE IPODVIEW This is the heart of the iPod view mode code. It +// applies the current filters (as defined by previous "move +// forward" actions). If we're not viewing tracks (stillFiltering +// == true), then display the results in the standard order, with +// dividers if applicable, with an "All" (i.e., no filter) item if +// there is more than one result, and with "Unknown" items if +// there are any. Note that the "All" item is a CollectionItem +// with the Sampler bit set, since it behaves similar to the +// Various Artists node. +// If we are viewing tracks (stillFiltering == +// false), then just apply all of the filters and show the +// selected tracks. The track ordering is similar to in list view +// mode; see the comments in buildIpodQuery() for details. +void +CollectionView::renderIpodModeView( bool /*=false*/ ) +{ + QStringList values; + QueryBuilder qb; + + if ( translateTimeFilter( timeFilter() ) > 0 ) + qb.addFilter( QueryBuilder::tabSong, QueryBuilder::valCreateDate, QString().setNum( QDateTime::currentDateTime().toTime_t() - translateTimeFilter( timeFilter() ) ), QueryBuilder::modeGreater ); + + int catArr[3] = {m_cat1, m_cat2, m_cat3}; + // stillFiltering is true when we're not viewing tracks + bool stillFiltering = (m_currentDepth < trackDepth()); + // q_cat is the "query category" -- it's undefined if + // stillFiltering is true; otherwise it's the category + // at the current iPod viewing depth (m_currentDepth), unless + // that category is IdVisYearAlbum, in which case it's IdAlbum. + int q_cat = (stillFiltering ? catArr[m_currentDepth] : catArr[0]); + // m_cat is the current actual category -- it agrees with q_cat + // if and only if m_cat != IdVisYearAlbum + int m_cat = q_cat; + // This is set to true if the current category is IdVisYearAlbum + // It is only used when stillFiltering == true. + bool VisYearAlbum = false; + //This is set to true if the current category is IdLabel + bool VisLabel = false; + + if( q_cat == IdVisYearAlbum && stillFiltering ) + { + VisYearAlbum = true; + q_cat = IdAlbum; + } + if ( q_cat == IdLabel && stillFiltering ) + VisLabel = true; + + // If we're viewing tracks, we don't want them to be sorted + // alphabetically, since we take great pains in + // buildIpodQuery() to have them returned to us from the + // database in the correct order. + setSorting( stillFiltering ? 0 : -1 ); + + // Do the grunt work of building the query (this method is also + // called by listSelected() ) + buildIpodQuery( qb, m_currentDepth, m_ipodFilters, m_ipodFilterYear ); + if(stillFiltering) + qb.setOptions( QueryBuilder::optRemoveDuplicates ); + + int tables = 0; + for( int i = 0; i < trackDepth(); ++i ) + tables |= (catArr[i] == IdVisYearAlbum + ? IdAlbum + : catArr[i]); + qb.setGoogleFilter( tables | QueryBuilder::tabSong, m_filter ); + + // Return values + if( stillFiltering ) + { + qb.addReturnValue( q_cat, QueryBuilder::valName, true ); + if( VisYearAlbum ) + qb.addReturnValue( QueryBuilder::tabYear, QueryBuilder::valName, true ); + } + else + { + qb.addReturnValue( QueryBuilder::tabSong, QueryBuilder::valURL ); + qb.addReturnValue( QueryBuilder::tabSong, QueryBuilder::valTitle ); + } + + values = qb.run(); + int total = values.count() / qb.countReturnValues(); + + // This can happen -- with the filter it might be empty + if( total == 0 ) + return; + + // We want to load the pixmap only once if we're still filtering + // Otherwise just load a dummy pixmap + QPixmap pixmap = iconForCategory( q_cat ); + QPixmap incPixmap = ipodIncrementIcon(); + int width = incPixmap.width() + 10; // Set the column width below + // Keep track of the dividers we've created. + QMap containedDivider; + + QStringList::Iterator itStep = values.end(); + QStringList::Iterator begin = values.begin(); + itStep -= qb.countReturnValues(); + // It's an awkward business stepping through a list backward + // when the elements are in tuples, going forward. + // This loop breaks at the bottom -- don't put a continue in here! + while( 1 ) + { + CollectionItem* item; + QStringList::Iterator it = itStep; + + // Add non-track items + if( stillFiltering ) + { + bool unknown = false; + + if( (*it).isEmpty() ) + { + unknown = true; + if ( VisLabel ) + *it = i18n( "No Label" ); + else + *it = i18n( "Unknown" ); + } + + item = new CollectionItem( this, m_cat, unknown ); + + if( VisYearAlbum ) + { + QString album = *it; + QString year = *(++it); + if( year.isEmpty() ) + year = "?"; + item->setText( 0, year + i18n(" - ") + album ); + } + else + item->setText( 0, *it ); + + item->setPixmap( 0, pixmap ); + item->setPixmap( 1, incPixmap ); + item->setText( 1, "" ); + // Calculate width + width = item->width( fontMetrics(), this, 1 ); + + // Only do dividers if we're not showing tracks since + // dividers don't really make sense in a track-only view + if( !unknown && m_showDivider ) + { + //Dividers for "The Who" should be created as "W", not "T", + //because that's how we sort it + QString actualStr = item->text( 0 ); + if ( m_cat == IdArtist && + actualStr.startsWith( "the ", false ) ) + manipulateThe( actualStr, true ); + + QString headerStr = DividerItem::createGroup( actualStr, m_cat ); + + if ( !containedDivider[headerStr] && !headerStr.isEmpty() ) + { + containedDivider[headerStr] = true; + (void)new DividerItem( this, headerStr, m_cat ); + } + } + } + + else // Add tracks + { + item = new CollectionItem( this ); + item->setUrl( *it ); + item->setText( 0, *(++it) ); + } + + item->setDragEnabled( true ); + item->setDropEnabled( false ); + + if( itStep == begin ) + break; + + itStep -= qb.countReturnValues(); + } + + // Add the "All" filter if necessary + if( stillFiltering ) + { + if( total > 1 ) + { + // The "All" filter has much the same behavior as the + // "Various Artists" item, so we use the isSampler bit + CollectionItem* item = new CollectionItem( this, m_cat, false, true ); + item->setDragEnabled( true ); + item->setDropEnabled( false ); + item->setPixmap( 0, pixmap ); + item->setText( 0, allForCategory( q_cat, total ) ); + item->setPixmap( 1, incPixmap ); + item->setText( 1, "" ); + + sort(); + } + setColumnWidth( 1, width ); + QResizeEvent rev( size(), QSize() ); + viewportResizeEvent( &rev ); + } + + removeDuplicatedHeaders(); +} + +////////////////////////////////////////////////////////////////////////////////////////// +// CLASS CollectionItem +////////////////////////////////////////////////////////////////////////////////////////// +void +CollectionItem::paintCell ( QPainter * painter, const QColorGroup & cg, + int column, int width, int align ) +{ + if( static_cast(column) == CollectionView::Rating ) + { + QPixmap buf( width, height() ); + QPainter p( &buf, true ); + + const QColorGroup _cg = listView()->palette().active(); + + QColor bg = isSelected() ? _cg.highlight() + : isAlternate() ? listView()->alternateBackground() + : listView()->viewport()->backgroundColor(); +#if KDE_IS_VERSION( 3, 3, 91 ) + if( listView()->shadeSortColumn() && !isSelected() && listView()->columnSorted() == column ) + { + /* from klistview.cpp + Copyright (C) 2000 Reginald Stadlbauer + Copyright (C) 2000,2003 Charles Samuels + Copyright (C) 2000 Peter Putzer */ + if ( bg == Qt::black ) + bg = QColor(55, 55, 55); // dark gray + else + { + int h,s,v; + bg.hsv(&h, &s, &v); + if ( v > 175 ) + bg = bg.dark(104); + else + bg = bg.light(120); + } + } +#endif + + buf.fill( bg ); + + int rating = text(column).toInt(); + int i = 1, x = 1; + const int y = height() / 2 - StarManager::instance()->getGreyStar()->height() / 2; + bool half = rating%2; + for(; i <= rating/2; ++i ) + { + bitBlt( p.device(), x, y, StarManager::instance()->getStar( half ? rating/2 + 1 : rating/2 ) ); + x += StarManager::instance()->getGreyStar()->width() + listView()->itemMargin(); + } + if( half ) + { + bitBlt( p.device(), x, y, StarManager::instance()->getHalfStar( rating/2 + 1 ) ); + x += StarManager::instance()->getGreyStar()->width() + listView()->itemMargin(); + } + + p.end(); + painter->drawPixmap( 0, 0, buf ); + } + else + { + KListViewItem::paintCell( painter, cg, column, width, align ); + } +} + +int +CollectionItem::compare( QListViewItem* i, int col, bool ascending ) const +{ + QString a = text( col ); + QString b = i->text( col ); + int ia, ib; + + //Special cases go first to take priority + + // Sampler is the first one in iPod view + CollectionView* view = static_cast( listView() ); + if( view->viewMode() == CollectionView::modeIpodView ) + { + if ( m_isSampler ) + return -1; + if ( dynamic_cast( i ) && static_cast( i )->m_isSampler ) + return 1; + } + else if( view->viewMode() == CollectionView::modeFlatView ) + { + ia = ib = 0; + // correctly treat numeric values + switch( col ) + { + case CollectionView::Track: + case CollectionView::DiscNumber: + case CollectionView::Bitrate: + case CollectionView::Score: + case CollectionView::Rating: + case CollectionView::Playcount: + case CollectionView::BPM: + ia = a.toInt(); + ib = b.toInt(); + break; + case CollectionView::Length: + ia = a.section( ':', 0, 0 ).toInt() * 60 + a.section( ':', 1, 1 ).toInt(); + ib = b.section( ':', 0, 0 ).toInt() * 60 + b.section( ':', 1, 1 ).toInt(); + break; + } + + if( ia || ib ) + { + if( ia < ib ) + return 1; + if( ia > ib ) + return -1; + return 0; + } + } + + // Unknown is always the first one unless we're doing iPod view, but if the two items to be compared are Unknown, + // then compare the normal way + if ( !( m_isUnknown && dynamic_cast( i ) && static_cast( i )->m_isUnknown ) ) + { + if ( m_isUnknown ) + return -1; + if ( dynamic_cast( i ) && static_cast( i )->m_isUnknown ) + return 1; + } + + // Various Artists is always after unknown + if ( m_isSampler ) + return -1; + if ( dynamic_cast( i ) && static_cast( i )->m_isSampler ) + return 1; + + //Group heading should go above the items in that group + if (dynamic_cast(i) && DividerItem::shareTheSameGroup(a, b, m_cat)) { + return ascending == false ? -1 : 1; + } + + switch( m_cat ) { + case IdVisYearAlbum: + a = a.left( a.find( i18n(" - ") ) ); + b = b.left( b.find( i18n(" - ") ) ); + // "?" are the last ones + if ( a == "?" ) + return 1; + if ( b == "?" ) + return -1; + //fall through + case IdYear: + ia = a.toInt(); + ib = b.toInt(); + if (ia==ib) + return QString::localeAwareCompare( text( col ).lower(), i->text( col ).lower() ); + if (ia(i) ) + a += a; + + // No special case, then fall on default + return QString::localeAwareCompare( a.lower(), b.lower() ); +} + +void +CollectionItem::sortChildItems ( int column, bool ascending ) { + CollectionView* view = static_cast( listView() ); + // Sort only if it's not the tracks + if ( depth() + 1 < view->trackDepth()) + QListViewItem::sortChildItems( column, ascending ); +} + +// +// DividerItem + +DividerItem::DividerItem( QListView* parent, QString txt, int cat/*, bool sortYearsInverted*/) +: KListViewItem( parent), m_blockText(false), m_text(txt), m_cat(cat)/*, m_sortYearsInverted(sortYearsInverted)*/ +{ + setExpandable(false); + setDropEnabled(false); + setSelectable(false); +} + +void +DividerItem::paintCell ( QPainter * p, const QColorGroup & cg, + int column, int width, int align ) +{ + p->save(); + + // be sure, that KListViewItem::paintCell() does not draw its text + setBlockText( true ); + KListViewItem::paintCell(p, cg, column, width, align); + setBlockText( false ); + + //use bold font for the divider + QFont f = p->font(); + f.setBold( true ); + p->setFont( f ); + + // draw the text offset a little bit + if( column == 0 ) // For iPod viewing mode + { + QFontMetrics fm( p->fontMetrics() ); + int x = !QApplication::reverseLayout() ? 25 : width - 25; + int y = fm.ascent() + (height() - fm.height())/2; + p->drawText( x, y, m_text ); + } + + //draw the baseline + p->setPen( QPen(Qt::gray, 2) ); + p->drawLine(0, height() -2 , width, height() -2 ); + + p->restore(); +} + +void +DividerItem::paintFocus ( QPainter* /*p*/, const QColorGroup& /*cg*/, const QRect& /*r*/ ) +{ + //not implemented, we don't to show focus +} + +//to draw the text on my own I have to be able to block the text, otherwise I could +// not use QListViewItem::paintCell() to draw the basic cell +QString +DividerItem::text(int column) const +{ + if (column == 0) { + return m_blockText ? "" : m_text; + } + return KListViewItem::text(column); +} + +int +DividerItem::compare( QListViewItem* i, int col, bool ascending ) const +{ + if (!i) { + return QString::localeAwareCompare( text(col).lower(), QString("") ); + } + if (dynamic_cast(i)) { + return -1 * i->compare(const_cast(this), col, ascending); + } + + if (m_cat == IdYear || + m_cat == IdVisYearAlbum) + { + bool ok_a, ok_b; + int ia = text(col).toInt(&ok_a); + int ib = i->text(col).toInt(&ok_b); + + if (ok_a && ok_b) + { + if (ia == ib) return 0; + else if (ia < ib) return 1; + else return -1; + } + } + return QString::localeAwareCompare( text(col).lower(), i->text(col).lower() ); +} + +QString +DividerItem::createGroup(const QString& src, int cat) +{ + QString ret; + switch (cat) { + case IdVisYearAlbum: { + ret = src.left( src.find(" - ") ); + break; + } + case IdYear: { + ret = src; + if (ret.length() == 2 || ret.length() == 4) { + ret = ret.left(ret.length() - 1) + '0'; + } + break; + } + default: + ret = src.stripWhiteSpace(); + // (Joe Rabinoff) deleted. The bug this fixes is as follows. + // If the category is Album and the album name is "Heroes" (by + // David Bowie -- the quotes are part of the title), it gets + // put under the H group, but then gets sorted under '"'. + // What I've done is the wrong fix -- albums should be sorted + // by their first alphanumeric character. + /* + while ( !ret.isEmpty() && !ret.at(0).isLetterOrNumber() ) { + ret = ret.right( ret.length()-1 ); + } + */ + if ( !ret.isEmpty() && ret.at(0).isLetterOrNumber() ) + ret = ret.left( 1 ).upper(); + else + return ""; + /*else + ret = i18n( "Others" );*/ + /* By returning an empty string, no header is created. */ + + if (QChar(ret.at(0)).isDigit()) { + ret = "0-9"; + } + } + + return ret; +} + +bool +DividerItem::shareTheSameGroup(const QString& itemStr, const QString& divStr, int cat) +{ + bool inGroup = false; + QString tmp = itemStr.stripWhiteSpace(); + + switch (cat) { + case IdVisYearAlbum: { + QString sa = itemStr.left( itemStr.find( i18n(" - ") ) ); + QString sb = divStr.left( divStr.find( i18n(" - ") ) ); + if (sa == sb) { + inGroup = true; + } + break; + } + case IdYear: { + int ia = itemStr.toInt(); + int ib = divStr.toInt(); + // they share one group if: + // o they are < 100 (short years '98') + // o they are > 1000 (long years '1998') + // o their 'century' is the same + // o are the same + if (((ia < 100 || ia > 1000) && ia/10 == ib/10) || + (ia == ib)) { + inGroup = true; + } + break; + } + case IdArtist: + //"The Who" should count as being in "W" and not "T" + if ( tmp.startsWith( "the ", false ) ) + CollectionView::manipulateThe( tmp, true ); + //Fall through + default: + if ( !tmp.isEmpty() ) { + if (divStr == "0-9" && QChar(tmp.at(0)).isDigit()) { + inGroup = true; + } + else if (tmp.startsWith(divStr, 0)) { + inGroup = true; + } + } + } + + return inGroup; +} + +#include "collectionbrowser.moc" diff --git a/amarok/src/collectionbrowser.h b/amarok/src/collectionbrowser.h new file mode 100644 index 00000000..407c4f7c --- /dev/null +++ b/amarok/src/collectionbrowser.h @@ -0,0 +1,397 @@ +// (c) 2004 Mark Kretschmann +// (c) 2004 Christian Muehlhaeuser +// (c) 2005 Gábor Lehel +// (c) 2005 Christan Baumgart +// See COPYING file for licensing information. + +#ifndef AMAROK_COLLECTIONBROWSER_H +#define AMAROK_COLLECTIONBROWSER_H + +#include +#include //stack allocated +#include //baseclass + +#include //baseclass +#include //stack allocated +#include //stack allocated +#include //baseclass +#include + +#include "multitabbar.h" //baseclass +#include "collectiondb.h" +#include "amarok_export.h" + +class ClickLineEdit; +class CollectionDB; + +class QCString; +class QDragObject; +class QPixmap; +class QPoint; +class QStringList; + +class KAction; +class KComboBox; +class KPopupMenu; +class KRadioAction; +class KTabBar; +class KToolBar; +class KToggleAction; + +class CollectionView; +class CollectionItem; +class DividerItem; +class OrganizeCollectionDialog; + +namespace CollectionBrowserIds +{ + enum CatMenuId { IdAlbum = QueryBuilder::tabAlbum, + IdArtist = QueryBuilder::tabArtist, + IdComposer = QueryBuilder::tabComposer, + IdGenre = QueryBuilder::tabGenre, + IdYear = QueryBuilder::tabYear , + IdScan = 32, IdNone = 64, + IdArtistAlbum = 128, IdGenreArtist = 256, IdGenreArtistAlbum = 512, IdVisYearAlbum = 1024, IdArtistVisYearAlbum = 2048, + IdLabel = QueryBuilder::tabLabels //=8192 + }; +} + +class CollectionBrowser: public QVBox +{ + Q_OBJECT + friend class CollectionView; + + public: + CollectionBrowser( const char* name ); + virtual bool eventFilter( QObject*, QEvent* ); + KToolBar* getToolBar() const { return m_toolbar; } + static CollectionBrowser *instance() { return s_instance; } + + public slots: + void setupDirs(); + void toggleDivider(); + + private slots: + void slotClearFilter(); + void slotSetFilterTimeout(); + void slotSetFilter(); + void slotSetFilter( const QString &filter ); + void slotEditFilter(); + + private: + void layoutToolbar(); + void ipodToolbar( bool activate ); + void appendSearchResults(); + + //attributes: + KTabBar* m_tabs; //tree-view, flat-view tabs + class KToolBar *m_toolbar; + KAction *m_configureAction; + // For iPod-style browsing + KAction *m_ipodIncrement, *m_ipodDecrement; + class KToolBar *m_ipodToolbar; + class QHBox *m_ipodHbox; + + KToggleAction *m_showDividerAction; + KRadioAction *m_treeViewAction; + KRadioAction *m_flatViewAction; + KRadioAction *m_ipodViewAction; + class KActionMenu *m_tagfilterMenuButton; + + KPopupMenu* m_categoryMenu; + KPopupMenu* m_cat1Menu; + KPopupMenu* m_cat2Menu; + KPopupMenu* m_cat3Menu; + KLineEdit* m_searchEdit; + KComboBox* m_timeFilter; + CollectionView* m_view; + QTimer* m_timer; + + bool m_returnPressed; + + static CollectionBrowser *s_instance; + + // for CatMenuId + friend class CollectionItem; + friend class DividerItem; +}; + +class DividerItem : public KListViewItem +{ +public: + static QString createGroup(const QString& src, int cat); + static bool shareTheSameGroup(const QString& a, const QString& b, int cat); + +public: + DividerItem( QListView* parent, QString txt, int cat); + + virtual void paintCell ( QPainter * p, const QColorGroup & cg, int column, int width, int align ); + virtual void paintFocus ( QPainter * p, const QColorGroup & cg, const QRect & r ); + + virtual QString text(int column) const; + + void setBlockText(bool block) { m_blockText = block; } + +private: + virtual int compare( QListViewItem*, int, bool ) const; + +private: + bool m_blockText; + QString m_text; + int m_cat; +}; + + + +class CollectionItem : public KListViewItem { + public: + CollectionItem( QListView* parent, int cat = 0, bool unknown = false, bool sampler=false ) + : KListViewItem( parent ) + , m_cat( cat ) + , m_isUnknown( unknown ) + , m_isSampler( sampler ) {}; + CollectionItem( QListViewItem* parent, int cat = 0, bool unknown = false, bool sampler=false ) + : KListViewItem( parent ) + , m_cat( cat ) + , m_isUnknown( unknown ) + , m_isSampler( sampler ) {}; + void setUrl( const QString& url ) { m_url.setPath( url ); } + const KURL& url() const { return m_url; } + + virtual void sortChildItems ( int column, bool ascending ); //reimplemented + + inline QString getSQLText( int column ) + { + return ( !column && m_isUnknown ) ? "" : text( column ); + } + + inline bool isUnknown() {return m_isUnknown;} + inline bool isSampler() {return m_isSampler;} + + virtual void setPixmap(int column, const QPixmap & pix); + + /// convenience functions + CollectionView *listView() const { return reinterpret_cast( KListViewItem::listView() ); } + + private: + friend class CollectionView; + virtual void paintCell ( QPainter * painter, const QColorGroup & cg, int column, int width, int align ); + + //for sorting + virtual int compare( QListViewItem*, int, bool ) const; //reimplemented + + //attributes: + KURL m_url; + int m_cat; + bool m_isUnknown; + bool m_isSampler; +}; + + +class CollectionView : public KListView, public DropProxyTarget +{ + Q_OBJECT + friend class CollectionBrowser; + + public: + enum ViewMode { modeTreeView, modeFlatView, modeIpodView }; + + friend class CollectionItem; // for access to m_cat2 + friend class ContextBrowser; // for setupDirs() + + CollectionView( CollectionBrowser* parent ); + ~CollectionView(); + + LIBAMAROK_EXPORT static CollectionView* instance() { return m_instance; } + + void setFilter( const QString &filter ) { m_filter = filter; } + void setTimeFilter( const uint timeFilter ) { m_timeFilter = timeFilter; } + QString filter() { return m_filter; } + uint timeFilter() { return m_timeFilter; } + CollectionItem* currentItem() { return static_cast( KListView::currentItem() ); } + + int trackDepth() { return m_trackDepth; } + int viewMode() const { return m_viewMode; } + + // Transform "The Who" -> "Who, The" or the other way + static void manipulateThe( QString &str, bool reverse ); + + void setShowDivider(bool show); + + bool isOrganizingFiles() const; + + //Useful helper function to avoid duplicating code + static inline void yearAlbumCalc( QString &year, QString &text ); + + protected: + // Reimplemented for iPod-style navigation, etc. + virtual void keyPressEvent( QKeyEvent *e ); + + + public slots: + /** Rebuilds and displays the treeview by querying the database. */ + void renderView(bool force = false); + + void databaseChanged() { m_dirty = true; renderView(); }; + + void setTreeMode() { setViewMode( modeTreeView ); }; + void setFlatMode() { setViewMode( modeFlatView ); }; + void setIpodMode() { setViewMode( modeIpodView ); }; + + void presetMenu( int id ); + void cat1Menu( int id, bool rerender = true ); + void cat2Menu( int id, bool rerender = true ); + void cat3Menu( int id, bool rerender = true ); + void organizeFiles( const KURL::List &list, const QString &caption, bool addToCollection=false ) LIBAMAROK_EXPORT; + + private slots: + void setupDirs(); + + void slotEnsureSelectedItemVisible(); + + void renderFlatModeView(bool force = false); + void renderTreeModeView(bool force = false); + void renderIpodModeView(bool force = false); + + void scanStarted(); + void scanDone( bool changed = true ); + + void slotExpand( QListViewItem* ); + void slotCollapse( QListViewItem* ); + void enableCat3Menu( bool ); + void invokeItem( QListViewItem*, const QPoint &, int column ); + void invokeItem( QListViewItem* ); + + // ipod-style navigation slots + void ipodItemClicked( QListViewItem*, const QPoint&, int ); + void incrementDepth ( bool rerender = true ); + void decrementDepth ( bool rerender = true ); + + void rmbPressed( QListViewItem*, const QPoint&, int ); + void selectAll() {QListView::selectAll(true); } + /** Tries to download the cover image from Amazon.com */ + void fetchCover(); + /** Shows dialog with information on selected track */ + void showTrackInfo(); + + /** + * Cancel Organizing files + */ + void cancelOrganizingFiles(); + + void ratingChanged( const QString&, int ); + + private: + enum Tag { Title = 0, Artist, Composer, Album, Genre, Length, DiscNumber, Track, Year, + Comment, Playcount, Score, Rating, Filename, Firstplay, Lastplay, Modified, + Bitrate, Filesize, BPM, NUM_TAGS }; + + void setViewMode( int mode, bool rerender = true ); + void removeDuplicatedHeaders(); + + void startDrag(); + QString getTrueItemText( int, QListViewItem* ) const; + QStringList listSelectedSiblingsOf( int, QListViewItem* ); + KURL::List listSelected(); + + void playlistFromURLs( const KURL::List &urls ); + QPixmap iconForCategory( const int cat ) const; + QString captionForCategory( const int cat ) const; + inline QString captionForTag( const Tag ) const; + + // For iPod-style navigation + QString allForCategory( const int cat, const int num ) const; + void resetIpodDepth ( void ); + void buildIpodQuery ( QueryBuilder &qb, int depth, QStringList filters[3], QStringList filterYear, bool recursiveSort = false, bool compilationsOnly = false ); + void selectIpodItems ( void ); + QPixmap ipodIncrementIcon ( void ); + QPixmap ipodDecrementIcon ( void ); + + void setCompilation( const KURL::List &urls, bool compilation ); + + /** Rebuild selections, viewport and expanded items after reloads */ + void cacheView(); + void restoreView(); + + //Used to store the name of an item (and its parents), so it can be recalled later + //even if the pointer to the item has been invalidated. + QStringList makeStructuredNameList( QListViewItem* ) const; + QListViewItem* findFromStructuredNameList( const QStringList& ) const; + + // avoid duplicated code + static inline bool endsInThe( const QString & text ); + inline void updateTrackDepth(); + + uint translateTimeFilter( uint filterMode ); + + /**Call when a category has changed **/ + void updateColumnHeader(); + // Reimplemented from KListView + void viewportPaintEvent( QPaintEvent* ); + void viewportResizeEvent( QResizeEvent* ); + bool eventFilter( QObject*, QEvent* ); + void contentsDragEnterEvent( QDragEnterEvent* ); + void contentsDragMoveEvent( QDragMoveEvent* ); + void contentsDropEvent( QDropEvent *e ); + // Reimplemented from DropProxyTarget + void dropProxyEvent( QDropEvent *e ); + + void safeClear(); + + //attributes: + LIBAMAROK_EXPORT static CollectionView* m_instance; + + CollectionBrowser* m_parent; + + QString m_filter; + uint m_timeFilter; + int m_cat1; + int m_cat2; + int m_cat3; + int m_trackDepth; + int m_viewMode; + + // The iPod-style viewing attributes + int m_currentDepth; // Current viewing depth + QStringList m_ipodFilters[3]; // Selections at each stage + QStringList m_ipodFilterYear; // See the comment for incrementDepth() + // For auto-selection + int m_ipodIncremented; // 0 = nothing, 1 = just incremented, 2 = just decremented + QStringList m_ipodSelected[3]; // Saved selections at lower levels + QString m_ipodCurrent[3]; // Saved current selections + QString m_ipodTopItem[3]; // Saved viewport positions + + bool m_dirty; // we use this to avoid re-rendering the view when unnecessary (eg, browser is not visible) + + QValueList m_cacheOpenItemPaths; + QStringList m_cacheViewportTopItem; + QStringList m_cacheCurrentItem; + KURL::List m_organizeURLs; + bool m_organizeCopyMode; + + bool m_organizingFileCancelled; + + bool m_showDivider; + QValueList m_flatColumnWidths; +}; + +// why is signal detailsClicked() missing from KDialogBase? +class OrganizeCollectionDialogBase : public KDialogBase +{ + Q_OBJECT + public: + OrganizeCollectionDialogBase( QWidget *parent=0, const char *name=0, bool modal=true, + const QString &caption=QString::null, + int buttonMask=Ok|Apply|Cancel ) + : KDialogBase( parent, name, modal, caption, buttonMask ) + { + } + + signals: + void detailsClicked(); + public slots: + void slotDetails() { KDialogBase::slotDetails(); emit detailsClicked(); adjustSize(); } +}; + + +#endif /* AMAROK_COLLECTIONBROWSER_H */ diff --git a/amarok/src/collectiondb.cpp b/amarok/src/collectiondb.cpp new file mode 100644 index 00000000..0fb4ca7f --- /dev/null +++ b/amarok/src/collectiondb.cpp @@ -0,0 +1,8074 @@ +// (c) 2004 Mark Kretschmann +// (c) 2004 Christian Muehlhaeuser +// (c) 2004 Sami Nieminen +// (c) 2005 Ian Monroe +// (c) 2005 Jeff Mitchell +// (c) 2005 Isaiah Damron +// (c) 2005-2006 Alexandre Pereira de Oliveira +// (c) 2006 Jonas Hurrelmann +// (c) 2006 Shane King +// (c) 2006 Peter C. Ndikuwera +// (c) 2006 Stanislav Nikolov +// See COPYING file for licensing information. + +#define DEBUG_PREFIX "CollectionDB" + +#include "app.h" +#include "amarok.h" +#include "amarokconfig.h" +#include "config.h" +#include "debug.h" +#include "collectionbrowser.h" //updateTags() +#include "collectiondb.h" +#include "coverfetcher.h" +#include "enginecontroller.h" +#include "expression.h" +#include "mediabrowser.h" +#include "metabundle.h" //updateTags() +#include "mountpointmanager.h" //buildQuery() +#include "organizecollectiondialog.h" +#include "playlist.h" +#include "playlistloader.h" +#include "playlistbrowser.h" +#include "podcastbundle.h" //addPodcast +#include "qstringx.h" +#include "scancontroller.h" +#include "scriptmanager.h" +#include "scrobbler.h" +#include "statusbar.h" +#include "threadmanager.h" + +#include +#include +#include +#include +#include +#include +#include //setHTMLLyrics() +#include +#include //createDragPixmap() +#include +#include //debugging, can be removed later + +#include //setHTMLLyrics() +#include +#include +#include //checkDatabase() +#include +#include //setupCoverFetcher() +#include //setupCoverFetcher() +#include +#include +#include +#include +#include +#include +#include + +#include //DbConnection::sqlite_power() +#include //query() +#include //exit() +#include //usleep() + +#include + +#include "sqlite/sqlite3.h" + +#ifdef USE_MYSQL + #include + #include +#endif + +#ifdef USE_POSTGRESQL + #include +#endif + +#undef HAVE_INOTIFY // NOTE Disabled for now, due to stability issues + +#ifdef HAVE_INOTIFY + #include + #include "inotify/inotify-syscalls.h" +#endif + +using Amarok::QStringx; + +#define DEBUG 0 + +////////////////////////////////////////////////////////////////////////////////////////// +// CLASS INotify +////////////////////////////////////////////////////////////////////////////////////////// + +INotify* INotify::s_instance = 0; + +INotify::INotify( CollectionDB *parent, int fd ) + : DependentJob( parent, "INotify" ) + , m_parent( parent ) + , m_fd( fd ) +{ + s_instance = this; +} + + +INotify::~INotify() +{} + + +bool +INotify::watchDir( const QString directory ) +{ +#ifdef HAVE_INOTIFY + int wd = inotify_add_watch( m_fd, directory.local8Bit(), IN_CLOSE_WRITE | IN_DELETE | IN_MOVE | + IN_MODIFY | IN_ATTRIB ); + if ( wd < 0 ) + debug() << "Could not add INotify watch for: " << directory << endl; + + return ( wd >= 0 ); +#else + Q_UNUSED(directory); +#endif + + return false; +} + + +bool +INotify::doJob() +{ +#ifdef HAVE_INOTIFY + DEBUG_BLOCK + + IdList list = MountPointManager::instance()->getMountedDeviceIds(); + QString deviceIds; + foreachType( IdList, list ) + { + if ( !deviceIds.isEmpty() ) deviceIds += ','; + deviceIds += QString::number(*it); + } + const QStringList values = m_parent->query( QString( "SELECT dir, deviceid FROM directories WHERE deviceid IN (%1);" ) + .arg( deviceIds ) ); + foreach( values ) + { + QString rpath = *it; + int deviceid = (*(++it)).toInt(); + QString abspath = MountPointManager::instance()->getAbsolutePath( deviceid, rpath ); + watchDir( abspath ); + } + + /* size of the event structure, not counting name */ + const int EVENT_SIZE = ( sizeof( struct inotify_event ) ); + /* reasonable guess as to size of 1024 events */ + const int BUF_LEN = 1024 * ( EVENT_SIZE + 16 ); + + while ( 1 ) + { + char buf[BUF_LEN]; + int len, i = 0; + len = read( m_fd, buf, BUF_LEN ); + if ( len < 0 ) + { + debug() << "Read from INotify failed" << endl; + return false; + } + else + { + if ( !len ) + { + /* BUF_LEN too small? */ + } + else + { + while ( i < len ) + { + struct inotify_event *event; + event = (struct inotify_event *) &buf[i]; + + i += EVENT_SIZE + event->len; + } + + QTimer::singleShot( 0, m_parent, SLOT( scanMonitor() ) ); + } + } + } +#endif + + // this shouldn't happen + return false; +} + + +////////////////////////////////////////////////////////////////////////////////////////// +// CLASS CollectionDB +////////////////////////////////////////////////////////////////////////////////////////// + +QMutex* CollectionDB::connectionMutex = new QMutex(); +QMutex* CollectionDB::itemCoverMapMutex = new QMutex(); +//we don't have to worry about this map leaking memory since ThreadManager limits the total +//number of QThreads ever created +QMap *CollectionDB::threadConnections = new QMap(); +QMap *CollectionDB::itemCoverMap = new QMap(); + +CollectionDB* CollectionDB::instance() +{ + static CollectionDB db; + return &db; +} + + +CollectionDB::CollectionDB() + : EngineObserver( EngineController::instance() ) + , m_autoScoring( true ) + , m_noCover( locate( "data", "amarok/images/nocover.png" ) ) + , m_shadowImage( locate( "data", "amarok/images/shadow_albumcover.png" ) ) + , m_scanInProgress( false ) + , m_rescanRequired( false ) + , m_aftEnabledPersistentTables() + , m_moveFileJobCancelled( false ) +{ + DEBUG_BLOCK + +#ifdef USE_MYSQL + if ( AmarokConfig::databaseEngine().toInt() == DbConnection::mysql ) + m_dbConnType = DbConnection::mysql; + else +#endif +#ifdef USE_POSTGRESQL + if ( AmarokConfig::databaseEngine().toInt() == DbConnection::postgresql ) + m_dbConnType = DbConnection::postgresql; + else +#endif + m_dbConnType = DbConnection::sqlite; + + //perform all necessary operations to allow MountPointManager to access the devices table here + //there is a recursive dependency between CollectionDB and MountPointManager and this is the workaround + //checkDatabase has to be able to access MountPointManager + + // + initialize(); + // + + // Remove cached "nocover" images, so that a new version actually gets shown + // The asterisk is for also deleting the shadow-caches. + const QStringList entryList = cacheCoverDir().entryList( "*nocover.png*", QDir::Files ); + foreach( entryList ) + cacheCoverDir().remove( *it ); + + + connect( this, SIGNAL(fileMoved(const QString&, const QString&, const QString&)), + this, SLOT(aftMigratePermanentTablesUrl(const QString&, const QString&, const QString&)) ); + connect( this, SIGNAL(uniqueIdChanged(const QString&, const QString&, const QString&)), + this, SLOT(aftMigratePermanentTablesUniqueId(const QString&, const QString&, const QString&)) ); + + connect( qApp, SIGNAL( aboutToQuit() ), this, SLOT( disableAutoScoring() ) ); + + connect( this, SIGNAL( coverRemoved( const QString&, const QString& ) ), + SIGNAL( coverChanged( const QString&, const QString& ) ) ); + connect( Scrobbler::instance(), SIGNAL( similarArtistsFetched( const QString&, const QStringList& ) ), + this, SLOT( similarArtistsFetched( const QString&, const QStringList& ) ) ); + + // If we're supposed to monitor dirs for changes, make sure we run it once + // on startup, since inotify can't inform us about old events +// QTimer::singleShot( 0, this, SLOT( scanMonitor() ) ) + initDirOperations(); + m_aftEnabledPersistentTables << "lyrics" << "statistics" << "tags_labels"; +} + + +CollectionDB::~CollectionDB() +{ + DEBUG_BLOCK + +#ifdef HAVE_INOTIFY + if ( INotify::instance()->fd() >= 0 ) + close( INotify::instance()->fd() ); +#endif + + destroy(); +} + + +inline QString +CollectionDB::exactCondition( const QString &right ) +{ + if ( DbConnection::mysql == instance()->getDbConnectionType() ) + return QString( "= BINARY '" + instance()->escapeString( right ) + '\'' ); + else + return QString( "= '" + instance()->escapeString( right ) + '\'' ); +} + + +QString +CollectionDB::likeCondition( const QString &right, bool anyBegin, bool anyEnd ) +{ + QString escaped = right; + escaped.replace( '/', "//" ).replace( '%', "/%" ).replace( '_', "/_" ); + escaped = instance()->escapeString( escaped ); + + QString ret; + if ( DbConnection::postgresql == instance()->getDbConnectionType() ) + ret = " ILIKE "; //case-insensitive according to locale + else + ret = " LIKE "; + + ret += '\''; + if ( anyBegin ) + ret += '%'; + ret += escaped; + if ( anyEnd ) + ret += '%'; + ret += '\''; + + //Use / as the escape character + ret += " ESCAPE '/' "; + + return ret; +} + +////////////////////////////////////////////////////////////////////////////////////////// +// PUBLIC +////////////////////////////////////////////////////////////////////////////////////////// + +void +CollectionDB::initDirOperations() +{ + //this code was originally part of the ctor. It has to call MountPointManager to + //generate absolute paths from deviceids and relative paths. MountPointManager's ctor + //absolutely has to access the database, which resulted in a recursive ctor call. To + //solve this problem, the directory access code was moved into its own method, which can + //only be called when the CollectionDB object already exists. + + //FIXME max: make sure we check additional directories if we connect a new device +#ifdef HAVE_INOTIFY + // Try to initialize inotify, if not available use the old timer approach. + int inotify_fd = inotify_init(); + if ( inotify_fd < 0 ) +#endif + { +// debug() << "INotify not available, using QTimer!" << endl; + startTimer( MONITOR_INTERVAL * 1000 ); + } +#ifdef HAVE_INOTIFY + else + { + debug() << "INotify enabled!" << endl; + ThreadManager::instance()->onlyOneJob( new INotify( this, inotify_fd ) ); + } +#endif + +} + + +/** + * Executes a SQL query on the already opened database + * @param statement SQL program to execute. Only one SQL statement is allowed. + * @return The queried data, or QStringList() on error. + */ +QStringList +CollectionDB::query( const QString& statement, bool suppressDebug ) +{ + m_mutex.lock(); + clock_t start; + if ( DEBUG ) + { + debug() << "Query-start: " << statement << endl; + start = clock(); + } + if ( statement.stripWhiteSpace().isEmpty() ) + { + m_mutex.unlock(); + return QStringList(); + } + + DbConnection *dbConn; + dbConn = getMyConnection(); + + QStringList values = dbConn->query( statement, suppressDebug ); + if ( DEBUG ) + { + clock_t finish = clock(); + const double duration = (double) (finish - start) / CLOCKS_PER_SEC; + debug() << "SQL-query (" << duration << "s): " << statement << endl; + } + m_mutex.unlock(); + return values; +} + + +/** + * Executes a SQL insert on the already opened database + * @param statement SQL statement to execute. Only one SQL statement is allowed. + * @return The rowid of the inserted item. + */ +int +CollectionDB::insert( const QString& statement, const QString& table ) +{ + m_mutex.lock(); + clock_t start; + if ( DEBUG ) + { + debug() << "insert-start: " << statement << endl; + start = clock(); + } + + DbConnection *dbConn; + dbConn = getMyConnection(); + + int id = dbConn->insert( statement, table ); + + if ( DEBUG ) + { + clock_t finish = clock(); + const double duration = (double) (finish - start) / CLOCKS_PER_SEC; + debug() << "SQL-insert (" << duration << "s): " << statement << endl; + } + m_mutex.unlock(); + return id; +} + +QString +CollectionDB::deviceidSelection( const bool showAll ) +{ + if ( !showAll ) + { + IdList list = MountPointManager::instance()->getMountedDeviceIds(); + QString deviceIds = ""; + foreachType( IdList, list ) + { + if ( it != list.begin() ) deviceIds += ','; + deviceIds += QString::number(*it); + } + return " AND tags.deviceid IN (" + deviceIds + ')'; + } + else return ""; +} + +QStringList +CollectionDB::URLsFromQuery( const QStringList &result ) const +{ + QStringList values; + foreach( result ) + { + const int id = (*it).toInt(); + values << MountPointManager::instance()->getAbsolutePath( id, *(++it) ); + } + return values; +} + +KURL::List +CollectionDB::URLsFromSqlDrag( const QStringList &values ) const +{ + KURL::List urls; + for( QStringList::const_iterator it = values.begin(); + it != values.end(); + it++ ) + { + const QString &rel = *it; + it++; + int id = (*it).toInt(); + urls += KURL::fromPathOrURL( MountPointManager::instance()->getAbsolutePath( id, rel ) ); + for( int i = 0; + i < QueryBuilder::dragFieldCount-1 && it != values.end(); + i++ ) + it++; + } + + return urls; +} + +bool +CollectionDB::isEmpty( ) +{ + QStringList values; + + values = query( "SELECT COUNT( url ) FROM tags LIMIT 1 OFFSET 0;" ); + + return values.isEmpty() ? true : values.first() == "0"; +} + + +bool +CollectionDB::isValid( ) +{ + QStringList values1; + QStringList values2; + QStringList values3; + QStringList values4; + QStringList values5; + + values1 = query( "SELECT COUNT( url ) FROM tags LIMIT 1 OFFSET 0;" ); + values2 = query( "SELECT COUNT( url ) FROM statistics LIMIT 1 OFFSET 0;" ); + values3 = query( "SELECT COUNT( url ) FROM podcastchannels LIMIT 1 OFFSET 0;" ); + values4 = query( "SELECT COUNT( url ) FROM podcastepisodes LIMIT 1 OFFSET 0;" ); + values5 = query( "SELECT COUNT( id ) FROM devices LIMIT 1 OFFSET 0;" ); + + //It's valid as long as we've got _some_ tables that have something in. + return !( values1.isEmpty() && values2.isEmpty() && values3.isEmpty() && values4.isEmpty() && values5.isEmpty() ); +} + + +QString +CollectionDB::adminValue( QString noption ) { + QStringList values; + values = query ( + QString( "SELECT value FROM admin WHERE noption = '%1';").arg(noption) + ); + return values.isEmpty() ? "" : values.first(); +} + + +void +CollectionDB::setAdminValue( QString noption, QString value ) { + + QStringList values = query( QString( "SELECT value FROM admin WHERE noption = '%1';").arg( noption )); + if(values.count() > 0) + { + query( QString( "UPDATE admin SET value = '%1' WHERE noption = '%2';" ).arg( value, noption ) ); + } + else + { + insert( QString( "INSERT INTO admin (value, noption) values ( '%1', '%2' );" ).arg( value, noption ), + NULL ); + } +} + + + +void +CollectionDB::createTables( const bool temporary ) +{ + DEBUG_BLOCK + + //create tag table + query( QString( "CREATE %1 TABLE tags%2 (" + "url " + exactTextColumnType() + "," + "dir " + exactTextColumnType() + "," + "createdate INTEGER," + "modifydate INTEGER," + "album INTEGER," + "artist INTEGER," + "composer INTEGER," + "genre INTEGER," + "title " + textColumnType() + "," + "year INTEGER," + "comment " + longTextColumnType() + "," + "track NUMERIC(4)," + "discnumber INTEGER," + "bitrate INTEGER," + "length INTEGER," + "samplerate INTEGER," + "filesize INTEGER," + "filetype INTEGER," + "sampler BOOL," + "bpm FLOAT," + "deviceid INTEGER);" ) + .arg( temporary ? "TEMPORARY" : "" ) + .arg( temporary ? "_temp" : "" ) ); + + QString albumAutoIncrement = ""; + QString artistAutoIncrement = ""; + QString composerAutoIncrement = ""; + QString genreAutoIncrement = ""; + QString yearAutoIncrement = ""; + if ( getDbConnectionType() == DbConnection::postgresql ) + { + if(!temporary) + { + query( QString( "CREATE SEQUENCE album_seq;" ) ); + query( QString( "CREATE SEQUENCE artist_seq;" ) ); + query( QString( "CREATE SEQUENCE composer_seq;" ) ); + query( QString( "CREATE SEQUENCE genre_seq;" ) ); + query( QString( "CREATE SEQUENCE year_seq;" ) ); + } + + albumAutoIncrement = QString("DEFAULT nextval('album_seq')"); + artistAutoIncrement = QString("DEFAULT nextval('artist_seq')"); + composerAutoIncrement = QString("DEFAULT nextval('composer_seq')"); + genreAutoIncrement = QString("DEFAULT nextval('genre_seq')"); + yearAutoIncrement = QString("DEFAULT nextval('year_seq')"); + } + else if ( getDbConnectionType() == DbConnection::mysql ) + { + albumAutoIncrement = "AUTO_INCREMENT"; + artistAutoIncrement = "AUTO_INCREMENT"; + composerAutoIncrement = "AUTO_INCREMENT"; + genreAutoIncrement = "AUTO_INCREMENT"; + yearAutoIncrement = "AUTO_INCREMENT"; + } + + //create album table + query( QString( "CREATE %1 TABLE album%2 (" + "id INTEGER PRIMARY KEY %3," + "name " + textColumnType() + ");" ) + .arg( temporary ? "TEMPORARY" : "" ) + .arg( temporary ? "_temp" : "" ) + .arg( albumAutoIncrement ) ); + + //create artist table + query( QString( "CREATE %1 TABLE artist%2 (" + "id INTEGER PRIMARY KEY %3," + "name " + textColumnType() + ");" ) + .arg( temporary ? "TEMPORARY" : "" ) + .arg( temporary ? "_temp" : "" ) + .arg( artistAutoIncrement ) ); + + //create composer table + query( QString( "CREATE %1 TABLE composer%2 (" + "id INTEGER PRIMARY KEY %3," + "name " + textColumnType() + ");" ) + .arg( temporary ? "TEMPORARY" : "" ) + .arg( temporary ? "_temp" : "" ) + .arg( composerAutoIncrement ) ); + + //create genre table + query( QString( "CREATE %1 TABLE genre%2 (" + "id INTEGER PRIMARY KEY %3," + "name " + textColumnType() +");" ) + .arg( temporary ? "TEMPORARY" : "" ) + .arg( temporary ? "_temp" : "" ) + .arg( genreAutoIncrement ) ); + + //create year table + query( QString( "CREATE %1 TABLE year%2 (" + "id INTEGER PRIMARY KEY %3," + "name " + textColumnType() + ");" ) + .arg( temporary ? "TEMPORARY" : "" ) + .arg( temporary ? "_temp" : "" ) + .arg( yearAutoIncrement ) ); + + //create images table + query( QString( "CREATE %1 TABLE images%2 (" + "path " + exactTextColumnType() + "," + "deviceid INTEGER," + "artist " + textColumnType() + "," + "album " + textColumnType() + ");" ) + .arg( temporary ? "TEMPORARY" : "" ) + .arg( temporary ? "_temp" : "" ) ); + + //create embed table + query( QString( "CREATE %1 TABLE embed%2 (" + "url " + exactTextColumnType() + "," + "deviceid INTEGER," + "hash " + exactTextColumnType() + "," + "description " + textColumnType() + ");" ) + .arg( temporary ? "TEMPORARY" : "" ) + .arg( temporary ? "_temp" : "" ) ); + + // create directory statistics table + query( QString( "CREATE %1 TABLE directories%2 (" + "dir " + exactTextColumnType() + "," + "deviceid INTEGER," + "changedate INTEGER);" ) + .arg( temporary ? "TEMPORARY" : "" ) + .arg( temporary ? "_temp" : "" ) ); + + //create uniqueid table + query( QString( "CREATE %1 TABLE uniqueid%2 (" + "url " + exactTextColumnType() + "," + "deviceid INTEGER," + "uniqueid " + exactTextColumnType(32) + " UNIQUE," + "dir " + exactTextColumnType() + ");" ) + .arg( temporary ? "TEMPORARY" : "" ) + .arg( temporary ? "_temp" : "" ) ); + + //create indexes + query( QString( "CREATE INDEX album_idx%1 ON album%2( name );" ) + .arg( temporary ? "_temp" : "" ).arg( temporary ? "_temp" : "" ) ); + query( QString( "CREATE INDEX artist_idx%1 ON artist%2( name );" ) + .arg( temporary ? "_temp" : "" ).arg( temporary ? "_temp" : "" ) ); + query( QString( "CREATE INDEX composer_idx%1 ON composer%2( name );" ) + .arg( temporary ? "_temp" : "" ).arg( temporary ? "_temp" : "" ) ); + query( QString( "CREATE INDEX genre_idx%1 ON genre%2( name );" ) + .arg( temporary ? "_temp" : "" ).arg( temporary ? "_temp" : "" ) ); + query( QString( "CREATE INDEX year_idx%1 ON year%2( name );" ) + .arg( temporary ? "_temp" : "" ).arg( temporary ? "_temp" : "" ) ); + + if ( !temporary ) + { + //create admin table -- holds the db version, put here other stuff if necessary + query( QString( "CREATE TABLE admin (" + "noption " + textColumnType() + ", " + "value " + textColumnType() + ");" ) ); + + // create related artists cache + query( QString( "CREATE TABLE related_artists (" + "artist " + textColumnType() + "," + "suggestion " + textColumnType() + "," + "changedate INTEGER );" ) ); + + createIndices(); + } + else + { + query( "CREATE UNIQUE INDEX url_tagtemp ON tags_temp( url, deviceid );" ); + query( "CREATE UNIQUE INDEX embed_urltemp ON embed_temp( url, deviceid );" ); + query( "CREATE UNIQUE INDEX dir_temp_dir ON directories_temp( dir, deviceid );" ); + query( "CREATE INDEX album_tagtemp ON tags_temp( album );" ); + query( "CREATE INDEX artist_tagtemp ON tags_temp( artist );" ); + query( "CREATE INDEX sampler_tagtemp ON tags_temp( sampler );" ); + query( "CREATE INDEX uniqueidtemp_uniqueid ON uniqueid_temp( uniqueid );"); + query( "CREATE INDEX uniqueidtemp_url ON uniqueid_temp( url, deviceid );"); + } +} + +void +CollectionDB::createIndices() +{ + //This creates the indices for tables created in createTables. It should not refer to + //tables which are not created in that function. + debug() << "Creating indices, ignore errors about already existing indices" << endl; + + query( "CREATE UNIQUE INDEX url_tag ON tags( url, deviceid );" ); + query( "CREATE INDEX album_tag ON tags( album );" ); + query( "CREATE INDEX artist_tag ON tags( artist );" ); + query( "CREATE INDEX composer_tag ON tags( composer );" ); + query( "CREATE INDEX genre_tag ON tags( genre );" ); + query( "CREATE INDEX year_tag ON tags( year );" ); + query( "CREATE INDEX sampler_tag ON tags( sampler );" ); + + query( "CREATE INDEX images_album ON images( album );" ); + query( "CREATE INDEX images_artist ON images( artist );" ); + + query( "CREATE INDEX images_url ON images( path, deviceid );" ); + + query( "CREATE UNIQUE INDEX embed_url ON embed( url, deviceid );" ); + query( "CREATE INDEX embed_hash ON embed( hash );" ); + + query( "CREATE UNIQUE INDEX directories_dir ON directories( dir, deviceid );" ); + query( "CREATE INDEX uniqueid_uniqueid ON uniqueid( uniqueid );"); + query( "CREATE INDEX uniqueid_url ON uniqueid( url, deviceid );"); + + query( "CREATE INDEX album_idx ON album( name );" ); + query( "CREATE INDEX artist_idx ON artist( name );" ); + query( "CREATE INDEX composer_idx ON composer( name );" ); + query( "CREATE INDEX genre_idx ON genre( name );" ); + query( "CREATE INDEX year_idx ON year( name );" ); + + query( "CREATE INDEX tags_artist_index ON tags( artist );" ); + query( "CREATE INDEX tags_album_index ON tags( album );" ); + query( "CREATE INDEX tags_deviceid_index ON tags( deviceid ); "); + query( "CREATE INDEX tags_url_index ON tags( url ); "); + + query( "CREATE INDEX embed_deviceid_index ON embed( deviceid ); "); + query( "CREATE INDEX embed_url_index ON embed( url ); "); + + query( "CREATE INDEX related_artists_artist ON related_artists( artist );" ); + + debug() << "Finished creating indices, stop ignoring errors" << endl; +} + +void +CollectionDB::createPermanentIndices() +{ + //this method creates all indices which are not referred to in createTables + //this method is called on each startup of amarok + //until we figure out a way to handle this better it produces SQL errors if the indices + //already exist, but these can be ignored + debug() << "Creating permanent indices, ignore errors about already existing indices" << endl; + + query( "CREATE UNIQUE INDEX lyrics_url ON lyrics( url, deviceid );" ); + query( "CREATE INDEX lyrics_uniqueid ON lyrics( uniqueid );" ); + query( "CREATE INDEX playlist_playlists ON playlists( playlist );" ); + query( "CREATE INDEX url_playlists ON playlists( url );" ); + query( "CREATE UNIQUE INDEX labels_name ON labels( name, type );" ); + query( "CREATE INDEX tags_labels_uniqueid ON tags_labels( uniqueid );" ); //m:n relationship, DO NOT MAKE UNIQUE! + query( "CREATE INDEX tags_labels_url ON tags_labels( url, deviceid );" ); //m:n relationship, DO NOT MAKE UNIQUE! + query( "CREATE INDEX tags_labels_labelid ON tags_labels( labelid );" ); //m:n relationship, DO NOT MAKE UNIQUE! + + query( "CREATE UNIQUE INDEX url_stats ON statistics( deviceid, url );" ); + query( "CREATE INDEX percentage_stats ON statistics( percentage );" ); + query( "CREATE INDEX rating_stats ON statistics( rating );" ); + query( "CREATE INDEX playcounter_stats ON statistics( playcounter );" ); + query( "CREATE INDEX uniqueid_stats ON statistics( uniqueid );" ); + + query( "CREATE INDEX url_podchannel ON podcastchannels( url );" ); + query( "CREATE INDEX url_podepisode ON podcastepisodes( url );" ); + query( "CREATE INDEX localurl_podepisode ON podcastepisodes( localurl );" ); + query( "CREATE INDEX url_podfolder ON podcastfolders( id );" ); + + debug() << "Finished creating permanent indices, stop ignoring errors" << endl; +} + + +void +CollectionDB::dropTables( const bool temporary ) +{ + query( QString( "DROP TABLE tags%1;" ).arg( temporary ? "_temp" : "" ) ); + query( QString( "DROP TABLE album%1;" ).arg( temporary ? "_temp" : "" ) ); + query( QString( "DROP TABLE artist%1;" ).arg( temporary ? "_temp" : "" ) ); + query( QString( "DROP TABLE composer%1;" ).arg( temporary ? "_temp" : "" ) ); + query( QString( "DROP TABLE genre%1;" ).arg( temporary ? "_temp" : "" ) ); + query( QString( "DROP TABLE year%1;" ).arg( temporary ? "_temp" : "" ) ); + query( QString( "DROP TABLE images%1;" ).arg( temporary ? "_temp" : "" ) ); + query( QString( "DROP TABLE embed%1;" ).arg( temporary ? "_temp" : "" ) ); + query( QString( "DROP TABLE directories%1;" ).arg( temporary ? "_temp" : "" ) ); + query( QString( "DROP TABLE uniqueid%1;" ).arg( temporary ? "_temp" : "" ) ); + if ( !temporary ) + { + query( QString( "DROP TABLE related_artists;" ) ); + debug() << "Dropping media table" << endl; + } + + if ( getDbConnectionType() == DbConnection::postgresql ) + { + if (temporary == false) { + query( QString( "DROP SEQUENCE album_seq;" ) ); + query( QString( "DROP SEQUENCE artist_seq;" ) ); + query( QString( "DROP SEQUENCE composer_seq;" ) ); + query( QString( "DROP SEQUENCE genre_seq;" ) ); + query( QString( "DROP SEQUENCE year_seq;" ) ); + } + } +} + + +void +CollectionDB::clearTables( const bool temporary ) +{ + QString clearCommand = "DELETE FROM"; + if ( getDbConnectionType() == DbConnection::mysql || getDbConnectionType() == DbConnection::postgresql) + { + // TRUNCATE TABLE is faster than DELETE FROM TABLE, so use it when supported. + clearCommand = "TRUNCATE TABLE"; + } + + query( QString( "%1 tags%2;" ).arg( clearCommand ).arg( temporary ? "_temp" : "" ) ); + query( QString( "%1 album%2;" ).arg( clearCommand ).arg( temporary ? "_temp" : "" ) ); + query( QString( "%1 artist%2;" ).arg( clearCommand ).arg( temporary ? "_temp" : "" ) ); + query( QString( "%1 composer%2;" ).arg( clearCommand ).arg( temporary ? "_temp" : "" ) ); + query( QString( "%1 genre%2;" ).arg( clearCommand ).arg( temporary ? "_temp" : "" ) ); + query( QString( "%1 year%2;" ).arg( clearCommand ).arg( temporary ? "_temp" : "" ) ); + query( QString( "%1 images%2;" ).arg( clearCommand ).arg( temporary ? "_temp" : "" ) ); + query( QString( "%1 embed%2;" ).arg( clearCommand ).arg( temporary ? "_temp" : "" ) ); + query( QString( "%1 directories%2;" ).arg( clearCommand ).arg( temporary ? "_temp" : "" ) ); + query( QString( "%1 uniqueid%2;" ).arg( clearCommand ).arg( temporary ? "_temp" : "" ) ); + if ( !temporary ) + { + query( QString( "%1 related_artists;" ).arg( clearCommand ) ); + //debug() << "Clearing media table" << endl; + //query( QString( "%1 media;" ).arg( clearCommand ) ); + } +} + + +void +CollectionDB::copyTempTables( ) +{ + DEBUG_BLOCK + + insert( "INSERT INTO tags SELECT * FROM tags_temp;", NULL ); + + //mysql 5 supports subqueries with IN, mysql 4 doesn't. this way will work for all SQL servers + QStringList albumIdList = query( "SELECT album.id FROM album;" ); + //in an empty database, albumIdList is empty. This would result in a SQL query like NOT IN ( ) without + //the -1 below which is invalid SQL. The auto generated values start at 1 so this is fine + QString albumIds = "-1"; + foreach( albumIdList ) + { + albumIds += ','; + albumIds += *it; + } + insert( QString ( "INSERT INTO album SELECT * FROM album_temp WHERE album_temp.id NOT IN ( %1 );" ).arg( albumIds ), NULL ); + + QStringList artistIdList = query( "SELECT artist.id FROM artist;" ); + QString artistIds = "-1"; + foreach( artistIdList ) + { + artistIds += ','; + artistIds += *it; + } + insert( QString ( "INSERT INTO artist SELECT * FROM artist_temp WHERE artist_temp.id NOT IN ( %1 );" ).arg( artistIds ), NULL ); + + QStringList composerIdList = query( "SELECT composer.id FROM composer;" ); + QString composerIds = "-1"; + foreach( composerIdList ) + { + composerIds += ','; + composerIds += *it; + } + insert( QString ( "INSERT INTO composer SELECT * FROM composer_temp WHERE composer_temp.id NOT IN ( %1 );" ).arg( composerIds ), NULL ); + + QStringList genreIdList = query( "SELECT genre.id FROM genre;" ); + QString genreIds = "-1"; + foreach( genreIdList ) + { + genreIds += ','; + genreIds += *it; + } + insert( QString ( "INSERT INTO genre SELECT * FROM genre_temp WHERE genre_temp.id NOT IN ( %1 );" ).arg( genreIds ), NULL ); + + QStringList yearIdList = query( "SELECT year.id FROM year;" ); + QString yearIds = "-1"; + foreach( yearIdList ) + { + yearIds += ','; + yearIds += *it; + } + insert( QString ( "INSERT INTO year SELECT * FROM year_temp WHERE year_temp.id NOT IN ( %1 );" ).arg( yearIds ), NULL ); + + insert( "INSERT INTO images SELECT * FROM images_temp;", NULL ); + insert( "INSERT INTO embed SELECT * FROM embed_temp;", NULL ); + insert( "INSERT INTO directories SELECT * FROM directories_temp;", NULL ); + insert( "INSERT INTO uniqueid SELECT * FROM uniqueid_temp;", NULL ); +} + +void +CollectionDB::prepareTempTables() +{ + DEBUG_BLOCK + insert( "INSERT INTO album_temp SELECT * from album;", 0 ); + insert( "INSERT INTO artist_temp SELECT * from artist;", 0 ); + insert( "INSERT INTO composer_temp SELECT * from composer;", 0 ); + insert( "INSERT INTO genre_temp SELECT * from genre;", 0 ); + insert( "INSERT INTO year_temp SELECT * from year;", 0 ); +} + +void +CollectionDB::createDevicesTable() +{ + debug() << "Creating DEVICES table" << endl; + QString deviceAutoIncrement = ""; + if ( getDbConnectionType() == DbConnection::postgresql ) + { + query( QString( "CREATE SEQUENCE devices_seq;" ) ); + deviceAutoIncrement = QString("DEFAULT nextval('devices_seq')"); + } + else if ( getDbConnectionType() == DbConnection::mysql ) + { + deviceAutoIncrement = "AUTO_INCREMENT"; + } + query( QString( "CREATE TABLE devices (" + "id INTEGER PRIMARY KEY %1," + "type " + textColumnType() + "," + "label " + textColumnType() + "," + "lastmountpoint " + textColumnType() + "," + "uuid " + textColumnType() + "," + "servername " + textColumnType() + "," + "sharename " + textColumnType() + ");" ) + .arg( deviceAutoIncrement ) ); + query( "CREATE INDEX devices_type ON devices( type );" ); + query( "CREATE INDEX devices_uuid ON devices( uuid );" ); + query( "CREATE INDEX devices_rshare ON devices( servername, sharename );" ); +} + +void +CollectionDB::createStatsTable() +{ + // create music statistics database + query( QString( "CREATE TABLE statistics (" + "url " + exactTextColumnType() + "," + "deviceid INTEGER," + "createdate INTEGER," + "accessdate INTEGER," + "percentage FLOAT," + "rating INTEGER DEFAULT 0," + "playcounter INTEGER," + "uniqueid " + exactTextColumnType(32) + " UNIQUE," + "deleted BOOL DEFAULT " + boolF() + "," + "PRIMARY KEY(url, deviceid) );" ) ); + +} + +//Old version, used in upgrade code. This should never be changed. +void +CollectionDB::createStatsTableV8() +{ + // create music statistics database - old form, for upgrade code. + query( QString( "CREATE TABLE statistics (" + "url " + textColumnType() + " UNIQUE," + "createdate INTEGER," + "accessdate INTEGER," + "percentage FLOAT," + "rating INTEGER DEFAULT 0," + "playcounter INTEGER," + "uniqueid " + textColumnType(8) + " UNIQUE," + "deleted BOOL DEFAULT " + boolF() + ");" ) ); + + query( "CREATE INDEX url_stats ON statistics( url );" ); + query( "CREATE INDEX percentage_stats ON statistics( percentage );" ); + query( "CREATE INDEX rating_stats ON statistics( rating );" ); + query( "CREATE INDEX playcounter_stats ON statistics( playcounter );" ); + query( "CREATE INDEX uniqueid_stats ON statistics( uniqueid );" ); +} + +//Old version, used in upgrade code +void +CollectionDB::createStatsTableV10( bool temp ) +{ + // create music statistics database + query( QString( "CREATE %1 TABLE statistics%2 (" + "url " + exactTextColumnType() + "," + "deviceid INTEGER," + "createdate INTEGER," + "accessdate INTEGER," + "percentage FLOAT," + "rating INTEGER DEFAULT 0," + "playcounter INTEGER," + "uniqueid " + exactTextColumnType(32) + " UNIQUE," + "deleted BOOL DEFAULT " + boolF() + "," + "PRIMARY KEY(url, deviceid) );" + ).arg( temp ? "TEMPORARY" : "" ) + .arg( temp ? "_fix_ten" : "" ) ); + + if ( !temp ) + { + query( "CREATE UNIQUE INDEX url_stats ON statistics( deviceid, url );" ); + query( "CREATE INDEX percentage_stats ON statistics( percentage );" ); + query( "CREATE INDEX rating_stats ON statistics( rating );" ); + query( "CREATE INDEX playcounter_stats ON statistics( playcounter );" ); + query( "CREATE INDEX uniqueid_stats ON statistics( uniqueid );" ); + } +} + + +void +CollectionDB::dropStatsTable() +{ + query( "DROP TABLE statistics;" ); +} + +void +CollectionDB::dropStatsTableV1() +{ + query( "DROP TABLE statistics;" ); +} + +void +CollectionDB::createPersistentTables() +{ + // create amazon table + query( "CREATE TABLE amazon ( " + "asin " + textColumnType(20) + ", " + "locale " + textColumnType(2) + ", " + "filename " + exactTextColumnType(33) + ", " + "refetchdate INTEGER );" ); + + // create lyrics table + query( QString( "CREATE TABLE lyrics (" + "url " + exactTextColumnType() + ", " + "deviceid INTEGER," + "lyrics " + longTextColumnType() + ", " + "uniqueid " + exactTextColumnType(32) + ");" ) ); + + query( QString( "CREATE TABLE playlists (" + "playlist " + textColumnType() + ", " + "url " + exactTextColumnType() + ", " + "tracknum INTEGER );" ) ); + + QString labelsAutoIncrement = ""; + if ( getDbConnectionType() == DbConnection::postgresql ) + { + query( QString( "CREATE SEQUENCE labels_seq;" ) ); + + labelsAutoIncrement = QString("DEFAULT nextval('labels_seq')"); + } + else if ( getDbConnectionType() == DbConnection::mysql ) + { + labelsAutoIncrement = "AUTO_INCREMENT"; + } + + //create labels tables + query( QString( "CREATE TABLE labels (" + "id INTEGER PRIMARY KEY " + labelsAutoIncrement + ", " + "name " + textColumnType() + ", " + "type INTEGER);" ) ); + + query( QString( "CREATE TABLE tags_labels (" + "deviceid INTEGER," + "url " + exactTextColumnType() + ", " + "uniqueid " + exactTextColumnType(32) + ", " //m:n relationship, DO NOT MAKE UNIQUE! + "labelid INTEGER REFERENCES labels( id ) ON DELETE CASCADE );" ) ); +} + +void +CollectionDB::createPersistentTablesV12() +{ + // create amazon table + query( "CREATE TABLE amazon ( " + "asin " + textColumnType(20) + ", " + "locale " + textColumnType(2) + ", " + "filename " + textColumnType(33) + ", " + "refetchdate INTEGER );" ); + + // create lyrics table + query( QString( "CREATE TABLE lyrics (" + "url " + textColumnType() + ", " + "lyrics " + longTextColumnType() + ");" ) ); + + // create labels table + query( QString( "CREATE TABLE label (" + "url " + textColumnType() + "," + "label " + textColumnType() + ");" ) ); + + query( QString( "CREATE TABLE playlists (" + "playlist " + textColumnType() + ", " + "url " + textColumnType() + ", " + "tracknum INTEGER );" ) ); + + query( "CREATE INDEX url_label ON label( url );" ); + query( "CREATE INDEX label_label ON label( label );" ); + query( "CREATE INDEX playlist_playlists ON playlists( playlist );" ); + query( "CREATE INDEX url_playlists ON playlists( url );" ); +} + +void +CollectionDB::createPersistentTablesV14( bool temp ) +{ + const QString a( temp ? "TEMPORARY" : "" ); + const QString b( temp ? "_fix" : "" ); + + // create amazon table + query( QString( "CREATE %1 TABLE amazon%2 ( " + "asin " + textColumnType(20) + ", " + "locale " + textColumnType(2) + ", " + "filename " + exactTextColumnType(33) + ", " + "refetchdate INTEGER );" ).arg( a,b ) ); + + // create lyrics table + query( QString( "CREATE %1 TABLE lyrics%2 (" + "url " + exactTextColumnType() + ", " + "deviceid INTEGER," + "lyrics " + longTextColumnType() + ");" ).arg( a,b ) ); + + query( QString( "CREATE %1 TABLE playlists%2 (" + "playlist " + textColumnType() + ", " + "url " + exactTextColumnType() + ", " + "tracknum INTEGER );" ).arg( a,b ) ); + + if ( !temp ) + { + query( "CREATE UNIQUE INDEX lyrics_url ON lyrics( url, deviceid );" ); + query( "CREATE INDEX playlist_playlists ON playlists( playlist );" ); + query( "CREATE INDEX url_playlists ON playlists( url );" ); + } +} + +void +CollectionDB::createPodcastTables() +{ + QString podcastAutoIncrement = ""; + QString podcastFolderAutoInc = ""; + if ( getDbConnectionType() == DbConnection::postgresql ) + { + query( QString( "CREATE SEQUENCE podcastepisode_seq;" ) ); + + query( QString( "CREATE SEQUENCE podcastfolder_seq;" ) ); + + podcastAutoIncrement = QString("DEFAULT nextval('podcastepisode_seq')"); + podcastFolderAutoInc = QString("DEFAULT nextval('podcastfolder_seq')"); + } + else if ( getDbConnectionType() == DbConnection::mysql ) + { + podcastAutoIncrement = "AUTO_INCREMENT"; + podcastFolderAutoInc = "AUTO_INCREMENT"; + } + + // create podcast channels table + query( QString( "CREATE TABLE podcastchannels (" + "url " + exactTextColumnType() + " UNIQUE," + "title " + textColumnType() + "," + "weblink " + exactTextColumnType() + "," + "image " + exactTextColumnType() + "," + "comment " + longTextColumnType() + "," + "copyright " + textColumnType() + "," + "parent INTEGER," + "directory " + textColumnType() + "," + "autoscan BOOL, fetchtype INTEGER, " + "autotransfer BOOL, haspurge BOOL, purgecount INTEGER );" ) ); + + // create podcast episodes table + query( QString( "CREATE TABLE podcastepisodes (" + "id INTEGER PRIMARY KEY %1, " + "url " + exactTextColumnType() + " UNIQUE," + "localurl " + exactTextColumnType() + "," + "parent " + exactTextColumnType() + "," + "guid " + exactTextColumnType() + "," + "title " + textColumnType() + "," + "subtitle " + textColumnType() + "," + "composer " + textColumnType() + "," + "comment " + longTextColumnType() + "," + "filetype " + textColumnType() + "," + "createdate " + textColumnType() + "," + "length INTEGER," + "size INTEGER," + "isNew BOOL );" ) + .arg( podcastAutoIncrement ) ); + + // create podcast folders table + query( QString( "CREATE TABLE podcastfolders (" + "id INTEGER PRIMARY KEY %1, " + "name " + textColumnType() + "," + "parent INTEGER, isOpen BOOL );" ) + .arg( podcastFolderAutoInc ) ); + + query( "CREATE INDEX url_podchannel ON podcastchannels( url );" ); + query( "CREATE INDEX url_podepisode ON podcastepisodes( url );" ); + query( "CREATE INDEX localurl_podepisode ON podcastepisodes( localurl );" ); + query( "CREATE INDEX url_podfolder ON podcastfolders( id );" ); +} + +void +CollectionDB::createPodcastTablesV2( bool temp ) +{ + const QString a( temp ? "TEMPORARY" : "" ); + const QString b( temp ? "_fix" : "" ); + + QString podcastAutoIncrement = ""; + QString podcastFolderAutoInc = ""; + if ( getDbConnectionType() == DbConnection::postgresql ) + { + query( QString( "CREATE SEQUENCE podcastepisode_seq;" ) ); + + query( QString( "CREATE SEQUENCE podcastfolder_seq;" ) ); + + podcastAutoIncrement = QString("DEFAULT nextval('podcastepisode_seq')"); + podcastFolderAutoInc = QString("DEFAULT nextval('podcastfolder_seq')"); + } + else if ( getDbConnectionType() == DbConnection::mysql ) + { + podcastAutoIncrement = "AUTO_INCREMENT"; + podcastFolderAutoInc = "AUTO_INCREMENT"; + } + + // create podcast channels table + query( QString( "CREATE %1 TABLE podcastchannels%2 (" + "url " + exactTextColumnType() + " UNIQUE," + "title " + textColumnType() + "," + "weblink " + exactTextColumnType() + "," + "image " + exactTextColumnType() + "," + "comment " + longTextColumnType() + "," + "copyright " + textColumnType() + "," + "parent INTEGER," + "directory " + textColumnType() + "," + "autoscan BOOL, fetchtype INTEGER, " + "autotransfer BOOL, haspurge BOOL, purgecount INTEGER );" ).arg( a,b ) ); + + // create podcast episodes table + query( QString( "CREATE %2 TABLE podcastepisodes%3 (" + "id INTEGER PRIMARY KEY %1, " + "url " + exactTextColumnType() + " UNIQUE," + "localurl " + exactTextColumnType() + "," + "parent " + exactTextColumnType() + "," + "guid " + exactTextColumnType() + "," + "title " + textColumnType() + "," + "subtitle " + textColumnType() + "," + "composer " + textColumnType() + "," + "comment " + longTextColumnType() + "," + "filetype " + textColumnType() + "," + "createdate " + textColumnType() + "," + "length INTEGER," + "size INTEGER," + "isNew BOOL );" ) + .arg( podcastAutoIncrement, a, b ) ); + + // create podcast folders table + query( QString( "CREATE %2 TABLE podcastfolders%3 (" + "id INTEGER PRIMARY KEY %1, " + "name " + textColumnType() + "," + "parent INTEGER, isOpen BOOL );" ) + .arg( podcastFolderAutoInc, a, b ) ); + + if ( !temp ) + { + query( "CREATE INDEX url_podchannel ON podcastchannels( url );" ); + query( "CREATE INDEX url_podepisode ON podcastepisodes( url );" ); + query( "CREATE INDEX localurl_podepisode ON podcastepisodes( localurl );" ); + query( "CREATE INDEX url_podfolder ON podcastfolders( id );" ); + } +} + + +void +CollectionDB::dropPersistentTables() +{ + query( "DROP TABLE amazon;" ); + query( "DROP TABLE lyrics;" ); + query( "DROP TABLE playlists;" ); + query( "DROP TABLE tags_labels;" ); + query( "DROP TABLE labels;" ); +} + +void +CollectionDB::dropPersistentTablesV14() +{ + query( "DROP TABLE amazon;" ); + query( "DROP TABLE lyrics;" ); + query( "DROP TABLE label;" ); + query( "DROP TABLE playlists;" ); +} + +void +CollectionDB::dropPodcastTables() +{ + query( "DROP TABLE podcastchannels;" ); + query( "DROP TABLE podcastepisodes;" ); + query( "DROP TABLE podcastfolders;" ); +} + +void +CollectionDB::dropPodcastTablesV2() +{ + query( "DROP TABLE podcastchannels;" ); + query( "DROP TABLE podcastepisodes;" ); + query( "DROP TABLE podcastfolders;" ); +} + +void +CollectionDB::dropDevicesTable() +{ + query( "DROP TABLE devices;" ); +} + +uint +CollectionDB::artistID( QString value, bool autocreate, const bool temporary, bool exact /* = true */ ) +{ + // lookup cache + if ( m_validArtistCache && m_cacheArtist[(int)temporary] == value ) + return m_cacheArtistID[(int)temporary]; + + uint id; + if ( exact ) + id = IDFromExactValue( "artist", value, autocreate, temporary ).toUInt(); + else + id = IDFromValue( "artist", value, autocreate, temporary ); + + // cache values + m_cacheArtist[(int)temporary] = value; + m_cacheArtistID[(int)temporary] = id; + m_validArtistCache = 1; + + return id; +} + + +QString +CollectionDB::artistValue( uint id ) +{ + // lookup cache + if ( m_cacheArtistID[0] == id ) + return m_cacheArtist[0]; + + QString value = valueFromID( "artist", id ); + + // cache values + m_cacheArtist[0] = value; + m_cacheArtistID[0] = id; + + return value; +} + + +uint +CollectionDB::composerID( QString value, bool autocreate, const bool temporary, bool exact /* = true */ ) +{ + // lookup cache + if ( m_validComposerCache && m_cacheComposer[(int)temporary] == value ) + return m_cacheComposerID[(int)temporary]; + + uint id; + if ( exact ) + id = IDFromExactValue( "composer", value, autocreate, temporary ).toUInt(); + else + id = IDFromValue( "composer", value, autocreate, temporary ); + + // cache values + m_cacheComposer[(int)temporary] = value; + m_cacheComposerID[(int)temporary] = id; + m_validComposerCache = 1; + + return id; +} + + +QString +CollectionDB::composerValue( uint id ) +{ + // lookup cache + if ( m_cacheComposerID[0] == id ) + return m_cacheComposer[0]; + + QString value = valueFromID( "composer", id ); + + // cache values + m_cacheComposer[0] = value; + m_cacheComposerID[0] = id; + + return value; +} + + +uint +CollectionDB::albumID( QString value, bool autocreate, const bool temporary, bool exact /* = true */ ) +{ + // lookup cache + if ( m_validAlbumCache && m_cacheAlbum[(int)temporary] == value ) + return m_cacheAlbumID[(int)temporary]; + + uint id; + if ( exact ) + id = IDFromExactValue( "album", value, autocreate, temporary ).toUInt(); + else + id = IDFromValue( "album", value, autocreate, temporary ); + + // cache values + m_cacheAlbum[(int)temporary] = value; + m_cacheAlbumID[(int)temporary] = id; + m_validAlbumCache = 1; + + return id; +} + +QString +CollectionDB::albumValue( uint id ) +{ + // lookup cache + if ( m_cacheAlbumID[0] == id ) + return m_cacheAlbum[0]; + + QString value = valueFromID( "album", id ); + + // cache values + m_cacheAlbum[0] = value; + m_cacheAlbumID[0] = id; + + return value; +} + +uint +CollectionDB::genreID( QString value, bool autocreate, const bool temporary, bool exact /* = true */ ) +{ + return exact ? + IDFromExactValue( "genre", value, autocreate, temporary ).toUInt() : + IDFromValue( "genre", value, autocreate, temporary ); +} + +QString +CollectionDB::genreValue( uint id ) +{ + return valueFromID( "genre", id ); +} + + +uint +CollectionDB::yearID( QString value, bool autocreate, const bool temporary, bool exact /* = true */ ) +{ + return exact ? + IDFromExactValue( "year", value, autocreate, temporary ).toUInt() : + IDFromValue( "year", value, autocreate, temporary ); +} + + +QString +CollectionDB::yearValue( uint id ) +{ + return valueFromID( "year", id ); +} + + +uint +CollectionDB::IDFromValue( QString name, QString value, bool autocreate, const bool temporary ) +{ + if ( temporary ) + name.append( "_temp" ); +// what the hell is the reason for this? +// else +// conn = NULL; + + QStringList values = + query( QString( + "SELECT id, name FROM %1 WHERE name %2;" ) + .arg( name ) + .arg( CollectionDB::likeCondition( value ) ) ); + + //check if item exists. if not, should we autocreate it? + uint id; + if ( values.isEmpty() && autocreate ) + { + id = insert( QString( "INSERT INTO %1 ( name ) VALUES ( '%2' );" ) + .arg( name ) + .arg( CollectionDB::instance()->escapeString( value ) ), name ); + + return id; + } + + return values.isEmpty() ? 0 : values.first().toUInt(); +} + + +QString +CollectionDB::valueFromID( QString table, uint id ) +{ + QStringList values = + query( QString( + "SELECT name FROM %1 WHERE id=%2;" ) + .arg( table ) + .arg( id ) ); + + + return values.isEmpty() ? 0 : values.first(); +} + + +QString +CollectionDB::albumSongCount( const QString &artist_id, const QString &album_id ) +{ + QStringList values = + query( QString( + "SELECT COUNT( url ) FROM tags WHERE album = %1 AND artist = %2;" ) + .arg( album_id ) + .arg( artist_id ) ); + return values.first(); +} + +bool +CollectionDB::albumIsCompilation( const QString &album_id ) +{ + QStringList values = + query( QString( + "SELECT sampler FROM tags WHERE sampler=%1 AND album=%2" ) + .arg( CollectionDB::instance()->boolT() ) + .arg( album_id ) ); + + return (values.count() != 0); +} + +QStringList +CollectionDB::albumTracks( const QString &artist_id, const QString &album_id ) +{ + QueryBuilder qb; + qb.addReturnValue( QueryBuilder::tabSong, QueryBuilder::valURL ); + qb.addMatch( QueryBuilder::tabAlbum, QueryBuilder::valID, album_id ); + const bool isCompilation = albumIsCompilation( album_id ); + if( !isCompilation ) + qb.addMatch( QueryBuilder::tabArtist, QueryBuilder::valID, artist_id ); + qb.sortBy( QueryBuilder::tabSong, QueryBuilder::valDiscNumber ); + qb.sortBy( QueryBuilder::tabSong, QueryBuilder::valTrack ); + QStringList ret = qb.run(); + + uint returnValues = qb.countReturnValues(); + if ( returnValues > 1 ) + { + QStringList ret2; + for ( QStringList::size_type i = 0; i < ret.size(); i += returnValues ) + ret2 << ret[ i ]; + return ret2; + } + else + return ret; +} + +QStringList +CollectionDB::albumDiscTracks( const QString &artist_id, const QString &album_id, const QString &discNumber) +{ + QStringList rs; + rs = query( QString( "SELECT tags.deviceid, tags.url FROM tags, year WHERE tags.album = %1 AND " + "tags.artist = %2 AND year.id = tags.year AND tags.discnumber = %3 " + + deviceidSelection() + " ORDER BY tags.track;" ) + .arg( album_id ) + .arg( artist_id ) + .arg( discNumber ) ); + QStringList result; + foreach( rs ) + { + const int id = (*it).toInt(); + result << MountPointManager::instance()->getAbsolutePath( id, *(++it) ); + } + return result; +} + +QStringList +CollectionDB::artistTracks( const QString &artist_id ) +{ + QStringList rs = query( QString( "SELECT tags.deviceid, tags.url FROM tags, album " + "WHERE tags.artist = '%1' AND album.id = tags.album " + deviceidSelection() + + "ORDER BY album.name, tags.discnumber, tags.track;" ) + .arg( artist_id ) ); + QStringList result = QStringList(); + foreach( rs ) + { + const int id = (*it).toInt(); + result << MountPointManager::instance()->getAbsolutePath( id, *(++it) ); + } + return result; +} + + +void +CollectionDB::addImageToAlbum( const QString& image, QValueList< QPair > info, const bool temporary ) +{ + int deviceid = MountPointManager::instance()->getIdForUrl( image ); + QString rpath = MountPointManager::instance()->getRelativePath( deviceid, image ); + for ( QValueList< QPair >::ConstIterator it = info.begin(); it != info.end(); ++it ) + { + if ( (*it).first.isEmpty() || (*it).second.isEmpty() ) + continue; + + QString sql = QString( "INSERT INTO images%1 ( path, deviceid, artist, album ) VALUES ( '%3', %2" ) + .arg( temporary ? "_temp" : "" ) + .arg( deviceid ) + .arg( escapeString( rpath ) ); + sql += QString( ", '%1'" ).arg( escapeString( (*it).first ) ); + sql += QString( ", '%1' );" ).arg( escapeString( (*it).second ) ); + +// debug() << "Added image for album: " << (*it).first << " - " << (*it).second << ": " << image << endl; + insert( sql, NULL ); + } +} + +void +CollectionDB::addEmbeddedImage( const QString& path, const QString& hash, const QString& description ) +{ +// debug() << "Added embedded image hash " << hash << " for file " << path << endl; + //TODO: figure out what this embedded table does and then add the necessary code + //what are embedded images anyway? + int deviceid = MountPointManager::instance()->getIdForUrl( path ); + QString rpath = MountPointManager::instance()->getRelativePath(deviceid, path ); + insert( QString( "INSERT INTO embed_temp ( url, deviceid, hash, description ) VALUES ( '%2', %1, '%3', '%4' );" ) + .arg( deviceid ) + .arg( escapeString( rpath ), escapeString( hash ), escapeString( description ) ), NULL ); +} + +void +CollectionDB::removeOrphanedEmbeddedImages() +{ + //TODO refactor + // do it the hard way, since a delete subquery wont work on MySQL + QStringList orphaned = query( "SELECT embed.deviceid, embed.url FROM embed LEFT JOIN tags ON embed.url = tags.url AND embed.deviceid = tags.deviceid WHERE tags.url IS NULL;" ); + foreach( orphaned ) { + QString deviceid = *it; + QString rpath = *(++it); + query( QString( "DELETE FROM embed WHERE embed.deviceid = %1 AND embed.url = '%2';" ) + .arg( deviceid, escapeString( rpath ) ) ); + } +} + +QPixmap +CollectionDB::createDragPixmapFromSQL( const QString &sql, QString textOverRide ) +{ + // it is too slow to check if the url is actually in the colleciton. + //TODO mountpointmanager: figure out what has to be done here + QStringList values = instance()->query( sql ); + KURL::List list; + foreach( values ) + { + KURL u = KURL::fromPathOrURL( *it ); + if( u.isValid() ) + list += u; + } + return createDragPixmap( list, textOverRide ); +} + +QPixmap +CollectionDB::createDragPixmap( const KURL::List &urls, QString textOverRide ) +{ + // settings + const int maxCovers = 4; // maximum number of cover images to show + const int coverSpacing = 20; // spacing between stacked covers + const int fontSpacing = 5; // spacing between covers and info text + const int coverW = AmarokConfig::coverPreviewSize() > 100 ? 100 : AmarokConfig::coverPreviewSize(); + const int coverH = coverW; + const int margin = 2; //px margin + + int covers = 0; + int songs = 0; + int pixmapW = 0; + int pixmapH = 0; + int remoteUrls = 0; + int playlists = 0; + + QMap albumMap; + QPixmap coverPm[maxCovers]; + + QString song, album; + + + // iterate urls, get covers and count artist/albums + bool correctAlbumCount = true; + KURL::List::ConstIterator it = urls.begin(); + for ( ; it != urls.end(); ++it ) + { + if( PlaylistFile::isPlaylistFile( *it ) + || (*it).protocol() == "playlist" || (*it).protocol() == "smartplaylist" + || (*it).protocol() == "dynamic" ) + { + playlists++; + } + else if( (*it).isLocalFile() ) + { + songs++; + + if( covers >= maxCovers ) + { + correctAlbumCount = false; + continue; + } + + MetaBundle mb( *it ); + + song = mb.title(); + album = mb.album(); + + QString artist = mb.artist(); + if( mb.compilation() == MetaBundle::CompilationYes ) + artist = QString( "Various_AMAROK_Artists" ); // magic key for the albumMap! + + if( !albumMap.contains( artist + album ) ) + { + albumMap[ artist + album ] = 1; + QString coverName = CollectionDB::instance()->albumImage( mb.artist(), album, false, coverW ); + + if ( !coverName.endsWith( "@nocover.png" ) ) + coverPm[covers++].load( coverName ); + } + } + else + { + MetaBundle mb( *it ); + if( !albumMap.contains( mb.artist() + mb.album() ) ) + { + albumMap[ mb.artist() + mb.album() ] = 1; + QString coverName = CollectionDB::instance()->podcastImage( mb, false, coverW ); + + if ( covers < maxCovers && !coverName.endsWith( "@nocover.png" ) ) + coverPm[covers++].load( coverName ); + } + remoteUrls++; + } + } + + // make better text... + int albums = albumMap.count(); + QString text; + + if( !textOverRide.isEmpty() ) + { + text = textOverRide; + } + else if( ( songs && remoteUrls ) || + ( songs && playlists ) || + ( playlists && remoteUrls ) ) + { + text = i18n( "One item", "%n items", songs + remoteUrls + playlists ); + } + else if( songs > 0 ) + { + if( correctAlbumCount ) { + text = i18n( "X songs from X albums", "%2 from %1" ); + text = text.arg( albums == 1 && !album.isEmpty() ? album : i18n( "one album", "%n albums",albums ) ); + } + else + text = "%1"; + text = text.arg( songs == 1 && !song.isEmpty() ? song : i18n( "One song", "%n songs", songs ) ); + } + else if( playlists > 0 ) + text = i18n( "One playlist", "%n playlists", playlists ); + else if ( remoteUrls > 0 ) + text = i18n( "One remote file", "%n remote files", remoteUrls ); + else + text = i18n( "Unknown item" ); + + QFont font; + QFontMetrics fm( font ); + int fontH = fm.height() + margin; + int minWidth = fm.width( text ) + margin*2; //margin either side + + if ( covers > 0 ) + { + // insert "..." cover as first image if appropriate + if ( covers < albums ) + { + if ( covers < maxCovers ) covers++; + for ( int i = maxCovers-1; i > 0; i-- ) + coverPm[i] = coverPm[i-1]; + + QImage im( locate( "data","amarok/images/more_albums.png" ) ); + coverPm[0].convertFromImage( im.smoothScale( coverW, coverH, QImage::ScaleMin ) ); + } + + pixmapH = coverPm[0].height(); + pixmapW = coverPm[0].width(); + + // caluclate pixmap height + int dW, dH; + for ( int i = 1; i < covers; i++ ) + { + dW = coverPm[i].width() - coverPm[i-1].width() + coverSpacing; + dH = coverPm[i].height() - coverPm[i-1].height() + coverSpacing; + if ( dW > 0 ) pixmapW += dW; + if ( dH > 0 ) pixmapH += dH; + } + pixmapH += fontSpacing + fontH; + + if ( pixmapW < minWidth ) + pixmapW = minWidth; + } + else + { + pixmapW = minWidth; + pixmapH = fontH; + } + + QPixmap pmdrag( pixmapW, pixmapH ); + QPixmap pmtext( pixmapW, fontH ); + + QPalette palette = QToolTip::palette(); + + QPainter p; + p.begin( &pmtext ); + p.fillRect( 0, 0, pixmapW, fontH, QBrush( Qt::black ) ); // border + p.fillRect( 1, 1, pixmapW-margin, fontH-margin, palette.brush( QPalette::Normal, QColorGroup::Background ) ); + p.setBrush( palette.color( QPalette::Normal, QColorGroup::Text ) ); + p.setFont( font ); + p.drawText( margin, fm.ascent() + 1, text ); + p.end(); + + QBitmap pmtextMask(pixmapW, fontH); + pmtextMask.fill( Qt::color1 ); + + // when we have found no covers, just display the text message + if( !covers ) + { + pmtext.setMask(pmtextMask); + return pmtext; + } + + // compose image + p.begin( &pmdrag ); + p.setBackgroundMode( Qt::TransparentMode ); + for ( int i = 0; i < covers; i++ ) + bitBlt( &pmdrag, i * coverSpacing, i * coverSpacing, &coverPm[i], 0, Qt::CopyROP ); + + bitBlt( &pmdrag, 0, pixmapH - fontH, &pmtext, 0, Qt::CopyROP ); + p.end(); + + QBitmap pmdragMask( pmdrag.size(), true ); + for ( int i = 0; i < covers; i++ ) + { + QBitmap coverMask( coverPm[i].width(), coverPm[i].height() ); + coverMask.fill( Qt::color1 ); + bitBlt( &pmdragMask, i * coverSpacing, i * coverSpacing, &coverMask, 0, Qt::CopyROP ); + } + bitBlt( &pmdragMask, 0, pixmapH - fontH, &pmtextMask, 0, Qt::CopyROP ); + pmdrag.setMask( pmdragMask ); + + return pmdrag; +} + +QImage +CollectionDB::fetchImage( const KURL& url, QString &/*tmpFile*/ ) +{ + if ( url.protocol() != "file" ) + { + QString tmpFile; + KIO::NetAccess::download( url, tmpFile, 0 ); //TODO set 0 to the window, though it probably doesn't really matter + return QImage( tmpFile ); + } + else + { + return QImage( url.path() ); + } + +} +bool +CollectionDB::setAlbumImage( const QString& artist, const QString& album, const KURL& url ) +{ + QString tmpFile; + bool success = setAlbumImage( artist, album, fetchImage(url, tmpFile) ); + KIO::NetAccess::removeTempFile( tmpFile ); //only removes file if it was created with NetAccess + return success; +} + + +bool +CollectionDB::setAlbumImage( const QString& artist, const QString& album, QImage img, const QString& amazonUrl, const QString& asin ) +{ + //show a wait cursor for the duration + Amarok::OverrideCursor keep; + + const bool isCompilation = albumIsCompilation( QString::number( albumID( album, false, false, true ) ) ); + const QString artist_ = isCompilation ? "" : artist; + + // remove existing album covers + removeAlbumImage( artist_, album ); + + QCString key = md5sum( artist_, album ); + newAmazonReloadDate(asin, AmarokConfig::amazonLocale(), key); + // Save Amazon product page URL as embedded string, for later retreival + if ( !amazonUrl.isEmpty() ) + img.setText( "amazon-url", 0, amazonUrl ); + + const bool b = img.save( largeCoverDir().filePath( key ), "PNG"); + emit coverChanged( artist_, album ); + return b; +} + + +QString +CollectionDB::podcastImage( const MetaBundle &bundle, const bool withShadow, uint width ) +{ + PodcastEpisodeBundle peb; + PodcastChannelBundle pcb; + + KURL url = bundle.url().url(); + + if( getPodcastEpisodeBundle( url, &peb ) ) + { + url = peb.parent().url(); + } + + if( getPodcastChannelBundle( url, &pcb ) ) + { + if( pcb.imageURL().isValid() ) + return podcastImage( pcb.imageURL().url(), withShadow, width ); + } + + return notAvailCover( withShadow, width ); +} + + +QString +CollectionDB::podcastImage( const QString &remoteURL, const bool withShadow, uint width ) +{ + // we aren't going to need a 1x1 size image. this is just a quick hack to be able to show full size images. + // width of 0 == full size + if( width == 1 ) + width = AmarokConfig::coverPreviewSize(); + + QString s = findAmazonImage( "Podcast", remoteURL, width ); + + if( s.isEmpty() ) + { + s = notAvailCover( withShadow, width ); + + const KURL url = KURL::fromPathOrURL( remoteURL ); + if( url.isValid() ) //KIO crashes with invalid URLs + { + KIO::Job *job = KIO::storedGet( url, false, false ); + m_podcastImageJobs[job] = remoteURL; + connect( job, SIGNAL( result( KIO::Job* ) ), SLOT( podcastImageResult( KIO::Job* ) ) ); + } + } + + if ( withShadow ) + s = makeShadowedImage( s ); + + return s; +} + +void +CollectionDB::podcastImageResult( KIO::Job *gjob ) +{ + QString url = m_podcastImageJobs[gjob]; + m_podcastImageJobs.remove( gjob ); + + KIO::StoredTransferJob *job = dynamic_cast( gjob ); + if( !job ) + { + debug() << "connected to wrong job type" << endl; + return; + } + + if( job->error() ) + { + debug() << "job finished with error" << endl; + return; + } + + if( job->isErrorPage() ) + { + debug() << "error page" << endl; + return; + } + + QImage image( job->data() ); + if( !image.isNull() ) + { + if( url.isEmpty() ) + url = job->url().url(); + + QCString key = md5sum( "Podcast", url ); + if( image.save( largeCoverDir().filePath( key ), "PNG") ) + emit imageFetched( url ); + } +} + + +QString +CollectionDB::albumImage( const QString &artist, const QString &album, bool withShadow, uint width, bool* embedded ) +{ + QString s; + // we aren't going to need a 1x1 size image. this is just a quick hack to be able to show full size images. + // width of 0 == full size + if( width == 1 ) + width = AmarokConfig::coverPreviewSize(); + if( embedded ) + *embedded = false; + + s = findAmazonImage( artist, album, width ); + + if( s.isEmpty() ) + s = findAmazonImage( "", album, width ); // handle compilations + + if( s.isEmpty() ) + s = findDirectoryImage( artist, album, width ); + + if( s.isEmpty() ) + { + s = findEmbeddedImage( artist, album, width ); + if( embedded && !s.isEmpty() ) + *embedded = true; + } + + if( s.isEmpty() ) + s = notAvailCover( withShadow, width ); + + if ( withShadow ) + s = makeShadowedImage( s ); + + return s; +} + + +QString +CollectionDB::albumImage( const uint artist_id, const uint album_id, bool withShadow, uint width, bool* embedded ) +{ + return albumImage( artistValue( artist_id ), albumValue( album_id ), withShadow, width, embedded ); +} + + +QString +CollectionDB::albumImage( const MetaBundle &trackInformation, bool withShadow, uint width, bool* embedded ) +{ + QString s; + if( width == 1 ) + width = AmarokConfig::coverPreviewSize(); + + QString album = trackInformation.album(); + QString artist = trackInformation.artist(); + + // this art is per track, so should check for it first + s = findMetaBundleImage( trackInformation, width ); + if( embedded ) + *embedded = !s.isEmpty(); + + if( s.isEmpty() ) + s = findAmazonImage( artist, album, width ); + if( s.isEmpty() ) + s = findAmazonImage( "", album, width ); // handle compilations + if( s.isEmpty() ) + s = findDirectoryImage( artist, album, width ); + if( s.isEmpty() ) + s = notAvailCover( withShadow, width ); + if ( withShadow ) + s = makeShadowedImage( s ); + return s; +} + +QString +CollectionDB::makeShadowedImage( const QString& albumImage, bool cache ) +{ + qApp->lock(); + const QImage original( albumImage, "PNG" ); + qApp->unlock(); + + if( original.hasAlphaBuffer() ) + return albumImage; + + const QFileInfo fileInfo( albumImage ); + const uint shadowSize = static_cast( original.width() / 100.0 * 6.0 ); + const QString cacheFile = fileInfo.fileName() + "@shadow"; + + if ( !cache && cacheCoverDir().exists( cacheFile ) ) + return cacheCoverDir().filePath( cacheFile ); + + QImage shadow; + + const QString folder = Amarok::saveLocation( "covershadow-cache/" ); + const QString file = QString( "shadow_albumcover%1x%2.png" ).arg( original.width() + shadowSize ).arg( original.height() + shadowSize ); + if ( QFile::exists( folder + file ) ) { + qApp->lock(); + shadow.load( folder + file, "PNG" ); + qApp->unlock(); + } + else { + shadow = QDeepCopy(instance()->m_shadowImage); + shadow = shadow.smoothScale( original.width() + shadowSize, original.height() + shadowSize ); + shadow.save( folder + file, "PNG" ); + } + + QImage target(shadow); + bitBlt( &target, 0, 0, &original ); + + if ( cache ) { + target.save( cacheCoverDir().filePath( cacheFile ), "PNG" ); + return cacheCoverDir().filePath( cacheFile ); + } + + target.save( albumImage, "PNG" ); + return albumImage; +} + + +// Amazon Image +QString +CollectionDB::findAmazonImage( const QString &artist, const QString &album, uint width ) +{ + QCString widthKey = makeWidthKey( width ); + + if ( artist.isEmpty() && album.isEmpty() ) + return QString(); + + QCString key = md5sum( artist, album ); + + // check cache for existing cover + if ( cacheCoverDir().exists( widthKey + key ) ) + return cacheCoverDir().filePath( widthKey + key ); + + // we need to create a scaled version of this cover + QDir imageDir = largeCoverDir(); + if ( imageDir.exists( key ) ) + { + if ( width > 1 ) + { + QImage img( imageDir.filePath( key ) ); + img.smoothScale( width, width, QImage::ScaleMin ).save( cacheCoverDir().filePath( widthKey + key ), "PNG" ); + + return cacheCoverDir().filePath( widthKey + key ); + } + else + return imageDir.filePath( key ); + } + + return QString(); +} + + +QString +CollectionDB::findDirectoryImage( const QString& artist, const QString& album, uint width ) +{ + if ( width == 1 ) + width = AmarokConfig::coverPreviewSize(); + QCString widthKey = makeWidthKey( width ); + if ( album.isEmpty() ) + return QString(); + + IdList list = MountPointManager::instance()->getMountedDeviceIds(); + QString deviceIds; + foreachType( IdList, list ) + { + if ( !deviceIds.isEmpty() ) deviceIds += ','; + deviceIds += QString::number(*it); + } + + QStringList rs; + if ( artist == i18n( "Various Artists" ) || artist.isEmpty() ) + { + rs = query( QString( + "SELECT distinct images.deviceid,images.path FROM images, artist, tags " + "WHERE images.artist = artist.name " + "AND artist.id = tags.artist " + "AND tags.sampler = %1 " + "AND images.album %2 " + "AND images.deviceid IN (%3) " ) + .arg( boolT() ) + .arg( CollectionDB::likeCondition( album ) ) + .arg( deviceIds ) ); + } + else + { + rs = query( QString( + "SELECT distinct images.deviceid,images.path FROM images WHERE artist %1 AND album %2 AND deviceid IN (%3) ORDER BY path;" ) + .arg( CollectionDB::likeCondition( artist ) ) + .arg( CollectionDB::likeCondition( album ) ) + .arg( deviceIds ) ); + } + QStringList values = URLsFromQuery( rs ); + if ( !values.isEmpty() ) + { + QString image( values.first() ); + uint matches = 0; + uint maxmatches = 0; + QRegExp iTunesArt( "^AlbumArt_.*Large" ); + for ( uint i = 0; i < values.count(); i++ ) + { + matches = values[i].contains( "front", false ) + values[i].contains( "cover", false ) + values[i].contains( "folder", false ) + values[i].contains( iTunesArt ); + if ( matches > maxmatches ) + { + image = values[i]; + maxmatches = matches; + } + } + + QCString key = md5sum( artist, album, image ); + + if ( width > 1 ) + { + QString path = cacheCoverDir().filePath( widthKey + key ); + if ( !QFile::exists( path ) ) + { + QImage img( image ); + img.smoothScale( width, width, QImage::ScaleMin ).save( path, "PNG" ); + } + return path; + } + else //large image + return image; + } + return QString(); +} + + +QString +CollectionDB::findEmbeddedImage( const QString& artist, const QString& album, uint width ) +{ + // In the case of multiple embedded images, we arbitrarily choose one from the newest file + // could potentially select multiple images within a file based on description, although a + // lot of tagging software doesn't fill in that field, so we just get whatever the DB + // happens to return for us + QStringList rs; + if ( artist == i18n("Various Artists") || artist.isEmpty() ) { + // VAs need special handling to not match on artist name but instead check for sampler flag + rs = query( QString( + "SELECT embed.hash, embed.deviceid, embed.url FROM " + "tags INNER JOIN embed ON tags.url = embed.url " + "INNER JOIN album ON tags.album = album.id " + "WHERE " + "album.name = '%1' " + "AND tags.sampler = %2 " + "ORDER BY modifydate DESC LIMIT 1;" ) + .arg( escapeString( album ) ) + .arg( boolT() ) ); + } else { + rs = query( QString( + "SELECT embed.hash, embed.deviceid, embed.url FROM " + "tags INNER JOIN embed ON tags.url = embed.url " + "INNER JOIN artist ON tags.artist = artist.id " + "INNER JOIN album ON tags.album = album.id " + "WHERE " + "artist.name = '%1' " + "AND album.name = '%2' " + "ORDER BY modifydate DESC LIMIT 1;" ) + .arg( escapeString( artist ) ) + .arg( escapeString( album ) ) ); + } + + QStringList values = QStringList(); + if ( rs.count() == 3 ) { + values += rs.first(); + values += MountPointManager::instance()->getAbsolutePath( rs[1].toInt(), rs[2] ); + } + + if ( values.count() == 2 ) { + QCString hash = values.first().utf8(); + QString result = loadHashFile( hash, width ); + if ( result.isEmpty() ) { + // need to get original from file first + MetaBundle mb( KURL::fromPathOrURL( values.last() ) ); + if ( extractEmbeddedImage( mb, hash ) ) { + // try again, as should be possible now + result = loadHashFile( hash, width ); + } + } + return result; + } + return QString(); +} + + +QString +CollectionDB::findMetaBundleImage( const MetaBundle& trackInformation, uint width ) +{ + int deviceid = MountPointManager::instance()->getIdForUrl( trackInformation.url() ); + QString rpath = MountPointManager::instance()->getRelativePath( deviceid, trackInformation.url().path() ); + QStringList values = + query( QString( + "SELECT embed.hash FROM tags LEFT JOIN embed ON tags.url = embed.url " + " AND tags.deviceid = embed.deviceid WHERE tags.url = '%2' AND tags.deviceid = %1 ORDER BY hash DESC LIMIT 1;" ) + .arg( deviceid ).arg( escapeString( rpath ) ) ); + + if ( values.empty() || !values.first().isEmpty() ) { + QCString hash; + QString result; + if( !values.empty() ) { // file in collection, so we know the hash + hash = values.first().utf8(); + result = loadHashFile( hash, width ); + } + if ( result.isEmpty() ) { + // need to get original from file first + if ( extractEmbeddedImage( trackInformation, hash ) ) { + // try again, as should be possible now + result = loadHashFile( hash, width ); + } + } + return result; + } + return QString(); +} + + +QCString +CollectionDB::makeWidthKey( uint width ) +{ + return QString::number( width ).local8Bit() + '@'; +} + + +bool +CollectionDB::removeAlbumImage( const QString &artist, const QString &album ) +{ + DEBUG_BLOCK + + QCString widthKey = "*@"; + QCString key = md5sum( artist, album ); + query( "DELETE FROM amazon WHERE filename='" + key + '\'' ); + + // remove scaled versions of images (and add the asterisk for the shadow-caches) + QStringList scaledList = cacheCoverDir().entryList( widthKey + key + '*' ); + if ( scaledList.count() > 0 ) + for ( uint i = 0; i < scaledList.count(); i++ ) + QFile::remove( cacheCoverDir().filePath( scaledList[ i ] ) ); + + bool deleted = false; + // remove large, original image + if ( largeCoverDir().exists( key ) && QFile::remove( largeCoverDir().filePath( key ) ) ) + deleted = true; + + QString hardImage = findDirectoryImage( artist, album ); + debug() << "hardImage: " << hardImage << endl; + + if( !hardImage.isEmpty() ) + { + int id = MountPointManager::instance()->getIdForUrl( hardImage ); + QString rpath = MountPointManager::instance()->getRelativePath( id, hardImage ); + query( "DELETE FROM images WHERE path='" + escapeString( hardImage ) + "' AND deviceid = " + QString::number( id ) + ';' ); + deleted = true; + } + + if ( deleted ) + { + emit coverRemoved( artist, album ); + return true; + } + + return false; +} + + +bool +CollectionDB::removeAlbumImage( const uint artist_id, const uint album_id ) +{ + return removeAlbumImage( artistValue( artist_id ), albumValue( album_id ) ); +} + + +QString +CollectionDB::notAvailCover( const bool withShadow, int width ) +{ + if ( width <= 1 ) + width = AmarokConfig::coverPreviewSize(); + QString widthKey = QString::number( width ) + '@'; + QString s; + + if( cacheCoverDir().exists( widthKey + "nocover.png" ) ) + s = cacheCoverDir().filePath( widthKey + "nocover.png" ); + else + { + m_noCover.smoothScale( width, width, QImage::ScaleMin ).save( cacheCoverDir().filePath( widthKey + "nocover.png" ), "PNG" ); + s = cacheCoverDir().filePath( widthKey + "nocover.png" ); + } + + if ( withShadow ) + s = makeShadowedImage( s ); + + return s; +} + + +QStringList +CollectionDB::artistList( bool withUnknowns, bool withCompilations ) +{ + QueryBuilder qb; + qb.addReturnValue( QueryBuilder::tabArtist, QueryBuilder::valName ); + + if ( !withUnknowns ) + qb.excludeMatch( QueryBuilder::tabArtist, i18n( "Unknown" ) ); + if ( !withCompilations ) + qb.setOptions( QueryBuilder::optNoCompilations ); + + qb.groupBy( QueryBuilder::tabArtist, QueryBuilder::valName ); + qb.setOptions( QueryBuilder::optShowAll ); + qb.sortBy( QueryBuilder::tabArtist, QueryBuilder::valName ); + return qb.run(); +} + + +QStringList +CollectionDB::composerList( bool withUnknowns, bool withCompilations ) +{ + DEBUG_BLOCK + QueryBuilder qb; + qb.addReturnValue( QueryBuilder::tabComposer, QueryBuilder::valName ); + + if ( !withUnknowns ) + qb.excludeMatch( QueryBuilder::tabComposer, i18n( "Unknown" ) ); + if ( !withCompilations ) + qb.setOptions( QueryBuilder::optNoCompilations ); + + qb.groupBy( QueryBuilder::tabComposer, QueryBuilder::valName ); + qb.setOptions( QueryBuilder::optShowAll ); + qb.sortBy( QueryBuilder::tabComposer, QueryBuilder::valName ); + return qb.run(); +} + + +QStringList +CollectionDB::albumList( bool withUnknowns, bool withCompilations ) +{ + QueryBuilder qb; + qb.addReturnValue( QueryBuilder::tabAlbum, QueryBuilder::valName ); + + if ( !withUnknowns ) + qb.excludeMatch( QueryBuilder::tabAlbum, i18n( "Unknown" ) ); + if ( !withCompilations ) + qb.setOptions( QueryBuilder::optNoCompilations ); + + qb.groupBy( QueryBuilder::tabAlbum, QueryBuilder::valName ); + qb.setOptions( QueryBuilder::optShowAll ); + qb.sortBy( QueryBuilder::tabAlbum, QueryBuilder::valName ); + return qb.run(); +} + + +QStringList +CollectionDB::genreList( bool withUnknowns, bool withCompilations ) +{ + QueryBuilder qb; + qb.addReturnValue( QueryBuilder::tabGenre, QueryBuilder::valName ); + + //Only report genres that currently have at least one song + qb.addFilter( QueryBuilder::tabSong, "" ); + + if ( !withUnknowns ) + qb.excludeMatch( QueryBuilder::tabGenre, i18n( "Unknown" ) ); + if ( !withCompilations ) + qb.setOptions( QueryBuilder::optNoCompilations ); + + qb.groupBy( QueryBuilder::tabGenre, QueryBuilder::valName ); + qb.setOptions( QueryBuilder::optShowAll ); + qb.sortBy( QueryBuilder::tabGenre, QueryBuilder::valName ); + return qb.run(); +} + + +QStringList +CollectionDB::yearList( bool withUnknowns, bool withCompilations ) +{ + QueryBuilder qb; + qb.addReturnValue( QueryBuilder::tabYear, QueryBuilder::valName ); + + if ( !withUnknowns ) + qb.excludeMatch( QueryBuilder::tabYear, i18n( "Unknown" ) ); + if ( !withCompilations ) + qb.setOptions( QueryBuilder::optNoCompilations ); + + qb.groupBy( QueryBuilder::tabYear, QueryBuilder::valName ); + qb.setOptions( QueryBuilder::optShowAll ); + qb.sortBy( QueryBuilder::tabYear, QueryBuilder::valName ); + return qb.run(); +} + +QStringList +CollectionDB::labelList() +{ + QueryBuilder qb; + qb.addReturnValue( QueryBuilder::tabLabels, QueryBuilder::valName ); + qb.groupBy( QueryBuilder::tabLabels, QueryBuilder::valName ); + qb.setOptions( QueryBuilder::optShowAll ); + qb.sortBy( QueryBuilder::tabLabels, QueryBuilder::valName ); + return qb.run(); +} + +QStringList +CollectionDB::albumListOfArtist( const QString &artist, bool withUnknown, bool withCompilations ) +{ + if (getDbConnectionType() == DbConnection::postgresql) + { + return query( "SELECT DISTINCT album.name, lower( album.name ) AS __discard FROM tags, album, artist WHERE " + "tags.album = album.id AND tags.artist = artist.id " + "AND lower(artist.name) = lower('" + escapeString( artist ) + "') " + + ( withUnknown ? QString::null : "AND album.name <> '' " ) + + ( withCompilations ? QString::null : "AND tags.sampler = " + boolF() ) + deviceidSelection() + + " ORDER BY lower( album.name );" ); + } + // mysql is case insensitive and lower() is very slow + else if (getDbConnectionType() == DbConnection::mysql) + { + return query( "SELECT DISTINCT album.name FROM tags, album, artist WHERE " + "tags.album = album.id AND tags.artist = artist.id " + "AND artist.name = '" + escapeString( artist ) + "' " + + ( withUnknown ? QString::null : "AND album.name <> '' " ) + + ( withCompilations ? QString::null : "AND tags.sampler = " + boolF() ) + deviceidSelection() + + " ORDER BY album.name;" ); + } + else // sqlite + { + return query( "SELECT DISTINCT album.name FROM tags, album, artist WHERE " + "tags.album = album.id AND tags.artist = artist.id " + "AND lower(artist.name) = lower('" + escapeString( artist ) + "') " + + ( withUnknown ? QString::null : "AND album.name <> '' " ) + + ( withCompilations ? QString::null : "AND tags.sampler = " + boolF() ) + deviceidSelection() + + " ORDER BY lower( album.name );" ); + } +} + + +QStringList +CollectionDB::artistAlbumList( bool withUnknown, bool withCompilations ) +{ + if (getDbConnectionType() == DbConnection::postgresql) + { + return query( "SELECT DISTINCT artist.name, album.name, lower( album.name ) AS __discard FROM tags, album, artist WHERE " + "tags.album = album.id AND tags.artist = artist.id " + + ( withUnknown ? QString::null : "AND album.name <> '' AND artist.name <> '' " ) + + ( withCompilations ? QString::null : "AND tags.sampler = " + boolF() ) + deviceidSelection() + + " ORDER BY lower( album.name );" ); + } + else + { + return query( "SELECT DISTINCT artist.name, album.name FROM tags, album, artist WHERE " + "tags.album = album.id AND tags.artist = artist.id " + + ( withUnknown ? QString::null : "AND album.name <> '' AND artist.name <> '' " ) + + ( withCompilations ? QString::null : "AND tags.sampler = " + boolF() ) + deviceidSelection() + + " ORDER BY lower( album.name );" ); + } +} + +bool +CollectionDB::addPodcastChannel( const PodcastChannelBundle &pcb, const bool &replace ) +{ + QString command; + if( replace ) { + command = "REPLACE INTO podcastchannels " + "( url, title, weblink, image, comment, copyright, parent, directory" + ", autoscan, fetchtype, autotransfer, haspurge, purgecount ) " + "VALUES ("; + } else { + command = "INSERT INTO podcastchannels " + "( url, title, weblink, image, comment, copyright, parent, directory" + ", autoscan, fetchtype, autotransfer, haspurge, purgecount ) " + "VALUES ("; + } + + QString title = pcb.title(); + KURL link = pcb.link(); + KURL image = pcb.imageURL(); + QString description = pcb.description(); + QString copyright = pcb.copyright(); + + if( title.isEmpty() ) + title = pcb.url().prettyURL(); + + command += '\'' + escapeString( pcb.url().url() ) + "',"; + command += ( title.isEmpty() ? "NULL" : '\'' + escapeString( title ) + '\'' ) + ','; + command += ( link.isEmpty() ? "NULL" : '\'' + escapeString( link.url() ) + '\'' ) + ','; + command += ( image.isEmpty() ? "NULL" : '\'' + escapeString( image.url() ) + '\'' ) + ','; + command += ( description.isEmpty() ? "NULL" : '\'' + escapeString( description ) + '\'' ) + ','; + command += ( copyright.isEmpty() ? "NULL" : '\'' + escapeString( copyright ) + '\'' ) + ','; + command += QString::number( pcb.parentId() ) + ",'"; + command += escapeString( pcb.saveLocation() ) + "',"; + command += pcb.autoscan() ? boolT() + ',' : boolF() + ','; + command += QString::number( pcb.fetchType() ) + ','; + command += pcb.autotransfer() ? boolT() + ',' : boolF() + ','; + command += pcb.hasPurge() ? boolT() + ',' : boolF() + ','; + command += QString::number( pcb.purgeCount() ) + ");"; + + //FIXME: currently there's no way to check if an INSERT query failed or not - always return true atm. + // Now it might be possible as insert returns the rowid. + insert( command, NULL ); + return true; +} + +int +CollectionDB::addPodcastEpisode( const PodcastEpisodeBundle &episode, const int idToUpdate ) +{ + QString command; + + if( idToUpdate ) { + command = "REPLACE INTO podcastepisodes " + "( id, url, localurl, parent, title, subtitle, composer, comment, filetype, createdate, guid, length, size, isNew ) " + "VALUES ("; + } else { + command = "INSERT INTO podcastepisodes " + "( url, localurl, parent, title, subtitle, composer, comment, filetype, createdate, guid, length, size, isNew ) " + "VALUES ("; + } + + QString localurl = episode.localUrl().url(); + QString title = episode.title(); + QString subtitle = episode.subtitle(); + QString author = episode.author(); + QString description = episode.description(); + QString type = episode.type(); + QString date = episode.date(); + QString guid = episode.guid(); + int duration = episode.duration(); + uint size = episode.size(); + + if( title.isEmpty() ) + title = episode.url().prettyURL(); + + if( idToUpdate ) + command += QString::number( idToUpdate ) + ','; + + command += '\'' + escapeString( episode.url().url() ) + "',"; + command += ( localurl.isEmpty() ? "NULL" : '\'' + escapeString( localurl ) + '\'' ) + ','; + command += '\'' + escapeString( episode.parent().url()) + "',"; + command += ( title.isEmpty() ? "NULL" : '\'' + escapeString( title ) + '\'' ) + ','; + command += ( subtitle.isEmpty() ? "NULL" : '\'' + escapeString( subtitle ) + '\'' ) + ','; + command += ( author.isEmpty() ? "NULL" : '\'' + escapeString( author ) + '\'' ) + ','; + command += ( description.isEmpty() ? "NULL" : '\'' + escapeString( description ) + '\'' ) + ','; + command += ( type.isEmpty() ? "NULL" : '\'' + escapeString( type ) + '\'' ) + ','; + command += ( date.isEmpty() ? "NULL" : '\'' + escapeString( date ) + '\'' ) + ','; + command += ( guid.isEmpty() ? "NULL" : '\'' + escapeString( guid ) + '\'' ) + ','; + command += QString::number( duration ) + ','; + command += QString::number( size ) + ','; + command += episode.isNew() ? boolT() + " );" : boolF() + " );"; + + insert( command, NULL ); + + if( idToUpdate ) return idToUpdate; + //This is a bit of a hack. We have just inserted an item, so it is going to be the one with the + //highest id. Change this if threaded insertions are used in the future. + QStringList values = query( QString("SELECT id FROM podcastepisodes WHERE url='%1' ORDER BY id DESC;") + .arg( escapeString( episode.url().url() ) ) ); + if( values.isEmpty() ) return -1; + + return values[0].toInt(); +} + +QValueList +CollectionDB::getPodcastChannels() +{ + QString command = "SELECT url, title, weblink, image, comment, copyright, parent, directory " + ", autoscan, fetchtype, autotransfer, haspurge, purgecount FROM podcastchannels;"; + + QStringList values = query( command ); + QValueList bundles; + + foreach( values ) + { + PodcastChannelBundle pcb; + pcb.setURL ( KURL::fromPathOrURL(*it) ); + pcb.setTitle ( *++it ); + pcb.setLink ( KURL::fromPathOrURL(*++it) ); + pcb.setImageURL ( KURL::fromPathOrURL(*++it) ); + pcb.setDescription ( *++it ); + pcb.setCopyright ( *++it ); + pcb.setParentId ( (*++it).toInt() ); + pcb.setSaveLocation( *++it ); + pcb.setAutoScan ( boolFromSql( *++it ) ); + pcb.setFetchType ( (*++it).toInt() ); + pcb.setAutoTransfer( boolFromSql( *++it ) ); + pcb.setPurge ( boolFromSql( *++it ) ); + pcb.setPurgeCount ( (*++it).toInt() ); + + bundles.append( pcb ); + } + + return bundles; +} + +QValueList +CollectionDB::getPodcastEpisodes( const KURL &parent, bool onlyNew, int limit ) +{ + QString command = QString( "SELECT id, url, localurl, parent, guid, title, subtitle, composer, comment, filetype, createdate, length, size, isNew FROM podcastepisodes WHERE ( parent='%1'" ).arg( parent.url() ); + if( onlyNew ) + command += QString( " AND isNew='%1'" ).arg( boolT() ); + command += " ) ORDER BY id"; + if( limit != -1 ) + command += QString( " DESC LIMIT %1 OFFSET 0" ).arg( limit ); + command += ';'; + + QStringList values = query( command ); + QValueList bundles; + + foreach( values ) + { + PodcastEpisodeBundle peb; + peb.setDBId ( (*it).toInt() ); + peb.setURL ( KURL::fromPathOrURL(*++it) ); + if( *++it != "NULL" ) + peb.setLocalURL ( KURL::fromPathOrURL(*it) ); + peb.setParent ( KURL::fromPathOrURL(*++it) ); + peb.setGuid ( *++it ); + peb.setTitle ( *++it ); + if( *++it != NULL ) + peb.setSubtitle( *it ); + peb.setAuthor ( *++it ); + peb.setDescription ( *++it ); + peb.setType ( *++it ); + peb.setDate ( *++it ); + peb.setDuration ( (*++it).toInt() ); + if( *++it == NULL ) + peb.setSize ( 0 ); + else + peb.setSize ( (*it).toInt() ); + peb.setNew ( boolFromSql( *++it ) ); + + bundles.append( peb ); + } + + return bundles; +} + +PodcastEpisodeBundle +CollectionDB::getPodcastEpisodeById( int id ) +{ + QString command = QString( "SELECT url, localurl, parent, guid, title, subtitle, composer, comment, filetype, createdate, length, size, isNew FROM podcastepisodes WHERE id=%1;").arg( id ); + + QStringList values = query( command ); + PodcastEpisodeBundle peb; + foreach( values ) + { + peb.setDBId ( id ); + peb.setURL ( KURL::fromPathOrURL(*it) ); + if( *++it != "NULL" ) + peb.setLocalURL( KURL::fromPathOrURL(*it) ); + peb.setParent ( KURL::fromPathOrURL(*++it) ); + peb.setGuid ( *++it ); + peb.setTitle ( *++it ); + peb.setSubtitle ( *++it ); + peb.setAuthor ( *++it ); + peb.setDescription ( *++it ); + peb.setType ( *++it ); + peb.setDate ( *++it ); + peb.setDuration ( (*++it).toInt() ); + if( *++it == NULL ) + peb.setSize ( 0 ); + else + peb.setSize ( (*it).toInt() ); + peb.setNew ( boolFromSql( *++it ) ); + } + + return peb; +} + +bool +CollectionDB::getPodcastEpisodeBundle( const KURL &url, PodcastEpisodeBundle *peb ) +{ + int id = 0; + if( url.isLocalFile() ) + { + QStringList values = + query( QString( "SELECT id FROM podcastepisodes WHERE localurl = '%1';" ) + .arg( escapeString( url.url() ) ) ); + if( !values.isEmpty() ) + id = values[0].toInt(); + } + else + { + QStringList values = + query( QString( "SELECT id FROM podcastepisodes WHERE url = '%1';" ) + .arg( escapeString( url.url() ) ) ); + if( !values.isEmpty() ) + id = values[0].toInt(); + } + + if( id ) + { + *peb = getPodcastEpisodeById( id ); + return true; + } + + return false; +} + +bool +CollectionDB::getPodcastChannelBundle( const KURL &url, PodcastChannelBundle *pcb ) +{ + QStringList values = query( QString( + "SELECT url, title, weblink, image, comment, copyright, parent, directory " + ", autoscan, fetchtype, autotransfer, haspurge, purgecount FROM podcastchannels WHERE url = '%1';" + ).arg( escapeString( url.url() ) ) ); + + foreach( values ) + { + pcb->setURL ( KURL::fromPathOrURL(*it) ); + pcb->setTitle ( *++it ); + pcb->setLink ( KURL::fromPathOrURL(*++it) ); + if( *++it != "NULL" ) + pcb->setImageURL( KURL::fromPathOrURL(*it) ); + pcb->setDescription ( *++it ); + pcb->setCopyright ( *++it ); + pcb->setParentId ( (*++it).toInt() ); + pcb->setSaveLocation( *++it ); + pcb->setAutoScan ( boolFromSql( *++it ) ); + pcb->setFetchType ( (*++it).toInt() ); + pcb->setAutoTransfer( boolFromSql( *++it ) ); + pcb->setPurge ( boolFromSql( *++it ) ); + pcb->setPurgeCount ( (*++it).toInt() ); + } + + return !values.isEmpty(); +} + +// return newly created folder id +int +CollectionDB::addPodcastFolder( const QString &name, const int parent_id, const bool isOpen ) +{ + QString command = QString( "INSERT INTO podcastfolders ( name, parent, isOpen ) VALUES ('" ); + command += escapeString( name ) + "',"; + command += QString::number( parent_id ) + ","; + command += isOpen ? boolT() + ");" : boolF() + ");"; + + insert( command, NULL ); + + command = QString( "SELECT id FROM podcastfolders WHERE name = '%1' AND parent = '%2';" ) + .arg( name, QString::number(parent_id) ); + QStringList values = query( command ); + + return values[0].toInt(); +} + +void +CollectionDB::updatePodcastChannel( const PodcastChannelBundle &b ) +{ + if( getDbConnectionType() == DbConnection::postgresql ) + { + query( QStringx( "UPDATE podcastchannels SET title='%1', weblink='%2', comment='%3', " + "copyright='%4', parent=%5, directory='%6', autoscan=%7, fetchtype=%8, " + "autotransfer=%9, haspurge=%10, purgecount=%11 WHERE url='%12';" ) + .args ( QStringList() + << escapeString( b.title() ) + << escapeString( b.link().url() ) + << escapeString( b.description() ) + << escapeString( b.copyright() ) + << QString::number( b.parentId() ) + << escapeString( b.saveLocation() ) + << ( b.autoscan() ? boolT() : boolF() ) + << QString::number( b.fetchType() ) + << (b.hasPurge() ? boolT() : boolF() ) + << (b.autotransfer() ? boolT() : boolF() ) + << QString::number( b.purgeCount() ) + << escapeString( b.url().url() ) + ) + ); + } + else { + addPodcastChannel( b, true ); //replace the already existing row + } +} + +void +CollectionDB::updatePodcastEpisode( const int id, const PodcastEpisodeBundle &b ) +{ + if( getDbConnectionType() == DbConnection::postgresql ) + { + query( QStringx( "UPDATE podcastepisodes SET url='%1', localurl='%2', parent='%3', title='%4', subtitle='%5', composer='%6', comment='%7', " + "filetype='%8', createdate='%9', guid='%10', length=%11, size=%12, isNew=%13 WHERE id=%14;" ) + .args( QStringList() + << escapeString( b.url().url() ) + << ( b.localUrl().isValid() ? escapeString( b.localUrl().url() ) : "NULL" ) + << escapeString( b.parent().url() ) + << escapeString( b.title() ) + << escapeString( b.subtitle() ) + << escapeString( b.author() ) + << escapeString( b.description() ) + << escapeString( b.type() ) + << escapeString( b.date() ) + << escapeString( b.guid() ) + << QString::number( b.duration() ) + << escapeString( QString::number( b.size() ) ) + << ( b.isNew() ? boolT() : boolF() ) + << QString::number( id ) + ) + ); + } + else { + addPodcastEpisode( b, id ); + } +} + +void +CollectionDB::updatePodcastFolder( const int folder_id, const QString &name, const int parent_id, const bool isOpen ) +{ + if( getDbConnectionType() == DbConnection::postgresql ) { + query( QStringx( "UPDATE podcastfolders SET name='%1', parent=%2, isOpen=%3 WHERE id=%4;" ) + .args( QStringList() + << escapeString(name) + << QString::number(parent_id) + << ( isOpen ? boolT() : boolF() ) + << QString::number(folder_id) + ) + ); + } + else { + query( QStringx( "REPLACE INTO podcastfolders ( id, name, parent, isOpen ) " + "VALUES ( %1, '%2', %3, %4 );" ) + .args( QStringList() + << QString::number(folder_id) + << escapeString(name) + << QString::number(parent_id) + << ( isOpen ? boolT() : boolF() ) + ) + ); + } +} + +void +CollectionDB::removePodcastChannel( const KURL &url ) +{ + //remove channel + query( QString( "DELETE FROM podcastchannels WHERE url = '%1';" ) + .arg( escapeString( url.url() ) ) ); + //remove all children + query( QString( "DELETE FROM podcastepisodes WHERE parent = '%1';" ) + .arg( escapeString( url.url() ) ) ); +} + + +/// Try not to delete by url, since some podcast feeds have all the same url +void +CollectionDB::removePodcastEpisode( const int id ) +{ + if( id < 0 ) return; + query( QString( "DELETE FROM podcastepisodes WHERE id = '%1';" ) + .arg( QString::number(id) ) ); +} + +void +CollectionDB::removePodcastFolder( const int id ) +{ + if( id < 0 ) return; + query( QString("DELETE FROM podcastfolders WHERE id=%1;") + .arg( QString::number(id) ) ); +} + +bool +CollectionDB::addSong( MetaBundle* bundle, const bool incremental ) +{ + if ( !QFileInfo( bundle->url().path() ).isReadable() ) return false; + + QString command = "INSERT INTO tags_temp " + "( url, dir, deviceid, createdate, modifydate, album, artist, composer, genre, year, title, " + "comment, track, discnumber, bpm, sampler, length, bitrate, " + "samplerate, filesize, filetype ) " + "VALUES ('"; + + QString artist = bundle->artist(); + QString title = bundle->title(); + if ( title.isEmpty() ) + { + title = bundle->url().fileName(); + if ( bundle->url().fileName().find( '-' ) > 0 ) + { + if ( artist.isEmpty() ) + { + artist = bundle->url().fileName().section( '-', 0, 0 ).stripWhiteSpace(); + bundle->setArtist( artist ); + } + title = bundle->url().fileName().section( '-', 1 ).stripWhiteSpace(); + title = title.left( title.findRev( '.' ) ).stripWhiteSpace(); + if ( title.isEmpty() ) title = bundle->url().fileName(); + } + bundle->setTitle( title ); + } + + int deviceId = MountPointManager::instance()->getIdForUrl( bundle->url() ); + KURL relativePath; + MountPointManager::instance()->getRelativePath( deviceId, bundle->url(), relativePath ); + //debug() << "File has deviceId " << deviceId << ", relative path " << relativePath.path() << ", absolute path " << bundle->url().path() << endl; + + command += escapeString( relativePath.path() ) + "','"; + command += escapeString( relativePath.directory() ) + "',"; + command += QString::number( deviceId ) + ','; + command += QString::number( QFileInfo( bundle->url().path() ).created().toTime_t() ) + ','; + command += QString::number( QFileInfo( bundle->url().path() ).lastModified().toTime_t() ) + ','; + + command += escapeString( QString::number( albumID( bundle->album(), true, !incremental, true ) ) ) + ','; + command += escapeString( QString::number( artistID( bundle->artist(), true, !incremental, true ) ) ) + ','; + command += escapeString( QString::number( composerID( bundle->composer(), true, !incremental, true ) ) ) + ','; + command += escapeString( QString::number( genreID( bundle->genre(), true, !incremental, true ) ) ) + ",'"; + command += escapeString( QString::number( yearID( QString::number( bundle->year() ), true, !incremental, true ) ) ) + "','"; + + command += escapeString( bundle->title() ) + "','"; + command += escapeString( bundle->comment() ) + "', "; + command += escapeString( QString::number( bundle->track() ) ) + " , "; + command += escapeString( QString::number( bundle->discNumber() ) ) + " , "; + command += escapeString( QString::number( bundle->bpm() ) ) + " , "; + switch( bundle->compilation() ) { + case MetaBundle::CompilationNo: + command += boolF(); + break; + + case MetaBundle::CompilationYes: + command += boolT(); + break; + + case MetaBundle::CompilationUnknown: + default: + command += "NULL"; + } + command += ','; + + // NOTE any of these may be -1 or -2, this is what we want + // see MetaBundle::Undetermined + command += QString::number( bundle->length() ) + ','; + command += QString::number( bundle->bitrate() ) + ','; + command += QString::number( bundle->sampleRate() ) + ','; + command += QString::number( bundle->filesize() ) + ','; + command += QString::number( bundle->fileType() ) + ')'; + + //FIXME: currently there's no way to check if an INSERT query failed or not - always return true atm. + // Now it might be possible as insert returns the rowid. + insert( command, NULL ); + + doAFTStuff( bundle, true ); + + return true; +} + +void +CollectionDB::doAFTStuff( MetaBundle* bundle, const bool tempTables ) +{ + if( bundle->uniqueId().isEmpty() || bundle->url().path().isEmpty() ) + return; + + MountPointManager *mpm = MountPointManager::instance(); + //const to make sure one isn't later modified without the other being changed + const int deviceIdInt = mpm->getIdForUrl( bundle->url().path() ); + const QString currdeviceid = QString::number( deviceIdInt ); + QString currid = escapeString( bundle->uniqueId() ); + QString currurl = escapeString( mpm->getRelativePath( deviceIdInt, bundle->url().path() ) ); + QString currdir = escapeString( mpm->getRelativePath( deviceIdInt, bundle->url().directory() ) ); + //debug() << "Checking currid = " << currid << ", currdir = " << currdir << ", currurl = " << currurl << endl; + //debug() << "tempTables = " << (tempTables?"true":"false") << endl; + + + QStringList urls = query( QString( + "SELECT url, uniqueid " + "FROM uniqueid%1 " + "WHERE deviceid = %2 AND url = '%3';" ) + .arg( tempTables ? "_temp" : "" + , currdeviceid + , currurl ) ); + + QStringList uniqueids = query( QString( + "SELECT url, uniqueid, deviceid " + "FROM uniqueid%1 " + "WHERE uniqueid = '%2';" ) + .arg( tempTables ? "_temp" : "" + , currid ) ); + + QStringList nonTempIDs = query( QString( + "SELECT url, uniqueid, deviceid " + "FROM uniqueid " + "WHERE uniqueid = '%1';" ) + .arg( currid ) ); + + QStringList nonTempURLs = query( QString( + "SELECT url, uniqueid " + "FROM uniqueid " + "WHERE deviceid = %1 AND url = '%2';" ) + .arg( currdeviceid + , currurl ) ); + + bool tempTablesAndInPermanent = false; + bool permanentFullMatch = false; + + //if we're not using temp tables here, i.e. tempTables is false, + //then the results from both sets of queries above should be equal, + //so behavior should be the same + if( tempTables && ( nonTempURLs.count() > 0 || nonTempIDs.count() > 0 ) ) + tempTablesAndInPermanent = true; + if( tempTablesAndInPermanent && nonTempURLs.count() > 0 && nonTempIDs.count() > 0 ) + permanentFullMatch = true; + + //debug() << "tempTablesAndInPermanent = " << (tempTablesAndInPermanent?"true":"false") << endl; + //debug() << "permanentFullMatch = " << (permanentFullMatch?"true":"false") << endl; + + //debug() << "Entering checks" << endl; + //first case: not in permanent table or temporary table + if( !tempTablesAndInPermanent && urls.empty() && uniqueids.empty() ) + { + //debug() << "first case" << endl; + QString insertline = QStringx( "INSERT INTO uniqueid%1 (deviceid, url, uniqueid, dir) " + "VALUES ( %2,'%3', '%4', '%5');" ) + .args( QStringList() + << ( tempTables ? "_temp" : "" ) + << currdeviceid + << currurl + << currid + << currdir ); + insert( insertline, NULL ); + //debug() << "aftCheckPermanentTables #1" << endl; + aftCheckPermanentTables( currdeviceid, currid, currurl ); + return; + } + + //next case: not in permanent table, but a match on one or the other in the temporary table + //OR, we are using permanent tables (and not considering temp ones) + if( !tempTablesAndInPermanent ) + { + if( urls.empty() ) //uniqueid already found in temporary table but not url; check the old URL + { + //stat the original URL + QString absPath = mpm->getAbsolutePath( uniqueids[2].toInt(), uniqueids[0] ); + //debug() << "At doAFTStuff, stat-ing file " << absPath << endl; + bool statSuccessful = false; + bool pathsSame = absPath == bundle->url().path(); + if( !pathsSame ) + statSuccessful = QFile::exists( absPath ); + if( statSuccessful ) //if true, new one is a copy + warning() << "Already-scanned file at " << absPath << " has same UID as new file at " << bundle->url().path() << endl; + else //it's a move, not a copy, or a copy and then both files were moved...can't detect that + { + //debug() << "stat was NOT successful, updating tables with: " << endl; + //debug() << QString( "UPDATE uniqueid%1 SET url='%2', dir='%3' WHERE uniqueid='%4';" ).arg( ( tempTables ? "_temp" : "" ), currurl, currdir, currid ) << endl; + query( QStringx( "UPDATE uniqueid%1 SET deviceid = %2, url='%3', dir='%4' WHERE uniqueid='%5';" ) + .args( QStringList() + << ( tempTables ? "_temp" : "" ) + << currdeviceid + << currurl + << currdir + << currid ) ); + if( !pathsSame ) + emit fileMoved( absPath, bundle->url().path(), bundle->uniqueId() ); + } + } + //okay then, url already found in temporary table but different uniqueid + //a file exists in the same place as before, but new uniqueid...assume + //that this is desired user behavior + //NOTE: this should never happen during an incremental scan with temporary tables...! + else if( uniqueids.empty() ) + { + //debug() << "file exists in same place as before, new uniqueid" << endl; + query( QString( "UPDATE uniqueid%1 SET uniqueid='%2' WHERE deviceid = %3 AND url='%4';" ) + .arg( tempTables ? "_temp" : "" + , currid + , currdeviceid + , currurl ) ); + emit uniqueIdChanged( bundle->url().path(), urls[1], bundle->uniqueId() ); + } + //else uniqueid and url match; nothing happened, so safely exit + return; + } + //okay...being here means, we are using temporary tables, AND it exists in the permanent table + else + { + //first case...full match exists in permanent table, should then be no match in temp table + //(since code below deleted from permanent table after changes) + //in this case, just insert into temp table + if( permanentFullMatch ) + { + QString insertline = QString( "INSERT INTO uniqueid_temp (deviceid, url, uniqueid, dir) " + "VALUES ( %1, '%2'" ) + .arg( currdeviceid + , currurl ); + insertline += QString( ", '%1', '%2');" ).arg( currid ).arg( currdir ); + //debug() << "running command: " << insertline << endl; + insert( insertline, NULL ); + //debug() << "aftCheckPermanentTables #2" << endl; + aftCheckPermanentTables( currdeviceid, currid, currurl ); + return; + } + + //second case...full match exists in permanent table, but path is different + if( nonTempURLs.empty() ) + { + //stat the original URL + QString absPath = mpm->getAbsolutePath( nonTempIDs[2].toInt(), nonTempIDs[0] ); + //debug() << "At doAFTStuff part 2, stat-ing file " << absPath << endl; + bool statSuccessful = false; + bool pathsSame = absPath == bundle->url().path(); + if( !pathsSame ) + statSuccessful = QFile::exists( absPath ); + if( statSuccessful ) //if true, new one is a copy + warning() << "Already-scanned file at " << absPath << " has same UID as new file at " << currurl << endl; + else //it's a move, not a copy, or a copy and then both files were moved...can't detect that + { + //debug() << "stat part 2 was NOT successful, updating tables with: " << endl; + query( QString( "DELETE FROM uniqueid WHERE uniqueid='%1';" ) + .arg( currid ) ); + query( QString( "INSERT INTO uniqueid_temp (deviceid, url, uniqueid, dir) " + "VALUES ( %1, '%2', '%3', '%4')" ) + .arg( currdeviceid + , currurl + , currid + , currdir ) ); + if( !pathsSame ) + emit fileMoved( absPath, bundle->url().path(), bundle->uniqueId() ); + } + } + else if( nonTempIDs.empty() ) + { + //debug() << "file exists in same place as before, part 2, new uniqueid" << endl; + query( QString( "DELETE FROM uniqueid WHERE deviceid = %1 AND url='%2';" ) + .arg( currdeviceid ) + .arg( currurl ) ); + query( QString( "INSERT INTO uniqueid_temp (deviceid, url, uniqueid, dir) VALUES ( %1, '%2', '%3', '%4')" ) + .arg( currdeviceid + , currurl + , currid + , currdir ) ); + emit uniqueIdChanged( bundle->url().path(), nonTempURLs[1], bundle->uniqueId() ); + } + //else do nothing...really this case should never happen + return; + } +} + +void +CollectionDB::emitFileDeleted( const QString &absPath, const QString &uniqueid ) +{ + if( uniqueid.isEmpty() ) + emit fileDeleted( absPath ); + else + emit fileDeleted( absPath, uniqueid ); +} + +void +CollectionDB::emitFileAdded( const QString &absPath, const QString &uniqueid ) +{ + if( uniqueid.isEmpty() ) + emit fileAdded( absPath ); + else + emit fileAdded( absPath, uniqueid ); +} + +QString +CollectionDB::urlFromUniqueId( const QString &id ) +{ + bool scanning = ( ScanController::instance() && ScanController::instance()->tablesCreated() ); + QStringList urls = query( QString( + "SELECT deviceid, url " + "FROM uniqueid%1 " + "WHERE uniqueid = '%2';" ) + .arg( scanning ? "_temp" : QString::null ) + .arg( id ), true ); + + if( urls.empty() && scanning ) + urls = query( QString( + "SELECT deviceid, url " + "FROM uniqueid " + "WHERE uniqueid = '%1';" ) + .arg( id ) ); + + if( urls.empty() ) + return QString(); + + return MountPointManager::instance()->getAbsolutePath( urls[0].toInt(), urls[1] ); +} + +QString +CollectionDB::uniqueIdFromUrl( const KURL &url ) +{ + MountPointManager *mpm = MountPointManager::instance(); + int currdeviceid = mpm->getIdForUrl( url.path() ); + QString currurl = escapeString( mpm->getRelativePath( currdeviceid, url.path() ) ); + + bool scanning = ( ScanController::instance() && ScanController::instance()->tablesCreated() ); + QStringList uid = query( QString( + "SELECT uniqueid " + "FROM uniqueid%1 " + "WHERE deviceid = %2 AND url = '%3';" ) + .arg( scanning ? "_temp" : QString::null ) + .arg( currdeviceid ) + .arg( currurl ), true ); + + if( uid.empty() && scanning ) + uid = query( QString( + "SELECT uniqueid " + "FROM uniqueid " + "WHERE deviceid = %1 AND url = '%2';" ) + .arg( currdeviceid ) + .arg( currurl ) ); + + if( uid.empty() ) + return QString(); + + return uid[0]; +} + +QString +CollectionDB::getURL( const MetaBundle &bundle ) +{ + uint artID = artistID( bundle.artist(), false ); + if( !artID ) + return QString(); + + uint albID = albumID( bundle.album(), false ); + if( !albID ) + return QString(); + + QString q = QString( "SELECT tags.deviceid, tags.url " + "FROM tags " + "WHERE tags.album = '%1' AND tags.artist = '%2' AND tags.track = '%3' AND tags.title = '%4'" + + deviceidSelection() + ';' ) + .arg( albID ) + .arg( artID ) + .arg( bundle.track() ) + .arg( escapeString( bundle.title() ) ); + + QStringList urls = URLsFromQuery( query( q ) ); + + if( urls.empty() ) + return QString(); + + if( urls.size() == 1 ) + { + return urls.first(); + } + + QString url = urls.first(); + int maxPlayed = -1; + for( QStringList::iterator it = urls.begin(); + it != urls.end(); + it++ ) + { + int pc = getPlayCount( *it ); + if( pc > maxPlayed ) + { + maxPlayed = pc; + url = *it; + } + } + + return url; +} + +// Helper function to convert the "tags.sampler" column to a MetaBundle::Collection value +// +// We use the first char of boolT / boolF as not all DBs store true/false as +// numerics (and it's only a single-char column) +static int +samplerToCompilation( const QString &it ) +{ + if( it == CollectionDB::instance()->boolT().mid( 0, 1 ) ) + { + return MetaBundle::CompilationYes; + } + else if( it == CollectionDB::instance()->boolF().mid( 0, 1 ) ) + { + return MetaBundle::CompilationNo; + } + return MetaBundle::CompilationUnknown; +} + +MetaBundle +CollectionDB::bundleFromQuery( QStringList::const_iterator *iter ) +{ + QStringList::const_iterator &it = *iter; + MetaBundle b; + //QueryBuilder automatically inserts the deviceid as return value if asked for the path + QString rpath = *it; + int deviceid = (*++it).toInt(); + b.setPath ( MountPointManager::instance()->getAbsolutePath( deviceid, rpath ) ); + b.setAlbum ( *++it ); + b.setArtist ( *++it ); + b.setComposer ( *++it ); + b.setGenre ( *++it ); + b.setTitle ( *++it ); + b.setYear ( (*++it).toInt() ); + b.setComment ( *++it ); + b.setTrack ( (*++it).toInt() ); + b.setBitrate ( (*++it).toInt() ); + b.setDiscNumber( (*++it).toInt() ); + b.setLength ( (*++it).toInt() ); + b.setSampleRate( (*++it).toInt() ); + b.setFilesize ( (*++it).toInt() ); + + b.setCompilation( samplerToCompilation( *it ) ); + ++it; + b.setFileType( (*++it).toInt() ); + b.setBpm ( (*++it).toFloat() ); + + b.setScore ( (*++it).toFloat() ); + b.setRating ( (*++it).toInt() ); + b.setPlayCount ( (*++it).toInt() ); + b.setLastPlay ( (*++it).toInt() ); + + if( false && b.length() <= 0 ) { + // we try to read the tags, despite the slow-down + debug() << "Audioproperties not known for: " << b.url().fileName() << endl; + b.readTags( TagLib::AudioProperties::Fast); + } + + return b; +} + +static void +fillInBundle( QStringList values, MetaBundle &bundle ) +{ + //TODO use this whenever possible + + // crash prevention + while( values.count() < 16 ) + values += "IF YOU CAN SEE THIS THERE IS A BUG!"; + + QStringList::ConstIterator it = values.begin(); + + bundle.setAlbum ( *it ); ++it; + bundle.setArtist ( *it ); ++it; + bundle.setComposer ( *it ); ++it; + bundle.setGenre ( *it ); ++it; + bundle.setTitle ( *it ); ++it; + bundle.setYear ( (*it).toInt() ); ++it; + bundle.setComment ( *it ); ++it; + bundle.setDiscNumber( (*it).toInt() ); ++it; + bundle.setTrack ( (*it).toInt() ); ++it; + bundle.setBitrate ( (*it).toInt() ); ++it; + bundle.setLength ( (*it).toInt() ); ++it; + bundle.setSampleRate( (*it).toInt() ); ++it; + bundle.setFilesize ( (*it).toInt() ); ++it; + bundle.setFileType ( (*it).toInt() ); ++it; + bundle.setBpm ( (*it).toFloat() ); ++it; + + bundle.setCompilation( samplerToCompilation( *it ) ); + ++it; + + bundle.setUniqueId(*it); +} + +bool +CollectionDB::bundleForUrl( MetaBundle* bundle ) +{ + int deviceid = MountPointManager::instance()->getIdForUrl( bundle->url() ); + KURL rpath; + MountPointManager::instance()->getRelativePath( deviceid, bundle->url(), rpath ); + QStringList values = query( QString( + "SELECT album.name, artist.name, composer.name, genre.name, tags.title, " + "year.name, tags.comment, tags.discnumber, " + "tags.track, tags.bitrate, tags.length, tags.samplerate, " + "tags.filesize, tags.filetype, tags.bpm, tags.sampler, uniqueid.uniqueid " + "FROM tags LEFT OUTER JOIN uniqueid ON tags.url = uniqueid.url AND tags.deviceid = uniqueid.deviceid," + "album, artist, composer, genre, year " + "WHERE album.id = tags.album AND artist.id = tags.artist AND composer.id = tags.composer AND " + "genre.id = tags.genre AND year.id = tags.year AND tags.url = '%2' AND tags.deviceid = %1;" ) + .arg( deviceid ) + .arg( escapeString( rpath.path( ) ) ) ); + + bool valid = false; + + if ( !values.empty() ) + { + fillInBundle( values, *bundle ); + valid = true; + } + else if( MediaBrowser::instance() && MediaBrowser::instance()->getBundle( bundle->url(), bundle ) ) + { + valid = true; + } + else + { + // check if it's a podcast + PodcastEpisodeBundle peb; + if( getPodcastEpisodeBundle( bundle->url(), &peb ) ) + { + if( bundle->url().protocol() == "file" && QFile::exists( bundle->url().path() ) ) + { + MetaBundle mb( bundle->url(), true /* avoid infinite recursion */ ); + *bundle = mb; + } + bundle->copyFrom( peb ); + valid = true; + } + } + + return valid; +} + + +QValueList +CollectionDB::bundlesByUrls( const KURL::List& urls ) +{ + BundleList bundles; + QStringList paths; + QueryBuilder qb; + + for( KURL::List::ConstIterator it = urls.begin(), end = urls.end(), last = urls.fromLast(); it != end; ++it ) + { + // non file stuff won't exist in the db, but we still need to + // re-insert it into the list we return, just with no tags assigned + paths += (*it).protocol() == "file" ? (*it).path() : (*it).url(); + + if( paths.count() == 50 || it == last ) + { + qb.clear(); + + qb.addReturnValue( QueryBuilder::tabAlbum, QueryBuilder::valName ); + qb.addReturnValue( QueryBuilder::tabArtist, QueryBuilder::valName ); + qb.addReturnValue( QueryBuilder::tabComposer, QueryBuilder::valName ); + qb.addReturnValue( QueryBuilder::tabGenre, QueryBuilder::valName ); + qb.addReturnValue( QueryBuilder::tabSong, QueryBuilder::valTitle ); + qb.addReturnValue( QueryBuilder::tabYear, QueryBuilder::valName ); + qb.addReturnValue( QueryBuilder::tabSong, QueryBuilder::valComment ); + qb.addReturnValue( QueryBuilder::tabSong, QueryBuilder::valTrack ); + qb.addReturnValue( QueryBuilder::tabSong, QueryBuilder::valBitrate ); + qb.addReturnValue( QueryBuilder::tabSong, QueryBuilder::valDiscNumber ); + qb.addReturnValue( QueryBuilder::tabSong, QueryBuilder::valLength ); + qb.addReturnValue( QueryBuilder::tabSong, QueryBuilder::valSamplerate ); + qb.addReturnValue( QueryBuilder::tabSong, QueryBuilder::valFilesize ); + qb.addReturnValue( QueryBuilder::tabSong, QueryBuilder::valFileType ); + qb.addReturnValue( QueryBuilder::tabSong, QueryBuilder::valBPM ); + qb.addReturnValue( QueryBuilder::tabSong, QueryBuilder::valURL ); + qb.addReturnValue( QueryBuilder::tabSong, QueryBuilder::valIsCompilation ); + + qb.addURLFilters( paths ); + qb.setOptions( QueryBuilder::optRemoveDuplicates ); + + const QStringList values = qb.run(); + + BundleList buns50; + MetaBundle b; + foreach( values ) + { + b.setAlbum ( *it ); + b.setArtist ( *++it ); + b.setComposer ( *++it ); + b.setGenre ( *++it ); + b.setTitle ( *++it ); + b.setYear ( (*++it).toInt() ); + b.setComment ( *++it ); + b.setTrack ( (*++it).toInt() ); + b.setBitrate ( (*++it).toInt() ); + b.setDiscNumber( (*++it).toInt() ); + b.setLength ( (*++it).toInt() ); + b.setSampleRate( (*++it).toInt() ); + b.setFilesize ( (*++it).toInt() ); + b.setFileType ( (*++it).toInt() ); + b.setBpm ( (*++it).toFloat() ); + b.setPath ( *++it ); + + b.setCompilation( samplerToCompilation( *it ) ); + ++it; + + b.checkExists(); + + buns50.append( b ); + } + + // we get no guarantee about the order that the database + // will return our values, and sqlite indeed doesn't return + // them in the desired order :( (MySQL does though) + foreach( paths ) + { + for( BundleList::Iterator jt = buns50.begin(), end = buns50.end(); jt != end; ++jt ) + { + if ( ( *jt ).url().path() == ( *it ) ) + { + bundles += *jt; + buns50.remove( jt ); + goto success; + } + } + + // if we get here, we didn't find an entry + { + KURL url = KURL::fromPathOrURL( *it ); + + if( !MediaBrowser::instance()->getBundle( url, &b ) ) + { + if( url.isLocalFile() ) + { + b = MetaBundle( url ); + } + else + { + b = MetaBundle(); + b.setUrl( url ); + // FIXME: more context for i18n after string freeze + b.setTitle( QString( "%1 %2 %3%4" ) + .arg( url.filename(), + i18n( "from" ), + url.hasHost() ? url.host() : QString(), + url.directory( false ) ) ); + } + + // check if it's a podcast + PodcastEpisodeBundle peb; + if( getPodcastEpisodeBundle( url, &peb ) ) + { + b.copyFrom( peb ); + } + else if( b.url().protocol() == "audiocd" || b.url().protocol() == "cdda" ) + { + // try to see if the engine has some info about the + // item (the intended behaviour should be that if the + // item is an AudioCD track, the engine can return + // CDDB data for it) + Engine::SimpleMetaBundle smb; + if ( EngineController::engine()->metaDataForUrl( b.url(), smb ) ) + { + b.setTitle( smb.title ); + b.setArtist( smb.artist ); + b.setAlbum( smb.album ); + b.setComment( smb.comment ); + b.setGenre( smb.genre ); + b.setBitrate( smb.bitrate.toInt() ); + b.setSampleRate( smb.samplerate.toInt() ); + b.setLength( smb.length.toInt() ); + b.setYear( smb.year.toInt() ); + b.setTrack( smb.tracknr.toInt() ); + } + } + + } + } + bundles += b; + +success: ; + } + + paths.clear(); + } + } + + return bundles; +} + + +void +CollectionDB::addAudioproperties( const MetaBundle& bundle ) +{ + int deviceid = MountPointManager::instance()->getIdForUrl( bundle.url() ); + KURL rpath; + MountPointManager::instance()->getRelativePath( deviceid, bundle.url(), rpath ); + query( QString( "UPDATE tags SET bitrate='%1', length='%2', samplerate='%3' WHERE url='%5' AND deviceid = %4;" ) + .arg( bundle.bitrate() ) + .arg( bundle.length() ) + .arg( bundle.sampleRate() ) + .arg( deviceid ) + .arg( escapeString( rpath.path() ) ) ); +} + + +void +CollectionDB::addSongPercentage( const QString &url, float percentage, + const QString &reason, const QDateTime *playtime ) +{ + //the URL must always be inserted last! an escaped URL can contain Strings like %1->bug + int deviceid = MountPointManager::instance()->getIdForUrl( url ); + QString rpath = MountPointManager::instance()->getRelativePath( deviceid, url ); + //statistics table might not have those values, but we need them later, so keep them + int statDevId = deviceid; + QString statRPath = rpath; + QStringList values = + query( QString( + "SELECT playcounter, createdate, percentage, rating FROM statistics " + "WHERE url = '%2' AND deviceid = %1;" ) + .arg( statDevId ).arg( escapeString( statRPath ) ) ); + + //handle corner case: deviceid!=-1 but there is a statistics row for that song with deviceid -1 + if ( values.isEmpty() ) + { + QString rpath2 = '.' + url; + values = query( QString( + "SELECT playcounter, createdate, percentage, rating FROM statistics " + "WHERE url = '%1' AND deviceid = -1;" ) + .arg( escapeString( rpath2 ) ) ); + if ( !values.isEmpty() ) + { + statRPath = rpath2; + statDevId = -1; + } + } + + uint atime = playtime ? playtime->toTime_t() : QDateTime::currentDateTime().toTime_t(); + + // check boundaries + if ( percentage > 100.f ) percentage = 100.f; + if ( percentage < 1.f ) percentage = 1.f; + + if ( !values.isEmpty() ) + { + + // increment playcounter and update accesstime + query( QString( "UPDATE statistics SET playcounter=%1, accessdate=%2 WHERE url='%4' AND deviceid= %3;" ) + .arg( values[0] + " + 1" ) + .arg( atime ) + .arg( statDevId ) + .arg( escapeString( statRPath ) ) ); + } + else + { + insert( QString( "INSERT INTO statistics ( url, deviceid, createdate, accessdate, percentage, playcounter, rating, uniqueid, deleted ) " + "VALUES ( '%6', %5, %1, %2, 0, 1, 0, %3, %4 );" ) + .arg( atime ) + .arg( atime ) + .arg( ( getUniqueId( url ).isNull() ? "NULL" : '\'' + escapeString( getUniqueId( url ) ) + '\'' ) ) + .arg( boolF() ) + .arg( statDevId ) + .arg( escapeString( statRPath ) ), 0 ); + } + + double prevscore = 50; + int playcount = 0; + if( !values.isEmpty() ) + { + playcount = values[ 0 ].toInt(); + // This stops setting the Rating (which creates a row) from affecting the + // prevscore of an unplayed track. See bug 127475 + if ( playcount ) + prevscore = values[ 2 ].toDouble(); + } + const QStringList v = query( QString( "SELECT length FROM tags WHERE url = '%2' AND deviceid = %1;" ) + .arg( deviceid ).arg( escapeString( rpath ) ) ); + const int length = v.isEmpty() ? 0 : v.first().toInt(); + + ScriptManager::instance()->requestNewScore( url, prevscore, playcount, length, percentage, reason ); +} + + +float +CollectionDB::getSongPercentage( const QString &url ) +{ + QueryBuilder qb; + qb.addReturnValue( QueryBuilder::tabStats, QueryBuilder::valScore ); + qb.addMatch( QueryBuilder::tabStats, QueryBuilder::valURL, url ); + + QStringList values = qb.run(); + + if( !values.isEmpty() ) + return values.first().toFloat(); + + return 0; +} + +int +CollectionDB::getSongRating( const QString &url ) +{ + QueryBuilder qb; + qb.addReturnValue( QueryBuilder::tabStats, QueryBuilder::valRating ); + qb.addMatch( QueryBuilder::tabStats, QueryBuilder::valURL, url ); + + QStringList values = qb.run(); + + if( values.count() ) + return kClamp( values.first().toInt(), 0, 10 ); + + return 0; +} + +void +CollectionDB::setSongPercentage( const QString &url , float percentage) +{ + int deviceid = MountPointManager::instance()->getIdForUrl( url ); + QString rpath = MountPointManager::instance()->getRelativePath( deviceid, url ); + QStringList values = + query( QString( + "SELECT playcounter, createdate, accessdate, rating FROM statistics WHERE url = '%2' AND deviceid = %1;" ) + .arg( deviceid ).arg( escapeString( rpath ) ) ); + + //handle corner case: deviceid!=-1 but there is a statistics row for that song with deviceid -1 + if ( values.isEmpty() ) + { + QString rpath2 = '.' + url; + values = query( QString( + "SELECT playcounter, createdate, accessdate, rating FROM statistics " + "WHERE url = '%1' AND deviceid = -1;" ) + .arg( escapeString( rpath2 ) ) ); + if ( !values.isEmpty() ) + { + rpath = rpath2; + deviceid = -1; + } + } + + // check boundaries + if ( percentage > 100.f ) percentage = 100.f; + if ( percentage < 0.f ) percentage = 0.f; + + if ( !values.isEmpty() ) + { + query( QString( "UPDATE statistics SET percentage=%1 WHERE url='%3' AND deviceid = %2;" ) + .arg( percentage ) + .arg( deviceid ).arg( escapeString( rpath ) ) ); + } + else + { + insert( QString( "INSERT INTO statistics ( url, deviceid, createdate, accessdate, percentage, playcounter, rating, uniqueid, deleted ) " + "VALUES ( '%7', %6, %2, %3, %1, 0, 0, %3, %4 );" ) + .arg( percentage ) + .arg( QDateTime::currentDateTime().toTime_t() ) + .arg( 0 ) + .arg( ( getUniqueId( url ).isNull() ? "NULL" : '\'' + escapeString( getUniqueId( url ) ) + '\'' ) ) + .arg( boolF() ) + .arg( deviceid ) + .arg( escapeString( rpath ) ),0 ); + } + + emit scoreChanged( url, percentage ); +} + +void +CollectionDB::setSongRating( const QString &url, int rating, bool toggleHalf ) +{ + int deviceid = MountPointManager::instance()->getIdForUrl( url ); + QString rpath = MountPointManager::instance()->getRelativePath( deviceid, url ); + QStringList values = + query( QString( + "SELECT playcounter, createdate, accessdate, percentage, rating FROM statistics WHERE url = '%2' AND deviceid = %1;" ) + .arg( deviceid ) + .arg( escapeString( rpath ) ) ); + + //handle corner case: deviceid!=-1 but there is a statistics row for that song with deviceid -1 + if( values.isEmpty() ) + { + QString rpath2 = '.' + url; + values = query( QString( + "SELECT playcounter, createdate, accessdate, percentage, rating FROM statistics " + "WHERE url = '%1' AND deviceid = -1;" ) + .arg( escapeString( rpath2 ) ) ); + if ( !values.isEmpty() ) + { + rpath = rpath2; + deviceid = -1; + } + } + + bool ok = true; + if( !values.isEmpty() ) + { + int prev = values[4].toInt( &ok ); + if( ok && toggleHalf && ( prev == rating || ( prev == 1 && rating == 2 ) ) ) + { + if( prev == 1 && rating == 2 ) + rating = 0; + else if( rating % 2 ) //.5 + rating++; + else + rating--; + } + } + + // check boundaries + if ( rating > 10 ) rating = 10; + if ( rating < 0 /*|| rating == 1*/ ) rating = 0; //ratings are 1-5 + + if ( !values.isEmpty() ) + { + query( QString( "UPDATE statistics SET rating=%1 WHERE url='%3' AND deviceid = %2;" ) + .arg( rating ) + .arg( deviceid ) + .arg( escapeString( rpath ) ) ); + } + else + { + insert( QString( "INSERT INTO statistics ( url, deviceid, createdate, accessdate, percentage, rating, playcounter, uniqueid, deleted ) " + "VALUES ( '%7', %6, %2, %3, 0, %1, 0, %4, %5 );" ) + .arg( rating ) + .arg( QDateTime::currentDateTime().toTime_t() ) + .arg( 0 ) + .arg( ( getUniqueId( url ).isNull() ? "NULL" : '\'' + escapeString( getUniqueId( url ) ) + '\'' ) ) + .arg( boolF() ) + .arg( deviceid ) + .arg( escapeString( rpath ) ), NULL ); + } + + emit ratingChanged( url, rating ); +} + +int +CollectionDB::getPlayCount( const QString &url ) +{ + //queryBuilder is good + QueryBuilder qb; + qb.addReturnValue( QueryBuilder::tabStats, QueryBuilder::valPlayCounter ); + qb.addMatch( QueryBuilder::tabStats, QueryBuilder::valURL, url ); + QStringList values = qb.run(); + if( values.count() ) + return values.first().toInt(); + return 0; +} + +QDateTime +CollectionDB::getFirstPlay( const QString &url ) +{ + QueryBuilder qb; + qb.addReturnValue( QueryBuilder::tabStats, QueryBuilder::valCreateDate ); + qb.addMatch( QueryBuilder::tabStats, QueryBuilder::valURL, url ); + QStringList values = qb.run(); + QDateTime dt; + if( values.count() ) + dt.setTime_t( values.first().toUInt() ); + return dt; +} + +QDateTime +CollectionDB::getLastPlay( const QString &url ) +{ + QueryBuilder qb; + qb.addReturnValue( QueryBuilder::tabStats, QueryBuilder::valAccessDate ); + qb.addMatch( QueryBuilder::tabStats, QueryBuilder::valURL, url ); + QStringList values = qb.run(); + QDateTime dt; + if( values.count() ) + dt.setTime_t( values.first().toUInt() ); + else + dt.setTime_t( 0 ); + return dt; +} +/*! + * @short: exchange url references in the database for a particular file + * @note: deletes all items for newURL, changes oldURL->newURL, deletes oldURL. + * FIXME: should we check if lyrics etc exist in the newURL and keep them if necessary? + */ +void +CollectionDB::migrateFile( const QString &oldURL, const QString &newURL ) +{ + int oldMediaid = MountPointManager::instance()->getIdForUrl( oldURL ); + QString oldRpath = MountPointManager::instance()->getRelativePath( oldMediaid, oldURL ); + + int newMediaid = MountPointManager::instance()->getIdForUrl( newURL ); + QString newRpath = MountPointManager::instance()->getRelativePath( newMediaid, newURL ); + + // Ensure destination is clear. + query( QString( "DELETE FROM tags WHERE url = '%2' AND deviceid = %1;" ) + .arg( newMediaid ).arg( escapeString( newRpath ) ) ); + + query( QString( "DELETE FROM statistics WHERE url = '%2' AND deviceid = %1;" ) + .arg( newMediaid ).arg( escapeString( newRpath ) ) ); + + query( QString( "DELETE FROM tags_labels WHERE url = '%2' and deviceid = %1;" ) + .arg( newMediaid).arg( escapeString( newRpath ) ) ); + + if ( !getLyrics( oldURL ).isEmpty() ) + query( QString( "DELETE FROM lyrics WHERE url = '%2' AND deviceid = %1;" ) + .arg( newMediaid ).arg( escapeString( newRpath ) ) ); + // Migrate + //code looks ugly but prevents problems when the URL contains HTTP escaped characters + query( QString( "UPDATE tags SET url = '%3', deviceid = %1" ) + .arg( newMediaid ).arg( escapeString( newRpath ) ) + + QString( " WHERE deviceid=%1 AND url = '%2';" ) + .arg( oldMediaid ).arg( escapeString( oldRpath ) ) ); + + query( QString( "UPDATE statistics SET url = '%2', deviceid = %1" ) + .arg( newMediaid ).arg( escapeString( newRpath ) ) + + QString( " WHERE deviceid=%1 AND url = '%2';" ) + .arg( oldMediaid ).arg( escapeString( oldRpath ) ) ); + + query( QString( "UPDATE lyrics SET url = '%2', deviceid = %1" ) + .arg( newMediaid ).arg( escapeString( newRpath ) ) + + QString( " WHERE deviceid=%1 AND url = '%2';" ) + .arg( oldMediaid ).arg( escapeString( oldRpath ) ) ); + + query( QString( "UPDATE tags_labels SET url = '%2', deviceid = %1 WHERE deviceid = %3 AND url = '%4';" ) + .arg( QString::number( newMediaid ), escapeString( newRpath ), QString::number( oldMediaid ), escapeString( oldRpath ) ) ); + + query( QString( "UPDATE uniqueid SET url = '%1', deviceid = %2 WHERE url = '%3' AND deviceid = %4;" ) + .arg( escapeString( newRpath ), QString::number( newMediaid ), + escapeString( oldRpath ), QString::number( oldMediaid ) ) ); + + query( QString( "UPDATE playlists SET url = '%1' WHERE url = '%2';" ) + .arg( escapeString( newURL ), + escapeString( oldURL ) ) ); +} + +void +CollectionDB::fileOperationResult( KIO::Job *job ) // slot +{ + if(job->error()) + { + m_fileOperationFailed = true; + debug() << "file operation failed: " << job->errorText() << endl; + } + else + { + m_fileOperationFailed = false; + } + + m_waitForFileOperation = false; +} + +void CollectionDB::cancelMovingFileJob() +{ + m_moveFileJobCancelled = true; +} + +bool +CollectionDB::organizeFile( const KURL &src, const OrganizeCollectionDialog &dialog, bool copy ) +{ + if( !MetaBundle::isKioUrl( src ) ) + return false; + + bool overwrite = dialog.overwriteCheck->isChecked(); + bool localFile = src.isLocalFile(); + KURL tmpSrc = src; + if( !localFile ) + { + QString tmp; + QString extension = src.url().section( '.', -1 ); + extension = extension.section("?", 0, 0); // remove trailling stuff lead by ?, if any + + int count = 0; + do + { + tmp = QString( dialog.folderCombo->currentText() + "/amarok-tmp-%1." + extension ).arg( count ); + count++; + } while( QFile::exists( tmp ) ); + tmpSrc = KURL::fromPathOrURL( tmp ); + + KIO::FileCopyJob *job = 0; + if( copy ) + { + job = KIO::file_copy( src, tmpSrc, -1, false, false, false ); + } + else + { + job = KIO::file_move( src, tmpSrc, -1, false, false, false ); + } + connect( job, SIGNAL(result( KIO::Job * )), SLOT(fileOperationResult( KIO::Job * )) ); + m_waitForFileOperation = true; + while( m_waitForFileOperation ) + { + if( m_moveFileJobCancelled ) + { + disconnect( job, SIGNAL(result( KIO::Job * )), this, SLOT(fileOperationResult( KIO::Job * )) ); + + QString partFile = QString( "%1.part" ).arg( (job->destURL()).path() ); + job->kill(); + QFile file( partFile ); + if( file.exists() ) file.remove(); + + m_waitForFileOperation = false; + m_fileOperationFailed = true; + continue; + } + + usleep( 10000 ); + kapp->processEvents( 100 ); + } + + if( m_fileOperationFailed ) + { + debug() << "failed to transfer " << src.url() << " to " << tmpSrc << endl; + + m_moveFileJobCancelled = false; + return false; + } + } + + //Building destination here. + MetaBundle mb( tmpSrc ); + QString dest = dialog.buildDestination( dialog.buildFormatString(), mb ); + + debug() << "Destination: " << dest << endl; + + if( !m_moveFileJobCancelled && tmpSrc.path() != dest ) //suppress error warning that file couldn't be moved + { + if( !CollectionDB::instance()->moveFile( tmpSrc.url(), dest, overwrite, copy && localFile ) ) + { + if( !localFile ) + QFile::remove( tmpSrc.path() ); + + m_moveFileJobCancelled = false; + return false; + } + } + + //Use cover image for folder icon + if( !m_moveFileJobCancelled && dialog.coverCheck->isChecked() && !mb.artist().isEmpty() && !mb.album().isEmpty() ) + { + KURL dstURL = KURL::fromPathOrURL( dest ); + dstURL.cleanPath(); + + QString path = dstURL.directory(); + QString cover = CollectionDB::instance()->albumImage( mb.artist(), mb.album(), false, 1 ); + + if( !QFile::exists(path + "/.directory") && !cover.endsWith( "nocover.png" ) ) + { + //QPixmap thumb; //Not amazon nice. + //if ( thumb.load( cover ) ){ + //thumb.save(path + "/.front.png", "PNG", -1 ); //hide files + + KSimpleConfig config(path + "/.directory"); + config.setGroup("Desktop Entry"); + + if( !config.hasKey("Icon") ) + { + config.writeEntry( "Icon", cover ); + config.sync(); + } + //} //Not amazon nice. + } + } + + if( localFile && isDirInCollection( src.directory() ) && QDir().rmdir( src.directory() ) ) + { + debug() << "removed: " << src.directory() << endl; + } + + m_moveFileJobCancelled = false; + + return true; +} + +bool +CollectionDB::moveFile( const QString &src, const QString &dest, bool overwrite, bool copy ) +{ + DEBUG_BLOCK + if(src == dest){ + debug() << "Source and destination URLs are the same, aborting." << endl; + return false; + } + + // Escape URL. + KURL srcURL = KURL::fromPathOrURL( src ); + KURL dstURL = KURL::fromPathOrURL( dest ); + + // Clean it. + srcURL.cleanPath(); + dstURL.cleanPath(); + + // Make sure it is valid. + if(!srcURL.isValid() || !dstURL.isValid()) + debug() << "Invalid URL " << endl; + + // Get just the directory. + KURL dir = dstURL; + dir.setFileName(QString::null); + + // Create the directory. + if(!KStandardDirs::exists(dir.path())) + if(!KStandardDirs::makeDir(dir.path())) { + debug() << "Unable to create directory " << dir.path() << endl; + } + + m_fileOperationFailed = false; + KIO::FileCopyJob *job = 0; + if( copy ) + { + job = KIO::file_copy( srcURL, dstURL, -1, overwrite, false, false ); + } + else + { + job = KIO::file_move( srcURL, dstURL, -1, overwrite, false, false ); + } + connect( job, SIGNAL(result( KIO::Job * )), SLOT(fileOperationResult( KIO::Job * )) ); + m_waitForFileOperation = true; + while( m_waitForFileOperation ) + { + if( m_moveFileJobCancelled ) + { + disconnect( job, SIGNAL(result( KIO::Job * )), this, SLOT(fileOperationResult( KIO::Job * )) ); + + QString partFile = QString( "%1.part" ).arg( (job->destURL()).path() ); + job->kill(); + QFile file( partFile ); + if( file.exists() ) file.remove(); + + m_waitForFileOperation = false; + m_fileOperationFailed = true; + continue; + } + + usleep( 10000 ); + kapp->processEvents( 100 ); + } + + if( !m_fileOperationFailed ) + { + if( copy ) + { + MetaBundle bundle( dstURL ); + if( bundle.isValidMedia() ) + { + addSong( &bundle, true ); + return true; + } + } + else + { + emit fileMoved( src, dest ); + migrateFile( srcURL.path(), dstURL.path() ); + + if( isFileInCollection( srcURL.path() ) ) + { + return true; + } + else + { + MetaBundle bundle( dstURL ); + if( bundle.isValidMedia() ) + { + addSong( &bundle, true ); + return true; + } + } + } + } + + return false; +} + + +void +CollectionDB::updateDirStats( QString path, const long datetime, const bool temporary ) +{ + if ( path.endsWith( "/" ) ) + path = path.left( path.length() - 1 ); + + int deviceid = MountPointManager::instance()->getIdForUrl( path ); + QString rpath = MountPointManager::instance()->getRelativePath( deviceid, path ); + + if (getDbConnectionType() == DbConnection::postgresql) + { + // REPLACE INTO is not valid SQL for postgres, so we need to check whether we + // should UPDATE() or INSERT() + QStringList values = query( QString("SELECT * FROM directories%1 WHERE dir='%3' AND deviceid=%2;") + .arg( temporary ? "_temp" : "") + .arg( deviceid ) + .arg( escapeString( rpath ) ) ); + + if(values.count() > 0 ) + { + query( QString( "UPDATE directories%1 SET changedate=%2 WHERE dir='%4'AND deviceid=%3;") + .arg( temporary ? "_temp" : "" ) + .arg( datetime ) + .arg( deviceid ) + .arg( escapeString( rpath ) ) ); + } + else + { + + query( QString( "INSERT INTO directories%1 (dir, deviceid,changedate) VALUES ('%4', %3, '%2');") + .arg( temporary ? "_temp" : "") + .arg( datetime ) + .arg( deviceid ) + .arg( escapeString( rpath ) ) ); + } + } + else + { + query( QString( "REPLACE INTO directories%1 ( dir, deviceid, changedate ) VALUES ( '%4', %3, %2 );" ) + .arg( temporary ? "_temp" : "" ) + .arg( datetime ) + .arg( deviceid ) + .arg( escapeString( rpath ) ) ); + } + + INotify::instance()->watchDir( path ); +} + + +void +CollectionDB::removeSongsInDir( QString path, QMap *tagsRemoved ) +{ + if ( path.endsWith( "/" ) ) + path = path.left( path.length() - 1 ); + int deviceid = MountPointManager::instance()->getIdForUrl( path ); + QString rpath = MountPointManager::instance()->getRelativePath( deviceid, path ); + + // Pass back the list of tags we actually delete if requested. + if( tagsRemoved ) + { + QStringList result + = query( QString( "SELECT tags.deviceid, tags.url, uniqueid.uniqueid FROM tags " + "LEFT JOIN uniqueid ON uniqueid.url = tags.url " + "AND uniqueid.deviceid = tags.deviceid " + "WHERE tags.dir = '%2' AND tags.deviceid = %1" ) + .arg( deviceid ) + .arg( escapeString( rpath ) ) ); + QStringList::ConstIterator it = result.begin(), end = result.end(); + while( it != end ) + { + int deviceid2 = (*(it++)).toInt(); + QString rpath2 = *(it++); + QString uniqueid = *(it++); + (*tagsRemoved)[uniqueid] = MountPointManager::instance()->getAbsolutePath( + deviceid2, rpath2 ); + } + } + + query( QString( "DELETE FROM tags WHERE dir = '%2' AND deviceid = %1;" ) + .arg( deviceid ) + .arg( escapeString( rpath ) ) ); + + query( QString( "DELETE FROM uniqueid WHERE dir = '%2' AND deviceid = %1;" ) + .arg( deviceid ) + .arg( escapeString( rpath ) ) ); +} + + +bool +CollectionDB::isDirInCollection( QString path ) +{ + if ( path.endsWith( "/" ) ) + path = path.left( path.length() - 1 ); + int deviceid = MountPointManager::instance()->getIdForUrl( path ); + QString rpath = MountPointManager::instance()->getRelativePath( deviceid, path ); + + QStringList values = + query( QString( "SELECT changedate FROM directories WHERE dir = '%2' AND deviceid = %1;" ) + .arg( deviceid ) + .arg( escapeString( rpath ) ) ); + + return !values.isEmpty(); +} + + +bool +CollectionDB::isFileInCollection( const QString &url ) +{ + int deviceid = MountPointManager::instance()->getIdForUrl( url ); + QString rpath = MountPointManager::instance()->getRelativePath( deviceid, url ); + + QString sql = QString( "SELECT url FROM tags WHERE url = '%2' AND deviceid = %1" ) + .arg( deviceid ) + .arg( escapeString( rpath ) ); + if ( deviceid == -1 ) + { + sql += ';'; + } + else + { + QString rpath2 = '.' + url; + sql += QString( " OR url = '%1' AND deviceid = -1;" ) + .arg( escapeString( rpath2 ) ); + } + QStringList values = query( sql ); + + return !values.isEmpty(); +} + + +void +CollectionDB::removeSongs( const KURL::List& urls ) +{ + for( KURL::List::ConstIterator it = urls.begin(), end = urls.end(); it != end; ++it ) + { + int deviceid = MountPointManager::instance()->getIdForUrl( *it ); + QString rpath = MountPointManager::instance()->getRelativePath( deviceid, (*it).path() ); + + query( QString( "DELETE FROM tags WHERE url = '%2' AND deviceid = %1;" ) + .arg( deviceid ) + .arg( escapeString( rpath ) ) ); + query( QString( "DELETE FROM uniqueid WHERE url = '%2' AND deviceid = %1;" ) + .arg( deviceid ) + .arg( escapeString( rpath ) ) ); + query( QString( "UPDATE statistics SET deleted = %1 WHERE url = '%3' AND deviceid = %2;" ) + .arg( boolT() ) + .arg( deviceid ) + .arg( escapeString( rpath ) ) ); + } +} + + +QStringList +CollectionDB::similarArtists( const QString &artist, uint count ) +{ + QStringList values; + + values = query( QString( "SELECT suggestion FROM related_artists WHERE artist = '%1' ORDER BY %2 LIMIT %3 OFFSET 0;" ) + .arg( escapeString( artist ), randomFunc(), QString::number( count ) ) ); + + if ( values.isEmpty() ) + Scrobbler::instance()->similarArtists( artist ); + + return values; +} + + +void +CollectionDB::sanitizeCompilations() +{ + query( QString( "UPDATE tags_temp SET sampler = %1 WHERE sampler IS NULL;").arg( boolF() ) ); +} + +void +CollectionDB::checkCompilations( const QString &path, const bool temporary ) +{ + QStringList albums; + QStringList artists; + QStringList dirs; + + int deviceid = MountPointManager::instance()->getIdForUrl( path ); + QString rpath = MountPointManager::instance()->getRelativePath( deviceid, path ); + + albums = query( QString( "SELECT DISTINCT album.name FROM tags_temp, album%1 AS album WHERE tags_temp.dir = '%3' AND tags_temp.deviceid = %2 AND album.id = tags_temp.album AND tags_temp.sampler IS NULL;" ) + .arg( temporary ? "_temp" : "" ) + .arg( deviceid ) + .arg( escapeString( rpath ) ) ); + + for ( uint i = 0; i < albums.count(); i++ ) + { + if ( albums[ i ].isEmpty() ) continue; + + const uint album_id = albumID( albums[ i ], false, temporary, true ); + artists = query( QString( "SELECT DISTINCT artist.name FROM tags_temp, artist%1 AS artist WHERE tags_temp.album = '%2' AND tags_temp.artist = artist.id;" ) + .arg( temporary ? "_temp" : "" ) + .arg( album_id ) ); + dirs = query( QString( "SELECT DISTINCT dir FROM tags_temp WHERE album = '%1';" ) + .arg( album_id ) ); + + if ( artists.count() > dirs.count() ) + { + debug() << "Detected compilation: " << albums[ i ] << " - " << artists.count() << ':' << dirs.count() << endl; + } + query( QString( "UPDATE tags_temp SET sampler = %1 WHERE album = '%2' AND sampler IS NULL;" ) + .arg(artists.count() > dirs.count() ? boolT() : boolF()).arg( album_id ) ); + } +} + +void +CollectionDB::setCompilation( const KURL::List &urls, bool enabled, bool updateView ) +{ + for ( KURL::List::const_iterator it = urls.begin(); it != urls.end(); ++it ) + { + QString url( ( *it ).path() ); + + int deviceid = MountPointManager::instance()->getIdForUrl( url ); + QString rpath = MountPointManager::instance()->getRelativePath( deviceid, url ); + + query( QString( "UPDATE tags SET sampler = %1 WHERE tags.url = '%2' AND tags.deviceid = %3;" ) + .arg( ( enabled ? boolT() : boolF() ), escapeString( rpath ), QString::number( deviceid ) ) ); + } + + // Update the Collection-Browser view, + // using QTimer to make sure we don't manipulate the GUI from a thread + if ( updateView ) + QTimer::singleShot( 0, CollectionView::instance(), SLOT( renderView() ) ); +} + + +void +CollectionDB::removeDirFromCollection( QString path ) +{ + //if ( path.endsWith( "/" ) ) + // path = path.left( path.length() - 1 ); + int deviceid = MountPointManager::instance()->getIdForUrl( path ); + QString rpath = MountPointManager::instance()->getRelativePath( deviceid, path ); + + query( QString( "DELETE FROM directories WHERE dir = '%2' AND deviceid = %1;" ) + .arg( deviceid ) + .arg( escapeString( rpath ) ) ); +} + + +QString +CollectionDB::IDFromExactValue( QString table, QString value, bool autocreate, bool temporary /* = false */ ) +{ + if ( temporary ) + { + table.append( "_temp" ); + } + + QString querystr( QString( "SELECT id FROM %1 WHERE name " ).arg( table ) ); + querystr += exactCondition( value ) + ';'; + QStringList result = query( querystr ); + if ( result.isEmpty() ) + { + if ( autocreate ) + return QString::number( insert( QString( "INSERT INTO %1 ( name ) VALUES ( '%2' );" ) + .arg( table, escapeString( value ) ), + table ) ); + else + return 0; + } + else + { + if ( result.count() > 1 ) + debug() << "More than one entry in the " << table << " database for '" << value << '\'' << endl; + return result.first(); + } +} + +void +CollectionDB::deleteRedundantName( const QString &table, const QString &id ) +{ + QString querystr( QString( "SELECT %1 FROM tags WHERE tags.%1 = %2 LIMIT 1;" ).arg( table, id ) ); + QStringList result = query( querystr ); + if ( result.isEmpty() ) + query( QString( "DELETE FROM %1 WHERE id = %2;" ).arg( table,id ) ); +} + +void +CollectionDB::deleteAllRedundant( const QString &table ) +{ + //This works with MySQL4. I thought it might not do, due to the comment in copyTempTables + query( QString( "DELETE FROM %1 WHERE id NOT IN ( SELECT %2 FROM tags )" ).arg( table, table ) ); +} + + +void +CollectionDB::updateTags( const QString &url, const MetaBundle &bundle, const bool updateView ) +{ + DEBUG_BLOCK + QueryBuilder qb; + qb.addReturnValue( QueryBuilder::tabSong, QueryBuilder::valTitle ); + qb.addReturnValue( QueryBuilder::tabArtist, QueryBuilder::valName ); + qb.addReturnValue( QueryBuilder::tabComposer, QueryBuilder::valName ); + qb.addReturnValue( QueryBuilder::tabAlbum, QueryBuilder::valName ); + qb.addReturnValue( QueryBuilder::tabGenre, QueryBuilder::valName ); + qb.addReturnValue( QueryBuilder::tabYear, QueryBuilder::valName ); + qb.addReturnValue( QueryBuilder::tabSong, QueryBuilder::valTrack ); + qb.addReturnValue( QueryBuilder::tabSong, QueryBuilder::valComment ); + qb.addReturnValue( QueryBuilder::tabSong, QueryBuilder::valDiscNumber ); + qb.addReturnValue( QueryBuilder::tabSong, QueryBuilder::valFilesize ); + qb.addReturnValue( QueryBuilder::tabSong, QueryBuilder::valFileType ); + // [10] is above. [11] is below. + qb.addReturnValue( QueryBuilder::tabSong, QueryBuilder::valBPM ); + qb.addReturnValue( QueryBuilder::tabArtist, QueryBuilder::valID ); + qb.addReturnValue( QueryBuilder::tabComposer, QueryBuilder::valID ); + qb.addReturnValue( QueryBuilder::tabAlbum, QueryBuilder::valID ); + qb.addReturnValue( QueryBuilder::tabGenre, QueryBuilder::valID ); + qb.addReturnValue( QueryBuilder::tabYear, QueryBuilder::valID ); + + qb.addURLFilters ( QStringList( url ) ); + qb.setOptions( QueryBuilder::optRemoveDuplicates ); + QStringList values = qb.run(); + + if ( values.count() > 17 ) + { + error() << "Query returned more than 1 song. Aborting updating metadata" << endl; + return; + } + + if ( !values.isEmpty() ) + { + bool art=false, comp=false, alb=false, gen=false, year=false; + + QString command = "UPDATE tags SET "; + if ( values[ 0 ] != bundle.title() ) + command += "title = '" + escapeString( bundle.title() ) + "', "; + if ( values[ 1 ] != bundle.artist() ) + { + art = true; + command += "artist = " + IDFromExactValue( "artist", bundle.artist() ) + ", "; + } + if ( values[ 2 ] != bundle.composer() ) + { + comp = true; + command += "composer = " + IDFromExactValue( "composer", bundle.composer() ) + ", "; + } + if ( values[ 3 ] != bundle.album() ) + { + alb = true; + command += "album = " + IDFromExactValue( "album", bundle.album() ) + ", "; + } + if ( values[ 4 ] != bundle.genre() ) + { + gen = true; + command += "genre = " + IDFromExactValue( "genre", bundle.genre() ) + ", "; + } + if ( values[ 5 ] != QString::number( bundle.year() ) ) + { + year = false; + command += "year = " + IDFromExactValue( "year", QString::number( bundle.year() ) ) + ", "; + } + if ( values[ 6 ] != QString::number( bundle.track() ) ) + command += "track = " + QString::number( bundle.track() ) + ", "; + if ( values[ 7 ] != bundle.comment() ) + command += "comment = '" + escapeString( bundle.comment() ) + "', "; + if ( values[ 8 ] != QString::number( bundle.discNumber() ) ) + command += "discnumber = '" + QString::number( bundle.discNumber() ) + "', "; + if ( values[ 9 ] != QString::number( bundle.filesize() ) ) + command += "filesize = '" + QString::number( bundle.filesize() ) + "', "; + if ( values[ 10 ] != QString::number( bundle.fileType() ) ) + command += "filetype = '" + QString::number( bundle.fileType() ) + "', "; + if ( values[ 11 ] != QString::number( bundle.bpm() ) ) + command += "bpm = '" + QString::number( bundle.bpm() ) + "', "; + + if ( "UPDATE tags SET " == command ) + { + debug() << "No tags selected to be changed" << endl; + } + else + { + int deviceid = MountPointManager::instance()->getIdForUrl( url ); + QString rpath = MountPointManager::instance()->getRelativePath( deviceid, url ); + //We have to remove the trailing comma from command + query( command.left( command.length() - 2 ) + " WHERE url = '" + escapeString( rpath ) + + "' AND deviceid = " + QString::number( deviceid ) + ';' ); + } + + //Check to see if we use the entry anymore. If not, delete it + if ( art ) + deleteRedundantName( "artist", values[ 12 ] ); + if ( comp ) + deleteRedundantName( "composer", values[ 13 ] ); + if ( alb ) + deleteRedundantName( "album", values[ 14 ] ); + if ( gen ) + deleteRedundantName( "genre", values[ 15 ] ); + if ( year ) + deleteRedundantName( "year", values[ 16 ] ); + + // Update the Collection-Browser view, + // using QTimer to make sure we don't manipulate the GUI from a thread + if ( updateView ) + QTimer::singleShot( 0, CollectionView::instance(), SLOT( databaseChanged() ) ); + + if( art || alb ) + emit tagsChanged( values[12], values[14] ); + } + + if ( EngineController::instance()->bundle().url() == bundle.url() ) + { + debug() << "Current song edited, updating widgets: " << bundle.title() << endl; + EngineController::instance()->currentTrackMetaDataChanged( bundle ); + } + + emit tagsChanged( bundle ); +} + + +void +CollectionDB::updateURL( const QString &url, const bool updateView ) +{ + // don't use the KURL ctor as it checks the db first + MetaBundle bundle; + bundle.setPath( url ); + bundle.readTags( TagLib::AudioProperties::Fast ); + + updateTags( url, bundle, updateView); + doAFTStuff( &bundle, false ); +} + +QString +CollectionDB::getUniqueId( const QString &url ) +{ + int deviceid = MountPointManager::instance()->getIdForUrl( url ); + QString rpath = MountPointManager::instance()->getRelativePath( deviceid, url ); + QStringList values = query( QString( "SELECT uniqueid FROM uniqueid WHERE deviceid = %1 AND url = '%2';" ) + .arg( deviceid ) + .arg( escapeString( rpath ) )); + if( !values.empty() ) + return values[0]; + else + return QString(); +} + +void +CollectionDB::setLyrics( const QString &url, const QString &lyrics, const QString &uniqueid ) +{ + int deviceid = MountPointManager::instance()->getIdForUrl( url ); + QString rpath = MountPointManager::instance()->getRelativePath( deviceid, url ); + + QStringList values = query(QString("SELECT lyrics FROM lyrics WHERE url = '%2' AND deviceid = %1;") + .arg( deviceid ).arg( escapeString( rpath ) ) ); + if(values.count() > 0) + { + if ( !lyrics.isEmpty() ) + query( QString( "UPDATE lyrics SET lyrics = '%1' WHERE url = '%3' AND deviceid = %2;" ) + .arg( escapeString( lyrics), QString::number(deviceid), escapeString( rpath ) ) ); + else + query( QString( "DELETE FROM lyrics WHERE url = '%2' AND deviceid = %1;" ) + .arg( deviceid).arg( escapeString( rpath ) ) ); + } + else + { + insert( QString( "INSERT INTO lyrics (deviceid, url, lyrics, uniqueid) values ( %1, '%2', '%3', '%4' );" ) + .arg( QString::number(deviceid), escapeString( rpath ), escapeString( lyrics ), escapeString( uniqueid ) ), NULL); + } +} + + +QString +CollectionDB::getLyrics( const QString &url ) +{ + int deviceid = MountPointManager::instance()->getIdForUrl( url ); + QString rpath = MountPointManager::instance()->getRelativePath( deviceid, url ); + QStringList values = query( QString( "SELECT lyrics FROM lyrics WHERE url = '%2' AND deviceid = %1;" ) + .arg( deviceid ).arg( escapeString( rpath ) ) ); + return values[0]; +} + +void CollectionDB::removeInvalidAmazonInfo( const QString& md5sum ) +{ + query( QString( "DELETE FROM amazon WHERE filename='%1'" ).arg( md5sum ) ); +} + +void CollectionDB::newAmazonReloadDate( const QString& asin, const QString& locale, const QString& md5sum) +{ + QStringList values = query(QString("SELECT filename FROM amazon WHERE filename = '%1'") + .arg(md5sum)); + if(values.count() > 0) + { + query( QString("UPDATE amazon SET asin = '%1', locale = '%2', refetchdate = '%3' WHERE filename = '%4'") + .arg(asin) + .arg(locale) + .arg(QDateTime::currentDateTime().addDays(80).toTime_t()) + .arg(md5sum)); + } + else + { + insert( QString( "INSERT INTO amazon ( asin, locale, filename, refetchdate ) VALUES ( '%1', '%2', '%3', '%4');" ) + .arg(asin) + .arg(locale) + .arg(md5sum) + .arg(QDateTime::currentDateTime().addDays(80).toTime_t()), NULL ); + } +} + +QStringList CollectionDB::staleImages() +{ + return query(QString("SELECT asin, locale, filename FROM amazon WHERE refetchdate < %1 ;") + .arg(QDateTime::currentDateTime().toTime_t() )); +} + +void +CollectionDB::applySettings() +{ + bool recreateConnections = false; + if ( AmarokConfig::databaseEngine().toInt() != getDbConnectionType() ) + { + if ( AmarokConfig::databaseEngine().toInt() == DbConnection::mysql ) + m_dbConnType = DbConnection::mysql; + else if ( AmarokConfig::databaseEngine().toInt() == DbConnection::postgresql ) + m_dbConnType = DbConnection::postgresql; + else m_dbConnType = DbConnection::sqlite; + recreateConnections = true; + } + else if ( AmarokConfig::databaseEngine().toInt() == DbConnection::mysql ) + { + // Using MySQL, so check if MySQL settings were changed + const MySqlConfig *config = + static_cast ( m_dbConfig ); + if ( AmarokConfig::mySqlHost() != config->host() ) + { + recreateConnections = true; + } + else if ( AmarokConfig::mySqlPort() != config->port() ) + { + recreateConnections = true; + } + else if ( AmarokConfig::mySqlDbName() != config->database() ) + { + recreateConnections = true; + } + else if ( AmarokConfig::mySqlUser() != config->username() ) + { + recreateConnections = true; + } + else if ( AmarokConfig::mySqlPassword2() != config->password() ) + { + recreateConnections = true; + } + } + else if ( AmarokConfig::databaseEngine().toInt() == DbConnection::postgresql ) + { + const PostgresqlConfig *config = + static_cast ( m_dbConfig ); + if ( AmarokConfig::postgresqlHost() != config->host() ) + { + recreateConnections = true; + } + else if ( AmarokConfig::postgresqlPort() != config->port() ) + { + recreateConnections = true; + } + else if ( AmarokConfig::postgresqlDbName() != config->database() ) + { + recreateConnections = true; + } + else if ( AmarokConfig::postgresqlUser() != config->username() ) + { + recreateConnections = true; + } + else if ( AmarokConfig::postgresqlPassword() != config->password() ) + { + recreateConnections = true; + } + } + + if ( recreateConnections ) + { + debug() + << "Database engine settings changed: " + << "recreating DbConnections" << endl; + // If Database engine was changed, recreate DbConnections. + destroy(); + initialize(); + CollectionView::instance()->renderView(); + PlaylistBrowser::instance()->loadPodcastsFromDatabase(); + + emit databaseEngineChanged(); + } +} + +DbConnection * CollectionDB::getMyConnection() +{ + //after some thought, to be thread-safe, must lock at the beginning of this function, + //not only if a new connection is made + connectionMutex->lock(); + + DbConnection *dbConn; + QThread *currThread = ThreadManager::Thread::getRunning(); + + if (threadConnections->contains(currThread)) + { + QMap::Iterator it = threadConnections->find(currThread); + dbConn = it.data(); + connectionMutex->unlock(); + return dbConn; + } + +#ifdef USE_MYSQL + if ( m_dbConnType == DbConnection::mysql ) + { + dbConn = new MySqlConnection( static_cast( m_dbConfig ) ); + } + else +#endif +#ifdef USE_POSTGRESQL + if ( m_dbConnType == DbConnection::postgresql ) + { + dbConn = new PostgresqlConnection( static_cast( m_dbConfig ) ); + } + else +#endif + { + dbConn = new SqliteConnection( static_cast( m_dbConfig ) ); + } + + threadConnections->insert(currThread, dbConn); + + connectionMutex->unlock(); + return dbConn; +} + + +void +CollectionDB::releasePreviousConnection(QThread *currThread) +{ + //if something already exists, delete the object, and erase knowledge of it from the QMap. + connectionMutex->lock(); + DbConnection *dbConn; + if (threadConnections->contains(currThread)) + { + QMap::Iterator it = threadConnections->find(currThread); + dbConn = it.data(); + delete dbConn; + threadConnections->erase(currThread); + } + connectionMutex->unlock(); +} + +bool +CollectionDB::isConnected() +{ + return getMyConnection()->isConnected(); +} + +////////////////////////////////////////////////////////////////////////////////////////// +// PROTECTED +////////////////////////////////////////////////////////////////////////////////////////// + +QCString +CollectionDB::md5sum( const QString& artist, const QString& album, const QString& file ) +{ + KMD5 context( artist.lower().local8Bit() + album.lower().local8Bit() + file.local8Bit() ); +// debug() << "MD5 SUM for " << artist << ", " << album << ": " << context.hexDigest() << endl; + return context.hexDigest(); +} + + +void CollectionDB::engineTrackEnded( int finalPosition, int trackLength, const QString &reason ) +{ + //This is where percentages are calculated + //TODO statistics are not calculated when currentTrack doesn't exist + + // Don't update statistics if song has been played for less than 15 seconds + // if ( finalPosition < 15000 ) return; + + //below check is necessary because if stop after current track is selected, + //the url's path will be empty, so check the previous URL for the path that + //had just played + const KURL url = EngineController::instance()->bundle().url().path().isEmpty() ? + EngineController::instance()->previousURL() : + EngineController::instance()->bundle().url(); + PodcastEpisodeBundle peb; + if( getPodcastEpisodeBundle( url.url(), &peb ) ) + { + PodcastEpisode *p = PlaylistBrowser::instance()->findPodcastEpisode( peb.url(), peb.parent() ); + if ( p ) + p->setListened(); + + if( !url.isLocalFile() ) + return; + } + + if ( url.path().isEmpty() || !m_autoScoring ) return; + + // sanity check + if ( finalPosition > trackLength || finalPosition <= 0 ) + finalPosition = trackLength; + + int pct = (int) ( ( (double) finalPosition / (double) trackLength ) * 100 ); + + // increase song counter & calculate new statistics + addSongPercentage( url.path(), pct, reason ); +} + + +void +CollectionDB::timerEvent( QTimerEvent* ) +{ + scanMonitor(); +} + + +////////////////////////////////////////////////////////////////////////////////////////// +// PUBLIC SLOTS +////////////////////////////////////////////////////////////////////////////////////////// + +void +CollectionDB::fetchCover( QWidget* parent, const QString& artist, const QString& album, bool noedit, QListViewItem* item ) //SLOT +{ + #ifdef AMAZON_SUPPORT + debug() << "Fetching cover for " << artist << " - " << album << endl; + + const bool isCompilation = albumIsCompilation( QString::number( albumID( album, false, false, true ) ) ); + CoverFetcher* fetcher; + if( isCompilation ) + // avoid putting various artists in front of album title. this causes problems for locales other than US. + fetcher = new CoverFetcher( parent, "", album ); + else + fetcher = new CoverFetcher( parent, artist, album ); + if( item ) + { + itemCoverMapMutex->lock(); + itemCoverMap->insert( item, fetcher ); + itemCoverMapMutex->unlock(); + } + connect( fetcher, SIGNAL(result( CoverFetcher* )), SLOT(coverFetcherResult( CoverFetcher* )) ); + fetcher->setUserCanEditQuery( !noedit ); + fetcher->startFetch(); + #endif +} + +void +CollectionDB::scanMonitor() //SLOT +{ + if ( AmarokConfig::monitorChanges() ) + scanModifiedDirs(); +} + + +void +CollectionDB::startScan() //SLOT +{ + QStringList folders = MountPointManager::instance()->collectionFolders(); + + if ( folders.isEmpty() ) + { + //dropTables( false ); + //createTables( false ); + clearTables( false ); + emit scanDone( true ); + } + else if( PlaylistBrowser::instance() ) + { + emit scanStarted(); + ThreadManager::instance()->queueJob( new ScanController( this, false, folders ) ); + } +} + + +void +CollectionDB::stopScan() //SLOT +{ + ThreadManager::instance()->abortAllJobsNamed( "CollectionScanner" ); +} + + +////////////////////////////////////////////////////////////////////////////////////////// +// PRIVATE SLOTS +////////////////////////////////////////////////////////////////////////////////////////// + +void +CollectionDB::dirDirty( const QString& path ) +{ + debug() << k_funcinfo << "Dirty: " << path << endl; + + ThreadManager::instance()->queueJob( new ScanController( this, false, path ) ); +} + + +void +CollectionDB::coverFetcherResult( CoverFetcher *fetcher ) +{ + if( fetcher->wasError() ) { + error() << fetcher->errors() << endl; + emit coverFetcherError( fetcher->errors().front() ); + } + + else { + setAlbumImage( fetcher->artist(), fetcher->album(), fetcher->image(), fetcher->amazonURL(), fetcher->asin() ); + emit coverFetched( fetcher->artist(), fetcher->album() ); + } + + //check the validity of the CollectionItem as it may have been deleted e.g. by a + //collection scan while fetching the cover + itemCoverMapMutex->lock(); + QMap::Iterator it; + for( it = itemCoverMap->begin(); it != itemCoverMap->end(); ++it ) + { + if( it.data() == fetcher ) + { + if( it.key()->isOpen() ) + static_cast(it.key())->setPixmap( 0, QPixmap() ); + itemCoverMap->erase( it ); + } + } + itemCoverMapMutex->unlock(); +} + +/** + * This query is fairly slow with sqlite, and often happens just + * after the OSD is shown. Threading it restores responsivity. + */ +class SimilarArtistsInsertionJob : public ThreadManager::DependentJob +{ + virtual bool doJob() + { + CollectionDB::instance()->query( QString( "DELETE FROM related_artists WHERE artist = '%1';" ).arg( escapedArtist ) ); + + const QString sql = "INSERT INTO related_artists ( artist, suggestion, changedate ) VALUES ( '%1', '%2', 0 );"; + foreach( suggestions ) + CollectionDB::instance()->insert( sql + .arg( escapedArtist, + CollectionDB::instance()->escapeString( *it ) ), NULL); + + return true; + } + + virtual void completeJob() { emit CollectionDB::instance()->similarArtistsFetched( artist ); } + + const QString artist; + const QString escapedArtist; + const QStringList suggestions; + +public: + SimilarArtistsInsertionJob( CollectionDB *parent, const QString &s, const QStringList &list ) + : ThreadManager::DependentJob( parent, "SimilarArtistsInsertionJob" ) + , artist( QDeepCopy(s) ) + , escapedArtist( parent->escapeString( QDeepCopy(s) ) ) + , suggestions( QDeepCopy(list) ) + {} +}; + +void +CollectionDB::similarArtistsFetched( const QString& artist, const QStringList& suggestions ) +{ + debug() << "Received similar artists\n"; + + ThreadManager::instance()->queueJob( new SimilarArtistsInsertionJob( this, artist, suggestions ) ); +} + +void +CollectionDB::aftCheckPermanentTables( const QString &currdeviceid, const QString &currid, const QString &currurl ) +{ + //DEBUG_BLOCK + //debug() << "deviceid = " << currdeviceid << endl << "url = " << currurl << endl << "uid = " << currid << endl; + + QStringList check1, check2; + + foreach( m_aftEnabledPersistentTables ) + { + //debug() << "Checking " << (*it) << endl;; + check1 = query( QString( + "SELECT url, deviceid " + "FROM %1 " + "WHERE uniqueid = '%2';" ) + .arg( escapeString( *it ) ) + .arg( currid ) ); + + check2 = query( QString( + "SELECT url, uniqueid " + "FROM %1 " + "WHERE deviceid = %2 AND url = '%3';" ) + .arg( escapeString( *it ) ) + .arg( currdeviceid + , currurl ) ); + + if( !check1.empty() ) + { + //debug() << "uniqueid found, updating url" << endl; + query( QString( "UPDATE %1 SET deviceid = %2, url = '%4' WHERE uniqueid = '%3';" ) + .arg( escapeString( *it ) ) + .arg( currdeviceid + , currid + , currurl ) ); + } + else if( !check2.empty() ) + { + //debug() << "url found, updating uniqueid" << endl; + query( QString( "UPDATE %1 SET uniqueid = '%2' WHERE deviceid = %3 AND url = '%4';" ) + .arg( escapeString( *it ) ) + .arg( currid + , currdeviceid + , currurl ) ); + } + } +} + +void +CollectionDB::aftMigratePermanentTablesUrl( const QString& /*oldUrl*/, const QString& newUrl, const QString& uniqueid ) +{ + //DEBUG_BLOCK + int deviceid = MountPointManager::instance()->getIdForUrl( newUrl ); + QString rpath = MountPointManager::instance()->getRelativePath( deviceid, newUrl ); + //NOTE: if ever do anything with "deleted" in the statistics table, set deleted to false in query + //below; will need special case. + //debug() << "deviceid = " << deviceid << endl << "newurl = " << newUrl << endl << "uid = " << uniqueid << endl; + foreach( m_aftEnabledPersistentTables ) + { + query( QString( "DELETE FROM %1 WHERE deviceid = %2 AND url = '%3';" ) + .arg( escapeString( *it ) ) + .arg( deviceid ) + .arg( escapeString( rpath ) ) ); + query( QString( "UPDATE %1 SET deviceid = %2, url = '%4' WHERE uniqueid = '%3';" ) + .arg( escapeString( *it ) ) + .arg( deviceid ) + .arg( escapeString( uniqueid ) ) + .arg( escapeString( rpath ) ) ); + } +} + +void +CollectionDB::aftMigratePermanentTablesUniqueId( const QString& /*url*/, const QString& oldid, const QString& newid ) +{ + //DEBUG_BLOCK + //debug() << "oldid = " << oldid << endl << "newid = " << newid << endl; + //NOTE: if ever do anything with "deleted" in the statistics table, set deleted to false in query + //below; will need special case. + foreach( m_aftEnabledPersistentTables ) + { + query( QString( "DELETE FROM %1 WHERE uniqueid = '%2';" ) + .arg( escapeString( *it ) ) + .arg( escapeString( newid ) ) ); + query( QString( "UPDATE %1 SET uniqueid = '%1' WHERE uniqueid = '%2';" ) + .arg( escapeString( *it ) ) + .arg( escapeString( newid ) ) + .arg( escapeString( oldid ) ) ); + } +} + + +////////////////////////////////////////////////////////////////////////////////////////// +// PRIVATE +////////////////////////////////////////////////////////////////////////////////////////// + +void +CollectionDB::initialize() +{ + DEBUG_BLOCK + + /// Create DBConfig instance: + +#ifdef USE_MYSQL + if ( m_dbConnType == DbConnection::mysql ) + { + QString appVersion = Amarok::config( "General Options" )->readEntry( "Version" ); + QString passwd = AmarokConfig::mySqlPassword2(); // stored as string type + + if( passwd.isEmpty() ) + { + if( appVersion.startsWith( "1.3" ) ) + { + /// This is because of the encrypted -> plaintext conversion + passwd = AmarokConfig::mySqlPassword(); // stored as password type + AmarokConfig::setMySqlPassword2( passwd ); + } + else if( appVersion.startsWith( "1.4" ) ) + { + passwd = Amarok::config( "MySql" )->readEntry( "MySqlPassword" ); //read the field as plaintext + AmarokConfig::setMySqlPassword2( passwd ); // store it in plaintext field + } + } + + m_dbConfig = new MySqlConfig( + AmarokConfig::mySqlHost(), + AmarokConfig::mySqlPort(), + AmarokConfig::mySqlDbName(), + AmarokConfig::mySqlUser(), + passwd ); + } + else +#endif +#ifdef USE_POSTGRESQL + if ( m_dbConnType == DbConnection::postgresql ) + { + QString appVersion = Amarok::config( "General Options" )->readEntry( "Version" ); + QString passwd = AmarokConfig::postgresqlPassword2(); + + if( passwd.isEmpty() ) + { + if( appVersion.startsWith( "1.3" ) ) + { + /// This is because of the encrypted -> plaintext conversion + passwd = AmarokConfig::postgresqlPassword(); // stored as password type + AmarokConfig::setPostgresqlPassword2( passwd ); + } + else if( appVersion.startsWith( "1.4" ) && + ( appVersion.contains( "beta", false ) || + appVersion.contains( "svn", false ) ) ) + { + passwd = Amarok::config( "Postgresql" )->readEntry( "PostgresqlPassword" ); + AmarokConfig::setPostgresqlPassword2( passwd ); + } + } + + m_dbConfig = new PostgresqlConfig( + AmarokConfig::postgresqlHost(), + AmarokConfig::postgresqlPort(), + AmarokConfig::postgresqlDbName(), + AmarokConfig::postgresqlUser(), + passwd ); + } + else +#endif + { + m_dbConfig = new SqliteConfig( + Amarok::config( "Sqlite" )->readPathEntry( "location", + Amarok::saveLocation() + "collection.db" ) ); + } + + DbConnection *dbConn = getMyConnection(); + + if ( !dbConn->isConnected() || !dbConn->isInitialized() ) + { + error() << "Failed to connect to or initialise database!" << endl; + Amarok::MessageQueue::instance()->addMessage( dbConn->lastError() ); + } + else + { + if ( !isValid() ) + { + //No tables seem to exist (as doing a count(url) didn't even return any number, even 0). + warning() << "Tables seem to not exist." << endl; + warning() << "Attempting to create tables (this should be safe; ignore any errors)..." << endl; + createTables(false); + createPersistentTables(); + createPodcastTables(); + createStatsTable(); + warning() << "Tables should now definitely exist. (Stop ignoring errors)" << endl; + + //Since we have created the tables, we need to make sure the version numbers are + //set to the correct values. If this is not done now, the database update code may + //run, which could corrupt things. + Amarok::config( "Collection Browser" )->writeEntry( "Database Version", DATABASE_VERSION ); + Amarok::config( "Collection Browser" )->writeEntry( "Database Stats Version", DATABASE_STATS_VERSION ); + Amarok::config( "Collection Browser" )->writeEntry( "Database Persistent Tables Version", DATABASE_PERSISTENT_TABLES_VERSION ); + Amarok::config( "Collection Browser" )->writeEntry( "Database Podcast Tables Version", DATABASE_PODCAST_TABLES_VERSION ); Amarok::config( "Collection Browser" )->writeEntry( "Database AFT Version", DATABASE_AFT_VERSION ); + + setAdminValue( "Database Version", QString::number( DATABASE_VERSION ) ); + setAdminValue( "Database Stats Version", QString::number( DATABASE_STATS_VERSION ) ); + setAdminValue( "Database Persistent Tables Version", QString::number( DATABASE_PERSISTENT_TABLES_VERSION ) ); + setAdminValue( "Database Podcast Tables Version", QString::number( DATABASE_PODCAST_TABLES_VERSION ) ); + setAdminValue( "Database AFT Version", QString::number( DATABASE_AFT_VERSION ) ); + } + + + // Due to a bug in our sqllite handling code, we have to recreate the indices. + // We should rmeove this before 1.4.5 + if ( m_dbConnType == DbConnection::sqlite ) { + QStringList indices = query( "SELECT name FROM sqlite_master WHERE type='index' ORDER BY name;" ); + if (!indices.contains("url_tag")) { + createIndices(); + } + } + + + //updates for the Devices table go here + //put all other update code into checkDatabase() + //make sure that there is no call to MountPointManager in CollectionDB's ctor + //or in methods called from the ctor. + if ( adminValue( "Database Devices Version" ).isEmpty() + && Amarok::config( "CollectionBrowser" )->readNumEntry( "Database Devices Version", 0 ) == 0 ) + { + createDevicesTable(); + } + else if ( adminValue( "Database Devices Version" ).toInt() != DATABASE_DEVICES_VERSION + || Amarok::config( "Collection Browser" )->readNumEntry( "Database Devices Version", 0 ) != DATABASE_DEVICES_VERSION ) + { + int prev = adminValue( "Database Devices Version" ).toInt(); + + if ( prev > DATABASE_DEVICES_VERSION || prev < 0 ) + { + error() << "Database devices version too new for this version of Amarok" << endl; + exit( 1 ); + //dropDevicesTable(); + } + else + { + debug() << "Updating DEVICES table" << endl; + //add future Devices update code here + } + } + Amarok::config( "Collection Browser" )->writeEntry( "Database Devices Version", DATABASE_DEVICES_VERSION ); + setAdminValue( "Database Devices Version", QString::number( DATABASE_DEVICES_VERSION ) ); + + //make sure that all indices exist + createIndices(); + createPermanentIndices(); + } + +} + +void +CollectionDB::checkDatabase() +{ + DEBUG_BLOCK + if ( isValid() ) + { + //Inform the user that he should attach as many devices with music as possible + //Hopefully this won't be necessary soon. + // + //Currently broken, so disabled - seems to cause crashes as events are sent to + //the Playlist - maybe it's not fully initialised? + /* + QString text = i18n( "Amarok has to update your database to be able to use the new Dynamic Collection(insert link) feature. Amarok now has to determine on which physical devices your collection is stored. Please attach all removable devices which contain part of your collection and continue. Cancelling will exit Amarok." ); + int result = KMessageBox::warningContinueCancel( 0, text, "Database migration" ); + if ( result != KMessageBox::Continue ) + { + error() << "Dynamic Collection migration was aborted by user...exiting" << endl; + exit( 1 ); + } + */ + + bool needsUpdate = ( adminValue( "Database Stats Version" ).toInt() != DATABASE_STATS_VERSION + || Amarok::config( "Collection Browser" )->readNumEntry( "Database Stats Version", 0 ) != DATABASE_STATS_VERSION + || Amarok::config( "Collection Browser" )->readNumEntry( "Database Version", 0 ) != DATABASE_VERSION + || adminValue( "Database Version" ).toInt() != DATABASE_VERSION + || Amarok::config( "Collection Browser" )->readNumEntry( "Database Persistent Tables Version", 0 ) != DATABASE_PERSISTENT_TABLES_VERSION + || adminValue( "Database Persistent Tables Version" ).toInt() != DATABASE_PERSISTENT_TABLES_VERSION + || Amarok::config( "Collection Browser" )->readNumEntry( "Database Podcast Tables Version", 0 ) != DATABASE_PODCAST_TABLES_VERSION + || adminValue( "Database Podcast Tables Version" ).toInt() != DATABASE_PODCAST_TABLES_VERSION + || Amarok::config( "Collection Browser" )->readNumEntry( "Database AFT Version", 0 ) != DATABASE_AFT_VERSION + || adminValue( "Database AFT Version" ).toInt() != DATABASE_AFT_VERSION ); + + if ( needsUpdate ) + { + + KDialogBase *dialog = new KDialogBase( KDialogBase::Swallow, + Qt::WType_TopLevel|Qt::WStyle_Customize|Qt::WStyle_DialogBorder, + 0, + "Update database warning dialog", + false, + i18n( "Updating database" ), + 0 ); + /* TODO: remove the standard window controls from the dialog window, the user should not be able + to close, minimize, maximize the dialog + add additional text, e.g. Amarok is currently updating your database. This may take a while. + Please wait. + + Consider using a ProgressBarDialog + */ + QLabel *label = new QLabel( i18n( "Updating database" ), dialog ); + dialog->setMainWidget( label ); + label->show(); + QTimer::singleShot( 0, dialog, SLOT( show() ) ); + //process events in the main event loop so that the dialog actually gets shown + kapp->processEvents(); + debug() << "Beginning database update" << endl; + + updateStatsTables(); + + updatePersistentTables(); + + updatePodcastTables(); + + //This is really a one-off call that fixes a Collection Browser glitch + updateGroupBy(); + + //remove database file if version is incompatible + if ( Amarok::config( "Collection Browser" )->readNumEntry( "Database Version", 0 ) != DATABASE_VERSION + || adminValue( "Database Version" ).toInt() != DATABASE_VERSION ) + { + debug() << "Rebuilding database!" << endl; + dropTables(false); + createTables(false); + } + delete dialog; + } + emit databaseUpdateDone(); + } + + // TODO Should write to config in dtor, but it crashes... + Amarok::config( "Collection Browser" )->writeEntry( "Database Version", DATABASE_VERSION ); + Amarok::config( "Collection Browser" )->writeEntry( "Database Stats Version", DATABASE_STATS_VERSION ); + Amarok::config( "Collection Browser" )->writeEntry( "Database Persistent Tables Version", DATABASE_PERSISTENT_TABLES_VERSION ); + Amarok::config( "Collection Browser" )->writeEntry( "Database Podcast Tables Version", DATABASE_PODCAST_TABLES_VERSION ); + Amarok::config( "Collection Browser" )->writeEntry( "Database AFT Version", DATABASE_AFT_VERSION ); + + setAdminValue( "Database Version", QString::number( DATABASE_VERSION ) ); + setAdminValue( "Database Stats Version", QString::number( DATABASE_STATS_VERSION ) ); + setAdminValue( "Database Persistent Tables Version", QString::number( DATABASE_PERSISTENT_TABLES_VERSION ) ); + setAdminValue( "Database Podcast Tables Version", QString::number( DATABASE_PODCAST_TABLES_VERSION ) ); + setAdminValue( "Database AFT Version", QString::number( DATABASE_AFT_VERSION ) ); + + initDirOperations(); +} + +void +CollectionDB::updateGroupBy() +{ + //This ugly bit of code makes sure the Group BY setting is preserved, after the + //meanings of the values were changed due to the addition of the Composer table. + int version = adminValue( "Database Version" ).toInt(); + if (!version) // an even older update + version = Amarok::config( "Collection Browser" )->readNumEntry( "Database Version", 0 ); + + if ( version && version < 32 ) + { + KConfig* config = Amarok::config( "Collection Browser" ); + int m_cat1 = config->readNumEntry( "Category1" ); + int m_cat2 = config->readNumEntry( "Category2" ); + int m_cat3 = config->readNumEntry( "Category3" ); + m_cat1 = m_cat1 ? ( m_cat1 > 2 ? m_cat1 << 1 : m_cat1 ) : CollectionBrowserIds::IdArtist; + m_cat2 = m_cat2 ? ( m_cat2 > 2 ? m_cat2 << 1 : m_cat2 ) : CollectionBrowserIds::IdAlbum; + m_cat3 = m_cat3 ? ( m_cat3 > 2 ? m_cat3 << 1 : m_cat3 ) : CollectionBrowserIds::IdNone; + config->writeEntry( "Category1", m_cat1 ); + config->writeEntry( "Category2", m_cat2 ); + config->writeEntry( "Category3", m_cat3 ); + } +} + +void +CollectionDB::updateStatsTables() +{ + if ( adminValue( "Database Stats Version" ).toInt() != DATABASE_STATS_VERSION + || Amarok::config( "Collection Browser" )->readNumEntry( "Database Stats Version", 0 ) != DATABASE_STATS_VERSION ) + { + debug() << "Different database stats version detected! Stats table will be updated or rebuilt." << endl; + + #if 0 // causes mysterious crashes + if( getType() == DbConnection::sqlite && QFile::exists( Amarok::saveLocation()+"collection.db" ) ) + { + debug() << "Creating a backup of the database in " + << Amarok::saveLocation()+"collection-backup.db" << '.' << endl; + + bool copied = KIO::NetAccess::file_copy( Amarok::saveLocation()+"collection.db", + Amarok::saveLocation()+"collection-backup.db", + -1 /*perms*/, true /*overwrite*/, false /*resume*/ ); + + if( !copied ) + { + debug() << "Backup failed! Perhaps the volume is not writable." << endl; + debug() << "Error was: " << KIO::NetAccess::lastErrorString() << endl; + } + } + #endif + + int prev = adminValue( "Database Stats Version" ).toInt(); + + /* If config returns 3 or lower, it came from an Amarok version that was not aware of + admin table, so we can't trust this table at all */ + if( !prev || ( Amarok::config( "Collection Browser" )->readNumEntry( "Database Stats Version", 0 ) + && Amarok::config( "Collection Browser" )->readNumEntry( "Database Stats Version", 0 ) <= 3 ) ) + prev = Amarok::config( "Collection Browser" )->readNumEntry( "Database Stats Version", 0 ); + + //pre somewhere in the 1.3-1.4 timeframe, the version wasn't stored in the DB, so try to guess it + const QString q = "SELECT COUNT( %1 ) FROM statistics;"; + if( !prev && query( q.arg( "url" ) ).first().toInt() + && query( q.arg( "createdate" ) ).first().toInt() + && query( q.arg( "accessdate" ) ).first().toInt() + && query( q.arg( "percentage" ) ).first().toInt() + && query( q.arg( "playcounter" ) ).first().toInt() ) + { + prev = 3; + } + + if ( prev < 3 ) //it is from before 1.2, or our poor user is otherwise fucked + { + debug() << "Rebuilding stats-database!" << endl; + dropStatsTableV1(); + createStatsTable(); + } + else //Incrementally update the stats table to reach the present version + { + if( prev < 4 ) //every version from 1.2 forward had a stats version of 3 + { + debug() << "Updating stats-database!" << endl; + query( "ALTER TABLE statistics ADD rating INTEGER DEFAULT 0;" ); + query( "CREATE INDEX rating_stats ON statistics( rating );" ); + query( "UPDATE statistics SET rating=0 WHERE " + boolT() + ';' ); + } + if( prev < 5 ) + { + debug() << "Updating stats-database!" << endl; + query( "UPDATE statistics SET rating = rating * 2;" ); + } + if( prev < 8 ) //Versions 6, 7 and 8 all were all attempts to add columns for ATF. his code should do it all. + { + query( QString( "CREATE TABLE statistics_fix (" + "url " + textColumnType() + " UNIQUE," + "createdate INTEGER," + "accessdate INTEGER," + "percentage FLOAT," + "rating INTEGER DEFAULT 0," + "playcounter INTEGER);" ) ); + + insert( "INSERT INTO statistics_fix (url, createdate, accessdate, percentage, playcounter, rating)" + "SELECT url, createdate, accessdate, percentage, playcounter, rating FROM statistics;" + , NULL ); + + dropStatsTableV1(); + createStatsTableV8(); + + insert( "INSERT INTO statistics (url, createdate, accessdate, percentage, playcounter, rating)" + "SELECT url, createdate, accessdate, percentage, playcounter, rating FROM statistics_fix;" + , NULL ); + query( "DROP TABLE statistics_fix" ); + } + if( prev < 9 ) + { + //Update for Dynamic Collection: + + //This is not technically for the stats table, but it is part of the same + //update, so put it here anyway. + MountPointManager::instance()->setCollectionFolders( Amarok::config( "Collection" )->readPathListEntry( "Collection Folders" ) ); + + query( "ALTER TABLE statistics ADD deviceid INTEGER;" ); + + //FIXME: (max) i know this is bad but its fast + QStringList oldURLs = query( "SELECT url FROM statistics;" ); + //it might be necessary to use batch updates to improve speed + debug() << "Updating " << oldURLs.count() << " rows in statistics" << endl; + foreach( oldURLs ) + { + bool exists = QFile::exists( *it ); + int deviceid = exists ? MountPointManager::instance()->getIdForUrl( *it ) : -2; + QString rpath = exists ? MountPointManager::instance()->getRelativePath( deviceid, *it ) : *it; + QString update = QString( "UPDATE statistics SET deviceid = %1, url = '%2' WHERE " ) + .arg( deviceid ) + .arg( escapeString( rpath ) ); + update += QString( "url = '%1';" ).arg( escapeString( *it ) ); + query ( update ); + } + } + if ( prev < 12 ) + { + //re-using old method cause just a slight change to one column... + //if people are upgrading from earlier than 11, just get the new column + //earlier :-) + createStatsTableV10( true ); + query( "INSERT INTO statistics_fix_ten SELECT url,deviceid,createdate," + "accessdate,percentage,rating,playcounter,uniqueid,deleted FROM " + "statistics;" ); + dropStatsTableV1(); + createStatsTableV10( false ); + query( "INSERT INTO statistics SELECT * FROM statistics_fix_ten;" ); + query( "UPDATE statistics SET uniqueid=NULL;" ); + } + else if( prev > DATABASE_STATS_VERSION ) + { + error() << "Database statistics version too new for this version of Amarok. Quitting..." << endl; + exit( 1 ); + } + } + } +} + +void +CollectionDB::updatePersistentTables() +{ + QString PersistentVersion = adminValue( "Database Persistent Tables Version" ); + if ( PersistentVersion.isEmpty() ) + { + /* persistent tables didn't have a version on 1.3X and older, but let's be nice and try to + copy/keep the good information instead of just deleting the tables */ + debug() << "Detected old schema for tables with important data. Amarok will convert the tables, ignore any \"table already exists\" errors." << endl; + createPersistentTables(); + /* Copy lyrics */ + debug() << "Trying to get lyrics from old db schema." << endl; + QStringList Lyrics = query( "SELECT url, lyrics FROM tags where tags.lyrics IS NOT NULL;" ); + for (uint i=0; igetIdForUrl( *it ); + QString rpath = MountPointManager::instance()->getRelativePath( deviceid, *it ); + QString update = QString( "UPDATE lyrics SET deviceid = %1, url = '%2' WHERE " ) + .arg( deviceid ) + .arg( escapeString( rpath ) ); + update += QString( "url = '%1';" ).arg( escapeString( *it ) ); + query ( update ); + } + } + if ( PersistentVersion.toInt() < 15 ) + { + createPersistentTablesV14( true ); + query( "INSERT INTO amazon_fix SELECT asin,locale,filename,refetchdate FROM amazon;" ); + query( "INSERT INTO lyrics_fix SELECT url,deviceid,lyrics FROM lyrics;" ); + query( "INSERT INTO playlists_fix SELECT playlist,url,tracknum FROM playlists;" ); + dropPersistentTablesV14(); + createPersistentTablesV14( false ); + query( "INSERT INTO amazon SELECT * FROM amazon_fix;" ); + query( "INSERT INTO lyrics SELECT * FROM lyrics_fix;" ); + query( "INSERT INTO playlists SELECT * FROM playlists_fix;" ); + } + if ( PersistentVersion.toInt() < 17 ) + { + //drop old labels and label tables, they were never used anyway and just confuse things + query( "DROP TABLE label;" ); + query( "DROP TABLE labels;" ); + query( "DROP TABLE tags_labels;" ); + //update for label support + QString labelsAutoIncrement = ""; + if ( getDbConnectionType() == DbConnection::postgresql ) + { + query( QString( "CREATE SEQUENCE labels_seq;" ) ); + + labelsAutoIncrement = QString("DEFAULT nextval('labels_seq')"); + } + else if ( getDbConnectionType() == DbConnection::mysql ) + { + labelsAutoIncrement = "AUTO_INCREMENT"; + } + + query( QString( "CREATE TABLE labels (" + "id INTEGER PRIMARY KEY " + labelsAutoIncrement + ", " + "name " + textColumnType() + ", " + "type INTEGER);" ) ); + + query( QString( "CREATE TABLE tags_labels (" + "deviceid INTEGER," + "url " + exactTextColumnType() + ", " + "uniqueid " + exactTextColumnType(32) + ", " //m:n relationship, DO NOT MAKE UNIQUE! + "labelid INTEGER REFERENCES labels( id ) ON DELETE CASCADE );" ) ); + + query( "CREATE UNIQUE INDEX labels_name ON labels( name, type );" ); + query( "CREATE INDEX tags_labels_uniqueid ON tags_labels( uniqueid );" ); //m:n relationship, DO NOT MAKE UNIQUE! + query( "CREATE INDEX tags_labels_url ON tags_labels( url, deviceid );" ); //m:n relationship, DO NOT MAKE UNIQUE! + } + if ( PersistentVersion.toInt() < 18 ) + { + query( "ALTER TABLE lyrics ADD uniqueid " + exactTextColumnType(32) + ';' ); + query( "CREATE INDEX lyrics_uniqueid ON lyrics( uniqueid );" ); + } + if ( PersistentVersion.toInt() < 19 ) + { + query( "CREATE INDEX tags_labels_labelid ON tags_labels( labelid );" ); //m:n relationship, DO NOT MAKE UNIQUE! + } + //Up to date. Keep this number \/ in sync! + if ( PersistentVersion.toInt() > 19 || PersistentVersion.toInt() < 0 ) + { + //Something is horribly wrong + if ( adminValue( "Database Persistent Tables Version" ).toInt() != DATABASE_PERSISTENT_TABLES_VERSION ) + { + error() << "There is a bug in Amarok: instead of destroying your valuable" + << " database tables, I'm quitting" << endl; + exit( 1 ); + + debug() << "Rebuilding persistent tables database!" << endl; + dropPersistentTables(); + createPersistentTables(); + } + } + } +} + +void +CollectionDB::updatePodcastTables() +{ + QString PodcastVersion = adminValue( "Database Podcast Tables Version" ); + if ( PodcastVersion.toInt() < 2 ) + { + createPodcastTablesV2( true ); + query( "INSERT INTO podcastchannels_fix SELECT url,title,weblink,image,comment," + "copyright,parent,directory,autoscan,fetchtype,autotransfer,haspurge," + "purgecount FROM podcastchannels;" ); + query( "INSERT INTO podcastepisodes_fix SELECT id,url,localurl,parent,guid,title," + "subtitle,composer,comment,filetype,createdate,length,size,isNew FROM " + "podcastepisodes;" ); + query( "INSERT INTO podcastfolders_fix SELECT id,name,parent,isOpen FROM podcastfolders;" ); + dropPodcastTablesV2(); + createPodcastTablesV2( false ); + query( "INSERT INTO podcastchannels SELECT * FROM podcastchannels_fix;" ); + query( "INSERT INTO podcastepisodes SELECT * FROM podcastepisodes_fix;" ); + query( "INSERT INTO podcastfolders SELECT * FROM podcastfolders_fix;" ); + } + + //Keep this number in sync \/ + if ( PodcastVersion.toInt() > 2 ) + { + error() << "Something is very wrong with the Podcast Tables. Aborting" << endl; + exit( 1 ); + dropPodcastTables(); + createPodcastTables(); + } +} + +void +CollectionDB::vacuum() +{ + if ( DbConnection::sqlite == getDbConnectionType() || + DbConnection::postgresql == getDbConnectionType() ) + { + //Clean up DB and free unused space. + debug() << "Running VACUUM" << endl; + query( "VACUUM;" ); + } +} + +void +CollectionDB::destroy() +{ + //do we need or want to delete the actual connection objects as well as clearing them from the QMap? + //or does QMap's clear function delete them? + //this situation is not at all likely to happen often, so leaving them might be okay to prevent a + //thread from having its connection torn out from under it...not likely, but possible + //and leaving it should not end up eating much memory at all + + connectionMutex->lock(); + + threadConnections->clear(); + delete m_dbConfig; + + connectionMutex->unlock(); +} + +void +CollectionDB::scanModifiedDirs() +{ + if ( !m_scanInProgress + && ( !CollectionView::instance() || !CollectionView::instance()->isOrganizingFiles() ) + && ( !MediaBrowser::instance() || !MediaBrowser::instance()->isTranscoding() ) ) + { + //we check if a job is pending because we don't want to abort incremental collection readings + if ( !ThreadManager::instance()->isJobPending( "CollectionScanner" ) && PlaylistBrowser::instance() ) + { + m_scanInProgress = true; + m_rescanRequired = false; + emit scanStarted(); + + ThreadManager::instance()->onlyOneJob( new ScanController( this, true ) ); + } + } + else + m_rescanRequired = true; +} + + +void +CollectionDB::customEvent( QCustomEvent *e ) +{ + if ( e->type() == (int)ScanController::JobFinishedEvent ) + { + ScanController* s = static_cast( e ); + m_scanInProgress = false; + + if ( s->isIncremental() ) + { + debug() << "JobFinishedEvent from Incremental ScanController received.\n"; + emit scanDone( s->hasChanged() ); + + // check if something changed while we were scanning. in this case we should + // rescan again, now. + if ( m_rescanRequired ) + QTimer::singleShot( 0, CollectionDB::instance(), SLOT( scanMonitor() ) ); + } + else + { + debug() << "JobFinishedEvent from ScanController received.\n"; + emit scanDone( s->wasSuccessful() ); + } + } +} + + +QString +CollectionDB::loadHashFile( const QCString& hash, uint width ) +{ + //debug() << "loadHashFile: " << hash << " - " << width << endl; + + QString full = tagCoverDir().filePath( hash ); + + if ( width == 0 ) { + if ( QFileInfo( full ).isReadable() ) { + //debug() << "loadHashFile: fullsize: " << full << endl; + return full; + } + } else { + if ( width == 1 ) width = AmarokConfig::coverPreviewSize(); + QCString widthKey = makeWidthKey( width ); + + QString path = cacheCoverDir().filePath( widthKey + hash ); + if ( QFileInfo( path ).isReadable() ) { + //debug() << "loadHashFile: scaled: " << path << endl; + return path; + } else if ( QFileInfo( full ).isReadable() ) { + //debug() << "loadHashFile: scaling: " << full << endl; + QImage image( full ); + if ( image.smoothScale( width, width, QImage::ScaleMin ).save( path, "PNG" ) ) { + //debug() << "loadHashFile: scaled: " << path << endl; + return path; + } + } + } + return QString(); +} + + +bool +CollectionDB::extractEmbeddedImage( const MetaBundle &trackInformation, QCString& hash ) +{ + //debug() << "extractEmbeddedImage: " << hash << " - " << trackInformation.url().path() << endl; + + MetaBundle::EmbeddedImageList images; + trackInformation.embeddedImages( images ); + foreachType ( MetaBundle::EmbeddedImageList, images ) { + if ( hash.isEmpty() || (*it).hash() == hash ) { + if ( (*it).save( tagCoverDir() ) ) { + //debug() << "extractEmbeddedImage: saved to " << tagCoverDir().path() << endl; + hash = (*it).hash(); + return true; + } + } + } + return false; +} + +QStringList +CollectionDB::getLabels( const QString &url, const uint type ) +{ + int deviceid = MountPointManager::instance()->getIdForUrl( url ); + QString rpath = MountPointManager::instance()->getRelativePath( deviceid, url ); + return query( QString( "SELECT labels.name FROM labels " + "LEFT JOIN tags_labels ON labels.id = tags_labels.labelid " + "WHERE labels.type = %1 AND tags_labels.deviceid = %2 AND tags_labels.url = '%3';" ) + .arg( type ).arg( deviceid ).arg( escapeString( rpath ) ) ); +} + +void +CollectionDB::cleanLabels() +{ + DEBUG_BLOCK + QStringList labelIds = query( "select labels.id " + "from labels left join tags_labels on labels.id = tags_labels.labelid " + "where tags_labels.labelid is NULL;" ); + if ( !labelIds.isEmpty() ) + { + QString ids; + foreach( labelIds ) + { + if ( !ids.isEmpty() ) + ids += ','; + ids += *it; + } + query( QString( "DELETE FROM labels " + "WHERE labels.id IN ( %1 );" ) + .arg( ids ) ); + } +} + +void +CollectionDB::setLabels( const QString &url, const QStringList &labels, const QString &uid, const uint type ) +{ + DEBUG_BLOCK + int deviceid = MountPointManager::instance()->getIdForUrl( url ); + QString rpath = escapeString( MountPointManager::instance()->getRelativePath( deviceid, url ) ); + QStringList labelIds = query( QString( "SELECT id FROM labels WHERE type = %1;" ).arg( type ) ); + QString ids; + if ( !labelIds.isEmpty() ) + { + foreach( labelIds ) + { + if ( !ids.isEmpty() ) + ids += ','; + ids += *it; + } + //TODO: max: add uniqueid handling + query( QString( "DELETE FROM tags_labels " + "WHERE tags_labels.labelid IN (%1) AND tags_labels.deviceid = %2 AND tags_labels.url = '%3';" ) + .arg( ids, QString::number(deviceid), rpath ) ); + } + + foreach( labels ) + { + int id = query( QString( "SELECT id FROM labels WHERE type = %1 AND name = '%2';" ) + .arg( type ).arg( escapeString( *it ) ) ).first().toInt(); + if ( !id ) + { + id = insert( QString( "INSERT INTO labels( name, type ) VALUES ( '%2', %1 );" ) + .arg( type ).arg( escapeString( *it ) ), "labels" ); + } + insert( QString( "INSERT INTO tags_labels( labelid, deviceid, url, uniqueid ) VALUES ( %1, %2, '%3', '%4' );" ) + .arg( QString::number(id), QString::number(deviceid), rpath, escapeString( uid ) ), 0 ); + } + + emit labelsChanged( url ); +} + +void +CollectionDB::removeLabels( const QString &url, const QStringList &labels, const uint type ) +{ + DEBUG_BLOCK + int deviceid = MountPointManager::instance()->getIdForUrl( url ); + QString rpath = escapeString( MountPointManager::instance()->getRelativePath( deviceid, url ) ); + QString sql = QString( "DELETE FROM tags_labels " + "FROM tags_labels AS t LEFT JOIN labels AS l ON t.labelid = l.id " + "WHERE l.type = %1 AND t.deviceid = %2 AND t.url = '%3' AND ( 0" ) + .arg( type ).arg( deviceid ).arg( rpath ); + foreach( labels ) + { + sql += QString( " OR l.name = '%1'" ).arg( escapeString( *it ) ); + } + sql += ");"; + query( sql ); + + emit labelsChanged( url ); +} + +bool +CollectionDB::addLabel( const QString &url, const QString &label, const QString &uid, const uint type ) +{ + DEBUG_BLOCK + int deviceid = MountPointManager::instance()->getIdForUrl( url ); + QString rpath = escapeString( MountPointManager::instance()->getRelativePath( deviceid, url ) ); + + int id = query( QString( "SELECT id FROM labels WHERE type = %1 AND name = '%2';" ) + .arg( type ).arg( escapeString( label ) ) ).first().toInt(); + bool labelAlreadyExists = id > 0; + if ( !id ) + { + id = insert( QString( "INSERT INTO labels( name, type ) VALUES ( '%2', %1 );" ) + .arg( type ).arg( escapeString( label ) ), "labels" ); + } + if ( labelAlreadyExists ) + { + //we can return if the link between the tags row and the labels row already exists + int count = query( QString( "SELECT COUNT(*) FROM tags_labels WHERE labelid = %1 AND deviceid = %2 AND url = '%3';" ) + .arg( id ).arg( deviceid ).arg( rpath ) ).first().toInt(); + if ( count ) + return false; + } + insert( QString( "INSERT INTO tags_labels( labelid, deviceid, url, uniqueid ) VALUES ( %1, %2, '%3', '%4' );" ) + .arg( QString::number(id), QString::number(deviceid), rpath, escapeString( uid ) ), "tags_labels" ); + + emit labelsChanged( url ); + return true; +} + +QStringList +CollectionDB::favoriteLabels( int type, int count ) +{ + return query( QString( "SELECT labels.name, count( tags_labels.labelid ) " + "FROM labels LEFT JOIN tags_labels ON labels.id = tags_labels.labelid " + "WHERE labels.type = %1 GROUP BY labels.name " + "ORDER BY count(tags_labels.labelid) DESC LIMIT %2;" ) + .arg( QString::number( type ), QString::number( count ) ) ); +} + +QDir +CollectionDB::largeCoverDir() //static +{ + return QDir( Amarok::saveLocation( "albumcovers/large/" ) ); +} + + +QDir +CollectionDB::tagCoverDir() //static +{ + return QDir( Amarok::saveLocation( "albumcovers/tagcover/" ) ); +} + + +QDir +CollectionDB::cacheCoverDir() //static +{ + return QDir( Amarok::saveLocation( "albumcovers/cache/" ) ); +} + + +////////////////////////////////////////////////////////////////////////////////////////// +// CLASS DbConnection +////////////////////////////////////////////////////////////////////////////////////////// + +DbConnection::DbConnection() + : m_initialized( false ) +{} + + +////////////////////////////////////////////////////////////////////////////////////////// +// CLASS SqliteConnection +////////////////////////////////////////////////////////////////////////////////////////// + +SqliteConnection::SqliteConnection( const SqliteConfig* config ) + : DbConnection() + , m_db( 0 ) +{ + + DEBUG_BLOCK + + const QCString path = QFile::encodeName( config->dbFile() ); + + // Open database file and check for correctness + QFile file( path ); + if ( file.open( IO_ReadOnly ) ) + { + QString format; + file.readLine( format, 50 ); + if ( !format.startsWith( "SQLite format 3" ) ) + { + warning() << "Database versions incompatible. Removing and rebuilding database.\n"; + } + else if ( sqlite3_open( path, &m_db ) != SQLITE_OK ) + { + warning() << "Database file corrupt. Removing and rebuilding database.\n"; + sqlite3_close( m_db ); + } + else + m_initialized = true; + } + + if ( !m_initialized ) + { + // Remove old db file; create new + QFile::remove( path ); + if ( sqlite3_open( path, &m_db ) == SQLITE_OK ) + { + m_initialized = true; + } + } + if ( m_initialized ) + { + if( sqlite3_create_function(m_db, "rand", 0, SQLITE_UTF8, NULL, sqlite_rand, NULL, NULL) != SQLITE_OK ) + m_initialized = false; + if( sqlite3_create_function(m_db, "power", 2, SQLITE_UTF8, NULL, sqlite_power, NULL, NULL) != SQLITE_OK ) + m_initialized = false; + if ( sqlite3_create_function(m_db, "like", 2, SQLITE_UTF8, NULL, sqlite_like_new, NULL, NULL) != SQLITE_OK ) + m_initialized = false; + if ( sqlite3_create_function(m_db, "like", 3, SQLITE_UTF8, NULL, sqlite_like_new, NULL, NULL) != SQLITE_OK ) + m_initialized = false; + } + + //optimization for speeding up SQLite + query( "PRAGMA default_synchronous = OFF;" ); +} + + +SqliteConnection::~SqliteConnection() +{ + if ( m_db ) sqlite3_close( m_db ); +} + + +QStringList SqliteConnection::query( const QString& statement, bool /*suppressDebug*/ ) +{ + + QStringList values; + int error; + int rc = 0; + const char* tail; + sqlite3_stmt* stmt; + int busyCnt = 0; + int retryCnt = 0; + + do { + //compile SQL program to virtual machine, reattempting if busy + do { + if ( busyCnt ) + { + ::usleep( 100000 ); // Sleep 100 msec + debug() << "sqlite3_prepare: BUSY counter: " << busyCnt << endl; + } + error = sqlite3_prepare( m_db, statement.utf8(), -1, &stmt, &tail ); + } + while ( SQLITE_BUSY==error && busyCnt++ < 120 ); + + if ( error != SQLITE_OK ) + { + if ( SQLITE_BUSY==error ) + Debug::error() << "Gave up waiting for lock to clear" << endl; + Debug::error() << k_funcinfo << " sqlite3_compile error:" << endl; + Debug::error() << sqlite3_errmsg( m_db ) << endl; + Debug::error() << "on query: " << statement << endl; + values = QStringList(); + break; + } + else + { + busyCnt = 0; + int number = sqlite3_column_count( stmt ); + //execute virtual machine by iterating over rows + while ( true ) + { + error = sqlite3_step( stmt ); + + if ( error == SQLITE_BUSY ) + { + if ( busyCnt++ > 120 ) { + Debug::error() << "Busy-counter has reached maximum. Aborting this sql statement!\n"; + break; + } + ::usleep( 100000 ); // Sleep 100 msec + debug() << "sqlite3_step: BUSY counter: " << busyCnt << endl; + continue; + } + if ( error == SQLITE_MISUSE ) + debug() << "sqlite3_step: MISUSE" << endl; + if ( error == SQLITE_DONE || error == SQLITE_ERROR ) + break; + + //iterate over columns + for ( int i = 0; i < number; i++ ) + { + values << QString::fromUtf8( reinterpret_cast( sqlite3_column_text( stmt, i ) ) ); + } + } + //deallocate vm resources + rc = sqlite3_finalize( stmt ); + + if ( error != SQLITE_DONE && rc != SQLITE_SCHEMA ) + { + Debug::error() << k_funcinfo << "sqlite_step error.\n"; + Debug::error() << sqlite3_errmsg( m_db ) << endl; + Debug::error() << "on query: " << statement << endl; + values = QStringList(); + } + if ( rc == SQLITE_SCHEMA ) + { + retryCnt++; + debug() << "SQLITE_SCHEMA error occurred on query: " << statement << endl; + if ( retryCnt < 10 ) + debug() << "Retrying now." << endl; + else + { + Debug::error() << "Retry-Count has reached maximum. Aborting this SQL statement!" << endl; + Debug::error() << "SQL statement: " << statement << endl; + values = QStringList(); + } + } + } + } + while ( rc == SQLITE_SCHEMA && retryCnt < 10 ); + + return values; +} + + +int SqliteConnection::insert( const QString& statement, const QString& /* table */ ) +{ + int error; + int rc = 0; + const char* tail; + sqlite3_stmt* stmt; + int busyCnt = 0; + int retryCnt = 0; + + do { + //compile SQL program to virtual machine, reattempting if busy + do { + if ( busyCnt ) + { + ::usleep( 100000 ); // Sleep 100 msec + debug() << "sqlite3_prepare: BUSY counter: " << busyCnt << endl; + } + error = sqlite3_prepare( m_db, statement.utf8(), -1, &stmt, &tail ); + } + while ( SQLITE_BUSY==error && busyCnt++ < 120 ); + + if ( error != SQLITE_OK ) + { + if ( SQLITE_BUSY==error ) + Debug::error() << "Gave up waiting for lock to clear" << endl; + Debug::error() << k_funcinfo << " sqlite3_compile error:" << endl; + Debug::error() << sqlite3_errmsg( m_db ) << endl; + Debug::error() << "on insert: " << statement << endl; + break; + } + else + { + busyCnt = 0; + //execute virtual machine by iterating over rows + while ( true ) + { + error = sqlite3_step( stmt ); + + if ( error == SQLITE_BUSY ) + { + if ( busyCnt++ > 120 ) { + Debug::error() << "Busy-counter has reached maximum. Aborting this sql statement!\n"; + break; + } + ::usleep( 100000 ); // Sleep 100 msec + debug() << "sqlite3_step: BUSY counter: " << busyCnt << endl; + } + if ( error == SQLITE_MISUSE ) + debug() << "sqlite3_step: MISUSE" << endl; + if ( error == SQLITE_DONE || error == SQLITE_ERROR ) + break; + } + //deallocate vm resources + rc = sqlite3_finalize( stmt ); + + if ( error != SQLITE_DONE && rc != SQLITE_SCHEMA) + { + Debug::error() << k_funcinfo << "sqlite_step error.\n"; + Debug::error() << sqlite3_errmsg( m_db ) << endl; + Debug::error() << "on insert: " << statement << endl; + } + if ( rc == SQLITE_SCHEMA ) + { + retryCnt++; + debug() << "SQLITE_SCHEMA error occurred on insert: " << statement << endl; + if ( retryCnt < 10 ) + debug() << "Retrying now." << endl; + else + { + Debug::error() << "Retry-Count has reached maximum. Aborting this SQL insert!" << endl; + Debug::error() << "SQL statement: " << statement << endl; + } + } + } + } + while ( SQLITE_SCHEMA == rc && retryCnt < 10 ); + return sqlite3_last_insert_rowid( m_db ); +} + + +// this implements a RAND() function compatible with the MySQL RAND() (0-param-form without seed) +void SqliteConnection::sqlite_rand(sqlite3_context *context, int /*argc*/, sqlite3_value ** /*argv*/) +{ + sqlite3_result_double( context, static_cast(KApplication::random()) / (RAND_MAX+1.0) ); +} + +// this implements a POWER() function compatible with the MySQL POWER() +void SqliteConnection::sqlite_power(sqlite3_context *context, int argc, sqlite3_value **argv) +{ + Q_ASSERT( argc==2 ); + if( sqlite3_value_type(argv[0])==SQLITE_NULL || sqlite3_value_type(argv[1])==SQLITE_NULL ) { + sqlite3_result_null(context); + return; + } + double a = sqlite3_value_double(argv[0]); + double b = sqlite3_value_double(argv[1]); + sqlite3_result_double( context, pow(a,b) ); +} + +// this implements a LIKE() function that overrides the default string comparison function +// Reason: default function is case-sensitive for utf8 strings (BUG: 116458, ...) +void SqliteConnection::sqlite_like_new( sqlite3_context *context, int argc, sqlite3_value **argv ) +{ + + const unsigned char *zA = sqlite3_value_text( argv[0] ); + const unsigned char *zB = sqlite3_value_text( argv[1] ); + + QString pattern = QString::fromUtf8( (const char*)zA ); + QString text = QString::fromUtf8( (const char*)zB ); + + int begin = pattern.startsWith( "%" ), end = pattern.endsWith( "%" ); + if (begin) + pattern = pattern.right( pattern.length() - 1 ); + if (end) + pattern = pattern.left( pattern.length() - 1 ); + + if( argc == 3 ) // The function is given an escape character. In likeCondition() it defaults to '/' + pattern.replace( "/%", "%" ).replace( "/_", "_" ).replace( "//", "/" ); + + int result = 0; + if ( begin && end ) result = ( text.find( pattern, 0, 0 ) != -1); + else if ( begin ) result = text.endsWith( pattern, 0 ); + else if ( end ) result = text.startsWith( pattern, 0 ); + else result = ( text.lower() == pattern.lower() ); + + sqlite3_result_int( context, result ); +} + +////////////////////////////////////////////////////////////////////////////////////////// +// CLASS MySqlConnection +////////////////////////////////////////////////////////////////////////////////////////// + +#ifdef USE_MYSQL +MySqlConnection::MySqlConnection( const MySqlConfig* config ) + : DbConnection() + , m_connected( false ) +{ + DEBUG_BLOCK + + debug() << k_funcinfo << endl; + m_db = mysql_init(NULL); + if (m_db) + { +// if ( config->username().isEmpty() ) +// pApp->slotConfigAmarok("MySql"); + + if ( mysql_real_connect( m_db, config->host().latin1(), + config->username().latin1(), + config->password().latin1(), + config->database().latin1(), + config->port(), + NULL, CLIENT_COMPRESS ) ) + { + m_initialized = true; + +#if MYSQL_VERSION_ID >= 40113 + // now set the right charset for the connection + QStringList my_qslist = query( "SHOW VARIABLES LIKE 'character_set_database'" ); + if( !my_qslist.isEmpty() && !mysql_set_character_set( m_db, const_cast( my_qslist[1].latin1() ) ) ) + //charset was updated + debug() << "Connection Charset is now: " << my_qslist[1].latin1() << endl; + else + error() << "Failed to set database charset\n"; +#endif + + m_db->reconnect = 1; //setting reconnect flag for newer mysqld + m_connected = true; + } + else + { + + if ( mysql_real_connect( + m_db, + config->host().latin1(), + config->username().latin1(), + config->password().latin1(), + NULL, + config->port(), + NULL, CLIENT_COMPRESS)) + { + if ( mysql_query(m_db, + QString( "CREATE DATABASE " + config->database() ).latin1() ) ) + { m_connected = true; m_initialized = true; } + else + { setMysqlError(); } + } + else + setMysqlError(); + + } + } + else + error() << "Failed to allocate/initialize MySql struct\n"; +} + + +MySqlConnection::~MySqlConnection() +{ + if ( m_db ) mysql_close( m_db ); +} + + +QStringList MySqlConnection::query( const QString& statement, bool suppressDebug ) +{ + QStringList values; + + if ( !mysql_query( m_db, statement.utf8() ) ) + { + MYSQL_RES* result; + if ( ( result = mysql_use_result( m_db ) ) ) + { + int number = mysql_field_count( m_db ); + MYSQL_ROW row; + while ( ( row = mysql_fetch_row( result ) ) ) + { + for ( int i = 0; i < number; i++ ) + { + values << QString::fromUtf8( (const char*)row[i] ); + } + } + } + else + { + if ( mysql_field_count( m_db ) != 0 ) + { + if ( !suppressDebug ) + debug() << "MYSQL QUERY FAILED: " << mysql_error( m_db ) << "\n" << "FAILED QUERY: " << statement << "\n"; + values = QStringList(); + } + } + mysql_free_result( result ); + } + else + { + if ( !suppressDebug ) + debug() << "MYSQL QUERY FAILED: " << mysql_error( m_db ) << "\n" << "FAILED QUERY: " << statement << "\n"; + values = QStringList(); + } + + return values; +} + + +int MySqlConnection::insert( const QString& statement, const QString& /* table */ ) +{ + mysql_query( m_db, statement.utf8() ); + if ( mysql_errno( m_db ) ) + { + debug() << "MYSQL INSERT FAILED: " << mysql_error( m_db ) << "\n" << "FAILED INSERT: " << statement << endl; + } + return mysql_insert_id( m_db ); +} + + +void +MySqlConnection::setMysqlError() +{ + m_error = i18n("MySQL reported the following error:
") + mysql_error(m_db) + + i18n("

You can configure MySQL in the Collection section under Settings->Configure Amarok

"); +} +#endif + + +////////////////////////////////////////////////////////////////////////////////////////// +// CLASS PostgresqlConnection +////////////////////////////////////////////////////////////////////////////////////////// + +#ifdef USE_POSTGRESQL +PostgresqlConnection::PostgresqlConnection( const PostgresqlConfig* config ) + : DbConnection() + , m_connected( false ) +{ + QString conninfo; + debug() << k_funcinfo << endl; + +// if ( config->username().isEmpty() ) +// pApp->slotConfigAmarok("Postgresql"); + + conninfo = "host='" + config->host() + + "' port=" + QString::number( config->port() ) + + " dbname='" + config->database() + + "' user='" + config->username() + + "' password='" + config->password() + '\''; + + m_db = PQconnectdb( conninfo.utf8() ); + + if (!m_db) + { + debug() << "POSTGRESQL CONNECT FAILED: " << PQerrorMessage( m_db ) << "\n"; + error() << "Failed to allocate/initialize Postgresql struct\n"; + setPostgresqlError(); + return; + } + + if (PQstatus(m_db) != CONNECTION_OK) + { + debug() << "POSTGRESQL CONNECT FAILED: " << PQerrorMessage( m_db ) << "\n"; + error() << "Failed to allocate/initialize Postgresql struct\n"; + setPostgresqlError(); + PQfinish(m_db); + m_db = NULL; + return; + } + + m_initialized = true; + m_connected = true; +} + + +PostgresqlConnection::~PostgresqlConnection() +{ + if ( m_db ) PQfinish( m_db ); +} + + +QStringList PostgresqlConnection::query( const QString& statement, bool suppressDebug ) +{ + QStringList values; + PGresult* result; + ExecStatusType status; + + result = PQexec(m_db, statement.utf8()); + if (result == NULL) + { + if ( !suppressDebug ) + debug() << "POSTGRESQL QUERY FAILED: " << PQerrorMessage( m_db ) << "\n" << "FAILED QUERY: " << statement << "\n"; + return values; + } + + status = PQresultStatus(result); + if ((status != PGRES_COMMAND_OK) && (status != PGRES_TUPLES_OK)) + { + if ( !suppressDebug ) + debug() << "POSTGRESQL QUERY FAILED: " << PQerrorMessage( m_db ) << "\n" << "FAILED QUERY: " << statement << "\n"; + PQclear(result); + return values; + } + + int cols = PQnfields( result ); + int rows = PQntuples( result ); + QMap discardCols; + for(int col=0; col< cols; col++) { + if (QString(PQfname(result, col)) == QString("__discard") || QString(PQfname(result, col)) == QString("__random")) + { + discardCols[col] = true; + } + } + + for(int row=0; row< rows; row++) + { + for(int col=0; col< cols; col++) + { + if (discardCols[col]) continue; + + values << QString::fromUtf8(PQgetvalue(result, row, col)); + } + } + + PQclear(result); + + return values; +} + + +int PostgresqlConnection::insert( const QString& statement, const QString& table ) +{ + PGresult* result; + ExecStatusType status; + QString curvalSql; + int id; + + result = PQexec(m_db, statement.utf8()); + if (result == NULL) + { + debug() << "POSTGRESQL INSERT FAILED: " << PQerrorMessage( m_db ) << "\n" << "FAILED SQL: " << statement << "\n"; + return 0; + } + + status = PQresultStatus(result); + if (status != PGRES_COMMAND_OK) + { + debug() << "POSTGRESQL INSERT FAILED: " << PQerrorMessage( m_db ) << "\n" << "FAILED SQL: " << statement << "\n"; + PQclear(result); + return 0; + } + PQclear(result); + + if (table == NULL) return 0; + + QString _table = table; + if (table.find("_temp") > 0) _table = table.left(table.find("_temp")); + + curvalSql = QString("SELECT currval('%1_seq');").arg(_table); + result = PQexec(m_db, curvalSql.utf8()); + if (result == NULL) + { + debug() << "POSTGRESQL INSERT FAILED: " << PQerrorMessage( m_db ) << "\n" << "FAILED SQL: " << curvalSql << "\n"; + return 0; + } + + status = PQresultStatus(result); + if (status != PGRES_TUPLES_OK) + { + debug() << "POSTGRESQL INSERT FAILED: " << PQerrorMessage( m_db ) << "\n" << "FAILED SQL: " << curvalSql << "\n"; + PQclear(result); + return 0; + } + + if ((PQnfields( result ) != 1) || (PQntuples( result ) != 1)) + { + debug() << "POSTGRESQL INSERT FAILED: " << PQerrorMessage( m_db ) << "\n" << "FAILED SQL: " << curvalSql << "\n"; + PQclear(result); + return 0; + } + + id = QString::fromUtf8(PQgetvalue(result, 0, 0)).toInt(); + PQclear(result); + + return id; +} + + +void PostgresqlConnection::setPostgresqlError() +{ + m_error = i18n("Postgresql reported the following error:
") + PQerrorMessage(m_db) + + i18n("

You can configure Postgresql in the Collection section under Settings->Configure Amarok

"); +} +#endif + + + +////////////////////////////////////////////////////////////////////////////////////////// +// CLASS SqliteConfig +////////////////////////////////////////////////////////////////////////////////////////// + +SqliteConfig::SqliteConfig( const QString& dbfile ) + : m_dbfile( dbfile ) +{} + + +////////////////////////////////////////////////////////////////////////////////////////// +// CLASS MySqlConfig +////////////////////////////////////////////////////////////////////////////////////////// + +MySqlConfig::MySqlConfig( + const QString& host, + const int port, + const QString& database, + const QString& username, + const QString& password ) + : m_host( host ), + m_port( port ), + m_database( database ), + m_username( username ), + m_password( password ) +{} + + +////////////////////////////////////////////////////////////////////////////////////////// +// CLASS PostgresqlConfig +////////////////////////////////////////////////////////////////////////////////////////// + +PostgresqlConfig::PostgresqlConfig( + const QString& host, + const int port, + const QString& database, + const QString& username, + const QString& password ) + : m_host( host ), + m_port( port ), + m_database( database ), + m_username( username ), + m_password( password ) +{} + +////////////////////////////////////////////////////////////////////////////////////////// +// CLASS QueryBuilder +////////////////////////////////////////////////////////////////////////////////////////// + +QueryBuilder::QueryBuilder() +{ + m_OR.push(false); + clear(); + // there are a few string members with a large number of appends. to + // avoid reallocations, pre-reserve 1024 bytes and try never to assign + // it, instead doing setLength(0) and appends + // Note: unfortunately, QT3's setLength(), which is also called from append, + // squeezes the string if it's less than 4x the length. So this is useless. + // Uncomment after porting to QT4 if it's smarter about this, as the docs say. +// m_query.reserve(1024); +// m_values.reserve(1024); +// m_tables.reserve(1024); +} + + +void +QueryBuilder::linkTables( int tables ) +{ + m_tables.setLength(0); + + m_tables += tableName( tabSong ); + + if ( !(tables & tabSong ) ) + { + // check if only one table is selected (does somebody know a better way to check that?) + if (tables == tabAlbum || tables==tabArtist || tables==tabGenre || tables == tabYear || tables == tabStats || tables == tabPodcastEpisodes || tables == tabPodcastFolders || tables == tabPodcastChannels || tables == tabLabels) { + m_tables.setLength( 0 ); + m_tables += tableName(tables); + } + else + tables |= tabSong; + } + + if ( tables & tabSong ) + { + if ( tables & tabAlbum ) + ((m_tables += " LEFT JOIN ") += tableName( tabAlbum)) += " ON album.id=tags.album"; + if ( tables & tabArtist ) + ((m_tables += " LEFT JOIN ") += tableName( tabArtist)) += " ON artist.id=tags.artist"; + if ( tables & tabComposer ) + ((m_tables += " LEFT JOIN ") += tableName( tabComposer)) += " ON composer.id=tags.composer"; + if ( tables & tabGenre ) + ((m_tables += " LEFT JOIN ") += tableName( tabGenre)) += " ON genre.id=tags.genre"; + if ( tables & tabYear ) + ((m_tables += " LEFT JOIN ") += tableName( tabYear)) += " ON year.id=tags.year"; + if ( tables & tabStats ) + { + ((m_tables += " LEFT JOIN ") += tableName( tabStats)) + += " ON statistics.url=tags.url AND statistics.deviceid = tags.deviceid"; + //if ( !m_url.isEmpty() ) { + // QString url = QString( '.' ) + m_url; + // m_tables += QString( " OR statistics.deviceid = -1 AND statistics.url = '%1'" ) + // .arg( CollectionDB::instance()->escapeString( url ) ); + //} + } + if ( tables & tabLyrics ) + ((m_tables += " LEFT JOIN ") += tableName( tabLyrics)) + += " ON lyrics.url=tags.url AND lyrics.deviceid = tags.deviceid"; + + if ( tables & tabDevices ) + ((m_tables += " LEFT JOIN ") += tableName( tabDevices )) += " ON tags.deviceid = devices.id"; + if ( tables & tabLabels ) + ( m_tables += " LEFT JOIN tags_labels ON tags.url = tags_labels.url AND tags.deviceid = tags_labels.deviceid" ) + += " LEFT JOIN labels ON tags_labels.labelid = labels.id"; + } +} + + +void +QueryBuilder::addReturnValue( int table, Q_INT64 value, bool caseSensitive /* = false, unless value refers to a string */ ) +{ + caseSensitive |= value == valName || value == valTitle || value == valComment; + + if ( !m_values.isEmpty() && m_values != "DISTINCT " ) m_values += ','; + + if ( value == valDummy ) + m_values += "''"; + else + { + if ( caseSensitive && CollectionDB::instance()->getType() == DbConnection::mysql ) + m_values += "BINARY "; + m_values += tableName( table ) + '.'; + m_values += valueName( value ); + } + + m_linkTables |= table; + m_returnValues++; + if ( value & valURL ) + { + // make handling of deviceid transparent to calling code + m_deviceidPos = m_returnValues + 1; //the return value after the url is the deviceid + m_values += ','; + m_values += tableName( table ); + m_values += '.'; + m_values += valueName( valDeviceId ); + } +} + +void +QueryBuilder::addReturnFunctionValue( int function, int table, Q_INT64 value) +{ + // translate NULL and 0 values into the default value for percentage/rating + // First translate 0 to NULL via NULLIF, then NULL to default via COALESCE + bool defaults = function == funcAvg && ( value & valScore || value & valRating ); + + if ( !m_values.isEmpty() && m_values != "DISTINCT " ) m_values += ','; + m_values += functionName( function ) + '('; + if ( defaults ) + m_values += "COALESCE(NULLIF("; + m_values += tableName( table ) + '.'; + m_values += valueName( value ); + if ( defaults ) + { + m_values += ", 0), "; + if ( value & valScore ) + m_values += "50"; + else + m_values += '6'; + m_values += ')'; + } + m_values += ") AS "; + m_values += functionName( function )+tableName( table )+valueName( value ); + + m_linkTables |= table; + if ( !m_showAll ) m_linkTables |= tabSong; + m_returnValues++; +} + +uint +QueryBuilder::countReturnValues() +{ + return m_returnValues; +} + +void +QueryBuilder::addURLFilters( const QStringList& filter ) +{ + if ( !filter.isEmpty() ) + { + m_where += ANDslashOR() + " ( " + CollectionDB::instance()->boolF() + ' '; + + for ( uint i = 0; i < filter.count(); i++ ) + { + int deviceid = MountPointManager::instance()->getIdForUrl( filter[i] ); + QString rpath = MountPointManager::instance()->getRelativePath( deviceid , filter[i] ); + m_where += "OR (tags.url = '" + CollectionDB::instance()->escapeString( rpath ) + "' "; + m_where += QString( "AND tags.deviceid = %1 ) " ).arg( QString::number( deviceid ) ); + //TODO MountPointManager fix this + } + + m_where += " ) "; + } + + m_linkTables |= tabSong; +} + +void +QueryBuilder::setGoogleFilter( int defaultTables, QString query ) +{ + //TODO MountPointManager fix google syntax + //no clue about what needs to be done atm + ParsedExpression parsed = ExpressionParser::parse( query ); + + for( uint i = 0, n = parsed.count(); i < n; ++i ) //check each part for matchiness + { + beginOR(); + for( uint ii = 0, nn = parsed[i].count(); ii < nn; ++ii ) + { + const expression_element &e = parsed[i][ii]; + QString s = e.text; + int mode; + switch( e.match ) + { + case expression_element::More: mode = modeGreater; break; + case expression_element::Less: mode = modeLess; break; + case expression_element::Contains: + default: mode = modeNormal; break; + } + bool exact = false; // enable for numeric values + + int table = -1; + Q_INT64 value = -1; + if( e.field == "artist" ) + table = tabArtist; + else if( e.field == "composer" ) + table = tabComposer; + else if( e.field == "album" ) + table = tabAlbum; + else if( e.field == "title" ) + table = tabSong; + else if( e.field == "genre" ) + table = tabGenre; + else if( e.field == "year" ) + { + table = tabYear; + value = valName; + exact = true; + } + else if( e.field == "score" ) + { + table = tabStats; + value = valScore; + exact = true; + } + else if( e.field == "rating" ) + { + table = tabStats; + value = valRating; + exact = true; + s = QString::number( int( s.toFloat() * 2 ) ); + } + else if( e.field == "directory" ) + { + table = tabSong; + value = valDirectory; + } + else if( e.field == "length" ) + { + table = tabSong; + value = valLength; + exact = true; + } + else if( e.field == "playcount" ) + { + table = tabStats; + value = valPlayCounter; + exact = true; + } + else if( e.field == "samplerate" ) + { + table = tabSong; + value = valSamplerate; + exact = true; + } + else if( e.field == "track" ) + { + table = tabSong; + value = valTrack; + exact = true; + } + else if( e.field == "disc" || e.field == "discnumber" ) + { + table = tabSong; + value = valDiscNumber; + exact = true; + } + else if( e.field == "size" || e.field == "filesize" ) + { + table = tabSong; + value = valFilesize; + exact = true; + if( s.lower().endsWith( "m" ) ) + s = QString::number( s.left( s.length()-1 ).toLong() * 1024 * 1024 ); + else if( s.lower().endsWith( "k" ) ) + s = QString::number( s.left( s.length()-1 ).toLong() * 1024 ); + } + else if( e.field == "filename" || e.field == "url" ) + { + table = tabSong; + value = valURL; + } + else if( e.field == "filetype" || e.field == "type" ) + { + table = tabSong; + value = valURL; + mode = modeEndMatch; + s.prepend( '.' ); + } + else if( e.field == "bitrate" ) + { + table = tabSong; + value = valBitrate; + exact = true; + } + else if( e.field == "comment" ) + { + table = tabSong; + value = valComment; + } + else if( e.field == "bpm" ) + { + table = tabSong; + value = valBPM; + exact = true; + } + else if( e.field == "lyrics" ) + { + table = tabLyrics; + value = valLyrics; + } + else if( e.field == "device" ) + { + table = tabDevices; + value = valDeviceLabel; + } + else if( e.field == "mountpoint" ) + { + table = tabDevices; + value = valMountPoint; + } + else if( e.field == "label" ) + { + table = tabLabels; + value = valName; + } + + if( e.negate ) + { + if( value >= 0 ) + excludeFilter( table, value, s, mode, exact ); + else + excludeFilter( table >= 0 ? table : defaultTables, s ); + } + else + { + if( value >= 0 ) + addFilter( table, value, s, mode, exact ); + else + addFilter( table >= 0 ? table : defaultTables, s ); + } + } + endOR(); + } +} + +void +QueryBuilder::addFilter( int tables, const QString& filter ) +{ + if ( !filter.isEmpty() ) + { + m_where += ANDslashOR() + " ( " + CollectionDB::instance()->boolF() + ' '; + + if ( tables & tabAlbum ) + m_where += "OR album.name " + CollectionDB::likeCondition( filter, true, true ); + if ( tables & tabArtist ) + m_where += "OR artist.name " + CollectionDB::likeCondition( filter, true, true ); + if ( tables & tabComposer ) + m_where += "OR composer.name " + CollectionDB::likeCondition( filter, true, true ); + if ( tables & tabGenre ) + m_where += "OR genre.name " + CollectionDB::likeCondition( filter, true, true ); + if ( tables & tabYear ) + m_where += "OR year.name " + CollectionDB::likeCondition( filter, false, false ); + if ( tables & tabSong ) + m_where += "OR tags.title " + CollectionDB::likeCondition( filter, true, true ); + if ( tables & tabLabels ) + m_where += "OR labels.name " + CollectionDB::likeCondition( filter, true, true ); + + if ( i18n( "Unknown" ).contains( filter, false ) ) + { + if ( tables & tabAlbum ) + m_where += "OR album.name = '' "; + if ( tables & tabArtist ) + m_where += "OR artist.name = '' "; + if ( tables & tabComposer ) + m_where += "OR composer.name = '' "; + if ( tables & tabGenre ) + m_where += "OR genre.name = '' "; + if ( tables & tabYear ) + m_where += "OR year.name = '' "; + if ( tables & tabSong ) + m_where += "OR tags.title = '' "; + } + if ( ( tables & tabArtist ) && i18n( "Various Artists" ).contains( filter, false ) ) + m_where += QString( "OR tags.sampler = %1 " ).arg( CollectionDB::instance()->boolT() ); + m_where += " ) "; + } + + m_linkTables |= tables; +} + +void +QueryBuilder::addFilter( int tables, Q_INT64 value, const QString& filter, int mode, bool exact ) +{ + //true for INTEGER fields (see comment of coalesceField(int, Q_INT64) + bool useCoalesce = coalesceField( tables, value ); + m_where += ANDslashOR() + " ( "; + + QString m, s; + if (mode == modeLess || mode == modeGreater) + { + QString escapedFilter; + if (useCoalesce && DbConnection::sqlite == CollectionDB::instance()->getDbConnectionType()) + escapedFilter = CollectionDB::instance()->escapeString( filter ); + else + escapedFilter = "'" + CollectionDB::instance()->escapeString( filter ) + "' "; + s = ( mode == modeLess ? "< " : "> " ) + escapedFilter; + } + else + { + if (exact) + if (useCoalesce && DbConnection::sqlite == CollectionDB::instance()->getDbConnectionType()) + s = " = " +CollectionDB::instance()->escapeString( filter ) + ' '; + else + s = " = '" + CollectionDB::instance()->escapeString( filter ) + "' "; + else + s = CollectionDB::likeCondition( filter, mode != modeBeginMatch, mode != modeEndMatch ); + } + + if( coalesceField( tables, value ) ) + m_where += QString( "COALESCE(%1.%2,0) " ).arg( tableName( tables ) ).arg( valueName( value ) ) + s; + else + m_where += QString( "%1.%2 " ).arg( tableName( tables ) ).arg( valueName( value ) ) + s; + + if ( !exact && (value & valName) && mode == modeNormal && i18n( "Unknown").contains( filter, false ) ) + m_where += QString( "OR %1.%2 = '' " ).arg( tableName( tables ) ).arg( valueName( value ) ); + + m_where += " ) "; + + m_linkTables |= tables; +} + +void +QueryBuilder::addFilters( int tables, const QStringList& filter ) +{ + if ( !filter.isEmpty() ) + { + m_where += ANDslashOR() + " ( " + CollectionDB::instance()->boolT() + ' '; + + for ( uint i = 0; i < filter.count(); i++ ) + { + m_where += ANDslashOR() + " ( " + CollectionDB::instance()->boolF() + ' '; + + if ( tables & tabAlbum ) + m_where += "OR album.name " + CollectionDB::likeCondition( filter[i], true, true ); + if ( tables & tabArtist ) + m_where += "OR artist.name " + CollectionDB::likeCondition( filter[i], true, true ); + if ( tables & tabComposer ) + m_where += "OR composer.name " + CollectionDB::likeCondition( filter[i], true, true ); + if ( tables & tabGenre ) + m_where += "OR genre.name " + CollectionDB::likeCondition( filter[i], true, true ); + if ( tables & tabYear ) + m_where += "OR year.name " + CollectionDB::likeCondition( filter[i], false, false ); + if ( tables & tabSong ) + m_where += "OR tags.title " + CollectionDB::likeCondition( filter[i], true, true ); + if ( tables & tabLabels ) + m_where += "OR labels.name " + CollectionDB::likeCondition( filter[i], true, true ); + + if ( i18n( "Unknown" ).contains( filter[i], false ) ) + { + if ( tables & tabAlbum ) + m_where += "OR album.name = '' "; + if ( tables & tabArtist ) + m_where += "OR artist.name = '' "; + if ( tables & tabComposer ) + m_where += "OR composer.name = '' "; + if ( tables & tabGenre ) + m_where += "OR genre.name = '' "; + if ( tables & tabYear ) + m_where += "OR year.name = '' "; + if ( tables & tabSong ) + m_where += "OR tags.title = '' "; + } + if ( i18n( "Various Artists" ).contains( filter[ i ], false ) && ( tables & tabArtist ) ) + m_where += "OR tags.sampler = " + CollectionDB::instance()->boolT() + ' '; + m_where += " ) "; + } + + m_where += " ) "; + } + + m_linkTables |= tables; +} + +void +QueryBuilder::excludeFilter( int tables, const QString& filter ) +{ + if ( !filter.isEmpty() ) + { + m_where += ANDslashOR() + " ( " + CollectionDB::instance()->boolT() + ' '; + + + if ( tables & tabAlbum ) + m_where += "AND album.name NOT " + CollectionDB::likeCondition( filter, true, true ); + if ( tables & tabArtist ) + m_where += "AND artist.name NOT " + CollectionDB::likeCondition( filter, true, true ); + if ( tables & tabComposer ) + m_where += "AND composer.name NOT " + CollectionDB::likeCondition( filter, true, true ); + if ( tables & tabGenre ) + m_where += "AND genre.name NOT " + CollectionDB::likeCondition( filter, true, true ); + if ( tables & tabYear ) + m_where += "AND year.name NOT " + CollectionDB::likeCondition( filter, false, false ); + if ( tables & tabSong ) + m_where += "AND tags.title NOT " + CollectionDB::likeCondition( filter, true, true ); + if ( tables & tabLabels ) + m_where += "AND labels.name NOT " + CollectionDB::likeCondition( filter, true, true ); + + if ( i18n( "Unknown" ).contains( filter, false ) ) + { + if ( tables & tabAlbum ) + m_where += "AND album.name <> '' "; + if ( tables & tabArtist ) + m_where += "AND artist.name <> '' "; + if ( tables & tabComposer ) + m_where += "AND composer.name <> '' "; + if ( tables & tabGenre ) + m_where += "AND genre.name <> '' "; + if ( tables & tabYear ) + m_where += "AND year.name <> '' "; + if ( tables & tabSong ) + m_where += "AND tags.title <> '' "; + } + + if ( i18n( "Various Artists" ).contains( filter, false ) && ( tables & tabArtist ) ) + m_where += "AND tags.sampler = " + CollectionDB::instance()->boolF() + ' '; + + + m_where += " ) "; + } + + m_linkTables |= tables; +} + +void +QueryBuilder::excludeFilter( int tables, Q_INT64 value, const QString& filter, int mode, bool exact ) +{ + m_where += ANDslashOR() + " ( "; + + QString m, s; + if (mode == modeLess || mode == modeGreater) + s = ( mode == modeLess ? ">= '" : "<= '" ) + CollectionDB::instance()->escapeString( filter ) + "' "; + else + { + if (exact) + { + bool isNumber; + filter.toInt( &isNumber ); + if (isNumber) + s = " <> " + CollectionDB::instance()->escapeString( filter ) + " "; + else + s = " <> '" + CollectionDB::instance()->escapeString( filter ) + "' "; + } + else + s = "NOT " + CollectionDB::instance()->likeCondition( filter, mode != modeBeginMatch, mode != modeEndMatch ) + ' '; + } + + if( coalesceField( tables, value ) ) + m_where += QString( "COALESCE(%1.%2,0) " ).arg( tableName( tables ) ).arg( valueName( value ) ) + s; + else + m_where += QString( "%1.%2 " ).arg( tableName( tables ) ).arg( valueName( value ) ) + s; + + if ( !exact && (value & valName) && mode == modeNormal && i18n( "Unknown").contains( filter, false ) ) + m_where += QString( "AND %1.%2 <> '' " ).arg( tableName( tables ) ).arg( valueName( value ) ); + + m_where += " ) "; + + m_linkTables |= tables; +} + +void +QueryBuilder::addMatch( int tables, const QString& match, bool interpretUnknown /* = true */, bool caseSensitive /* = true */ ) +{ + QString matchCondition = caseSensitive ? CollectionDB::exactCondition( match ) : CollectionDB::likeCondition( match ); + + (((m_where += ANDslashOR()) += " ( ") += CollectionDB::instance()->boolF()) += ' '; + if ( tables & tabAlbum ) + (m_where += "OR album.name ") += matchCondition; + if ( tables & tabArtist ) + (m_where += "OR artist.name ") += matchCondition; + if ( tables & tabComposer ) + (m_where += "OR composer.name ") += matchCondition; + if ( tables & tabGenre ) + (m_where += "OR genre.name ") += matchCondition; + if ( tables & tabYear ) + (m_where += "OR year.name ") += matchCondition; + if ( tables & tabSong ) + (m_where += "OR tags.title ") += matchCondition; + if ( tables & tabLabels ) + (m_where += "OR labels.name ") += matchCondition; + + static QString i18nUnknown = i18n("Unknown"); + + if ( interpretUnknown && match == i18nUnknown ) + { + if ( tables & tabAlbum ) m_where += "OR album.name = '' "; + if ( tables & tabArtist ) m_where += "OR artist.name = '' "; + if ( tables & tabComposer ) m_where += "OR composer.name = '' "; + if ( tables & tabGenre ) m_where += "OR genre.name = '' "; + if ( tables & tabYear ) m_where += "OR year.name = '' "; + } + if ( tables & tabLabels && match.isEmpty() ) + m_where += " OR labels.name IS NULL "; + m_where += " ) "; + + m_linkTables |= tables; +} + + +void +QueryBuilder::addMatch( int tables, Q_INT64 value, const QString& match, bool interpretUnknown /* = true */, bool caseSensitive /* = true */ ) +{ + m_where += ANDslashOR() + " ( " + CollectionDB::instance()->boolF() + ' '; + if ( value & valURL ) + m_url = match; + //FIXME max: doesn't work yet if we are querying for the mount point part of a directory + if ( value & valURL || value & valDirectory ) + { + int deviceid = MountPointManager::instance()->getIdForUrl( match ); + QString rpath = MountPointManager::instance()->getRelativePath( deviceid, match ); + //we are querying for a specific path, so we don't need the tags.deviceid IN (...) stuff + //which is automatially appended if m_showAll = false + m_showAll = true; + m_where += QString( "OR %1.%2 " ) + .arg( tableName( tables ) ) + .arg( valueName( value ) ); + m_where += caseSensitive ? CollectionDB::exactCondition( rpath ) : CollectionDB::likeCondition( rpath ); + m_where += QString( " AND %1.deviceid = %2 " ).arg( tableName( tables ) ).arg( deviceid ); + if ( deviceid != -1 ) + { + //handle corner case + QString rpath2( '.' + match ); + m_where += QString( " OR %1.%2 " ).arg( tableName( tables ) ).arg( valueName( value ) ); + m_where += caseSensitive ? CollectionDB::exactCondition( rpath2 ) : CollectionDB::likeCondition( rpath2 ); + m_where += QString( " AND %1.deviceid = -1 " ).arg( tableName( tables ) ); + } + } + else + { + m_where += QString( "OR %1.%2 " ).arg( tableName( tables ) ).arg( valueName( value ) ); + m_where += caseSensitive ? CollectionDB::exactCondition( match ) : CollectionDB::likeCondition( match ); + } + + if ( ( value & valName ) && interpretUnknown && match == i18n( "Unknown" ) ) + m_where += QString( "OR %1.%2 = '' " ).arg( tableName( tables ) ).arg( valueName( value ) ); + + m_where += " ) "; + + m_linkTables |= tables; +} + + +void +QueryBuilder::addMatches( int tables, const QStringList& match, bool interpretUnknown /* = true */, bool caseSensitive /* = true */ ) +{ + QStringList matchConditions; + for ( uint i = 0; i < match.count(); i++ ) + matchConditions << ( caseSensitive ? CollectionDB::exactCondition( match[i] ) : CollectionDB::likeCondition( match[i] ) ); + + m_where += ANDslashOR() + " ( " + CollectionDB::instance()->boolF() + ' '; + + for ( uint i = 0; i < match.count(); i++ ) + { + if ( tables & tabAlbum ) + m_where += "OR album.name " + matchConditions[ i ]; + if ( tables & tabArtist ) + m_where += "OR artist.name " + matchConditions[ i ]; + if ( tables & tabComposer ) + m_where += "OR composer.name " + matchConditions[ i ]; + if ( tables & tabGenre ) + m_where += "OR genre.name " + matchConditions[ i ]; + if ( tables & tabYear ) + m_where += "OR year.name " + matchConditions[ i ]; + if ( tables & tabSong ) + m_where += "OR tags.title " + matchConditions[ i ]; + if ( tables & tabStats ) + m_where += "OR statistics.url " + matchConditions[ i ]; + if ( tables & tabLabels ) + (m_where += "OR labels.name ") += matchConditions[ i ]; + + + if ( interpretUnknown && match[i] == i18n( "Unknown" ) ) + { + if ( tables & tabAlbum ) m_where += "OR album.name = '' "; + if ( tables & tabArtist ) m_where += "OR artist.name = '' "; + if ( tables & tabComposer ) m_where += "OR composer.name = '' "; + if ( tables & tabGenre ) m_where += "OR genre.name = '' "; + if ( tables & tabYear ) m_where += "OR year.name = '' "; + } + if ( tables & tabLabels && match[i].isEmpty() ) + m_where += " OR labels.name IS NULL "; + } + + m_where += " ) "; + m_linkTables |= tables; +} + +void +QueryBuilder::excludeMatch( int tables, const QString& match ) +{ + m_where += ANDslashOR() + " ( " + CollectionDB::instance()->boolT() + ' '; + if ( tables & tabAlbum ) m_where += "AND album.name <> '" + CollectionDB::instance()->escapeString( match ) + "' "; + if ( tables & tabArtist ) m_where += "AND artist.name <> '" + CollectionDB::instance()->escapeString( match ) + "' "; + if ( tables & tabComposer ) m_where += "AND composer.name <> '" + CollectionDB::instance()->escapeString( match ) + "' "; + if ( tables & tabGenre ) m_where += "AND genre.name <> '" + CollectionDB::instance()->escapeString( match ) + "' "; + if ( tables & tabYear ) m_where += "AND year.name <> '" + CollectionDB::instance()->escapeString( match ) + "' "; + if ( tables & tabSong ) m_where += "AND tags.title <> '" + CollectionDB::instance()->escapeString( match ) + "' "; + if ( tables & tabLabels ) m_where += "AND labels.name <> '" + CollectionDB::instance()->escapeString( match ) + "' "; + + if ( match == i18n( "Unknown" ) ) + { + if ( tables & tabAlbum ) m_where += "AND album.name <> '' "; + if ( tables & tabArtist ) m_where += "AND artist.name <> '' "; + if ( tables & tabComposer ) m_where += "AND composer.name <> '' "; + if ( tables & tabGenre ) m_where += "AND genre.name <> '' "; + if ( tables & tabYear ) m_where += "AND year.name <> '' "; + } + m_where += " ) "; + + m_linkTables |= tables; +} + + +void +QueryBuilder::exclusiveFilter( int tableMatching, int tableNotMatching, Q_INT64 value ) +{ + m_where += " AND "; + m_where += tableName( tableNotMatching ) + '.'; + m_where += valueName( value ); + m_where += " IS null "; + + m_linkTables |= tableMatching; + m_linkTables |= tableNotMatching; +} + + +void +QueryBuilder::addNumericFilter(int tables, Q_INT64 value, const QString &n, + int mode /* = modeNormal */, + const QString &endRange /* = QString::null */ ) +{ + m_where.append( ANDslashOR() ).append( " ( " ); + + if ( coalesceField( tables, value) ) + m_where.append("COALESCE("); + + m_where.append( tableName( tables ) ).append( '.' ).append( valueName( value ) ); + + if ( coalesceField( tables, value) ) + m_where.append(",0)"); + + switch (mode) { + case modeNormal: + m_where.append( " = " ); break; + case modeLess: + m_where.append( " < " ); break; + case modeGreater: + m_where.append( " > " ); break; + case modeBetween: + m_where.append( " BETWEEN " ); break; + case modeNotBetween: + m_where.append(" NOT BETWEEN "); break; + default: + qWarning( "Unhandled mode in addNumericFilter, using equals: %d", mode ); + m_where.append( " = " ); + } + + m_where.append( n ); + if ( mode == modeBetween || mode == modeNotBetween ) + m_where.append( " AND " ).append( endRange ); + + m_where.append( " ) " ); + m_linkTables |= tables; +} + + + +void +QueryBuilder::setOptions( int options ) +{ + if ( options & optNoCompilations || options & optOnlyCompilations ) + m_linkTables |= tabSong; + + if ( options & optNoCompilations ) m_where += QString("AND tags.sampler = %1 ").arg(CollectionDB::instance()->boolF()); + if ( options & optOnlyCompilations ) m_where += QString("AND tags.sampler = %1 ").arg(CollectionDB::instance()->boolT()); + + if (CollectionDB::instance()->getType() == DbConnection::postgresql && options & optRemoveDuplicates && options & optRandomize) + { + m_values = "DISTINCT " + CollectionDB::instance()->randomFunc() + " AS __random "+ m_values; + if ( !m_sort.isEmpty() ) + m_sort += ','; + m_sort += CollectionDB::instance()->randomFunc() + ' '; + } + else + { + if ( options & optRemoveDuplicates ) + m_values = "DISTINCT " + m_values; + if ( options & optRandomize ) + { + if ( !m_sort.isEmpty() ) m_sort += ','; + m_sort += CollectionDB::instance()->randomFunc() + ' '; + } + } + + if ( options & optShowAll ) m_showAll = true; +} + + +void +QueryBuilder::sortBy( int table, Q_INT64 value, bool descending ) +{ + //shall we sort case-sensitively? (not for integer columns!) + bool b = true; + if ( value & valID || value & valTrack || value & valScore || value & valRating || value & valLength || value & valBitrate || + value & valSamplerate || value & valPlayCounter || value & valAccessDate || value & valCreateDate || + value & valFilesize || value & valDiscNumber || + table & tabYear ) + b = false; + + // only coalesce for certain columns + bool c = false; + if ( value & valScore || value & valRating || value & valPlayCounter || value & valAccessDate || value & valCreateDate ) + c = true; + + if ( !m_sort.isEmpty() ) m_sort += ','; + if ( b ) m_sort += "LOWER( "; + if ( c ) m_sort += "COALESCE( "; + + m_sort += tableName( table ) + '.'; + m_sort += valueName( value ); + + if ( c ) m_sort += ", 0 )"; + + if ( b ) m_sort += " ) "; + if ( descending ) m_sort += " DESC "; + + if (CollectionDB::instance()->getType() == DbConnection::postgresql) + { + if (!m_values.isEmpty()) m_values += ','; + if ( b ) m_values += "LOWER( "; + m_values += tableName( table ) + '.'; + m_values += valueName( value ); + if ( b ) m_values += ')'; + m_values += " as __discard "; + } + + m_linkTables |= table; +} + +void +QueryBuilder::sortByFunction( int function, int table, Q_INT64 value, bool descending ) +{ + // This function should be used with the equivalent addReturnFunctionValue (with the same function on same values) + // since it uses the "func(table.value) AS functablevalue" definition. + + // this column is already coalesced, but need to reconstruct for postgres + bool defaults = function == funcAvg && ( value & valScore || value & valRating ); + + //shall we sort case-sensitively? (not for integer columns!) + bool b = true; + if ( value & valID || value & valTrack || value & valScore || value & valRating || value & valLength || value & valBitrate || + value & valSamplerate || value & valPlayCounter || value & valAccessDate || value & valCreateDate || + value & valFilesize || value & valDiscNumber || + table & tabYear ) + b = false; + + // only coalesce for certain columns + bool c = false; + if ( !defaults && ( value & valScore || value & valRating || value & valPlayCounter || value & valAccessDate || value & valCreateDate ) ) + c = true; + + if ( !m_sort.isEmpty() ) m_sort += ','; + //m_sort += functionName( function ) + '('; + if ( b ) m_sort += "LOWER( "; + if ( c && CollectionDB::instance()->getType() != DbConnection::mysql) m_sort += "COALESCE( "; + + QString columnName; + + if (CollectionDB::instance()->getType() == DbConnection::postgresql) + { + columnName = functionName( function ) + '('; + if ( defaults ) + columnName += "COALESCE(NULLIF("; + columnName += tableName( table )+'.'+valueName( value ); + if ( defaults ) + { + columnName += ", 0), "; + if ( value & valScore ) + columnName += "50"; + else + columnName += '6'; + columnName += ')'; + } + columnName += ')'; + } + else + columnName = functionName( function )+tableName( table )+valueName( value ); + + m_sort += columnName; + + if ( c && CollectionDB::instance()->getType() != DbConnection::mysql) m_sort += ", 0 )"; + + if ( b ) m_sort += " ) "; + //m_sort += " ) "; + if ( descending ) m_sort += " DESC "; + + m_linkTables |= table; +} + +void +QueryBuilder::groupBy( int table, Q_INT64 value ) +{ + if ( !m_group.isEmpty() ) m_group += ','; + + //Do case-sensitive comparisons for MySQL too. See also QueryBuilder::addReturnValue + if ( DbConnection::mysql == CollectionDB::instance()->getDbConnectionType() && + ( value == valName || value == valTitle || value == valComment ) ) + { + m_group += "BINARY "; + } + + m_group += tableName( table ) + '.'; + m_group += valueName( value ); + + m_linkTables |= table; +} + +void +QueryBuilder::having( int table, Q_INT64 value, int function, int mode, const QString& match ) +{ + if( !m_having.isEmpty() ) m_having += " AND "; + + QString fn = functionName( function ); + fn.isEmpty() ? + m_having += tableName( table ) + '.' + valueName( value ) : + m_having += functionName( function )+'('+tableName( table )+'.'+valueName( value )+')'; + + switch( mode ) + { + case modeNormal: + m_having += '=' + match; + break; + + case modeLess: + m_having += '<' + match; + break; + + case modeGreater: + m_having += '>' + match; + + default: + break; + } +} + +void +QueryBuilder::setLimit( int startPos, int length ) +{ + m_limit = QString( " LIMIT %2 OFFSET %1 " ).arg( startPos ).arg( length ); +} + +void +QueryBuilder::shuffle( int table, Q_INT64 value ) +{ + if ( !m_sort.isEmpty() ) m_sort += " , "; + if ( table == 0 || value == 0 ) { + // simple random + m_sort += CollectionDB::instance()->randomFunc(); + } else { + // This is the score weighted random order. + + // The RAND() function returns random values equally distributed between 0.0 + // (inclusive) and 1.0 (exclusive). The obvious way to get this order is to + // put every track times into a list, sort the list by RAND() + // (i.e. shuffle it) and discard every occurrence of every track but the very + // first of each. By putting every track into the list only once but applying + // a transfer function T_s(x) := 1-(1-x)^(1/s) where s is the score, to RAND() + // before sorting the list, exactly the same distribution of tracks can be + // achieved (for a proof write to Stefan Siegel ) + + // In the query below a simplified function is used: The score is incremented + // by one to prevent division by zero, RAND() is used instead of 1-RAND() + // because it doesn't matter if it becomes zero (the exponent is always + // non-zero), and finally POWER(...) is used instead of 1-POWER(...) because it + // only changes the order type. + m_sort += QString("POWER( %1, 1.0 / (%2.%3 + 1) ) DESC") + .arg( CollectionDB::instance()->randomFunc() ) + .arg( tableName( table ) ) + .arg( valueName( value ) ); + + m_linkTables |= table; + } +} + + +/* NOTE: It's important to keep these two functions and the const in sync! */ +/* NOTE: It's just as important to keep tags.url first! */ +const int +QueryBuilder::dragFieldCount = 21; + +QString +QueryBuilder::dragSQLFields() +{ + return "tags.url, tags.deviceid, album.name, artist.name, composer.name, " + "genre.name, tags.title, year.name, " + "tags.comment, tags.track, tags.bitrate, tags.discnumber, " + "tags.length, tags.samplerate, tags.filesize, " + "tags.sampler, tags.filetype, tags.bpm, " + "statistics.percentage, statistics.rating, statistics.playcounter, " + "statistics.accessdate"; +} + +void +QueryBuilder::initSQLDrag() +{ + clear(); + addReturnValue( QueryBuilder::tabSong, QueryBuilder::valURL ); + addReturnValue( QueryBuilder::tabAlbum, QueryBuilder::valName ); + addReturnValue( QueryBuilder::tabArtist, QueryBuilder::valName ); + addReturnValue( QueryBuilder::tabComposer, QueryBuilder::valName ); + addReturnValue( QueryBuilder::tabGenre, QueryBuilder::valName ); + addReturnValue( QueryBuilder::tabSong, QueryBuilder::valTitle ); + addReturnValue( QueryBuilder::tabYear, QueryBuilder::valName ); + addReturnValue( QueryBuilder::tabSong, QueryBuilder::valComment ); + addReturnValue( QueryBuilder::tabSong, QueryBuilder::valTrack ); + addReturnValue( QueryBuilder::tabSong, QueryBuilder::valBitrate ); + addReturnValue( QueryBuilder::tabSong, QueryBuilder::valDiscNumber ); + addReturnValue( QueryBuilder::tabSong, QueryBuilder::valLength ); + addReturnValue( QueryBuilder::tabSong, QueryBuilder::valSamplerate ); + addReturnValue( QueryBuilder::tabSong, QueryBuilder::valFilesize ); + addReturnValue( QueryBuilder::tabSong, QueryBuilder::valIsCompilation ); + addReturnValue( QueryBuilder::tabSong, QueryBuilder::valFileType ); + addReturnValue( QueryBuilder::tabSong, QueryBuilder::valBPM ); + addReturnValue( QueryBuilder::tabStats, QueryBuilder::valScore ); + addReturnValue( QueryBuilder::tabStats, QueryBuilder::valRating ); + addReturnValue( QueryBuilder::tabStats, QueryBuilder::valPlayCounter ); + addReturnValue( QueryBuilder::tabStats, QueryBuilder::valAccessDate ); +} + + +void +QueryBuilder::buildQuery( bool withDeviceidPlaceholder ) +{ + if ( m_query.isEmpty() ) + { + linkTables( m_linkTables ); + m_query += "SELECT "; + m_query += m_values; + m_query += " FROM "; + m_query += m_tables; + m_query += ' '; + m_query += m_join; + m_query += " WHERE "; + m_query += CollectionDB::instance()->boolT(); + m_query += ' '; + m_query += m_where; + if ( !m_showAll && ( m_linkTables & tabSong || m_tables.contains( tableName( tabSong) ) ) ) //Only stuff on mounted devices, unless you use optShowAll + { + if ( withDeviceidPlaceholder ) + m_query += "(*MountedDeviceSelection*)"; + else + { + IdList list = MountPointManager::instance()->getMountedDeviceIds(); + //debug() << "number of device ids " << list.count() << endl; + m_query += " AND tags.deviceid IN ("; + foreachType( IdList, list ) + { + if ( it != list.begin() ) m_query += ','; + m_query += QString::number( *it ); + } + m_query += ')'; + } + } + // GROUP BY must be before ORDER BY for sqlite + // HAVING must be between GROUP BY and ORDER BY + if ( !m_group.isEmpty() ) { m_query += " GROUP BY "; m_query += m_group; } + if ( !m_having.isEmpty() ) { m_query += " HAVING "; m_query += m_having; } + if ( !m_sort.isEmpty() ) { m_query += " ORDER BY "; m_query += m_sort; } + m_query += m_limit; + m_query += ';'; + } +} + +// get the builded SQL-Query (used in smartplaylisteditor soon) +QString +QueryBuilder::getQuery() +{ + if ( m_query.isEmpty()) + { + buildQuery(); + } + return m_query; +} + +QStringList +QueryBuilder::run() +{ + buildQuery(); + //debug() << m_query << endl; + QStringList rs = CollectionDB::instance()->query( m_query ); + //calling code is unaware of the dynamic collection implementation, it simply expects an URL + if( m_deviceidPos > 0 ) + return cleanURL( rs ); + else + return rs; +} + + +void +QueryBuilder::clear() +{ + m_query.setLength(0); + m_values.setLength(0); + m_tables.setLength(0); + m_join.setLength(0); + m_where.setLength(0); + m_sort.setLength(0); + m_group.setLength(0); + m_limit.setLength(0); + m_having.setLength(0); + + m_linkTables = 0; + m_returnValues = 0; + + m_showAll = false; + m_deviceidPos = 0; +} + + +Q_INT64 +QueryBuilder::valForFavoriteSorting() { + Q_INT64 favSortBy = valRating; + if ( !AmarokConfig::useScores() && !AmarokConfig::useRatings() ) + favSortBy = valPlayCounter; + else if( !AmarokConfig::useRatings() ) + favSortBy = valScore; + return favSortBy; +} + +void +QueryBuilder::sortByFavorite() { + if ( AmarokConfig::useRatings() ) + sortBy(tabStats, valRating, true ); + if ( AmarokConfig::useScores() ) + sortBy(tabStats, valScore, true ); + sortBy(tabStats, valPlayCounter, true ); + +} + +void +QueryBuilder::sortByFavoriteAvg() { + // Due to MySQL4 weirdness, we need to add the function we're using to sort + // as return values as well. + if ( AmarokConfig::useRatings() ) { + sortByFunction(funcAvg, tabStats, valRating, true ); + addReturnFunctionValue( funcAvg, tabStats, valRating ); + } + if ( AmarokConfig::useScores() ) { + sortByFunction(funcAvg, tabStats, valScore, true ); + addReturnFunctionValue( funcAvg, tabStats, valScore ); + } + sortByFunction(funcAvg, tabStats, valPlayCounter, true ); + addReturnFunctionValue( funcAvg, tabStats, valPlayCounter ); + + //exclude unrated and unplayed + if( !m_having.isEmpty() ) + m_having += " AND "; + m_having += " ("; + if (AmarokConfig::useRatings() ) + m_having += QString("%1(%2.%3) > 0 OR ") + .arg( functionName( funcAvg ), tableName(tabStats), valueName(valRating) ); + m_having += QString("%1(%2.%3) > 0") + .arg( functionName( funcAvg ), tableName(tabStats), valueName(valPlayCounter) ); + m_having += ")"; +} + +// Helper method -- given a value, returns the index of the bit that is +// set, if only one, otherwise returns -1 +// Binsearch seems appropriate since the values enum has 40 members +template +static inline int +searchBit( ValueType value, int numBits ) { + int low = 0, high = numBits - 1; + while( low <= high ) { + int mid = (low + high) / 2; + ValueType compare = static_cast( 1 ) << mid; + if ( value == compare ) return mid; + else if ( value < compare ) high = mid - 1; + else low = mid + 1; + } + + return -1; +} + +QString +QueryBuilder::tableName( int table ) +{ + // optimize for 1 table which is by far the most frequent case + static const char tabNames[][16] = { + "album", + "artist", + "composer", + "genre", + "year", + "", // 32 is missing from the enum + "tags", + "statistics", + "lyrics", + "podcastchannels", + "podcastepisodes", + "podcasttables", + "devices", + "labels" + }; + + int oneBit = searchBit( table, sizeof( tabNames ) / sizeof( QString ) ); + if ( oneBit >= 0 ) return tabNames[oneBit]; + + // slow path: multiple tables. This seems to be unneeded at the moment, + // but leaving it here since it appears to be intended usage + QString tables; + + if ( CollectionDB::instance()->getType() != DbConnection::postgresql ) + { + if ( table & tabSong ) tables += ",tags"; + } + if ( table & tabArtist ) tables += ",artist"; + if ( table & tabComposer ) tables += ",composer"; + if ( table & tabAlbum ) tables += ",album"; + if ( table & tabGenre ) tables += ",genre"; + if ( table & tabYear ) tables += ",year"; + if ( table & tabStats ) tables += ",statistics"; + if ( table & tabLyrics ) tables += ",lyrics"; + if ( table & tabPodcastChannels ) tables += ",podcastchannels"; + if ( table & tabPodcastEpisodes ) tables += ",podcastepisodes"; + if ( table & tabPodcastFolders ) tables += ",podcasttables"; + if ( CollectionDB::instance()->getType() == DbConnection::postgresql ) + { + if ( table & tabSong ) tables += ",tags"; + } + + if ( table & tabDevices ) tables += ",devices"; + if ( table & tabLabels ) tables += ",labels"; + // when there are multiple tables involved, we always need table tags for linking them + return tables.mid( 1 ); +} + + +const QString & +QueryBuilder::valueName( Q_INT64 value ) +{ + static const QString values[] = { + "id", + "name", + "url", + "title", + "track", + "percentage", + "comment", + "bitrate", + "length", + "samplerate", + "playcounter", + "createdate", + "accessdate", + "percentage", + "artist", + "album", + "year", + "genre", + "dir", + "lyrics", + "rating", + "composer", + "discnumber", + "filesize", + "filetype", + "sampler", + "bpm", + "copyright", + "parent", + "weblink", + "autoscan", + "fetchtype", + "autotransfer", + "haspurge", + "purgeCount", + "isNew", + "deviceid", + "url", + "label", + "lastmountpoint", + "type" + }; + + int oneBit = searchBit( value, sizeof( values ) / sizeof( QString ) ); + if ( oneBit >= 0 ) return values[oneBit]; + + static const QString error( "" ); + return error; +} + +/* + * Return true if we should call COALESCE(..,0) for this DB field + * (field names sourced from the old smartplaylistbrowser.cpp code) + * Warning: addFilter( int, Q_INT64, const QString&, int bool ) + * expects this method to return true for all statistics table clomuns of type INTEGER + * Sqlite doesn't like comparing strings to an INTEGER column. + */ +bool +QueryBuilder::coalesceField( int table, Q_INT64 value ) +{ + if( tableName( table ) == "statistics" && + ( valueName( value ) == "playcounter" || + valueName( value ) == "rating" || + valueName( value ) == "percentage" || + valueName( value ) == "accessdate" || + valueName( value ) == "createdate" + ) + ) + return true; + return false; +} + +QString +QueryBuilder::functionName( int function ) +{ + QString functions; + + if ( function & funcCount ) functions += "Count"; + if ( function & funcMax ) functions += "Max"; + if ( function & funcMin ) functions += "Min"; + if ( function & funcAvg ) functions += "Avg"; + if ( function & funcSum ) functions += "Sum"; + + return functions; +} + +// FIXME: the two functions below are inefficient, but this patch is getting too +// big already. They are not on any critical path right now. Ovy +int +QueryBuilder::getTableByName(const QString &name) +{ + for ( int i = 1; i <= tabLabels; i <<= 1 ) + { + if (tableName(i) == name) return i; + } + return -1; +} + +Q_INT64 +QueryBuilder::getValueByName(const QString &name) +{ + for ( Q_INT64 i = 1; i <= valType; i <<= 1 ) { + if (valueName(i) == name) return i; + } + + return -1; +} + +bool +QueryBuilder::getField(const QString &tableValue, int *table, Q_INT64 *value) +{ + int dotIndex = tableValue.find( '.' ) ; + if ( dotIndex < 0 ) return false; + int tmpTable = getTableByName( tableValue.left(dotIndex) ); + Q_UINT64 tmpValue = getValueByName( tableValue.mid( dotIndex + 1 ) ); + if ( tmpTable >= 0 && value ) { + *table = tmpTable; + *value = tmpValue; + return true; + } + else + { + qFatal("invalid table.value: %s", tableValue.ascii()); + return false; + } +} + + + +QStringList +QueryBuilder::cleanURL( QStringList result ) +{ + //this method replaces the fields for relative path and devive/media id with a + //single field containing the absolute path for each row + //TODO Max improve this code + int count = 1; + for( QStringList::Iterator it = result.begin(), end = result.end(); it != end; ) + { + QString rpath; + if ( (count % (m_returnValues + 1)) + 1== m_deviceidPos ) + { + //this block is reached when the iterator points at the relative path + //deviceid is next + QString rpath = *it; + int deviceid = (*(++it)).toInt(); + QString abspath = MountPointManager::instance()->getAbsolutePath( deviceid, rpath ); + it = result.remove(--it); + result.insert( it, abspath ); + it = result.remove( it ); + //we advanced the iterator over two fields in this iteration + ++count; + } + else + ++it; + ++count; + } + return result; +} + + +#include "collectiondb.moc" diff --git a/amarok/src/collectiondb.h b/amarok/src/collectiondb.h new file mode 100644 index 00000000..a86fd2fa --- /dev/null +++ b/amarok/src/collectiondb.h @@ -0,0 +1,868 @@ +// (c) 2004 Mark Kretschmann +// (c) 2004 Christian Muehlhaeuser +// (c) 2004 Sami Nieminen +// (c) 2005 Ian Monroe +// (c) 2005 Jeff Mitchell +// (c) 2005 Isaiah Damron +// (c) 2005 Alexandre Pereira de Oliveira +// (c) 2006 Jonas Hurrelmann +// (c) 2006 Shane King +// (c) 2006 Peter C. Ndikuwera +// See COPYING file for licensing information. + +#ifndef AMAROK_COLLECTIONDB_H +#define AMAROK_COLLECTIONDB_H + +#include "engineobserver.h" +#include "threadmanager.h" //baseclass +#include "amarok_export.h" + +#include +#include //stack allocated +#include +#include +#include +#include //baseclass +#include //baseclass +#include //stack allocated +#include //stack allocated +#include +#include +#include + +namespace KIO { class Job; } + +class DbConnection; +class CoverFetcher; +class MetaBundle; +class OrganizeCollectionDialog; +class PodcastChannelBundle; +class PodcastEpisodeBundle; +class QListViewItem; +class Scrobbler; + +class DbConfig +{}; + + +class SqliteConfig : public DbConfig +{ + public: + SqliteConfig( const QString& /* dbfile */ ); + + QString dbFile() const { return m_dbfile; } + + private: + QString m_dbfile; +}; + + +class MySqlConfig : public DbConfig +{ + public: + MySqlConfig( + const QString& /* host */, + const int /* port */, + const QString& /* database */, + const QString& /* username */, + const QString& /* password */); + + QString host() const { return m_host; } + int port() const { return m_port; } + QString database() const { return m_database; } + QString username() const { return m_username; } + QString password() const { return m_password; } + + private: + QString m_host; + int m_port; + QString m_database; + QString m_username; + QString m_password; +}; + + +class PostgresqlConfig : public DbConfig +{ + public: + PostgresqlConfig( + const QString& /* host */, + const int /* port */, + const QString& /* database */, + const QString& /* username */, + const QString& /* password */); + + QString host() const { return m_host; } + int port() const { return m_port; } + QString database() const { return m_database; } + QString username() const { return m_username; } + QString password() const { return m_password; } + + private: + QString m_host; + int m_port; + QString m_database; + QString m_username; + QString m_password; +}; + + +class DbConnection +{ + public: + enum DbConnectionType { sqlite = 0, mysql = 1, postgresql = 2 }; + + DbConnection(); + virtual ~DbConnection() {} + + virtual QStringList query( const QString& /* statement */, bool suppressDebug ) = 0; + virtual int insert( const QString& /* statement */, const QString& /* table */ ) = 0; + bool isInitialized() const { return m_initialized; } + virtual bool isConnected() const = 0; + virtual QString lastError() const { return "None"; } + + protected: + bool m_initialized; +}; + + +typedef struct sqlite3 sqlite3; +typedef struct sqlite3_context sqlite3_context; +typedef struct Mem sqlite3_value; + +class SqliteConnection : public DbConnection +{ + public: + SqliteConnection( const SqliteConfig* /* config */ ); + ~SqliteConnection(); + + QStringList query( const QString& /* statement */, bool suppressDebug = false ); + int insert( const QString& /* statement */, const QString& /* table */ ); + bool isConnected()const { return true; } + private: + static void sqlite_rand( sqlite3_context *context, int /*argc*/, sqlite3_value ** /*argv*/ ); + static void sqlite_power( sqlite3_context *context, int argc, sqlite3_value **argv ); + static void sqlite_like_new( sqlite3_context *context, int argc, sqlite3_value **argv ); + + sqlite3* m_db; +}; + + +#ifdef USE_MYSQL +typedef struct st_mysql MYSQL; + +class MySqlConnection : public DbConnection +{ + public: + MySqlConnection( const MySqlConfig* /* config */ ); + ~MySqlConnection(); + + QStringList query( const QString& /* statement */, bool suppressDebug = false ); + int insert( const QString& /* statement */, const QString& /* table */ ); + bool isConnected()const { return m_connected; } + QString lastError() const { return m_error; } + private: + void setMysqlError(); + MYSQL* m_db; + bool m_connected; + QString m_error; +}; +#endif + + +#ifdef USE_POSTGRESQL +typedef struct pg_conn PGconn; + +class PostgresqlConnection : public DbConnection +{ + public: + PostgresqlConnection( const PostgresqlConfig* /* config */ ); + ~PostgresqlConnection(); + + QStringList query( const QString& /* statement */, bool suppressDebug = false ); + int insert( const QString& /* statement */, const QString& /* table */ ); + bool isConnected()const { return m_connected; } + QString lastError() const { return m_error; } + private: + void setPostgresqlError(); + PGconn* m_db; + bool m_connected; + QString m_error; +}; +#endif + + +class LIBAMAROK_EXPORT CollectionDB : public QObject, public EngineObserver +{ + Q_OBJECT + + friend class SimilarArtistsInsertionJob; + + signals: + void scanStarted(); + void scanDone( bool changed ); + void databaseEngineChanged(); + + void databaseUpdateDone(); + + void scoreChanged( const QString &url, float score ); + void ratingChanged( const QString &url, int rating ); + void labelsChanged( const QString &url ); + void fileMoved( const QString &srcUrl, const QString &dstUrl ); + void fileMoved( const QString &srcUrl, const QString &dstUrl, const QString &uniqueid ); + void fileDeleted( const QString &absPath ); + void fileDeleted( const QString &absPath, const QString &uniqueid ); + void fileAdded( const QString &absPath ); + void fileAdded( const QString &absPath, const QString &uniqueid ); + void filesAdded( const QMap &map ); + void uniqueIdChanged( const QString &url, const QString &originalid, const QString &newid ); + void coverChanged( const QString &artist, const QString &album ); //whenever a cover changes + void coverFetched( const QString &artist, const QString &album ); //only when fetching from amazon + void coverRemoved( const QString &artist, const QString &album ); + void coverFetcherError( const QString &error ); + + void similarArtistsFetched( const QString &artist ); + void tagsChanged( const MetaBundle &bundle ); + void tagsChanged( const QString &oldArtist, const QString &oldAlbum ); + void imageFetched( const QString &remoteURL ); //for fetching remote podcast images + + public: + CollectionDB(); + ~CollectionDB(); + + static CollectionDB *instance(); + + /** + * performs all initializations which require directory or URL data stored in the + * database. + */ + void initDirOperations(); + + enum labelTypes { typeUser = 1 }; //add new types add the end! + + QString escapeString(QString string ) const + { + return + #ifdef USE_MYSQL + // We have to escape "\" for mysql, but can't do so for sqlite + ( m_dbConnType == DbConnection::mysql ) + ? string.replace("\\", "\\\\").replace( '\'', "''" ) : + #endif + string.replace( '\'', "''" ); + } + + QString boolT() const { if (getDbConnectionType() == DbConnection::postgresql) return "true"; else return "1"; } + QString boolF() const { if (getDbConnectionType() == DbConnection::postgresql) return "false"; else return "0"; } + inline bool boolFromSql( const QString &b ) { return ( b == boolT() || b == "t" ); } + //textColumnType should be used for normal strings, which need to be compared + //either case-sensitively or -insensitively + QString textColumnType( int length=255 ) const { if ( getDbConnectionType() == DbConnection::postgresql ) return "TEXT"; else return QString("VARCHAR(%1)").arg(length); } + //exactTextColumnType should be used for strings that must be stored exactly, such + //as URLs (necessary for holding control chars etc. if present in URL), except for + //trailing spaces. Comparisions should always be done case-sensitively. + //As we create indices on these columns, we have to restrict them to + //<= 255 chars for mysql < 5.0.3 + QString exactTextColumnType( int length=1024 ) const { if ( getDbConnectionType() == DbConnection::mysql ) return QString( "VARBINARY(%1)" ).arg( length>255 ? 255 : length ); else return textColumnType( length ); } + // We might consider using LONGTEXT type, as some lyrics could be VERY long..??? + QString longTextColumnType() const { if ( getDbConnectionType() == DbConnection::postgresql ) return "TEXT"; else return "TEXT"; } + QString randomFunc() const { if ( getDbConnectionType() == DbConnection::postgresql ) return "random()"; else return "RAND()"; } + + inline static QString exactCondition( const QString &right ); + static QString likeCondition( const QString &right, bool anyBegin=false, bool anyEnd=false ); + int getType() { return getDbConnectionType(); } + + //sql helper methods + QStringList query( const QString& statement, bool suppressDebug = false ); + int insert( const QString& statement, const QString& table ); + + /** + * TODO: write doc + * @param showAll + * @return a string which can be appended to an existing sql where statement + */ + QString deviceidSelection( const bool showAll = false ); + + /** + * converts the result of a query which contains a deviceid and a relative path + * to a list of absolute paths. the order of entries in each result row must be + * deviceid first, relative path second. + * @param result the result of the sql query, deviceid first, relative path second + * @return a list of urls + */ + QStringList URLsFromQuery( const QStringList &result ) const; + + /** + * converts the result list of a amarok-sql query to a list of urls + */ + KURL::List URLsFromSqlDrag( const QStringList &values ) const; + + //table management methods + bool isEmpty(); + bool isValid(); + QString adminValue( QString noption ); + void setAdminValue( QString noption, QString value ); + void createTables( const bool temporary = false ); + void createIndices( ); + void createPermanentIndices(); + void dropTables( const bool temporary = false); + void clearTables( const bool temporary = false); + void copyTempTables( ); + void prepareTempTables(); + + uint artistID( QString value, bool autocreate = true, const bool temporary = false, bool exact = true ); + uint composerID( QString value, bool autocreate = true, const bool temporary = false, bool exact = true ); + uint albumID( QString value, bool autocreate = true, const bool temporary = false, bool exact = true ); + uint genreID( QString value, bool autocreate = true, const bool temporary = false, bool exact = true ); + uint yearID( QString value, bool autocreate = true, const bool temporary = false, bool exact = true ); + + bool isDirInCollection( QString path ); + bool isFileInCollection( const QString &url ); + QString getURL( const MetaBundle &bundle ); + void removeDirFromCollection( QString path ); + void removeSongsInDir( QString path, QMap *tagsRemoved = 0 ); + void removeSongs( const KURL::List& urls ); + void updateDirStats( QString path, const long datetime, const bool temporary = false ); + + //song methods + bool addSong( MetaBundle* bundle, const bool incremental = false ); + void aftCheckPermanentTables( const QString &currdeviceid, const QString &currid, const QString &currurl ); + void doAFTStuff( MetaBundle *bundle, const bool tempTables = true ); + void emitFileAdded( const QString &absPath, + const QString &uniqueid = QString::null ); + void emitFilesAdded( const QMap &map ) { emit filesAdded( map ); } + void emitFileDeleted( const QString &absPath, + const QString &uniqueid = QString::null ); + bool newUniqueIdForFile( const QString &path ); + bool removeUniqueIdFromFile( const QString &path ); + QString urlFromUniqueId( const QString &id ); + QString uniqueIdFromUrl( const KURL &url ); + + //podcast methods + /// Insert a podcast channel into the database. If @param replace is true, replace the row + /// use updatePodcastChannel() always in preference + bool addPodcastChannel( const PodcastChannelBundle &pcb, const bool &replace=false ); + /// Insert a podcast episode into the database. If @param idToUpdate is provided, replace the row + /// use updatePodcastEpisode() always in preference + int addPodcastEpisode( const PodcastEpisodeBundle &episode, const int idToUpdate=0 ); + int addPodcastFolder( const QString &name, const int parent_id=0, const bool isOpen=false ); + QValueList getPodcastChannels(); + PodcastEpisodeBundle getPodcastEpisodeById( int id ); + QValueList getPodcastEpisodes( const KURL &parent, bool newOnly=false, int limit=-1 ); + void removePodcastChannel( const KURL &url ); // will remove all episodes too + void removePodcastEpisode( const int id ); + void removePodcastFolder( const int id ); + void updatePodcastChannel( const PodcastChannelBundle &b ); + void updatePodcastEpisode( const int id, const PodcastEpisodeBundle &b ); + void updatePodcastFolder( const int folder_id, const QString &name, const int parent_id=0, const bool isOpen=false ); + // these return false when no bundle was available + bool getPodcastChannelBundle( const KURL &url, PodcastChannelBundle *channel ); + bool getPodcastEpisodeBundle( const KURL &url, PodcastEpisodeBundle *channel ); + + MetaBundle bundleFromQuery( QStringList::const_iterator *iter ); + /** + * The @p bundle parameter's url() will be looked up in the Collection + * @param bundle this will be filled in with tags for you + * @return true if in the collection + */ + bool bundleForUrl( MetaBundle* bundle ); + QValueList bundlesByUrls( const KURL::List& urls ); + void addAudioproperties( const MetaBundle& bundle ); + + //Helper function for updateTags + void deleteRedundantName( const QString &table, const QString &id ); + + void deleteAllRedundant( const QString &table ); + + void updateTags( const QString &url, const MetaBundle &bundle, const bool updateView = true); + void updateURL( const QString &url, const bool updateView = true ); + QString getUniqueId( const QString &url ); + + //statistics methods + void addSongPercentage( const QString &url, float percentage, + const QString &reason, const QDateTime *playtime = 0 ); + float getSongPercentage( const QString &url ); + int getSongRating( const QString &url ); + void setSongPercentage( const QString &url, float percentage ); + void setSongRating( const QString &url, int percentage, bool toggleHalf = false ); + int getPlayCount( const QString &url ); + QDateTime getFirstPlay( const QString &url ); + QDateTime getLastPlay( const QString &url ); + void migrateFile( const QString &oldURL, const QString &newURL ); + bool moveFile( const QString &src, const QString &dest, bool overwrite, bool copy = false ); + bool organizeFile( const KURL &src, const OrganizeCollectionDialog &dialog, bool copy ); + + //artist methods + QStringList similarArtists( const QString &artist, uint count ); + + //album methods + void checkCompilations( const QString &path, const bool temporary = false ); + void setCompilation( const KURL::List &urls, bool enabled, bool updateView ); + QString albumSongCount( const QString &artist_id, const QString &album_id ); + bool albumIsCompilation( const QString &album_id ); + void sanitizeCompilations(); + + //label methods + QStringList getLabels( const QString &url, const uint type ); + void removeLabels( const QString &url, const QStringList &labels, const uint type ); + bool addLabel( const QString &url, const QString &label, const QString &uid, const uint type ); + void setLabels( const QString &url, const QStringList &labels, const QString &uid, const uint type ); + + void cleanLabels(); + + QStringList favoriteLabels( int type = CollectionDB::typeUser, int count = 10 ); + + //list methods + QStringList artistList( bool withUnknowns = true, bool withCompilations = true ); + QStringList composerList( bool withUnknowns = true, bool withCompilations = true ); + QStringList albumList( bool withUnknowns = true, bool withCompilations = true ); + QStringList genreList( bool withUnknowns = true, bool withCompilations = true ); + QStringList yearList( bool withUnknowns = true, bool withCompilations = true ); + QStringList labelList(); + + QStringList albumListOfArtist( const QString &artist, bool withUnknown = true, bool withCompilations = true ); + QStringList artistAlbumList( bool withUnknown = true, bool withCompilations = true ); + + QStringList albumTracks( const QString &artist_id, const QString &album_id ); + QStringList albumDiscTracks( const QString &artist_id, const QString &album_id, const QString &discNumber ); + QStringList artistTracks( const QString &artist_id ); + + //cover management methods + /** Returns the image from a given URL, network-transparently. + * You must run KIO::NetAccess::removeTempFile( tmpFile ) when you are finished using the image; + **/ + static QImage fetchImage( const KURL& url, QString &tmpFile ); + /** Saves images located on the user's filesystem */ + bool setAlbumImage( const QString& artist, const QString& album, const KURL& url ); + /** Saves images obtained from CoverFetcher */ + bool setAlbumImage( const QString& artist, const QString& album, QImage img, const QString& amazonUrl = QString::null, const QString& asin = QString::null ); + + QString findAmazonImage( const QString &artist, const QString &album, const uint width = 1 ); + QString findDirectoryImage( const QString& artist, const QString& album, uint width = 0 ); + QString findEmbeddedImage( const QString& artist, const QString& album, uint width = 1 ); + QString findMetaBundleImage( const MetaBundle &trackInformation, const uint = 1 ); + + /// ensure the sql only return urls to tracks for efficiency + static QPixmap createDragPixmapFromSQL( const QString &sql, QString textOverRide=QString::null ); + static QPixmap createDragPixmap( const KURL::List &urls, QString textOverRide=QString::null ); + static const int DRAGPIXMAP_OFFSET_X = -12; + static const int DRAGPIXMAP_OFFSET_Y = -28; + + /* + * Retrieves the path to the local copy of the image pointed to by url, + * initiates fetching of the remote image if necessary. + * @param width the size of the image. 0 == full size, 1 == preview size + */ + QString podcastImage( const MetaBundle &bundle, const bool withShadow = false, uint width = 1 ); + QString podcastImage( const QString &remoteURL, const bool withShadow = false, uint width = 1 ); + + /** + * Retrieves the path to the image for the album of the requested item + * @param width the size of the image. 0 == full size, 1 == preview size + * @param embedded if not NULL, sets a bool indicating whether the path is an embedded image + */ + QString albumImage( const MetaBundle &trackInformation, const bool withShadow = false, uint width = 1, bool* embedded = 0 ); + QString albumImage( const uint artist_id, const uint album_id, const bool withShadow = false, uint width = 1, bool* embedded = 0 ); + QString albumImage( const QString &artist, const QString &album, const bool withShadow = false, uint width = 1, bool* embedded = 0 ); + QMap * getItemCoverMap() { return itemCoverMap; } + QMutex * getItemCoverMapMutex() { return itemCoverMapMutex; } + + bool removeAlbumImage( const uint artist_id, const uint album_id ); + bool removeAlbumImage( const QString &artist, const QString &album ); + + static QString makeShadowedImage( const QString& albumImage, bool cache = true ); + + //local cover methods + void addImageToAlbum( const QString& image, QValueList< QPair > info, const bool temporary ); + QString notAvailCover( const bool withShadow = false, int width = 1 ); + + //embedded cover methods + void addEmbeddedImage( const QString& path, const QString& hash, const QString& description ); + void removeOrphanedEmbeddedImages(); + + void applySettings(); + + void setLyrics( const QString& url, const QString& lyrics, const QString &uniqueid = QString::null ); + QString getLyrics( const QString& url ); + + /** Remove from the amazon table the item with the specified md5sum **/ + void removeInvalidAmazonInfo( const QString& md5sum ); + void newAmazonReloadDate( const QString& asin, const QString& locale, const QString& md5sum ); + QStringList staleImages(); + + DbConnection::DbConnectionType getDbConnectionType() const { return m_dbConnType; } + bool isConnected(); + void releasePreviousConnection(QThread *currThread); + + void invalidateArtistAlbumCache() { m_validArtistCache=false; m_validComposerCache=false; m_validAlbumCache=false; }; + + void vacuum(); + + /** + * Cancel the underlying move/copy file action + */ + void cancelMovingFileJob(); + + protected: + QCString md5sum( const QString& artist, const QString& album, const QString& file = QString::null ); + void engineTrackEnded( int finalPosition, int trackLength, const QString &reason ); + /** Manages regular folder monitoring scan */ + void timerEvent( QTimerEvent* e ); + + public slots: + void fetchCover( QWidget* parent, const QString& artist, const QString& album, bool noedit, QListViewItem* item = 0 ); + void scanMonitor(); + void startScan(); + void stopScan(); + void scanModifiedDirs(); + void disableAutoScoring( bool disable = true ) { m_autoScoring = !disable; } + + void checkDatabase(); + + private slots: + void dirDirty( const QString& path ); + void coverFetcherResult( CoverFetcher* ); + void similarArtistsFetched( const QString& artist, const QStringList& suggestions ); + void fileOperationResult( KIO::Job *job ); // moveFile depends on it + void podcastImageResult( KIO::Job *job ); //for fetching remote podcast images + void aftMigratePermanentTablesUrl( const QString& oldUrl, const QString& newUrl, const QString& uniqueid ); //AFT-enable stats + void aftMigratePermanentTablesUniqueId( const QString& url, const QString& oldid, const QString& newid ); + + private: + //bump DATABASE_VERSION whenever changes to the table structure are made. + // This erases tags, album, artist, composer, genre, year, images, embed, directory and related_artists tables. + static const int DATABASE_VERSION = 35; + // Persistent Tables hold data that is somehow valuable to the user, and can't be erased when rescaning. + // When bumping this, write code to convert the data! + static const int DATABASE_PERSISTENT_TABLES_VERSION = 19; + // Bumping this erases stats table. If you ever need to, write code to convert the data! + static const int DATABASE_STATS_VERSION = 12; + // When bumping this, you should provide code to convert the data. + static const int DATABASE_PODCAST_TABLES_VERSION = 2; + static const int DATABASE_AFT_VERSION = 2; + // persistent table. you should provide code to convert the data when bumping this + static const int DATABASE_DEVICES_VERSION = 1; + + static const int MONITOR_INTERVAL = 60; //sec + + static QDir largeCoverDir(); + static QDir tagCoverDir(); + static QDir cacheCoverDir(); + + void initialize(); + void destroy(); + DbConnection* getMyConnection(); + + //helper methods which perform updates of amarok's database + void updateStatsTables(); + void updatePersistentTables(); + void updatePodcastTables(); + + //A dirty hack to preserve Group By settings in Collection Browser after addition + //of Composer table + void updateGroupBy(); + + void customEvent( QCustomEvent * ); + + // helpers for embedded images + QString loadHashFile( const QCString& hash, uint width ); + bool extractEmbeddedImage( const MetaBundle &trackInformation, QCString& hash ); + + //general management methods + void createStatsTable(); + void dropStatsTable(); + void createPersistentTables(); + void dropPersistentTables(); + void createPodcastTables(); + void dropPodcastTables(); + void createDevicesTable(); + void dropDevicesTable(); + + //Archived forms of the above. useful for providing a linear upgrade routine that + //stays the same + void createStatsTableV8(); + void createStatsTableV10( bool temp ); + void dropStatsTableV1(); + void createPersistentTablesV12(); + void createPersistentTablesV14( bool temp ); + void dropPersistentTablesV14(); + void createPodcastTablesV2( bool temp ); + void dropPodcastTablesV2(); + + + QCString makeWidthKey( uint width ); + QString artistValue( uint id ); + QString composerValue( uint id ); + QString albumValue( uint id ); + QString genreValue( uint id ); + QString yearValue( uint id ); + + //These should be avoided as they will be slow and potentially unsafe. + //Use the Exact version where possible (faster and safer). + //To convert output from Exact version from QString to uint, use .toUInt() + uint IDFromValue( QString name, QString value, bool autocreate = true, const bool temporary = false ); + QString IDFromExactValue( QString table, QString value, bool autocreate = true, bool temporary = false ); + QString valueFromID( QString table, uint id ); + + //member variables + QString m_amazonLicense; + bool m_validArtistCache; + bool m_validComposerCache; + bool m_validAlbumCache; + QString m_cacheArtist[2]; + uint m_cacheArtistID[2]; + QString m_cacheComposer[2]; + uint m_cacheComposerID[2]; + QString m_cacheAlbum[2]; + uint m_cacheAlbumID[2]; + + bool m_monitor; + bool m_autoScoring; + + static QMap *itemCoverMap; + static QMutex *itemCoverMapMutex; + QImage m_noCover, m_shadowImage; + + static QMap *threadConnections; + static QMutex *connectionMutex; + DbConnection::DbConnectionType m_dbConnType; + DbConfig *m_dbConfig; + + //organize files stuff + bool m_waitForFileOperation; + bool m_fileOperationFailed; + bool m_scanInProgress; + bool m_rescanRequired; + + QStringList m_aftEnabledPersistentTables; + + // Cancel move/copy job + bool m_moveFileJobCancelled; + + // for handling podcast image url redirects + QMap m_podcastImageJobs; + + // protect against multiple simultaneous queries/inserts + QMutex m_mutex; +}; + +class INotify : public ThreadManager::DependentJob +{ + Q_OBJECT + + public: + INotify( CollectionDB *parent, int fd ); + ~INotify(); + + static INotify *instance() { return s_instance; } + + bool watchDir( const QString directory ); + int fd() { return m_fd; } + + private: + virtual bool doJob(); + + CollectionDB* m_parent; + int m_fd; + + static INotify* s_instance; +}; + + +class QueryBuilder +{ + public: + //attributes: + enum qBuilderTables { tabAlbum = 1, tabArtist = 2, tabComposer = 4, tabGenre = 8, tabYear = 16, tabSong = 64, + tabStats = 128, tabLyrics = 256, tabPodcastChannels = 512, + tabPodcastEpisodes = 1024, tabPodcastFolders = 2048, + tabDevices = 4096, tabLabels = 8192 + /* dummy table for filtering */, tabDummy = 0 }; + enum qBuilderOptions { optNoCompilations = 1, optOnlyCompilations = 2, optRemoveDuplicates = 4, + optRandomize = 8, + optShowAll = 16 /* get all songs, not just mounted ones */ }; + /* This has been an enum in the past, but 32 bits wasn't enough anymore :-( */ + static const Q_INT64 valDummy = 0; + static const Q_INT64 valID = 1LL << 0; + static const Q_INT64 valName = 1LL << 1; + static const Q_INT64 valURL = 1LL << 2; + static const Q_INT64 valTitle = 1LL << 3; + static const Q_INT64 valTrack = 1LL << 4; + static const Q_INT64 valScore = 1LL << 5; + static const Q_INT64 valComment = 1LL << 6; + static const Q_INT64 valBitrate = 1LL << 7; + static const Q_INT64 valLength = 1LL << 8; + static const Q_INT64 valSamplerate = 1LL << 9; + static const Q_INT64 valPlayCounter = 1LL << 10; + static const Q_INT64 valCreateDate = 1LL << 11; + static const Q_INT64 valAccessDate = 1LL << 12; + //static const Q_INT64 valPercentage = 1LL << 13; // same as valScore + static const Q_INT64 valArtistID = 1LL << 14; + static const Q_INT64 valAlbumID = 1LL << 15; + static const Q_INT64 valYearID = 1LL << 16; + static const Q_INT64 valGenreID = 1LL << 17; + static const Q_INT64 valDirectory = 1LL << 18; + static const Q_INT64 valLyrics = 1LL << 19; + static const Q_INT64 valRating = 1LL << 20; + static const Q_INT64 valComposerID = 1LL << 21; + static const Q_INT64 valDiscNumber = 1LL << 22; + static const Q_INT64 valFilesize = 1LL << 23; + static const Q_INT64 valFileType = 1LL << 24; + static const Q_INT64 valIsCompilation = 1LL << 25; + static const Q_INT64 valBPM = 1LL << 26; + // podcast relevant: + static const Q_INT64 valCopyright = 1LL << 27; + static const Q_INT64 valParent = 1LL << 28; + static const Q_INT64 valWeblink = 1LL << 29; + static const Q_INT64 valAutoscan = 1LL << 30; + static const Q_INT64 valFetchtype = 1LL << 31; + static const Q_INT64 valAutotransfer = 1LL << 32; + static const Q_INT64 valPurge = 1LL << 33; + static const Q_INT64 valPurgeCount = 1LL << 34; + static const Q_INT64 valIsNew = 1LL << 35; + // dynamic collection relevant: + static const Q_INT64 valDeviceId = 1LL << 36; + static const Q_INT64 valRelativePath = 1LL << 37; + static const Q_INT64 valDeviceLabel = 1LL << 38; + static const Q_INT64 valMountPoint = 1LL << 39; + //label relevant + static const Q_INT64 valType = 1LL << 40; + + static Q_INT64 valForFavoriteSorting(); + void sortByFavorite(); + + // sortByFavoriteAvg() add the average rating, if enabled, the average score, if enabled, + // and the average playcounter as return values! + void sortByFavoriteAvg(); + + enum qBuilderFunctions { funcNone = 0, funcCount = 1, funcMax = 2, funcMin = 4, funcAvg = 8, funcSum = 16 }; + + // Note: modes beginMatch, endMatch are only supported for string filters + // Likewise, modes between and notBetween are only supported for numeric filters + enum qBuilderFilter { modeNormal = 0, modeLess = 1, modeGreater = 2, modeEndMatch = 3, modeBeginMatch = 4, modeBetween = 5, modeNotBetween = 6}; + + QueryBuilder(); + + void addReturnValue( int table, Q_INT64 value, bool caseSensitive = false /* unless value refers to a string */ ); + void addReturnFunctionValue( int function, int table, Q_INT64 value); + uint countReturnValues(); + + // Note: the filter chain begins in AND mode + void beginOR(); //filters will be ORed instead of ANDed + void endOR(); //don't forget to end it! + void beginAND(); // These do the opposite; for recursive and/or + void endAND(); + + void setGoogleFilter( int defaultTables, QString query ); + + void addURLFilters( const QStringList& filter ); + + void addFilter( int tables, const QString& filter); + void addFilter( int tables, Q_INT64 value, const QString& filter, int mode = modeNormal, bool exact = false ); + void addFilters( int tables, const QStringList& filter ); + void excludeFilter( int tables, const QString& filter ); + void excludeFilter( int tables, Q_INT64 value, const QString& filter, int mode = modeNormal, bool exact = false ); + + void addMatch( int tables, const QString& match, bool interpretUnknown = true, bool caseSensitive = true ); + void addMatch( int tables, Q_INT64 value, const QString& match, bool interpretUnknown = true, bool caseSensitive = true ); + void addMatches( int tables, const QStringList& match, bool interpretUnknown = true, bool caseSensitive = true ); + void excludeMatch( int tables, const QString& match ); + void having( int table, Q_INT64 value, int function, int mode, const QString& match ); + + void exclusiveFilter( int tableMatching, int tableNotMatching, Q_INT64 value ); + + // For numeric filters: + // modeNormal means strict equality; modeBeginMatch and modeEndMatch are not + // allowed; modeBetween needs a second value endRange + void addNumericFilter(int tables, Q_INT64 value, const QString &n, + int mode = modeNormal, + const QString &endRange = QString::null); + + void setOptions( int options ); + void sortBy( int table, Q_INT64 value, bool descending = false ); + void sortByFunction( int function, int table, Q_INT64 value, bool descending = false ); + void groupBy( int table, Q_INT64 value ); + void setLimit( int startPos, int length ); + + // Returns the results in random order. + // If a \p table and \p value are specified, uses weighted random order on + // that field. + // The shuffle is cumulative with other sorts, but any sorts after this are + // pointless because of the precision of the random function. + void shuffle( int table = 0, Q_INT64 value = 0 ); + + static const int dragFieldCount; + static QString dragSQLFields(); + void initSQLDrag(); + + void buildQuery( bool withDeviceidPlaceholder = false ); + QString getQuery(); + //use withDeviceidPlaceholder = false if the query isn't run immediately (*CurrentTimeT*) + //and replace (*MountedDeviceSelection*) with CollectionDB::instance()->deviceIdSelection() + QString query( bool withDeviceidPlaceholder = false ) { buildQuery( withDeviceidPlaceholder ); return m_query; }; + void clear(); + + QStringList run(); + + // Transform a string table.value "field" into enum values + // @return true if we succeeded + bool getField(const QString &tableValue, int *table, Q_INT64 *value); + + private: + QString tableName( int table ); + const QString &valueName( Q_INT64 value ); + QString functionName( int functions ); + bool coalesceField( int table, Q_INT64 value ); + + int getTableByName(const QString &name); + Q_INT64 getValueByName(const QString &field); + + QStringList cleanURL( QStringList result ); + + void linkTables( int tables ); + + QValueStack m_OR; + bool m_showAll; + uint m_deviceidPos; + + QString ANDslashOR() const; + + QString m_query; + QString m_values; + QString m_tables; + QString m_join; + QString m_where; + QString m_sort; + QString m_group; + QString m_limit; + QString m_having; + + QString m_url; //url is used as primary key and linkTables needs to do some special stuff with it + + int m_linkTables; + uint m_returnValues; +}; + +inline void QueryBuilder::beginOR() +{ + m_where += ANDslashOR() + " ( " + CollectionDB::instance()->boolF() + ' '; + m_OR.push(true); +} +inline void QueryBuilder::endOR() +{ + m_where += " ) "; + m_OR.pop(); +} +inline void QueryBuilder::beginAND() +{ + m_where += ANDslashOR() + " ( " + CollectionDB::instance()->boolT() + ' '; + m_OR.push(false); +} +inline void QueryBuilder::endAND() +{ + m_where += " ) "; + m_OR.pop(); +} +inline QString QueryBuilder::ANDslashOR() const { return m_OR.top() ? "OR" : "AND"; } + + +#endif /* AMAROK_COLLECTIONDB_H */ diff --git a/amarok/src/collectionscanner/Makefile.am b/amarok/src/collectionscanner/Makefile.am new file mode 100644 index 00000000..51c2f386 --- /dev/null +++ b/amarok/src/collectionscanner/Makefile.am @@ -0,0 +1,28 @@ +bin_PROGRAMS = amarokcollectionscanner + +METASOURCES = AUTO + +INCLUDES = \ + -I$(top_srcdir)/amarok/src \ + $(TAGLIB_CFLAGS) \ + $(all_includes) + +noinst_HEADERS = \ + collectionscannerdcophandler.h + +amarokcollectionscanner_SOURCES = \ + main.cpp \ + collectionscannerdcopiface.skel \ + collectionscannerdcophandler.cpp \ + collectionscanner.cpp + +amarokcollectionscanner_LDADD = \ + $(top_builddir)/amarok/src/metadata/libmetadata.la \ + $(top_builddir)/amarok/src/libamarok.la \ + $(LIB_QT) \ + $(LIB_KDECORE) \ + $(TAGLIB_LIBS) + +amarokcollectionscanner_LDFLAGS = \ + $(all_libraries) \ + $(KDE_RPATH) diff --git a/amarok/src/collectionscanner/collectionscanner.cpp b/amarok/src/collectionscanner/collectionscanner.cpp new file mode 100644 index 00000000..50e10e48 --- /dev/null +++ b/amarok/src/collectionscanner/collectionscanner.cpp @@ -0,0 +1,480 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by The Amarok Developers * + * * + * 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 program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#define DEBUG_PREFIX "CollectionScanner" + +#include "amarok.h" +#include "collectionscanner.h" +#include "collectionscannerdcophandler.h" +#include "debug.h" + +#include +#include + +#include //stat +#include //PATH_MAX +#include //realpath + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + + +CollectionScanner::CollectionScanner( const QStringList& folders, + bool recursive, + bool incremental, + bool importPlaylists, + bool restart ) + : KApplication( /*allowStyles*/ false, /*GUIenabled*/ false ) + , m_importPlaylists( importPlaylists ) + , m_folders( folders ) + , m_recursively( recursive ) + , m_incremental( incremental ) + , m_restart( restart ) + , m_logfile( Amarok::saveLocation( QString::null ) + "collection_scan.log" ) + , m_pause( false ) +{ + DcopCollectionScannerHandler* dcsh = new DcopCollectionScannerHandler(); + connect( dcsh, SIGNAL(pauseRequest()), this, SLOT(pause()) ); + connect( dcsh, SIGNAL(unpauseRequest()), this, SLOT(resume()) ); + kapp->setName( QString( "amarokcollectionscanner" ).ascii() ); + if( !restart ) + QFile::remove( m_logfile ); + + QTimer::singleShot( 0, this, SLOT( doJob() ) ); +} + + +CollectionScanner::~CollectionScanner() +{ + DEBUG_BLOCK +} + +void +CollectionScanner::pause() +{ + DEBUG_BLOCK + m_pause = true; +} + +void +CollectionScanner::resume() +{ + DEBUG_BLOCK + m_pause = false; +} + + + +void +CollectionScanner::doJob() //SLOT +{ + std::cout << ""; + std::cout << ""; + + + QStringList entries; + + if( m_restart ) { + QFile logFile( m_logfile ); + QString lastFile; + if ( !logFile.open( IO_ReadOnly ) ) + warning() << "Failed to open log file " << logFile.name() << " read-only" + << endl; + else { + QTextStream logStream; + logStream.setDevice(&logFile); + logStream.setEncoding(QTextStream::UnicodeUTF8); + lastFile = logStream.read(); + logFile.close(); + } + + QFile folderFile( Amarok::saveLocation( QString::null ) + "collection_scan.files" ); + if ( !folderFile.open( IO_ReadOnly ) ) + warning() << "Failed to open folder file " << folderFile.name() + << " read-only" << endl; + else { + QTextStream folderStream; + folderStream.setDevice(&folderFile); + folderStream.setEncoding(QTextStream::UnicodeUTF8); + entries = QStringList::split( "\n", folderStream.read() ); + } + + for( int count = entries.findIndex( lastFile ) + 1; count; --count ) + entries.pop_front(); + + } + else { + foreachType( QStringList, m_folders ) { + if( (*it).isEmpty() ) + //apparently somewhere empty strings get into the mix + //which results in a full-system scan! Which we can't allow + continue; + + QString dir = *it; + if( !dir.endsWith( "/" ) ) + dir += '/'; + + readDir( dir, entries ); + } + + QFile folderFile( Amarok::saveLocation( QString::null ) + "collection_scan.files" ); + if ( !folderFile.open( IO_WriteOnly ) ) + warning() << "Failed to open folder file " << folderFile.name() + << " read-only" << endl; + else { + QTextStream stream( &folderFile ); + stream.setEncoding(QTextStream::UnicodeUTF8); + stream << entries.join( "\n" ); + folderFile.close(); + } + } + + if( !entries.isEmpty() ) { + if( !m_restart ) { + AttributeMap attributes; + attributes["count"] = QString::number( entries.count() ); + writeElement( "itemcount", attributes ); + } + + scanFiles( entries ); + } + + std::cout << "" << std::endl; + + quit(); +} + + +void +CollectionScanner::readDir( const QString& dir, QStringList& entries ) +{ + static DCOPRef dcopRef( "amarok", "collection" ); + + // linux specific, but this fits the 90% rule + if( dir.startsWith( "/dev" ) || dir.startsWith( "/sys" ) || dir.startsWith( "/proc" ) ) + return; + + const QCString dir8Bit = QFile::encodeName( dir ); + DIR *d = opendir( dir8Bit ); + if( d == NULL ) { + warning() << "Skipping, " << strerror(errno) << ": " << dir << endl; + return; + } +#ifdef USE_SOLARIS + int dfd = d->d_fd; +#else + int dfd = dirfd(d); +#endif + if (dfd == -1) { + warning() << "Skipping, unable to obtain file descriptor: " << dir << endl; + closedir(d); + return; + } + + struct stat statBuf; + struct stat statBuf_symlink; + fstat( dfd, &statBuf ); + + struct direntry de; + memset(&de, 0, sizeof(struct direntry)); + de.dev = statBuf.st_dev; + de.ino = statBuf.st_ino; + + int f = -1; +#if __GNUC__ < 4 + for( unsigned int i = 0; i < m_processedDirs.size(); ++i ) + if( memcmp( &m_processedDirs[i], &de, sizeof( direntry ) ) == 0 ) { + f = i; break; + } +#else + f = m_processedDirs.find( de ); +#endif + + if ( ! S_ISDIR( statBuf.st_mode ) || f != -1 ) { + debug() << "Skipping, already scanned: " << dir << endl; + closedir(d); + return; + } + + AttributeMap attributes; + attributes["path"] = dir; + writeElement( "folder", attributes ); + + m_processedDirs.resize( m_processedDirs.size() + 1 ); + m_processedDirs[m_processedDirs.size() - 1] = de; + + for( dirent *ent; ( ent = readdir( d ) ); ) { + QCString entry (ent->d_name); + QCString entryname (ent->d_name); + + if ( entry == "." || entry == ".." ) + continue; + + entry.prepend( dir8Bit ); + + if ( stat( entry, &statBuf ) != 0 ) + continue; + if ( lstat( entry, &statBuf_symlink ) != 0 ) + continue; + + // loop protection + if ( ! ( S_ISDIR( statBuf.st_mode ) || S_ISREG( statBuf.st_mode ) ) ) + continue; + + if ( S_ISDIR( statBuf.st_mode ) && m_recursively && entry.length() && entryname[0] != '.' ) + { + if ( S_ISLNK( statBuf_symlink.st_mode ) ) { + char nosymlink[PATH_MAX]; + if ( realpath( entry, nosymlink ) ) { + debug() << entry << " is a symlink. Using: " << nosymlink << endl; + entry = nosymlink; + } + } + const QString file = QFile::decodeName( entry ); + + bool isInCollection = false; + if( m_incremental ) + dcopRef.call( "isDirInCollection", file ).get( isInCollection ); + + if( !m_incremental || !isInCollection ) + // we MUST add a '/' after the dirname + readDir( file + '/', entries ); + } + + else if( S_ISREG( statBuf.st_mode ) ) + entries.append( QFile::decodeName( entry ) ); + } + + closedir( d ); +} + + +void +CollectionScanner::scanFiles( const QStringList& entries ) +{ + DEBUG_BLOCK + + typedef QPair CoverBundle; + + QStringList validImages; validImages << "jpg" << "png" << "gif" << "jpeg"; + QStringList validPlaylists; validPlaylists << "m3u" << "pls"; + + QValueList covers; + QStringList images; + + int itemCount = 0; + + DCOPRef dcopRef( "amarok", "collection" ); + + foreachType( QStringList, entries ) { + const QString path = *it; + const QString ext = extension( path ); + const QString dir = directory( path ); + + itemCount++; + + // Write path to logfile + if( !m_logfile.isEmpty() ) { + QFile log( m_logfile ); + if( log.open( IO_WriteOnly ) ) { + QCString cPath = path.utf8(); + log.writeBlock( cPath, cPath.length() ); + log.close(); + } + } + + if( validImages.contains( ext ) ) + images += path; + + else if( m_importPlaylists && validPlaylists.contains( ext ) ) { + AttributeMap attributes; + attributes["path"] = path; + writeElement( "playlist", attributes ); + } + + else { + MetaBundle::EmbeddedImageList images; + MetaBundle mb( KURL::fromPathOrURL( path ), true, TagLib::AudioProperties::Fast, &images ); + const AttributeMap attributes = readTags( mb ); + + if( !attributes.empty() ) { + writeElement( "tags", attributes ); + + CoverBundle cover( attributes["artist"], attributes["album"] ); + + if( !covers.contains( cover ) ) + covers += cover; + + foreachType( MetaBundle::EmbeddedImageList, images ) { + AttributeMap attributes; + attributes["path"] = path; + attributes["hash"] = (*it).hash(); + attributes["description"] = (*it).description(); + writeElement( "embed", attributes ); + } + } + } + + // Update Compilation-flag, when this is the last loop-run + // or we're going to switch to another dir in the next run + QStringList::ConstIterator itTemp( it ); + ++itTemp; + if( path == entries.last() || dir != directory( *itTemp ) ) + { + // we entered the next directory + foreachType( QStringList, images ) { + // Serialize CoverBundle list with AMAROK_MAGIC as separator + QString string; + + for( QValueList::ConstIterator it2 = covers.begin(); it2 != covers.end(); ++it2 ) + string += (*it2).first + "AMAROK_MAGIC" + (*it2).second + "AMAROK_MAGIC"; + + AttributeMap attributes; + attributes["path"] = *it; + attributes["list"] = string; + writeElement( "image", attributes ); + } + + AttributeMap attributes; + attributes["path"] = dir; + writeElement( "compilation", attributes ); + + // clear now because we've processed them + covers.clear(); + images.clear(); + } + + if( itemCount % 20 == 0 ) + { + kapp->processEvents(); // let DCOP through! + if( m_pause ) + { + dcopRef.send( "scannerAcknowledged" ); + while( m_pause ) + { + sleep( 1 ); + kapp->processEvents(); + } + dcopRef.send( "scannerAcknowledged" ); + } + } + + } +} + + +AttributeMap +CollectionScanner::readTags( const MetaBundle& mb ) +{ + // Tests reveal the following: + // + // TagLib::AudioProperties Relative Time Taken + // + // No AudioProp Reading 1 + // Fast 1.18 + // Average Untested + // Accurate Untested + + AttributeMap attributes; + + if ( !mb.isValidMedia() ) { + std::cout << ""; + return attributes; + } + + attributes["path"] = mb.url().path(); + attributes["title"] = mb.title(); + attributes["artist"] = mb.artist(); + attributes["composer"]= mb.composer(); + attributes["album"] = mb.album(); + attributes["comment"] = mb.comment(); + attributes["genre"] = mb.genre(); + attributes["year"] = mb.year() ? QString::number( mb.year() ) : QString(); + attributes["track"] = mb.track() ? QString::number( mb.track() ) : QString(); + attributes["discnumber"] = mb.discNumber() ? QString::number( mb.discNumber() ) : QString(); + attributes["bpm"] = mb.bpm() ? QString::number( mb.bpm() ) : QString(); + attributes["filetype"] = QString::number( mb.fileType() ); + attributes["uniqueid"] = mb.uniqueId(); + attributes["compilation"] = QString::number( mb.compilation() ); + + if ( mb.audioPropertiesUndetermined() ) + attributes["audioproperties"] = "false"; + else { + attributes["audioproperties"] = "true"; + attributes["bitrate"] = QString::number( mb.bitrate() ); + attributes["length"] = QString::number( mb.length() ); + attributes["samplerate"] = QString::number( mb.sampleRate() ); + } + + if ( mb.filesize() >= 0 ) + attributes["filesize"] = QString::number( mb.filesize() ); + + return attributes; +} + + +void +CollectionScanner::writeElement( const QString& name, const AttributeMap& attributes ) +{ + QDomDocument doc; // A dummy. We don't really use DOM, but SAX2 + QDomElement element = doc.createElement( name ); + + foreachType( AttributeMap, attributes ) + { + // There are at least some characters that Qt cannot categorize which make the resulting + // xml document ill-formed and prevent the parser from processing the remaining document. + // Because of this we skip attributes containing characters not belonging to any category. + QString data = it.data(); + const unsigned len = data.length(); + bool nonPrint = false; + for( unsigned i = 0; i < len; i++ ) + { + if( data.ref( i ).category() == QChar::NoCategory ) + { + nonPrint = true; + break; + } + } + + if( nonPrint ) + continue; + + element.setAttribute( it.key(), it.data() ); + } + + QString text; + QTextStream stream( &text, IO_WriteOnly ); + element.save( stream, 0 ); + + std::cout << text.utf8() << std::endl; +} + + +#include "collectionscanner.moc" + diff --git a/amarok/src/collectionscanner/collectionscanner.h b/amarok/src/collectionscanner/collectionscanner.h new file mode 100644 index 00000000..a6a17a2f --- /dev/null +++ b/amarok/src/collectionscanner/collectionscanner.h @@ -0,0 +1,116 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by The Amarok Developers * + * * + * 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 program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#ifndef COLLECTIONSCANNER_H +#define COLLECTIONSCANNER_H + +#include "metabundle.h" + +#include +#include +#include + +#include +#include + +#include + +typedef QMap AttributeMap; + + +/** + * @class CollectionScanner + * @short Scans directories and builds the Collection + */ + +class CollectionScanner : public KApplication +{ + Q_OBJECT + +public: + CollectionScanner( const QStringList& folders, + bool recursive, + bool incremental, + bool importPlaylists, + bool restart ); + + ~CollectionScanner(); + int newInstance() { return 0; } + +public slots: + void pause(); + void resume(); + +private slots: + void doJob(); + +private: + void readDir( const QString& dir, QStringList& entries ); + void scanFiles( const QStringList& entries ); + + /** + * Read metadata tags of a given file. + * @mb MetaBundle for the file. + * @return QMap containing tags, or empty QMap on failure. + */ + AttributeMap readTags( const MetaBundle& mb ); + + /** + * Helper method for writing XML elements to stdout. + * @name Name of the element. + * @attributes Key/value map of attributes. + */ + void writeElement( const QString& name, const AttributeMap& attributes ); + + + /** + * @return the LOWERCASE file extension without the preceding '.', or "" if there is none + */ + inline QString extension( const QString &fileName ) + { + return fileName.contains( '.' ) ? fileName.mid( fileName.findRev( '.' ) + 1 ).lower() : ""; + } + + /** + * @return the last directory in @param fileName + */ + inline QString directory( const QString &fileName ) + { + return fileName.section( '/', 0, -2 ); + } + + + const bool m_importPlaylists; + QStringList m_folders; + const bool m_recursively; + const bool m_incremental; + const bool m_restart; + const QString m_logfile; + bool m_pause; + + struct direntry { + dev_t dev; + ino_t ino; + } KDE_PACKED; + + QMemArray m_processedDirs; +}; + + +#endif // COLLECTIONSCANNER_H diff --git a/amarok/src/collectionscanner/collectionscannerdcophandler.cpp b/amarok/src/collectionscanner/collectionscannerdcophandler.cpp new file mode 100644 index 00000000..c758d571 --- /dev/null +++ b/amarok/src/collectionscanner/collectionscannerdcophandler.cpp @@ -0,0 +1,49 @@ +/*************************************************************************** + collectionscannerdcophandler.cpp - DCOP Implementation + ------------------- + begin : 16/08/05 + copyright : (C) 2006 by Jeff Mitchell + email : kde-dev@emailgoeshere.com + ***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#include "collectionscannerdcophandler.h" + +#include + +///////////////////////////////////////////////////////////////////////////////////// +// class DcopCollectionScannerHandler +///////////////////////////////////////////////////////////////////////////////////// + + DcopCollectionScannerHandler::DcopCollectionScannerHandler() + : DCOPObject( "scanner" ) + , QObject( kapp ) + { + // Register with DCOP + if ( !kapp->dcopClient()->isRegistered() ) { + kapp->dcopClient()->registerAs( "amarokcollectionscanner", false ); + kapp->dcopClient()->setDefaultObject( objId() ); + } + } + + void DcopCollectionScannerHandler::pause() + { + //do nothing for now + emit pauseRequest(); + } + + void DcopCollectionScannerHandler::unpause() + { + //do nothing for now + emit unpauseRequest(); + } + +#include "collectionscannerdcophandler.moc" diff --git a/amarok/src/collectionscanner/collectionscannerdcophandler.h b/amarok/src/collectionscanner/collectionscannerdcophandler.h new file mode 100644 index 00000000..e4133212 --- /dev/null +++ b/amarok/src/collectionscanner/collectionscannerdcophandler.h @@ -0,0 +1,43 @@ +/*************************************************************************** + collectionscannerdcophandler.h - DCOP Interface + ------------------- + begin : 16/08/05 + copyright : (C) 2006 by Jeff Mitchell + email : kde-dev@emailgoeshere.com + ***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#ifndef COLLECTIONSCANNER_DCOP_HANDLER_H +#define COLLECTIONSCANNER_DCOP_HANDLER_H + +#include "collectionscannerdcopiface.h" + +#include + +#include + +class DcopCollectionScannerHandler : public QObject, virtual public CollectionScannerInterface +{ + Q_OBJECT + + public: + DcopCollectionScannerHandler(); + + signals: + void pauseRequest(); + void unpauseRequest(); + + public: + virtual void pause(); + virtual void unpause(); +}; + +#endif diff --git a/amarok/src/collectionscanner/collectionscannerdcopiface.h b/amarok/src/collectionscanner/collectionscannerdcopiface.h new file mode 100644 index 00000000..b0872686 --- /dev/null +++ b/amarok/src/collectionscanner/collectionscannerdcopiface.h @@ -0,0 +1,37 @@ +/*************************************************************************** + collectionscannerdcopiface.h - DCOP Interface + ------------------- + begin : 16/08/05 + copyright : (C) 2006 by Jeff Mitchell + email : kde-dev@emailgoeshere.com + ***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#ifndef COLLECTIONSCANNER_DCOPIFACE_H +#define COLLECTIONSCANNER_DCOPIFACE_H + +#include + +/////////////////////////////////////////////////////////////////////// +// WARNING! Please ask on #amarok before modifying the DCOP interface! +/////////////////////////////////////////////////////////////////////// + + +class CollectionScannerInterface : virtual public DCOPObject +{ + K_DCOP + +k_dcop: + virtual void pause() = 0; ///< Pause the scanner + virtual void unpause() = 0; ///< Unpause the scanner +}; + +#endif diff --git a/amarok/src/collectionscanner/main.cpp b/amarok/src/collectionscanner/main.cpp new file mode 100644 index 00000000..8ca61a6d --- /dev/null +++ b/amarok/src/collectionscanner/main.cpp @@ -0,0 +1,80 @@ +/*************************************************************************** + * Copyright (C) 2003-2006 by The Amarok Developers * + * * + * 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 program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#include "collectionscanner.h" +#include "metadata/tplugins.h" + +#include + +#include +#include +#include +#include + +int main( int argc, char *argv[] ) +{ + const KAboutData about( "amarokcollectionscanner", + I18N_NOOP( "Amarok Collection Scanner\n\nNote: For debugging purposes this application can be invoked from the command line, but it will not actually build a collection this way." ), "0.1", + I18N_NOOP( "Collection Scanner for Amarok" ), KAboutData::License_GPL, + I18N_NOOP( "(C) 2003-2006, The Amarok Developers" ), + I18N_NOOP( "IRC:\nserver: irc.freenode.net / channels: #amarok #amarok.de #amarok.es\n\nFeedback:\namarok@kde.org" ), + I18N_NOOP( "http://amarok.kde.org" ) ); + + + static KCmdLineOptions options[] = + { + { "+Folder(s)", I18N_NOOP( "Folders to scan" ), 0 }, + { "r", 0, 0 }, + { "recursive", I18N_NOOP( "Scan folders recursively" ), 0 }, + { "i", 0, 0 }, + { "incremental", I18N_NOOP( "Incremental Scan (modified folders only)" ), 0 }, + { "p", 0, 0 }, + { "importplaylists", I18N_NOOP( "Import playlist" ), 0 }, + { "s", 0, 0 }, + { "restart", I18N_NOOP( "Restart the scanner at last position, after a crash" ), "" }, + { 0, 0, 0 } + }; + + + KCmdLineArgs::reset(); + KCmdLineArgs::init( argc, argv, &about ); //calls KApplication::addCmdLineOptions() + KCmdLineArgs::addCmdLineOptions( options ); //add our own options + + const KCmdLineArgs* const args = KCmdLineArgs::parsedArgs(); + + // Parse list of folder arguments + QStringList folders; + for( int i = 0; i < args->count(); i++ ) + folders << QFile::decodeName( args->arg( i ) ); + + const bool recursive = args->isSet( "recursive" ); + const bool incremental = args->isSet( "incremental" ); + const bool importplaylists = args->isSet( "importplaylists" ); + const bool restart = args->isSet( "restart" ); + + KApplication::disableAutoDcopRegistration(); + + CollectionScanner scanner( folders, recursive, incremental, importplaylists, restart ); + + registerTaglibPlugins(); + + return scanner.exec(); +} + + diff --git a/amarok/src/colorgenerator.h b/amarok/src/colorgenerator.h new file mode 100644 index 00000000..d209012f --- /dev/null +++ b/amarok/src/colorgenerator.h @@ -0,0 +1,81 @@ +/*************************************************************************** + copyright : (C) 2004 by amarok squad + email : amarok@kde.org + ***************************************************************************/ + +/*************************************************************************** + * This library is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Lesser General Public License version * + * 2.1 as published by the Free Software Foundation. * + * * + * This library is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301 * + * USA * + ***************************************************************************/ + +#ifndef COLORGENERATOR_H +#define COLORGENERATOR_H + +#include "debug.h" + +namespace Amarok { + + +class Color : public QColor +{ + static const int CONTRAST = 130; + static const int SATURATION_TARGET = 30; + +public: + Color( const QColor &c ) : QColor( c ) + { + DEBUG_BLOCK + + int h,s1,s,v1,v; + getHsv( &h, &s1, &v1 ); + + debug() << "Initial Color Properties: s:" << s1 << " v:" << v1 << endl; + + //we want the new colour to be low saturation + //TODO what if s is less than SATURATION_TARGET to start with + s = s1 - CONTRAST; + v = v1; + + if ( s < SATURATION_TARGET ) { + int remainingContrast = SATURATION_TARGET - s; + s = SATURATION_TARGET; + + debug() << "Unapplied Contrast: " << remainingContrast << endl; + + //we only add to the value to avoid the dreaded "grey-gradient" + v += remainingContrast; + + if ( v > 255 ) { + int error = v - 255; + debug() << "Over-compensation: " << error << endl; + + //if the error is significant then this must be a pretty bright colour + //it would look better if the gradient was dark + if( error > CONTRAST/2 ) + v = v1 - error; + else + v = 255; + } + } + + setHsv( h, s, v ); + + debug() << "Final Colour Properties: s:" << s << " v:" << v << endl; + } +}; + +} + +#endif + diff --git a/amarok/src/columnlist.cpp b/amarok/src/columnlist.cpp new file mode 100644 index 00000000..21d2ffee --- /dev/null +++ b/amarok/src/columnlist.cpp @@ -0,0 +1,226 @@ +/* + Copyright (c) 2006 Gábor Lehel + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include + +#include +#include +#include +#include +#include +#include +#include "amarokconfig.h" +#include "metabundle.h" +#include "playlist.h" + +#include "columnlist.h" + +class MyCheckListItem: public QCheckListItem +{ + typedef QCheckListItem super; + ColumnList *m_list; +public: + int column; + MyCheckListItem( int c, QListView *v, const QString &s, Type t, ColumnList *list ): + super( v, s, t ), m_list( list ), column( c ) { } + virtual void paintCell( QPainter * p, const QColorGroup &cg, int c, int w, int a ) + { + QFont f = p->font(); + if( isOn() ) + f.setBold( !f.bold() ); + p->setFont( f ); + super::paintCell( p, cg, c, w, a ); + } + virtual void stateChange( bool b ) + { + super::stateChange( b ); + m_list->setChanged(); + } + MyCheckListItem *itemAbove() { return static_cast( QCheckListItem::itemAbove() ); } + MyCheckListItem *itemBelow() { return static_cast( QCheckListItem::itemBelow() ); } +}; + +ColumnList::ColumnList( QWidget *parent, const char *name ) + : QHBox( parent, name ), m_changed( true ) +{ + setSpacing( 5 ); + + QVBox *buttonbox = new QVBox( this ); + + m_up = new KPushButton( KGuiItem( QString::null, "up" ), buttonbox ); + QToolTip::add( m_up, i18n( "Move column up" ) ); + connect( m_up, SIGNAL( clicked() ), this, SLOT( moveUp() ) ); + + m_down = new KPushButton( KGuiItem( QString::null, "down" ), buttonbox ); + QToolTip::add( m_down, i18n( "Move column down" ) ); + connect( m_down, SIGNAL( clicked() ), this, SLOT( moveDown() ) ); + + m_list = new KListView( this ); + m_list->addColumn(""); + m_list->header()->hide(); + m_list->setSelectionMode( QListView::Single ); + m_list->setResizeMode( QListView::LastColumn ); + m_list->setSorting( -1 ); + m_list->setAcceptDrops( true ); + m_list->setDragEnabled( true ); + m_list->setDropVisualizer( true ); + m_list->setDropVisualizerWidth( 3 ); + connect( m_list, SIGNAL( moved() ), this, SLOT( updateUI() ) ); + connect( m_list, SIGNAL( moved() ), this, SLOT( setChanged() ) ); + connect( m_list, SIGNAL( currentChanged( QListViewItem* ) ), this, SLOT( updateUI() ) ); + + QHeader* const h = Playlist::instance()->header(); + for( int i = h->count() - 1; i >= 0; --i ) + { + const int s = h->mapToSection( i ); + if( ( s != MetaBundle::Rating || AmarokConfig::useRatings() ) && + ( s != MetaBundle::Mood || AmarokConfig::showMoodbar() ) && + ( s != MetaBundle::Score || AmarokConfig::useScores() ) ) + { + ( new MyCheckListItem( s, m_list, MetaBundle::prettyColumnName( s ), QCheckListItem::CheckBox, this ) ) + ->setOn( h->sectionSize( s ) ); + } + } + + m_list->setCurrentItem( m_list->firstChild() ); + updateUI(); + resetChanged(); +} + +QValueList ColumnList::visibleColumns() const +{ + QValueList v; + for( MyCheckListItem *item = static_cast( m_list->firstChild() ); item; item = item->itemBelow() ) + if( item->isOn() ) + v.append( item->column ); + return v; +} + +QValueList ColumnList::columnOrder() const +{ + QValueList v; + for( MyCheckListItem *item = static_cast( m_list->firstChild() ); item; item = item->itemBelow() ) + v.append( item->column ); + return v; +} + +bool ColumnList::isChanged() const +{ + return m_changed; +} + +void ColumnList::resetChanged() +{ + m_changed = false; +} + +void ColumnList::moveUp() +{ + if( QListViewItem *item = m_list->currentItem() ) + if( item->itemAbove() ) + { + m_list->moveItem( item, 0, item->itemAbove()->itemAbove() ); + m_list->setCurrentItem( item ); + m_list->ensureItemVisible( item ); + updateUI(); + setChanged(); + } +} + +void ColumnList::moveDown() +{ + if( QListViewItem *item = m_list->currentItem() ) + { + m_list->moveItem( item, 0, item->itemBelow() ); + m_list->setCurrentItem( item ); + m_list->ensureItemVisible( item ); + updateUI(); + setChanged(); + } +} + +void ColumnList::updateUI() +{ + m_up->setEnabled( m_list->currentItem() && m_list->currentItem()->itemAbove() ); + m_down->setEnabled( m_list->currentItem() && m_list->currentItem()->itemBelow() ); +} + +void ColumnList::setChanged() //slot +{ + if( !m_changed ) + { + m_changed = true; + emit changed(); + } +} + +ColumnsDialog::ColumnsDialog() + : KDialogBase( PlaylistWindow::self(), 0, false, i18n( "Playlist Columns" ) ), + m_list( new ColumnList( this ) ) +{ + setMainWidget( m_list ); + enableButtonApply( false ); + connect( m_list, SIGNAL( changed() ), this, SLOT( slotChanged() ) ); +} + +ColumnsDialog::~ColumnsDialog() +{ + s_instance = 0; +} + +void ColumnsDialog::slotApply() +{ + apply(); + KDialogBase::slotApply(); +} + +void ColumnsDialog::slotOk() +{ + apply(); + KDialogBase::slotOk(); +} + +void ColumnsDialog::hide() +{ + KDialogBase::hide(); + delete this; +} + +void ColumnsDialog::apply() +{ + Playlist::instance()->setColumns( m_list->columnOrder(), m_list->visibleColumns() ); + m_list->resetChanged(); + enableButtonApply( false ); +} + +void ColumnsDialog::display() //static +{ + if( !s_instance ) + s_instance = new ColumnsDialog; + s_instance->show(); +} + +void ColumnsDialog::slotChanged() //slot +{ + enableButtonApply( true ); +} + +ColumnsDialog *ColumnsDialog::s_instance = 0; + +#include "columnlist.moc" diff --git a/amarok/src/columnlist.h b/amarok/src/columnlist.h new file mode 100644 index 00000000..dd32e7da --- /dev/null +++ b/amarok/src/columnlist.h @@ -0,0 +1,76 @@ +/* + Copyright (c) 2006 Gábor Lehel + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef AMAROK_COLUMNLIST_H +#define AMAROK_COLUMNLIST_H + +#include +#include + +class KListView; +class KPushButton; +template class QValueList; + +class ColumnList: public QHBox +{ + Q_OBJECT +public: + ColumnList( QWidget *parent = 0, const char *name = 0 ); + QValueList visibleColumns() const; + QValueList columnOrder() const; + bool isChanged() const; + void resetChanged(); + +signals: + void changed(); + +private slots: + void moveUp(); + void moveDown(); + void updateUI(); + void setChanged(); + +private: + friend class MyCheckListItem; + KListView *m_list; + KPushButton *m_up, *m_down; + bool m_changed; +}; + +class ColumnsDialog: public KDialogBase +{ + Q_OBJECT +public: + static void display(); + +private: + ColumnList *m_list; + static ColumnsDialog *s_instance; + ColumnsDialog(); + ~ColumnsDialog(); +public: + virtual void slotApply(); + virtual void slotOk(); + virtual void hide(); + void apply(); +public slots: + void slotChanged(); +}; + +#endif diff --git a/amarok/src/configdialog.cpp b/amarok/src/configdialog.cpp new file mode 100644 index 00000000..d39d329e --- /dev/null +++ b/amarok/src/configdialog.cpp @@ -0,0 +1,449 @@ +/*************************************************************************** +begin : 2004/02/07 +copyright : (C) Mark Kretschmann +email : markey@web.de +***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#include "amarok.h" +#include "amarokconfig.h" +#include "app.h" +#include "collectiondb.h" +#include "config.h" // Has USE_MYSQL +#include "configdialog.h" +#include "contextbrowser.h" +#include "dbsetup.h" +#include "debug.h" +#include "directorylist.h" +#include "enginecontroller.h" +#include "mediabrowser.h" +#include "mediumpluginmanager.h" +#include "Options1.h" +#include "Options2.h" +#include "Options4.h" +#include "Options5.h" +#include "Options7.h" +#include "Options8.h" +#include "osd.h" +#include "playlistwindow.h" +#include "playerwindow.h" +#include "plugin/pluginconfig.h" +#include "pluginmanager.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include //kapp +#include +#include +#include +#include +#include +#include + +namespace Amarok { + int databaseTypeCode( const QString type ) + { + // can't use kconfigxt for the database comboxbox since we need the DBConnection id and not the index + int dbType = DbConnection::sqlite; + if ( type == "MySQL") + dbType = DbConnection::mysql; + else if ( type == "Postgresql" ) + dbType = DbConnection::postgresql; + return dbType; + } +} + + +int AmarokConfigDialog::s_currentPage = 0; + +////////////////////////////////////////////////////////////////////////////////////////// +// PUBLIC +////////////////////////////////////////////////////////////////////////////////////////// + +AmarokConfigDialog::AmarokConfigDialog( QWidget *parent, const char* name, KConfigSkeleton *config ) + : KConfigDialog( parent, name, config ) + , m_engineConfig( 0 ) + , m_opt4( 0 ) +{ + setWFlags( WDestructiveClose ); + + // IMPORTANT Don't simply change the page names, they are used as identifiers in other parts of the app. + m_opt1 = new Options1( 0, "General" ); +#ifdef Q_WS_MAC + m_opt1->kcfg_ShowSplashscreen->setEnabled(false); + m_opt1->kcfg_ShowTrayIcon->setEnabled(false); + m_opt1->kcfg_AnimateTrayIcon->setEnabled(false); + m_opt1->kcfg_ShowPlayerWindow->setEnabled(false); +#endif + m_opt2 = new Options2( 0, "Appearance" ); + m_opt4 = new Options4( 0, "Playback" ); +#ifdef Q_WS_X11 + Options5 *opt5 = new Options5( 0, "OSD" ); +#endif + QVBox *opt6 = new QVBox; + m_opt7 = new Options7( 0, "Collection" ); + Options8 *opt8 = new Options8( 0, "Scrobbler" ); + QVBox *opt9 = new QVBox; + + // Sound System + opt6->setName( "Engine" ); + opt6->setSpacing( KDialog::spacingHint() ); + QWidget *groupBox, *aboutEngineButton; + groupBox = new QGroupBox( 2, Qt::Horizontal, i18n("Sound System"), opt6 ); + m_engineConfigFrame = new QGroupBox( 1, Qt::Horizontal, opt6 ); + m_soundSystem = new QComboBox( false, groupBox ); + aboutEngineButton = new QPushButton( i18n("About"), groupBox ); + + QToolTip::add( m_soundSystem, i18n("Click to select the sound system to use for playback.") ); + QToolTip::add( aboutEngineButton, i18n("Click to get the plugin information.") ); + + /// Populate the engine selection combo box + KTrader::OfferList offers = PluginManager::query( "[X-KDE-Amarok-plugintype] == 'engine'" ); + KTrader::OfferList::ConstIterator end( offers.end() ); + for( KTrader::OfferList::ConstIterator it = offers.begin(); it != end; ++it ) { + // Don't list the (void engine) entry if it's not currently active, + // cause there's no point in choosing this engine (it's a dummy, after all). + if( (*it)->property( "X-KDE-Amarok-name" ).toString() == "void-engine" + && AmarokConfig::soundSystem() != "void-engine" ) continue; + + m_soundSystem->insertItem( (*it)->name() ); + // Save name properties in QMap for lookup + m_pluginName[(*it)->name()] = (*it)->property( "X-KDE-Amarok-name" ).toString(); + m_pluginAmarokName[(*it)->property( "X-KDE-Amarok-name" ).toString()] = (*it)->name(); + } + + // Collection +#if !defined(USE_MYSQL) && !defined(USE_POSTGRESQL) + m_opt7->databaseBox->hide(); +#endif + +#ifndef USE_MYSQL + //FIXME we do this because this widget breaks the Apply button (always enabled). + //It breaks because it is set to type="password" in the .kcfg file. Setting to + //type="string" also fixes this bug, but means the password is stored in plain + //text. This is a temporary fix so that the majority of users get a fixed Apply + //button. + delete m_opt7->dbSetupFrame->kcfg_MySqlPassword2; +#endif + m_opt7->collectionFoldersBox->setColumns( 1 ); + new CollectionSetup( m_opt7->collectionFoldersBox ); //TODO this widget doesn't update the apply/ok buttons + + // Media Devices + opt9->setName( "Media Devices" ); + opt9->setSpacing( KDialog::spacingHint() ); + QVBox *topbox = new QVBox( opt9 ); + topbox->setSpacing( KDialog::spacingHint() ); + QGroupBox *mediaBox = new QGroupBox( 2, Qt::Horizontal, i18n("Media Devices"), topbox ); + mediaBox->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Minimum ); + QVBox *vbox = new QVBox( mediaBox ); + vbox->setSpacing( KDialog::spacingHint() ); + m_deviceManager = new MediumPluginManager( vbox ); + + QHBox *hbox = new QHBox( topbox ); + hbox->setSpacing( KDialog::spacingHint() ); + hbox->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Minimum ); + KPushButton *autodetect = new KPushButton( i18n( "Autodetect Devices" ), hbox ); + autodetect->setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed ) ); + connect( autodetect, SIGNAL(clicked()), m_deviceManager, SLOT(redetectDevices()) ); + KPushButton *add = new KPushButton( i18n( "Add Device..." ), hbox ); + add->setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed ) ); + connect( add, SIGNAL(clicked()), m_deviceManager, SLOT(newDevice()) ); + + QFrame *frame = new QFrame( topbox ); + frame->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ); + + // add pages + addPage( m_opt1, i18n( "General" ), Amarok::icon( "settings_general" ), i18n( "Configure General Options" ) ); + addPage( m_opt2, i18n( "Appearance" ), Amarok::icon( "settings_view" ), i18n( "Configure Amarok's Appearance" ) ); + addPage( m_opt4, i18n( "Playback" ), Amarok::icon( "settings_playback" ), i18n( "Configure Playback" ) ); +#ifdef Q_WS_X11 + addPage( opt5, i18n( "OSD" ), Amarok::icon( "settings_indicator" ), i18n( "Configure On-Screen-Display" ) ); +#endif + addPage( opt6, i18n( "Engine" ), Amarok::icon( "settings_engine" ), i18n( "Configure Engine" ) ); + addPage( m_opt7, i18n( "Collection" ), Amarok::icon( "collection" ), i18n( "Configure Collection" ) ); + addPage( opt8, i18n( "last.fm" ), Amarok::icon( "audioscrobbler" ), i18n( "Configure last.fm Support" ) ); + addPage( opt9, i18n( "Media Devices" ), Amarok::icon( "device" ), i18n( "Configure Portable Player Support" ) ); + + // Show information labels (must be done after insertions) + QObjectList *list = queryList( "QLabel", "infoPixmap" ); + QPixmap const info = KGlobal::iconLoader()->iconPath( "messagebox_info", -KIcon::SizeHuge ); + for( QObject *label = list->first(); label; label = list->next() ) + static_cast(label)->setPixmap( info ); + delete list; + + //stop KFont Requesters getting stupidly large + list = queryList( "QLabel", "m_sampleLabel" ); + for( QObject *label = list->first(); label; label = list->next() ) + static_cast(label)->setMaximumWidth( 250 ); + delete list; + + connect( m_deviceManager, SIGNAL(changed()), SLOT(updateButtons()) ); + connect( m_soundSystem, SIGNAL(activated( int )), SLOT(updateButtons()) ); + connect( aboutEngineButton, SIGNAL(clicked()), SLOT(aboutEngine()) ); +#ifdef Q_WS_X11 + connect( opt5, SIGNAL(settingsChanged()), SLOT(updateButtons()) ); //see options5.ui.h +#endif + connect( m_opt2->styleComboBox, SIGNAL( activated( int ) ), SLOT( updateButtons() ) ); + connect( m_opt7->dbSetupFrame->databaseEngine, SIGNAL( activated( int ) ), SLOT( updateButtons() ) ); + connect( m_opt1->kComboBox_browser, SIGNAL( activated( int ) ), SLOT( updateButtons() ) ); + connect( m_opt1->kLineEdit_customBrowser, SIGNAL( textChanged( const QString& ) ), SLOT( updateButtons() ) ); +} + +AmarokConfigDialog::~AmarokConfigDialog() +{ + DEBUG_FUNC_INFO + + s_currentPage = activePageIndex(); + + delete m_engineConfig; + delete m_deviceManager; +} + + +/** Reimplemented from KConfigDialog */ +void AmarokConfigDialog::addPage( QWidget *page, const QString &itemName, const QString &pixmapName, const QString &header, bool manage ) +{ + // Add the widget pointer to our list, for later reference + m_pageList << page; + + KConfigDialog::addPage( page, itemName, pixmapName, header, manage ); +} + + +/** Show page by object name */ +void AmarokConfigDialog::showPageByName( const QCString& page ) +{ + for( uint index = 0; index < m_pageList.count(); index++ ) { + if ( m_pageList[index]->name() == page ) { + KConfigDialog::showPage( index ); + return; + } + } +} + + +////////////////////////////////////////////////////////////////////////////////////////// +// PROTECTED SLOTS +////////////////////////////////////////////////////////////////////////////////////////// + +/** + * Update the buttons. + * REIMPLEMENTED + */ + +void AmarokConfigDialog::updateButtons() +{ + KConfigDialog::updateButtons(); +} + +/** + * Update the settings from the dialog. + * Example use: User clicks Ok or Apply button in a configure dialog. + * REIMPLEMENTED + */ +void AmarokConfigDialog::updateSettings() +{ +#ifdef Q_WS_X11 + OSDPreviewWidget *osd = static_cast( child( "osdpreview" ) ); + AmarokConfig::setOsdAlignment( osd->alignment() ); + AmarokConfig::setOsdYOffset( osd->y() ); + Amarok::OSD::instance()->applySettings(); +#endif + + static_cast(child("CollectionSetup"))->writeConfig(); + + if ( m_engineConfig ) m_engineConfig->save(); + + AmarokConfig::setExternalBrowser( externalBrowser() ); + + // When sound system has changed, update engine config page + if ( m_soundSystem->currentText() != m_pluginAmarokName[AmarokConfig::soundSystem()] ) { + AmarokConfig::setSoundSystem( m_pluginName[m_soundSystem->currentText()] ); + emit settingsChanged(); + soundSystemChanged(); + } + + if ( m_opt2->styleComboBox->currentText() != AmarokConfig::contextBrowserStyleSheet() ) { + //can't use kconfigxt for the style comboxbox's since we need the string, not the index + AmarokConfig::setContextBrowserStyleSheet( m_opt2->styleComboBox->currentText() ); + ContextBrowser::instance()->reloadStyleSheet(); + } + + int dbType = Amarok::databaseTypeCode( m_opt7->dbSetupFrame->databaseEngine->currentText() ); + if ( dbType != AmarokConfig::databaseEngine().toInt() ) { + AmarokConfig::setDatabaseEngine( QString::number( dbType ) ); + emit settingsChanged(); + } + + m_deviceManager->finished(); + + if( MediaBrowser::isAvailable() ) + { + PlaylistWindow::self()->addBrowser( "MediaBrowser", MediaBrowser::instance(), i18n( "Media Device" ), Amarok::icon( "device" ) ); + //to re-enable mediabrowser hiding, uncomment this: + //connect( MediaBrowser::instance(), SIGNAL( availabilityChanged( bool ) ), + // PlaylistWindow::self(), SLOT( mbAvailabilityChanged( bool ) ) ); + + } + + Amarok::setUseScores( m_opt1->kcfg_UseScores->isChecked() ); + Amarok::setUseRatings( m_opt1->kcfg_UseRatings->isChecked() ); + + // The following makes everything with a moodbar redraw itself. + Amarok::setMoodbarPrefs( m_opt1->kcfg_ShowMoodbar->isChecked(), + m_opt1->kcfg_MakeMoodier->isChecked(), + m_opt1->kcfg_AlterMood->currentItem(), + m_opt1->kcfg_MoodsWithMusic->isChecked() ); +} + + +/** + * Update the configuration-widgets in the dialog based on Amarok's current settings. + * Example use: Initialisation of dialog. + * Example use: User clicks Reset button in a configure dialog. + * REIMPLEMENTED + */ +void AmarokConfigDialog::updateWidgets() +{ + m_soundSystem->setCurrentText( m_pluginAmarokName[AmarokConfig::soundSystem()] ); + soundSystemChanged(); +} + + +/** + * Update the configuration-widgets in the dialog based on the default values for Amarok's settings. + * Example use: User clicks Defaults button in a configure dialog. + * REIMPLEMENTED + */ +void AmarokConfigDialog::updateWidgetsDefault() +{ + m_soundSystem->setCurrentItem( 0 ); +} + + +////////////////////////////////////////////////////////////////////////////////////////// +// PROTECTED +////////////////////////////////////////////////////////////////////////////////////////// + +/** + * @return true if any configuration items we are managing changed from Amarok's stored settings + * We manage the engine combo box and some of the OSD settings + * REIMPLEMENTED + */ +bool AmarokConfigDialog::hasChanged() +{ +#ifdef Q_WS_X11 + OSDPreviewWidget *osd = static_cast( child( "osdpreview" ) ); +#endif + + return m_soundSystem->currentText() != m_pluginAmarokName[AmarokConfig::soundSystem()] || +#ifdef Q_WS_X11 + osd->alignment() != AmarokConfig::osdAlignment() || + osd->alignment() != OSDWidget::Center && osd->y() != AmarokConfig::osdYOffset() || +#endif + m_opt2->styleComboBox->currentText() != AmarokConfig::contextBrowserStyleSheet() || + Amarok::databaseTypeCode( m_opt7->dbSetupFrame->databaseEngine->currentText() ) != AmarokConfig::databaseEngine().toInt() || + m_engineConfig && m_engineConfig->hasChanged() || + m_deviceManager && m_deviceManager->hasChanged() || + externalBrowser() != AmarokConfig::externalBrowser(); +} + + +/** REIMPLEMENTED */ +bool AmarokConfigDialog::isDefault() +{ + AMAROK_NOTIMPLEMENTED + + //TODO hard to implement - what are default settings for OSD etc? + + return false; +} + + +////////////////////////////////////////////////////////////////////////////////////////// +// PRIVATE SLOTS +////////////////////////////////////////////////////////////////////////////////////////// + +void AmarokConfigDialog::aboutEngine() //SLOT +{ + PluginManager::showAbout( QString( "Name == '%1'" ).arg( m_soundSystem->currentText() ) ); +} + + +////////////////////////////////////////////////////////////////////////////////////////// +// PRIVATE +////////////////////////////////////////////////////////////////////////////////////////// + +void AmarokConfigDialog::soundSystemChanged() +{ + ///A new sound system has been LOADED + ///If only the sound system widget has been changed don't call this! + + // remove old engine config widget + // will delete the view if implementation is done correctly + delete m_engineConfig; + + if( EngineController::hasEngineProperty( "HasConfigure" ) ) + { + m_engineConfig = EngineController::engine()->configure(); + m_engineConfig->view()->reparent( m_engineConfigFrame, QPoint() ); + m_engineConfig->view()->show(); + m_engineConfigFrame->setTitle( i18n( "to change settings", "Configure %1" ).arg( m_soundSystem->currentText() ) ); + m_engineConfigFrame->show(); + + connect( m_engineConfig, SIGNAL(viewChanged()), SLOT(updateButtons()) ); + } + else { + m_engineConfig = 0; + m_engineConfigFrame->hide(); + } + + const bool hasCrossfade = EngineController::hasEngineProperty( "HasCrossfade" ); + const bool crossfadeOn = m_opt4->kcfg_Crossfade->isOn(); + // Enable crossfading option when available + m_opt4->kcfg_Crossfade->setEnabled( hasCrossfade ); + m_opt4->kcfg_CrossfadeLength->setEnabled( hasCrossfade && crossfadeOn ); + m_opt4->crossfadeLengthLabel->setEnabled( hasCrossfade && crossfadeOn ); + m_opt4->kcfg_CrossfadeType->setEnabled( hasCrossfade && crossfadeOn ); + + if (!hasCrossfade) + { + m_opt4->radioButtonNormalPlayback->setChecked( true ); + } +} + +QString AmarokConfigDialog::externalBrowser() const +{ + return m_opt1->kComboBox_browser->isEnabled() ? +#ifdef Q_WS_MAC + m_opt1->kComboBox_browser->currentText() == i18n( "Default Browser" ) ? + "open" : +#else + m_opt1->kComboBox_browser->currentText() == i18n( "Default KDE Browser" ) ? + "kfmclient openURL" : +#endif + m_opt1->kComboBox_browser->currentText().lower() : + m_opt1->kLineEdit_customBrowser->text().lower(); +} + + +#include "configdialog.moc" diff --git a/amarok/src/configdialog.h b/amarok/src/configdialog.h new file mode 100644 index 00000000..2ffd50dc --- /dev/null +++ b/amarok/src/configdialog.h @@ -0,0 +1,81 @@ +/*************************************************************************** +begin : 2004/02/07 +copyright : (C) Mark Kretschmann +email : markey@web.de +***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#ifndef AMAROKCONFIGDIALOG_H +#define AMAROKCONFIGDIALOG_H + +#include +#include + +#include + +class QComboBox; +class QGroupBox; +class QVBox; + +namespace Amarok { + class PluginConfig; +} + +class MediumPluginManager; + +class AmarokConfigDialog : public KConfigDialog +{ + Q_OBJECT + + public: + AmarokConfigDialog( QWidget *parent, const char* name, KConfigSkeleton *config ); + ~AmarokConfigDialog(); + + void addPage( QWidget *page, const QString &itemName, const QString &pixmapName, + const QString &header=QString::null, bool manage=true); + + void showPageByName( const QCString& page ); + + static int s_currentPage; + + protected slots: + void updateButtons(); + void updateSettings(); + void updateWidgets(); + void updateWidgetsDefault(); + + private slots: + void aboutEngine(); + + protected: + bool hasChanged(); + bool isDefault(); + + private: + void soundSystemChanged(); + QString externalBrowser() const; + + QComboBox* m_soundSystem; + Amarok::PluginConfig *m_engineConfig; + QGroupBox *m_engineConfigFrame; + class Options1 *m_opt1; + class Options2 *m_opt2; + class Options4 *m_opt4; + class Options7 *m_opt7; + MediumPluginManager *m_deviceManager; + + QValueList m_pageList; + QMap m_pluginName; + QMap m_pluginAmarokName; +}; + + +#endif // AMAROKCONFIGDIALOG_H diff --git a/amarok/src/contextbrowser.cpp b/amarok/src/contextbrowser.cpp new file mode 100644 index 00000000..63cfa614 --- /dev/null +++ b/amarok/src/contextbrowser.cpp @@ -0,0 +1,4501 @@ +// (c) 2004 Christian Muehlhaeuser +// (c) 2005 Reigo Reinmets +// (c) 2005 Mark Kretschmann +// (c) 2006 Peter C. Ndikuwera +// (c) 2006 Alexandre Pereira de Oliveira +// (c) 2006 Maximilian Kossick +// License: GNU General Public License V2 + + +#define DEBUG_PREFIX "ContextBrowser" + +#include "amarok.h" +#include "amarokconfig.h" +#include "app.h" +#include "browserToolBar.h" +#include "debug.h" +#include "clicklineedit.h" +#include "collectiondb.h" +#include "collectionbrowser.h" +#include "colorgenerator.h" +#include "contextbrowser.h" +#include "coverfetcher.h" +#include "covermanager.h" +#include "cuefile.h" +#include "enginecontroller.h" +#include "htmlview.h" +#include "lastfm.h" +#include "mediabrowser.h" +#include "metabundle.h" +#include "mountpointmanager.h" +#include "playlist.h" //appendMedia() +#include "podcastbundle.h" +#include "qstringx.h" +#include "scriptmanager.h" +#include "starmanager.h" +#include "statusbar.h" +#include "tagdialog.h" +#include "threadmanager.h" + +#include +#include +#include +#include +#include +#include +#include // External CSS reading +#include //wiki tab +#include +#include +#include +#include +#include +#include + +#include +#include //kapp +#include // for Amarok::verboseTimeSince() +#include // suggested/related/favorite box visibility +#include +#include +#include +#include +#include +#include +#include // for data: URLs +#include +#include +#include +#include +#include + +#include //usleep() + +namespace Amarok +{ + QString escapeHTML( const QString &s ) + { + return QString(s).replace( "&", "&" ).replace( "<", "<" ).replace( ">", ">" ); + // .replace( "%", "%25" ) has to be the first(!) one, otherwise we would do things like converting spaces into %20 and then convert them into %25%20 + } + + QString escapeHTMLAttr( const QString &s ) + { + return QString(s).replace( "%", "%25" ).replace( "'", "%27" ).replace( "\"", "%22" ).replace( "#", "%23" ).replace( "?", "%3F" ); + } + + QString unescapeHTMLAttr( const QString &s ) + { + return QString(s).replace( "%3F", "?" ).replace( "%23", "#" ).replace( "%22", "\"" ).replace( "%27", "'" ).replace( "%25", "%" ); + } + + QString verboseTimeSince( const QDateTime &datetime ) + { + const QDateTime now = QDateTime::currentDateTime(); + const int datediff = datetime.daysTo( now ); + + if( datediff >= 6*7 /*six weeks*/ ) { // return absolute month/year + const KCalendarSystem *cal = KGlobal::locale()->calendar(); + const QDate date = datetime.date(); + return i18n( "monthname year", "%1 %2" ).arg( cal->monthName(date), cal->yearString(date, false) ); + } + + //TODO "last week" = maybe within 7 days, but prolly before last sunday + + if( datediff >= 7 ) // return difference in weeks + return i18n( "One week ago", "%n weeks ago", (datediff+3)/7 ); + + if( datediff == -1 ) + return i18n( "Tomorrow" ); + + const int timediff = datetime.secsTo( now ); + + if( timediff >= 24*60*60 /*24 hours*/ ) // return difference in days + return datediff == 1 ? + i18n( "Yesterday" ) : + i18n( "One day ago", "%n days ago", (timediff+12*60*60)/(24*60*60) ); + + if( timediff >= 90*60 /*90 minutes*/ ) // return difference in hours + return i18n( "One hour ago", "%n hours ago", (timediff+30*60)/(60*60) ); + + //TODO are we too specific here? Be more fuzzy? ie, use units of 5 minutes, or "Recently" + + if( timediff >= 0 ) // return difference in minutes + return timediff/60 ? + i18n( "One minute ago", "%n minutes ago", (timediff+30)/60 ) : + i18n( "Within the last minute" ); + + return i18n( "The future" ); + } + + QString verboseTimeSince( uint time_t ) + { + if( !time_t ) + return i18n( "Never" ); + + QDateTime dt; + dt.setTime_t( time_t ); + return verboseTimeSince( dt ); + } + + extern KConfig *config( const QString& ); + + /** + * Function that must be used when separating contextBrowser escaped urls + * detail can contain track/discnumber + */ + void albumArtistTrackFromUrl( QString url, QString &artist, QString &album, QString &detail ) + { + if ( !url.contains("@@@") ) return; + //KHTML removes the trailing space! + if ( url.endsWith( " @@@" ) ) + url += ' '; + + const QStringList list = QStringList::split( " @@@ ", url, true ); + + int size = list.count(); + + Q_ASSERT( size>0 ); + + artist = size > 0 ? unescapeHTMLAttr( list[0] ) : ""; + album = size > 1 ? unescapeHTMLAttr( list[1] ) : ""; + detail = size > 2 ? unescapeHTMLAttr( list[2] ) : ""; + } + +} + + +using Amarok::QStringx; +using Amarok::escapeHTML; +using Amarok::escapeHTMLAttr; +using Amarok::unescapeHTMLAttr; + + +static +QString albumImageTooltip( const QString &albumImage, int size ) +{ + if ( albumImage == CollectionDB::instance()->notAvailCover( false, size ) ) + return escapeHTMLAttr( i18n( "Click to fetch cover from amazon.%1, right-click for menu." ).arg( CoverManager::amazonTld() ) ); + + return escapeHTMLAttr( i18n( "Click for information from Amazon, right-click for menu." ) ); +} + + +ContextBrowser *ContextBrowser::s_instance = 0; +QString ContextBrowser::s_wikiLocale = "en"; + + +ContextBrowser::ContextBrowser( const char *name ) + : KTabWidget( 0, name ) + , EngineObserver( EngineController::instance() ) + , m_dirtyCurrentTrackPage( true ) + , m_dirtyLyricsPage( true ) + , m_dirtyWikiPage( true ) + , m_emptyDB( CollectionDB::instance()->isEmpty() ) + , m_wikiBackPopup( new KPopupMenu( this ) ) + , m_wikiForwardPopup( new KPopupMenu( this ) ) + , m_wikiJob( NULL ) + , m_wikiConfigDialog( NULL ) + , m_relatedOpen( true ) + , m_suggestionsOpen( true ) + , m_favoritesOpen( true ) + , m_labelsOpen( true ) + , m_showFreshPodcasts( true ) + , m_showFavoriteAlbums( true ) + , m_showNewestAlbums( true ) + , m_browseArtists( false ) + , m_browseLabels( false ) + , m_cuefile( NULL ) +{ + s_instance = this; + s_wikiLocale = AmarokConfig::wikipediaLocale(); + + m_contextTab = new QVBox(this, "context_tab"); + + m_currentTrackPage = new HTMLView( m_contextTab, "current_track_page", true /* DNDEnabled */, + true /*JScriptEnabled*/ ); + + m_lyricsTab = new QVBox(this, "lyrics_tab"); + + m_lyricsToolBar = new Browser::ToolBar( m_lyricsTab ); + m_lyricsToolBar->setIconText( KToolBar::IconTextRight, false ); + m_lyricsToolBar->insertButton( Amarok::icon( "refresh" ), LYRICS_REFRESH, true, i18n("Refresh") ); + m_lyricsToolBar->insertButton( Amarok::icon( "add_lyrics" ), LYRICS_ADD, true, i18n("Add") ); + m_lyricsToolBar->insertButton( Amarok::icon( "edit" ), LYRICS_EDIT, true, i18n("Edit") ); + m_lyricsToolBar->setToggle( LYRICS_EDIT, true ); + m_lyricsToolBar->insertButton( Amarok::icon( "search" ), LYRICS_SEARCH, true, i18n("Search") ); + m_lyricsToolBar->setIconText( KToolBar::IconOnly, false ); + m_lyricsToolBar->insertButton( Amarok::icon( "external" ), LYRICS_BROWSER, true, i18n("Open in external browser") ); + + { //Search text inside lyrics. Code inspired/copied from playlistwindow.cpp + m_lyricsTextBar = new KToolBar( m_lyricsTab, "NotMainToolBar" ); + m_lyricsTextBar->hide(); + m_lyricsTextBarShowed=false; + + m_lyricsTextBar->setIconSize( 22, false ); //looks more sensible + m_lyricsTextBar->setFlat( true ); //removes the ugly frame + m_lyricsTextBar->setMovingEnabled( false ); //removes the ugly frame + + m_lyricsTextBar->boxLayout()->addStretch(); + + QWidget *button = new KToolBarButton( "locationbar_erase", 1, m_lyricsTextBar ); + QLabel *filter_label = new QLabel( i18n("S&earch:") + ' ', m_lyricsTextBar ); + m_lyricsSearchText = new ClickLineEdit( i18n( "Search in lyrics" ), m_lyricsTextBar ); + filter_label->setBuddy( m_lyricsSearchText ); + + m_lyricsTextBar->setStretchableWidget(m_lyricsSearchText ); + + m_lyricsSearchText->setFrame( QFrame::Sunken ); + m_lyricsSearchText->installEventFilter( this ); //we intercept keyEvents + + connect( button, SIGNAL(clicked()), m_lyricsSearchText, SLOT(clear()) ); + + QToolTip::add( button, i18n( "Clear search" ) ); + QString filtertip = i18n( "Enter text to search for. Press enter to advance to the next match." ); + + QToolTip::add( m_lyricsSearchText, filtertip ); + + connect ( button, SIGNAL(clicked()), m_lyricsSearchText, SLOT(clear()) ); + connect ( m_lyricsSearchText, SIGNAL(textChanged(const QString &)), this, SLOT(lyricsSearchText(const QString & )) ); + connect ( m_lyricsSearchText, SIGNAL(returnPressed()), this, (SLOT(lyricsSearchTextNext())) ); + Amarok::actionCollection()->setAutoConnectShortcuts ( true ); + new KAction( i18n("Search text in lyrics"), KShortcut("/"), this,SLOT( lyricsSearchTextShow() ), Amarok::actionCollection(), "search_text_lyric"); + Amarok::actionCollection()->setAutoConnectShortcuts ( false ); + } + + + + m_lyricsPage = new HTMLView( m_lyricsTab, "lyrics_page", true /* DNDEnabled */, false /* JScriptEnabled*/ ); + m_lyricsTextEdit = new KTextEdit ( m_lyricsTab, "lyrics_text_edit"); + m_lyricsTextEdit->setTextFormat( Qt::PlainText ); + m_lyricsTextEdit->hide(); + + m_wikiTab = new QVBox(this, "wiki_tab"); + + m_wikiToolBar = new Browser::ToolBar( m_wikiTab ); + m_wikiToolBar->insertButton( "back", WIKI_BACK, false, i18n("Back") ); + m_wikiToolBar->insertButton( "forward", WIKI_FORWARD, false, i18n("Forward") ); + m_wikiToolBar->insertLineSeparator(); + m_wikiToolBar->insertButton( Amarok::icon( "artist" ), WIKI_ARTIST, false, i18n("Artist Page") ); + m_wikiToolBar->insertButton( Amarok::icon( "album" ), WIKI_ALBUM, false, i18n("Album Page") ); + m_wikiToolBar->insertButton( Amarok::icon( "track" ), WIKI_TITLE, false, i18n("Title Page") ); + m_wikiToolBar->insertLineSeparator(); + m_wikiToolBar->insertButton( Amarok::icon( "external" ), WIKI_BROWSER, true, i18n("Open in external browser") ); + m_wikiToolBar->insertButton( Amarok::icon( "change_language" ), WIKI_CONFIG, true, i18n("Change Locale") ); + + m_wikiToolBar->setDelayedPopup( WIKI_BACK, m_wikiBackPopup ); + m_wikiToolBar->setDelayedPopup( WIKI_FORWARD, m_wikiForwardPopup ); + + m_wikiPage = new HTMLView( m_wikiTab, "wiki_page", true /* DNDEnabled */, false /* JScriptEnabled */ ); + + m_cuefile = CueFile::instance(); + connect( m_cuefile, SIGNAL(metaData( const MetaBundle& )), + EngineController::instance(), SLOT(currentTrackMetaDataChanged( const MetaBundle& )) ); + connect( m_cuefile, SIGNAL(newCuePoint( long, long, long )), + Scrobbler::instance(), SLOT(subTrack( long, long, long )) ); + + addTab( m_contextTab, SmallIconSet( Amarok::icon( "music" ) ), i18n( "Music" ) ); + addTab( m_lyricsTab, SmallIconSet( Amarok::icon( "lyrics" ) ), i18n( "Lyrics" ) ); + addTab( m_wikiTab, SmallIconSet( Amarok::icon( "artist" ) ), i18n( "Artist" ) ); + + setTabEnabled( m_lyricsTab, false ); + setTabEnabled( m_wikiTab, false ); + + m_showRelated = Amarok::config( "ContextBrowser" )->readBoolEntry( "ShowRelated", true ); + m_showSuggested = Amarok::config( "ContextBrowser" )->readBoolEntry( "ShowSuggested", true ); + m_showFaves = Amarok::config( "ContextBrowser" )->readBoolEntry( "ShowFaves", true ); + m_showLabels = Amarok::config( "ContextBrowser" )->readBoolEntry( "ShowLabels", true ); + + m_showFreshPodcasts = Amarok::config( "ContextBrowser" )->readBoolEntry( "ShowFreshPodcasts", true ); + m_showNewestAlbums = Amarok::config( "ContextBrowser" )->readBoolEntry( "ShowNewestAlbums", true ); + m_showFavoriteAlbums = Amarok::config( "ContextBrowser" )->readBoolEntry( "ShowFavoriteAlbums", true ); + + // Delete folder with the cached coverimage shadow pixmaps + KIO::del( KURL::fromPathOrURL( Amarok::saveLocation( "covershadow-cache/" ) ), false, false ); + + connect( this, SIGNAL( currentChanged( QWidget* ) ), SLOT( tabChanged( QWidget* ) ) ); + + connect( m_currentTrackPage->browserExtension(), SIGNAL( openURLRequest( const KURL &, const KParts::URLArgs & ) ), + this, SLOT( openURLRequest( const KURL & ) ) ); + connect( m_lyricsPage->browserExtension(), SIGNAL( openURLRequest( const KURL &, const KParts::URLArgs & ) ), + this, SLOT( openURLRequest( const KURL & ) ) ); + connect( m_wikiPage->browserExtension(), SIGNAL( openURLRequest( const KURL &, const KParts::URLArgs & ) ), + this, SLOT( openURLRequest( const KURL & ) ) ); + + connect( m_currentTrackPage, SIGNAL( popupMenu( const QString&, const QPoint& ) ), + this, SLOT( slotContextMenu( const QString&, const QPoint& ) ) ); + connect( m_lyricsPage, SIGNAL( popupMenu( const QString&, const QPoint& ) ), + this, SLOT( slotContextMenu( const QString&, const QPoint& ) ) ); + connect( m_wikiPage, SIGNAL( popupMenu( const QString&, const QPoint& ) ), + this, SLOT( slotContextMenu( const QString&, const QPoint& ) ) ); + + connect( m_lyricsToolBar->getButton( LYRICS_ADD ), SIGNAL(clicked( int )), SLOT(lyricsAdd()) ); + connect( m_lyricsToolBar->getButton( LYRICS_EDIT ), SIGNAL(toggled( int )), SLOT(lyricsEditToggle()) ); + connect( m_lyricsToolBar->getButton( LYRICS_SEARCH ), SIGNAL(clicked( int )), SLOT(lyricsSearch()) ); + connect( m_lyricsToolBar->getButton( LYRICS_REFRESH ), SIGNAL(clicked( int )), SLOT(lyricsRefresh()) ); + connect( m_lyricsToolBar->getButton( LYRICS_BROWSER ), SIGNAL(clicked( int )), SLOT(lyricsExternalPage()) ); + + connect( m_wikiToolBar->getButton( WIKI_BACK ), SIGNAL(clicked( int )), SLOT(wikiHistoryBack()) ); + connect( m_wikiToolBar->getButton( WIKI_FORWARD ), SIGNAL(clicked( int )), SLOT(wikiHistoryForward()) ); + connect( m_wikiToolBar->getButton( WIKI_ARTIST ), SIGNAL(clicked( int )), SLOT(wikiArtistPage()) ); + connect( m_wikiToolBar->getButton( WIKI_ALBUM ), SIGNAL(clicked( int )), SLOT(wikiAlbumPage()) ); + connect( m_wikiToolBar->getButton( WIKI_TITLE ), SIGNAL(clicked( int )), SLOT(wikiTitlePage()) ); + connect( m_wikiToolBar->getButton( WIKI_BROWSER ), SIGNAL(clicked( int )), SLOT(wikiExternalPage()) ); + connect( m_wikiToolBar->getButton( WIKI_CONFIG ), SIGNAL(clicked( int )), SLOT(wikiConfig()) ); + + connect( m_wikiBackPopup, SIGNAL(activated( int )), SLOT(wikiBackPopupActivated( int )) ); + connect( m_wikiForwardPopup, SIGNAL(activated( int )), SLOT(wikiForwardPopupActivated( int )) ); + + connect( CollectionDB::instance(), SIGNAL( scanStarted() ), SLOT( collectionScanStarted() ) ); + connect( CollectionDB::instance(), SIGNAL( scanDone( bool ) ), SLOT( collectionScanDone( bool ) ) ); + connect( CollectionDB::instance(), SIGNAL( databaseEngineChanged() ), SLOT( renderView() ) ); + connect( CollectionDB::instance(), SIGNAL( coverFetched( const QString&, const QString& ) ), + this, SLOT( coverFetched( const QString&, const QString& ) ) ); + connect( CollectionDB::instance(), SIGNAL( coverChanged( const QString&, const QString& ) ), + this, SLOT( coverRemoved( const QString&, const QString& ) ) ); + connect( CollectionDB::instance(), SIGNAL( similarArtistsFetched( const QString& ) ), + this, SLOT( similarArtistsFetched( const QString& ) ) ); + connect( CollectionDB::instance(), SIGNAL( tagsChanged( const MetaBundle& ) ), + this, SLOT( tagsChanged( const MetaBundle& ) ) ); + connect( CollectionDB::instance(), SIGNAL( tagsChanged( const QString&, const QString& ) ), + this, SLOT( tagsChanged( const QString&, const QString& ) ) ); + connect( CollectionDB::instance(), SIGNAL( ratingChanged( const QString&, int ) ), + this, SLOT( ratingOrScoreOrLabelsChanged( const QString& ) ) ); + connect( StarManager::instance(), SIGNAL( ratingsColorsChanged() ), + this, SLOT( ratingOrScoreOrLabelsChanged( const QString& ) ) ); + connect( CollectionDB::instance(), SIGNAL( scoreChanged( const QString&, float ) ), + this, SLOT( ratingOrScoreOrLabelsChanged( const QString& ) ) ); + connect( CollectionDB::instance(), SIGNAL( labelsChanged( const QString& ) ), + this, SLOT( ratingOrScoreOrLabelsChanged( const QString& ) ) ); + connect( CollectionDB::instance(), SIGNAL( imageFetched( const QString& ) ), + this, SLOT( imageFetched( const QString& ) ) ); + + connect( App::instance(), SIGNAL( useScores( bool ) ), + this, SLOT( refreshCurrentTrackPage() ) ); + connect( App::instance(), SIGNAL( useRatings( bool ) ), + this, SLOT( refreshCurrentTrackPage() ) ); + + connect( MountPointManager::instance(), SIGNAL( mediumConnected( int ) ), + this, SLOT( renderView() ) ); + connect( MountPointManager::instance(), SIGNAL( mediumRemoved( int ) ), + this, SLOT( renderView() ) ); + + showContext( KURL( "current://track" ) ); + +// setMinimumHeight( AmarokConfig::coverPreviewSize() + (fontMetrics().height()+2)*5 + tabBar()->height() ); +} + + +ContextBrowser::~ContextBrowser() +{ + DEBUG_BLOCK + + ThreadManager::instance()->abortAllJobsNamed( "CurrentTrackJob" ); + + // Ensure the KHTMLPart dies before its KHTMLView dies, + // because KHTMLPart's dtoring relies on its KHTMLView still being alive + // (see bug 130494). + delete m_currentTrackPage; + delete m_lyricsPage; + delete m_wikiPage; + m_cuefile->clear(); +} + + +////////////////////////////////////////////////////////////////////////////////////////// +// PUBLIC METHODS +////////////////////////////////////////////////////////////////////////////////////////// + +void ContextBrowser::setFont( const QFont &newFont ) +{ + QWidget::setFont( newFont ); + reloadStyleSheet(); +} + + +////////////////////////////////////////////////////////////////////////////////////////// +// PUBLIC SLOTS +////////////////////////////////////////////////////////////////////////////////////////// + +void ContextBrowser::openURLRequest( const KURL &url ) +{ + QString artist, album, track; + Amarok::albumArtistTrackFromUrl( url.path(), artist, album, track ); + + // All http links should be loaded inside wikipedia tab, as that is the only tab that should contain them. + // Streams should use stream:// protocol. + if ( url.protocol() == "http" ) + { + if ( url.hasHTMLRef() ) + { + KURL base = url; + base.setRef(QString::null); + // Wikipedia also has links to otherpages with Anchors, so we have to check if it's for the current one + if ( m_wikiCurrentUrl == base.url() ) { + m_wikiPage->gotoAnchor( url.htmlRef() ); + return; + } + } + // new page + m_dirtyWikiPage = true; + m_wikiCurrentEntry = QString::null; + showWikipedia( url.url() ); + } + else if ( url.protocol() == "show" ) + { + if ( url.path().contains( "suggestLyric-" ) ) + { + QString _url = url.url().mid( url.url().find( QString( "-" ) ) +1 ); + debug() << "Clicked lyrics URL: " << _url << endl; + m_dirtyLyricsPage = true; + showLyrics( _url ); + } + else if ( url.path() == "collectionSetup" ) + { + CollectionView::instance()->setupDirs(); + } + else if ( url.path() == "scriptmanager" ) + { + ScriptManager::instance()->show(); + ScriptManager::instance()->raise(); + } + else if ( url.path() == "editLabels" ) + { + showLabelsDialog(); + } + // Konqueror sidebar needs these + if (url.path() == "context") { m_dirtyCurrentTrackPage=true; showContext( KURL( "current://track" ) ); saveHtmlData(); } + if (url.path() == "wiki") { m_dirtyWikiPage=true; showWikipedia(); saveHtmlData(); } + if (url.path() == "lyrics") { m_dirtyLyricsPage=true; m_wikiJob=false; showLyrics(); saveHtmlData(); } + } + + else if ( url.protocol() == "runscript" ) + { + ScriptManager::instance()->runScript( url.path() ); + } + + // When left-clicking on cover image, open browser with amazon site + else if ( url.protocol() == "fetchcover" ) + { + QString albumPath = CollectionDB::instance()->albumImage(artist, album, false, 0 ); + if ( albumPath == CollectionDB::instance()->notAvailCover( false, 0 ) ) + { + CollectionDB::instance()->fetchCover( this, artist, album, false ); + return; + } + + QImage img( albumPath ); + const QString amazonUrl = img.text( "amazon-url" ); + + if ( amazonUrl.isEmpty() ) + KMessageBox::information( this, i18n( "

There is no product information available for this image.

Right-click on image for menu." ) ); + else + Amarok::invokeBrowser( amazonUrl ); + } + + /* open konqueror with musicbrainz search result for artist-album */ + else if ( url.protocol() == "musicbrainz" ) + { + const QString url = "http://www.musicbrainz.org/taglookup.html?artist=%1&album=%2&track=%3"; + Amarok::invokeBrowser( url.arg( KURL::encode_string_no_slash( artist, 106 /*utf-8*/ ), + KURL::encode_string_no_slash( album, 106 /*utf-8*/ ), + KURL::encode_string_no_slash( track, 106 /*utf-8*/ ) ) ); + } + + else if ( url.protocol() == "externalurl" ) + Amarok::invokeBrowser( url.url().replace( QRegExp( "^externalurl:" ), "http:") ); + + else if ( url.protocol() == "lastfm" ) + { + LastFm::WebService *lfm = LastFm::Controller::instance()->getService(); + if ( url.path() == "skip" ) lfm->skip(); + else if ( url.path() == "love" ) lfm->love(); + else if ( url.path() == "ban" ) lfm->ban(); + } + else if ( url.protocol() == "togglebox" ) + { + if ( url.path() == "ra" ) m_relatedOpen ^= true; + else if ( url.path() == "ss" ) m_suggestionsOpen ^= true; + else if ( url.path() == "ft" ) m_favoritesOpen ^= true; + else if ( url.path() == "sl" ) m_labelsOpen ^= true; + } + + else if ( url.protocol() == "seek" ) + { + EngineController::instance()->seek(url.path().toLong()); + } + + // browse albums of a related artist. Don't do this if we are viewing Home tab + else if ( url.protocol() == "artist" || url.protocol() == "current" || url.protocol() == "showlabel") + { + if( EngineController::engine()->loaded() ) // song must be active + showContext( url ); + } + + else if( url.protocol() == "artistback" ) + { + contextHistoryBack(); + } + + else if ( url.protocol() == "wikipedia" ) + { + m_dirtyWikiPage = true; + QString entry = unescapeHTMLAttr( url.path() ); + showWikipediaEntry( entry ); + } + + else if( url.protocol() == "ggartist" ) + { + const QString url2 = QString( "http://www.google.com/musicsearch?q=%1&res=artist" ) + .arg( KURL::encode_string_no_slash( unescapeHTMLAttr( url.path() ).replace( " ", "+" ), 106 /*utf-8*/ ) ); + Amarok::invokeBrowser( url2 ); + } + + else if( url.protocol() == "file" ) + { + Playlist::instance()->insertMedia( url, Playlist::DefaultOptions ); + } + + else if( url.protocol() == "stream" ) + { + Playlist::instance()->insertMedia( KURL::fromPathOrURL( url.url().replace( QRegExp( "^stream:" ), "http:" ) ), Playlist::DefaultOptions ); + } + + else if( url.protocol() == "compilationdisc" || url.protocol() == "albumdisc" ) + { + Playlist::instance()->insertMedia( expandURL( url ) , Playlist::DefaultOptions ); + } + + else + HTMLView::openURLRequest( url ); +} + + +void ContextBrowser::collectionScanStarted() +{ + m_emptyDB = CollectionDB::instance()->isEmpty(); + if( m_emptyDB && currentPage() == m_contextTab ) + showCurrentTrack(); +} + + +void ContextBrowser::collectionScanDone( bool changed ) +{ + if ( CollectionDB::instance()->isEmpty() ) + { + m_emptyDB = true; + if ( currentPage() == m_contextTab ) + showCurrentTrack(); + } + else if ( m_emptyDB ) + { + m_emptyDB = false; + PlaylistWindow::self()->showBrowser("CollectionBrowser"); + } + else if( changed && currentPage() == m_contextTab ) + { + m_dirtyCurrentTrackPage = true; + showCurrentTrack(); + } +} + + +void ContextBrowser::renderView() +{ + m_dirtyCurrentTrackPage = true; + m_dirtyLyricsPage = true; + m_dirtyWikiPage = true; + m_emptyDB = CollectionDB::instance()->isEmpty(); + + showCurrentTrack(); +} + + +void ContextBrowser::lyricsChanged( const QString &url ) { + if ( url == EngineController::instance()->bundle().url().path() ) { + m_dirtyLyricsPage = true; + if ( currentPage() == m_lyricsTab ) + showLyrics(); + } +} + +void ContextBrowser::lyricsScriptChanged() { + m_dirtyLyricsPage = true; + if ( currentPage() == m_lyricsTab ) + showLyrics(); +} + +////////////////////////////////////////////////////////////////////////////////////////// +// PROTECTED +////////////////////////////////////////////////////////////////////////////////////////// + +void ContextBrowser::engineNewMetaData( const MetaBundle& bundle, bool trackChanged ) +{ + bool newMetaData = false; + m_dirtyCurrentTrackPage = true; + m_dirtyLyricsPage = true; + m_wikiJob = 0; //New metadata, so let's forget previous wiki-fetching jobs + + if ( MetaBundle( m_currentURL ).artist() != bundle.artist() ) + m_dirtyWikiPage = true; + // Prepend stream metadata history item to list + if ( !m_metadataHistory.first().contains( bundle.prettyTitle() ) ) + { + newMetaData = true; + const QString timeString = KGlobal::locale()->formatTime( QTime::currentTime() ).replace(" ", " "); // don't break over lines + m_metadataHistory.prepend( QString( "" + timeString + " " + escapeHTML( bundle.prettyTitle() ) + "" ) ); + } + + if ( currentPage() == m_contextTab && ( bundle.url() != m_currentURL || newMetaData || !trackChanged ) ) + showCurrentTrack(); + else if ( currentPage() == m_lyricsTab ) + { + EngineController::engine()->isStream() ? + lyricsRefresh() : // can't call showLyrics() because the url hasn't changed + showLyrics() ; + } + else if ( CollectionDB::instance()->isEmpty() || !CollectionDB::instance()->isValid() ) + showCurrentTrack(); + + if (trackChanged) + { + m_cuefile->clear(); + + if (bundle.url().isLocalFile()) + { + + /** The cue file that is provided with the media might have different name than the + * media file itself, hence simply cutting the media extension and adding ".cue" + * is not always enough to find the matching cue file. In such cases we have + * to search for all the cue files in the directory and have a look inside them for + * the matching FILE="" stanza. However the FILE="" stanza does not always + * point at the corresponding media file (e.g. it is quite often set to the misleading + * FILE="audio.wav" WAV). Therfore we also have to check blindly if there is a cue + * file having the same name as the media file played, as described above. + */ + + // look for the cue file that matches the media file played first + QString path = bundle.url().path(); + QString cueFile = path.left( path.findRev('.') ) + ".cue"; + + m_cuefile->setCueFileName( cueFile ); + + if( m_cuefile->load( bundle.length() ) ) + debug() << "[CUEFILE]: " << cueFile << " - Shoot blindly, found and loaded. " << endl; + + // if unlucky, let's have a look inside cue files, if any + else + { + debug() << "[CUEFILE]: " << cueFile << " - Shoot blindly and missed, searching for other cue files." << endl; + + bool foundCueFile = false; + QDir dir ( bundle.directory() ); + dir.setFilter( QDir::Files ) ; + dir.setNameFilter( "*.cue *.CUE" ) ; + + QStringList cueFilesList = dir.entryList(); + + if ( !cueFilesList.empty() ) + for ( QStringList::Iterator it = cueFilesList.begin(); it != cueFilesList.end() && !foundCueFile; ++it ) + { + QFile file ( dir.filePath(*it) ); + if( file.open( IO_ReadOnly ) ) + { + debug() << "[CUEFILE]: " << *it << " - Opened, looking for the matching FILE stanza." << endl; + QTextStream stream( &file ); + QString line; + + while ( !stream.atEnd() && !foundCueFile) + { + line = stream.readLine().simplifyWhiteSpace(); + + if( line.startsWith( "file", false ) ) + { + line = line.mid( 5 ).remove( '"' ); + + if ( line.contains( bundle.filename(), false ) ) + { + cueFile = dir.filePath(*it); + foundCueFile = true; + m_cuefile->setCueFileName( cueFile ); + if( m_cuefile->load( bundle.length() ) ) + debug() << "[CUEFILE]: " << cueFile << " - Looked inside cue files, found and loaded proper one" << endl; + } + } + } + + file.close(); + } + } + + if ( !foundCueFile ) + debug() << "[CUEFILE]: - Didn't find any matching cue file." << endl; + } + } + } +} + + +void ContextBrowser::engineStateChanged( Engine::State state, Engine::State oldState ) +{ + DEBUG_BLOCK + + if( state != Engine::Paused /*pause*/ && oldState != Engine::Paused /*resume*/ + || state == Engine::Empty ) + { + // Pause shouldn't clear everything (but stop should, even when paused) + m_dirtyCurrentTrackPage = true; + m_dirtyLyricsPage = true; + m_wikiJob = 0; //let's forget previous wiki-fetching jobs + } + + switch( state ) + { + case Engine::Empty: + m_metadataHistory.clear(); + if ( currentPage() == m_contextTab || currentPage() == m_lyricsTab ) + { + showCurrentTrack(); + } + blockSignals( true ); + setTabEnabled( m_lyricsTab, false ); + if ( currentPage() != m_wikiTab ) { + setTabEnabled( m_wikiTab, false ); + m_dirtyWikiPage = true; + } + else // current tab is wikitab, disable some buttons. + { + m_wikiToolBar->setItemEnabled( WIKI_ARTIST, false ); + m_wikiToolBar->setItemEnabled( WIKI_ALBUM, false ); + m_wikiToolBar->setItemEnabled( WIKI_TITLE, false ); + } + blockSignals( false ); + break; + + case Engine::Playing: + if ( oldState != Engine::Paused ) + m_metadataHistory.clear(); + blockSignals( true ); + setTabEnabled( m_lyricsTab, true ); + setTabEnabled( m_wikiTab, true ); + m_wikiToolBar->setItemEnabled( WIKI_ARTIST, true ); + m_wikiToolBar->setItemEnabled( WIKI_ALBUM, true ); + m_wikiToolBar->setItemEnabled( WIKI_TITLE, true ); + blockSignals( false ); + break; + + default: + ; + } +} + +void ContextBrowser::saveHtmlData() +{ + QFile exportedDocument( Amarok::saveLocation() + "contextbrowser.html" ); + if ( !exportedDocument.open( IO_WriteOnly ) ) + warning() << "Failed to open file " << exportedDocument.name() + << " write-only" << endl; + else { + QTextStream stream( &exportedDocument ); + stream.setEncoding( QTextStream::UnicodeUTF8 ); + stream << m_HTMLSource // the pure html data.. + .replace( "", + QString( "" ) + .arg( HTMLView::loadStyleSheet() ) ); // and the + // stylesheet + // code + exportedDocument.close(); + } +} + + +void ContextBrowser::paletteChange( const QPalette& /* pal */ ) +{ +// KTabWidget::paletteChange( pal ); + HTMLView::paletteChange(); + reloadStyleSheet(); +} + +void ContextBrowser::reloadStyleSheet() +{ + m_currentTrackPage->setUserStyleSheet( HTMLView::loadStyleSheet() ); + m_lyricsPage->setUserStyleSheet( HTMLView::loadStyleSheet() ); + m_wikiPage->setUserStyleSheet( HTMLView::loadStyleSheet() ); +} + +////////////////////////////////////////////////////////////////////////////////////////// +// PROTECTED SLOTS +////////////////////////////////////////////////////////////////////////////////////////// + +//parts of this function from ktabwidget.cpp, copyright (C) 2003 Zack Rusin and Stephan Binner +//fucking setCurrentTab() isn't virtual so we have to override this instead =( +void ContextBrowser::wheelDelta( int delta ) +{ + if ( count() < 2 || delta == 0 ) + return; + + int index = currentPageIndex(), start = index; + do + { + if( delta < 0 ) + index = (index + 1) % count(); + else + { + index = index - 1; + if( index < 0 ) + index = count() - 1; + } + if( index == start ) // full circle, none enabled + return; + } while( !isTabEnabled( page( index ) ) ); + setCurrentPage( index ); +} + + +////////////////////////////////////////////////////////////////////////////////////////// +// PRIVATE SLOTS +////////////////////////////////////////////////////////////////////////////////////////// + +void ContextBrowser::tabChanged( QWidget *page ) +{ +DEBUG_FUNC_INFO + setFocusProxy( page ); //so focus is given to a sensible widget when the tab is opened + + if ( page == m_contextTab ) + showCurrentTrack(); + else if ( page == m_lyricsTab ) + showLyrics(); + else if ( page == m_wikiTab ) + showWikipedia(); +} + +void ContextBrowser::slotContextMenu( const QString& urlString, const QPoint& point ) +{ + enum { APPEND, ASNEXT, MAKE, MEDIA_DEVICE, INFO, TITLE, RELATED, SUGGEST, FAVES, FRESHPODCASTS, NEWALBUMS, FAVALBUMS, LABELS }; + debug() << "url string: " << urlString << endl; + + if( urlString.startsWith( "musicbrainz" ) || + urlString.startsWith( "externalurl" ) || + urlString.startsWith( "show:suggest" ) || + urlString.startsWith( "http" ) || + urlString.startsWith( "wikipedia" ) || + urlString.startsWith( "seek" ) || + urlString.startsWith( "ggartist" ) || + urlString.startsWith( "artistback" ) || + urlString.startsWith( "current" ) || + urlString.startsWith( "lastfm" ) || + urlString.startsWith( "showlabel" ) || + urlString.startsWith( "show:editLabels" ) || + currentPage() != m_contextTab ) + return; + + KURL url( urlString ); + + KPopupMenu menu; + KURL::List urls( url ); + QString artist, album, track; // track unused here + Amarok::albumArtistTrackFromUrl( url.path(), artist, album, track ); + + if( urlString.isEmpty() ) + { + debug() << "url string empty. loaded?" << EngineController::engine()->loaded() << endl; + if( EngineController::engine()->loaded() ) + { + menu.setCheckable( true ); + menu.insertItem( i18n("Show Labels"), LABELS ); + menu.insertItem( i18n("Show Related Artists"), RELATED ); + menu.insertItem( i18n("Show Suggested Songs"), SUGGEST ); + menu.insertItem( i18n("Show Favorite Tracks"), FAVES ); + + menu.setItemChecked( RELATED, m_showRelated ); + menu.setItemChecked( SUGGEST, m_showSuggested ); + menu.setItemChecked( FAVES, m_showFaves ); + menu.setItemChecked( LABELS, m_showLabels ); + } else { + // the home info page + menu.setCheckable( true ); + menu.insertItem( i18n("Show Fresh Podcasts"), FRESHPODCASTS ); + menu.insertItem( i18n("Show Newest Albums"), NEWALBUMS ); + menu.insertItem( i18n("Show Favorite Albums"), FAVALBUMS ); + + menu.setItemChecked( FRESHPODCASTS, m_showFreshPodcasts ); + menu.setItemChecked( NEWALBUMS, m_showNewestAlbums ); + menu.setItemChecked( FAVALBUMS, m_showFavoriteAlbums ); + } + } + else if( url.protocol() == "fetchcover" ) + { + Amarok::coverContextMenu( this, point, artist, album ); + return; + } + else if( url.protocol() == "stream" ) + { + url = KURL::fromPathOrURL( url.url().replace( QRegExp( "^stream:" ), "http:" ) ); + urls = KURL::List( url ); + menu.insertTitle( i18n("Podcast"), TITLE ); + menu.insertItem( SmallIconSet( Amarok::icon( "files" ) ), i18n( "&Load" ), MAKE ); + menu.insertItem( SmallIconSet( Amarok::icon( "add_playlist" ) ), i18n( "&Append to Playlist" ), APPEND ); + menu.insertItem( SmallIconSet( Amarok::icon( "queue_track" ) ), i18n( "&Queue Podcast" ), ASNEXT ); + //menu.insertSeparator(); + //menu.insertItem( SmallIconSet( "down" ), i18n( "&Download" ), DOWNLOAD ); + } + else if( url.protocol() == "file" || url.protocol() == "artist" || url.protocol() == "album" || url.protocol() == "compilation" || url.protocol() == "albumdisc" || url.protocol() == "compilationdisc") + { + //TODO it would be handy and more usable to have this menu under the cover one too + + menu.insertTitle( i18n("Track"), TITLE ); + menu.insertItem( SmallIconSet( Amarok::icon( "files" ) ), i18n( "&Load" ), MAKE ); + menu.insertItem( SmallIconSet( Amarok::icon( "add_playlist" ) ), i18n( "&Append to Playlist" ), APPEND ); + menu.insertItem( SmallIconSet( Amarok::icon( "queue_track" ) ), i18n( "&Queue Track" ), ASNEXT ); + if( MediaBrowser::isAvailable() ) + menu.insertItem( SmallIconSet( Amarok::icon( "device" ) ), i18n( "&Transfer to Media Device" ), MEDIA_DEVICE ); + + menu.insertSeparator(); + menu.insertItem( SmallIconSet( Amarok::icon( "info" ) ), i18n( "Edit Track &Information..." ), INFO ); + + if ( url.protocol() == "artist" ) + { + urls = expandURL( url ); + + menu.changeTitle( TITLE, i18n("Artist") ); + menu.changeItem( INFO, i18n("Edit Artist &Information..." ) ); + menu.changeItem( ASNEXT, i18n("&Queue Artist's Songs") ); + } + if ( url.protocol() == "album" ) + { + urls = expandURL( url ); + + menu.changeTitle( TITLE, i18n("Album") ); + menu.changeItem( INFO, i18n("Edit Album &Information..." ) ); + menu.changeItem( ASNEXT, i18n("&Queue Album") ); + } + if ( url.protocol() == "albumdisc" ) + { + urls = expandURL( url ); + + menu.changeTitle( TITLE, i18n("Album Disc") ); + menu.changeItem( INFO, i18n("Edit Album Disc &Information..." ) ); + menu.changeItem( ASNEXT, i18n("&Queue Album Disc") ); + } + if ( url.protocol() == "compilation" ) + { + urls = expandURL( url ); + + menu.changeTitle( TITLE, i18n("Compilation") ); + menu.changeItem( INFO, i18n("Edit Album &Information..." ) ); + menu.changeItem( ASNEXT, i18n("&Queue Album") ); + } + if ( url.protocol() == "compilationdisc" ) + { + urls = expandURL( url ); + + menu.changeTitle( TITLE, i18n("Compilation Disc") ); + menu.changeItem( INFO, i18n("Edit Compilation Disc &Information..." ) ); + menu.changeItem( ASNEXT, i18n("&Queue Compilation Disc") ); + } + + if( urls.count() == 0 ) + { + menu.setItemEnabled( MAKE, false ); + menu.setItemEnabled( APPEND, false ); + menu.setItemEnabled( ASNEXT, false ); + menu.setItemEnabled( MEDIA_DEVICE, false ); + menu.setItemEnabled( INFO, false ); + } + } + + //Not all these are used in the menu, it depends on the context + switch( menu.exec( point ) ) + { + + case RELATED: + m_showRelated = !menu.isItemChecked( RELATED ); + Amarok::config( "ContextBrowser" )->writeEntry( "ShowRelated", m_showRelated ); + m_dirtyCurrentTrackPage = true; + showCurrentTrack(); + break; + + case SUGGEST: + m_showSuggested = !menu.isItemChecked( SUGGEST ); + Amarok::config( "ContextBrowser" )->writeEntry( "ShowSuggested", m_showSuggested ); + m_dirtyCurrentTrackPage = true; + showCurrentTrack(); + break; + + case FAVES: + m_showFaves = !menu.isItemChecked( FAVES ); + Amarok::config( "ContextBrowser" )->writeEntry( "ShowFaves", m_showFaves ); + m_dirtyCurrentTrackPage = true; + showCurrentTrack(); + break; + + case LABELS: + m_showLabels = !menu.isItemChecked( LABELS ); + Amarok::config( "ContextBrowser" )->writeEntry( "ShowLabels", m_showLabels ); + m_dirtyCurrentTrackPage = true; + showCurrentTrack(); + break; + + case FRESHPODCASTS: + m_showFreshPodcasts = !menu.isItemChecked( FRESHPODCASTS ); + Amarok::config( "ContextBrowser" )->writeEntry( "ShowFreshPodcasts", m_showFreshPodcasts ); + m_dirtyCurrentTrackPage = true; + showCurrentTrack(); + break; + + case NEWALBUMS: + m_showNewestAlbums = !menu.isItemChecked( NEWALBUMS ); + Amarok::config( "ContextBrowser" )->writeEntry( "ShowNewestAlbums", m_showNewestAlbums ); + m_dirtyCurrentTrackPage = true; + showCurrentTrack(); + break; + + case FAVALBUMS: + m_showFavoriteAlbums = !menu.isItemChecked( FAVALBUMS ); + Amarok::config( "ContextBrowser" )->writeEntry( "ShowFavoriteAlbums", m_showFavoriteAlbums ); + m_dirtyCurrentTrackPage = true; + showCurrentTrack(); + break; + + case ASNEXT: + Playlist::instance()->insertMedia( urls, Playlist::Queue ); + break; + + case INFO: + { + if ( urls.count() > 1 ) + { + TagDialog* dialog = new TagDialog( urls, instance() ); + dialog->show(); + } + else if ( !urls.isEmpty() ) + { + TagDialog* dialog = new TagDialog( urls.first(), instance() ); + dialog->show(); + } + break; + } + + case MAKE: + Playlist::instance()->clear(); + //FALL_THROUGH + + case APPEND: + Playlist::instance()->insertMedia( urls, Playlist::Append ); + break; + + case MEDIA_DEVICE: + MediaBrowser::queue()->addURLs( urls ); + break; + + } +} + + + +////////////////////////////////////////////////////////////////////////////////////////// +// Current-Tab +////////////////////////////////////////////////////////////////////////////////////////// + +/** This is the slowest part of track change, so we thread it */ +class CurrentTrackJob : public ThreadManager::DependentJob +{ +public: + CurrentTrackJob( ContextBrowser *parent ) + : ThreadManager::DependentJob( parent, "CurrentTrackJob" ) + , b( parent ) + , m_currentTrack( QDeepCopy( EngineController::instance()->bundle() ) ) + , m_isStream( EngineController::engine()->isStream() ) + { + for( QStringList::iterator it = b->m_metadataHistory.begin(); + it != b->m_metadataHistory.end(); + ++it ) + { + m_metadataHistory += QDeepCopy( *it ); + } + + + m_amarokIconPath = QDeepCopy(KGlobal::iconLoader()->iconPath( "amarok", + -KIcon::SizeEnormous ) ); + m_musicBrainIconPath = QDeepCopy(locate( "data", "amarok/images/musicbrainz.png" ) + ); + m_lastfmIcon = "file://" + locate( "data","amarok/images/lastfm.png" ); + } + +private: + virtual bool doJob(); + void addMetaHistory(); + void showLastFm( const MetaBundle ¤tTrack ); + void showStream( const MetaBundle ¤tTrack ); + void showPodcast( const MetaBundle ¤tTrack ); + void showBrowseArtistHeader( const QString &artist ); + void showBrowseLabelHeader( const QString &label ); + void showCurrentArtistHeader( const MetaBundle ¤tTrack ); + void showRelatedArtists( const QString &artist, const QStringList &relArtists ); + void showSuggestedSongs( const QStringList &relArtists ); + void showSongsWithLabel( const QString &label ); + void showArtistsFaves( const QString &artistName, uint artist_id ); + void showArtistsAlbums( const QString &artist, uint artist_id, uint album_id ); + void showArtistsCompilations( const QString &artist, uint artist_id, uint album_id ); + void showHome(); + void showUserLabels( const MetaBundle ¤tTrack ); + QString fetchLastfmImage( const QString& url ); + QStringList showHomeByAlbums(); + void constructHTMLAlbums( const QStringList &albums, QString &htmlCode, const QString &idPrefix ); + static QString statsHTML( int score, int rating, bool statsbox = true ); // meh. + + virtual void completeJob() + { + // are we still showing the currentTrack page? +// if( b->currentPage() != b->m_contextTab ) +// return; + + b->m_shownAlbums.clear(); + for( QStringList::iterator it = m_shownAlbums.begin(); + it != m_shownAlbums.end(); + ++it ) + b->m_shownAlbums.append( QDeepCopy( *it ) ); + b->m_HTMLSource = QDeepCopy( m_HTMLSource ); + b->m_currentTrackPage->set( m_HTMLSource ); + b->m_dirtyCurrentTrackPage = false; + b->saveHtmlData(); // Send html code to file + } + QString m_HTMLSource; + QString m_amarokIconPath; + QString m_musicBrainIconPath; + QString m_lastfmIcon; + + ContextBrowser *b; + MetaBundle m_currentTrack; + bool m_isStream; + QStringList m_shownAlbums; + QStringList m_metadataHistory; +}; + +void +ContextBrowser::showContext( const KURL &url, bool fromHistory ) +{ + if ( currentPage() != m_contextTab ) + { + blockSignals( true ); + showPage( m_contextTab ); + blockSignals( false ); + } + + m_dirtyCurrentTrackPage = true; + m_contextURL = url.url(); + + if( url.protocol() == "current" ) + { + m_browseArtists = false; + m_browseLabels = false; + m_label = QString::null; + m_artist = QString::null; + m_contextBackHistory.clear(); + m_contextBackHistory.push_back( "current://track" ); + } + else if( url.protocol() == "artist" ) + { + m_browseArtists = true; + m_browseLabels = false; + m_label = QString::null; + m_artist = unescapeHTMLAttr( url.path() ); + } + else if( url.protocol() == "showlabel" ) + { + m_browseLabels = true; + m_browseArtists = false; + m_artist = QString::null; + m_label = unescapeHTMLAttr( url.path() ); + } + + // Append new URL to history + if ( !fromHistory ) { + m_contextBackHistory += m_contextURL.url(); + } + // Limit number of items in history + if ( m_contextBackHistory.count() > CONTEXT_MAX_HISTORY ) + m_contextBackHistory.pop_front(); + + showCurrentTrack(); +} + +void +ContextBrowser::contextHistoryBack() //SLOT +{ + if( m_contextBackHistory.size() > 0 ) + { + m_contextBackHistory.pop_back(); + + m_dirtyCurrentTrackPage = true; + + showContext( KURL( m_contextBackHistory.last() ), true ); + } +} + + +void ContextBrowser::showCurrentTrack() //SLOT +{ +#if 0 + if( BrowserBar::instance()->currentBrowser() != this ) + { + debug() << "current browser is not context, aborting showCurrentTrack()" << endl; + m_dirtyCurrentTrackPage = true; + m_currentTrackPage->set( QString( "

%1
" ) + .arg( i18n( "Updating..." ) ) ); + return; + } +#endif + if ( currentPage() != m_contextTab ) { + blockSignals( true ); + showPage( m_contextTab ); + blockSignals( false ); + } + + // TODO: Show CurrentTrack or Lyric tab if they were selected + // If it's not a streaming, check for a collection + if ( !EngineController::engine()->isStream() ) + { + if ( m_emptyDB && CollectionDB::instance()->isValid() && !MountPointManager::instance()->collectionFolders().isEmpty() ) + { + showScanning(); + return; + } + else if ( CollectionDB::instance()->isEmpty() || !CollectionDB::instance()->isValid() ) + { + showIntroduction(); + return; + } + } + if( !m_dirtyCurrentTrackPage ) + return; + m_currentURL = EngineController::instance()->bundle().url(); + m_currentTrackPage->write( QString::null ); + ThreadManager::instance()->onlyOneJob( new CurrentTrackJob( this ) ); +} + + + +////////////////////////////////////////////////////////////////////////////////////////// +// Shows the statistics summary when no track is playing +////////////////////////////////////////////////////////////////////////////////////////// + +void CurrentTrackJob::showHome() +{ + QueryBuilder qb; + + qb.clear(); //Song count + qb.addReturnFunctionValue( QueryBuilder::funcCount, QueryBuilder::tabSong, QueryBuilder::valURL ); + qb.setOptions( QueryBuilder::optRemoveDuplicates ); + QStringList a = qb.run(); + QString songCount = a[0]; + + qb.clear(); //Artist count + //qb.addReturnFunctionValue( QueryBuilder::funcCount, QueryBuilder::tabArtist, QueryBuilder::valID ); + //qb.setOptions( QueryBuilder::optRemoveDuplicates ); + //a = qb.run(); + //QString artistCount = a[0]; + qb.setOptions( QueryBuilder::optRemoveDuplicates ); + qb.addReturnValue( QueryBuilder::tabSong, QueryBuilder::valArtistID ); + //I can't get the correct value w/o suing a subquery, and querybuilder doesn't support those + QString artistCount = QString::number( qb.run().count() ); + + qb.clear(); //Album count + //qb.addReturnFunctionValue( QueryBuilder::funcCount, QueryBuilder::tabAlbum, QueryBuilder::valID ); + //qb.setOptions( QueryBuilder::optRemoveDuplicates ); + //a = qb.run(); + //QString albumCount = a[0]; + qb.setOptions( QueryBuilder::optRemoveDuplicates ); + qb.addReturnValue( QueryBuilder::tabSong, QueryBuilder::valAlbumID ); + QString albumCount = QString::number( qb.run().count() ); + + qb.clear(); //Genre count + //qb.addReturnFunctionValue( QueryBuilder::funcCount, QueryBuilder::tabGenre, QueryBuilder::valID ); + //qb.setOptions( QueryBuilder::optRemoveDuplicates ); + //a = qb.run(); + //QString genreCount = a[0]; + qb.setOptions( QueryBuilder::optRemoveDuplicates ); + qb.addReturnValue( QueryBuilder::tabSong, QueryBuilder::valGenreID ); + QString genreCount = QString::number( qb.run().count() ); + + qb.clear(); //Total Playtime + qb.addReturnFunctionValue( QueryBuilder::funcSum, QueryBuilder::tabSong, QueryBuilder::valLength ); + a = qb.run(); + QString playTime = MetaBundle::fuzzyTime( a[0].toInt() ); + + m_HTMLSource.append( + QStringx( + "
\n" + "
\n" + "\n" + + i18n( "No Track Playing" ) + + "\n" + "
\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "
\n" + "\n" + "\n" + "%3
\n" + "%4
\n" + "%5
\n" + "%6
\n" + "%7
\n" + "
\n" + "
\n" ) + .args( QStringList() + << escapeHTMLAttr( "externalurl://amarok.kde.org" ) + << escapeHTMLAttr( m_amarokIconPath ) + << i18n( "1 Track", "%n Tracks", songCount.toInt() ) + << i18n( "1 Artist", "%n Artists", artistCount.toInt() ) + << i18n( "1 Album", "%n Albums", albumCount.toInt() ) + << i18n( "1 Genre", "%n Genres", genreCount.toInt() ) + << i18n( "%1 Play-time" ).arg ( playTime ) ) ); + + m_shownAlbums = showHomeByAlbums(); + + m_HTMLSource.append( + "\n"); +} + + +void +CurrentTrackJob::constructHTMLAlbums( const QStringList &reqResult, QString &htmlCode, const QString &stID ) +{ + // This function create the html code used to display a list of albums. Each album + // is a 'toggleable' block. + // Parameter stID is used to differentiate same albums in different album list. So if this function + // is called multiple time in the same HTML code, stID must be different. + for ( uint i = 0; i < reqResult.count(); i += 4 ) + { + QueryBuilder qb; + qb.clear(); + qb.addReturnValue( QueryBuilder::tabSong, QueryBuilder::valTitle ); + qb.addReturnValue( QueryBuilder::tabSong, QueryBuilder::valURL ); + qb.addReturnValue( QueryBuilder::tabSong, QueryBuilder::valTrack ); + qb.addReturnValue( QueryBuilder::tabYear, QueryBuilder::valName ); + qb.addReturnValue( QueryBuilder::tabSong, QueryBuilder::valLength ); + qb.addReturnValue( QueryBuilder::tabArtist, QueryBuilder::valName ); + qb.addReturnValue( QueryBuilder::tabArtist, QueryBuilder::valID ); + qb.addReturnValue( QueryBuilder::tabSong, QueryBuilder::valDiscNumber ); + qb.addMatch( QueryBuilder::tabSong, QueryBuilder::valAlbumID, reqResult[i+1] ); + qb.addMatch( QueryBuilder::tabSong, QueryBuilder::valArtistID, reqResult[i+3] ); + qb.sortBy( QueryBuilder::tabSong, QueryBuilder::valDiscNumber ); + qb.sortBy( QueryBuilder::tabSong, QueryBuilder::valTrack ); + qb.sortBy( QueryBuilder::tabSong, QueryBuilder::valTitle ); + qb.setOptions( QueryBuilder::optNoCompilations ); // samplers __need__ to be handled differently + QStringList albumValues = qb.run(); + + QString albumYear; + if ( !albumValues.isEmpty() ) + { + albumYear = albumValues[ 3 ]; + for ( uint j = 0; j < albumValues.count(); j += qb.countReturnValues()) + if ( albumValues[j + 3] != albumYear || albumYear == "0" ) + { + albumYear = QString::null; + break; + } + } + + uint i_albumLength = 0; + for ( uint j = 0; j < albumValues.count(); j += qb.countReturnValues() ) + i_albumLength += QString(albumValues[j + 4]).toInt(); + + QString albumLength = ( i_albumLength==0 ? i18n( "Unknown" ) : MetaBundle::prettyTime( i_albumLength, true ) ); + + htmlCode.append( QStringx ( + "\n" + "\n" + "
\n" + "\n" + "\n") + .args( QStringList() + << stID + reqResult[i+1] )); + + QString albumName = escapeHTML( reqResult[ i ].isEmpty() ? i18n( "Unknown album" ) : reqResult[ i ] ); + + QString artistName = albumValues[5].isEmpty() ? i18n( "Unknown artist" ) : albumValues[5]; + + QString albumImage = ContextBrowser::getEncodedImage( CollectionDB::instance()->albumImage( albumValues[5], reqResult[ i ], true, 50 ) ); + QString albumImageTitleAttr = albumImageTooltip( albumImage, 50 ); + + // Album image + htmlCode.append( QStringx ( + "\n" + "\n") + .args( QStringList() + << i18n( "Single", "%n Tracks", albumValues.count() / qb.countReturnValues() ) + << albumYear + << albumLength) ); + + // Begining of the 'toggleable div' that contains the songs + htmlCode.append( QStringx ( + "\n" + "
\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "%6\n" + "\n" + " - \n" + "\n" + "%9\n" + "\n" ) + .args( QStringList() + << escapeHTMLAttr( albumValues[5] ) // artist name + << escapeHTMLAttr( reqResult[ i ].isEmpty() ? i18n( "Unknown" ) : reqResult[ i ] ) // album.name + << albumImageTitleAttr + << escapeHTMLAttr( albumImage ) + << escapeHTMLAttr( artistName ) + << escapeHTML( artistName ) + << albumValues[6] + << reqResult[ i + 1 ] //album.id + << albumName ) ); + + // Tracks number, year and length + htmlCode.append( QStringx ( + "%1 " + "
\n" + "%2\n" + "%3\n" + "
\n" + "
\n" + "
\n" ) + .args( QStringList() + << "none" /* shows it if it's the current track album */ + << stID + reqResult[ i + 1 ] ) ); + + QString discNumber; + + if ( !albumValues.isEmpty() ) + { + for ( uint j = 0; j < albumValues.count(); j += qb.countReturnValues() ) + { + QString newDiscNumber = albumValues[ j + 7 ].stripWhiteSpace(); + if( discNumber != newDiscNumber && newDiscNumber.toInt() > 0) + { + discNumber = newDiscNumber; + htmlCode.append( QStringx ( + "
\n" + "\n" + "%4" + "\n" + "
\n" ) + .args( QStringList() + << albumValues[6] + << reqResult[ i + 1 ] //album.id + << escapeHTMLAttr( discNumber ) + << i18n( "Disc %1" ).arg( discNumber ) ) ); + } + QString track = albumValues[j + 2].stripWhiteSpace(); + if( track.length() > 0 ) + { + if( track.length() == 1 ) + track.prepend( "0" ); + + track = "\n" + track + " \n"; + } + + QString length; + if( albumValues[j + 4] != "0" ) + length = "(" + MetaBundle::prettyTime( QString(albumValues[j + 4]).toInt(), true ) + ")\n"; + + htmlCode.append( + "\n" ); + } + } + + htmlCode.append( + "
\n" + "\n" + "\n" ); + } +} + + +// return list of albums shown +QStringList +CurrentTrackJob::showHomeByAlbums() +{ + QueryBuilder qb; + + m_HTMLSource.append( "\n" ); + + // + if( ContextBrowser::instance()->m_showFreshPodcasts ) + { + qb.clear(); + qb.addReturnValue( QueryBuilder::tabPodcastEpisodes, QueryBuilder::valParent ); + qb.addFilter( QueryBuilder::tabPodcastEpisodes, QueryBuilder::valIsNew, CollectionDB::instance()->boolT(), QueryBuilder::modeNormal, true ); + qb.sortBy( QueryBuilder::tabPodcastEpisodes, QueryBuilder::valID, true ); + qb.setOptions( QueryBuilder::optRemoveDuplicates ); + qb.setLimit( 0, 5 ); + QStringList channels = qb.run(); + + if( channels.count() > 0 ) + { + m_HTMLSource.append( + "\n" + "\n" + "\n" ); + } + } + // + + QStringList albums; + // + if( ContextBrowser::instance()->m_showNewestAlbums ) + { + qb.clear(); + qb.addReturnValue( QueryBuilder::tabAlbum, QueryBuilder::valName ); + qb.addReturnValue( QueryBuilder::tabAlbum, QueryBuilder::valID ); + qb.addReturnFunctionValue( QueryBuilder::funcMax, QueryBuilder::tabSong, QueryBuilder::valCreateDate ); + qb.addReturnValue( QueryBuilder::tabArtist, QueryBuilder::valID ); + qb.sortByFunction( QueryBuilder::funcMax, QueryBuilder::tabSong, QueryBuilder::valCreateDate, true ); + qb.excludeMatch( QueryBuilder::tabAlbum, i18n( "Unknown" ) ); + qb.groupBy( QueryBuilder::tabAlbum, QueryBuilder::valID ); + qb.groupBy( QueryBuilder::tabArtist, QueryBuilder::valID ); + qb.groupBy( QueryBuilder::tabAlbum, QueryBuilder::valName ); + qb.setOptions( QueryBuilder::optNoCompilations ); // samplers __need__ to be handled differently + qb.setLimit( 0, 5 ); + QStringList recentAlbums = qb.run(); + + foreach( recentAlbums ) + { + albums += *it; + it++; + it++; + it++; + } + // toggle html here so we get correct albums + m_HTMLSource.append( + "\n" + "\n" + "\n" ); + } + // + + // + if( ContextBrowser::instance()->m_showFavoriteAlbums ) + { + qb.clear(); + qb.addReturnValue( QueryBuilder::tabAlbum, QueryBuilder::valName ); + qb.addReturnValue( QueryBuilder::tabAlbum, QueryBuilder::valID ); + qb.sortByFavoriteAvg(); // this function adds return values! + qb.addReturnValue( QueryBuilder::tabArtist, QueryBuilder::valID ); + // only albums with more than 3 tracks + qb.having( QueryBuilder::tabAlbum, QueryBuilder::valID, QueryBuilder::funcCount, QueryBuilder::modeGreater, "3" ); + qb.excludeMatch( QueryBuilder::tabAlbum, i18n( "Unknown" ) ); + qb.groupBy( QueryBuilder::tabAlbum, QueryBuilder::valID ); + qb.groupBy( QueryBuilder::tabArtist, QueryBuilder::valID ); + qb.groupBy( QueryBuilder::tabAlbum, QueryBuilder::valName ); + qb.setOptions( QueryBuilder::optNoCompilations ); // samplers __need__ to be handled differently + qb.setLimit( 0, 5 ); + QStringList faveAlbums = qb.run(); + QStringList faveResults; + + bool ratings = AmarokConfig::useRatings(); + bool scores = AmarokConfig::useScores(); + + foreach( faveAlbums ) { + albums += *it; + faveResults += *(it++); + faveResults += *(it++); + faveResults += *(it++); + // sortByFavoriteAvg add some return values, and constructHTMLAlbums expects + // a specific set of return values, so we might need to skip some values + if ( ratings ) + it++; + if ( scores ) + it++; + faveResults += *(it); + } + + m_HTMLSource.append( + "\n" ); + } + // + + m_HTMLSource.append( "
\n" + "
\n" + "\n" + + i18n( "Fresh Podcast Episodes" ) + + "\n" + "
\n" + "\n" ); + + uint i = 0; + for( QStringList::iterator it = channels.begin(); + it != channels.end(); + it++ ) + { + PodcastChannelBundle pcb; + if( !CollectionDB::instance()->getPodcastChannelBundle( *it, &pcb ) ) + continue; + + QValueList episodes = CollectionDB::instance()->getPodcastEpisodes( *it, true /* only new */, 1 ); + if( !episodes.isEmpty() ) + { + PodcastEpisodeBundle &ep = *episodes.begin(); + + QString date; + ep.dateTime().isNull() ? + date = ep.date() : + date = ep.dateTime().toString(); + + QString image = CollectionDB::instance()->podcastImage( pcb.imageURL().url(), true, 50 ); + QString imageAttr = escapeHTMLAttr( i18n( "Click to go to podcast website: %1." ).arg( pcb.link().prettyURL() ) ); + + m_HTMLSource.append( QStringx ( + "\n" + "\n" + "\n" ); + i++; + } + } + m_HTMLSource.append( + "
\n" + "
\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "
\n" + "\n" + "\n" + "\n" + "\n" + "%5 \n" + "%7\n" + "
\n" + "%8\n" + "
\n" + "
\n" + "
\n" ) + .args( QStringList() + << QString::number( i ) + << pcb.link().url().replace( QRegExp( "^http:" ), "externalurl:" ) + << escapeHTMLAttr( imageAttr ) + << escapeHTMLAttr( image ) + << escapeHTML( ep.duration() ? MetaBundle::prettyTime( ep.duration() ) : QString( "" ) ) + << ( ep.localUrl().isValid() + ? ep.localUrl().url() + : ep.url().url().replace( QRegExp( "^http:" ), "stream:" ) ) + << escapeHTML( pcb.title() + ": " + ep.title() ) + << escapeHTML( date ) + << "none" + << QString::number( i ) + ) + ); + + m_HTMLSource.append( QStringx ( "

%1

\n" ).arg( ep.description() ) ); + + m_HTMLSource.append( + "
\n" + "
\n" + "
\n" + "
\n" + "
\n" + "
\n" + "\n" + + i18n( "Your Newest Albums" ) + + "\n" + "
\n" + "\n" ); + constructHTMLAlbums( recentAlbums, m_HTMLSource, "1" ); + + m_HTMLSource.append( + "
\n" + "
\n" + "
\n" + "
\n" + "
\n" + "\n" + + i18n( "Favorite Albums" ) + + "\n" + "
\n" + "\n" ); + + if ( faveAlbums.count() == 0 ) + { + m_HTMLSource.append( + "

\n" + + (QueryBuilder::valForFavoriteSorting() == QueryBuilder::valRating + ? i18n( "A list of your favorite albums will appear here, once you have rated a few of your songs." ) + : i18n( "A list of your favorite albums will appear here, once you have played a few of your songs." ) ) + + "

\n" ); + } + else + { + constructHTMLAlbums( faveResults, m_HTMLSource, "2" ); + } + + m_HTMLSource.append( + "
\n" ); + + return albums; +} + + +void CurrentTrackJob::showLastFm( const MetaBundle ¤tTrack ) +{ + if( !LastFm::Controller::instance()->isPlaying() ) return; + + const LastFm::Bundle *lastFmInfo = currentTrack.lastFmBundle(); + if ( !lastFmInfo ) return; + + const QString username = AmarokConfig::scrobblerUsername(); + const QString userpage = "www.last.fm/user/" + username; //no http + const QString albumUrl = lastFmInfo->albumUrl(); + const QString artistUrl = lastFmInfo->artistUrl(); + const QString titleUrl = lastFmInfo->titleUrl(); + + const QString coverImage = ContextBrowser::getEncodedImage( lastFmInfo->imageUrl() ); + + QPtrList newUrls; + newUrls.append( &albumUrl ); + newUrls.append( &artistUrl ); + newUrls.append( &titleUrl ); + + for ( QString* url = newUrls.first(); url; url = newUrls.next() ) + url->replace( QRegExp( "^http:" ), "externalurl:" ); + + const QString skipIcon = KGlobal::iconLoader()->iconPath( Amarok::icon("next"), -KIcon::SizeSmallMedium ); + const QString loveIcon = KGlobal::iconLoader()->iconPath( Amarok::icon("love"), -KIcon::SizeSmallMedium ); + const QString banIcon = KGlobal::iconLoader()->iconPath( Amarok::icon("remove"), -KIcon::SizeSmallMedium ); + + + m_HTMLSource.append( QStringx( + "
\n" + "
\n" + "%1 " + "
\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "
\n" + "%3 - %5" + "
\n" + "%7" + "
\n" + "" + "\n" + "\n" + "
\n" + "\n" + "\n" + "\n" + "
\n" + "\n" + "\n" + "\n" + "\n" + "
\n" + "%14\n" + "\n" + "\n" + "
\n" + "%16\n" + "\n" + "\n" + "
\n" + "%18\n" + "\n" + "\n" + "
\n" + "
\n" + "
\n" ) + .args( QStringList() + << escapeHTML( LastFm::Controller::stationDescription() ) //1 + << artistUrl //2 + << escapeHTML( currentTrack.artist() ) //3 + << titleUrl //4 + << escapeHTML( currentTrack.title() ) //5 + << albumUrl //6 + << escapeHTML( currentTrack.album() ) //7 + << albumUrl //8 + << coverImage //9 + << escapeHTMLAttr( currentTrack.album() )//10 + << escapeHTMLAttr( userpage ) //11 + << escapeHTMLAttr( userpage ) //12 + << escapeHTMLAttr( m_lastfmIcon ) //13 + << escapeHTML( i18n( "Skip" ) ) //14 + << escapeHTMLAttr( skipIcon ) //15 + << escapeHTML( i18n( "Love" ) ) //16 + << escapeHTMLAttr( loveIcon ) //17 + << escapeHTML( i18n( "Ban" ) ) //18 + << escapeHTMLAttr( banIcon ) //19 + ) ); + + addMetaHistory(); + + if( ContextBrowser::instance()->m_showRelated || ContextBrowser::instance()->m_showSuggested ) + { + QStringList relArtists = CollectionDB::instance()->similarArtists( currentTrack.artist(), 10 ); + if ( !relArtists.isEmpty() ) + { + if( ContextBrowser::instance()->m_showRelated ) + showRelatedArtists( currentTrack.artist(), relArtists ); + + if( ContextBrowser::instance()->m_showSuggested ) + showSuggestedSongs( relArtists ); + } + } + + const uint artist_id = CollectionDB::instance()->artistID( currentTrack.artist(), false /* don't autocreate */ ); + if( artist_id ) + { + if( ContextBrowser::instance()->m_showFaves ) + showArtistsFaves( currentTrack.artist(), artist_id ); + + const uint album_id = CollectionDB::instance()->albumID ( currentTrack.album(), false /* don't autocreate */ ); + showArtistsAlbums( currentTrack.artist(), artist_id, album_id ); + showArtistsCompilations( currentTrack.artist(), artist_id, album_id ); + } + + m_HTMLSource.append( "\n" ); +} + + +void CurrentTrackJob::showStream( const MetaBundle ¤tTrack ) +{ + m_HTMLSource.append( QStringx( + "
\n" + "
\n" + "%1 " + "
\n" + "\n" + "\n" + "\n" + "\n" + "
\n" + "%2\n" + "
\n" + "
\n" + "%3" + "
\n" + "
\n" + "%4" + "
\n" + "%5 kbps" + "
\n" + "%6" + "
\n" + "%7" + "
\n" + "
\n" ) + .args( QStringList() + << i18n( "Stream Details" ) + << escapeHTML( currentTrack.prettyTitle() ) + << escapeHTML( currentTrack.streamName() ) + << escapeHTML( currentTrack.genre() ) + << escapeHTML( currentTrack.prettyBitrate() ) + << escapeHTML( currentTrack.streamUrl() ) + << escapeHTML( currentTrack.prettyURL() ) ) ); + + addMetaHistory(); + + m_HTMLSource.append( "\n" ); +} + +void CurrentTrackJob::addMetaHistory() +{ + if ( m_metadataHistory.count() > 0 ) + { + m_HTMLSource.append( + "
\n" + "
\n" + i18n( "Metadata History" ) + "
\n" + "\n" ); + + for ( uint i = 0; i < m_metadataHistory.count(); ++i ) + { + const QString &str = m_metadataHistory[i]; + m_HTMLSource.append( QStringx( "\n" ).arg( str ) ); + } + + m_HTMLSource.append( + "
%1
\n" + "
\n" ); + } +} + +void CurrentTrackJob::showPodcast( const MetaBundle ¤tTrack ) +{ + if( !currentTrack.podcastBundle() ) + return; + + PodcastEpisodeBundle peb = *currentTrack.podcastBundle(); + PodcastChannelBundle pcb; + bool channelInDB = true; + if( !CollectionDB::instance()->getPodcastChannelBundle( peb.parent(), &pcb ) ) + { + pcb.setTitle( i18n( "Unknown Channel (not in Database)" ) ); + channelInDB = false; + } + + QString image; + if( pcb.imageURL().isValid() ) + image = CollectionDB::instance()->podcastImage( pcb.imageURL().url(), true ); + else + image = CollectionDB::instance()->notAvailCover( true ); + + QString imageAttr = escapeHTMLAttr( pcb.link().isValid() + ? i18n( "Click to go to podcast website: %1." ).arg( pcb.link().prettyURL() ) + : i18n( "No podcast website." ) + ); + + m_HTMLSource.append( QStringx( + "
\n" + "
\n" + "%1 " + "
\n" + "%2\n" + "
\n" + + "\n" + "\n" + "\n" + "\n" + "
\n" + "\n" + "\n" + "\n" + "\n" + "%6" + "%7" + "
\n" + "
\n" ) + .args( QStringList() + << escapeHTML( pcb.title() ) + << escapeHTML( peb.title() ) + << ( pcb.link().isValid() + ? pcb.link().url().replace( QRegExp( "^http:" ), "externalurl:" ) + : "current://track" ) + << image + << imageAttr + << escapeHTML( peb.author().isEmpty() + ? i18n( "Podcast" ) + : i18n( "Podcast by %1" ).arg( peb.author() ) ) + << ( peb.localUrl().isValid() + ? "
\n" + escapeHTML( i18n( "(Cached)" ) ) + : "" ) + ) + ); + + if ( m_isStream && m_metadataHistory.count() > 1 ) + { + m_HTMLSource.append( + "
\n" + "
\n" + i18n( "Metadata History" ) + "
\n" + "\n" ); + + for ( uint i = 0; i < m_metadataHistory.count(); ++i ) + { + const QString &str = m_metadataHistory[i]; + m_HTMLSource.append( QStringx( "\n" ).arg( str ) ); + } + + m_HTMLSource.append( + "
%1
\n" + "
\n" ); + } + + m_HTMLSource.append( + "
\n" + "
\n" + "\n" + + ( channelInDB + ? i18n( "Episodes from %1" ).arg( escapeHTML( pcb.title() ) ) + : i18n( "Episodes from this Channel" ) + ) + + "\n" + "
\n" + "\n" ); + + uint i = 0; + QValueList episodes = CollectionDB::instance()->getPodcastEpisodes( peb.parent() ); + while( !episodes.isEmpty() ) + { + PodcastEpisodeBundle &ep = episodes.back(); + QString date; + + ep.dateTime().isNull() ? + date = ep.date() : + date = ep.dateTime().toString(); + + m_HTMLSource.append( QStringx ( + "\n" + "\n" + "\n" ); + i++; + episodes.pop_back(); + } + + m_HTMLSource.append("\n" ); +} + +void CurrentTrackJob::showBrowseArtistHeader( const QString &artist ) +{ + // + bool linkback = ( b->m_contextBackHistory.size() > 0 ); + QString back = ( linkback + ? "\n" + + escapeHTML( i18n( "<- Back" ) ) + + "\n" + : QString( "" ) + ); + m_HTMLSource.append( + QString( + "
\n" + "
\n" + "%1\n" + "
\n" + "
\n" + "
\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "
\n" + "%2 " + "%4\n" + "
\n" + "%5\n" + "
\n" + "
\n" + "
\n" ) + .args( QStringList() + << QString::number( i ) + << escapeHTML( ep.duration() ? MetaBundle::prettyTime( ep.duration() ) : QString( "" ) ) + << ( ep.localUrl().isValid() + ? ep.localUrl().url() + : ep.url().url().replace( QRegExp( "^http:" ), "stream:" ) ) + << escapeHTML( ep.title() ) + << escapeHTML( date ) + << (peb.url() == ep.url() ? "block" : "none" ) + << QString::number( i ) + ) + ); + + m_HTMLSource.append( QStringx ( "

%1

\n" ).arg( ep.description() ) ); + + m_HTMLSource.append( + "
\n" + "
\n" + "\n" + "\n" + "
%2
%3
\n" + "
\n" ) + .arg( escapeHTML( artist ) ) + .arg( escapeHTML( i18n( "Browse Artist" ) ) ) + .arg( back ) ); + m_HTMLSource.append( + "\n" + ); + + m_HTMLSource.append( + "\n" + "\n" + "\n" + ); + + m_HTMLSource.append( + "\n" + "\n" + "\n"); + m_HTMLSource.append( + "\n" + "\n" + "\n" + ); + + m_HTMLSource.append( + "\n" + "\n" + "
\n" + + QString( "\n" ) + + i18n( "Information for Current Track" ) + + "\n" + "
\n" + + QString( "\n" ).arg( escapeHTMLAttr( artist + b->wikiArtistPostfix() ) ) + + i18n( "Wikipedia Information for %1" ).arg( escapeHTML( artist ) ) + + "\n" + "
\n" + + QString( "\n" ).arg( escapeHTMLAttr( artist ) ) + + i18n( "Google Musicsearch for %1" ).arg( escapeHTML( artist ) ) + + "\n" + "
\n" + "\n" ); + // +} + +void +CurrentTrackJob::showBrowseLabelHeader( const QString &label ) +{ + bool linkback = ( b->m_contextBackHistory.size() > 0 ); + QString back = ( linkback + ? "\n" + + escapeHTML( i18n( "<- Back" ) ) + + "\n" + : QString( "" ) + ); + m_HTMLSource.append( + QString( + "
\n" + "
\n" + "%1\n" + "
\n" + "\n" + "\n" + "\n" + "
%2
%3
\n" + "
\n" ) + .arg( escapeHTML( label ) ) + .arg( escapeHTML( i18n( "Browse Label" ) ) ) + .arg( back ) ); + m_HTMLSource.append( + "\n" + ); + + m_HTMLSource.append( + "\n" + "\n" + "\n" + ); + + m_HTMLSource.append( + "\n" + "\n" + "\n"); + + m_HTMLSource.append( + "\n" + "\n" + "
\n" + + QString( "\n" ) + + i18n( "Information for Current Track" ) + + "\n" + "
\n" + + QString( "\n" ).arg( escapeHTMLAttr( label ) ) + + i18n( "Last.fm Information for %1" ).arg( escapeHTML( label ) ) + + "\n" + "
\n" + "
\n" ); +} + +void CurrentTrackJob::showCurrentArtistHeader( const MetaBundle ¤tTrack ) +{ + QueryBuilder qb; + QStringList values; + // + qb.clear(); + qb.addReturnValue( QueryBuilder::tabStats, QueryBuilder::valCreateDate ); + qb.addReturnValue( QueryBuilder::tabStats, QueryBuilder::valAccessDate ); + qb.addReturnValue( QueryBuilder::tabStats, QueryBuilder::valPlayCounter ); + qb.addReturnValue( QueryBuilder::tabStats, QueryBuilder::valScore ); + qb.addReturnValue( QueryBuilder::tabStats, QueryBuilder::valRating ); + qb.addMatch( QueryBuilder::tabStats, QueryBuilder::valURL, currentTrack.url().path() ); + values = qb.run(); + usleep( 10000 ); + + //making 2 tables is most probably not the cleanest way to do it, but it works. + QString albumImage = ContextBrowser::getEncodedImage( CollectionDB::instance()->albumImage( currentTrack, true, 1 ) ); + QString albumImageTitleAttr = albumImageTooltip( albumImage, 0 ); + + bool isCompilation = false; + if( !currentTrack.album().isEmpty() ) + { + isCompilation = CollectionDB::instance()->albumIsCompilation( + QString::number( CollectionDB::instance()->albumID( currentTrack.album() ) ) + ); + } + + m_HTMLSource.append( + "
\n" + "
\n" + // Show "Title - Artist \n Album", or only "PrettyTitle" if there's no title tag + + ( !currentTrack.title().isEmpty() + ? QStringx( + "%1 " + "- " + "%2\n" + "
\n" + "%3\n" + "
\n" + "\n" + "\n" + "\n" + "\n" ).arg( i18n( "Score: %1" ).arg( score ) ) + + "\n" + "\n" + "\n"; + + if( AmarokConfig::useRatings() ) + { + contents += QString( "\n" ).arg( i18n( "Rating: %1" ) + .arg( MetaBundle::ratingDescription( rating ) ) ) + + "\n" + "\n"; + } + + return table.arg( contents ); +} + +bool CurrentTrackJob::doJob() +{ + m_HTMLSource.append( "\n" + "\n" ); + + if( !b->m_browseArtists ) + { + if( !EngineController::engine()->loaded() ) + { + showHome(); + return true; + } + MetaBundle mb( m_currentTrack.url() ); + if( mb.podcastBundle() ) + { + showPodcast( mb ); + return true; + } + + if( m_currentTrack.url().protocol() == "lastfm" ) + { + showLastFm( m_currentTrack ); + return true; + } + + if( m_isStream && m_currentTrack.url().protocol() != "daap" ) + { + showStream( m_currentTrack ); + return true; + } + } + + QString artist; + if( b->m_browseArtists ) + { + artist = b->m_artist; + if( artist == m_currentTrack.artist() ) + { + b->m_browseArtists = false; + b->m_artist = QString::null; + b->m_contextBackHistory.clear(); + b->m_contextBackHistory.push_back( "current://track" ); + } + } + else + artist = m_currentTrack.artist(); + + const uint artist_id = CollectionDB::instance()->artistID( artist ); + const uint album_id = CollectionDB::instance()->albumID ( m_currentTrack.album() ); + QueryBuilder qb; + QStringList values; + if( b->m_browseArtists ) + showBrowseArtistHeader( artist ); + else if( b->m_browseLabels ) + { + showBrowseLabelHeader( b->m_label ); + showSongsWithLabel( b->m_label ); + m_HTMLSource.append( "\n" ); + + return true; + } + else + showCurrentArtistHeader( m_currentTrack ); + + if ( ContextBrowser::instance()->m_showLabels && !b->m_browseArtists ) + showUserLabels( m_currentTrack ); + + if( ContextBrowser::instance()->m_showRelated || ContextBrowser::instance()->m_showSuggested ) + { + QStringList relArtists = CollectionDB::instance()->similarArtists( artist, 10 ); + if ( !relArtists.isEmpty() ) + { + if( ContextBrowser::instance()->m_showRelated ) + showRelatedArtists( artist, relArtists ); + + if( ContextBrowser::instance()->m_showSuggested ) + showSuggestedSongs( relArtists ); + } + } + + QString artistName = artist.isEmpty() ? i18n( "This Artist" ) : artist ; + if ( !artist.isEmpty() ) + { + if( ContextBrowser::instance()->m_showFaves ) + showArtistsFaves( artistName, artist_id ); + + showArtistsAlbums( artist, artist_id, album_id ); + showArtistsCompilations( artist, artist_id, album_id ); + } + m_HTMLSource.append( "\n" ); + + return true; +} + + +void ContextBrowser::showIntroduction() +{ + if ( currentPage() != m_contextTab ) + { + blockSignals( true ); + showPage( m_contextTab ); + blockSignals( false ); + } + + // Do we have to rebuild the page? I don't care + m_HTMLSource = QString::null; + m_HTMLSource.append( + "\n" + "
\n" + "
\n" + "\n" + + i18n( "Hello Amarok user!" ) + + "\n" + "
\n" + "
\n" + "

\n" + + i18n( "This is the Context Browser: " + "it shows you contextual information about the currently playing track. " + "In order to use this feature of Amarok, you need to build a Collection." + ) + + "

\n" + "
\n" + "

\n" + "
\n" + "
\n" + "\n" + ); + + m_currentTrackPage->set( m_HTMLSource ); + saveHtmlData(); // Send html code to file +} + + +void ContextBrowser::showScanning() +{ + if ( currentPage() != m_contextTab ) + { + blockSignals( true ); + showPage( m_contextTab ); + blockSignals( false ); + } + + // Do we have to rebuild the page? I don't care + m_HTMLSource=""; + m_HTMLSource.append( + "\n" + "
\n" + "
\n" + "\n" + + i18n( "Building Collection Database..." ) + + "\n" + "
\n" + "
\n" + "

\n" + i18n( "Please be patient while Amarok scans your music collection. You can watch the progress of this activity in the statusbar." ) + "

\n" + "
\n" + "
\n" + "\n" + ); + + m_currentTrackPage->set( m_HTMLSource ); + saveHtmlData(); // Send html code to file +} + +QString +ContextBrowser::getEncodedImage( const QString &imageUrl ) +{ + // Embed cover image in html (encoded string), to get around khtml's caching + //debug() << "Encoding imageUrl: " << imageUrl << endl; + qApp->lock(); + const QImage img( imageUrl, "PNG" ); + qApp->unlock(); + QByteArray ba; + QBuffer buffer( ba ); + buffer.open( IO_WriteOnly ); + qApp->lock(); + img.save( &buffer, "PNG" ); // writes image into ba in PNG format + qApp->unlock(); + const QString coverImage = QString( "data:image/png;base64,%1" ).arg( KCodecs::base64Encode( ba ) ); + //debug() << "Encoded imageUrl: " << coverImage << endl; + return coverImage; +} + +////////////////////////////////////////////////////////////////////////////////////////// +// Lyrics-Tab +////////////////////////////////////////////////////////////////////////////////////////// + +void ContextBrowser::showLyrics( const QString &url ) +{ + #if 0 + if( BrowserBar::instance()->currentBrowser() != this ) + { + debug() << "current browser is not context, aborting showLyrics()" << endl; + m_dirtyLyricsPage = true; + return; + } + #endif + + DEBUG_BLOCK + + if ( currentPage() != m_lyricsTab ) + { + blockSignals( true ); + showPage( m_lyricsTab ); + blockSignals( false ); + } + if ( !m_dirtyLyricsPage ) return; + + QString lyrics = CollectionDB::instance()->getLyrics( EngineController::instance()->bundle().url().path() ); + // don't rely on caching for streams + const bool cached = !lyrics.isEmpty() && !EngineController::engine()->isStream(); + QString title = EngineController::instance()->bundle().title(); + QString artist = EngineController::instance()->bundle().artist(); + + if( title.contains("PREVIEW: buy it at www.magnatune.com", true) >= 1 ) + title = title.remove(" (PREVIEW: buy it at www.magnatune.com)"); + if( artist.contains("PREVIEW: buy it at www.magnatune.com", true) >= 1 ) + artist = artist.remove(" (PREVIEW: buy it at www.magnatune.com)"); + + if ( title.isEmpty() ) { + /* If title is empty, try to use pretty title. + The fact that it often (but not always) has artist name together, can be bad, + but at least the user will hopefully get nice suggestions. */ + QString prettyTitle = EngineController::instance()->bundle().prettyTitle(); + int h = prettyTitle.find( '-' ); + if ( h != -1 ) + { + title = prettyTitle.mid( h+1 ).stripWhiteSpace(); + if( title.contains("PREVIEW: buy it at www.magnatune.com", true) >= 1 ) + title = title.remove(" (PREVIEW: buy it at www.magnatune.com)"); + if ( artist.isEmpty() ) { + artist = prettyTitle.mid( 0, h ).stripWhiteSpace(); + if( artist.contains("PREVIEW: buy it at www.magnatune.com", true) >= 1 ) + artist = artist.remove(" (PREVIEW: buy it at www.magnatune.com)"); + } + + } + } + + m_lyricSearchUrl = QString( "http://www.google.com/search?ie=UTF-8&q=lyrics+%1+%2" ) + .arg( KURL::encode_string_no_slash( '"' + artist + '"', 106 /*utf-8*/ ), + KURL::encode_string_no_slash( '"' + title + '"', 106 /*utf-8*/ ) ); + + m_lyricsToolBar->getButton( LYRICS_BROWSER )->setEnabled(false); + + if( ( !cached || url == "reload" ) && !ScriptManager::instance()->lyricsScriptRunning() ) { + const QStringList scripts = ScriptManager::instance()->lyricsScripts(); + lyrics = + i18n( "Sorry, no lyrics script running.") + "
\n" + + "
\n"+ + i18n( "Available Lyrics Scripts:" ) + "
\n"; + foreach ( scripts ) { + lyrics += QString( "%2
\n" ).arg( *it, *it ); + } + lyrics += "
\n" + i18n( "Click on one of the scripts to run it, or use the Script Manager, to be able" + " to see all the scripts, and download new ones from the Web." ); + lyrics += "
\n" + "

\n"; + + m_HTMLSource = QString ( + "\n" + "
\n" + "
\n" + "\n" + + ( cached ? i18n( "Cached Lyrics" ) : i18n( "Lyrics" ) ) + + "\n" + "
\n" + "
\n" + + lyrics + + "
\n" + "
\n" + "\n" + ); + m_lyricsPage->set( m_HTMLSource ); + + m_dirtyLyricsPage = false; + saveHtmlData(); // Send html code to file + + return; + } + + if( cached && url.isEmpty() ) + { + lyricsResult( lyrics.utf8(), true ); + } + else + { + m_HTMLSource = QString ( + "\n" + "
\n" + "
\n" + "\n" + + i18n( "Fetching Lyrics" ) + + "\n" + "
\n" + "
\n" + "

\n" + i18n( "Fetching Lyrics..." ) + "

\n" + "
\n" + "
\n" + "\n" + ); + m_lyricsPage->set( m_HTMLSource ); + saveHtmlData(); // Send html code to file + + + if( url.isNull() || url == "reload" ) + ScriptManager::instance()->notifyFetchLyrics( artist, title ); + else + ScriptManager::instance()->notifyFetchLyricsByUrl( url ); + } +} + + +void +ContextBrowser::lyricsResult( QCString cXmlDoc, bool cached ) //SLOT +{ + QDomDocument doc; + QString xmldoc = QString::fromUtf8( cXmlDoc ); + if( !doc.setContent( xmldoc ) ) + { + m_HTMLSource=""; + m_HTMLSource.append( + "\n" + "
\n" + "
\n" + "\n" + + i18n( "Error" ) + + "\n" + "
\n" + "

\n" + + i18n( "Lyrics could not be retrieved because the server was not reachable." ) + + "

\n" + "
\n" + "\n" + ); + m_lyricsPage->set( m_HTMLSource ); + saveHtmlData(); // Send html code to file + + m_dirtyLyricsPage = false; + + return; + } + + QString lyrics; + + QDomElement el = doc.documentElement(); + m_lyricCurrentUrl = el.attribute( "page_url" ); + + ScriptManager* const sm = ScriptManager::instance(); + KConfig spec( sm->specForScript( sm->lyricsScriptRunning() ), true, false ); + spec.setGroup( "Lyrics" ); + + if ( el.attribute( "add_url" ).isEmpty() ) + { + m_lyricAddUrl = spec.readPathEntry( "add_url" ); + m_lyricAddUrl.replace( "MAGIC_ARTIST", KURL::encode_string_no_slash( EngineController::instance()->bundle().artist() ) ); + m_lyricAddUrl.replace( "MAGIC_TITLE", KURL::encode_string_no_slash( EngineController::instance()->bundle().title() ) ); + m_lyricAddUrl.replace( "MAGIC_ALBUM", KURL::encode_string_no_slash( EngineController::instance()->bundle().album() ) ); + m_lyricAddUrl.replace( "MAGIC_YEAR", KURL::encode_string_no_slash( QString::number( EngineController::instance()->bundle().year() ) ) ); + } + else + m_lyricAddUrl = el.attribute( "add_url" ); + + if ( el.tagName() == "suggestions" ) + { + + + const QDomNodeList l = doc.elementsByTagName( "suggestion" ); + + if( l.length() ==0 ) + { + lyrics = i18n( "Lyrics for track not found" ); + } + else + { + lyrics = i18n( "Lyrics for track not found, here are some suggestions:" ) + "

\n"; + for( uint i = 0; i < l.length(); ++i ) { + const QString url = l.item( i ).toElement().attribute( "url" ); + const QString artist = l.item( i ).toElement().attribute( "artist" ); + const QString title = l.item( i ).toElement().attribute( "title" ); + + lyrics += "\n" + i18n("%1 - %2").arg( artist, title ); + lyrics += "
\n"; + } + } + lyrics += i18n( "

You can search for the lyrics on the Web.

" ) + .arg( QString( m_lyricSearchUrl ).replace( QRegExp( "^http:" ), "externalurl:" ) ); + } + else { + lyrics = el.text(); + lyrics.replace( "\n", "
\n" ); // Plaintext -> HTML + + const QString title = el.attribute( "title" ); + const QString artist = el.attribute( "artist" ); + const QString site = el.attribute( "site" ).isEmpty() ? spec.readEntry( "site" ) : el.attribute( "site" ); + const QString site_url = el.attribute( "site_url" ).isEmpty() ? spec.readEntry( "site_url" ) : el.attribute( "site_url" ); + + lyrics.prepend( "\n" + title + "
\n" + artist+ "

\n" ); + + if( !cached ) { + lyrics.append( "

\n" + i18n( "Powered by %1 (%2)" ).arg( site, site_url ) + "\n" ); + CollectionDB::instance()->setLyrics( EngineController::instance()->bundle().url().path(), xmldoc, EngineController::instance()->bundle().uniqueId() ); + } + } + + m_HTMLSource=""; + m_HTMLSource.append( + "\n" + "
\n" + "
\n" + "\n" + + ( cached ? i18n( "Cached Lyrics" ) : i18n( "Lyrics" ) ) + + "\n" + "
\n" + "
\n" + + lyrics + + "
\n" + "
\n" + "\n" + ); + + + m_lyricsPage->set( m_HTMLSource ); + //Reset scroll + m_lyricsPage->view()->setContentsPos(0, 0); + saveHtmlData(); // Send html code to file + + m_lyricsToolBar->getButton( LYRICS_BROWSER )->setEnabled( !m_lyricCurrentUrl.isEmpty() ); + m_dirtyLyricsPage = false; +} + + +void +ContextBrowser::lyricsExternalPage() //SLOT +{ + Amarok::invokeBrowser( m_lyricCurrentUrl ); +} + + +void +ContextBrowser::lyricsAdd() //SLOT +{ + Amarok::invokeBrowser( m_lyricAddUrl ); +} + +void +ContextBrowser::lyricsEditToggle() //SLOT +{ + if ( m_lyricsToolBar->getButton( LYRICS_EDIT )->isOn() ) + { + m_lyricsBeingEditedUrl = EngineController::instance()->bundle().url().path(); + m_lyricsBeingEditedArtist = EngineController::instance()->bundle().artist(); + m_lyricsBeingEditedTitle = EngineController::instance()->bundle().title(); + QString xml = CollectionDB::instance()->getLyrics( m_lyricsBeingEditedUrl ), lyrics; + QDomDocument doc; + if( doc.setContent( xml ) ) + lyrics = doc.documentElement().text(); + else + lyrics = QString::null; + m_lyricsTextEdit->setText( lyrics ); + m_lyricsPage->hide(); + m_lyricsTextEdit->show(); + } + else + { + m_lyricsTextEdit->hide(); + + QDomDocument doc; + QDomElement e = doc.createElement( "lyrics" ); + e.setAttribute( "artist", m_lyricsBeingEditedArtist ); + e.setAttribute( "title", m_lyricsBeingEditedTitle ); + QDomText t = doc.createTextNode( m_lyricsTextEdit->text() ); + e.appendChild( t ); + doc.appendChild( e ); + CollectionDB::instance()->setLyrics( m_lyricsBeingEditedUrl, doc.toString(), CollectionDB::instance()->uniqueIdFromUrl( KURL( m_lyricsBeingEditedUrl) ) ); + m_lyricsPage->show(); + lyricsChanged( m_lyricsBeingEditedUrl ); + } +} + +void +ContextBrowser::lyricsSearch() //SLOT +{ + Amarok::invokeBrowser( m_lyricSearchUrl ); +} + + +void +ContextBrowser::lyricsRefresh() //SLOT +{ + m_dirtyLyricsPage = true; + showLyrics( "reload" ); +} + +void +ContextBrowser::lyricsSearchText(QString const &text) //SLOT +{ + m_lyricsPage->findText( text, 0 ); + lyricsSearchTextNext(); +} + +void +ContextBrowser::lyricsSearchTextNext() //SLOT +{ + m_lyricsPage->findTextNext(); +} + +void +ContextBrowser::lyricsSearchTextShow() //SLOT +{ + m_lyricsSearchText->setEnabled( true ); + m_lyricsTextBar->show(); + m_lyricsTextBarShowed = true; + m_lyricsSearchText->setFocus(); +} + + +void +ContextBrowser::lyricsSearchTextHide() //SLOT +{ + m_lyricsSearchText->setText(""); + m_lyricsSearchText->setEnabled( false ); + m_lyricsTextBar->hide(); + m_lyricsTextBarShowed=false; +} + + +void +ContextBrowser::lyricsSearchTextToggle() //SLOT +{ + if ( m_lyricsTextBarShowed ) + { + lyricsSearchTextHide(); + } + else + { + lyricsSearchTextShow(); + } +} + +// Wikipedia-Tab +////////////////////////////////////////////////////////////////////////////////////////// + +QString +ContextBrowser::wikiArtistPostfix() const +{ + if( wikiLocale() == "en" ) + return " (band)"; + else if( wikiLocale() == "de" ) + return " (Band)"; + else + return ""; +} + +QString +ContextBrowser::wikiAlbumPostfix() const +{ + if( wikiLocale() == "en" ) + return " (album)"; + else + return ""; +} + +QString +ContextBrowser::wikiTrackPostfix() const +{ + if( wikiLocale() == "en" ) + return " (song)"; + else + return ""; +} + +void +ContextBrowser::wikiConfigChanged( int /*activeItem*/ ) // SLOT +{ + // keep in sync with localeList in wikiConfig + QString text = m_wikiLocaleCombo->currentText(); + + m_wikiLocaleEdit->setEnabled( text == i18n("Other...") ); + + if( text == i18n("English") ) + m_wikiLocaleEdit->setText( "en" ); + + else if( text == i18n("German") ) + m_wikiLocaleEdit->setText( "de" ); + + else if( text == i18n("French") ) + m_wikiLocaleEdit->setText( "fr" ); + + else if( text == i18n("Polish") ) + m_wikiLocaleEdit->setText( "pl" ); + + else if( text == i18n("Japanese") ) + m_wikiLocaleEdit->setText( "ja" ); + + else if( text == i18n("Spanish") ) + m_wikiLocaleEdit->setText( "es" ); +} + +void +ContextBrowser::wikiConfigApply() // SLOT +{ + const bool changed = m_wikiLocaleEdit->text() != wikiLocale(); + setWikiLocale( m_wikiLocaleEdit->text() ); + + if ( changed && currentPage() == m_wikiTab && !m_wikiCurrentEntry.isNull() ) + { + m_dirtyWikiPage = true; + showWikipediaEntry( m_wikiCurrentEntry ); + } + + showWikipedia(); +} + +void +ContextBrowser::wikiConfig() // SLOT +{ + QStringList localeList; + localeList + << i18n( "English" ) + << i18n( "German" ) + << i18n( "French" ) + << i18n( "Polish" ) + << i18n( "Japanese" ) + << i18n( "Spanish" ) + << i18n( "Other..." ); + + int index; + + if( wikiLocale() == "en" ) + index = 0; + else if( wikiLocale() == "de" ) + index = 1; + else if( wikiLocale() == "fr" ) + index = 2; + else if( wikiLocale() == "pl" ) + index = 3; + else if( wikiLocale() == "ja" ) + index = 4; + else if( wikiLocale() == "es" ) + index = 5; + else // other + index = 6; + + m_wikiConfigDialog = new KDialogBase( this, 0, true, 0, KDialogBase::Ok|KDialogBase::Apply|KDialogBase::Cancel ); + kapp->setTopWidget( m_wikiConfigDialog ); + m_wikiConfigDialog->setCaption( kapp->makeStdCaption( i18n( "Wikipedia Locale" ) ) ); + QVBox *box = m_wikiConfigDialog->makeVBoxMainWidget(); + + m_wikiLocaleCombo = new QComboBox( box ); + m_wikiLocaleCombo->insertStringList( localeList ); + + QHBox *hbox = new QHBox( box ); + QLabel *otherLabel = new QLabel( i18n( "Locale: " ), hbox ); + m_wikiLocaleEdit = new QLineEdit( "en", hbox ); + + otherLabel->setBuddy( m_wikiLocaleEdit ); + QToolTip::add( m_wikiLocaleEdit, i18n( "2-letter language code for your Wikipedia locale" ) ); + + connect( m_wikiLocaleCombo, SIGNAL( activated(int) ), SLOT( wikiConfigChanged(int) ) ); + connect( m_wikiConfigDialog, SIGNAL( applyClicked() ), SLOT( wikiConfigApply() ) ); + + m_wikiLocaleEdit->setText( wikiLocale() ); + m_wikiLocaleCombo->setCurrentItem( index ); + wikiConfigChanged( index ); // a little redundant, but saves ugly code, and ensures the lineedit enabled status is correct + + m_wikiConfigDialog->setInitialSize( QSize( 240, 100 ) ); + const int result = m_wikiConfigDialog->exec(); + + + if( result == QDialog::Accepted ) + wikiConfigApply(); + + delete m_wikiConfigDialog; +} + +QString +ContextBrowser::wikiLocale() +{ + if( s_wikiLocale.isEmpty() ) + return QString( "en" ); + + return s_wikiLocale; +} + +void +ContextBrowser::setWikiLocale( const QString &locale ) +{ + AmarokConfig::setWikipediaLocale( locale ); + s_wikiLocale = locale; +} + +QString +ContextBrowser::wikiURL( const QString &item ) +{ + // add any special characters to be replaced here + QString wStr = QString(item).replace( "/", " " ); + + return QString( "http://%1.wikipedia.org/wiki/" ).arg( wikiLocale() ) + + KURL::encode_string_no_slash( wStr, 106 /*utf-8*/ ); +} + +void +ContextBrowser::reloadWikipedia() +{ + m_wikiJob = NULL; + showWikipediaEntry( m_wikiCurrentEntry, true ); +} + +void +ContextBrowser::showWikipediaEntry( const QString &entry, bool replaceHistory ) +{ + m_wikiCurrentEntry = entry; + showWikipedia( wikiURL( entry ), false, replaceHistory ); +} + +void +ContextBrowser::showLabelsDialog() +{ + DEBUG_BLOCK + KURL currentUrl = EngineController::instance()->bundle().url(); + QStringList allLabels = CollectionDB::instance()->labelList(); + QStringList trackLabels = CollectionDB::instance()->getLabels( currentUrl.path(), CollectionDB::typeUser ); + debug() << "Showing add label dialog" << endl; + KDialogBase *dialog = new KDialogBase( this, 0, false, QString::null, KDialogBase::Ok|KDialogBase::Cancel ); + dialog->makeVBoxMainWidget(); + + QLabel *labelText = new QLabel( i18n( + "

Add a new label in the field below and press Enter, or choose labels from the list

"), + dialog->mainWidget() ); + m_addLabelEdit = new ClickLineEdit( i18n( "Add new label" ), dialog->mainWidget() ); + m_addLabelEdit->installEventFilter( this ); + m_addLabelEdit->setFrame( QFrame::Sunken ); + QToolTip::add( m_addLabelEdit, i18n( "Enter a new label and press Return to add it" ) ); + dialog->setFocusProxy( m_addLabelEdit ); + labelText->setBuddy( m_addLabelEdit ); + + m_labelListView = new QListView( dialog->mainWidget() ); + m_labelListView->addColumn( i18n( "Label" ) ); + m_labelListView->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ); + m_labelListView->setColumnWidthMode( 0, QListView::Maximum ); + + foreach( allLabels ) + { + QCheckListItem *item = new QCheckListItem( m_labelListView, *it, QCheckListItem::CheckBox ); + item->setOn( trackLabels.contains( *it ) ); + } + if( dialog->exec() == QDialog::Accepted ) + { + debug() << "Dialog closed, updating labels" << endl; + QStringList newTrackLabels; + QListViewItemIterator iter( m_labelListView ); + while( iter.current() ) + { + QCheckListItem *item = static_cast( iter.current() ); + if( item->isOn() ) + newTrackLabels.append( item->text() ); + iter++; + } + CollectionDB::instance()->setLabels( currentUrl.path(), + newTrackLabels, + CollectionDB::instance()->uniqueIdFromUrl( currentUrl ), + CollectionDB::typeUser ); + CollectionDB::instance()->cleanLabels(); + if( newTrackLabels != trackLabels + && currentUrl == EngineController::instance()->bundle().url() ) + { + m_dirtyCurrentTrackPage = true; + showCurrentTrack(); + } + } + delete dialog; //deletes children + m_addLabelEdit = 0; + m_labelListView = 0; +} + +bool +ContextBrowser::eventFilter( QObject *o, QEvent *e ) +{ + switch( e->type() ) + { + case 6/*QEvent::KeyPress*/: + #define e static_cast(e) + + if( o == m_addLabelEdit ) //the add label lineedit + { + switch( e->key() ) + { + case Key_Return: + case Key_Enter: + { + QCheckListItem *item = new QCheckListItem( m_labelListView, m_addLabelEdit->text(), QCheckListItem::CheckBox ); + item->setOn( true ); + m_addLabelEdit->setText( QString() ); + return true; + } + + default: + return false; + } + } + if (o == m_lyricsSearchText) + { + switch ( e->key() ) + { + case Key_Escape: + { + lyricsSearchTextHide(); + return true; + } + default: + return false; + } + } + + default: + break; + } + + return KTabWidget::eventFilter( o, e ); +} + +void ContextBrowser::showWikipedia( const QString &url, bool fromHistory, bool replaceHistory ) +{ + #if 0 + if( BrowserBar::instance()->currentBrowser() != this ) + { + debug() << "current browser is not context, aborting showWikipedia()" << endl; + m_dirtyWikiPage = true; + return; + } + #endif + + if ( currentPage() != m_wikiTab ) + { + blockSignals( true ); + showPage( m_wikiTab ); + blockSignals( false ); + } + if ( !m_dirtyWikiPage || m_wikiJob ) return; + + // Disable the Open in a Browser button, because while loading it would open wikipedia main page. + m_wikiToolBar->setItemEnabled( WIKI_BROWSER, false ); + + m_HTMLSource=""; + m_HTMLSource.append( + "\n" + "
\n" + "
\n" + "\n" + + i18n( "Wikipedia" ) + + "\n" + "
\n" + "
\n" + "

\n" + i18n( "Fetching Wikipedia Information" ) + " ...

\n" + "
\n" + "
\n" + "\n" + ); + + m_wikiPage->set( m_HTMLSource ); + saveHtmlData(); // Send html code to file + + if ( url.isEmpty() ) + { + QString tmpWikiStr; + + if ( (EngineController::instance()->bundle().url().protocol() == "lastfm") || + (EngineController::instance()->bundle().url().protocol() == "daap") || + !EngineController::engine()->isStream() ) + { + if ( !EngineController::instance()->bundle().artist().isEmpty() ) + { + tmpWikiStr = EngineController::instance()->bundle().artist(); + tmpWikiStr += wikiArtistPostfix(); + } + else if ( !EngineController::instance()->bundle().title().isEmpty() ) + { + tmpWikiStr = EngineController::instance()->bundle().title(); + } + else + { + tmpWikiStr = EngineController::instance()->bundle().prettyTitle(); + } + } + else + { + tmpWikiStr = EngineController::instance()->bundle().prettyTitle(); + } + + //Hack to make wiki searches work with magnatune preview tracks + if (tmpWikiStr.contains( "PREVIEW: buy it at www.magnatune.com" ) >= 1 ) { + tmpWikiStr = tmpWikiStr.remove(" (PREVIEW: buy it at www.magnatune.com)" ); + int index = tmpWikiStr.find( '-' ); + if ( index != -1 ) { + tmpWikiStr = tmpWikiStr.left (index - 1); + } + + } + m_wikiCurrentEntry = tmpWikiStr; + + m_wikiCurrentUrl = wikiURL( tmpWikiStr ); + } + else + { + m_wikiCurrentUrl = url; + } + + // Append new URL to history + if ( replaceHistory ) + { + m_wikiBackHistory.back() = m_wikiCurrentUrl; + } + else if ( !fromHistory ) { + m_wikiBackHistory += m_wikiCurrentUrl; + m_wikiForwardHistory.clear(); + } + // Limit number of items in history + if ( m_wikiBackHistory.count() > WIKI_MAX_HISTORY ) + m_wikiBackHistory.pop_front(); + + // Remove all items from the button-menus + m_wikiBackPopup->clear(); + m_wikiForwardPopup->clear(); + + // Populate button menus with URLs from the history + QStringList::ConstIterator it; + uint count; + // Reverse iterate over both lists + count = m_wikiBackHistory.count()-1; + it = m_wikiBackHistory.fromLast(); + if( count > 0 ) + it--; + for ( uint i=0; iinsertItem( SmallIconSet( "wiki" ), *it, i ); + count = m_wikiForwardHistory.count(); + it = m_wikiForwardHistory.fromLast(); + for ( uint i=0; iinsertItem( SmallIconSet( "wiki" ), *it, i ); + + m_wikiToolBar->setItemEnabled( WIKI_BACK, m_wikiBackHistory.size() > 1 ); + m_wikiToolBar->setItemEnabled( WIKI_FORWARD, m_wikiForwardHistory.size() > 0 ); + + m_wikiBaseUrl = m_wikiCurrentUrl.mid(0 , m_wikiCurrentUrl.find("wiki/")); + m_wikiJob = KIO::storedGet( m_wikiCurrentUrl, false, false ); + + Amarok::StatusBar::instance()->newProgressOperation( m_wikiJob ) + .setDescription( i18n( "Fetching Wikipedia Information" ) ); + + connect( m_wikiJob, SIGNAL( result( KIO::Job* ) ), SLOT( wikiResult( KIO::Job* ) ) ); +} + + +void +ContextBrowser::wikiHistoryBack() //SLOT +{ + //Disable the button as history may be empty. Reenabled later by showWikipedia. + m_wikiToolBar->setItemEnabled( WIKI_BACK, false ); + m_wikiToolBar->setItemEnabled( WIKI_FORWARD, false ); + + m_wikiForwardHistory += m_wikiBackHistory.last(); + m_wikiBackHistory.pop_back(); + + m_dirtyWikiPage = true; + m_wikiCurrentEntry = QString::null; + showWikipedia( m_wikiBackHistory.last(), true ); +} + + +void +ContextBrowser::wikiHistoryForward() //SLOT +{ + //Disable the button as history may be empty. Reenabled later by showWikipedia. + m_wikiToolBar->setItemEnabled( WIKI_FORWARD, false ); + m_wikiToolBar->setItemEnabled( WIKI_BACK, false ); + + m_wikiBackHistory += m_wikiForwardHistory.last(); + m_wikiForwardHistory.pop_back(); + + m_dirtyWikiPage = true; + m_wikiCurrentEntry = QString::null; + showWikipedia( m_wikiBackHistory.last(), true ); +} + + +void +ContextBrowser::wikiBackPopupActivated( int id ) //SLOT +{ + do + { + m_wikiForwardHistory += m_wikiBackHistory.last(); + m_wikiBackHistory.pop_back(); + if ( m_wikiForwardHistory.count() > WIKI_MAX_HISTORY ) + m_wikiForwardHistory.pop_front(); + id--; + } while( id >= 0 ); + + m_dirtyWikiPage = true; + m_wikiCurrentEntry = QString::null; + showWikipedia( m_wikiBackHistory.last(), true ); +} + +void +ContextBrowser::wikiForwardPopupActivated( int id ) //SLOT +{ + do + { + m_wikiBackHistory += m_wikiForwardHistory.last(); + m_wikiForwardHistory.pop_back(); + if ( m_wikiBackHistory.count() > WIKI_MAX_HISTORY ) + m_wikiBackHistory.pop_front(); + + id--; + } while( id >= 0 ); + + m_dirtyWikiPage = true; + m_wikiCurrentEntry = QString::null; + showWikipedia( m_wikiBackHistory.last(), true ); +} + + +void +ContextBrowser::wikiArtistPage() //SLOT +{ + m_dirtyWikiPage = true; + showWikipedia(); // Will fall back to title, if artist is empty(streams!). +} + + +void +ContextBrowser::wikiAlbumPage() //SLOT +{ + m_dirtyWikiPage = true; + showWikipediaEntry( EngineController::instance()->bundle().album() + wikiAlbumPostfix() ); +} + + +void +ContextBrowser::wikiTitlePage() //SLOT +{ + m_dirtyWikiPage = true; + showWikipediaEntry( EngineController::instance()->bundle().title() + wikiTrackPostfix() ); +} + + +void +ContextBrowser::wikiExternalPage() //SLOT +{ + Amarok::invokeBrowser( m_wikiCurrentUrl ); +} + + +void +ContextBrowser::wikiResult( KIO::Job* job ) //SLOT +{ + DEBUG_BLOCK + + if ( !job->error() == 0 ) + { + m_HTMLSource=""; + m_HTMLSource.append( + "
\n" + "
\n" + "\n" + + i18n( "Error" ) + + "\n" + "
\n" + "

\n" + + i18n( "Artist information could not be retrieved because the server was not reachable." ) + + "

\n" + "
\n" + ); + m_wikiPage->set( m_HTMLSource ); + + m_dirtyWikiPage = false; + //m_wikiPage = NULL; // FIXME: what for? leads to crashes + saveHtmlData(); // Send html code to file + + warning() << "[WikiFetcher] KIO error! errno: " << job->error() << endl; + return; + } + if ( job != m_wikiJob ) + return; //not the right job, so let's ignore it + + KIO::StoredTransferJob* const storedJob = static_cast( job ); + m_wiki = QString( storedJob->data() ); + + // Enable the Open in a Brower button, Disabled while loading, guz it would open wikipedia main page. + m_wikiToolBar->setItemEnabled( WIKI_BROWSER, true ); + + // FIXME: Get a safer Regexp here, to match only inside of at least. + if ( m_wiki.contains( "charset=utf-8" ) ) { + m_wiki = QString::fromUtf8( storedJob->data().data(), storedJob->data().size() ); + } + + if( m_wiki.find( "var wgArticleId = 0" ) != -1 ) + { + debug() << "Article not found." << endl; + + // article was not found + if( !wikiArtistPostfix().isEmpty() && m_wikiCurrentEntry.endsWith( wikiArtistPostfix() ) ) + { + m_wikiCurrentEntry = m_wikiCurrentEntry.left( m_wikiCurrentEntry.length() - wikiArtistPostfix().length() ); + reloadWikipedia(); + return; + } + else if( !wikiAlbumPostfix().isEmpty() && m_wikiCurrentEntry.endsWith( wikiAlbumPostfix() ) ) + { + m_wikiCurrentEntry = m_wikiCurrentEntry.left( m_wikiCurrentEntry.length() - wikiAlbumPostfix().length() ); + reloadWikipedia(); + return; + } + else if( !wikiTrackPostfix().isEmpty() && m_wikiCurrentEntry.endsWith( wikiTrackPostfix() ) ) + { + m_wikiCurrentEntry = m_wikiCurrentEntry.left( m_wikiCurrentEntry.length() - wikiTrackPostfix().length() ); + reloadWikipedia(); + return; + } + } + + //remove the new-lines and tabs(replace with spaces IS needed). + m_wiki.replace( "\n", " " ); + m_wiki.replace( "\t", " " ); + + m_wikiLanguages = QString::null; + // Get the available language list + if ( m_wiki.find("
") != -1 ) + { + m_wikiLanguages = m_wiki.mid( m_wiki.find("
") ); + m_wikiLanguages = m_wikiLanguages.mid( m_wikiLanguages.find("
    ") ); + m_wikiLanguages = m_wikiLanguages.mid( 0, m_wikiLanguages.find( "
" ) ); + } + + QString copyright; + QString copyrightMark = "
  • "; + if ( m_wiki.find( copyrightMark ) != -1 ) + { + copyright = m_wiki.mid( m_wiki.find(copyrightMark) + copyrightMark.length() ); + copyright = copyright.mid( 0, copyright.find( "
  • " ) ); + copyright.replace( "
    ", QString::null ); + //only one br at the beginning + copyright.prepend( "
    " ); + } + + // Ok lets remove the top and bottom parts of the page + m_wiki = m_wiki.mid( m_wiki.find( "

    " ) ); + m_wiki = m_wiki.mid( 0, m_wiki.find( "
    " ) ); + // Adding back license information + m_wiki += copyright; + m_wiki.append( "
    " ); + m_wiki.replace( QRegExp("

    [^<]*

    "), QString::null ); + + m_wiki.replace( QRegExp( "]*>[^<]*<[^>]*>[^<]*<[^>]*>[^<]*" ), QString::null ); + + m_wiki.replace( QRegExp( "]*>([^<]*)" ), "\\1" ); + + // Remove anything inside of a class called urlexpansion, as it's pointless for us + m_wiki.replace( QRegExp( "[^(]*[(][^)]*[)]" ), QString::null ); + + // Remove hidden table rows as well + QRegExp hidden( "
    .*", false ); + hidden.setMinimal( true ); //greedy behaviour wouldn't be any good! + m_wiki.replace( hidden, QString::null ); + + // we want to keep our own style (we need to modify the stylesheet a bit to handle things nicely) + m_wiki.replace( QRegExp( "style= *\"[^\"]*\"" ), QString::null ); + m_wiki.replace( QRegExp( "class= *\"[^\"]*\"" ), QString::null ); + // let's remove the form elements, we don't want them. + m_wiki.replace( QRegExp( "]*>" ), QString::null ); + m_wiki.replace( QRegExp( "]*>" ), QString::null ); + m_wiki.replace( "\n" , QString::null ); + m_wiki.replace( QRegExp( "]*>" ), QString::null ); + m_wiki.replace( "\n" , QString::null ); + m_wiki.replace( QRegExp( "]*>" ), QString::null ); + m_wiki.replace( "" , QString::null ); + + //first we convert all the links with protocol to external, as they should all be External Links. + m_wiki.replace( QRegExp( "href= *\"http:" ), "href=\"externalurl:" ); + m_wiki.replace( QRegExp( "href= *\"/" ), "href=\"" +m_wikiBaseUrl ); + m_wiki.replace( QRegExp( "href= *\"#" ), "href=\"" +m_wikiCurrentUrl + '#' ); + + m_HTMLSource = "\n"; + m_HTMLSource.append( + "
    \n" + "
    \n" + "\n" + + i18n( "Wikipedia Information" ) + + "\n" + "
    \n" + "
    \n" + + m_wiki + + "
    \n" + "
    \n" + ); + if ( !m_wikiLanguages.isEmpty() ) + { + m_HTMLSource.append( + "
    \n" + "
    \n" + "\n" + + i18n( "Wikipedia Other Languages" ) + + "\n" + "
    \n" + "
    \n" + + m_wikiLanguages + + "
    \n" + "
    \n" + ); + } + m_HTMLSource.append( "\n" ); + m_wikiPage->set( m_HTMLSource ); + + m_dirtyWikiPage = false; + saveHtmlData(); // Send html code to file + m_wikiJob = NULL; +} + + +void +ContextBrowser::coverFetched( const QString &artist, const QString &album ) //SLOT +{ + if ( currentPage() == m_contextTab && + !EngineController::engine()->loaded() && + !m_browseArtists ) + { + m_dirtyCurrentTrackPage = true; + if( m_shownAlbums.contains( album ) ) + showCurrentTrack(); + return; + } + + const MetaBundle ¤tTrack = EngineController::instance()->bundle(); + if ( currentTrack.artist().isEmpty() && currentTrack.album().isEmpty() ) + return; + + if ( currentPage() == m_contextTab && + ( currentTrack.artist().string() == artist || m_artist == artist || currentTrack.album().string() == album ) ) // this is for compilations or when artist is empty + { + m_dirtyCurrentTrackPage = true; + showCurrentTrack(); + } +} + + +void +ContextBrowser::coverRemoved( const QString &artist, const QString &album ) //SLOT +{ + if ( currentPage() == m_contextTab && + !EngineController::engine()->loaded() && + !m_browseArtists ) + { + m_dirtyCurrentTrackPage = true; + if( m_shownAlbums.contains( album ) ) + showCurrentTrack(); + return; + } + + const MetaBundle ¤tTrack = EngineController::instance()->bundle(); + if ( currentTrack.artist().isEmpty() && currentTrack.album().isEmpty() && m_artist.isNull() ) + return; + + if ( currentPage() == m_contextTab && + ( currentTrack.artist().string() == artist || m_artist == artist || currentTrack.album().string() == album ) ) // this is for compilations or when artist is empty + { + m_dirtyCurrentTrackPage = true; + showCurrentTrack(); + } +} + + +void +ContextBrowser::similarArtistsFetched( const QString &artist ) //SLOT +{ + if( artist == m_artist || EngineController::instance()->bundle().artist().string() == artist ) { + m_dirtyCurrentTrackPage = true; + if ( currentPage() == m_contextTab ) + showCurrentTrack(); + } +} + +void +ContextBrowser::imageFetched( const QString &url ) //SLOT +{ + const MetaBundle ¤tTrack = EngineController::instance()->bundle(); + PodcastEpisodeBundle peb; + if( CollectionDB::instance()->getPodcastEpisodeBundle( currentTrack.url(), &peb ) ) + { + PodcastChannelBundle pcb; + if( CollectionDB::instance()->getPodcastChannelBundle( peb.parent(), &pcb ) ) + { + if( pcb.imageURL().url() == url ) + { + m_dirtyCurrentTrackPage = true; + showCurrentTrack(); + } + } + } +} + +void ContextBrowser::ratingOrScoreOrLabelsChanged( const QString &path ) //SLOT +{ + const MetaBundle ¤tTrack = EngineController::instance()->bundle(); + + //Always refresh if using ratings, otherwise suggested songs and other songs by artist that + //have their ratings changed in the playlist won't be reflected until the context browser refreshes + //which can be confusing, and looks less polished/professional + //This can be changed if it slows things down too much... + if( m_browseLabels || ( currentTrack.isFile() && ( currentTrack.url().path() == path || AmarokConfig::useRatings() ) ) ) + m_dirtyCurrentTrackPage = true; // will be reloaded when viewed (much faster) + + if( currentPage() == m_contextTab ) + refreshCurrentTrackPage(); +} + +void ContextBrowser::tagsChanged( const MetaBundle &bundle ) //SLOT +{ + const MetaBundle ¤tTrack = EngineController::instance()->bundle(); + + if( !m_shownAlbums.contains( bundle.album() ) && m_artist != bundle.artist() ) + { + if( currentTrack.artist().isEmpty() && currentTrack.album().isEmpty() ) + return; + + if( bundle.artist() != currentTrack.artist() && bundle.album() != currentTrack.album() ) + return; + } + + refreshCurrentTrackPage(); +} + +void ContextBrowser::tagsChanged( const QString &oldArtist, const QString &oldAlbum ) //SLOT +{ + const MetaBundle ¤tTrack = EngineController::instance()->bundle(); + + if( !m_shownAlbums.contains( oldAlbum ) && m_artist != oldArtist ) + { + if( currentTrack.artist().isEmpty() && currentTrack.album().isEmpty() ) + return; + + if( oldArtist != currentTrack.artist() && oldAlbum != currentTrack.album() ) + return; + } + + refreshCurrentTrackPage(); +} + + +void ContextBrowser::refreshCurrentTrackPage() //SLOT +{ + if ( currentPage() == m_contextTab ) // this is for compilations or when artist is empty + { + m_dirtyCurrentTrackPage = true; + showCurrentTrack(); + } +} + + +bool +ContextBrowser::hasContextProtocol( const KURL &url ) +{ + QString protocol = url.protocol(); + return protocol == "album" + || protocol == "artist" + || protocol == "stream" + || protocol == "compilation" + || protocol == "albumdisc" + || protocol == "compilationdisc" + || protocol == "fetchcover"; +} + +KURL::List +ContextBrowser::expandURL( const KURL &url ) +{ + KURL::List urls; + QString protocol = url.protocol(); + + if( protocol == "artist" ) { + uint artist_id = CollectionDB::instance()->artistID( url.path(), false ); + if( artist_id ) + { + QStringList trackUrls = CollectionDB::instance()->artistTracks( QString::number( artist_id ) ); + foreach( trackUrls ) + urls += KURL::fromPathOrURL( *it ); + } + } + else if( protocol == "album" ) { + QString artist, album, track; // track unused here + Amarok::albumArtistTrackFromUrl( url.path(), artist, album, track ); + + QStringList trackUrls = CollectionDB::instance()->albumTracks( artist, album ); + foreach( trackUrls ) { + urls += KURL::fromPathOrURL( *it ); + } + } + else if( protocol == "albumdisc" ) { + QString artist, album, discnumber; // discnumber is returned in track number field + Amarok::albumArtistTrackFromUrl( url.path(), artist, album, discnumber ); + + QStringList trackUrls = CollectionDB::instance()->albumDiscTracks( artist, album, discnumber ); + foreach( trackUrls ) { + urls += KURL::fromPathOrURL( *it ); + } + } + else if( protocol == "compilation" ) { + QueryBuilder qb; + qb.addReturnValue( QueryBuilder::tabSong, QueryBuilder::valURL ); + qb.addMatch( QueryBuilder::tabSong, QueryBuilder::valAlbumID, url.path() ); + qb.sortBy( QueryBuilder::tabSong, QueryBuilder::valDiscNumber ); + qb.sortBy( QueryBuilder::tabSong, QueryBuilder::valTrack ); + qb.setOptions( QueryBuilder::optOnlyCompilations ); + QStringList values = qb.run(); + + for( QStringList::ConstIterator it = values.begin(), end = values.end(); it != end; ++it ) { + urls += KURL::fromPathOrURL( *it ); + } + } + else if( protocol == "compilationdisc") { + QString artist, album, discnumber; // artist is unused + Amarok::albumArtistTrackFromUrl( url.path(), artist, album, discnumber ); + + QueryBuilder qb; + qb.addReturnValue( QueryBuilder::tabSong, QueryBuilder::valURL ); + qb.addMatch( QueryBuilder::tabSong, QueryBuilder::valAlbumID, album ); + qb.addMatch( QueryBuilder::tabSong, QueryBuilder::valDiscNumber, discnumber ); + qb.sortBy( QueryBuilder::tabSong, QueryBuilder::valTrack ); + qb.setOptions( QueryBuilder::optOnlyCompilations ); + QStringList values = qb.run(); + + for( QStringList::ConstIterator it = values.begin(), end = values.end(); it != end; ++it ) { + urls += KURL::fromPathOrURL( *it ); + } + } + + else if( protocol == "fetchcover" ) { + QString artist, album, track; // track unused here + Amarok::albumArtistTrackFromUrl( url.path(), artist, album, track ); + + QString artistID = QString::number( CollectionDB::instance()->artistID( artist ) ); + QString albumID = QString::number( CollectionDB::instance()->albumID( album ) ); + + QStringList trackUrls = CollectionDB::instance()->albumTracks( artistID, albumID ); + foreach( trackUrls ) { + urls += KURL::fromPathOrURL( *it ); + } + } + + else if( protocol == "stream" ) { + urls += KURL::fromPathOrURL( url.url().replace( QRegExp( "^stream:" ), "http:" ) ); + } + + return urls; +} + + +#include "contextbrowser.moc" diff --git a/amarok/src/contextbrowser.h b/amarok/src/contextbrowser.h new file mode 100644 index 00000000..f99304a9 --- /dev/null +++ b/amarok/src/contextbrowser.h @@ -0,0 +1,222 @@ +// (c) 2004 Christian Muehlhaeuser +// (c) 2005 Reigo Reinmets +// (c) 2005 Mark Kretschmann +// (c) 2006 Peter C. Ndikuwera +// License: GNU General Public License V2 + + +#ifndef AMAROK_CONTEXTBROWSER_H +#define AMAROK_CONTEXTBROWSER_H + +#include "amarokdcophandler.h" +#include "clicklineedit.h" +#include "engineobserver.h" + +#include +#include +#include + +class ClickLineEdit; +class CollectionDB; +class Color; +class HTMLView; +class KPopupMenu; +class MetaBundle; +class QPalette; +class QVBox; +class QLineEdit; +class QComboBox; +class KDialogBase; +class KTabBar; +class KTextEdit; + +class CueFile; + +namespace Browser { class ToolBar; } +namespace KIO { class Job; class TransferJob; } + + +class ContextBrowser : public KTabWidget, public EngineObserver +{ + Q_OBJECT + + friend class CurrentTrackJob; + friend class Amarok::DcopContextBrowserHandler; + + static ContextBrowser *s_instance; + + public: + ContextBrowser( const char *name ); + ~ContextBrowser(); + + static ContextBrowser *instance() { return s_instance; } + + void setFont( const QFont& ); + void reloadStyleSheet(); + static KURL::List expandURL( const KURL &url ); // expand urls (album, compilation, ...) + static bool hasContextProtocol( const KURL &url ); // is url expandable by context browser? + + virtual bool eventFilter( QObject *o, QEvent *e ); //required by the labels dialog + + public slots: + void openURLRequest(const KURL &url ); + void collectionScanStarted(); + void collectionScanDone( bool changed ); + void renderView(); + void lyricsChanged( const QString& ); + void lyricsScriptChanged(); + void lyricsResult( QCString cXmlDoc, bool cached = false ); + + protected: + void engineNewMetaData( const MetaBundle&, bool ); + void engineStateChanged( Engine::State, Engine::State = Engine::Empty ); + void paletteChange( const QPalette& ); + + protected slots: + void wheelDelta( int delta ); + + private slots: + void tabChanged( QWidget *page ); + void slotContextMenu( const QString& urlString, const QPoint& point ); + void showContext( const KURL& url, bool fromHistory = false ); + void showCurrentTrack(); + void showLyrics( const QString& url = QString::null ); + void showWikipedia( const QString& url = QString::null, bool fromHistory = false, bool replaceHistory = false ); + void showWikipediaEntry( const QString& entry, bool replaceHistory = false ); + void reloadWikipedia(); + void showLabelsDialog(); + + void coverFetched( const QString &artist, const QString &album ); + void coverRemoved( const QString &artist, const QString &album ); + void similarArtistsFetched( const QString &artist ); + void imageFetched( const QString &remoteURL ); + void tagsChanged( const MetaBundle &bundle ); + void tagsChanged( const QString &oldArtist, const QString &oldAlbum ); + void ratingOrScoreOrLabelsChanged( const QString &path ); + void refreshCurrentTrackPage(); + + void contextHistoryBack(); + + void lyricsAdd(); + void lyricsEditToggle(); + void lyricsSearch(); + void lyricsRefresh(); + void lyricsExternalPage(); + + void lyricsSearchText( const QString &text ); + void lyricsSearchTextNext(); + void lyricsSearchTextHide(); + void lyricsSearchTextShow(); + void lyricsSearchTextToggle(); + + void wikiHistoryBack(); + void wikiHistoryForward(); + void wikiBackPopupActivated( int id ); + void wikiForwardPopupActivated( int id ); + void wikiArtistPage(); + void wikiAlbumPage(); + void wikiTitlePage(); + void wikiExternalPage(); + void wikiResult( KIO::Job* job ); + void wikiConfigApply(); + void wikiConfig(); + void wikiConfigChanged( int activeItem ); + + private: + enum { CONTEXT_BACK, CONTEXT_FORWARD, CONTEXT_CURRENT, CONTEXT_HOME, CONTEXT_SEARCH }; + enum { LYRICS_ADD, LYRICS_EDIT, LYRICS_SEARCH, LYRICS_REFRESH, LYRICS_BROWSER }; + enum { WIKI_BACK, WIKI_FORWARD, WIKI_ARTIST, WIKI_ALBUM, WIKI_TITLE, WIKI_BROWSER, WIKI_CONFIG }; + typedef enum {SHOW_ALBUM_NORMAL, SHOW_ALBUM_SCORE, SHOW_ALBUM_LEAST_PLAY} T_SHOW_ALBUM_TYPE; + static const uint WIKI_MAX_HISTORY = 20; + static const uint CONTEXT_MAX_HISTORY = 20; + + void showIntroduction(); + void saveHtmlData(); + void showScanning(); + + static QString getEncodedImage( const QString &imageUrl ); + + static QString wikiLocale(); + static void setWikiLocale( const QString &locale ); + static QString wikiURL( const QString &item ); + QString wikiArtistPostfix() const; + QString wikiAlbumPostfix() const; + QString wikiTrackPostfix() const; + + HTMLView *m_currentTrackPage; + HTMLView *m_lyricsPage; + HTMLView *m_wikiPage; + + QVBox *m_contextTab; + QVBox *m_lyricsTab; + QVBox *m_wikiTab; + // These controls are used to dictate whether the page should be rebuilt + // true -> need rebuild + bool m_dirtyCurrentTrackPage; + bool m_dirtyLyricsPage; + bool m_dirtyWikiPage; + + QStringList m_contextBackHistory; + KURL m_contextURL; + + QString m_styleSheet; + bool m_emptyDB; + QString m_lyricAddUrl; + QString m_lyricSearchUrl; + QString m_lyricCurrentUrl; + Browser::ToolBar* m_lyricsToolBar; + KTextEdit* m_lyricsTextEdit; + QString m_lyricsBeingEditedUrl; + QString m_lyricsBeingEditedArtist; + QString m_lyricsBeingEditedTitle; + ClickLineEdit* m_lyricsSearchText; + KToolBar* m_lyricsTextBar; + bool m_lyricsTextBarShowed; + + + QString m_wiki; + QString m_wikiLanguages; + static QString s_wikiLocale; + QString m_wikiBaseUrl; + QString m_wikiCurrentUrl; + QString m_wikiCurrentEntry; + QStringList m_wikiBackHistory; + QStringList m_wikiForwardHistory; + KPopupMenu* m_wikiBackPopup; + KPopupMenu* m_wikiForwardPopup; + KIO::TransferJob* m_wikiJob; + Browser::ToolBar* m_wikiToolBar; + QLineEdit* m_wikiLocaleEdit; + QComboBox* m_wikiLocaleCombo; + KDialogBase* m_wikiConfigDialog; + + QString m_HTMLSource; + QStringList m_metadataHistory; + KURL m_currentURL; + + bool m_relatedOpen; + bool m_suggestionsOpen; + bool m_favoritesOpen; + bool m_labelsOpen; + bool m_showRelated; + bool m_showSuggested; + bool m_showFaves; + bool m_showLabels; + + bool m_showFreshPodcasts; + bool m_showFavoriteAlbums; + bool m_showNewestAlbums; + + bool m_browseArtists; + QString m_artist; + QStringList m_shownAlbums; + + bool m_browseLabels; + QString m_label; + ClickLineEdit* m_addLabelEdit; + QListView* m_labelListView; + + CueFile *m_cuefile; +}; + +#endif /* AMAROK_CONTEXTBROWSER_H */ diff --git a/amarok/src/coverfetcher.cpp b/amarok/src/coverfetcher.cpp new file mode 100644 index 00000000..40e79e7c --- /dev/null +++ b/amarok/src/coverfetcher.cpp @@ -0,0 +1,678 @@ +// (C) 2004 Mark Kretschmann +// (C) 2004 Stefan Bogner +// (C) 2004 Max Howell +// See COPYING file for licensing information. + +#include "amarok.h" +#include "amarokconfig.h" +#include "collectiondb.h" +#include "config.h" //for AMAZON_SUPPORT +#include "covermanager.h" +#include "coverfetcher.h" +#include "debug.h" +#include "statusbar.h" + +#include +#include +#include +#include +#include + +#include +#include +#include //waiting cursor +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +void +Amarok::coverContextMenu( QWidget *parent, QPoint point, const QString &artist, const QString &album, bool showCoverManager ) +{ + KPopupMenu menu; + enum { SHOW, FETCH, CUSTOM, DELETE, MANAGER }; + + menu.insertTitle( i18n( "Cover Image" ) ); + + menu.insertItem( SmallIconSet( Amarok::icon( "zoom" ) ), i18n( "&Show Fullsize" ), SHOW ); + menu.insertItem( SmallIconSet( Amarok::icon( "download" ) ), i18n( "&Fetch From amazon.%1" ).arg( CoverManager::amazonTld() ), FETCH ); + menu.insertItem( SmallIconSet( Amarok::icon( "files" ) ), i18n( "Set &Custom Cover" ), CUSTOM ); + bool disable = !album.isEmpty(); // disable setting covers for unknown albums + menu.setItemEnabled( FETCH, disable ); + menu.setItemEnabled( CUSTOM, disable ); + menu.insertSeparator(); + + menu.insertItem( SmallIconSet( Amarok::icon( "remove" ) ), i18n( "&Unset Cover" ), DELETE ); + if ( showCoverManager ) { + menu.insertSeparator(); + menu.insertItem( SmallIconSet( Amarok::icon( "covermanager" ) ), i18n( "Cover &Manager" ), MANAGER ); + } + #ifndef AMAZON_SUPPORT + menu.setItemEnabled( FETCH, false ); + #endif + disable = !CollectionDB::instance()->albumImage( artist, album, 0 ).contains( "nocover" ); + menu.setItemEnabled( SHOW, disable ); + menu.setItemEnabled( DELETE, disable ); + + switch( menu.exec( point ) ) + { + case SHOW: + CoverManager::viewCover( artist, album, parent ); + break; + + case DELETE: + { + const int button = KMessageBox::warningContinueCancel( parent, + i18n( "Are you sure you want to remove this cover from the Collection?" ), + QString::null, + KStdGuiItem::del() ); + + if ( button == KMessageBox::Continue ) + CollectionDB::instance()->removeAlbumImage( artist, album ); + break; + } + + case FETCH: + #ifdef AMAZON_SUPPORT + CollectionDB::instance()->fetchCover( parent, artist, album, false ); + break; + #endif + + case CUSTOM: + { + QString artist_id; artist_id.setNum( CollectionDB::instance()->artistID( artist ) ); + QString album_id; album_id.setNum( CollectionDB::instance()->albumID( album ) ); + QStringList values = CollectionDB::instance()->albumTracks( artist_id, album_id ); + QString startPath = ":homedir"; + + if ( !values.isEmpty() ) { + KURL url; + url.setPath( values.first() ); + startPath = url.directory(); + } + + KURL file = KFileDialog::getImageOpenURL( startPath, parent, i18n("Select Cover Image File") ); + if ( !file.isEmpty() ) + CollectionDB::instance()->setAlbumImage( artist, album, file ); + break; + } + + case MANAGER: + CoverManager::showOnce( album ); + break; + } +} + + + +CoverLabel::CoverLabel ( QWidget * parent, const char * name, WFlags f ) + : QLabel( parent, name, f) +{} + + +void CoverLabel::mouseReleaseEvent(QMouseEvent *pEvent) { + if (pEvent->button() == LeftButton || pEvent->button() == RightButton) + { + Amarok::coverContextMenu( this, pEvent->globalPos(), m_artist, m_album, false ); + } +} + + +CoverFetcher::CoverFetcher( QWidget *parent, const QString &artist, QString album ) + : QObject( parent, "CoverFetcher" ) + , m_artist( artist ) + , m_album( album ) + , m_size( 2 ) + , m_success( true ) +{ + DEBUG_FUNC_INFO + + QStringList extensions; + extensions << i18n("disc") << i18n("disk") << i18n("remaster") << i18n("cd") << i18n("single") << i18n("soundtrack") << i18n("part") + << "disc" << "disk" << "remaster" << "cd" << "single" << "soundtrack" << "part" << "cds" /*cd single*/; + + //we do several queries, one raw ie, without the following modifications + //the others have the above strings removed with the following regex, as this can increase hit-rate + const QString template1 = " ?-? ?[(^{]* ?%1 ?\\d*[)^}\\]]* *$"; //eg album - [disk 1] -> album + foreach( extensions ) { + QRegExp regexp( template1.arg( *it ) ); + regexp.setCaseSensitive( false ); + album.remove( regexp ); + } + + //TODO try queries that remove anything in album after a " - " eg Les Mis. - Excerpts + + /** + * We search for artist - album, and just album, using the exact album text and the + * manipulated album text. + */ + + //search on our modified term, then the original + if ( !m_artist.isEmpty() ) + m_userQuery = m_artist + " - "; + m_userQuery += m_album; + + m_queries += m_artist + " - " + album; + m_queries += m_userQuery; + m_queries += album; + m_queries += m_album; + + //don't do the same searches twice in a row + if( m_album == album ) { + m_queries.pop_front(); + m_queries.pop_back(); + } + + /** + * Finally we do a search for just the artist, just in case as this often + * turns up a cover, and it might just be the right one! Also it would be + * the only valid search if m_album.isEmpty() + */ + m_queries += m_artist; + + QApplication::setOverrideCursor( KCursor::workingCursor() ); +} + +CoverFetcher::~CoverFetcher() +{ + DEBUG_FUNC_INFO + + QApplication::restoreOverrideCursor(); +} + +void +CoverFetcher::startFetch() +{ + DEBUG_FUNC_INFO + + // Static license Key. Thanks hydrogen ;-) + const QString LICENSE( "11ZKJS8X1ETSTJ6MT802" ); + + // reset all values + m_coverAmazonUrls.clear(); + m_coverAsins.clear(); + m_coverUrls.clear(); + m_coverNames.clear(); + m_xml = QString::null; + m_size = 2; + + if ( m_queries.isEmpty() ) { + debug() << "m_queries is empty" << endl; + finishWithError( i18n("No cover found") ); + return; + } + QString query = m_queries.front(); + m_queries.pop_front(); + + // '&' breaks searching + query.remove('&'); + + QString locale = AmarokConfig::amazonLocale(); + QString tld; + + if( locale == "us" ) + tld = "com"; + else if( locale =="uk" ) + tld = "co.uk"; + else + tld = locale; + + int mibenum = 106; // utf-8 + + QString url; + url = "http://ecs.amazonaws." + tld + + "/onca/xml?Service=AWSECommerceService&Version=2007-10-29&Operation=ItemSearch&AssociateTag=webservices-20&AWSAccessKeyId=" + LICENSE + + "&Keywords=" + KURL::encode_string_no_slash( query, mibenum ) + + "&SearchIndex=Music&ResponseGroup=Small,Images"; + debug() << url << endl; + + KIO::TransferJob* job = KIO::storedGet( url, false, false ); + connect( job, SIGNAL(result( KIO::Job* )), SLOT(finishedXmlFetch( KIO::Job* )) ); + + Amarok::StatusBar::instance()->newProgressOperation( job ); +} + + +////////////////////////////////////////////////////////////////////////////////////////// +// PRIVATE SLOTS +////////////////////////////////////////////////////////////////////////////////////////// + +void +CoverFetcher::finishedXmlFetch( KIO::Job *job ) //SLOT +{ + DEBUG_BLOCK + + // NOTE: job can become 0 when this method is called from attemptAnotherFetch() + + if( job && job->error() ) { + finishWithError( i18n("There was an error communicating with Amazon."), job ); + return; + } + if ( job ) { + KIO::StoredTransferJob* const storedJob = static_cast( job ); + m_xml = QString::fromUtf8( storedJob->data().data(), storedJob->data().size() ); + } + + QDomDocument doc; + if( !doc.setContent( m_xml ) ) { + m_errors += i18n("The XML obtained from Amazon is invalid."); + startFetch(); + return; + } + + m_coverAsins.clear(); + m_coverAmazonUrls.clear(); + m_coverUrls.clear(); + m_coverNames.clear(); + + // the url for the Amazon product info page + const QDomNodeList list = doc.documentElement().namedItem( "Items" ).childNodes(); + + for(int i = 0; i < list.count(); i++ ) + { + QDomNode n = list.item( i ); + if( n.isElement() && n.nodeName() == "IsValid" ) + { + if( n.toElement().text() == "False" ) + { + warning() << "The XML Is Invalid!"; + return; + } + } + else if( list.item( i ).nodeName() == "Item" ) + { + const QDomNode node = list.item( i ); + parseItemNode( node ); + } + } + attemptAnotherFetch(); +} + +void CoverFetcher::parseItemNode( const QDomNode &node ) +{ + QDomNode it = node.firstChild(); + + QString size; + switch( m_size ) + { + case 0: size = "Small"; break; + case 1: size = "Medium"; break; + default: size = "Large"; break; + } + size += "Image"; + + while ( !it.isNull() ) { + if ( it.isElement() ) { + QDomElement e = it.toElement(); + if(e.tagName()=="ASIN") + { + m_asin = e.text(); + m_coverAsins += m_asin; + } + else if(e.tagName() == "DetailPageURL" ) + { + m_amazonURL = e.text(); + m_coverAmazonUrls += m_amazonURL; + } + else if( e.tagName() == size ) + { + QDomNode subIt = e.firstChild(); + while( !subIt.isNull() ) + { + if( subIt.isElement() ) + { + QDomElement subE = subIt.toElement(); + if( subE.tagName() == "URL" ) + { + const QString coverUrl = subE.text(); + m_coverUrls += coverUrl; + break; + } + } + subIt = subIt.nextSibling(); + } + } + else if( e.tagName() == "ItemAttributes" ) + { + QDomNodeList nodes = e.childNodes(); + QDomNode iter; + QString artist; + QString album; + for( int i = 0; i < nodes.count(); i++ ) + { + iter = nodes.item( i ); + + if( iter.isElement() ) + { + if( iter.nodeName() == "Artist" ) + { + artist = iter.toElement().text(); + } + else if( iter.nodeName() == "Title" ) + { + album = iter.toElement().text(); + } + } + } + m_coverNames += QString( artist + " - " + album ); + } + } + it = it.nextSibling(); + } +} + +void +CoverFetcher::finishedImageFetch( KIO::Job *job ) //SLOT +{ + if( job->error() ) { + debug() << "finishedImageFetch(): KIO::error(): " << job->error() << endl; + + m_errors += i18n("The cover could not be retrieved."); + + attemptAnotherFetch(); + return; + } + + m_image.loadFromData( static_cast( job )->data() ); + + if( m_image.width() <= 1 ) { + //Amazon seems to offer images of size 1x1 sometimes + //Amazon has nothing to offer us for the requested image size + m_errors += i18n("The cover-data produced an invalid image."); + attemptAnotherFetch(); + } + + else if( m_userCanEditQuery ) + //yay! image found :) + //lets see if the user wants it + showCover(); + + else + //image loaded successfully yay! + finish(); +} + + +void +CoverFetcher::attemptAnotherFetch() +{ + DEBUG_BLOCK + + if( !m_coverUrls.isEmpty() ) { + // Amazon suggested some more cover URLs to try before we + // try a different query + + KIO::TransferJob* job = KIO::storedGet( KURL(m_coverUrls.front()), false, false ); + connect( job, SIGNAL(result( KIO::Job* )), SLOT(finishedImageFetch( KIO::Job* )) ); + + Amarok::StatusBar::instance()->newProgressOperation( job ); + + m_coverUrls.pop_front(); + + m_currentCoverName = m_coverNames.front(); + m_coverNames.pop_front(); + + m_amazonURL = m_coverAmazonUrls.front(); + m_coverAmazonUrls.pop_front(); + + m_asin = m_coverAsins.front(); + m_coverAsins.pop_front(); + } + + else if( !m_xml.isEmpty() && m_size > 0 ) { + // we need to try smaller sizes, this often is + // fruitless, but does work out sometimes. + m_size--; + + finishedXmlFetch( 0 ); + } + + else if( !m_queries.isEmpty() ) { + // we have some queries left in the pot + startFetch(); + } + + else if( m_userCanEditQuery ) { + // we have exhausted all the predetermined queries + // so lets let the user give it a try + getUserQuery( i18n("You have seen all the covers Amazon returned using the query below. Perhaps you can refine it:") ); + m_coverAmazonUrls.clear(); + m_coverAsins.clear(); + m_coverUrls.clear(); + m_coverNames.clear(); + } + else + finishWithError( i18n("No cover found") ); +} + + +// Moved outside the only function that uses it because +// gcc 2.95 doesn't like class declarations there. + class EditSearchDialog : public KDialog + { + public: + EditSearchDialog( QWidget* parent, const QString &text, const QString &keyword, CoverFetcher *fetcher ) + : KDialog( parent ) + { + setCaption( i18n( "Amazon Query Editor" ) ); + + // amazon combo box + KComboBox* amazonLocale = new KComboBox( this ); + amazonLocale->insertItem( i18n("International"), CoverFetcher::International ); + amazonLocale->insertItem( i18n("Canada"), CoverFetcher::Canada ); + amazonLocale->insertItem( i18n("France"), CoverFetcher::France ); + amazonLocale->insertItem( i18n("Germany"), CoverFetcher::Germany ); + amazonLocale->insertItem( i18n("Japan"), CoverFetcher::Japan); + amazonLocale->insertItem( i18n("United Kingdom"), CoverFetcher::UK ); + if( CoverManager::instance() ) + connect( amazonLocale, SIGNAL( activated(int) ), + CoverManager::instance(), SLOT( changeLocale(int) ) ); + else + connect( amazonLocale, SIGNAL( activated(int) ), + fetcher, SLOT( changeLocale(int) ) ); + QHBoxLayout *hbox1 = new QHBoxLayout( 8 ); + hbox1->addWidget( new QLabel( i18n( "Amazon Locale: " ), this ) ); + hbox1->addWidget( amazonLocale ); + + int currentLocale = CoverFetcher::localeStringToID( AmarokConfig::amazonLocale() ); + amazonLocale->setCurrentItem( currentLocale ); + + KPushButton* cancelButton = new KPushButton( KStdGuiItem::cancel(), this ); + KPushButton* searchButton = new KPushButton( i18n("&Search"), this ); + + QHBoxLayout *hbox2 = new QHBoxLayout( 8 ); + hbox2->addItem( new QSpacerItem( 160, 8, QSizePolicy::Expanding, QSizePolicy::Minimum ) ); + hbox2->addWidget( searchButton ); + hbox2->addWidget( cancelButton ); + + QVBoxLayout *vbox = new QVBoxLayout( this, 8, 8 ); + vbox->addLayout( hbox1 ); + vbox->addWidget( new QLabel( "" + text, this ) ); + vbox->addWidget( new KLineEdit( keyword, this, "Query" ) ); + vbox->addLayout( hbox2 ); + + searchButton->setDefault( true ); + + adjustSize(); + setFixedHeight( height() ); + + connect( searchButton, SIGNAL(clicked()), SLOT(accept()) ); + connect( cancelButton, SIGNAL(clicked()), SLOT(reject()) ); + } + + QString query() { return static_cast(child( "Query" ))->text(); } + }; + +QString +CoverFetcher::localeIDToString( int id )//static +{ + switch ( id ) + { + case International: + return "us"; + case Canada: + return "ca"; + case France: + return "fr"; + case Germany: + return "de"; + case Japan: + return "jp"; + case UK: + return "uk"; + } + + return "us"; +} + +int +CoverFetcher::localeStringToID( const QString &s ) +{ + int id = International; + if( s == "fr" ) id = France; + else if( s == "de" ) id = Germany; + else if( s == "jp" ) id = Japan; + else if( s == "uk" ) id = UK; + else if( s == "ca" ) id = Canada; + + return id; +} + +void +CoverFetcher::changeLocale( int id )//SLOT +{ + QString locale = localeIDToString( id ); + AmarokConfig::setAmazonLocale( locale ); +} + + +void +CoverFetcher::getUserQuery( QString explanation ) +{ + if( explanation.isEmpty() ) + explanation = i18n("Ask Amazon for covers using this query:"); + + EditSearchDialog dialog( + static_cast( parent() ), + explanation, + m_userQuery, + this ); + + switch( dialog.exec() ) + { + case QDialog::Accepted: + m_userQuery = dialog.query(); + m_queries = m_userQuery; + startFetch(); + break; + default: + finishWithError( i18n( "Aborted." ) ); + break; + } +} + + class CoverFoundDialog : public KDialog + { + public: + CoverFoundDialog( QWidget *parent, const QImage &cover, const QString &productname ) + : KDialog( parent ) + { + // Gives the window a small title bar, and skips a taskbar entry + KWin::setType( winId(), NET::Utility ); + KWin::setState( winId(), NET::SkipTaskbar ); + + (new QVBoxLayout( this ))->setAutoAdd( true ); + + QLabel *labelPix = new QLabel( this ); + QLabel *labelName = new QLabel( this ); + QHBox *buttons = new QHBox( this ); + KPushButton *save = new KPushButton( KStdGuiItem::save(), buttons ); + KPushButton *newsearch = new KPushButton( i18n( "Ne&w Search..." ), buttons, "NewSearch" ); + KPushButton *nextcover = new KPushButton( i18n( "&Next Cover" ), buttons, "NextCover" ); + KPushButton *cancel = new KPushButton( KStdGuiItem::cancel(), buttons ); + + labelPix ->setAlignment( Qt::AlignHCenter ); + labelName->setAlignment( Qt::AlignHCenter ); + labelPix ->setPixmap( cover ); + labelName->setText( productname ); + + save->setDefault( true ); + this->setFixedSize( sizeHint() ); + this->setCaption( i18n("Cover Found") ); + + connect( save, SIGNAL(clicked()), SLOT(accept()) ); + connect( newsearch, SIGNAL(clicked()), SLOT(accept()) ); + connect( nextcover, SIGNAL(clicked()), SLOT(accept()) ); + connect( cancel, SIGNAL(clicked()), SLOT(reject()) ); + } + + virtual void accept() + { + if( qstrcmp( sender()->name(), "NewSearch" ) == 0 ) + done( 1000 ); + else if( qstrcmp( sender()->name(), "NextCover" ) == 0 ) + done( 1001 ); + else + KDialog::accept(); + } + }; + + +void +CoverFetcher::showCover() +{ + CoverFoundDialog dialog( static_cast( parent() ), m_image, m_currentCoverName ); + + switch( dialog.exec() ) + { + case KDialog::Accepted: + finish(); + break; + case 1000: //showQueryEditor() + getUserQuery(); + m_coverAmazonUrls.clear(); + m_coverAsins.clear(); + m_coverUrls.clear(); + m_coverNames.clear(); + break; + case 1001: //nextCover() + attemptAnotherFetch(); + break; + default: + finishWithError( i18n( "Aborted." ) ); + break; + } +} + + +void +CoverFetcher::finish() +{ + emit result( this ); + + deleteLater(); +} + +void +CoverFetcher::finishWithError( const QString &message, KIO::Job *job ) +{ + if( job ) + warning() << message << " KIO::error(): " << job->errorText() << endl; + + m_errors += message; + m_success = false; + + emit result( this ); + + deleteLater(); +} + +#include "coverfetcher.moc" diff --git a/amarok/src/coverfetcher.h b/amarok/src/coverfetcher.h new file mode 100644 index 00000000..a24761d3 --- /dev/null +++ b/amarok/src/coverfetcher.h @@ -0,0 +1,121 @@ +// (c) 2004 Mark Kretschmann +// (c) 2004 Stefan Bogner +// See COPYING file for licensing information. + +#ifndef AMAROK_COVERFETCHER_H +#define AMAROK_COVERFETCHER_H + +#include //baseclass +#include //stack allocated +#include //baseclass +#include //stack allocated +#include //stack allocated + +namespace Amarok { + void coverContextMenu( QWidget *parent, QPoint point, const QString &artist, const QString &album, bool showCoverManager = true ); +} + + +class CoverLabel : public QLabel { + public: + CoverLabel ( QWidget * parent, const char * name = 0, WFlags f = 0 ); + + void setInformation( const QString artist, const QString album ) { + m_artist = artist; + m_album = album; + } + + protected: + virtual void mouseReleaseEvent(QMouseEvent *pEvent); + + private: + QString m_artist; + QString m_album; +}; + + + +namespace KIO { class Job; } + +class CoverFetcher : public QObject +{ + friend class EditSearchDialog; + Q_OBJECT + + static const uint MAX_COVERS_CHOICE = 10; + +public: + CoverFetcher( QWidget *parent, const QString &artist, QString album ); + ~CoverFetcher(); + + /// allow the user to edit the query? + void setUserCanEditQuery( bool b ) { m_userCanEditQuery = b; } + + /// starts the fetch + void startFetch(); + + QString artist() const { return m_artist; } + QString album() const { return m_album; } + QString amazonURL() const { return m_amazonURL; } + QString asin() const { return m_asin; } + QImage image() const { return m_image; } + + bool wasError() const { return !m_success; } + QStringList errors() const { return m_errors; } + + enum Locale { International=0, France, Germany, Japan, UK, Canada }; + static QString localeIDToString( int id ); + static int localeStringToID( const QString &locale ); + +signals: + /// The CollectionDB can get the cover information using the pointer + void result( CoverFetcher* ); + +private slots: + void finishedXmlFetch( KIO::Job* job ); + void finishedImageFetch( KIO::Job* job ); + void changeLocale( int id ); + +private: + const QString m_artist; + const QString m_album; + + bool m_userCanEditQuery; + QString m_userQuery; /// the query from the query edit dialog + QString m_xml; + QImage m_image; + QString m_amazonURL; + QString m_asin; + int m_size; + + QStringList m_queries; + QStringList m_coverAsins; + QStringList m_coverAmazonUrls; + QStringList m_coverUrls; + QStringList m_coverNames; + QString m_currentCoverName; + QStringList m_errors; + + bool m_success; + +private: + /// The fetch was successful! + void finish(); + + /// Parse one QDomNode and append results. + void parseItemNode( const QDomNode &node ); + + /// The fetch failed, finish up and log an error message + void finishWithError( const QString &message, KIO::Job *job = 0 ); + + /// Prompt the user for a query + void getUserQuery( QString explanation = QString::null ); + + /// Will try all available queries, and then prompt the user, if allowed + void attemptAnotherFetch(); + + /// Show the cover that has been found + void showCover(); +}; + +#endif /* AMAROK_COVERFETCHER_H */ diff --git a/amarok/src/covermanager.cpp b/amarok/src/covermanager.cpp new file mode 100644 index 00000000..d4996beb --- /dev/null +++ b/amarok/src/covermanager.cpp @@ -0,0 +1,1067 @@ +// (c) Pierpaolo Di Panfilo 2004 +// (c) 2005 Isaiah Damron +// See COPYING file for licensing information + +#include "amarok.h" +#include "amarokconfig.h" +#include "browserToolBar.h" +#include "clicklineedit.h" +#include "debug.h" +#include "collectionbrowser.h" //manipulateThe() +#include "collectiondb.h" +#include "config.h" +#include "coverfetcher.h" +#include "covermanager.h" +#include "pixmapviewer.h" +#include "playlist.h" + +#include //ctor: desktop size +#include +#include //paintItem() +#include +#include +#include +#include //used to delete all cover fetchers +#include //paintItem() +#include //paintItem() +#include +#include +#include +#include +#include +#include +#include //search filter timer +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include //showCoverMenu() +#include +#include +#include //showCoverMenu() +#include +#include +#include //status label +#include +#include //paintItem +#include +#include //clear filter button +#include +#include +#include + +static QString artistToSelectInInitFunction; +CoverManager *CoverManager::s_instance = 0; + +class ArtistItem : public KListViewItem +{ + public: + ArtistItem(QListView *view, QListViewItem *item, const QString &text) + : KListViewItem(view, item, text) {} + protected: + int compare( QListViewItem* i, int col, bool ascending ) const + { + Q_UNUSED(col); + Q_UNUSED(ascending); + + QString a = text(0); + QString b = i->text(0); + + if ( a.startsWith( "the ", false ) ) + CollectionView::manipulateThe( a, true ); + if ( b.startsWith( "the ", false ) ) + CollectionView::manipulateThe( b, true ); + + return QString::localeAwareCompare( a.lower(), b.lower() ); + } +}; + +CoverManager::CoverManager() + : QSplitter( 0, "TheCoverManager" ) + , m_timer( new QTimer( this ) ) //search filter timer + , m_fetchCounter( 0 ) + , m_fetchingCovers( 0 ) + , m_coversFetched( 0 ) + , m_coverErrors( 0 ) +{ + DEBUG_BLOCK + + s_instance = this; + + // Sets caption and icon correctly (needed e.g. for GNOME) + kapp->setTopWidget( this ); + setCaption( kapp->makeStdCaption( i18n("Cover Manager") ) ); + setWFlags( WDestructiveClose ); + setMargin( 4 ); + + //artist listview + m_artistView = new KListView( this ); + m_artistView->addColumn(i18n( "Albums By" )); + m_artistView->setFullWidth( true ); + m_artistView->setSorting( 0 ); + m_artistView->setMinimumWidth( 180 ); + ArtistItem *item = 0; + + //load artists from the collection db + const QStringList artists = CollectionDB::instance()->artistList( false, false ); + foreach( artists ) + { + QString artist = *it; + item = new ArtistItem( m_artistView, item, artist ); + item->setPixmap( 0, SmallIcon( Amarok::icon( "artist" ) ) ); + } + m_artistView->sort(); + + m_artistView->setSorting( -1 ); + ArtistItem *last = static_cast(m_artistView->lastItem()); + item = new ArtistItem( m_artistView, 0, i18n( "All Albums" ) ); + item->setPixmap( 0, SmallIcon( Amarok::icon( "album" ) ) ); + + QueryBuilder qb; + qb.addReturnValue( QueryBuilder::tabAlbum, QueryBuilder::valName ); + qb.groupBy( QueryBuilder::tabAlbum, QueryBuilder::valName ); + qb.setOptions( QueryBuilder::optOnlyCompilations ); + qb.setLimit( 0, 1 ); + if ( qb.run().count() ) { + item = new ArtistItem( m_artistView, last, i18n( "Various Artists" ) ); + item->setPixmap( 0, SmallIcon("personal") ); + } + + QVBox *vbox = new QVBox( this ); + QHBox *hbox = new QHBox( vbox ); + + vbox->setSpacing( 4 ); + hbox->setSpacing( 4 ); + + { // + QHBox *searchBox = new QHBox( hbox ); + KToolBar* searchToolBar = new Browser::ToolBar( searchBox ); + KToolBarButton *button = new KToolBarButton( "locationbar_erase", 0, searchToolBar ); + m_searchEdit = new ClickLineEdit( i18n( "Enter search terms here" ), searchToolBar ); + m_searchEdit->setFrame( QFrame::Sunken ); + + searchToolBar->setStretchableWidget( m_searchEdit ); + connect( button, SIGNAL(clicked()), m_searchEdit, SLOT(clear()) ); + + QToolTip::add( button, i18n( "Clear search field" ) ); + QToolTip::add( m_searchEdit, i18n( "Enter space-separated terms to search in the albums" ) ); + + hbox->setStretchFactor( searchBox, 1 ); + } // + + // view menu + m_viewMenu = new KPopupMenu( this ); + m_viewMenu->insertItem( i18n("All Albums"), AllAlbums ); + m_viewMenu->insertItem( i18n("Albums With Cover"), AlbumsWithCover ); + m_viewMenu->insertItem( i18n("Albums Without Cover"), AlbumsWithoutCover ); + m_viewMenu->setItemChecked( AllAlbums, true ); + connect( m_viewMenu, SIGNAL( activated(int) ), SLOT( changeView(int) ) ); + + #ifdef AMAZON_SUPPORT + // amazon locale menu + m_amazonLocaleMenu = new KPopupMenu( this ); + m_amazonLocaleMenu->insertItem( i18n("International"), CoverFetcher::International ); + m_amazonLocaleMenu->insertItem( i18n("Canada"), CoverFetcher::Canada ); + m_amazonLocaleMenu->insertItem( i18n("France"), CoverFetcher::France ); + m_amazonLocaleMenu->insertItem( i18n("Germany"), CoverFetcher::Germany ); + m_amazonLocaleMenu->insertItem( i18n("Japan"), CoverFetcher::Japan); + m_amazonLocaleMenu->insertItem( i18n("United Kingdom"), CoverFetcher::UK ); + connect( m_amazonLocaleMenu, SIGNAL( activated(int) ), SLOT( changeLocale(int) ) ); + #endif + + KToolBar* toolBar = new KToolBar( hbox ); + toolBar->setIconText( KToolBar::IconTextRight ); + toolBar->setFrameShape( QFrame::NoFrame ); + toolBar->insertButton( "view_choose", 1, m_viewMenu, true, i18n( "View" ) ); + #ifdef AMAZON_SUPPORT + toolBar->insertButton( "babelfish", 2, m_amazonLocaleMenu, true, i18n( "Amazon Locale" ) ); + + QString locale = AmarokConfig::amazonLocale(); + m_currentLocale = CoverFetcher::localeStringToID( locale ); + m_amazonLocaleMenu->setItemChecked( m_currentLocale, true ); + + //fetch missing covers button + m_fetchButton = new KPushButton( KGuiItem( i18n("Fetch Missing Covers"), Amarok::icon( "download" ) ), hbox ); + connect( m_fetchButton, SIGNAL(clicked()), SLOT(fetchMissingCovers()) ); + #endif + + //cover view + m_coverView = new CoverView( vbox ); + + //status bar + KStatusBar *m_statusBar = new KStatusBar( vbox ); + m_statusBar->addWidget( m_statusLabel = new KSqueezedTextLabel( m_statusBar ), 4 ); + m_statusLabel->setIndent( 3 ); + m_statusBar->addWidget( m_progressBox = new QHBox( m_statusBar ), 1, true ); + KPushButton *stopButton = new KPushButton( KGuiItem(i18n("Abort"), "stop"), m_progressBox ); + connect( stopButton, SIGNAL(clicked()), SLOT(stopFetching()) ); + m_progress = new KProgress( m_progressBox ); + m_progress->setCenterIndicator( true ); + + const int h = m_statusLabel->height() + 3; + m_statusLabel->setFixedHeight( h ); + m_progressBox->setFixedHeight( h ); + m_progressBox->hide(); + + + // signals and slots connections + connect( m_artistView, SIGNAL(selectionChanged( QListViewItem* ) ), + SLOT(slotArtistSelected( QListViewItem* )) ); + connect( m_coverView, SIGNAL(contextMenuRequested( QIconViewItem*, const QPoint& )), + SLOT(showCoverMenu( QIconViewItem*, const QPoint& )) ); + connect( m_coverView, SIGNAL(executed( QIconViewItem* )), + SLOT(coverItemExecuted( QIconViewItem* )) ); + connect( m_timer, SIGNAL(timeout()), + SLOT(slotSetFilter()) ); + connect( m_searchEdit, SIGNAL(textChanged( const QString& )), + SLOT(slotSetFilterTimeout()) ); + + #ifdef AMAZON_SUPPORT + connect( CollectionDB::instance(), SIGNAL(coverFetched( const QString&, const QString& )), + SLOT(coverFetched( const QString&, const QString& )) ); + connect( CollectionDB::instance(), SIGNAL(coverRemoved( const QString&, const QString& )), + SLOT(coverRemoved( const QString&, const QString& )) ); + connect( CollectionDB::instance(), SIGNAL(coverFetcherError( const QString& )), + SLOT(coverFetcherError()) ); + #endif + + m_currentView = AllAlbums; + + QSize size = QApplication::desktop()->screenGeometry( this ).size() / 1.5; + resize( Amarok::config( "Cover Manager" )->readSizeEntry( "Window Size", &size ) ); + + show(); + + QTimer::singleShot( 0, this, SLOT(init()) ); +} + + +CoverManager::~CoverManager() +{ + DEBUG_BLOCK + + Amarok::config( "Cover Manager" )->writeEntry( "Window Size", size() ); + + s_instance = 0; +} + + +void CoverManager::init() +{ + DEBUG_BLOCK + + QListViewItem *item = 0; + + if ( !artistToSelectInInitFunction.isEmpty() ) + for ( item = m_artistView->firstChild(); item; item = item->nextSibling() ) + if ( item->text( 0 ) == artistToSelectInInitFunction ) + break; + + if ( item == 0 ) + item = m_artistView->firstChild(); + + m_artistView->setSelected( item, true ); +} + + +CoverViewDialog::CoverViewDialog( const QString& artist, const QString& album, QWidget *parent ) + : QDialog( parent, 0, false, WDestructiveClose | WType_TopLevel | WNoAutoErase ) + , m_pixmap( CollectionDB::instance()->albumImage( artist, album, false, 0 ) ) +{ + KWin::setType( winId(), NET::Utility ); + kapp->setTopWidget( this ); + setCaption( kapp->makeStdCaption( i18n("%1 - %2").arg( artist, album ) ) ); + + m_layout = new QHBoxLayout( this ); + m_layout->setAutoAdd( true ); + m_pixmapViewer = new PixmapViewer( this, m_pixmap ); + + setFixedSize( m_pixmapViewer->maximalSize() ); +} + + +void CoverManager::viewCover( const QString& artist, const QString& album, QWidget *parent ) //static +{ + //QDialog means "escape" works as expected + QDialog *dialog = new CoverViewDialog( artist, album, parent ); + dialog->show(); +} + + +QString CoverManager::amazonTld() //static +{ + if (AmarokConfig::amazonLocale() == "us") + return "com"; + else if (AmarokConfig::amazonLocale()== "jp") + return "co.jp"; + else if (AmarokConfig::amazonLocale() == "uk") + return "co.uk"; + else if (AmarokConfig::amazonLocale() == "ca") + return "ca"; + else + return AmarokConfig::amazonLocale(); +} + + +void CoverManager::fetchMissingCovers() //SLOT +{ + #ifdef AMAZON_SUPPORT + + DEBUG_BLOCK + + for ( QIconViewItem *item = m_coverView->firstItem(); item; item = item->nextItem() ) { + CoverViewItem *coverItem = static_cast( item ); + if( !coverItem->hasCover() ) { + m_fetchCovers += coverItem->artist() + " @@@ " + coverItem->album(); + m_fetchingCovers++; + } + } + + if( !m_fetchCounter ) //loop isn't started yet + fetchCoversLoop(); + + updateStatusBar(); + m_fetchButton->setEnabled( false ); + + #endif +} + + +void CoverManager::fetchCoversLoop() //SLOT +{ + #ifdef AMAZON_SUPPORT + + if( m_fetchCounter < m_fetchCovers.count() ) { + //get artist and album from keyword + const QStringList values = QStringList::split( " @@@ ", m_fetchCovers[m_fetchCounter], true ); + + if( values.count() > 1 ) + CollectionDB::instance()->fetchCover( this, values[0], values[1], m_fetchCovers.count() != 1); //edit mode when fetching 1 cover + + m_fetchCounter++; + + // Wait 1 second, since amazon caps the number of accesses per client + QTimer::singleShot( 1000, this, SLOT( fetchCoversLoop() ) ); + } + else { + m_fetchCovers.clear(); + m_fetchCounter = 0; + } + + #endif +} + + +void CoverManager::showOnce( const QString &artist ) +{ + if ( !s_instance ) { + artistToSelectInInitFunction = artist; + new CoverManager(); //shows itself + } + else { + s_instance->setActiveWindow(); + s_instance->raise(); + } +} + +void CoverManager::slotArtistSelected( QListViewItem *item ) //SLOT +{ + if( item->depth() ) //album item + return; + + QString artist = item->text(0); + + if( artist.endsWith( ", The" ) ) + CollectionView::instance()->manipulateThe( artist, false ); + + m_coverView->clear(); + m_coverItems.clear(); + + // reset current view mode state to "AllAlbum" which is the default on artist change in left panel + m_currentView = AllAlbums; + m_viewMenu->setItemChecked( AllAlbums, true ); + m_viewMenu->setItemChecked( AlbumsWithCover, false ); + m_viewMenu->setItemChecked( AlbumsWithoutCover, false ); + + QProgressDialog progress( this, 0, true ); + progress.setLabelText( i18n("Loading Thumbnails...") ); + progress.QDialog::setCaption( i18n("...") ); + + //NOTE we MUST show the dialog, otherwise the closeEvents get processed + // in the processEvents() calls below, GRUMBLE! Qt sux0rs + progress.show(); + progress.repaint( false ); //ensures the dialog isn't blank + + //this is an extra processEvent call for the sake of init() and aesthetics + //it isn't necessary + kapp->processEvents(); + + //this can be a bit slow + QApplication::setOverrideCursor( KCursor::waitCursor() ); + QueryBuilder qb; + QStringList albums; + + qb.addReturnValue( QueryBuilder::tabArtist, QueryBuilder::valName ); + qb.addReturnValue( QueryBuilder::tabAlbum, QueryBuilder::valName ); + + qb.excludeMatch( QueryBuilder::tabAlbum, i18n( "Unknown" ) ); + qb.sortBy( QueryBuilder::tabAlbum, QueryBuilder::valName ); + qb.setOptions( QueryBuilder::optRemoveDuplicates ); + qb.setOptions( QueryBuilder::optNoCompilations ); + + if ( item != m_artistView->firstChild() ) + qb.addMatches( QueryBuilder::tabArtist, artist ); + + albums = qb.run(); + + //also retrieve compilations when we're showing all items (first treenode) or + //"Various Artists" (last treenode) + if ( item == m_artistView->firstChild() || item == m_artistView->lastChild() ) + { + QStringList cl; + + qb.clear(); + qb.addReturnValue( QueryBuilder::tabAlbum, QueryBuilder::valName ); + + qb.excludeMatch( QueryBuilder::tabAlbum, i18n( "Unknown" ) ); + qb.sortBy( QueryBuilder::tabAlbum, QueryBuilder::valName ); + qb.setOptions( QueryBuilder::optRemoveDuplicates ); + qb.setOptions( QueryBuilder::optOnlyCompilations ); + cl = qb.run(); + + for ( uint i = 0; i < cl.count(); i++ ) { + albums.append( i18n( "Various Artists" ) ); + albums.append( cl[ i ] ); + } + } + + QApplication::restoreOverrideCursor(); + + progress.setTotalSteps( (albums.count()/2) + (albums.count()/10) ); + + //insert the covers first because the list view is soooo paint-happy + //doing it in the second loop looks really bad, unfortunately + //this is the slowest step in the bit that we can't process events + uint x = 0; + foreach( albums ) + { + const QString artist = *it; + const QString album = *(++it); + m_coverItems.append( new CoverViewItem( m_coverView, m_coverView->lastItem(), artist, album ) ); + + if ( ++x % 50 == 0 ) { + progress.setProgress( x / 5 ); // we do it less often due to bug in Qt, ask Max + kapp->processEvents(); // QProgressDialog also calls this, but not always due to Qt bug! + + //only worth testing for after processEvents() is called + if( progress.wasCancelled() ) + break; + } + } + + //now, load the thumbnails + for( QIconViewItem *item = m_coverView->firstItem(); item; item = item->nextItem() ) { + progress.setProgress( progress.progress() + 1 ); + kapp->processEvents(); + + if( progress.wasCancelled() ) + break; + + static_cast(item)->loadCover(); + } + + updateStatusBar(); +} + +void CoverManager::showCoverMenu( QIconViewItem *item, const QPoint &p ) //SLOT +{ + #define item static_cast(item) + if( !item ) return; + + enum { SHOW, FETCH, CUSTOM, DELETE, APPEND }; + + KPopupMenu menu; + + menu.insertTitle( i18n( "Cover Image" ) ); + + QPtrList selected = selectedItems(); + if( selected.count() > 1 ) { + menu.insertItem( SmallIconSet( Amarok::icon( "download" ) ), i18n( "&Fetch Selected Covers" ), FETCH ); + menu.insertItem( SmallIconSet( Amarok::icon( "files" ) ), i18n( "Set &Custom Cover for Selected Albums" ), CUSTOM ); + menu.insertItem( SmallIconSet( Amarok::icon( "remove" ) ), i18n( "&Unset Selected Covers" ), DELETE ); + menu.insertItem( SmallIconSet( Amarok::icon( "add_playlist" ) ), i18n( "&Append to Playlist" ), APPEND ); + } + else { + menu.insertItem( SmallIconSet( Amarok::icon( "zoom" ) ), i18n( "&Show Fullsize" ), SHOW ); + menu.insertItem( SmallIconSet( Amarok::icon( "download" ) ), i18n( "&Fetch From amazon.%1" ).arg( CoverManager::amazonTld() ), FETCH ); + menu.insertItem( SmallIconSet( Amarok::icon( "files" ) ), i18n( "Set &Custom Cover" ), CUSTOM ); + menu.insertItem( SmallIconSet( Amarok::icon( "add_playlist" ) ), i18n( "&Append to Playlist" ), APPEND ); + menu.insertSeparator(); + + menu.insertItem( SmallIconSet( Amarok::icon( "remove" ) ), i18n( "&Unset Cover" ), DELETE ); + menu.setItemEnabled( SHOW, item->hasCover() ); + menu.setItemEnabled( DELETE, item->canRemoveCover() ); + } + #ifndef AMAZON_SUPPORT + menu.setItemEnabled( FETCH, false ); + #endif + + switch( menu.exec(p) ) { + case SHOW: + viewCover( item->artist(), item->album(), this ); + break; + + #ifdef AMAZON_SUPPORT + case FETCH: + fetchSelectedCovers(); + break; + #endif + + case CUSTOM: + { + setCustomSelectedCovers(); + break; + } + + case DELETE: + deleteSelectedCovers(); + break; + + case APPEND: + { + CoverViewItem* sel; + for ( sel = selected.first(); sel; sel = selected.next() ) + { + QString artist_id; + QString album_id; + artist_id.setNum( CollectionDB::instance()->artistID( sel->artist() ) ); + album_id.setNum( CollectionDB::instance()->albumID( sel->album() ) ); + Playlist::instance()->insertMedia( CollectionDB::instance()->albumTracks( artist_id, album_id ), Playlist::Append ); + } + break; + } + + default: ; + } + + #undef item +} + +void CoverManager::coverItemExecuted( QIconViewItem *item ) //SLOT +{ + #define item static_cast(item) + + if( !item ) return; + + item->setSelected( true ); + if ( item->hasCover() ) + viewCover( item->artist(), item->album(), this ); + else + fetchSelectedCovers(); + + #undef item +} + + +void CoverManager::slotSetFilter() //SLOT +{ + m_filter = m_searchEdit->text(); + + m_coverView->selectAll( false); + QIconViewItem *item = m_coverView->firstItem(); + while ( item ) + { + QIconViewItem *tmp = item->nextItem(); + m_coverView->takeItem( item ); + item = tmp; + } + + m_coverView->setAutoArrange( false ); + for( QIconViewItem *item = m_coverItems.first(); item; item = m_coverItems.next() ) + { + CoverViewItem *coverItem = static_cast(item); + if( coverItem->album().contains( m_filter, false ) || coverItem->artist().contains( m_filter, false ) ) + m_coverView->insertItem( item, m_coverView->lastItem() ); + } + m_coverView->setAutoArrange( true ); + + m_coverView->arrangeItemsInGrid(); + updateStatusBar(); +} + + +void CoverManager::slotSetFilterTimeout() //SLOT +{ + if ( m_timer->isActive() ) m_timer->stop(); + m_timer->start( 180, true ); +} + + +void CoverManager::changeView( int id ) //SLOT +{ + if( m_currentView == id ) return; + + //clear the iconview without deleting items + m_coverView->selectAll( false); + QIconViewItem *item = m_coverView->firstItem(); + while ( item ) { + QIconViewItem *tmp = item->nextItem(); + m_coverView->takeItem( item ); + item = tmp; + } + + m_coverView->setAutoArrange(false ); + for( QIconViewItem *item = m_coverItems.first(); item; item = m_coverItems.next() ) { + bool show = false; + CoverViewItem *coverItem = static_cast(item); + if( !m_filter.isEmpty() ) { + if( !coverItem->album().contains( m_filter, false ) && !coverItem->artist().contains( m_filter, false ) ) + continue; + } + + if( id == AllAlbums ) //show all albums + show = true; + else if( id == AlbumsWithCover && coverItem->hasCover() ) //show only albums with cover + show = true; + else if( id == AlbumsWithoutCover && !coverItem->hasCover() ) //show only albums without cover + show = true; + + if( show ) m_coverView->insertItem( item, m_coverView->lastItem() ); + } + m_coverView->setAutoArrange( true ); + + m_viewMenu->setItemChecked( m_currentView, false ); + m_viewMenu->setItemChecked( id, true ); + + m_coverView->arrangeItemsInGrid(); + m_currentView = id; +} + +void CoverManager::changeLocale( int id ) //SLOT +{ + QString locale = CoverFetcher::localeIDToString( id ); + AmarokConfig::setAmazonLocale( locale ); + m_amazonLocaleMenu->setItemChecked( m_currentLocale, false ); + m_amazonLocaleMenu->setItemChecked( id, true ); + m_currentLocale = id; +} + + +void CoverManager::coverFetched( const QString &artist, const QString &album ) //SLOT +{ + loadCover( artist, album ); + m_coversFetched++; + updateStatusBar(); +} + + +void CoverManager::coverRemoved( const QString &artist, const QString &album ) //SLOT +{ + loadCover( artist, album ); + m_coversFetched--; + updateStatusBar(); +} + + +void CoverManager::coverFetcherError() +{ + DEBUG_FUNC_INFO + + m_coverErrors++; + updateStatusBar(); +} + + +void CoverManager::stopFetching() +{ + Debug::Block block( __PRETTY_FUNCTION__ ); + + m_fetchCovers.clear(); + m_fetchCounter = 0; + + //delete all cover fetchers + QObjectList* list = queryList( "CoverFetcher" ); + for( QObject *obj = list->first(); obj; obj = list->next() ) + obj->deleteLater(); + + delete list; + + m_fetchingCovers = 0; + updateStatusBar(); +} + +// PRIVATE + +void CoverManager::loadCover( const QString &artist, const QString &album ) +{ + for( QIconViewItem *item = m_coverItems.first(); item; item = m_coverItems.next() ) + { + CoverViewItem *coverItem = static_cast(item); + if ( album == coverItem->album() && ( artist == coverItem->artist() || ( artist.isEmpty() && coverItem->artist().isEmpty() ) ) ) + { + coverItem->loadCover(); + return; + } + } +} + +void CoverManager::setCustomSelectedCovers() +{ + //function assumes something is selected + QPtrList selected = selectedItems(); + CoverViewItem* first = selected.getFirst(); + + QString artist_id; artist_id.setNum( CollectionDB::instance()->artistID( first->artist() ) ); + QString album_id; album_id.setNum( CollectionDB::instance()->albumID( first->album() ) ); + QStringList values = CollectionDB::instance()->albumTracks( artist_id, album_id ); + + QString startPath = ":homedir"; + if ( !values.isEmpty() ) { + KURL url; + url.setPath( values.first() ); + startPath = url.directory(); + } + KURL file = KFileDialog::getImageOpenURL( startPath, this, i18n( "Select Cover Image File" ) ); + if ( !file.isEmpty() ) { + qApp->processEvents(); //it may takes a while so process pending events + QString tmpFile; + QImage image = CollectionDB::fetchImage(file, tmpFile); + for ( CoverViewItem* item = selected.first(); item; item = selected.next() ) { + CollectionDB::instance()->setAlbumImage( item->artist(), item->album(), image ); + item->loadCover(); + } + KIO::NetAccess::removeTempFile( tmpFile ); + } +} + +void CoverManager::fetchSelectedCovers() +{ + QPtrList selected = selectedItems(); + for ( CoverViewItem* item = selected.first(); item; item = selected.next() ) + m_fetchCovers += item->artist() + " @@@ " + item->album(); + + m_fetchingCovers += selected.count(); + + if( !m_fetchCounter ) //loop isn't started yet + fetchCoversLoop(); + + updateStatusBar(); +} + + +void CoverManager::deleteSelectedCovers() +{ + QPtrList selected = selectedItems(); + + int button = KMessageBox::warningContinueCancel( this, + i18n( "Are you sure you want to remove this cover from the Collection?", + "Are you sure you want to delete these %n covers from the Collection?", + selected.count() ), + QString::null, + KStdGuiItem::del() ); + + if ( button == KMessageBox::Continue ) { + for ( CoverViewItem* item = selected.first(); item; item = selected.next() ) { + qApp->processEvents(); + if ( CollectionDB::instance()->removeAlbumImage( item->artist(), item->album() ) ) //delete selected cover + coverRemoved( item->artist(), item->album() ); + } + } +} + + +QPtrList CoverManager::selectedItems() +{ + QPtrList selectedItems; + for ( QIconViewItem* item = m_coverView->firstItem(); item; item = item->nextItem() ) + if ( item->isSelected() ) + selectedItems.append( static_cast(item) ); + + return selectedItems; +} + + +void CoverManager::updateStatusBar() +{ + QString text; + + //cover fetching info + if( m_fetchingCovers ) { + //update the progress bar + m_progress->setTotalSteps( m_fetchingCovers ); + m_progress->setProgress( m_coversFetched + m_coverErrors ); + if( m_progressBox->isHidden() ) + m_progressBox->show(); + + //update the status text + if( m_coversFetched + m_coverErrors >= m_progress->totalSteps() ) { + //fetching finished + text = i18n( "Finished." ); + if( m_coverErrors ) + text += i18n( " Cover not found", " %n covers not found", m_coverErrors ); + //reset counters + m_fetchingCovers = 0; + m_coversFetched = 0; + m_coverErrors = 0; + QTimer::singleShot( 2000, this, SLOT( updateStatusBar() ) ); + } + + if( m_fetchingCovers == 1 ) { + QStringList values = QStringList::split( " @@@ ", m_fetchCovers[0], true ); //get artist and album name + if ( values.count() >= 2 ) + { + if( values[0].isEmpty() ) + text = i18n( "Fetching cover for %1..." ).arg( values[1] ); + else + text = i18n( "Fetching cover for %1 - %2..." ).arg( values[0], values[1] ); + } + } + else if( m_fetchingCovers ) { + text = i18n( "Fetching 1 cover: ", "Fetching %n covers... : ", m_fetchingCovers ); + if( m_coversFetched ) + text += i18n( "1 fetched", "%n fetched", m_coversFetched ); + if( m_coverErrors ) { + if( m_coversFetched ) text += i18n(" - "); + text += i18n( "1 not found", "%n not found", m_coverErrors ); + } + if( m_coversFetched + m_coverErrors == 0 ) + text += i18n( "Connecting..." ); + } + } + else { + m_coversFetched = 0; + m_coverErrors = 0; + + uint totalCounter = 0, missingCounter = 0; + + if( m_progressBox->isShown() ) + m_progressBox->hide(); + + //album info + for( QIconViewItem *item = m_coverView->firstItem(); item; item = item->nextItem() ) { + totalCounter++; + if( !static_cast( item )->hasCover() ) + missingCounter++; //counter for albums without cover + } + + if( !m_filter.isEmpty() ) + text = i18n( "1 result for \"%1\"", "%n results for \"%1\"", totalCounter ).arg( m_filter ); + else if( m_artistView->selectedItem() ) { + text = i18n( "1 album", "%n albums", totalCounter ); + if( m_artistView->selectedItem() != m_artistView->firstChild() ) //showing albums by an artist + { + QString artist = m_artistView->selectedItem()->text(0); + if( artist.endsWith( ", The" ) ) + CollectionView::instance()->manipulateThe( artist, false ); + text += i18n( " by " ) + artist; + } + } + + if( missingCounter ) + text += i18n(" - ( %1 without cover )" ).arg( missingCounter ); + + #ifdef AMAZON_SUPPORT + m_fetchButton->setEnabled( missingCounter ); + #endif + } + + m_statusLabel->setText( text ); +} + +void CoverManager::setStatusText( QString text ) +{ + m_oldStatusText = m_statusLabel->text(); + m_statusLabel->setText( text ); +} + +////////////////////////////////////////////////////////////////////// +// CLASS CoverView +///////////////////////////////////////////////////////////////////// + +CoverView::CoverView( QWidget *parent, const char *name, WFlags f ) + : KIconView( parent, name, f ) +{ + Debug::Block block( __PRETTY_FUNCTION__ ); + + setArrangement( QIconView::LeftToRight ); + setResizeMode( QIconView::Adjust ); + setSelectionMode( QIconView::Extended ); + arrangeItemsInGrid(); + setAutoArrange( true ); + setItemsMovable( false ); + + // as long as QIconView only shows tooltips when the cursor is over the + // icon (and not the text), we have to create our own tooltips + setShowToolTips( false ); + + connect( this, SIGNAL( onItem( QIconViewItem * ) ), SLOT( setStatusText( QIconViewItem * ) ) ); + connect( this, SIGNAL( onViewport() ), CoverManager::instance(), SLOT( updateStatusBar() ) ); +} + + +QDragObject *CoverView::dragObject() +{ + CoverViewItem *item = static_cast( currentItem() ); + if( !item ) + return 0; + + const QString sql = "SELECT tags.url FROM tags, album WHERE album.name %1 AND tags.album = album.id ORDER BY tags.track;"; + const QStringList values = CollectionDB::instance()->query( sql.arg( CollectionDB::likeCondition( item->album() ) ) ); + + KURL::List urls; + for( QStringList::ConstIterator it = values.begin(), end = values.end(); it != end; ++it ) + urls += *it; + + QString imagePath = CollectionDB::instance()->albumImage( item->artist(), item->album(), false, 1 ); + KMultipleDrag *drag = new KMultipleDrag( this ); + drag->setPixmap( item->coverPixmap() ); + drag->addDragObject( new QIconDrag( this ) ); + drag->addDragObject( new QImageDrag( QImage( imagePath ) ) ); + drag->addDragObject( new KURLDrag( urls ) ); + + return drag; +} + +void CoverView::setStatusText( QIconViewItem *item ) +{ + #define item static_cast( item ) + if ( !item ) + return; + + bool sampler = false; + //compilations have valDummy for artist. see QueryBuilder::addReturnValue(..) for explanation + //FIXME: Don't rely on other independent code, use an sql query + if( item->artist().isEmpty() ) sampler = true; + + QString tipContent = i18n( "%1 - %2" ).arg( sampler ? i18n("Various Artists") : item->artist() ) + .arg( item->album() ); + + CoverManager::instance()->setStatusText( tipContent ); + + #undef item +} + +////////////////////////////////////////////////////////////////////// +// CLASS CoverViewItem +///////////////////////////////////////////////////////////////////// + +CoverViewItem::CoverViewItem( QIconView *parent, QIconViewItem *after, const QString &artist, const QString &album ) + : KIconViewItem( parent, after, album ) + , m_artist( artist ) + , m_album( album ) + , m_coverImagePath( CollectionDB::instance()->albumImage( m_artist, m_album, false, 0, &m_embedded ) ) + , m_coverPixmap( 0 ) +{ + setDragEnabled( true ); + setDropEnabled( true ); + calcRect(); +} + +bool CoverViewItem::hasCover() const +{ + return !m_coverImagePath.endsWith( "nocover.png" ) && QFile::exists( m_coverImagePath ); +} + +void CoverViewItem::loadCover() +{ + m_coverImagePath = CollectionDB::instance()->albumImage( m_artist, m_album, false, 1, &m_embedded ); + m_coverPixmap = QPixmap( m_coverImagePath ); //create the scaled cover + + repaint(); +} + + +void CoverViewItem::calcRect( const QString& ) +{ + int thumbWidth = AmarokConfig::coverPreviewSize(); + + QFontMetrics fm = iconView()->fontMetrics(); + QRect itemPixmapRect( 5, 1, thumbWidth, thumbWidth ); + QRect itemRect = rect(); + itemRect.setWidth( thumbWidth + 10 ); + itemRect.setHeight( thumbWidth + fm.lineSpacing() + 2 ); + QRect itemTextRect( 0, thumbWidth+2, itemRect.width(), fm.lineSpacing() ); + + setPixmapRect( itemPixmapRect ); + setTextRect( itemTextRect ); + setItemRect( itemRect ); +} + + +void CoverViewItem::paintItem(QPainter* p, const QColorGroup& cg) +{ + QRect itemRect = rect(); + + p->save(); + p->translate( itemRect.x(), itemRect.y() ); + + // draw the border + p->setPen( cg.mid() ); + p->drawRect( 0, 0, itemRect.width(), pixmapRect().height()+2 ); + + // draw the cover image + if( !m_coverPixmap.isNull() ) + p->drawPixmap( pixmapRect().x() + (pixmapRect().width() - m_coverPixmap.width())/2, + pixmapRect().y() + (pixmapRect().height() - m_coverPixmap.height())/2, m_coverPixmap ); + + //justify the album name + QString str = text(); + QFontMetrics fm = p->fontMetrics(); + int nameWidth = fm.width( str ); + if( nameWidth > textRect().width() ) + { + str = KStringHandler::rPixelSqueeze( str, p->fontMetrics(), textRect().width() ); + } + p->setPen( cg.text() ); + p->drawText( textRect(), Qt::AlignCenter, str ); + + if( isSelected() ) { + p->setPen( cg.highlight() ); + p->drawRect( pixmapRect() ); + p->drawRect( pixmapRect().left()+1, pixmapRect().top()+1, pixmapRect().width()-2, pixmapRect().height()-2); + p->drawRect( pixmapRect().left()+2, pixmapRect().top()+2, pixmapRect().width()-4, pixmapRect().height()-4); + } + + p->restore(); +} + + +void CoverViewItem::dropped( QDropEvent *e, const QValueList & ) +{ + if( QImageDrag::canDecode( e ) ) { + if( hasCover() ) { + int button = KMessageBox::warningContinueCancel( iconView(), + i18n( "Are you sure you want to overwrite this cover?"), + i18n("Overwrite Confirmation"), + i18n("&Overwrite") ); + if( button == KMessageBox::Cancel ) + return; + } + + QImage img; + QImageDrag::decode( e, img ); + CollectionDB::instance()->setAlbumImage( artist(), album(), img ); + m_coverImagePath = CollectionDB::instance()->albumImage( m_artist, m_album, false, 0 ); + loadCover(); + } +} + + +void CoverViewItem::dragEntered() +{ + setSelected( true ); +} + + +void CoverViewItem::dragLeft() +{ + setSelected( false ); +} + +#include "covermanager.moc" diff --git a/amarok/src/covermanager.h b/amarok/src/covermanager.h new file mode 100644 index 00000000..bba3f910 --- /dev/null +++ b/amarok/src/covermanager.h @@ -0,0 +1,166 @@ + +// (c) Pierpaolo Di Panfilo 2004 +// See COPYING file for licensing information + +#ifndef COVERMANAGER_H +#define COVERMANAGER_H + +#include +#include +#include +#include +#include + +class QListViewItem; +class CoverViewItem; +class ClickLineEdit; +class KPushButton; +class KPopupMenu; +class QToolButton; +class QLabel; +class KListView; +class CoverView; +class QHBox; +class KProgress; +class QHBoxLayout; +class PixmapViewer; + +class CoverManager : public QSplitter +{ + Q_OBJECT + + static CoverManager *s_instance; + + public: + CoverManager(); + ~CoverManager(); + + static CoverManager *instance() { return s_instance; } + + static void showOnce( const QString &artist = QString::null ); + static void viewCover( const QString& artist, const QString& album, QWidget *parent=0 ); + + void setStatusText( QString text ); + + /** + * Return the top level domain for the current locale + **/ + static QString amazonTld(); + public slots: + void updateStatusBar(); + void changeLocale( int id ); + + private slots: + void slotArtistSelected( QListViewItem* ); + void coverItemExecuted( QIconViewItem *item ); + void showCoverMenu( QIconViewItem *item, const QPoint& ); + void slotSetFilter(); + void slotSetFilterTimeout(); + void changeView( int id ); + void fetchMissingCovers(); + void fetchCoversLoop(); + void coverFetched( const QString&, const QString& ); + void coverRemoved( const QString&, const QString& ); + void coverFetcherError(); + void stopFetching(); + + void init(); + + private: + enum View { AllAlbums=0, AlbumsWithCover, AlbumsWithoutCover }; + + void loadCover( const QString &, const QString & ); + void setCustomSelectedCovers(); + void fetchSelectedCovers(); + void deleteSelectedCovers(); + QPtrList selectedItems(); + + KListView *m_artistView; + CoverView *m_coverView; + ClickLineEdit *m_searchEdit; + KPushButton *m_fetchButton; + KPopupMenu *m_amazonLocaleMenu; + KPopupMenu *m_viewMenu; + QToolButton *m_amazonLocaleButton; + QToolButton *m_viewButton; + int m_currentLocale; + int m_currentView; + + //status bar widgets + QLabel *m_statusLabel; + QHBox *m_progressBox; + KProgress *m_progress; + QString m_oldStatusText; + + QTimer *m_timer; //search filter timer + QPtrList m_coverItems; //used for filtering + QString m_filter; + + + // Used by fetchCoversLoop() for temporary storage + QStringList m_fetchCovers; + uint m_fetchCounter; + + //used to display information about cover fetching in the status bar + int m_fetchingCovers; + int m_coversFetched; + int m_coverErrors; +}; + +class CoverView : public KIconView +{ + Q_OBJECT + + public: + CoverView( QWidget *parent = 0, const char *name = 0, WFlags f = 0 ); + + protected: + QDragObject *dragObject(); + + private slots: + void setStatusText( QIconViewItem *item ); +}; + +class CoverViewItem : public KIconViewItem +{ + public: + CoverViewItem( QIconView *parent, QIconViewItem *after, const QString &artist, const QString &album ); + + void loadCover(); + bool hasCover() const; + bool canRemoveCover() const { return !m_embedded && hasCover(); } + QString artist() const { return m_artist; } + QString album() const { return m_album; } + QPixmap coverPixmap() const { return m_coverPixmap; } + + protected: + void paintItem(QPainter* painter, const QColorGroup& colorGroup); + void paintFocus(QPainter *, const QColorGroup &) { } + void dropped( QDropEvent *, const QValueList & ); + void dragEntered(); + void dragLeft(); + void calcRect( const QString& text_=QString::null ); + + private: + QString m_artist; + QString m_album; + QString m_coverImagePath; + QPixmap m_coverPixmap; + bool m_embedded; +}; + + +class CoverViewDialog : public QDialog { + Q_OBJECT + + public: + CoverViewDialog(const QString& artist, const QString& album, QWidget *parent); + + private: + QHBoxLayout *m_layout; + QPixmap m_pixmap; + PixmapViewer *m_pixmapViewer; + QLabel *m_label; +}; + +#endif diff --git a/amarok/src/cuefile.cpp b/amarok/src/cuefile.cpp new file mode 100644 index 00000000..d8eb4a60 --- /dev/null +++ b/amarok/src/cuefile.cpp @@ -0,0 +1,268 @@ +// (c) 2005 Martin Ehmke +// License: GNU General Public License V2 + +#define DEBUG_PREFIX "CueFile" + +#include +#include +#include + +#include + +#include "cuefile.h" +#include "metabundle.h" +#include "enginecontroller.h" +#include "debug.h" + +CueFile *CueFile::instance() +{ + static CueFile *s_instance = 0; + + if(!s_instance) + { + s_instance = new CueFile(EngineController::instance()); // FIXME berkus: le grand borkage (if engine is changed, e.g.)? + } + + return s_instance; +} + +CueFile::~CueFile() +{ + debug() << "shmack! destructed" << endl; +} + + +/* ++ - set and continue in the same state +x - cannot happen +track - switch to new state + +state/next token > + v PERFORMER TITLE FILE TRACK INDEX PREGAP +begin + + file x x x +file x x file track x x +track + + file x + + +index x x file track + x + +1. Ignore FILE completely. +2. INDEX 00 is gap abs position in cue formats we care about (use it to calc length of prev track and then drop on the floor). +3. Ignore subsequent INDEX entries (INDEX 02, INDEX 03 etc). - FIXME? this behavior is different from state chart above. +4. For a valid cuefile at least TRACK and INDEX are required. +*/ +enum { + BEGIN = 0, + TRACK_FOUND, // track found, index not yet found + INDEX_FOUND +}; + + +/** +* @return true if the cuefile could be successfully loaded +*/ +bool CueFile::load(int mediaLength) +{ + clear(); + m_lastSeekPos = -1; + + if( QFile::exists( m_cueFileName ) ) + { + QFile file( m_cueFileName ); + int track = 0; + QString defaultArtist = QString::null; + QString defaultAlbum = QString::null; + QString artist = QString::null; + QString title = QString::null; + long length = 0; + long prevIndex = -1; + bool index00Present = false; + long index = -1; + + int mode = BEGIN; + if( file.open( IO_ReadOnly ) ) + { + QTextStream stream( &file ); + QString line; + + while ( !stream.atEnd() ) + { + line = stream.readLine().simplifyWhiteSpace(); + + if( line.startsWith( "title", false ) ) + { + title = line.mid( 6 ).remove( '"' ); + if( mode == BEGIN ) + { + defaultAlbum = title; + title = QString::null; + debug() << "Album: " << defaultAlbum << endl; + } + else + debug() << "Title: " << title << endl; + } + + else if( line.startsWith( "performer", false )) + { + artist = line.mid( 10 ).remove( '"' ); + if( mode == BEGIN ) + { + defaultArtist = artist; + artist = QString::null; + debug() << "Album Artist: " << defaultArtist << endl; + } + else + debug() << "Artist: " << artist << endl; + } + + else if( line.startsWith( "track", false ) ) + { + if( mode == TRACK_FOUND ) + { + // not valid, because we have to have an index for the previous track + file.close(); + debug() << "Mode is TRACK_FOUND, abort." << endl; + return false; + } + if( mode == INDEX_FOUND ) + { + if(artist.isNull()) + artist = defaultArtist; + + debug() << "Inserting item: " << title << " - " << artist << " on " << defaultAlbum << " (" << track << ")" << endl; + // add previous entry to map + insert( index, CueFileItem( title, artist, defaultAlbum, track, index ) ); + prevIndex = index; + title = QString::null; + artist = QString::null; + track = 0; + } + track = line.section (' ',1,1).toInt(); + debug() << "Track: " << track << endl; + mode = TRACK_FOUND; + } + else if( line.startsWith( "index", false ) ) + { + if( mode == TRACK_FOUND) + { + int indexNo = line.section(' ',1,1).toInt(); + + if( indexNo == 1 ) + { + QStringList time = QStringList::split( QChar(':'),line.section (' ',-1,-1) ); + + index = time[0].toLong()*60*1000 + time[1].toLong()*1000 + time[2].toLong()*1000/75; //75 frames per second + + if( prevIndex != -1 && !index00Present ) // set the prev track's length if there is INDEX01 present, but no INDEX00 + { + length = index - prevIndex; + debug() << "Setting length of track " << (*this)[prevIndex].getTitle() << " to " << length << " msecs." << endl; + (*this)[prevIndex].setLength(length); + } + + index00Present = false; + mode = INDEX_FOUND; + length = 0; + } + + else if( indexNo == 0 ) // gap, use to calc prev track length + { + QStringList time = QStringList::split( QChar(':'),line.section (' ',-1,-1) ); + length = time[0].toLong()*60*1000 + time[1].toLong()*1000 + time[2].toLong()*1000/75; //75 frames per second + + if( prevIndex != -1 ) + { + length -= prevIndex; //this[prevIndex].getIndex(); + debug() << "Setting length of track " << (*this)[prevIndex].getTitle() << " to " << length << " msecs." << endl; + (*this)[prevIndex].setLength(length); + index00Present = true; + } + else + length = 0; + } + else + { + debug() << "Skipping unsupported INDEX " << indexNo << endl; + } + } + else + { + // not valid, because we don't have an associated track + file.close(); + debug() << "Mode is not TRACK_FOUND but encountered INDEX, abort." << endl; + return false; + } + debug() << "index: " << index << endl; + } + } + + if(artist.isNull()) + artist = defaultArtist; + + debug() << "Inserting item: " << title << " - " << artist << " on " << defaultAlbum << " (" << track << ")" << endl; + // add previous entry to map + insert( index, CueFileItem( title, artist, defaultAlbum, track, index ) ); + file.close(); + } + + /** + * Because there is no way to set the length for the last track in a normal way, + * we have to do some magic here. Having the total length of the media file given + * we can set the lenth for the last track after all the cue file was loaded into array. + */ + + (*this)[index].setLength(mediaLength*1000 - index); + debug() << "Setting length of track " << (*this)[index].getTitle() << " to " << mediaLength*1000 - index << " msecs." << endl; + + return true; + } + + else + return false; +} + +void CueFile::engineTrackPositionChanged( long position, bool userSeek ) +{ + position /= 1000; + if(userSeek || position > m_lastSeekPos) + { + CueFile::Iterator it = end(); + while( it != begin() ) + { + --it; +// debug() << "Checking " << position << " against pos " << it.key()/1000 << " title " << it.data().getTitle() << endl; + if(it.key()/1000 <= position) + { + MetaBundle bundle = EngineController::instance()->bundle(); // take current one and modify it + if(it.data().getTitle() != bundle.title() + || it.data().getArtist() != bundle.artist() + || it.data().getAlbum() != bundle.album() + || it.data().getTrackNumber() != bundle.track()) + { + bundle.setTitle(it.data().getTitle()); + bundle.setArtist(it.data().getArtist()); + bundle.setAlbum(it.data().getAlbum()); + bundle.setTrack(it.data().getTrackNumber()); + emit metaData(bundle); + + long length = it.data().getLength(); + if ( length == -1 ) // need to calculate + { + ++it; + long nextKey = it == end() ? bundle.length() * 1000 : it.key(); + --it; + length = kMax( nextKey - it.key(), 0L ); + } + emit newCuePoint( position, it.key() / 1000, ( it.key() + length ) / 1000 ); + } + break; + } + } + } + + m_lastSeekPos = position; +} + + +#include "cuefile.moc" + + + diff --git a/amarok/src/cuefile.h b/amarok/src/cuefile.h new file mode 100644 index 00000000..621830ab --- /dev/null +++ b/amarok/src/cuefile.h @@ -0,0 +1,82 @@ +// (c) 2005 Martin Ehmke +// License: GNU General Public License V2 + +#ifndef CUEFILE_H +#define CUEFILE_H + +#include +#include + +#include +#include "engineobserver.h" + +class CueFileItem { + public: + CueFileItem (const QString& title, const QString& artist, const QString& album, const int trackNumber, const long index) + : m_title( title ) + , m_artist( artist ) + , m_album( album ) + , m_trackNumber( trackNumber ) + , m_index( index ) + , m_length( -1 ) + {} + + CueFileItem() + : m_title( ) + , m_artist( ) + , m_album( ) + , m_trackNumber( -1 ) + , m_index( -1 ) + , m_length( -1 ) + {} + + void setLength(const long length) { m_length = length; } + + const QString getTitle () const { return m_title; } + const QString getArtist () const { return m_artist; } + const QString getAlbum () const { return m_album; } + const int getTrackNumber () const { return m_trackNumber; } + const long getIndex () const { return m_index; } + const long getLength () const { return m_length; } + + private: + QString m_title; + QString m_artist; + QString m_album; + int m_trackNumber; + long m_index; + long m_length; +}; + +// <> +class CueFile : public QObject, public QMap, public EngineObserver +{ + Q_OBJECT + + public: + static CueFile *instance(); + + void setCueFileName( QString name ) { m_cueFileName = name; }; + bool load(int mediaLength); + + // EngineObserver + virtual void engineTrackPositionChanged( long /*position*/ , bool /*userSeek*/ ); + + signals: + /** Transmits new metadata bundle */ + void metaData( const MetaBundle& ); + /** Transmits new length information associated with current cue */ + void newCuePoint( long currentPos, long startPos, long endPos ); + + protected: + CueFile() : EngineObserver(), m_lastSeekPos(-1) { }; + CueFile(EngineSubject *s) : EngineObserver(s), m_lastSeekPos(-1) { }; + ~CueFile(); + + private: + QString m_cueFileName; + int m_lastSeekPos; // in seconds +}; + + +#endif diff --git a/amarok/src/data/Amarok_1.4_Welcome.ogg b/amarok/src/data/Amarok_1.4_Welcome.ogg new file mode 100644 index 0000000000000000000000000000000000000000..a59cc3d32197e6629c6b1ce957abb87918caa4f8 GIT binary patch literal 131454 zcmagG1y~)u)-XCqad&t3jTP6@7I$}dDDG0McyV`km*P;|-L1GoDORAs-Sj=@JLi7) zKK~!Gvol%AT3MD^$(}*p+*}!e1pbvQwn3oRYS^%HAt-8V=xA?E24dkPQ#Z9SwzoBfc=OLZ=*7gDgdAXfB_XfO1zmQePKc_okvQ-$JbgvTUbg$SU;X|BxvB@1sSIaHUK~a z{&eus`TLSq6TIelw9$?^yq0o2#bmJIia&Vazu(Xp+Z8nwS=&v}Q^O%^f;oc(L_U%S zV=28BMxo>(c;Mm-k>e0~afTxTLrG2m0{akIpbYy_LA)H_QAvI@-!Waolo%p&!;F*+ zXkN{@ssl#X+i8OLAmd*J{9iuEz;h9U!8bt@gL_3EN?(-X2uAe}TV#Mgm`&hAES6*~ z)@Uu!_%xOLFD6Ayu4xVlWffUfaB|UAcQsvfby;-v(M$=}{^q0kEm(UgSobd25HI9k z`M1yJ-E02KIvE6zL&=-8L!U=Mkhjg72Ne`j0STDAii8Y4j)FceU$V%|s>(9G$)dc; zcC?OZv<~TS7XSj>Hj^8SPU@$B4Ak#IuH#yP)R6J4LUNx{*~|$ z0M@C95>vk;=LaXwK_>xl{^0y>I6-FoS_bvsN_bT}0EmEy`W=Xd!O~D-o^|9>cM@E5 zl3fIg;#DI5=k)ony?_}Z&ooQ2@`vMyG5jkQe=u6STsU#0e<*=Fgy!y&rA?(>jq{?U z?d6nWrO)<+sif1kl%}Nr)u7D;{tj4|^dqVLIjK`=P7{(yX@&pPJTTH>D@$j-Q)&ApEm+`MSVB5&V~ff^`2J-UrFt0?J-l1s5n(Io)-CY5 z;M%=O#`!q>Yuo=e9}%$L443c*G6TqbqG61%$cxD1Nu0l;#OHv9=nF`qqTfrE1}Z4w z(-t}h{N z<;hDTMGR_io@QrO<`7g>QB`-f)l7C-Z4TC5a@kz;*<6g$#|`=4hV?(51Av_-=&w&E z7)62xGXmwr;QvwZzdgqRYcQ5*IF?Gfj!JfhY5W(b{1xXkri3D=tTMLlG`7nuj;S)Y z?ktz-tg7j}tLbu+sdlrEX4AhM<{!7&Ty*&_&v|tb3f}0Qk8#ld@thpG=xv^8O364X z%{Us5B=fMe($uV@(maI!i^^+Smc`<+Z+6!qI^GUV??I+A-laNuPR8c+lPf6x%956^KzayaMR1PDGe zVThNuFn&goo;eARgPytw!+0kne@1e+AOTN0JhK49III#p2!I8D5Ob$uupvaS0Kf=I z34uO1L1}`1KSpT;v`3^Q#ao=AJi!Y`95e!g$qYoJFCS7a@qO+=^s;Q=ms-~-{=AEkgf{!#9sw}&Vt*Y9jx_o1+n&h+Ptg5SS zx_qaqiQ}ui7`%1svxWDHd2!wa3^iOZHEed-q_a%3w8|?fOQS0*D=llXEvu^3&O0p2 zD;udZFRQ8;sjDupvN-`mt4hl1n99nS%Ihi~%a~4@O3GR*N9te zRaKSOoxVa_D^CU-PFmT#i|9v-%8y%1TU+f;S{=rkY>D#AjyoAfzt@b^U7WPCHn@;0 zs=BNv=$St*$1n-R9e9?Zv3Pvg1~U(M~WS(At1KQ5zeP?kYDJ zkXcuW=wm!|2R~`OI^gigw{z=~UyTGm_!Vp$5U;j@IU{{o zH84&}sxmQ7X)GiD6;)uglq7vqoRkcBR#H;6!Ae>-poI)28!qsyz^LM+Y?ueMv}GDy z(6kW$&Z@Fe&DyGR08E^^5KmgFwh_nL4zy1oWm)-3ytE%jI~q(}*_x|p!Ri&DZ7aBt z<|I|QFsBp+1{m9R%%^!>{F`$lE9QX(qsmv}47G`IVB-5LT9Ld7Ge%%kaY~Wk5!yB_ zMM+j*K$=oSdm~Y49Dw>1)f48hF+5hXO?W;Z1X1mylPt6%AFIkhDZ| z9OGqx7aQZHEp?O;qo-FL6NRI0oZzLW?_c1Bqb|geW?=4Dv!#!jiL|_td~M8W3X)ToKVCV@Qo)#>F9$Lq!{Rf z(WE4)3&GJc0ueZ#vzbcJAOcV5pLN6l$p{G)LguW(&VU{yBSl;K87$FC7qBT5_K#^< zRW>Y%z*Pn>uuD^eO~I_PFGY(H08YUZI{mwjPy;crfCyebm@cn5n$Z~VArU8dWh4qh zVHn5=L=l6-22TTguNvb87ieN&065;u4Fng32{W$%C8;Aaa2tK0^D7`h2>}2YK7k{m z8buSQfY==Hnh6Pbysnv2ys)_m@977~65^D=pNYFYZ1Dl2omn$0B ztOm5ir7HV1tgFZi7mO;n7Qsf`xSwV%1qN7;w*bJ}8(5$Qico@^77w0*)8sD`{$yY= zMuI}HjKV<&8RBsC{VCuWrwH#g^Z?&vU*nHRFaUfx00!_L5WN-|PSO7fO#V}Y|Nmj4 z6dY%vtAX#<19*gR|3u~QG5%f}+x#m?Pxd6(C;-#YoTM0cq*nGeqouDO^>ZGK^ zKwX&hx^&EGNilh`71o(ffaR^B)sQ*6w zknkYYnMkT5XI zKIa+n0UA06L$LlxQBe&Mh==UmZ*$Vj!1GBIqWrU) zh5$$aAP$*~f+CGK8s#f$3|cID97a550@!>3;0J_1D&SA`CNwnEEIPk;2a4*QNl-!G zE;RLD@M{4F_s>=Z`fvT8-L{CpKj7Eh_A41jMP+z4nuE2?Ejn5{T1Gk$2OBpRJ0}Mx zh#j0jY-}L5wGBr4*Tew=adPa=u5NGaqM7E6SBc`KSNe+vEjQcSa`Dxos8($f+yweQ zo+&LdhWLjg0tD}o9tfm9L!3L!Ki>9PFMMLEqUd%`8v>2AHkkdm-X8o-(JFk9b|>Gu zQW7=E%A(u(cs$x}e6Y}S5{7;eurrsYc+$;gMSKzr!^G3+>~CpRTt!S9GH|(!?{D43 zUD;R#W5x`j&2!_ItUSwPLH|^bcW;$6rm4D<5P{fo$=nCpWBnWn^6M#j%FTtI+)~im@e+) z_qmyxMWtM9IL=GD!~Enfe3nqA;wPzBlSz;cFLSQ&}naS*R(p{n<|9kC9L|)JehU==5VWgGw=H zQOA#bzObzkM`4SqtfdcPZi9}*N2mFoSCR3p(sAJ}1(G-}t%Wj;DWCH$7T)N6AWC3{ z6jOCmv8gn)?~V)m43dqxEo0V4t8@x?_<6F^^}Mvc?CpD)@^!Xpbk?hxdDVvdmTe#N z%L-2-*~ii~VPXS>$##mY3L=;2fy8M#&>q7fDg1H6xgu;s2r^TABH7|QgglEd_upIE z=EiF0$9p+)bC z6t}0DxB}=2)B!l^YoVvA2<+F}VG6xo+Rr=-iX<)fhk1x>w=*Kcr!sTlJ+?X>{NJ=V z@V)I{n5%6Z*=C;$_tyC$<||C>OQFVxZ=Qc4Xr~nD_9-0U;=A}KVOzyJekt&oaP~FJ z=aQY{`C<0Xdb;7ZlExgXy!~60+lNg|O{5YpTCA@|WScIDk&Ar_$#3;(zrss}p@xh4K}l@vh6u1*GR)l>#hvAumj$ zM94mCZbZfypt^h-wRWXSxSIN&NYiZL7EPK4K;Kp&E$}~{Tw~ci7~s7BewvB5SyHLV zx@-orMftQ-(ZXSYSUztLH+HF4D&mnDv)nlkZU5sJu`(%66c&lyHNPv#iRovqL|1(G z-TQI=_M@s=*N+}9T55VN{yn`UPfuSO>Xx@B7_bi(VXJfG_+gst(zGyOz+}}veWIC4 zv!j7vBM5z?T`^DXMx{1Q?-1Bqtx?V$j9uf9S`X4e=}=^i7l<$Py|Cx_yt5UUj9Xox z)J|`P^x(sFozfSj-O^?1CZM4J544M==5mkSi2j)LK>pU-`^Z@rp5%8wCpxCsDlvP) zlq8n=2YEy$3MI(V!|X$?nM1^HSAPa~#HG_u;q}ghM|-%4P0I8%#{tZ9*-Z)Ek!Dd` zjiji`6z?GC>;!S4j`wH>Z##7Lbz_jb)w`ND1w8dAZVf+7p7y%zj@1ZgpA!vic4XzC znYJhFzd%OXb)+}Uvnu*t9zXfPt6+sdf3RGT;|*ux)@^L^BH8G7_DW!~mN!UMy}o0H z{3WsGcvBzp#5$nTZnsJB0j-Y=r90W{=!HcCSs0uT|FR>4*i?rCZ&zo>lX(^$<3~vc@+c{2|w63&E*7at7R&~n0sI2@NZc*z$ z47{TGF#~z6)(COhMb^dAG_ZO|tRe&?sS`(Mk+5kl|I zzD^F60dtNw@geusUp60aPm)ml=N$H3j7$77K8?xC8!0z?6zt96w7qDnmWsK!BHw16 z&of~H1)hSqlQBNCdA&+AHrB1R0BEZBN$Xav0d{|W&dXYr`(<~Ff+_a$SFf@p?}gLM zj(H5koUdRy#N}v8yw32Sq*?{V-)RISawG0!ezz!?#y)maNE^}4jwxk`q$MUKl$Rdd_&0~&lAJ79ZR zt@UPntph{EB*?E$b)O-!=G$KaorQDS(M;AX?_K0-sk*V^2I_Hx{h=E|1i^=W@q?B2 zUiy#(p#cxNHe5x@o*~OxGMk(p^5uik5f!enp#fZ1-ro0tDZrYqM?K+bw!o~)&$m7f z^u(OszL`HuaTCwM%zqL<_R2x=3iAD0_>-2ybO)nav=Y)LJfayoQg?qpnXU;p9Ud7v zWIn@PCKA!PZEm;EZ%M|j{7Hl6e75O@9Q1&ImqwDgYS}lHOwPSqe zQJ~CKTkwQmhrw}&T1)2Kl_&QNVn6zFXU&G_Gk5iZ`wn_`>aDKHexX%nM*@~66H~*$ zs7W?&!`sWZffskwt+dh7ww1I)*hZ|P@^Xf<<*7VbSuNTD;g)4N+9&st(LbSsN2530 z3Oj{Dnj6u3jw6==$^ki14qVe&c6p1KoU6;47mWsYRlVQxKJL!WFdal zf$_it*7UGl@;?uiN)eY(| zxD+S=zvGL>Pyq%T$1z;qH_x`TWe@C{4|Gm%)r&RleLpuoEI(-+?mCHv>Fs(bedj*b zT>yYLj^?;~*;wn(K1-zQmef6f#lRQVc+v0|I**!!@t8xji9O$7492@bZf+!)6?kA7 zaQ5uSTWfwf8n@Fly2~RhK!~nB5De^&mD)himdOPg4+Vm}MugUD!f1doS5-g=Jw!M#W>$pEBCrM7&_zoV zIaes%_|xhhFX<}ZrhZD;{SBoXFy?u0M3xzajHu`Ah}8`RvH13aeC4N_HT-0IgWmn; zFFz;n7x{r(YM{cZ@XDa=VX*zWaXd3729iZ@(Mas@mX&3z<0{bLY6f)MI4@`o;)YrC zG@bd#N=i9AxY#NI$tLyFY;MjPJ6C+TQ{2lton>#|s;@(s4Z{9@t zAHD&|`_%ArH*Jp?t|PJ=3P*(kHXy2YWUe~eR_c*|bCBYE<(b^B62nC#Tdwoy8Nb~9MalT@CM2Ud4I zZkhT9i&SKhlqI$K>(3)+yi6*QWhZ;xUty&`2(smU$$$#I`&~=-*3A`V#Oq?jJ{ls# zz?Dp->#TMp+o6Xw(xl_sqa>ev?6R{?9f8u!;@0KqVIT_x&sEN<7jB&XN=?TwOW z2mk>P;bCeylHAMz2I!iACrNCdFR{js3tf1mivwSjxxAJTWwQhQsI$Xfc&!!hDhR9zpCHHq;P>&wU{}SxyDS4!XueGOVSkcmeRLYB&yl@@6-Qb_h z>wFe1ngdujdTOg#?k8K=sD`?HA>*7`Rs`2Fo*=!B|I=eAq+`#cRog+?6%rq`+kO2HU zXC<>-N%%-Oo;HH=Yzm>W$_hn?PldPb7u*~VBR9-Hks=V{L~F`u56A6=(Nq{_LsDIt zteqva>}RzoZg9z${^?o~R@eS>6<3=#KlJpJh-g*l#1XCy^ReCtWy*Y&GtsL%$M$pd z)7;VZqN;fnLyK)GA?z*(hioLIFAUaW(SWd;k!DZ~qq@&7Q;Dr87SV|e?X_*BQL9wF z`!{aQ6(cB<0$boXK^knocYW~>Ws z_JZ$qMt{hzZ;+yBupMz56nu4gfZLpgMGaA$U=}bF$LqiUtd>9mFtpjF=GXLCXUopqu-)_GM)Wq zO!#81pgjAY``1;gHVw2oc~C-55eLuyTrax@3sQnqzm2K(SvZiI<)J=(>1=@C~H30&8Y=16BI3U+e{53S7nXJmxV%b z;M|?P-$v2c9L+RgMJhD>;LS46@Bm39r!}aw8R3zQ%2vBgu-T5{=|2assW&(DIGb%s zux=J7ge(uGid%9Vd%(4HI!#Jh`bAaLaH=D|lM+5f{TVEv-pWC}J%tHAlX(tL{50YD z;dWGu#|+HX(ya2^blquI0tjCyJY`FuFIu@cbOxft0n|SXr(^gu%!aNH+t0;eyV1mM zLS5M8dV!~!{lIUyx zC&OSUomu-21vM@k=2xYese9@*Ygm%0~=? zM+vUDFJ?QRX^j1R-D0(LVs50gYR2Hw@)OUSZ8O3^6YKtK(B|N zY{gv)aOlR*%SCC8d$n}c9l*}E42x34k?t{J`vL_ z@?erRCRjApCU~~Y%%5a&e!r~qL>FHN z#WgYqLMos<*{`NKGQ^g>Kk2+cUBZQ^Bd6^kbek!f*91nSewlo8e;o~HWKR16-Cc7Z z)vw55`V$4OzUmfYe}*|7w13Iy-JuhztU~yV+ERb;PJ!Oi2p$NH3^K z!fv?(#9F-DV4qQj%u#ld>3I0#B(1)g;r;O@74UwOI;}z300TtIk0xAmxM3eh6p z;4{f5wkLl33@yg|t)~syUn$w^l%G;dESj4<%0X943$ua+W12kH)qSwODn_G$G!=tPMa>*B$ocwI>`mMWRa zMdm>7Pz)bd*nN(0pC(Hay@f%)<>=ohJFBu^U_{QDNQL`_5KPA4cs8EC-HYj5apIDM~jkw)*$HTG;tTzxYj57+F>yoauk08Qk z2aD7EWXjwy9Y4s;{P2b8OOx{Z26R!#j;*n-Bs=2Lw~CsDQ2moZ66*+JxcS9)2fJl;^+*s%C}p6Z+IR59sWtsc6N zot{5B3)J>u5wjDjKL;6loYoO9xj@B^(`qeDGOF}k`(^che1E%Y`rWYlQVtR(R)02U z7Vz~D*r#SkVkJn?qoa(ay2duJsULLAQk-V%*OiZYXqo-?5y85``v9qHY&ByIW8ic^ zQ@-zdekt!Ls?Mm-dgdrb|2#5{hM9s9wH9XEVdP2lQc_qO5-xxcn_C!Fe71`l%Oq03 zAuAqzy)|dHBK6T!V$~AUEVH{NE)hEWc+xcapfnY^VD}Xv39n zq`5d44?T>hhS32Z#>1HLsxjx1cOJKxf(1N5X#-tZ2F<+eIFfIA*k*)BS~~;hsI<@y z?UaLVhn}NH0<&4BrWMjVZoK>L(W_W2QjyV(gWv)cm{p-D;8=eJCHj1qj`}pZ>7)oe zySLw#)`phCn(p5TBBO=oxRBX8=}AoTP;=BI;?^>6uPU5OFa@nejdhGg)Enz*7sJms z4JSP9R(!N>&j<}Zyi0Wmt(93!8>3Xz<3~4-jQ@iY#9LW-veEbGe7DDs_2gemx4IhM zVnuQ>eR|{XLq?IL;MxzjJZZjP6XW!b3SymGmKY)Kur%v;!FGSos@k{)_FF++)_FLY zjxfVQl)Vjs&A|E1#6gDscy3k-)d@zgeCzvzUU& z`ncvLj{~U{_}x>Sj;6l}k_Kd{wLGgBFJt&}y1DznG}f|Fci#moxOe&_zD%X=cU_XO z`re1%hSdsyX;u_;&=^;Jd>edb_sQ@FUBfz=8pdf`VS6zjVdH>vsr~c^s9H2vO5M6F z($A7;)BB|5ZlVPJ44Vwp2^Nygh8ODwJm{*jE=BygvPZOp#N)^`tQ!7|?{YHi$EKb2 ze-9_m%kf3r#@P-e6qJsWOEGL^r-Ir3%)m$7>ob`_X{lEtyn>L*RPGU`LDM>DVB#*LQ9%?j( z=cdpA+&G49_&O%?8_fmBgld;7sP{~)EzG^&;Ublhwrz<0?WHUa z%^_$WwfM%HZg!Q86@uzSWVbx&sBGM&WcF7~mebO-u^O&pDF(7pqTFiZIXSOT--=$Q z&B3_)=I4?#xff|-&-X_7p@_wti+L3k@*ujqkGMZ;-uV~Edxsl%QooY~YsJPTJO*+g z$g88i;BXAZXr>4hmW8ebB6BU@eEy!Y!ZDD_pA^Wk4>~zP&EA8~mS^I#*uhJN%{A|x zAq?fL5oFZ5AK*%CA~74;S|H=5Eprt!c)`IQW#+hGnY;iMeMm=usKh(+A_2hSs>hB|1sFgHQjCgz~Z)Q zR(TkgtqoeGTj=#kKAdpO)H%UklJP7g{4TzF{^Yc-;92BVtNI8;eE8~X&N~DEo$Agm zvKV5c+*WTMBfWF%A-=2bWpKZzEBe?W=;wTH@{NP!P7fvO=#DkoH)VYwQe0beq~7?M zc{Lt&v4_ZmmNA%n8##02*+vTvSmqV6G7BJAypOzDk+;D-1~#?7v21&Pp_9gVgQmb$ zKEKUsr(LL5Im0YZbk#H09#KnO=@Q&DDt+Ioz8gi|PC#&=Fm#YaLiwXrxef>JEfY`A4ow)kZ=?Kax}_+6fvHrg}MD%?U81!w+xGC=FTW<@;bx7yUFLlWD4w@ z_J#sa`lKHfn3NAVZT&lWcGvk?k{b~v<~<$FLKRI5m|ZU#zfVhjr+|*;h{oAp`AftY ztQijK`@g=^Kxe1624Q$c)baG10%$CKN|Vhld%IN0$7$+R)dulZ$*l(VTmzZS_|52M zV)%TLk{IPJ#mniqcNdiI($aw_O-PrP}sY*g_d9p1N4@in-l!^c4u>G zb8xcV_0Da@uy1lRo&AIwI1GSe@;0$%kDMfc&t6RdA1p>z6MOp?&}?}X*>9ZW$*L^ z{XaZyZ$@cRV`2EId#sr_=H7Fa-m-Zq*%j}m>&Y#w!m6oLO)k%V0 zA84l@LGiK?@Ykc(>p8dwVMp3PhS4nYoSU9K_xYbmf21B)>qt=lUWlG&URmcRxtj|l1<&8v?9pv`+7{;>jd*MC4t?#>NG>6Bcemqkp1+U25s{nztr{%Zgq_w^=4*G=R6-F!47(N72`lgoJYe%TRqv!Z;EcErcSl$aXVP zQgW>UzZI12Tj<+VkE&7y1^sKcpUhOg-fk2^?0WWc)O%a2#k%<&utIR62{bhLTACEn z+q~eFoULSVy1fW-WXCt#pYcfic^7)B3z_kLSZwY2yiy+rWX^(FfbjD)pBMb>yqEN+!xd_-%H}YLtD*1X?~IbzQCW7M3KZ(9GtVdav86?`DNW5Ifs>R zDv-6MQQGQ=yPj9|oU5ADFA?r1$iPT$%N|A5z_2s-sCS`i*am(R`>|7^%#z8tEX5`%c(BAT zp-ciM+uc20DEy^(<}MgDb>Nd8b)*`{_j&yE-;w2-Pgo?6%3c{^U|^0zY6YMg&u?`gJ{s&i4vqdN;X`7 zSfv!fL3Bm1Y&S7usjZy*5V@Mo5qoVcIX-RZw%*YDJY7kpHw5ek>}|*mdQDg^AL*+v z07kg)e#H1BCxy|FPSA}06!^fAw=>l?s!{jJwY7sF&buC>!F68f)0r?*7uH%D6VM=n z_Bkqllb1sFsZ!KTo@9ZKFh-)7NIx6uO{jVT@h3;5G1tZZ2?2ijQiVd_MQsQgqaSS9 zU5Cf;#E*iRGH)e0PsoVG^)8AWP;~dM9<*uJ-!lPoA$wb4gZ4>=pZgZIN?`iP3~v|3 zIY`D9GB@LN&cd3i9m&35cE8P|cHFIEr|D8l6@jd9?VX1D@-ny7)y%+|V&Ot`UGwv2 zwsl(-l5*x1ZB)IeDUGMh;eC`SeCE;bof`~G4`A2#$`bXFr~%NA-KQ1t4+$RF>~F1} zT<~deiT3Z*K9qC)QdU;dBxBLsJng!1%iIGrg!IH8Tz*=Z{Aumgzm~&w3HRmj-dzf_ zN*0v2U_xMnB4_l@_YdLVH`?w$xBOKV!e1c@9LM|XTk~(a%1kaF^y9_5-@$%)d2x7S zM0BXmH$Q=VJ4;cz!)}k83dh)dEOReZotX}?>w9K7_^etA=Zt_CG@MQ0ufK{iWwn$`F6#02H=0A1-BhK!f8WzDahr*sNpY1_oQ~n>Pbpl@vyM z&$SNgAzEkUKEwqIm(w51L7)8`4(?si`oW4>J`Q_QCrvln(O*O}$lHc!hn*FRME+&d z9j7B9KDF2Dk3qQ(N zb2ViaYRosie6ne)4Z9&3=UodH_qk^{Y%~K-1MY1GZ&`gAA?J-&wRGQD^z?2$*B{*4 zj%bnA4WC|1-Aq3>=yNUn-gEX4ebhNR*HC-|)8Kl)an<=WtO#ws=qkGaNTH^}hS0VR zaF1oOP1mTgdT<#OFW#)sjJ&h7w5dw|jRKr{j_>x|W|Lk`ER1UTH%{T&?IybjcK*Jn zEF)fbpLu~UXBL(poW?`Ld;t(Hmh2gMG2S^(M5xX(c8=>xt zF_yAv-rOOxAS=#hvy>u8kgX>)pkFpcH|BYE6!vSfU_f*8X%$O>eCVfdu*`Bw;qzPP z$g`!gE0pBNZTyGKP{-h&Q`kn)=%KPc*?6I)*vsK49oH!R~*BbJ+p;FoAB{Z zQju+=t8R&?6r~Agjn3i@9f3dOT!_bA-+A`48uTyiE@EN@=5GLs?0(R&0|nwsi}(Po zJghCuZ%vm7?h+DSZd$Leo3YP07;p)$mqbii4?<5mAiSbTSAL+c`@0>a_jw;hmUZzW zdphIVC7Gzve#+@!1H7YU7-smOvM_@oFc+YCm*ah;k2T1aIX8F{z)%DG)hlslN#ccK zsQ2jiDb4hfZ&NrAA3R`NVtUo~V;utgfRBURQ2yj^>Dm;pi!<73HD$A+P#m5Xz=ZUt9i_R)|72htTM_H{a{0p1r;gtnD5Yiy4SZN-t(Kvmt%sEf&AL#r%RK-QTbe=wU_XWKZ zzUN--(>ApknwIevhx==O1zWffKx9a1TBuL)lQKwDwf&SU4nn~Sfvlw!8-^=$VoBC< zIcZk%crOfcRH)Hp_1Kk!P@O!MY@Jo#!rd28Kz?3pG^KVTLnPPg%6*GT?)TQq0eOwE*#@PGyH(Kds!weu_@E8 zdJK_4N<9@a$JQuITdWahRORwX3A}GDLay*##-SQGKg+JxkWrMnwv;aMhfV2fc4lx9 zKWj2Ui0KLvH;{d_%AZx3`P0S5wWcyjk2)59dDPQ6`=@lN`;X6hG>X5HGI7eVR^FgE56ZoxJ%Y zW%P8=OvDv*!TI=$wUJoS`=y+npMr6olz8Y*JuX0S-+1q$oAtHv=B-^<90?ArA15&7 z{@z*SA_qdm3LbUaB5W5LIK+ji)B5q9prNfEae_r3>L?O_tSu3~8M9A5eYI~R6S|Ro zt7FTPAy)8WL(MIc=Zd>T5-oQ8QQrCHVE{hD3U8fbwrzr(xZYJXq>4*!{sW|XKsJ>4 zXQF+i(6}(6fa|9l9I*i6eWFO67gD(4dy=IS()JE#{uH!@^mER@5{b!5LM=v|)#*N# z>L0)HU(!W`eTTNwSjrLJVNx74^4#~16^cO33}CvUm*BS)BQ?>7-yiPo42NccUqtq8 z?f-P1{*-?W;gd8$=fn2g9##9NDSN z&&1ljqvms^d+hxp>sE%2sJf-d@eR`11N4S&!hA{+VF%Zsn5I;S-^<7MJMU`mmY&-1 zU|1lWsB};74fF};Q#Q^)n0f|LT-`2)Zye{4&{6u7_YFoZ0sK-g&IZ9 zL+$;|x7b6DpYXE{YMMU@C_OY=*L`3K87wr!Ut2u5V}kTIg%73HWEi+jS2~7%RQn~al5?%%WorOC#A7dE~-nNtZlAf8)yh}%6LfEtS08nuXNd6MEsw4UjO`9Y`} zhO#7m;}n`Bu|hH;ZO#>KwU-akw0BJrX|wvC$yv7ZyVg04MAAVJb8j)^YRuEMaRHrZ z+qc-aT85(-X8sTFkr0-ClED4OZ*QS9>G;y7pgtx2`~5cvUYuyyDCSiItki@Nqz-By z6lmI~Fh!{h&#rx+M5pp!@K+JNp+ww$a_xNgDq z`1%O%Sh^(Xt@}%P;wYLX10V3i=H3-vsK9@i!TpmUY>V*2ZZcVhNq&R}_o+Hl~4MxCIgu8e%`06Z+VaikHg$m5$Ar{W53 zQyhE4w0kzTLH7-;L8l`^0XTmUqZjHmjHg^l5H2IpC+QjNZjdTBIiOr zEQ>hsDcy?-mCy~tC^|)Ee{_Ula)t%H4^1qUn`r`JvqlPU@^9U=9A1(H4RN_j{8WG( z7I>h8(@q3#y9NQq0KxwzIqHi9#1Wg+ zMGF1_H>UYkK|%ot|O)oiO(`W|Lk6|^}_yEe3?=GO8R2h7C?hKt5WsmV*s{) zi0xN;|J=cs_&3XwJx-CMeff2tz2ZN)zOgI3-OD|M3paW6o~$8ikBt=<{`EbMvD}a_PlXRnz91uu==rakRlU z)u-ZH8-D16cWT7vtP`uiE`oFwcdK$jMmzrakC-o#!CdyRoUWW|2z2qL)V4E+RKI?r zc|JTX%7GNQs5sf^DSJ+pfI09`($z@7)811>OXr7+v zTk7fy3=n;uN9mrP#+TVD?5@g+koa)Ei@GeTWt~JZ-XWzjqkf�hBn@IUR##Rp2mx zk65GpX>Z@h)=v%`d=qq(=O&%$ew8HXqEeL~z(=Y-Q^-}w^GYVz4TxaKMnL;&Slx0s%y6l{j;J;`B5;%eMWIEJ%8(C5H{c<7}2 zaUn&`$6JpN_OTS+2)`|RMsazZS6#4(uk~z|k4R=gx0Zj@fTPIMU$mBm?!-^HFtVhZ z5go$(!nHTV_AgPmp$yW>A#GY)uUnJh5ZS4%;(&*h@)0DWJb3KEr1#OYz4qyyBO&gj zpEti4PF_b&d_-+jaXAl3@`AMs_4LK~^9;wJyXt8jiw>-QEJ`1a_m%8+ zd1OjcF4+U=pxK7RprH{Ouxm9v+>N$x+Wu$Z_~&5^QEh{GsSEd$@1(Kmpv{WRQQKe- zI+a}))J7%5pM$|{GG9Z4S1L@lZ6zxGaPmE-i@V6cQ%g`HYG=D-dkTRc>&C~0gZ8Nu zUd#8c$!pTjgLq`uKzdWseJPW>qwPY*r%z? zrY7V}9Eq>k7s9HiBAUyNYJElWR3WsZ!vht7GtlpX_%h=^L7@R?IK)JuJc^ukuA^xY z>aNU}udB869<%!7A`U|zExKLwxXIW^fHAutNe}uL5BXBRTi;&p@ymW*N4WQs`OOPy zvKnnK)OOhPia-TERyH`yeqs0bgsLRH2V{ZDHJ1?szE7d*yOe2hNdRIZ0~-svrqvo_ zFQxvi9Q@@8nZ>Dv6J4}5+ArtKI~kWcdUf4~(i1+G3aj3BoKy>>U+QtFJ{3~Db~_m% z61%y(1w1tRD>>{I2sVFT800QbKt-Mg3_49mX;f`q{SqD?dDuaULPN8U@}1c=D0!<7 z*LryHx>~Ov?84X*`PEesSt3$qyD+ndOIei1C0p3nXZ?E-i@(8Yr`T=*Kga}5uB;h8Qcl!^0xW8zu!`dgT_hWJ#*q5Mg z_(`kV8beLv{_2k`xN%TQA6c4qYP#V07p=O3u8ixBl1~V=G zCq6jentLH>3M8j=qoQab^Z~#%|6@Yu5+AZwvIl1hQK`Q?SXMjV>aFXK;)X$X=WqLS zX)4`tj$$H?^2-?*hB=^eKs3njx0OIl~8u+Fw*Ebb6B85=lM; zVMw75p<3c_HZQ*i7aL;2pCbV7Tocmkt4ph|eNI7uMEY2R6Anz>wf)W#HOzyOGv}4( zUPV~uT2h%Ym{9tCZZ0Tws5#Izlsu!q>wAz%m6!V<06pU* zAOF07H5%PJcrvI-T=qTx?d+lM1G15=AliUTQL4EDJ;G&O7Ut)de^5gADvim*n}-f{ zYNVHkx{5Fr{f$SygRb|;8V{GO#pKX9`Rg*@gBo6i$jgv433{}Cb7Qgu1YpnNyfzIS zFpCA~{M>w~=T;8xQ7!Qc6>iznTE<0>Xgd*#AN20rTBN|_T*ysfqn$CSls9M8VKl36IFURPU2 zob0B^ce$3EGE!|0FHSem!DjpAt4?I@~mrCYcTNSvu+BA_J|l z3DZK7434cBTT0h5xTkQ5T;OG)xjs+5`r2hQBbR3OGDu9{SugcwzBk1^EHMYX$bUQJ zZiGzxWh4+^XQm_03m0>s1pgt)!JX%PS;@Ox;da63G3OlLCT#ZjDrxUW39FOz;6ru; zOqSU(vTe-AyGCUYYb`q(9QzNCX}e>1Ce5sWA&5@byLhG^WadxKoC|8?T`;1Cw0od& zz>Pii+Uf^iAQF^>Mvjj1^3Le1uATR(n-q9(IpR4;eo`tG&DZo&`qX>Z`s2iCWuW&$N5UD%R29F-OfHk(ZzRL zMub>+4R)oepBfilb~$zoHEWO;l50M=b0^hdpN`?G^rjKO3VBs6;ZCMw(|EK!-1z#&vf!s)nH3V??alu5o0vMVfOMY&2LAuMMP*nYLW#JGyUZa7EqQrAvrqdX|Dzt?09d%$Pw@^lO z{d+7|{9^B&b%A6FT9$VLYEurO-#`#9`+W2d&%X$RA2`{5g}Zb4^*K*fPkZfPOytYH z)qZ5}g{h9=1!m|m?*yUheV)Gpy&vRcCs$3Yne~{)BJTpj8pGw?rBZdrd6^AQYagMR z?xavX@W3%Ux!u$0#%QVZ)?6GN`XA~`UuhV|c(p_g%ZzpJNJjzd3urnKM6cw z&S_uru5`)Y_2kRPDQrbvl3nLrw@`;L#S)EN6wgRTk190ymM}>J06xzHPdd&v0E$5U z(x;z_b4}$MUnCJZ|I)tBSr5~~I$16G@qVWrd_$P>?z#f=KpWwbXh?t)yc{@o+kS8SYz6&_3i^2!WUC*(JWkLy)4ALxQKz^D1aia1@NXJ!5uOBC zh|MV|FT^$$${jq|B}rLf?^1@g)xSY+_z(eoInEyQZeSo8Q-tcPwMm z-`p!b74|Mf=JXYHi)A)H25QjN3EXlx9BdZlRQ(ei0DIo!QWq~ezRCaI9HH>x)?`gX zu}VwhCRt)|XM;LsPqO1ee$Gl9DT~w2y!%*~Lqk@wU~2z3<5ttsR%&z|G-A`TT*>al zV)BtqTlaaCT)o_Ok#QEbc%S5j$3g}lzxyn4$WxyiUJfxi`P)0*HvgIVg5*aU)6q*^ zX@#n8*-q{3v&grpT)YI4Y(Z~@W&p$l0I2MUU@0}Un?Jshu8ZSML;q1hOwfK!ye9}t-0eK zH@5-I>EQtm)Y;ee&&-=>$!-WpXa2LPUgP}z*2}S=?9@O?I1Jf^Tg&bvwAT1oZ=^b`e)4EZ~ZClG8hB-~e3Apy-%(L%4_YVG=ruAL{m;k(VM8y&k5oFvgq zDT@@OtT!ldhSdP}TxX;Fy$hf6u;XjrHnBuu(Nzw;5ar<2rsE3E-?Zw}U*BGn$k=Dm zagw>oi<=S0To1$0p5H0X-gg0`KU@)$MqJk9iiu{)%MLRN)RgRJM*ImcgPGcCr<{oS z_kvQbUH7%1V_Z9|7C(|d=>^un3{-v5hN;bAUvEsJ0qMHqrW+YeUS>}k_(Tl-=E4*N zZFBr}eM$Sb3QY^01RT+)zE4*KJ*QE$8weZ)d`+k_0g4Wz<3jiM&x{hmjCs!zrePn6 z!)3ou!l)8tIm1^`HtK?fTdBE-4`-R7FjDv-pd4ZOWftHq)gNuucjgy6)gMk_1+QAJfro>gjpJbX;%YU^b>7fGhV=bt3U~7PgCckVnLNHWJ?9IrHIL2;eJ0Ss@jtn)=tc>YcF^=JCH#6SZ7fML2 zpYkNZs(=S{Ijy%2(S_MgV|1#N){XWRn@NgajQgvtJDz*?&`Q?%iw@c>yLUI=EF}kW z13yR3U#|~#YE9oho@rPKtJabB#tMAU#$MA|FWAa%vx$W2vZs%u07Ap%xaMAyc#_)* zZXRRaEnHsB=n9D&4$dYTeu1;W;*2*gcgdrgyV_fD95g8D6E;_og{BYejd57IB^kCm zGrXgdoc8(7w#D>E(ys?fr)P7b)9fOfl(=dO_tGl0KgsZ9)PTX^Qw)0ypP+6mw zzXA2^t&=K!6PH87a0SB00!$N*ANRR=^0@cZ-UOdKhwT^uh{oKJJ_rOqLJ)k zMaN&<&AC(NYTX02mawA!s@QLwJo&fgDhNDR5yRofy{IM%D1Qv$g1c*`DD& zF#&!uTbG{Qz`Np}3I@vXpZYn`???pmVG!yng;`e96;!pgh*(gyONV@b-=yJ?umEm2 zN#voFx4`^Fzv-){wHJ@zl4)D+79txAqly0wgyx zP0RuQo4Lv$I{|=(D=%=}@v>o$T0fMAI|ek56zAXRVej!({iL$j5PLc{7MXiE6A|EG zwVsoH0`b7BmbF*`<_+Zg!Vu(|$L)4p>gxOHUgh3{wa;m zPV>OT%dWHWhiinR_$%|V!t@F@ImL5Ae&uo9snR)p^yDh^_BLU1d`zkdMJqp;L9=DT zXsySSY4WafupfW$d8CzTc6oxN>>>`BFl7~|WBo-?hSuyX%QyllP|mn!65#;gAia(I zHDc!^GP>st3`mYe9x_@-n`LnNVBr7bHr={&=FFn%3%FJEvSXRNMgU5Z!y^COC0@UR zjp#M{A>V@5P+T(7NAzrM0e`x+KAZL+!~exGV9)&$@b=3M@h5Ns;LBW$rIlg!5LWH{ zIEyDI)vk8nS`=!e&>P32PA+bWwXnVcv-hCj>_K@CbwK0WA0hy7`)c~2B7@`6@Yu7- z^CK~@oM60=x8VwXG#w*OPOe=^0q(|_6J7ygp zQ}E>-J!D0FXEr8bS`fh0OCd;{)Iqfho$T6-zf`MNnKStCf+!odoF%OEt;0yf&p%<7 z+^fUqld6F8%X{9++D4o1(HR^6j!qL?MOvXkKpgg zqKLz+n1A;PZ?RX32>kv0l&lx>CnkPz@fYk%$2+4er(U3h=Wplu6M2M}#T;moO_~6&Nch2=)$U7qERu2;~VKOqns+Ot)QkzVQBht4Tr-5q;IUuWavYm5{=eFPCy{(}d zr_*uR@PIKh-9)wxeuiojArbl?k?gH}HAl=^U{FRJ?4su^P5X|oeBOG>yBVtE^+V|& z_deJnZ+G;+xegr3pO(%6#Kk7La3uo`(=>5JAnMwq^Ga92)>91{+wxnRx_^&-Q$>V0 zZ(E2cudbF_UJK41&h$&o!ng?Z5kR0m>TaHYx86#kWBVQUL}LZi!@Lb5>q%~Hneq@3 z5@5{<%-AW6?&DOSx_(Nhs%Dd?UgT$>JpK?57Q5cLm^`Vw;I+nMI^l#Lv9-Hvm4dnh z?Uys(ir6d~J(N<|j(SbS5c6{?~D*Gjvkznq31KjQ;c7B|L7PH<@~iJUG%y1w;dM$QEd4V9wY% zk*s~5#*a%C?PL9n@ShOz!`ts}tGCrI5LffOiXn8vp?bw&?T53iZUp+8tNCXksYfN+ zBbtEhU@A;Brx5urs&&o$hjT{mDgt`7lPk5W*&*xszP8kB<^+N&|9b}7k$3!-KRv`- zs!Grzj9YiJ%zqs2R61k5nPVrhLADlngquc~ml!9obtqu=@2eq*cdpAxo71eDaw|c8 z4JRB3sKnb@B-hoil8u5!3bL!L)qh^8g-}QHIk_>`98VwdVxi$X+o0$49QW!&dJ>g= zD0#qX#1s*5L9o_isnn{_6|cxQDUm<2mC?6uk18ws(uzISKnbWtw9N0D7rO+1HO`QD zIdjadReW*xJ~@8A{G{%uhbe6h5-v}WNb`nv_aOZbpGcK}HyFH(>WqZ70mSKKIGGxq z)5_Io4~h?@fgifn@>pdEJ#V@qu~Ctcb_}rGT~5mbAsx48j3ZL!F;9F*-41RO3E6o9hkQ z36#>$|ExB+Wy1P~YJExrU`s;$i{K?i?Q=^PETGRo^OEZFZx+!}CeG`<2cK=B*_!rN zTI#$=$FvqdkG6h0IkK)MKt_QpvPs34O*Wa+ILpRy=m*`{ex3-^8zB>yC;)n)~ zIGaU*K|~kCNbc|5rs85gviYTCD(tmDg2shbxpmk%I|+_zH)H3+q%{)5S`0E(4F8Ft znfi$Dt8dIX(M+=4^oQEkDKIM}->WD@hs*|up~jp{sb)Uqv^H)Ueq;c}P`;kOWd9}p z7ThxQ>@r`}S|50I=>+!Fx_X$+b(aM0?!ef|#E57BAYx`ip5-@@7Eec!085PEpTB^H zi9d^U!&r6M3$Ph1c15Ru$#3*{PjgWA37nPvlWS65R@J}WKN2|^y!cM_i7URFy)6NU zojP`axD&1YZ!UY^G9r_ak@5gBz}K}r-FBejpIM8aD9Y!|8PRI&Ujfyb@_cU_8#?r! zQ?(O(Q^Us!)*g`*AE%u7i7h`ZOM77eXJ-0ZG za9+A53YzVAxBE`7kDX_OR;cxc{}MvJMEZFq$F+yp*R}1UpUW)z6_q5E&L~qP&6f2A zFYE{JE^Sqz-P{D$pCh>RT#COqLRDG)9*pVOvD-a<6GGN-<(}P6o-R1Z{rvKc^Wwbi zutx2}rBAbl@fmR>_QFWNRSR(D^j$rSZGz`gK!oK7j!CmYf3_!X+RWQ)m%eUyt}UEu z@Vnx_I5MUn=1v1x$#o7Pbbz<+eH*6?Ix|Z*ytw@<03d6UHCJS?l-NPLB?P50wkm!(Q2P6!2$7wNGVCa;aX?Q|O_MjgRCi`h$G;mEZfk6@ThC zH(qj@%v^LG>(uGGV8)Ft3&q0CLXJ2sVhh=~lAQ%g5ckGFtgVrH8+Y98)|baZ%-*$0 zzc68+9I9DYsSJP6oCBsT8@77Tcb^Nc99)scD!lM>YInQhSJ?$K3TGxgA z6dj#P)3|+0FiG477KHK^1!hP)4IV80uMYb>aJhhntBt++jA>#zC0=dt`DtC4oZkI>q-Ibd2d|>qsfza?cHPMn3(>DJ5|1z4 z@~PkTr=JZpoifTN6<5Q#rNsvLWh#$ndRu7V`(dy!x;HjTD4MNLHi^_~x``q>Ag{vg z@6V?CSN6x_>9k1^0r!Pl;oEJd%fE+@cZD*YtdceV-ZXG3M)GahUigsshvjvT3 z#Y)a0*?X)PeY4bWf4(_sl(Sit98$h6^Egd=2!}{std3)Tm*ak&kl~NS>Q-}Ur!z; z3AG=LyWOsK?Mkd^D!aQDeCFd&n$lABTn3Im6PN8R4C>^>T1Qg-%Ni9CGCA=lJ+W;C zswdQdb{t*?jdN)g$;PzeeC0j{{?%%4;w-o5jBO{J8Q^rvh?qb%0Gzt6MsvGKeZfza z?kHr7_BXGdM87Xh?IN+^tQdKWR?zpC&>^;G&jNaR;9M?2T&oPP3pckCebmH@rHTmB zQ?EhvF4qsG)=ovXUxZR{s-Q>Y|3x8zkTMbf9si*acu33hvrJcfZi`+;XRpbwr^Y59xvbVSY=JSMZd%?`DGjaZA8sUTy_zd;5 zXmgS&%}{JRVHv#qC&vb#d`aVhUxq z9K$5bDLlmgR^F%#-XB+2$?SVv)DcHFVV55L1*NrXoM9iL6wPV2&yGix{rxX$qAkd5 ziNVVmrq!M6vt#Z20U`k#HNCcxaIaEPNZ3zEb)ftKZUOy z&&hsckt#?(r?Z8tmPj^>XyDbO`DVsRsZMy(I){qm2Z%RH2*KMSlbx4GrZ8N7*)9s- zL8-`|lvS3How0&H0DsTvust!v8X4S%qPv%v&VAZR}kB@Eh%Z zAPd>d6e((&-Qy|6qtk9%`NAo>%9WhAdzs=M&+HGkk$87 z6kfE*uVeIsOyX9qDX&g75fj#`^Z6=TKqY85?*tFiT64vf`c`0ha7N&7ZimV;4TvrN zbuR>yT4r18cb_&zC#~%YPL~%2qgJ_|V=7&DW3HaRzawgVBEq3Ob;D|68sOo4rm3j= z`npy9x|orL7qGY_S#Sg8AU^VEymAz`=F-FNQ?d1_L-k**bcH48keKY=OZc1HIc0~R zyCUVDcPgTqeRBEG$y(=D8|n&Civ^b-xLZ((&M`ljJ_{4n_I2nH1#}3j&iy%-vRNTs z^WK(txI6`4wcPRnWPYYg&eA^fg8kVCUd}715|2>kh74Qu&mjXaFHwEEDWO`h-lh0M z($ruHZ7GrNJA8y$1n)P~QnQ_YeOvl81LbSD650U9Ow>M%Z7k7gtzVd0dw;h|>h6Cq zsgFq(fWa;H@+DAXR^z2nLtzrS6P@s%)WrHltJ1y*E57zTM6n)I5_RvQY#(J$bs+)< zy~`I7FE6YEqCDRHp{PHpnhNCdCN#$uC@Q@Y(Ium!7FtS?pSr*i=`~wtmJ55CciuC= z1bgOsUXpU4Fxw$IT~o_GGq9Fa zpFjw|co=X7?#0?uNik zrh|~P$BoWv$pFdnn%rmOYD2Zb>T`$b`%DD`{pw;=GU!zaXIk`KR)KmG;e}fz|u?;VHlNO?_Tz`gKCX>y+I^{}9b=ox3GcCyaxaG~80AK*uiDU-Xj4qC zR(bUm&MUwotL34o!EdWp*S2tFLFGM5!OTHPJERygc|+IQ7~v+);q)dHVx*!r$c~pa zu&@sDmUnoR%gDKwetcZ=Rplq8-6x->59f8?!$;Nv9}BTIA+h!hn18Rj~~V&{h}YKNz_et z%cg`fsUzQ093}#Wvht9E^eOmhi%s87-=oaCYwjO4w;djPX{PQxk= zfcPwXiABFeIRi*OsEaM1iT!ezy|6ZF82$x&8XL6f6v9}i#A-Oi9_j)Opzplb5wBP< zWsSCm-2>6&zPDADmz*|DOsa1if4>$Dv8lq%>xAV-+E#OTrlo7mGnIPGUi%dq zb))_3UsE(YMFM6LJm^!8kLYKMyQa=so)AGK+(dPH1-r}%K(dxPY>2gHi18{!^G6*X zC0?LAjr~wj-MV8x@61_Mr5`-018c%A4s3GLFL0aeL5q94gf04RQx|V_EapfmvR{L~ zdDhxBJp6Qr8n!Cq2aWU=m*I&-0+XyIv#&%@nTR8z4`2&7tIdG82G*(5TW^Q4)R5y{ zcZAG2`Aqt64O-->R#Q#>6JRc)9maS{@=z7df95Q$Yz&rEdOMNeFQHeQJV&?X< zEWISrNBPCwVM5=_nlc4bgj`&+hCd~UU|S}g8+HA{>(hdRKl6=wAznjIST?)i`7br8 zLg_Rz5^4PQmt|%`8ar=M{R3MK=b#IRJ;6Ae6DrF%00=V|-m{C{_z!8qRM%j^S<*we z;o=JcMT1r2vM?5nK+&=>VE1M1Yt4AlogbZ6$(aK>j!d#!9NbI*YoA~2vv=oh>~bvE zgGrVKRRodHdD@P^VXkicn{;#e6svI?hVEwvpKDmf32*x-r#rbwF>dh^6gI{M5(JIW zR;d;I;&j%VBKmB$fDFr?KF!8rkHgxKjL=clBFy%`CJ31`BWq3?>ejnG20vkVrX7_&fs^|)peQBjtD3C||8FWrQp;z@e8CjjCuunt8o010#<38j6_ z8y(?-fGXqQNw%%naXdQ)y|!MgGQEh10l2$&FoR1i&l`OF3N6hZGZpw-c038cpaTt< z{jf*_rtnF4rYmp&_=4}Ei0LW%CcI!+KleD}pt>tWwe<*;j$x*hYNl_vggdEiSiEjW zQi*23rC+_k#K(?L@GM#r+D?+^?OLEi&ECDw^yj!`_xL2iy>9NR_nw9s8B_Zb2q~6n z7|A=VY{ICP*DREd|4d>LVch*U+QI&+dX(M}rfIfoMk*k9Dn48L%i0qem=QjP12b!^ zh_7<8N(umA%Ax~^S1PzSsg-S&;7eoc4#Z)W9F#NJp61`LM|WL6>8_qS9Qq?rz|k0fF1>uv+*xT6|8#aSnYWD~`~xa8iO$;#%Y;Z>p0uVd- z)y)}K)fF;PM*U9F9kgZlW=*fcm$yK`UBhoPR3@N9_VTvhwHQ{JDNKlTiKIW3bRIo+ z>aS*LLPNIN7jX>0kYI$fEvLVRBT0^k+CiEL9Tk9^W(?`G-b0W-dwl1k)Q0yFpVSuz zN-{x!hQ6|L)7OTe{-av9iS}i7VeYq1iwj(~nt*Ry`xo91c;h2oUeoyyE$dpiLi}f^ zyepD!bcG8t{Plqz44fGD^7v{66-ET|d4hN`tyd#)-6CyrCK)~e2H_G*inD~(Y268% zANnXOL=*afwf%a9&L{N?H=!A-5MDfif6h+?o{ju!g`Bo@ULD{`wssW3cv3NV;wx@G zk-EUx7L8no3m!%6UGx9=iEBjAAhCVFv-dfjM!<>Q)j&SZMpZ3me6bHR4Guh7(#|c; zpQkj4kab>@&;*6mRbB?{HnDyps~*LYK@^}Ain4uQCW}a`j9+FL_u7YfJ@)-N8QiBj z^U;;kCp$5OR^68pZa;(u-%NCRXt(0itcPEqCcO0d>|8SPu-k?Im+b_6A0Bt^gdL{T z2^-a9pdkIfe8zwkp#wk>%Cga#?A^7X&O`{<8pfE5g({x@0WsQL+BKZ~g1&tg>#KA_ zJXlF6ZiCxL;|p*OHNuIh?dR^@d0#7VrVfU6O0(2I1w~B?>i+Qu4WCwa4=02CRmPTBtAy&7rp zhf_@c{pw{r@+aN)EQe>^1~?kfS$sedIp=fQ5qvB(gu`GhflvjS);0DK0po8hUjnd$ ztOGG!vF`Q88^>)^jL!MTjv9^{7Gu&JUOJ*?ebW!g{mM!ps)PNhO*Zb2e=U_Dd;So( zY&mqo>nwI~uCXEI^1I1u2BMW}xRJagI?^1seld`&txjsg%9Ief3W znGZ6!Tz+;UqndtfMPN*^3Tf%qy*GO9cgSiqq=PIV*8TjVj~x257vd2y0fmjd;*Jj7^^abS)gVqVSSs_>)$=ZfQl`OzAnEEUft*s zjcPkjr9T|E3x==f5vqNk9$`|6ag!NU9GUD8$DFRW{gQGgxfB0h-$EU2v3eBUo7q*B zpik80YOVU6nypd|A^JGIVekIv4kG;|0W;{`U%C;~&f%Nr6F9QjD0*y@SU4;dQ=;PV z0Z{;}=QSI>b_GZexLujlzi%2U9JE8Gh10ynM0YRW8+l%kuQ1aWE;qEOM)%kgD)w5- zOovpO4@s6KGd9fRuIA_du@ktJlYW>rv){+>xUJEn4B__Un1C2O7O`$*Ti%nTq+|V^ zSu6F(^;PCp8#AMKnQsmx?q{E#E0NB;mxpxC4K6+t?`^!d63~Vjjk2#P)=xj!sORlHw1aQ!bY zn8AW9;NzLgGtES9y#4=I){OW+`0!sPkk7pVP~s4b@L#?M1U0ZjSOUv`RDlh01}XKy z&B4ye#mT|L3_%fe%yjIWclXCT3;&e`VP@fApreQ6f^cv_uHN%b=x$9_UHcPc^QsQT zMD21DS4kEhvo}`Fgu*!@7PD!qXqnrS(&VORTsOf$4ttLe(T0I_YeQ=O>9ESZ%>tVM z+jUcHOv~&dT?tD<6u2Hx?hNPPnH>2OoO93z4|h`CBSeF{ofP7t(?w$Q!66d_N5r2= zU63|1^0WU7+dVU<)amNKe6G_<$KsUo#hza0-+gjxuGgmHY!kgX0UsY<3y!xWXa34< zNOYPS*uD$spEi=_U`t{3y|ND4T^H)F5EQSP0rs56Gwt(B^|a0ViNXH>$r9c1*t4WKvMh0W5|RCG+hop*vYjr<$9%M3ciJnRfsUtc@aHP_{h}|CB9VJVh57zeQb4qsA z==)88H2M0cZw#EXKnxK4Gyj~^P$pT(8wVrk`Z<&gQ-4x9<{O2*?r5!z{xG+QV4Quf zRhakdiADL^NY?lIx0EMENZJN?=sdhJ@eUajKS?pg<}x%LHx|o%MTdYAutM;0R!6b* zcJuXD?c^r)NFuG>0v{)^%Bmc(Y=fl*UuCa=0!%u$Zj9yV8Q5i*WW2JYM)oJsUH5ZU49vF|@4Y_lkmrTK3_X!36ym zxq0&UjgMWi|MDAX>e@A8v{xBU0o>p@!K&-~KYwNqQzxMK(Lmqvha&^@ZPyO|6_vBk z-n8V(n*SHWrqM)d_1zP;48J33N=uXUp7xCAxmm{dyX{ExIihcz`m*$< zIc9B#P=z_w$jDK>PYJVik$9| zSx30!C?V`#l==*~#^;o2cu!Ux3E(FMk=Ja_OW#$HDZk_l7_ctHzFVH z!?FT~ z1vw5gRKV&Z#svZO&CdyD!Jo{{VLw>prH3EehX1O4fC$27t+)fMA)l-fRQi3ASI=bC7O<^Uq{3f>c}^Pc)50 zJx~u%YL+Lkj`ImUG8R_|ta3b##C>e)4lr>a_y?5;G7YJLgI2SK(RJcz>SEQsN_nuK z_OB%%$v+)*zS+q&nz<;LrrVbW|-LxXZS9oKIC2GgbYTT$4jY%E_EM0HU;4J6P@ zM2}1j@wJ>fR&zBRP8keda~de~2HA9qBK~@(l-AdD`K;avD3bP~5e-jgGp08$&dIRar3G8yPfFemOG^3Slb2nb z|Hkr7J1+u&c}-vk_skzZ=;s@0Itve=>Z`dl!;V3%tP$CFBSkR}F?jz!glAY3 zy|)VQeauzcQ$%#5*%N3a2&A9-ii}Y+gM*O(Me^S{>CU*AqIQKYNdnna>(=XMrM}L% z(k;LGBCOI~LMEBkUV<-RGB8R6v2{IFg=jI68l|2gCdRl*WT%A5K z{lM&iIXwK@9ZPi0vvYy4tflr~Gi*&Tur-9$=0QBr)jk(AGhx2<&|1sacq5P;aQLaQ z%afbxTTm()+7zRFi(l3(u6Ut&h&S9Zj;gsW+gr8>OTMfgb z_;DX6sx}#sP`Zu#Ufo+tBnzep%eu&unUDDM%%fy1P~6E_mPAjL5C&vx9$(-`bi?LVMv-6(6&Wa9;N!6k5)O78CtuM)jh}emOJdqHPs@0Q>rdqt=%X?)v zzb=BZMf=uF5I@&@|Mi5cD^6dOVC&7*a>|Y3&!yt+fBDY0iW1jkG8cn0RGq`aA=SM7 zlnty8Rg{pc6{xP=Slq9sH=$N@aV9Q9ZWL>D@J;egH0`U?SNiUPirZ0}$$GpUO zJ_^Yf(hZh|3ZlyBu`gb8Nwex}ZDxV0nFK{_z37}|H_@u09>BDVZBbYlV}C=Z!)MJJ zOG|ucfPQFnoV%i|=L~Xf1nDFVfn^Y6tnkaeUt#GF#;XJvKYufAMX6e=>SeAXcSYp0 zROEwI-%?kI)qU{l>%Aeqt7`r$YeeDG6UT_^`N!S@-f61$kH{=5%L_*yseU&J@^*>! zBUVXZ1moo)0V_uXP)mjpCDu0SIx0tzestZr&0fIFGu^804H&&GUuiH`g>PfaI5vi) zG@kwsK1tW0cT&(z$dc>rrs$Pkv!!P?k8rOp8{03#7VNP2(9@7Ie1vv$nDWG8A@ zwH-i$%sPfF1_Rqw=~77QLq1%A(m6O~=#3TXMJxvO6Av~CFVy0XU?eV`am9}>?EkLe z!WnM@m<8wW$c#qo>Vkl2C9zg!G_>-{rK8_TVN4=^!W+~U>mAMI97Mh9Da!628#mbd zw1imZZ%}(Ikf?f}J@FYmpJAf=f&oyX9bbxXmEjPOc1$MBxG@k%Ua=bsT_xXP20IC) zU}RNixG@1)V1y}nh44zeGmSAsgU^aU0)(00BJ0SV{9MYPyMPoIzW_cK`^3>;RhY6a zL*9CkB>7JX4%N21K&;9c@2Ay{^gm4Ni~URTjmTOg>42g)0QATTn(~>l6k^++|G!Ms z2`gnI)WHrNX4X}lwJ|EqqJtx4YW~-4!qTRb@K2T%TlPV zKCQy*rr4IABj6{P3_68T#ZAidR2YaLh(EhRE777>Y0XzyjeA-sIIOiu97hownYhR& zq?4AZO>J5PusHvOfn|+X4h#2ejC0inaOHkd%ct4jp?_b6`bA@xV!iyZeiN1g{4n}r zaqVV6y2k+h2;t*j9F$+l1cBP$>!&ylFy&HT$gzpCA`m1L?RZF(sWbpxcOtGcR%*XOSefJV&5Sh(VZm z0E~MWT?j74u+o_5Mi}AD(X0)Ou>v&7H`APjY7`($I63r^i&0q-%u?dCXUI+Zw@6n{ z*}j8gi8@ui7Exxt`l%8|3tkY0Zx{fxv=StibEL$&Y1_u52;l+$rY^)_RQNcFYrS6QVfbMYbt-=;mC!7U<&x9akyW|S@BNSC zwd4Hq(xOF+#)Qq!E;^;e{1srA)-hqc(HCE&#KY%RgmAH|=xztu;EtIt&D0qahthmz z9~HNaa;QOL#iCaqE@4Ud|5w@L_y3UyNJU-%ogdvh7X&*%DvENl@cd^-!3J@nfV8ZO zOSIJVwA8dT5EgN^v;j#H*<9aVU}U7F=Ym*nvT{`EjpQ^fU|_?lBik9Fh$SM%aq#nd z;Z5w<1f4mFO+wqa`p;C;+n9R3-JZrg22-jGT7i-1byH10RzKXKIZ1V7NgGh?Gid!3 z7^)j?ySFCsn_Pr6xrVTEj>T(~y){*#AuInOLi>4|U-hCh7eZ*NYKd_9mC^sQ)Spu1 ziw4Xuerx_i?~nwUSDYu}a@L<=IKc-)HCauEB^xZREKp7^MB!3$=STC4+KPYA)u~A4 z7k=M#A7l>5$@M_h!_3c~$kdF``vkecI?qYlZ7Q+(WfDxE5iZkOt|-OyG5 zhxxuqV>tyb`m~Ip!7L_OiZ6gQyAZ?;T^iQvn z`bk<=FQ?s7P-enm1ma9Ef-KuZJ&oi5dkNlQh{D7A9{}G#Aiu2M?@28cHMfqfkhYNgu0$Zaq&zdU8L}67*$YKdL7v3$isCM@8 zEd=(uJaqRNDDN37WcW{yE<<;mZiNZpm$HfEmC34779`Li3I<~X9Szt~rv9>uo6Tyu zZm1?U?hf;PCcrCHFmVtWVKDS+!3l=Uut+l98nQ918Wah}$u3Rhu_{roBzt~epX z;2jdC3e}EwTmjr}kqbA{zr7jqg5-IAR3fNk>ur|$Xt^!cxzcc1%GYW|Hg~@k44Xp+ zhhauj>3&DlD%sI7xDL ztj(z=*hG0O3)Euqf@b~7oiTO`jObO50my{YnKbt`Aa}J*Q#~e=>xI&3`KcHHzNR%i zzam(;TKLD+09<0J8xX%vDAMi#8G^kD{O`PCRO7_CYF#(1p0=tRj_7*L=o{1D$?4AB zKHnnH1Nyj(_{jbgF2Apw$9`V>^;!Ey4-7g@z;u#~0Y!{aYYI$+tG;dE~QmoQLTfAB4xB!uN2%7{K&EPcD19A z>{3#h)AFz5h=f(;#&9muEYa3ea1#ARnL=_cHnqrPo-_-H#)5N)f^ibN0Q=^q; zliS+J}VC($Z0=4PuQ1^j?T>4a=fVdgws!Bk8SW<8+mhOD4yU zTG94gg$|$G1df>qxoiJi65bIb90mHcZCc?j##l^eoU}{dTML!~(7Gv))AVOp*MscC z2GfO&QcUv3w0=zL-y9R>JK8}Mm1|tmh2(nW2!T&mA*@3+hkIb;-1)txs}WUNP6*c6 za`y68xpkOOz%0ZHnVJJS^f#QFReQrOsPU$E0Rz;U4endmZN+q_mbs=vE!4c!Ga zwQC(~1KX(p^!da?r@Os&2+2fsz(}|q>jn~SQ+|}nuX5p8-VxPgj|j` ze`=5L?wmOQo#9YUi+jz*Xpg}ko69sWzsg+{#2r|MMPon!{Qam4hq!4Ek)L4-L^NVI zH&Rj>#Qa|SD)+gt!faH3|!tms_L)W`_8glpm_EA#6mN{QX~OaR>Z zbf+-xM{P*$)Fogfq_$QS zw?^kly!hB|`bM|=?~i?Al|Oi~$t(55bWZ2mscctqJpuz1^7O=rG4ur5Oe#U?m15&2 zQ#tyicDAp>A*BlzThGLJ(v6+7 zd7nHj|6 zTSg7Tc!1pyfwUdR5>ioEtrgYWZ@yg$6SC4XG#Om=F@I*idwyv&#|hKBsWEksn~*Qj z-|7ZGhxTexO=#Pne4A)n&*Yj;$L5#qsY!X!L~0%~PMf!Q3tC0*kC(DVp-I!31U7{F zo{en|weyM-VGPc!)}1QYuWn7K{nb>W^+$=0vtX6|@`mCe!~JNPf|1rWjRT<8TGk!6 zOAENm7-3QP381=J2MxEL9NEJMJe4YE0xsM1x4eRu_y)9*xmzHT;a3T}Zwym*yM2=q ztTuaiCE{mozz2X0-dKzYCd&vPmQpRdyLvWO3*~UCYLpY%8cPbs?ccLf?zv?-$@P4h zKkW|no7eTpUr%Poh51W%OLg$fyLFfsthS^9 zcu}%dLKupt-dJtBPiyk}P@8%Be#yKa8o&SkFt+wQZOPGQV5dk1vM89{j4K1vYnjr> zncURb_otcn%=`0$Y(uq(RSe%WiSR=th0Oc&?m#*bCJ4`UU%?+BTWDr~%wAe-08u#)$_28xFduBmn2<6v+TElq~2ovMBbd@Rquokq^9d%Q^`+<#Xvy z0*66NYA|v_#}?GSlD7>fPvcL<$yJmnrRTY@)^qba7kYJttg}7if2}mfTvrM4#Wst& zwc}gvWB@7VPITD5*Ez6>93+Lv^)qPej?~BvJQNf6h3boXGKSWzl9R6UrNF-c09+36 zWfzzsZv6h*|054K8}6>7l)4P~2vpz2nUrYSy&BRTcWmmVWh(*7>)mzgx$wcXDw(^P zJOKWQb>PTpQ@=j~`-eVwH4**;0GNQV4Ol8buVx(}2|!G;nPdRil@)pqD=mc%Y@uf5 zM1+AuEHA{DX6tu?M3EB5v>kSD62*02v+ml9-OE{GKhOSi3rU{|4{DU3QB7@MUWq>S zbli|2G|fD>t*PQ!>Swty;*8TNkgrv~eXxQO0FeER-nA>_EyNH|ZIn0el8GnYG=Qkh z{rg%Kd$vpO3)l|<+__K4GDliiG;`WYk#)LlEom~pH8P(48F-5gfXM@grFM*XPD^{G zxb3M)j@Pz%skqMhun4X6$5sNiiFJ5ypZRbFE|)W#wL-71>$9DX?@82ArIPSo#|nX} zX%H7NVWzig9I0K8_+5Lvk6An>*Q=LK8?V>0c-x~HLCh-FKDvZ)y(I7^4MbH;}=0i<4W zHC=>hR<_=sV&Vi59mEuQw(3f`)<89MN||ilm7zw4KRm7OktG1MYTKcvYRFfwLKkf2 z`a2*{XdsRWgvUKzymT@%A-OorG2ApaRAmfZ%wsfHB65yL(%zu~v}WN&7R_&*J=-g9 z{XPK5;_bi`r~QpI)4u6`&koz7+wyWO`LEY3wNAzPAi%yXK$fTjjArF9V>0@-rnyMo z%~f&sq=5z*1??>levp#cl{qN-WZJwNU8m<5jp3b(XJ+4;02Yy|MG4Ge-Db|gH&7Z& zsqr(>+5B47f@rg)M^D*08eLUQvd*f0RjL30001J z;xRD_0000IG5q#0F)}bQH}B@y+tk}ACMGE;Dl#%GDk>~0EHgJX~w3Y#c zHdkP>?Jh)C>&+A>jf(R3C5jn0inXQQFs3idP-uE~5R^a(jl zq=%NHQN+_dwgWU&vQ7YB%R1bIM8fUlL3DeRLK45XrSW?aC`@BjOYqS8Y7zYp;Rpdb zIK&0P8b8pDYNCDn^3v(3b0}uZ7Qi^R)%v9csa*(k0d{eM1q zo83XX+OCW~Nb`u0YxdH*3L9@KeN&>q4ioF3;aO-tjZOK$g{gCNIFvDLr1dDi(Sda)Z=2Pl^kS3bx@*H;v@m>la z?c6fk<(33UhTjI2S{qKx@LPG8dWMjLVk^OrE2hNCW+jxFGcWd-T?)O=ay&-EQQyPt z9@f`LLMOcDk(*BON6OT1|!dLcDFzR^Aa(7YnQ#X1y&n`akEnc}}${V`g#?ip7?c zj@bX*SG&2(R*BYgd&4D-;`g)H{CKI*RtQyTlZi%uytO;)o`U8$yI+TDSIQHLcqT5a zYigdPup*QKt5xowx4I|U?dT~m-&?zY_91xU$X0Vo$ihKx9EO>3LUwf~{e_2FbjDT# zzD5iMgg*Sh%+OumOn06JeKUY+ODb9>Sr87kKi`&@@BG-)yAlsCyWQXqG4+C>-PVJs z4h5&E&BI_9MV02AqyW%Fim@HS{`T#msDlzioOT$8`^sss1`ge$Kj$ed0W#1_DG7${ zL?;vT^9$$+o?)6K|OQF|WZZjWi=%tB$w~1oU>3kZhh&Gxz6LcrG z$5N){Im31s-dBVPCvpaLg6>RPm&pwW+G!l+)`k>YLpavYp45Ns>fBA2>|XOHFD~Yu z=X)sM)8{R9Od9J;x*SNzH3cUnyP&BJ#?~a2W$@tbqVlYy1EV2}kdM#e-I*GIJiG!& zL(6W%(|j7f@Y12@!+9&i?c4*lgT=J zbM|v}=fU;852@L#vGM6KMe}sEnqNmZ;hdzmXjajlAn;|1c~LPEo4#K%6Bj#BwXb8^ zV!DM%GlrK!2St;*1%3>2g4x|B=!eL%N9K6Bt-K=_3V}2TRP;F5}J49c?hu?x{{a(L%YPCp*^DZiY>`Gcc2DA~5 zDb7FFH|HO^8wY3e3zuJbc{axD>mX{TChyc~o!@Ah7Mfa~cXsU$z?tomS!sKZ#}3!I z?D<3e;3g%Yc za^NKEbqnQ)q3CsSlbXKNsnV@l>RhJ=AEo)Mn{TwLmz)*e1(zUEW-udu%$4*A0(7`& z-mjTfTkE^?zNyg^TKiEOzLsv$J~C}&-ktOM#6P~H-2uG)^ed1w-138S_62k*Hmn~T zh?knaBi4fzpYoo{?=eIzW+R_hgbBR)d<%f)O7mu%fRWtnTbzeZ#9fsmF;CUq3?rd? zS+|L7$)KVDjQvUmNZdProaq8C;WJvAIZR29JpJ}UR^}Nf6T-zR^w@!w7fx*DiXGn# zbiv0biUujTzJtwKzs^>z+0iP;JMRF^<6$eY`; zvO_Dwxa&?YZosQd*RO65w^kY0ZCn6^`AVB%fT#*3@c7CN;L71C_kSccNJm1+Yt#Hk z3l<=qo2Hf+1qf+k0mK2l92ngt@nrjz3DU)Ah~r{Oj#R}93yu@ppGS7S(1)dc>#DU* zuhxGutXC~T^?Uc4P^DMX1*7+JFVy88-Pm$Y6K9sR9<4bq2wyI79{WfyC_~`HBNAlB}~d zFBX9FQUfWWdcPtJOPyQJ8I9m`MPlOyiGkS7<0VEG0G#=D4@G9ewf&-a2HC8ph6`?c zI+11KUVpsL?QwSNM-Nq3q_A(9@Q3Z{0mSu63|rI^ozydWd~XAA9fvMoNRc7LwfEC| zR?#?qS_LUs(zFEljOqZ~@k+8;1mQ01sNp-G0&vOs^Tn`@yCXg9O4;J$nQQaGZpyj zXqFU|Bml(iN)h5AMHRG<=tHABqQTLxA3B}imRp-?KbF*#wc@zc3>TJ0O}|6+F^a)@ z03X50wHlIj0$lxe6T3ZfKK!1z|GGfMc#8PSVc>W{jZ)<%(S9-^5@~`miqOTVF4y%g^lDfWT^>Je3@`%!)}^$ z(tMlGs1xQb{MkQjY|L47s;uki2E6yPMQ7vSFoB{SR@UNS*p->|yYp-sTHDU;315HB zhBwO!!aRb1+CKBR%LL1&7lNLyUJJp*V8kyqrG>Ai(E; zJ6#EFBP|)WtbXC~7SeAGH<~Ffu$DE8EMQ-tAK}1)Lg8TUi?CLDw7T$wl^9oAx=1hm z>}$=&3RlJWWj#NtcMH=jYo}&lQpkH%3apeB5>`gsK)9q*kOxmXwND?@(QEdc>@fQk zU`2tk1{_pSh_tMj;vwqW^i%x|ZV&VJcVsp5-23mN(!4+TH!fQ~(X6?sK|;gjDQSewLY~3&w(J?}knRr1gpb`?R@ih|($h z5Rp-F??_qB@)F_4f9G-gP^LZ}UE(tQ>*qM#NzuBA8UmU8`f>j7hXB;^${5`xSELf| z`8x-YM!aSun~Bigwfz{mBRuMQr(UYAVex1;C^DseYXt7!B<`e50J=sTWG;3wdMUBB z0jKuW*NeBkp?=*K;uB1Iy>JmDsg=h}Kf1%)k4L?MKOgFBgo{__)gIb1c$CLvU2tu& zWrrUhGj*ZlG^en=d)i@08h42#J{FD_UU+r}W+&cr-y8W%-3&DFs`CVSKl=d`)ojcb zV^`V*?a`)Po@GWsYn@CRK?Z~cx!`XXw(3$7Y)nY9r#&9e=Nd6@{fxNGA;Z&43KFGr zfj9!)7^c44i`I9o;h17Pqr|L?QiCktFB-P)GJ82?X7%81`{luT_OlXR znVaYAXG59p)AWq8T--WrK(<^vatReUuS8Np$S>+$e=4w2I*gb^BGXfPAgICn zkz*rl&rX;pjZCj%Ycvik_n#70JvFIl=j4RbC27|wW2&uWXW`6II6KT4TFvPI33GyeEWxHsO3ko3-6hv^CsQo67Cc_{*C0q_lDmH{}m3 zc}AhmX7Qq4!UC-MbjG+JojM_Tr!pHvB%j7#$%ysxKH8 z0G$1b2R0{vi|cIwDOd|Q_SRMtb102^pqy|_a_4V`qZ_!w7D<^N0lfXZ7p4i}K#ZR| z61aqcGL#yxJe7RwGYQ}9r4Va(*x_95$N{|h2=N$pR=**)e*-WoR_6OPS4z2!jqHP} z`(~-?(KT7&+fku00gU;?>}fa1cO}MXU#P%H&Yd;ejlA3kB%nCk2S;jvAl=)~F*crEG zbl?kNy<&kZwwm5{ykaNw?FBxyF5A(aK2I$r6P^9mT4S3YJm?RQY&x_K)b%KQ~oPWyeO=*1+y|~NGmvx^1J)_ojwpWsyhJXL>qH1f4F`fX-0YO*xgMJha_38_x zWw_fgR)Kvx_NKtg*HppQI&~nv;*{S_^zViATBFw9i4_3k^?+eUQ6^;&rkO5a1kai< zO9E0RckfOGTt_YlTXwBs&A%Pp$quafu&X^h%(Y0`s{>rZ`0u$vLui7Vtelb^oEgV6 zoJnPu$h(^k2ebh}_pl+YIlJB`6A*PS3z`fL{6-I+`rvTc-L<0IZ13jEmoziLcQP%F zH@{rb0Z(UVQvd+KKLP*%0001J;xRD`0002s@g&47Gc(`bC@L^9H7YJNF(@c9GchPJ zH8U_UGTYk6%E#R$Cnhg9I4e0dOswgGpn@vXJ3QB^iLzt*Vf zef+xr;@T5CkppuA^S?FlH^SN)lS2TE0lm?|mZV%pU~8)wW4# zFKVBG`I*Q}UDG>s5ZR2rSZRIv@2jo9E?i(eXpy=go3eH0QI!u>onfIuX$)h}F+cp( z3YUV_9@=sC$u1E&nF%ZqouJ%AjVw#`2-L8N%q&6jYIHvlnneL*_Hv!T$QS_yX)#09$>ve(iSiuZ6jkx(-Bmpw3UA~s8x^*7{ee;R zJ`5Z)Q?sl+U|U`iBN-50Z-p^?F<;=bbTTZsmR%gN(zs%x0hUHI;}|-D6qptFyE{Jg zAVb|9>o-^mrfqCXC*`!v6!K+QeETvr(q66C4S{ia^r6rHymEHo@@UDLPr`+>s#Ckb znkmXq#5$143_TkgpXWMgOLZE&>)0a@u1cT`R2!~V{b+bNnI-_gYrH#BHf2yEzgU7i zhP*M^z!1a2F~vDcs1c)n-b1KtB13v(TuO$ATd&6YC3BScTArw3dTTG!SZncQRLkSg zd4$a(Vv!C^#EqsHvk;m6xO^AcD^!C>#1$}ET0oI;IU{zQ$#`VeuLZN6PGbs;paB46 z`A|iZp*0GA=4KQLT!|**7#f6DJ@7Jnu#D)q9YmuuiNWVM(o6t6{qz-dC6ws40{|W& zD+`dHEfCEr@})AX6qV$3Up1zOT-()p)eAaD3QYA1lQvFjEk|dl84TR{asQ~SFZVwB zXLy?@N%l!s>HoRh{x2!x)b+jg5B60K0W9%~S)wPDnPOU#$G*^=a&EsmCXINI`TWjj zAO9*)^m`p8cdQAw2+N!hn36i*ITVxux$$hL#3UV%8NS_zF!6>#ymM?kV7P5lt$K2_ zaf;7y&{#h3EdFAD<&Lq@;K)~=sSf~@`E;vRjIP_(j+{RO^ad{3$pvAE=47c-YBosc z>h;ujN&(s|uo1I*_FMB~uF@Z_9Lax^1RYR_g>Zmybt9esv+Omu#r z7tYOGufRJb3bnmaO1|U~(B)b$3kxa^9Z%w&UY1^K{BvbygYQQeAODW%Q06PwQHL}Yis|TQ?2Zw&Y6gfv_rxp{jArqZHX1so zX|}0SComF>3OP#yL>!uI>i0v?iH}?Ip9*zPhtehhoMz3t5h0x_YLDG|7qmkrWpClC zK^~CZ&vBf%nfwR%y|sT09cnkz!nnoE>=OCN;sCsAb?+YFR}VULU9|lgLZCwY_=(*V zrmx*?+5=VuX1(v8rR9>BBFGzfUd<+mISTwhL4VOzQIi>-W~*9V&evJt{xGpc zw=w|!qZel(i=V0(jBfd1+*O#*{A62IBEcc%Ncx5i*^dU;oW&4At#dj3X#qda)@?0M z+H$&HwI(fpA{zj#V$Gh}XKy>qVdrN7s-R~QZELnRReK?~<5;-NSBF4N8^40uipXc) z6k^}2q6Pxwa;dvwJrb|g@z!ktsHE9o9L%aJoLUcJDK@T!=L+QFX*sLQjgxGNW_>ZjpD!WclIrA)==QlZn7g?Yvz@N2yyXIasEckw|t!n~glmArWe9w?z{Hz zO$*1Ih@RBEfph7@^xNyi7P4Mr@82)XJJ;;qbEJgZ5v~dDA>iFnuI8;{jO1u{-8yyC z?gX?tstS!ka{N_w+qtg_05)sOxr-TNRpRwy;5N5fV}#CBUq#nztxs-25GlGV=UFE( zv+Y$g2XHg%Rj#4=HLczXX_Y%!|ee9gKXDZ`BVo5Ypsobeva!` zY}x-3yi;urSFtGIYMUHOh>R~kAOT*xo#RI{PcgHEmT+8ojC{?_Z6In5OeNw(XP5Sv z0>IH5vlUPcWx8wBCYkNKefS=L>%3l zZjt$A1gRe2ZgFnHg#ZtZ3fVIRN@6BWOZQU~igjD9|GFI?GVN+h)A&L=OItXn+g*62 z!mXvEmLc&P24dzH6|77~Obu`0U}*c-GSBa}^<9swtIL%OZE^4;3A$W0-d=4!+D%7H zS^Y?B{g_OfL~FVEd2v1kP*>(R(wzO;H0NcivvDE(SQH-6>{;eaMZ@1w-Bp#+-ebyZ z@yuEn?lw!%BPL%dI9dTUM1qZNq_g8uj#Yd+S{p6P@3xlm7-7E8DmWNrvvu6 zyo`IQ8KE~VY7sxz_$a<)ynRbx2UJbQ@?m1| z(6o`889#13K8zi)*5yUXA^>%k*#BpuC$+PCuzrc#WCAw(x|GKyZuSp`M5+X_-5HyA z=5_)0f-Vmf+5`af0c8f23+GOAKLIY`@3nU#ipoA`_Ew~iBIMUCJ3FfEc33H1G31VXPMVt=*(%2}ITPiZm386T2QFGd&Lf@3R0 ze4E5G5=5*{h0WQUYaFNpEam_uiGi&J6JdS(U`_D1?oK^5`<-xCUcH~ysGu9GJg@%b z)1a%W`Y{Gj=Fsb#J_j|;aC`tf?oc3OJgq^{Q8&SVPN3YZ?G!UvolAQ0Zx3-=zp&(| zMi@tnFA?kummm78c5g;2Y5={gH(>x2{t&_Ok*Teuy7L7f4To1-M4d1sc1JCR_dJJ} zX%JT}ZMcgDd0&UMECAf$&>}AON;N>y$PUmI^jcW}ef`aRA8W_86Uo?^P`(d8j~1ch zKkhgNFL+b1OXTGe@E!JN=r{n(=|CwpJ0%rvYX2udd2qxF4r#3?!TZErQM$0kB7Q+W85@cz2)*LfGg*4*=~SOyAaTm zzN3XMvzxIa9qU1Qjp6Y*>o?dZkh@iz5kA~KW%bff*PS3k ?S+HGjF$ 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 Iw*1KdA6#Cg!~g&Q literal 0 HcmV?d00001 diff --git a/amarok/src/data/Cool-Streams.xml b/amarok/src/data/Cool-Streams.xml new file mode 100644 index 00000000..51fd5072 --- /dev/null +++ b/amarok/src/data/Cool-Streams.xml @@ -0,0 +1,81 @@ + + + + http://www.ah.fm/192k.m3u + + + http://www.bassdrive.com/v2/streams/BassDrive.m3u + + + http://www.digitalgunfire.com/playlist.pls + + + http://di.fm/mp3/chillout.pls + + + http://di.fm/mp3/classictechno.pls + + + http://di.fm/mp3/trance.pls + + + http://dnbradio.com/hi.pls + + + http://www.shouted.fm/tunein/electro-dsl.m3u + + + http://streams.frequence3.net/hd-mp3.m3u + + + http://www.somafm.com/groovesalad.pls + + + http://somafm.com/dronezone.pls + + + http://somafm.com/tagstrance.pls + + + http://www.somafm.com/indiepop.pls + + + http://la.campus.ltu.se:8000/stream.ogg.m3u + + + http://www.sky.fm/mp3/classical.pls + + + http://stream.mth-house.de:8500/listen.pls + + + http://nectarine.sik.fi:8002/live.mp3.m3u + + + http://philosomatika.com/Philosomatika.pls + + + http://protonradio.com/proton.m3u + + + http://www.puredj.com/etc/pls/128K.pls + + + http://www.radioparadise.com/musiclinks/rp_128.m3u + + + http://www.raggakings.net/listen.m3u + + + http://somafm.com/secretagent.pls + + + http://sc.slayradio.org:8000/listen.pls + + + http://www.smgradio.com/core/audio/mp3/live.pls?service=vrbb + + + http://stream.xtcradio.com:8069/listen.pls + + diff --git a/amarok/src/data/Makefile.am b/amarok/src/data/Makefile.am new file mode 100644 index 00000000..afb5f56e --- /dev/null +++ b/amarok/src/data/Makefile.am @@ -0,0 +1,14 @@ +amarokdatadir = \ + $(kde_datadir)/amarok/data + +amarokdata_DATA = \ + Cool-Streams.xml \ + Amarok_1.4_Welcome.ogg \ + ball.png \ + dot.png \ + equalizer_presets.xml \ + firstrun.m3u \ + grid.png \ + wirl1.png \ + wirl2.png \ + magnatune_logo.png diff --git a/amarok/src/data/ball.png b/amarok/src/data/ball.png new file mode 100644 index 0000000000000000000000000000000000000000..2e406b213796a35425e243b7ba101219cb1eee05 GIT binary patch literal 1773 zcmVWFU8GbZ8({Xk{QrNlj4iWF>9@00vP>L_t(|+SON0h*Vb= z{_gMlt5Unws&3M~(2AQ3f;vVfWHBoj!Zf6z5nV)PGYm;Ky-BY`Tsey{&^SnTNeDP2 zI1>qhxadvB%wkqzcOwd8F^leyvd|V{r>pAzy)5p1uU=Khkc7Z61*hJ9_uco-ch3FJ z&jtR+Kk{Q2zA--^&%QKsx;fQ2W34+OrEEo!oFE_=DD_M!)#jYvecb8X-q`$V<&#hT z(f>sNoIihVvURw%Sg+ULO49TI=K`EFP)b241p$!f4$cAA8W?NP+vz=d{`~p-?RIH=$NqNkU}GUo6C_E3D2gB>2`MBv z=ir=!QVL2bkOMh#IOkx42aMKGQbKSJCIqG_dxtaD1aN*F1RDpiu&{8oIo14qmSsrO z3~A;;BuRoKjuFQ(q9{TnBS$wP+@eOj;AvnAb~4$cJ_XJE_=r9*-iBrAAA z2zkBPXnwG|w%QxvK&#bStkr6bDE3O?C`J^;h-4HLONcQJ6iV_C1=d;^X1w*mY6QjB zIT&M5tJNAWA9;BZz@^dyZ_Llft;2`^TC3O6EJ+b3DUu{c9HanIST2Igg%BfFvjS9b zJpkl|_0GXL2WK5z?tZm*&)&b>zklCw0GOG1>2#W=2c+=(DW!mrp7oN8T`p&wfl(G* zOpwFDS_j9NPfmm&2g=B>#==Mqr38c&NR#xy?Ck7m0H1LHXf~T?3fBq|t_7=uwc}ET zVX%-=uMCwvcS68e3vLX!_Bo`Gsi~-pLiD8E0A=@z@QjC>Ki*e!hg57~!BlpD` z&W7YHzRwtkwf00wLMfvbWfY9j(lSOVXyu+#I^63qraR|K<<<2=VW5nS%ta(RNs-!h%s)OvvuJ z@pnk^M@H9|0JKuiibU~40u0B_xse0|0Io95E6+y8{xOtN&q@GFskU~=u{Ay~ zn2-aUv;G}3T*o=TTLO68?cUbLc;%)jso@$#SH`AX>&2MSwH}cFmNzOq4+a=(@pb3x z+hrJjwYj-6J2U$vNs{3m!apU|xIeH}y2je#^fnx0ehjxo| z{eB<4UJp;7KK=96t5;V>s$sj`Uf#23&mSe{4K8>{N+H{kij~}zO=&e1yFB;qH3mi- zD5cO3%7@a&_V#x7(W6JpyZV9k_4VG#U!UxzSvt>xy9piE^TP7NdRrlFv1MB6VLw<} z-Y+fh^?K-byKgTqFaP^{JEAZD_2ufTCtp1nCGn}E(iPf$NgXP8&O#dtWBk7U80DQ# z=h~%9m;UsFP3hYD+9KnuNg8CqCl?o*2@5R^gyut1QtHcbCDx0!d@xW@s@V5Fy4~*I zS69~-$Mx-MN*_La==SZ~cPp!9`=yLdd6!q*9>S?~y;e|KK`9;D@ID3=!u5JP=yW>Q z?%lihhnqKVnxAwqxp?v7g~`du_v-apL&~_U<^EhiecMIIkQdIv6k(_owzvJkUEx8#ns+fB1a|=n+j4PRZmm P00000NkvXXu0mjfO|wth literal 0 HcmV?d00001 diff --git a/amarok/src/data/dot.png b/amarok/src/data/dot.png new file mode 100644 index 0000000000000000000000000000000000000000..11e3b58d1d7efde700b9db0856a04c1ddd20d6d2 GIT binary patch literal 890 zcmV-=1BLvFP) z000W>0fLJSS^xk52XskIMF-RZ4-GR547r#@0009SNkl_kzpdw5_`m&`~bG~g~~IOn>xdk*`twPt7gC&RAgau^+S$6+Cw8As>` zS_i7o!!R(j=r{nawboj7eM%|M>Wr=b3p~7V>}qS#r)Hgcu`v6Qw=Ar2Ccu94G_sx~|KzEXzU? zN|19-DFF>=DW$bWsrP-?w4^J5qTg<}+wJ!G`N?9QXH?=`rj%lg)P@i(0oxu$Rszd` z?#r@(zOL&$&(k!KK`F(1KaL}6P>+;SDFp=3_I-cUz$Ss#phK{(>(|#8Dyc*v1n)gz z=A0?`wrwI(O0mr^)^@biN~6Uf0}(+7WC4_HI3R>?9qiS~!*i%ms+B6H5^*BudCobr zQPdBwpkZD>pIkI$n^jG#wvL23hed_F|?r-h$&->sAfeBj|W9A?tk^YyR^*N4w z(%G-N7`>WO^4^CK*rt6DLI@$GlrSP(uVZ@4Kn+BL1)XlwF ztU_rqq$>bG7#} zN{LxZ-5n> + + + 0 + 0 + 0 + 0 + 0 + 0 + -40 + -40 + -40 + -50 + + + 0 + 0 + 20 + 30 + 30 + 30 + 20 + 0 + 0 + 0 + + + 50 + 35 + 10 + 0 + 0 + -30 + -40 + -40 + 0 + 0 + + + 70 + 70 + 70 + 40 + 20 + -20 + -45 + -50 + -55 + -55 + + + -50 + -50 + -50 + -25 + 15 + 55 + 80 + 80 + 80 + 85 + + + 35 + 30 + 0 + -40 + -25 + 10 + 45 + 55 + 60 + 60 + + + 25 + 50 + 25 + -20 + 0 + -30 + -40 + -40 + 0 + 0 + + + 50 + 50 + 30 + 30 + 0 + -25 + -25 + -25 + 0 + 0 + + + -25 + 0 + 20 + 25 + 30 + 30 + 20 + 15 + 15 + 10 + + + 35 + 35 + 0 + 0 + 0 + 0 + 0 + 0 + 35 + 35 + + + -10 + 25 + 35 + 40 + 25 + -5 + -15 + -15 + -10 + -10 + + + 0 + 0 + -5 + -30 + 0 + -35 + -35 + 0 + 0 + 0 + + + 40 + 25 + -30 + -40 + -20 + 20 + 45 + 55 + 55 + 55 + + + 25 + 10 + -5 + -15 + -5 + 20 + 45 + 50 + 55 + 60 + + + -15 + -25 + -25 + -5 + 20 + 30 + 45 + 50 + 55 + 50 + + + 20 + 20 + 10 + -5 + -25 + -30 + -20 + -5 + 15 + 45 + + + 40 + 30 + 0 + -30 + -25 + 0 + 40 + 50 + 50 + 45 + + + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + + diff --git a/amarok/src/data/firstrun.m3u b/amarok/src/data/firstrun.m3u new file mode 100644 index 00000000..a19f59c6 --- /dev/null +++ b/amarok/src/data/firstrun.m3u @@ -0,0 +1,2 @@ +#EXTM3U +Amarok_1.4_Welcome.ogg diff --git a/amarok/src/data/grid.png b/amarok/src/data/grid.png new file mode 100644 index 0000000000000000000000000000000000000000..3dbbc25ab38af4c66d1acc26e158163624479f02 GIT binary patch literal 320 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=Y)RhkE)4%caKYZ?lYt_f1s;*b z3=G`DAk4@xYmNj^kiEpy*OmPWlZb#QlWMGUB~VDV#5JPCIX^cyHLrxhxhOTUBsE2$ zJhLQ2AtWPJ!QIn0;C+f}9#G*DPZ!6Kid%1Q81gk62)JBSd=&R2!!D|5(?X9WS1wrA zX)CVEotHA<|FvzA72n?fp8BM=@Vm#SId%KzOXe1Tl>B>k%kO?2JD@>O5VoI3*7I2J zX_vJ7_rvz{Su<5N$UQLUfJrkr@Lyn-VYCHNHOz2Ph8K)g4ay7HK~z3MlmV#j$9_vj WW3$58JJwBBAQ?|rKbLh*2~7Z66JZqq literal 0 HcmV?d00001 diff --git a/amarok/src/data/magnatune_logo.png b/amarok/src/data/magnatune_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..f92003b34fb8d333a628dc241f229e62f555ba12 GIT binary patch literal 4193 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 literal 0 HcmV?d00001 diff --git a/amarok/src/data/wirl1.png b/amarok/src/data/wirl1.png new file mode 100644 index 0000000000000000000000000000000000000000..e6af564d5f33ae75b05ce2454e341a0eaad0b551 GIT binary patch literal 46390 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͝^^y1ICT5Zu)F^w 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 a!2b`rAyuQNAg81N0000Pzp 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 literal 0 HcmV?d00001 diff --git a/amarok/src/database_refactor/README b/amarok/src/database_refactor/README new file mode 100644 index 00000000..4bd89d81 --- /dev/null +++ b/amarok/src/database_refactor/README @@ -0,0 +1,9 @@ + DATABASE +========== + +This folder contains a draft of the planned plugin-based database redesign. If you are +interested in helping, please contact our mailing list amarok-devel@lists.sf.net + + +WORK IN PROGRESS + diff --git a/amarok/src/database_refactor/_Makefile.am b/amarok/src/database_refactor/_Makefile.am new file mode 100644 index 00000000..b6c32c96 --- /dev/null +++ b/amarok/src/database_refactor/_Makefile.am @@ -0,0 +1,35 @@ +noinst_LTLIBRARIES = libdbengine.la + +INCLUDES = \ + -I$(top_srcdir)/amarok/src/plugin \ + -I$(top_srcdir)/amarok/src \ + -I$(top_srcdir)/amarok/src/engine \ + -I$(top_srcdir)/amarok/src/amarokcore \ + -I$(top_srcdir)/amarok/src/statusbar \ + $(all_includes) + +#if enable_sqlite + SQLITE_DBENGINE_SUBDIR = sqlite +#endif + +if enable_mysql + MYSQL_DBENGINE_SUBDIR = mysql +endif + +if enable_postgresql + POSTGRESQL_DBENGINE_SUBDIR = postgresql +endif + +libdbengine_la_SOURCES = \ + dbenginebase.cpp + +noinst_HEADERS = dbenginebase.h + +METASOURCES = \ + AUTO + +SUBDIRS = . \ + $(SQLITE_DBENGINE_SUBDIR) \ + $(MYSQL_DBENGINE_SUBDIR) \ + $(POSTGRESQL_DBENGINE_SUBDIR) + diff --git a/amarok/src/database_refactor/collectiondb.cpp b/amarok/src/database_refactor/collectiondb.cpp new file mode 100644 index 00000000..32a5a237 --- /dev/null +++ b/amarok/src/database_refactor/collectiondb.cpp @@ -0,0 +1,1975 @@ +// (c) 2004 Mark Kretschmann +// (c) 2004 Christian Muehlhaeuser +// (c) 2004 Sami Nieminen +// (c) 2005 Ian Monroe +// See COPYING file for licensing information. + +#define DEBUG_PREFIX "CollectionDB" + +#include "app.h" +#include "amarok.h" +#include "amarokconfig.h" +#include "config.h" +#include "debug.h" +#include "collectionbrowser.h" //updateTags() +#include "collectiondb.h" +#include "collectionreader.h" +#include "coverfetcher.h" +#include "enginecontroller.h" +#include "metabundle.h" //updateTags() +#include "playlist.h" +#include "playlistbrowser.h" +#include "pluginmanager.h" +#include "scrobbler.h" +#include "statusbar.h" +#include "threadweaver.h" + +#include +#include +#include + +#include +#include +#include +#include //setupCoverFetcher() +#include +#include //setupCoverFetcher() +#include +#include +#include +#include +#include + +#include //DbConnection::sqlite_power() +#include //query() +#include //usleep() + +#include +#include +#include +#include +#include + + +////////////////////////////////////////////////////////////////////////////////////////// +// CLASS CollectionDB +////////////////////////////////////////////////////////////////////////////////////////// + +CollectionDB* CollectionDB::instance() +{ + static CollectionDB db; + return &db; +} + + +CollectionDB::CollectionDB() + : EngineObserver( EngineController::instance() ) + , m_cacheDir( amaroK::saveLocation() ) + , m_coverDir( amaroK::saveLocation() ) +{ + DEBUG_BLOCK + + // create cover dir, if it doesn't exist. + if( !m_coverDir.exists( "albumcovers", false ) ) + m_coverDir.mkdir( "albumcovers", false ); + m_coverDir.cd( "albumcovers" ); + + // create image cache dir, if it doesn't exist. + if( !m_cacheDir.exists( "albumcovers/cache", false ) ) + m_cacheDir.mkdir( "albumcovers/cache", false ); + m_cacheDir.cd( "albumcovers/cache" ); + + // Load DBEngine plugin + QString query = "[X-KDE-Amarok-plugintype] == 'dbengine' and [X-KDE-Amarok-name] != '%1'"; + KTrader::OfferList offers = PluginManager::query( query.arg( "sqlite-dbengine" ) ); + m_dbEngine = (DBEngine*) PluginManager::createFromService( offers.first() ); + + // + initialize(); + // + + // TODO: Should write to config in dtor, but it crashes... + KConfig* config = amaroK::config( "Collection Browser" ); + config->writeEntry( "Database Version", DATABASE_VERSION ); + config->writeEntry( "Database Stats Version", DATABASE_STATS_VERSION ); + + startTimer( MONITOR_INTERVAL * 1000 ); + + connect( Scrobbler::instance(), SIGNAL( similarArtistsFetched( const QString&, const QStringList& ) ), + this, SLOT( similarArtistsFetched( const QString&, const QStringList& ) ) ); +} + + +CollectionDB::~CollectionDB() +{ + DEBUG_FUNC_INFO + + destroy(); + +// This crashes so it's done at the end of ctor. +// KConfig* const config = amaroK::config( "Collection Browser" ); +// config->writeEntry( "Database Version", DATABASE_VERSION ); +// config->writeEntry( "Database Stats Version", DATABASE_STATS_VERSION ); +} + + +////////////////////////////////////////////////////////////////////////////////////////// +// PUBLIC +////////////////////////////////////////////////////////////////////////////////////////// + + +DbConnection +*CollectionDB::getStaticDbConnection() +{ + return m_dbConnPool->getDbConnection(); +} + + +void +CollectionDB::returnStaticDbConnection( DbConnection *conn ) +{ + m_dbConnPool->putDbConnection( conn ); +} + + +/** + * Executes a SQL query on the already opened database + * @param statement SQL program to execute. Only one SQL statement is allowed. + * @return The queried data, or QStringList() on error. + */ +QStringList +CollectionDB::query( const QString& statement, DbConnection *conn ) +{ + if ( DEBUG ) + debug() << "Query-start: " << statement << endl; + + clock_t start = clock(); + + DbConnection *dbConn; + if ( conn != NULL ) + { + dbConn = conn; + } + else + { + dbConn = m_dbConnPool->getDbConnection(); + } + + QStringList values = dbConn->query( statement ); + + if ( conn == NULL ) + { + m_dbConnPool->putDbConnection( dbConn ); + } + + if ( DEBUG ) + { + clock_t finish = clock(); + const double duration = (double) (finish - start) / CLOCKS_PER_SEC; + debug() << "SQL-query (" << duration << "s): " << statement << endl; + } + return values; +} + + +/** + * Executes a SQL insert on the already opened database + * @param statement SQL statement to execute. Only one SQL statement is allowed. + * @return The rowid of the inserted item. + */ +int +CollectionDB::insert( const QString& statement, const QString& table, DbConnection *conn ) +{ + if ( DEBUG ) + debug() << "insert-start: " << statement << endl; + + clock_t start = clock(); + + DbConnection *dbConn; + if ( conn != NULL ) + { + dbConn = conn; + } + else + { + dbConn = m_dbConnPool->getDbConnection(); + } + + int id = dbConn->insert( statement, table ); + + if ( conn == NULL ) + { + m_dbConnPool->putDbConnection( dbConn ); + } + + if ( DEBUG ) + { + clock_t finish = clock(); + const double duration = (double) (finish - start) / CLOCKS_PER_SEC; + debug() << "SQL-insert (" << duration << "s): " << statement << endl; + } + return id; +} + + +bool +CollectionDB::isEmpty() +{ + QStringList values; + + if (m_dbConnPool->getDbConnectionType() == DbConnection::postgresql) + { + values = query( "SELECT COUNT( url ) FROM tags OFFSET 0 LIMIT 1;" ); + } + else + { + values = query( "SELECT COUNT( url ) FROM tags LIMIT 0, 1;" ); + } + + return values.isEmpty() ? true : values.first() == "0"; +} + + +bool +CollectionDB::isValid() +{ + QStringList values1; + QStringList values2; + + if (m_dbConnPool->getDbConnectionType() == DbConnection::postgresql) { + values1 = query( "SELECT COUNT( url ) FROM tags OFFSET 0 LIMIT 1;" ); + values2 = query( "SELECT COUNT( url ) FROM statistics OFFSET 0 LIMIT 1;" ); + } + else + { + values1 = query( "SELECT COUNT( url ) FROM tags LIMIT 0, 1;" ); + values2 = query( "SELECT COUNT( url ) FROM statistics LIMIT 0, 1;" ); + } + + //TODO? this returns true if value1 or value2 is not empty. Shouldn't this be and (&&)??? + return !values1.isEmpty() || !values2.isEmpty(); +} + + +void +CollectionDB::createTables( DbConnection *conn ) +{ + DEBUG_FUNC_INFO + + //create tag table + query( QString( "CREATE %1 TABLE tags%2 (" + "url " + textColumnType() + "," + "dir " + textColumnType() + "," + "createdate INTEGER," + "album INTEGER," + "artist INTEGER," + "genre INTEGER," + "title " + textColumnType() + "," + "year INTEGER," + "comment " + textColumnType() + "," + "track NUMERIC(4)," + "bitrate INTEGER," + "length INTEGER," + "samplerate INTEGER," + "sampler BOOL );" ) + .arg( conn ? "TEMPORARY" : "" ) + .arg( conn ? "_temp" : "" ), conn ); + + QString albumAutoIncrement = ""; + QString artistAutoIncrement = ""; + QString genreAutoIncrement = ""; + QString yearAutoIncrement = ""; + if ( m_dbConnPool->getDbConnectionType() == DbConnection::postgresql ) + { + query( QString( "CREATE SEQUENCE album_seq;" ), conn ); + query( QString( "CREATE SEQUENCE artist_seq;" ), conn ); + query( QString( "CREATE SEQUENCE genre_seq;" ), conn ); + query( QString( "CREATE SEQUENCE year_seq;" ), conn ); + + albumAutoIncrement = QString("DEFAULT nextval('album_seq')"); + artistAutoIncrement = QString("DEFAULT nextval('artist_seq')"); + genreAutoIncrement = QString("DEFAULT nextval('genre_seq')"); + yearAutoIncrement = QString("DEFAULT nextval('year_seq')"); + } + else if ( m_dbConnPool->getDbConnectionType() == DbConnection::mysql ) + { + albumAutoIncrement = "AUTO_INCREMENT"; + artistAutoIncrement = "AUTO_INCREMENT"; + genreAutoIncrement = "AUTO_INCREMENT"; + yearAutoIncrement = "AUTO_INCREMENT"; + } + //create album table + query( QString( "CREATE %1 TABLE album%2 (" + "id INTEGER PRIMARY KEY %3," + "name " + textColumnType() + ");" ) + .arg( conn ? "TEMPORARY" : "" ) + .arg( conn ? "_temp" : "" ) + .arg( albumAutoIncrement ), conn ); + + //create artist table + query( QString( "CREATE %1 TABLE artist%2 (" + "id INTEGER PRIMARY KEY %3," + "name " + textColumnType() + ");" ) + .arg( conn ? "TEMPORARY" : "" ) + .arg( conn ? "_temp" : "" ) + .arg( artistAutoIncrement ), conn ); + + //create genre table + query( QString( "CREATE %1 TABLE genre%2 (" + "id INTEGER PRIMARY KEY %3," + "name " + textColumnType() +");" ) + .arg( conn ? "TEMPORARY" : "" ) + .arg( conn ? "_temp" : "" ) + .arg( genreAutoIncrement ), conn ); + + //create year table + query( QString( "CREATE %1 TABLE year%2 (" + "id INTEGER PRIMARY KEY %3," + "name " + textColumnType() + ");" ) + .arg( conn ? "TEMPORARY" : "" ) + .arg( conn ? "_temp" : "" ) + .arg( yearAutoIncrement ), conn ); + + //create images table + query( QString( "CREATE %1 TABLE images%2 (" + "path " + textColumnType() + "," + "artist " + textColumnType() + "," + "album " + textColumnType() + ");" ) + .arg( conn ? "TEMPORARY" : "" ) + .arg( conn ? "_temp" : "" ), conn ); + + // create directory statistics table + query( QString( "CREATE %1 TABLE directories%2 (" + "dir " + textColumnType() + " UNIQUE," + "changedate INTEGER );" ) + .arg( conn ? "TEMPORARY" : "" ) + .arg( conn ? "_temp" : "" ), conn ); + + + //create indexes + query( QString( "CREATE INDEX album_idx%1 ON album%2( name );" ) + .arg( conn ? "_temp" : "" ).arg( conn ? "_temp" : "" ), conn ); + query( QString( "CREATE INDEX artist_idx%1 ON artist%2( name );" ) + .arg( conn ? "_temp" : "" ).arg( conn ? "_temp" : "" ), conn ); + query( QString( "CREATE INDEX genre_idx%1 ON genre%2( name );" ) + .arg( conn ? "_temp" : "" ).arg( conn ? "_temp" : "" ), conn ); + query( QString( "CREATE INDEX year_idx%1 ON year%2( name );" ) + .arg( conn ? "_temp" : "" ).arg( conn ? "_temp" : "" ), conn ); + + if ( !conn ) + { + // create related artists cache + query( QString( "CREATE TABLE related_artists (" + "artist " + textColumnType() + "," + "suggestion " + textColumnType() + "," + "changedate INTEGER );" ) ); + + query( "CREATE INDEX url_tag ON tags( url );" ); + query( "CREATE INDEX album_tag ON tags( album );" ); + query( "CREATE INDEX artist_tag ON tags( artist );" ); + query( "CREATE INDEX genre_tag ON tags( genre );" ); + query( "CREATE INDEX year_tag ON tags( year );" ); + query( "CREATE INDEX sampler_tag ON tags( sampler );" ); + + query( "CREATE INDEX images_album ON images( album );" ); + query( "CREATE INDEX images_artist ON images( artist );" ); + + query( "CREATE INDEX directories_dir ON directories( dir );" ); + query( "CREATE INDEX related_artists_artist ON related_artists( artist );" ); + } +} + + +void +CollectionDB::dropTables( DbConnection *conn ) +{ + DEBUG_FUNC_INFO + + query( QString( "DROP TABLE tags%1;" ).arg( conn ? "_temp" : "" ), conn ); + query( QString( "DROP TABLE album%1;" ).arg( conn ? "_temp" : "" ), conn ); + query( QString( "DROP TABLE artist%1;" ).arg( conn ? "_temp" : "" ), conn ); + query( QString( "DROP TABLE genre%1;" ).arg( conn ? "_temp" : "" ), conn ); + query( QString( "DROP TABLE year%1;" ).arg( conn ? "_temp" : "" ), conn ); + query( QString( "DROP TABLE images%1;" ).arg( conn ? "_temp" : "" ), conn ); + query( QString( "DROP TABLE directories%1;" ).arg( conn ? "_temp" : "" ), conn ); + if ( !conn ) + { + query( QString( "DROP TABLE related_artists;" ) ); + } + + if ( m_dbConnPool->getDbConnectionType() == DbConnection::postgresql ) + { + if (conn == NULL) { + query( QString( "DROP SEQUENCE album_seq;" ), conn ); + query( QString( "DROP SEQUENCE artist_seq;" ), conn ); + query( QString( "DROP SEQUENCE genre_seq;" ), conn ); + query( QString( "DROP SEQUENCE year_seq;" ), conn ); + } + } +} + + +void +CollectionDB::clearTables( DbConnection *conn ) +{ + DEBUG_FUNC_INFO + + QString clearCommand = "DELETE FROM"; + if ( m_dbConnPool->getDbConnectionType() == DbConnection::mysql ) + { + // TRUNCATE TABLE is faster than DELETE FROM TABLE, so use it when supported. + clearCommand = "TRUNCATE TABLE"; + } + + query( QString( "%1 tags%2;" ).arg( clearCommand ).arg( conn ? "_temp" : "" ), conn ); + query( QString( "%1 album%2;" ).arg( clearCommand ).arg( conn ? "_temp" : "" ), conn ); + query( QString( "%1 artist%2;" ).arg( clearCommand ).arg( conn ? "_temp" : "" ), conn ); + query( QString( "%1 genre%2;" ).arg( clearCommand ).arg( conn ? "_temp" : "" ), conn ); + query( QString( "%1 year%2;" ).arg( clearCommand ).arg( conn ? "_temp" : "" ), conn ); + query( QString( "%1 images%2;" ).arg( clearCommand ).arg( conn ? "_temp" : "" ), conn ); + query( QString( "%1 directories%2;" ).arg( clearCommand ).arg( conn ? "_temp" : "" ), conn ); + if ( !conn ) + { + query( QString( "%1 related_artists;" ).arg( clearCommand ) ); + } +} + + +void +CollectionDB::moveTempTables( DbConnection *conn ) +{ + insert( "INSERT INTO tags SELECT * FROM tags_temp;", NULL, conn ); + insert( "INSERT INTO album SELECT * FROM album_temp;", NULL, conn ); + insert( "INSERT INTO artist SELECT * FROM artist_temp;", NULL, conn ); + insert( "INSERT INTO genre SELECT * FROM genre_temp;", NULL, conn ); + insert( "INSERT INTO year SELECT * FROM year_temp;", NULL, conn ); + insert( "INSERT INTO images SELECT * FROM images_temp;", NULL, conn ); + insert( "INSERT INTO directories SELECT * FROM directories_temp;", NULL, conn ); +} + + +void +CollectionDB::createStatsTable() +{ + DEBUG_FUNC_INFO + + // create music statistics database + query( QString( "CREATE TABLE statistics (" + "url " + textColumnType() + " UNIQUE," + "createdate INTEGER," + "accessdate INTEGER," + "percentage FLOAT," + "playcounter INTEGER );" ) ); + + query( "CREATE INDEX url_stats ON statistics( url );" ); + query( "CREATE INDEX percentage_stats ON statistics( percentage );" ); + query( "CREATE INDEX playcounter_stats ON statistics( playcounter );" ); +} + + +void +CollectionDB::dropStatsTable() +{ + DEBUG_FUNC_INFO + + query( "DROP TABLE statistics;" ); +} + + +uint +CollectionDB::artistID( QString value, bool autocreate, const bool temporary, const bool updateSpelling, DbConnection *conn ) +{ + // lookup cache + if ( m_cacheArtist == value ) + return m_cacheArtistID; + + uint id = IDFromValue( "artist", value, autocreate, temporary, updateSpelling, conn ); + + // cache values + m_cacheArtist = value; + m_cacheArtistID = id; + + return id; +} + + +QString +CollectionDB::artistValue( uint id ) +{ + // lookup cache + if ( m_cacheArtistID == id ) + return m_cacheArtist; + + QString value = valueFromID( "artist", id ); + + // cache values + m_cacheArtist = value; + m_cacheArtistID = id; + + return value; +} + + + +uint +CollectionDB::albumID( QString value, bool autocreate, const bool temporary, const bool updateSpelling, DbConnection *conn ) +{ + // lookup cache + if ( m_cacheAlbum == value ) + return m_cacheAlbumID; + + uint id = IDFromValue( "album", value, autocreate, temporary, updateSpelling, conn ); + + // cache values + m_cacheAlbum = value; + m_cacheAlbumID = id; + + return id; +} + + +QString +CollectionDB::albumValue( uint id ) +{ + // lookup cache + if ( m_cacheAlbumID == id ) + return m_cacheAlbum; + + QString value = valueFromID( "album", id ); + + // cache values + m_cacheAlbum = value; + m_cacheAlbumID = id; + + return value; +} + + +uint +CollectionDB::genreID( QString value, bool autocreate, const bool temporary, const bool updateSpelling, DbConnection *conn ) +{ + return IDFromValue( "genre", value, autocreate, temporary, updateSpelling, conn ); +} + + +QString +CollectionDB::genreValue( uint id ) +{ + return valueFromID( "genre", id ); +} + + +uint +CollectionDB::yearID( QString value, bool autocreate, const bool temporary, const bool updateSpelling, DbConnection *conn ) +{ + return IDFromValue( "year", value, autocreate, temporary, updateSpelling, conn ); +} + + +QString +CollectionDB::yearValue( uint id ) +{ + return valueFromID( "year", id ); +} + + +uint +CollectionDB::IDFromValue( QString name, QString value, bool autocreate, const bool temporary, const bool updateSpelling, DbConnection *conn ) +{ + if ( temporary ) + name.append( "_temp" ); + else + conn = NULL; + + QStringList values = + query( QString( + "SELECT id, name FROM %1 WHERE name LIKE '%2';" ) + .arg( name ) + .arg( CollectionDB::instance()->escapeString( value ) ), conn ); + + if ( updateSpelling && !values.isEmpty() && ( values[1] != value ) ) + { + query( QString( "UPDATE %1 SET id = %2, name = '%3' WHERE id = %4;" ) + .arg( name ) + .arg( values.first() ) + .arg( CollectionDB::instance()->escapeString( value ) ) + .arg( values.first() ), conn ); + } + + //check if item exists. if not, should we autocreate it? + uint id; + if ( values.isEmpty() && autocreate ) + { + id = insert( QString( "INSERT INTO %1 ( name ) VALUES ( '%2' );" ) + .arg( name ) + .arg( CollectionDB::instance()->escapeString( value ) ), name, conn ); + + return id; + } + + return values.isEmpty() ? 0 : values.first().toUInt(); +} + + +QString +CollectionDB::valueFromID( QString table, uint id ) +{ + QStringList values = + query( QString( + "SELECT name FROM %1 WHERE id=%2;" ) + .arg( table ) + .arg( id ) ); + + + return values.isEmpty() ? 0 : values.first(); +} + + +QString +CollectionDB::albumSongCount( const QString &artist_id, const QString &album_id ) +{ + QStringList values = + query( QString( + "SELECT COUNT( url ) FROM tags WHERE album = %1 AND artist = %2;" ) + .arg( album_id ) + .arg( artist_id ) ); + return values.first(); +} + +bool +CollectionDB::albumIsCompilation( const QString &album_id ) +{ + QStringList values = + query( QString( + "SELECT sampler FROM tags WHERE sampler=%1 AND album=%2" ) + .arg( CollectionDB::instance()->boolT() ) + .arg( album_id ) ); + + return (values.count() != 0); +} + +QStringList +CollectionDB::albumTracks( const QString &artist_id, const QString &album_id ) +{ + if (m_dbConnPool->getDbConnectionType() == DbConnection::postgresql) { + return query( QString( "SELECT tags.url, tags.track AS __discard FROM tags, year WHERE tags.album = %1 AND " + "( tags.sampler = %2 OR tags.artist = %3 ) AND year.id = tags.year " + "ORDER BY tags.track;" ) + .arg( album_id ) + .arg( boolT() ) + .arg( artist_id ) ); + } + else + { + return query( QString( "SELECT tags.url FROM tags, year WHERE tags.album = %1 AND " + "( tags.sampler = 1 OR tags.artist = %2 ) AND year.id = tags.year " + "ORDER BY tags.track;" ) + .arg( album_id ) + .arg( artist_id ) ); + } +} + + +void +CollectionDB::addImageToAlbum( const QString& image, QValueList< QPair > info, DbConnection *conn ) +{ + for ( QValueList< QPair >::ConstIterator it = info.begin(); it != info.end(); ++it ) + { + if ( (*it).first.isEmpty() || (*it).second.isEmpty() ) + continue; + + debug() << "Added image for album: " << (*it).first << " - " << (*it).second << ": " << image << endl; + insert( QString( "INSERT INTO images%1 ( path, artist, album ) VALUES ( '%1', '%2', '%3' );" ) + .arg( conn ? "_temp" : "" ) + .arg( escapeString( image ) ) + .arg( escapeString( (*it).first ) ) + .arg( escapeString( (*it).second ) ), NULL, conn ); + } +} + +QImage +CollectionDB::fetchImage(const KURL& url, QString &/*tmpFile*/) +{ + if(url.protocol() != "file") + { + QString tmpFile; + KIO::NetAccess::download( url, tmpFile, 0); //TODO set 0 to the window, though it probably doesn't really matter + return QImage(tmpFile); + } + else + { + return QImage( url.path() ); + } + +} +bool +CollectionDB::setAlbumImage( const QString& artist, const QString& album, const KURL& url ) +{ + QString tmpFile; + bool success = setAlbumImage( artist, album, fetchImage(url, tmpFile) ); + KIO::NetAccess::removeTempFile( tmpFile ); //only removes file if it was created with NetAccess + return success; +} + + +bool +CollectionDB::setAlbumImage( const QString& artist, const QString& album, QImage img, const QString& amazonUrl ) +{ + debug() << "Saving cover for: " << artist << " - " << album << endl; + + //show a wait cursor for the duration + amaroK::OverrideCursor keep; + + // remove existing album covers + removeAlbumImage( artist, album ); + + QDir largeCoverDir( amaroK::saveLocation( "albumcovers/large/" ) ); + QCString key = md5sum( artist, album ); + + // Save Amazon product page URL as embedded string, for later retreival + if ( !amazonUrl.isEmpty() ) + img.setText( "amazon-url", 0, amazonUrl ); + + return img.save( largeCoverDir.filePath( key ), "PNG"); +} + + +QString +CollectionDB::findImageByMetabundle( MetaBundle trackInformation, uint width ) +{ + if( width == 1 ) width = AmarokConfig::coverPreviewSize(); + + QCString widthKey = makeWidthKey( width ); + QCString tagKey = md5sum( trackInformation.artist(), trackInformation.album() ); + QDir tagCoverDir( amaroK::saveLocation( "albumcovers/tagcover/" ) ); + + //FIXME: the cached versions will never be refreshed + if ( tagCoverDir.exists( widthKey + tagKey ) ) + { + // cached version + return tagCoverDir.filePath( widthKey + tagKey ); + } else + { + // look into the tag + TagLib::MPEG::File f( QFile::encodeName( trackInformation.url().path() ) ); + TagLib::ID3v2::Tag *tag = f.ID3v2Tag(); + + if ( tag ) + { + TagLib::ID3v2::FrameList l = f.ID3v2Tag()->frameListMap()[ "APIC" ]; + if ( !l.isEmpty() ) + { + debug() << "Found APIC frame(s)" << endl; + TagLib::ID3v2::Frame *f = l.front(); + TagLib::ID3v2::AttachedPictureFrame *ap = (TagLib::ID3v2::AttachedPictureFrame*)f; + + const TagLib::ByteVector &imgVector = ap->picture(); + debug() << "Size of image: " << imgVector.size() << " byte" << endl; + + // ignore APIC frames without picture and those with obviously bogus size + if( imgVector.size() == 0 || imgVector.size() > 10000000 /*10MB*/ ) + return QString(); + + QImage image; + if( image.loadFromData((const uchar*)imgVector.data(), imgVector.size()) ) + { + if ( width > 1 ) + { + image.smoothScale( width, width, QImage::ScaleMin ).save( m_cacheDir.filePath( widthKey + tagKey ), "PNG" ); + return m_cacheDir.filePath( widthKey + tagKey ); + } else + { + image.save( tagCoverDir.filePath( tagKey ), "PNG" ); + return tagCoverDir.filePath( tagKey ); + } + } // image.isNull + } // apic list is empty + } // tag is empty + } // caching + + return QString(); +} + + +QString +CollectionDB::findImageByArtistAlbum( const QString &artist, const QString &album, uint width ) +{ + QCString widthKey = makeWidthKey( width ); + + if ( artist.isEmpty() && album.isEmpty() ) + return notAvailCover( width ); + else + { + QCString key = md5sum( artist, album ); + + // check cache for existing cover + if ( m_cacheDir.exists( widthKey + key ) ) + return m_cacheDir.filePath( widthKey + key ); + else + { + // we need to create a scaled version of this cover + QDir largeCoverDir( amaroK::saveLocation( "albumcovers/large/" ) ); + if ( largeCoverDir.exists( key ) ) + if ( width > 1 ) + { + QImage img( largeCoverDir.filePath( key ) ); + img.smoothScale( width, width, QImage::ScaleMin ).save( m_cacheDir.filePath( widthKey + key ), "PNG" ); + + return m_cacheDir.filePath( widthKey + key ); + } + else + return largeCoverDir.filePath( key ); + } + + // no amazon cover found, let's try to find a cover in the song's directory + return getImageForAlbum( artist, album, width ); + } +} + + +QString +CollectionDB::albumImage( const QString &artist, const QString &album, uint width ) +{ + QString s; + // we aren't going to need a 1x1 size image. this is just a quick hack to be able to show full size images. + if ( width == 1 ) width = AmarokConfig::coverPreviewSize(); + + s = findImageByArtistAlbum( artist, album, width ); + if ( s == notAvailCover( width ) ) + return findImageByArtistAlbum( "", album, width ); + + return s; +} + + +QString +CollectionDB::albumImage( const uint artist_id, const uint album_id, const uint width ) +{ + return albumImage( artistValue( artist_id ), albumValue( album_id ), width ); +} + + +QString +CollectionDB::albumImage( MetaBundle trackInformation, uint width ) +{ + QString path = findImageByMetabundle( trackInformation, width ); + if( path.isEmpty() ) + path =albumImage( trackInformation.artist(), trackInformation.album(), width ); + + return path; +} + + +QCString +CollectionDB::makeWidthKey( uint width ) +{ + return QString::number( width ).local8Bit() + "@"; +} + +// get image from path +QString +CollectionDB::getImageForAlbum( const QString& artist, const QString& album, uint width ) +{ + if ( width == 1 ) width = AmarokConfig::coverPreviewSize(); + QCString widthKey = QString::number( width ).local8Bit() + "@"; + + if ( album.isEmpty() ) + return notAvailCover( width ); + + QStringList values = + query( QString( + "SELECT path FROM images WHERE artist LIKE '%1' AND album LIKE '%2' ORDER BY path;" ) + .arg( escapeString( artist ) ) + .arg( escapeString( album ) ) ); + + if ( !values.isEmpty() ) + { + QString image( values.first() ); + uint matches = 0; + uint maxmatches = 0; + for ( uint i = 0; i < values.count(); i++ ) + { + matches = values[i].contains( "front", false ) + values[i].contains( "cover", false ) + values[i].contains( "folder", false ); + if ( matches > maxmatches ) + { + image = values[i]; + maxmatches = matches; + } + } + + QCString key = md5sum( artist, album, image ); + + if ( width > 1 ) + { + if ( !m_cacheDir.exists( widthKey + key ) ) + { + QImage img = QImage( image ); + img.smoothScale( width, width, QImage::ScaleMin ).save( m_cacheDir.filePath( widthKey + key ), "PNG" ); + } + + return m_cacheDir.filePath( widthKey + key ); + } + else //large image + { + return image; + } + } + + return notAvailCover( width ); +} + + +bool +CollectionDB::removeAlbumImage( const QString &artist, const QString &album ) +{ + QCString widthKey = "*@"; + QCString key = md5sum( artist, album ); + + // remove scaled versions of images + QStringList scaledList = m_cacheDir.entryList( widthKey + key ); + if ( scaledList.count() > 0 ) + for ( uint i = 0; i < scaledList.count(); i++ ) + QFile::remove( m_cacheDir.filePath( scaledList[ i ] ) ); + + // remove large, original image + QDir largeCoverDir( amaroK::saveLocation( "albumcovers/large/" ) ); + + if ( largeCoverDir.exists( key ) && QFile::remove( largeCoverDir.filePath( key ) ) ) { + emit coverRemoved( artist, album ); + return true; + } + + return false; +} + +bool +CollectionDB::removeAlbumImage( const uint artist_id, const uint album_id ) +{ + return removeAlbumImage( artistValue( artist_id ), albumValue( album_id ) ); +} + + +QString +CollectionDB::notAvailCover( int width ) +{ + if ( !width ) width = AmarokConfig::coverPreviewSize(); + QString widthKey = QString::number( width ) + "@"; + + if( m_cacheDir.exists( widthKey + "nocover.png" ) ) + return m_cacheDir.filePath( widthKey + "nocover.png" ); + else + { + QImage nocover( locate( "data", "amarok/images/nocover.png" ) ); + nocover.smoothScale( width, width, QImage::ScaleMin ).save( m_cacheDir.filePath( widthKey + "nocover.png" ), "PNG" ); + return m_cacheDir.filePath( widthKey + "nocover.png" ); + } +} + + +QStringList +CollectionDB::artistList( bool withUnknowns, bool withCompilations ) +{ + QueryBuilder qb; + qb.addReturnValue( QueryBuilder::tabArtist, QueryBuilder::valName ); + + if ( !withUnknowns ) + qb.excludeMatch( QueryBuilder::tabArtist, i18n( "Unknown" ) ); + if ( !withCompilations ) + qb.setOptions( QueryBuilder::optNoCompilations ); + + qb.setOptions( QueryBuilder::optRemoveDuplicates ); + qb.sortBy( QueryBuilder::tabArtist, QueryBuilder::valName ); + return qb.run(); +} + + +QStringList +CollectionDB::albumList( bool withUnknowns, bool withCompilations ) +{ + QueryBuilder qb; + qb.addReturnValue( QueryBuilder::tabAlbum, QueryBuilder::valName ); + + if ( !withUnknowns ) + qb.excludeMatch( QueryBuilder::tabAlbum, i18n( "Unknown" ) ); + if ( !withCompilations ) + qb.setOptions( QueryBuilder::optNoCompilations ); + + qb.setOptions( QueryBuilder::optRemoveDuplicates ); + qb.sortBy( QueryBuilder::tabAlbum, QueryBuilder::valName ); + return qb.run(); +} + + +QStringList +CollectionDB::genreList( bool withUnknowns, bool withCompilations ) +{ + QueryBuilder qb; + qb.addReturnValue( QueryBuilder::tabGenre, QueryBuilder::valName ); + + if ( !withUnknowns ) + qb.excludeMatch( QueryBuilder::tabGenre, i18n( "Unknown" ) ); + if ( !withCompilations ) + qb.setOptions( QueryBuilder::optNoCompilations ); + + qb.setOptions( QueryBuilder::optRemoveDuplicates ); + qb.sortBy( QueryBuilder::tabGenre, QueryBuilder::valName ); + return qb.run(); +} + + +QStringList +CollectionDB::yearList( bool withUnknowns, bool withCompilations ) +{ + QueryBuilder qb; + qb.addReturnValue( QueryBuilder::tabYear, QueryBuilder::valName ); + + if ( !withUnknowns ) + qb.excludeMatch( QueryBuilder::tabYear, i18n( "Unknown" ) ); + if ( !withCompilations ) + qb.setOptions( QueryBuilder::optNoCompilations ); + + qb.setOptions( QueryBuilder::optRemoveDuplicates ); + qb.sortBy( QueryBuilder::tabYear, QueryBuilder::valName ); + return qb.run(); +} + + +QStringList +CollectionDB::albumListOfArtist( const QString &artist, bool withUnknown, bool withCompilations ) +{ + if (m_dbConnPool->getDbConnectionType() == DbConnection::postgresql) + { + return query( "SELECT DISTINCT album.name, lower( album.name ) AS __discard FROM tags, album, artist WHERE " + "tags.album = album.id AND tags.artist = artist.id " + "AND artist.name = '" + escapeString( artist ) + "' " + + ( withUnknown ? QString::null : "AND album.name <> '' " ) + + ( withCompilations ? QString::null : "AND tags.sampler = " + boolF() ) + + " ORDER BY lower( album.name );" ); + } + else + { + return query( "SELECT DISTINCT album.name FROM tags, album, artist WHERE " + "tags.album = album.id AND tags.artist = artist.id " + "AND artist.name = '" + escapeString( artist ) + "' " + + ( withUnknown ? QString::null : "AND album.name <> '' " ) + + ( withCompilations ? QString::null : "AND tags.sampler = " + boolF() ) + + " ORDER BY lower( album.name );" ); + } +} + + +QStringList +CollectionDB::artistAlbumList( bool withUnknown, bool withCompilations ) +{ + if (m_dbConnPool->getDbConnectionType() == DbConnection::postgresql) + { + return query( "SELECT DISTINCT artist.name, album.name, lower( album.name ) AS __discard FROM tags, album, artist WHERE " + "tags.album = album.id AND tags.artist = artist.id " + + ( withUnknown ? QString::null : "AND album.name <> '' AND artist.name <> '' " ) + + ( withCompilations ? QString::null : "AND tags.sampler = " + boolF() ) + + " ORDER BY lower( album.name );" ); + } + else + { + return query( "SELECT DISTINCT artist.name, album.name FROM tags, album, artist WHERE " + "tags.album = album.id AND tags.artist = artist.id " + + ( withUnknown ? QString::null : "AND album.name <> '' AND artist.name <> '' " ) + + ( withCompilations ? QString::null : "AND tags.sampler = " + boolF() ) + + " ORDER BY lower( album.name );" ); + } +} + + +bool +CollectionDB::addSong( MetaBundle* bundle, const bool incremental, DbConnection *conn ) +{ + if ( !QFileInfo( bundle->url().path() ).isReadable() ) return false; + + QString command = "INSERT INTO tags_temp " + "( url, dir, createdate, album, artist, genre, year, title, comment, track, sampler, length, bitrate, samplerate ) " + "VALUES ('"; + + QString artist = bundle->artist(); + QString title = bundle->title(); + if ( title.isEmpty() ) + { + title = bundle->url().fileName(); + if ( bundle->url().fileName().find( '-' ) > 0 ) + { + if ( artist.isEmpty() ) artist = bundle->url().fileName().section( '-', 0, 0 ).stripWhiteSpace(); + title = bundle->url().fileName().section( '-', 1 ).stripWhiteSpace(); + title = title.left( title.findRev( '.' ) ).stripWhiteSpace(); + if ( title.isEmpty() ) title = bundle->url().fileName(); + } + } + bundle->setArtist( artist ); + bundle->setTitle( title ); + + command += escapeString( bundle->url().path() ) + "','"; + command += escapeString( bundle->url().directory() ) + "',"; + command += QString::number( QFileInfo( bundle->url().path() ).lastModified().toTime_t() ) + ","; + + command += escapeString( QString::number( albumID( bundle->album(), true, !incremental, false, conn ) ) ) + ","; + command += escapeString( QString::number( artistID( bundle->artist(), true, !incremental, false, conn ) ) ) + ","; + command += escapeString( QString::number( genreID( bundle->genre(), true, !incremental, false, conn ) ) ) + ",'"; + command += escapeString( QString::number( yearID( bundle->year(), true, !incremental, false, conn ) ) ) + "','"; + + command += escapeString( bundle->title() ) + "','"; + command += escapeString( bundle->comment() ) + "', "; + command += ( bundle->track().isEmpty() ? "NULL" : escapeString( bundle->track() ) ) + " , "; + command += artist == i18n( "Various Artists" ) ? boolT() + "," : boolF() + ","; + + // NOTE any of these may be -1 or -2, this is what we want + // see MetaBundle::Undetermined + command += QString::number( bundle->length() ) + ","; + command += QString::number( bundle->bitrate() ) + ","; + command += QString::number( bundle->sampleRate() ) + ")"; + + //FIXME: currently there's no way to check if an INSERT query failed or not - always return true atm. + // Now it might be possible as insert returns the rowid. + insert( command, NULL, conn ); + return true; +} + + +static void +fillInBundle( QStringList values, MetaBundle &bundle ) +{ + //TODO use this whenever possible + + // crash prevention + while( values.count() != 10 ) + values += "IF YOU CAN SEE THIS THERE IS A BUG!"; + + QStringList::ConstIterator it = values.begin(); + + bundle.setAlbum ( *it ); ++it; + bundle.setArtist ( *it ); ++it; + bundle.setGenre ( *it ); ++it; + bundle.setTitle ( *it ); ++it; + bundle.setYear ( *it ); ++it; + bundle.setComment ( *it ); ++it; + bundle.setTrack ( *it ); ++it; + bundle.setBitrate ( (*it).toInt() ); ++it; + bundle.setLength ( (*it).toInt() ); ++it; + bundle.setSampleRate( (*it).toInt() ); +} + +bool +CollectionDB::bundleForUrl( MetaBundle* bundle ) +{ + QStringList values = query( QString( + "SELECT album.name, artist.name, genre.name, tags.title, " + "year.name, tags.comment, tags.track, tags.bitrate, tags.length, " + "tags.samplerate " + "FROM tags, album, artist, genre, year " + "WHERE album.id = tags.album AND artist.id = tags.artist AND " + "genre.id = tags.genre AND year.id = tags.year AND tags.url = '%1';" ) + .arg( escapeString( bundle->url().path() ) ) ); + + if ( !values.empty() ) + fillInBundle( values, *bundle ); + + return !values.isEmpty(); +} + + +QValueList +CollectionDB::bundlesByUrls( const KURL::List& urls ) +{ + typedef QValueList BundleList; + BundleList bundles; + QStringList paths; + QueryBuilder qb; + + for( KURL::List::ConstIterator it = urls.begin(), end = urls.end(), last = urls.fromLast(); it != end; ++it ) + { + // non file stuff won't exist in the db, but we still need to + // re-insert it into the list we return, just with no tags assigned + paths += (*it).protocol() == "file" ? (*it).path() : (*it).url(); + + if( paths.count() == 50 || it == last ) + { + qb.clear(); + + qb.addReturnValue( QueryBuilder::tabAlbum, QueryBuilder::valName ); + qb.addReturnValue( QueryBuilder::tabArtist, QueryBuilder::valName ); + qb.addReturnValue( QueryBuilder::tabGenre, QueryBuilder::valName ); + qb.addReturnValue( QueryBuilder::tabSong, QueryBuilder::valTitle ); + qb.addReturnValue( QueryBuilder::tabYear, QueryBuilder::valName ); + qb.addReturnValue( QueryBuilder::tabSong, QueryBuilder::valComment ); + qb.addReturnValue( QueryBuilder::tabSong, QueryBuilder::valTrack ); + qb.addReturnValue( QueryBuilder::tabSong, QueryBuilder::valBitrate ); + qb.addReturnValue( QueryBuilder::tabSong, QueryBuilder::valLength ); + qb.addReturnValue( QueryBuilder::tabSong, QueryBuilder::valSamplerate ); + qb.addReturnValue( QueryBuilder::tabSong, QueryBuilder::valURL ); + + qb.addURLFilters( paths ); + qb.setOptions( QueryBuilder::optRemoveDuplicates ); + + const QStringList values = qb.run(); + + BundleList buns50; + MetaBundle b; + foreach( values ) + { + b.setAlbum ( *it ); + b.setArtist ( *++it ); + b.setGenre ( *++it ); + b.setTitle ( *++it ); + b.setYear ( *++it ); + b.setComment ( *++it ); + b.setTrack ( *++it ); + b.setBitrate ( (*++it).toInt() ); + b.setLength ( (*++it).toInt() ); + b.setSampleRate( (*++it).toInt() ); + b.setPath ( *++it ); + + buns50.append( b ); + } + + // we get no guarantee about the order that the database + // will return our values, and sqlite indeed doesn't return + // them in the desired order :( (MySQL does though) + foreach( paths ) { + for( BundleList::Iterator jt = buns50.begin(), end = buns50.end(); jt != end; ++jt ) + if ( (*jt).url().path() == (*it) ) { + bundles += *jt; + buns50.remove( jt ); + goto success; + } + + // if we get here, we didn't find an entry + debug() << "No bundle recovered for: " << *it << endl; + b = MetaBundle(); + b.setUrl( KURL::fromPathOrURL(*it) ); + bundles += b; + + success: ; + } + + paths.clear(); + } + } + + return bundles; +} + + +void +CollectionDB::addAudioproperties( const MetaBundle& bundle ) +{ + query( QString( "UPDATE tags SET bitrate='%1', length='%2', samplerate='%3' WHERE url='%4';" ) + .arg( bundle.bitrate() ) + .arg( bundle.length() ) + .arg( bundle.sampleRate() ) + .arg( escapeString( bundle.url().path() ) ) ); +} + + +int +CollectionDB::addSongPercentage( const QString &url, int percentage ) +{ + float score; + QStringList values = + query( QString( + "SELECT playcounter, createdate, percentage FROM statistics " + "WHERE url = '%1';" ) + .arg( escapeString( url ) ) ); + + // check boundaries + if ( percentage > 100 ) percentage = 100; + if ( percentage < 1 ) percentage = 1; + + if ( !values.isEmpty() ) + { + // entry exists, increment playcounter and update accesstime + score = ( ( values[2].toDouble() * values.first().toInt() ) + percentage ) / ( values.first().toInt() + 1 ); + + if (m_dbConnPool->getDbConnectionType() == DbConnection::postgresql) { + query( QString( "UPDATE statistics SET percentage=%1, playcounter=%2+1 WHERE url='%3';" ) + .arg( score ) + .arg( values[0] + " + 1" ) + .arg( escapeString( url ) ) ); + } + else + { + query( QString( "REPLACE INTO statistics ( url, createdate, accessdate, percentage, playcounter ) " + "VALUES ( '%1', %2, %3, %4, %5 );" ) + .arg( escapeString( url ) ) + .arg( values[1] ) + .arg( QDateTime::currentDateTime().toTime_t() ) + .arg( score ) + .arg( values[0] + " + 1" ) ); + } + } + else + { + // entry didn't exist yet, create a new one + score = ( ( 50 + percentage ) / 2 ); + + insert( QString( "INSERT INTO statistics ( url, createdate, accessdate, percentage, playcounter ) " + "VALUES ( '%1', %2, %3, %4, 1 );" ) + .arg( escapeString( url ) ) + .arg( QDateTime::currentDateTime().toTime_t() ) + .arg( QDateTime::currentDateTime().toTime_t() ) + .arg( score ), NULL ); + } + + int iscore = getSongPercentage( url ); + emit scoreChanged( url, iscore ); + return iscore; +} + + +int +CollectionDB::getSongPercentage( const QString &url ) +{ + QStringList values = query( QString( "SELECT round( percentage + 0.4 ) FROM statistics WHERE url = '%1';" ) + .arg( escapeString( url ) ) ); + + if( values.count() ) + return values.first().toInt(); + + return 0; +} + + +void +CollectionDB::setSongPercentage( const QString &url , int percentage ) +{ + QStringList values = + query( QString( + "SELECT playcounter, createdate, accessdate FROM statistics WHERE url = '%1';" ) + .arg( escapeString( url ) ) ); + + // check boundaries + if ( percentage > 100 ) percentage = 100; + if ( percentage < 1 ) percentage = 1; + + if ( !values.isEmpty() ) + { + if (m_dbConnPool->getDbConnectionType() == DbConnection::postgresql) { + query( QString( "UPDATE statistics SET percentage=%1 WHERE url='%2';" ) + .arg( percentage ) + .arg( escapeString( url ) ) ); + } + else + { + // entry exists + query( QString( "REPLACE INTO statistics ( url, createdate, accessdate, percentage, playcounter ) " + "VALUES ( '%1', '%2', '%3', %4, %5 );" ) + .arg( escapeString( url ) ) + .arg( values[1] ) + .arg( values[2] ) + .arg( percentage ) + .arg( values[0] ) ); + } + } + else + { + insert( QString( "INSERT INTO statistics ( url, createdate, accessdate, percentage, playcounter ) " + "VALUES ( '%1', %2, %3, %4, 0 );" ) + .arg( escapeString( url ) ) + .arg( QDateTime::currentDateTime().toTime_t() ) + .arg( QDateTime::currentDateTime().toTime_t() ) + .arg( percentage ), NULL ); + } + + emit scoreChanged( url, percentage ); +} + + +void +CollectionDB::updateDirStats( QString path, const long datetime, DbConnection *conn ) +{ + if ( path.endsWith( "/" ) ) + path = path.left( path.length() - 1 ); + + if (m_dbConnPool->getDbConnectionType() == DbConnection::postgresql) { + query( QString( "UPDATE directories%1 SET changedate=%2 WHERE dir='%3';") + .arg( conn ? "_temp" : "" ) + .arg( datetime ) + .arg( escapeString( path ) ), conn ); + } + else + { + query( QString( "REPLACE INTO directories%1 ( dir, changedate ) VALUES ( '%3', %2 );" ) + .arg( conn ? "_temp" : "" ) + .arg( datetime ) + .arg( escapeString( path ) ), + conn ); + } +} + + +void +CollectionDB::removeSongsInDir( QString path ) +{ + if ( path.endsWith( "/" ) ) + path = path.left( path.length() - 1 ); + + query( QString( "DELETE FROM tags WHERE dir = '%1';" ) + .arg( escapeString( path ) ) ); +} + + +bool +CollectionDB::isDirInCollection( QString path ) +{ + if ( path.endsWith( "/" ) ) + path = path.left( path.length() - 1 ); + + QStringList values = + query( QString( "SELECT changedate FROM directories WHERE dir = '%1';" ) + .arg( escapeString( path ) ) ); + + return !values.isEmpty(); +} + + +bool +CollectionDB::isFileInCollection( const QString &url ) +{ + QStringList values = + query( QString( "SELECT url FROM tags WHERE url = '%1';" ) + .arg( escapeString( url ) ) ); + + return !values.isEmpty(); +} + + +void +CollectionDB::removeSongs( const KURL::List& urls ) +{ + for( KURL::List::ConstIterator it = urls.begin(), end = urls.end(); it != end; ++it ) + { + query( QString( "DELETE FROM tags WHERE url = '%1';" ) + .arg( escapeString( (*it).path() ) ) ); + } +} + + +QStringList +CollectionDB::similarArtists( const QString &artist, uint count ) +{ + QStringList values; + + if (m_dbConnPool->getDbConnectionType() == DbConnection::postgresql) { + values = query( QString( "SELECT suggestion FROM related_artists WHERE artist = '%1' OFFSET 0 LIMIT %2;" ) + .arg( escapeString( artist ) ).arg( count ) ); + } + else + { + values = query( QString( "SELECT suggestion FROM related_artists WHERE artist = '%1' LIMIT 0, %2;" ) + .arg( escapeString( artist ) ).arg( count ) ); + } + + if ( values.isEmpty() ) + Scrobbler::instance()->similarArtists( artist ); + + return values; +} + + +void +CollectionDB::checkCompilations( const QString &path, const bool temporary, DbConnection *conn ) +{ + QStringList albums; + QStringList artists; + QStringList dirs; + + albums = query( QString( "SELECT DISTINCT album.name FROM tags_temp, album%1 AS album WHERE tags_temp.dir = '%2' AND album.id = tags_temp.album;" ) + .arg( temporary ? "_temp" : "" ) + .arg( escapeString( path ) ), conn ); + + for ( uint i = 0; i < albums.count(); i++ ) + { + if ( albums[ i ].isEmpty() ) continue; + + const uint album_id = albumID( albums[ i ], false, temporary, false, conn ); + artists = query( QString( "SELECT DISTINCT artist.name FROM tags_temp, artist%1 AS artist WHERE tags_temp.album = '%2' AND tags_temp.artist = artist.id;" ) + .arg( temporary ? "_temp" : "" ) + .arg( album_id ), conn ); + dirs = query( QString( "SELECT DISTINCT dir FROM tags_temp WHERE album = '%1';" ) + .arg( album_id ), conn ); + + if ( artists.count() > dirs.count() ) + { + debug() << "Detected compilation: " << albums[ i ] << " - " << artists.count() << ":" << dirs.count() << endl; + query( QString( "UPDATE tags_temp SET sampler = %1 WHERE album = '%2';" ) + .arg(boolT()).arg( album_id ), conn ); + } + } +} + + +void +CollectionDB::setCompilation( const QString &album, const bool enabled, const bool updateView ) +{ + query( QString( "UPDATE tags, album SET tags.sampler = %1 WHERE tags.album = album.id AND album.name = '%2';" ) + .arg( enabled ? "1" : "0" ) + .arg( escapeString( album ) ) ); + + // Update the Collection-Browser view, + // using QTimer to make sure we don't manipulate the GUI from a thread + if ( updateView ) + QTimer::singleShot( 0, CollectionView::instance(), SLOT( renderView() ) ); +} + + +void +CollectionDB::removeDirFromCollection( QString path ) +{ + if ( path.endsWith( "/" ) ) + path = path.left( path.length() - 1 ); + + query( QString( "DELETE FROM directories WHERE dir = '%1';" ) + .arg( escapeString( path ) ) ); +} + + +void +CollectionDB::updateTags( const QString &url, const MetaBundle &bundle, const bool updateView ) +{ + QString command = "UPDATE tags SET "; + command += "title = '" + escapeString( bundle.title() ) + "', "; + command += "artist = " + QString::number( artistID( bundle.artist(), true, false, true ) ) + ", "; + command += "album = " + QString::number( albumID( bundle.album(), true, false, true ) ) + ", "; + command += "genre = " + QString::number( genreID( bundle.genre(), true, false, true ) ) + ", "; + command += "year = " + QString::number( yearID( bundle.year(), true, false, true ) ) + ", "; + if ( !bundle.track().isEmpty() ) + command += "track = " + bundle.track() + ", "; + command += "comment = '" + escapeString( bundle.comment() ) + "' "; + command += "WHERE url = '" + escapeString( url ) + "';"; + + query( command ); + + if ( EngineController::instance()->bundle().url() == bundle.url() ) + { + debug() << "Current song edited, updating widgets: " << bundle.title() << endl; + EngineController::instance()->currentTrackMetaDataChanged( bundle ); + } + + // Update the Collection-Browser view, + // using QTimer to make sure we don't manipulate the GUI from a thread + if ( updateView ) + QTimer::singleShot( 0, CollectionView::instance(), SLOT( renderView() ) ); +} + + +void +CollectionDB::updateURL( const QString &url, const bool updateView ) +{ + // don't use the KURL ctor as it checks the db first + MetaBundle bundle; + bundle.setPath( url ); + bundle.readTags( TagLib::AudioProperties::Fast ); + + updateTags( url, bundle, updateView ); +} + + +void +CollectionDB::applySettings() +{ + bool recreateConnections = false; + if ( AmarokConfig::databaseEngine().toInt() != m_dbConnPool->getDbConnectionType() ) + { + recreateConnections = true; + } + else if ( AmarokConfig::databaseEngine().toInt() == DbConnection::mysql ) + { + // Using MySQL, so check if MySQL settings were changed + const MySqlConfig *config = + static_cast ( m_dbConnPool->getDbConfig() ); + if ( AmarokConfig::mySqlHost() != config->host() ) + { + recreateConnections = true; + } + else if ( AmarokConfig::mySqlPort() != config->port() ) + { + recreateConnections = true; + } + else if ( AmarokConfig::mySqlDbName() != config->database() ) + { + recreateConnections = true; + } + else if ( AmarokConfig::mySqlUser() != config->username() ) + { + recreateConnections = true; + } + else if ( AmarokConfig::mySqlPassword() != config->password() ) + { + recreateConnections = true; + } + } + else if ( AmarokConfig::databaseEngine().toInt() == DbConnection::postgresql ) + { + const PostgresqlConfig *config = + static_cast ( m_dbConnPool->getDbConfig() ); + if ( AmarokConfig::postgresqlConninfo() != config->conninfo() ) + { + recreateConnections = true; + } + } + if ( recreateConnections ) + { + debug() + << "Database engine settings changed: " + << "recreating DbConnections" << endl; + // If Database engine was changed, recreate DbConnections. + destroy(); + initialize(); + CollectionView::instance()->renderView(); + emit databaseEngineChanged(); + } +} + + +////////////////////////////////////////////////////////////////////////////////////////// +// PROTECTED +////////////////////////////////////////////////////////////////////////////////////////// + +QCString +CollectionDB::md5sum( const QString& artist, const QString& album, const QString& file ) +{ + KMD5 context( artist.lower().local8Bit() + album.lower().local8Bit() + file.local8Bit() ); +// debug() << "MD5 SUM for " << artist << ", " << album << ": " << context.hexDigest() << endl; + return context.hexDigest(); +} + + +void CollectionDB::engineTrackEnded( int finalPosition, int trackLength ) +{ + //This is where percentages are calculated + //TODO statistics are not calculated when currentTrack doesn't exist + + // Don't update statistics if song has been played for less than 15 seconds + // if ( finalPosition < 15000 ) return; + + const KURL url = EngineController::instance()->bundle().url(); + if ( url.path().isEmpty() ) return; + + // sanity check + if ( finalPosition > trackLength || finalPosition <= 0 ) + finalPosition = trackLength; + + int pct = (int) ( ( (double) finalPosition / (double) trackLength ) * 100 ); + + // increase song counter & calculate new statistics + addSongPercentage( url.path(), pct ); +} + + +void +CollectionDB::timerEvent( QTimerEvent* ) +{ + if ( AmarokConfig::monitorChanges() ) + scanMonitor(); +} + + +////////////////////////////////////////////////////////////////////////////////////////// +// PUBLIC SLOTS +////////////////////////////////////////////////////////////////////////////////////////// + +void +CollectionDB::fetchCover( QWidget* parent, const QString& artist, const QString& album, bool noedit ) //SLOT +{ + #ifdef AMAZON_SUPPORT + debug() << "Fetching cover for " << artist << " - " << album << endl; + + CoverFetcher* fetcher = new CoverFetcher( parent, artist, album ); + connect( fetcher, SIGNAL(result( CoverFetcher* )), SLOT(coverFetcherResult( CoverFetcher* )) ); + fetcher->setUserCanEditQuery( !noedit ); + fetcher->startFetch(); + #endif +} + + +void +CollectionDB::scanMonitor() //SLOT +{ + scanModifiedDirs(); +} + + +void +CollectionDB::startScan() //SLOT +{ + QStringList folders = MountPointManager::instance()->collectionFolders(); + + if ( folders.isEmpty() ) { + dropTables(); + createTables(); + } + else if( PlaylistBrowser::instance() ) + { + emit scanStarted(); + + ThreadWeaver::instance()->queueJob( new CollectionReader( this, folders ) ); + } +} + + +void +CollectionDB::stopScan() //SLOT +{ + ThreadWeaver::instance()->abortAllJobsNamed( "CollectionReader" ); +} + + +////////////////////////////////////////////////////////////////////////////////////////// +// PRIVATE SLOTS +////////////////////////////////////////////////////////////////////////////////////////// + +void +CollectionDB::dirDirty( const QString& path ) +{ + debug() << k_funcinfo << "Dirty: " << path << endl; + + ThreadWeaver::instance()->queueJob( new CollectionReader( this, path ) ); +} + + +void +CollectionDB::coverFetcherResult( CoverFetcher *fetcher ) +{ + if( fetcher->wasError() ) { + error() << fetcher->errors() << endl; + emit coverFetcherError( fetcher->errors().front() ); + } + + else { + setAlbumImage( fetcher->artist(), fetcher->album(), fetcher->image(), fetcher->amazonURL() ); + emit coverFetched( fetcher->artist(), fetcher->album() ); + } +} + +/** + * This query is fairly slow with sqlite, and often happens just + * after the OSD is shown. Threading it restores responsivity. + */ +class SimilarArtistsInsertionJob : public ThreadWeaver::DependentJob +{ + virtual bool doJob() + { + CollectionDB::instance()->query( QString( "DELETE FROM related_artists WHERE artist = '%1';" ).arg( escapedArtist ) ); + + const QString sql = "INSERT INTO related_artists ( artist, suggestion, changedate ) VALUES ( '%1', '%2', 0 );"; + foreach( suggestions ) + CollectionDB::instance()->insert( sql + .arg( escapedArtist ) + .arg( CollectionDB::instance()->escapeString( *it ) ), NULL ); + + return true; + } + + virtual void completeJob() { emit CollectionDB::instance()->similarArtistsFetched( artist ); } + + const QString artist; + const QString escapedArtist; + const QStringList suggestions; + +public: + SimilarArtistsInsertionJob( CollectionDB *parent, const QString &s, const QStringList &list ) + : ThreadWeaver::DependentJob( parent, "SimilarArtistsInsertionJob" ) + , artist( s ) + , escapedArtist( parent->escapeString( s ) ) + , suggestions( list ) + {} +}; + +void +CollectionDB::similarArtistsFetched( const QString& artist, const QStringList& suggestions ) +{ + debug() << "Received similar artists\n"; + + ThreadWeaver::instance()->queueJob( new SimilarArtistsInsertionJob( this, artist, suggestions ) ); +} + + +////////////////////////////////////////////////////////////////////////////////////////// +// PRIVATE +////////////////////////////////////////////////////////////////////////////////////////// + + +void +CollectionDB::initialize() +{ + m_dbConnPool = new DbConnectionPool(); + DbConnection *dbConn = m_dbConnPool->getDbConnection(); + m_dbConnPool->putDbConnection( dbConn ); + + KConfig* config = amaroK::config( "Collection Browser" ); + if(!dbConn->isConnected()) + amaroK::MessageQueue::instance()->addMessage(dbConn->lastError()); + if ( !dbConn->isInitialized() || !isValid() ) + { + createTables(); + createStatsTable(); + } + else + { + //remove database file if version is incompatible + if ( config->readNumEntry( "Database Version", 0 ) != DATABASE_VERSION ) + { + debug() << "Rebuilding database!" << endl; + dropTables(); + createTables(); + } + if ( config->readNumEntry( "Database Stats Version", 0 ) != DATABASE_STATS_VERSION ) + { + debug() << "Rebuilding stats-database!" << endl; + dropStatsTable(); + createStatsTable(); + } + } + + m_dbConnPool->createDbConnections(); +} + + +void +CollectionDB::destroy() +{ + delete m_dbConnPool; +} + + +void +CollectionDB::scanModifiedDirs() +{ + //we check if a job is pending because we don't want to abort incremental collection readings + if ( !ThreadWeaver::instance()->isJobPending( "CollectionReader" ) && PlaylistBrowser::instance() ) { + emit scanStarted(); + ThreadWeaver::instance()->onlyOneJob( new IncrementalCollectionReader( this ) ); + } +} + + +void +CollectionDB::customEvent( QCustomEvent *e ) +{ + if ( e->type() == (int)CollectionReader::JobFinishedEvent ) + emit scanDone( static_cast(e)->wasSuccessful() ); +} + + +////////////////////////////////////////////////////////////////////////////////////////// +// CLASS DbConnectionPool +////////////////////////////////////////////////////////////////////////////////////////// + +DbConnectionPool::DbConnectionPool() : m_semaphore( POOL_SIZE ) +{ +#ifdef USE_MYSQL + if ( AmarokConfig::databaseEngine().toInt() == DbConnection::mysql ) + m_dbConnType = DbConnection::mysql; + else +#endif +#ifdef USE_POSTGRESQL + if ( AmarokConfig::databaseEngine().toInt() == DbConnection::postgresql ) + m_dbConnType = DbConnection::postgresql; + else +#endif + m_dbConnType = DbConnection::sqlite; + + m_semaphore += POOL_SIZE; + DbConnection *dbConn; + +#ifdef USE_MYSQL + if ( m_dbConnType == DbConnection::mysql ) + { + m_dbConfig = + new MySqlConfig( + AmarokConfig::mySqlHost(), + AmarokConfig::mySqlPort(), + AmarokConfig::mySqlDbName(), + AmarokConfig::mySqlUser(), + AmarokConfig::mySqlPassword() ); + dbConn = new MySqlConnection( static_cast ( m_dbConfig ) ); + } + else +#endif +#ifdef USE_POSTGRESQL + if ( m_dbConnType == DbConnection::postgresql ) + { + m_dbConfig = + new PostgresqlConfig( + AmarokConfig::postgresqlConninfo() ); + dbConn = new PostgresqlConnection( static_cast ( m_dbConfig ) ); + } + else +#endif + { + m_dbConfig = new SqliteConfig( "collection.db" ); + dbConn = new SqliteConnection( static_cast ( m_dbConfig ) ); + } + enqueue( dbConn ); + m_semaphore--; + debug() << "Available db connections: " << m_semaphore.available() << endl; +} + + +DbConnectionPool::~DbConnectionPool() +{ + m_semaphore += POOL_SIZE; + DbConnection *conn; + bool vacuum = true; + + while ( ( conn = dequeue() ) != 0 ) + { + if ( m_dbConnType == DbConnection::sqlite && vacuum ) + { + vacuum = false; + debug() << "Running VACUUM" << endl; + conn->query( "VACUUM; "); + } + + delete conn; + } + + delete m_dbConfig; +} + + +void DbConnectionPool::createDbConnections() +{ + for ( int i = 0; i < POOL_SIZE - 1; i++ ) + { + DbConnection *dbConn; + +#ifdef USE_MYSQL + if ( m_dbConnType == DbConnection::mysql ) + dbConn = new MySqlConnection( static_cast ( m_dbConfig ) ); + else +#endif +#ifdef USE_POSTGRESQL + if ( m_dbConnType == DbConnection::postgresql ) + dbConn = new PostgresqlConnection( static_cast ( m_dbConfig ) ); + else +#endif + dbConn = new SqliteConnection( static_cast ( m_dbConfig ) ); + enqueue( dbConn ); + m_semaphore--; + } + debug() << "Available db connections: " << m_semaphore.available() << endl; +} + + +DbConnection *DbConnectionPool::getDbConnection() +{ + m_semaphore++; + return dequeue(); +} + + +void DbConnectionPool::putDbConnection( const DbConnection *conn ) +{ + enqueue( conn ); + m_semaphore--; +} + + +#include "collectiondb.moc" diff --git a/amarok/src/database_refactor/collectiondb.h b/amarok/src/database_refactor/collectiondb.h new file mode 100644 index 00000000..7d0f6d12 --- /dev/null +++ b/amarok/src/database_refactor/collectiondb.h @@ -0,0 +1,239 @@ +// (c) 2004 Mark Kretschmann +// (c) 2004 Christian Muehlhaeuser +// (c) 2004 Sami Nieminen +// See COPYING file for licensing information. + +#ifndef AMAROK_COLLECTIONDB_H +#define AMAROK_COLLECTIONDB_H + +#include "engineobserver.h" +#include "dbenginebase.h" +#include +#include //stack allocated +#include //baseclass +#include //baseclass +#include //stack allocated +#include //stack allocated + +class CoverFetcher; +class MetaBundle; +class Scrobbler; + + +class DbConnectionPool : QPtrQueue +{ + public: + DbConnectionPool(); + ~DbConnectionPool(); + + const DbConnection::DbConnectionType getDbConnectionType() const { return m_dbConnType; } + const DbConfig *getDbConfig() const { return m_dbConfig; } + void createDbConnections(); + + DbConnection *getDbConnection(); + void putDbConnection( const DbConnection* /* conn */ ); + + private: + static const int POOL_SIZE = 5; + QSemaphore m_semaphore; + DbConnection::DbConnectionType m_dbConnType; + DbConfig *m_dbConfig; +}; + + +class CollectionDB : public QObject, public EngineObserver +{ + Q_OBJECT + + friend class SimilarArtistsInsertionJob; + + signals: + void scanStarted(); + void scanDone( bool changed ); + void databaseEngineChanged(); + + void scoreChanged( const QString &url, int score ); + + void coverFetched( const QString &artist, const QString &album ); + void coverRemoved( const QString &artist, const QString &album ); + void coverFetcherError( const QString &error ); + + void similarArtistsFetched( const QString &artist ); + + public: + static CollectionDB *instance(); + + QString escapeString( QString string ) { return m_dbConnPool->escapeString(string); } + int getType() { return m_dbConnPool->getDbConnectionType(); } + + + /** + * This method returns a static DbConnection for components that want to use + * the same connection for the whole time. Should not be used anywhere else + * but in CollectionReader. + * + * @return static DbConnection + */ + DbConnection *getStaticDbConnection(); + + /** + * Returns the DbConnection back to connection pool. + * + * @param conn DbConnection to be returned + */ + void returnStaticDbConnection( DbConnection *conn ); + + //sql helper methods + QStringList query( const QString& statement, DbConnection *conn = NULL ); + int insert( const QString& statement, const QString& table, DbConnection *conn = NULL ); + + //table management methods + bool isEmpty(); + bool isValid(); + void createTables( DbConnection *conn = NULL ); + void dropTables( DbConnection *conn = NULL ); + void clearTables( DbConnection *conn = NULL ); + void moveTempTables( DbConnection *conn ); + + uint artistID( QString value, bool autocreate = true, const bool temporary = false, const bool updateSpelling = false, DbConnection *conn = NULL ); + uint albumID( QString value, bool autocreate = true, const bool temporary = false, const bool updateSpelling = false, DbConnection *conn = NULL ); + uint genreID( QString value, bool autocreate = true, const bool temporary = false, const bool updateSpelling = false, DbConnection *conn = NULL ); + uint yearID( QString value, bool autocreate = true, const bool temporary = false, const bool updateSpelling = false, DbConnection *conn = NULL ); + + bool isDirInCollection( QString path ); + bool isFileInCollection( const QString &url ); + void removeDirFromCollection( QString path ); + void removeSongsInDir( QString path ); + void removeSongs( const KURL::List& urls ); + void updateDirStats( QString path, const long datetime, DbConnection *conn = NULL ); + + //song methods + bool addSong( MetaBundle* bundle, const bool incremental = false, DbConnection *conn = NULL ); + + /** + * The @p bundle parameter's url() will be looked up in the Collection + * @param bundle this will be filled in with tags for you + * @return true if in the collection + */ + bool bundleForUrl( MetaBundle* bundle ); + QValueList bundlesByUrls( const KURL::List& urls ); + void addAudioproperties( const MetaBundle& bundle ); + + void updateTags( const QString &url, const MetaBundle &bundle, const bool updateView = true ); + void updateURL( const QString &url, const bool updateView = true ); + + //statistics methods + int addSongPercentage( const QString &url, int percentage ); + int getSongPercentage( const QString &url ); + void setSongPercentage( const QString &url , int percentage ); + + //artist methods + QStringList similarArtists( const QString &artist, uint count ); + + //album methods + void checkCompilations( const QString &path, const bool temporary = false, DbConnection *conn = NULL ); + void setCompilation( const QString &album, const bool enabled, const bool updateView = true ); + QString albumSongCount( const QString &artist_id, const QString &album_id ); + bool albumIsCompilation( const QString &album_id ); + + //list methods + QStringList artistList( bool withUnknowns = true, bool withCompilations = true ); + QStringList albumList( bool withUnknowns = true, bool withCompilations = true ); + QStringList genreList( bool withUnknowns = true, bool withCompilations = true ); + QStringList yearList( bool withUnknowns = true, bool withCompilations = true ); + + QStringList albumListOfArtist( const QString &artist, bool withUnknown = true, bool withCompilations = true ); + QStringList artistAlbumList( bool withUnknown = true, bool withCompilations = true ); + + QStringList albumTracks( const QString &artist_id, const QString &album_id ); + + //cover management methods + /** Returns the image from a given URL, network-transparently. + * You must run KIO::NetAccess::removeTempFile( tmpFile ) when you are finished using the image; + **/ + static QImage fetchImage(const KURL& url, QString &tmpFile); + /** Saves images located on the user's filesystem */ + bool setAlbumImage( const QString& artist, const QString& album, const KURL& url ); + /** Saves images obtained from CoverFetcher */ + bool setAlbumImage( const QString& artist, const QString& album, QImage img, const QString& amazonUrl = QString::null ); + + QString findImageByMetabundle( MetaBundle trackInformation, const uint = 1 ); + QString findImageByArtistAlbum( const QString &artist, const QString &album, const uint width = 1 ); + QString albumImage( MetaBundle trackInformation, const uint width = 1 ); + QString albumImage( const uint artist_id, const uint album_id, const uint width = 1 ); + QString albumImage( const QString &artist, const QString &album, const uint width = 1 ); + + bool removeAlbumImage( const uint artist_id, const uint album_id ); + bool removeAlbumImage( const QString &artist, const QString &album ); + + //local cover methods + void addImageToAlbum( const QString& image, QValueList< QPair > info, DbConnection *conn = NULL ); + QString getImageForAlbum( const QString& artist, const QString& album, uint width = 0 ); + QString notAvailCover( int width = 0 ); + + void applySettings(); + + protected: + CollectionDB(); + ~CollectionDB(); + + QCString md5sum( const QString& artist, const QString& album, const QString& file = QString::null ); + void engineTrackEnded( int finalPosition, int trackLength ); + /** Manages regular folder monitoring scan */ + void timerEvent( QTimerEvent* e ); + + public slots: + void fetchCover( QWidget* parent, const QString& artist, const QString& album, bool noedit ); + void scanMonitor(); + void startScan(); + void stopScan(); + + private slots: + void dirDirty( const QString& path ); + void coverFetcherResult( CoverFetcher* ); + void similarArtistsFetched( const QString& artist, const QStringList& suggestions ); + + private: + //bump DATABASE_VERSION whenever changes to the table structure are made. will remove old db file. + static const int DATABASE_VERSION = 18; + static const int DATABASE_STATS_VERSION = 3; + static const int MONITOR_INTERVAL = 60; //sec + static const bool DEBUG = false; + + void initialize(); + void destroy(); + void customEvent( QCustomEvent* ); + + //general management methods + void createStatsTable(); + void dropStatsTable(); + void scanModifiedDirs(); + + QCString makeWidthKey( uint width ); + QString artistValue( uint id ); + QString albumValue( uint id ); + QString genreValue( uint id ); + QString yearValue( uint id ); + + uint IDFromValue( QString name, QString value, bool autocreate = true, const bool temporary = false, + const bool updateSpelling = false, DbConnection *conn = NULL ); + + QString valueFromID( QString table, uint id ); + + //member variables + QString m_amazonLicense; + QString m_cacheArtist; + uint m_cacheArtistID; + QString m_cacheAlbum; + uint m_cacheAlbumID; + + DBEngine* m_dbEngine; + DbConnectionPool *m_dbConnPool; + + bool m_monitor; + QDir m_cacheDir; + QDir m_coverDir; +}; + + +#endif /* AMAROK_COLLECTIONDB_H */ diff --git a/amarok/src/database_refactor/dbenginebase.cpp b/amarok/src/database_refactor/dbenginebase.cpp new file mode 100644 index 00000000..42f40366 --- /dev/null +++ b/amarok/src/database_refactor/dbenginebase.cpp @@ -0,0 +1,544 @@ +/*************************************************************************** + * Copyright (C) 2004-2005 Mark Kretschmann * + * 2004 Christian Muehlhaeuser * + * 2004 Sami Nieminen * + * 2005 Ian Monroe * + * * + * 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 program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#include +#include + +#include + + +////////////////////////////////////////////////////////////////////////////////////////// +// CLASS DBConnection +////////////////////////////////////////////////////////////////////////////////////////// + +DbConnection::DbConnection( DbConfig* config ) + : m_config( config ) +{} + + +DbConnection::~DbConnection() +{} + + +////////////////////////////////////////////////////////////////////////////////////////// +// CLASS QueryBuilder +////////////////////////////////////////////////////////////////////////////////////////// + +QueryBuilder::QueryBuilder() +{ + clear(); +} + + +void +QueryBuilder::linkTables( int tables ) +{ + + m_tables = tableName( tabSong ); + + if ( !(tables & tabSong ) ) + { + // check if only one table is selected (does somebody know a better way to check that?) + if (tables == tabAlbum || tables==tabArtist || tables==tabGenre || tables == tabYear || tables == tabStats) + m_tables = tableName(tables); + else + tables |= tabSong; + } + + + if ( tables & tabSong ) + { + if ( tables & tabAlbum ) + m_tables += " INNER JOIN " + tableName( tabAlbum) + " ON album.id=tags.album"; + if ( tables & tabArtist ) + m_tables += " INNER JOIN " + tableName( tabArtist) + " ON artist.id=tags.artist"; + if ( tables & tabGenre ) + m_tables += " INNER JOIN " + tableName( tabGenre) + " ON genre.id=tags.genre"; + if ( tables & tabYear ) + m_tables += " INNER JOIN " + tableName( tabYear) + " ON year.id=tags.year"; + if ( tables & tabStats ) + m_tables += " INNER JOIN " + tableName( tabStats) + " ON statistics.url=tags.url"; + } +} + + +void +QueryBuilder::addReturnValue( int table, int value ) +{ + if ( !m_values.isEmpty() && m_values != "DISTINCT " ) m_values += ","; + if ( table & tabStats && value & valScore ) m_values += "round("; + + if ( value == valDummy ) + m_values += "''"; + else + { + m_values += tableName( table ) + "."; + m_values += valueName( value ); + } + + if ( table & tabStats && value & valScore ) m_values += " + 0.4 )"; + + m_linkTables |= table; + m_returnValues++; +} + +void +QueryBuilder::addReturnFunctionValue( int function, int table, int value) +{ + if ( !m_values.isEmpty() && m_values != "DISTINCT " ) m_values += ","; + m_values += functionName( function ) + "("; + m_values += tableName( table ) + "."; + m_values += valueName( value )+ ")"; + m_values += " AS "; + m_values += functionName( function )+tableName( table )+valueName( value ); + + m_linkTables |= table; + m_returnValues++; +} + +uint +QueryBuilder::countReturnValues() +{ + return m_returnValues; +} + + +void +QueryBuilder::addURLFilters( const QStringList& filter ) +{ + if ( !filter.isEmpty() ) + { + m_where += "AND ( true "; + + for ( uint i = 0; i < filter.count(); i++ ) + { + m_where += "OR tags.url = '" + escapeString( filter[i] ) + "' "; + } + + m_where += " ) "; + } + + m_linkTables |= tabSong; +} + + +void +QueryBuilder::addFilter( int tables, const QString& filter, int /*mode*/ ) +{ + if ( !filter.isEmpty() ) + { + m_where += "AND ( true "; + if ( tables & tabAlbum ) m_where += "OR album.name LIKE '%" + escapeString( filter ) + "%' "; + if ( tables & tabArtist ) m_where += "OR artist.name LIKE '%" + escapeString( filter ) + "%' "; + if ( tables & tabGenre ) m_where += "OR genre.name LIKE '%" + escapeString( filter ) + "%' "; + if ( tables & tabYear ) m_where += "OR year.name LIKE '%" + escapeString( filter ) + "%' "; + if ( tables & tabSong ) m_where += "OR tags.title LIKE '%" + escapeString( filter ) + "%' "; + m_where += " ) "; + } + + m_linkTables |= tables; +} + + +void +QueryBuilder::addFilters( int tables, const QStringList& filter ) +{ + if ( !filter.isEmpty() ) + { + m_where += "AND ( true "; + + for ( uint i = 0; i < filter.count(); i++ ) + { + m_where += " AND ( true "; + if ( tables & tabAlbum ) m_where += "OR album.name LIKE '%" + escapeString( filter[i] ) + "%' "; + if ( tables & tabArtist ) m_where += "OR artist.name LIKE '%" + escapeString( filter[i] ) + "%' "; + if ( tables & tabGenre ) m_where += "OR genre.name LIKE '%" + escapeString( filter[i] ) + "%' "; + if ( tables & tabYear ) m_where += "OR year.name LIKE '%" + escapeString( filter[i] ) + "%' "; + if ( tables & tabSong ) m_where += "OR tags.title LIKE '%" + escapeString( filter[i] ) + "%' "; + m_where += " ) "; + } + + m_where += " ) "; + } + + m_linkTables |= tables; +} + + +void +QueryBuilder::addMatch( int tables, const QString& match ) +{ + if ( !match.isEmpty() ) + { + m_where += "AND ( true "; + if ( tables & tabAlbum ) m_where += "OR album.name LIKE '" + escapeString( match ) + "' "; + if ( tables & tabArtist ) m_where += "OR artist.name LIKE '" + escapeString( match ) + "' "; + if ( tables & tabGenre ) m_where += "OR genre.name LIKE '" + escapeString( match ) + "' "; + if ( tables & tabYear ) m_where += "OR year.name LIKE '" + escapeString( match ) + "' "; + if ( tables & tabSong ) m_where += "OR tags.title LIKE '" + escapeString( match ) + "' "; + + if ( match == i18n( "Unknown" ) ) + { + if ( tables & tabAlbum ) m_where += "OR album.name = '' "; + if ( tables & tabArtist ) m_where += "OR artist.name = '' "; + if ( tables & tabGenre ) m_where += "OR genre.name = '' "; + if ( tables & tabYear ) m_where += "OR year.name = '' "; + } + m_where += " ) "; + } + + m_linkTables |= tables; +} + + +void +QueryBuilder::addMatch( int tables, int value, const QString& match ) +{ + if ( !match.isEmpty() ) + { + m_where += "AND ( true "; + m_where += QString( "OR %1.%2 LIKE '" ).arg( tableName( tables ) ).arg( valueName( value ) ) + escapeString( match ) + "' "; + + if ( ( value & valName ) && match == i18n( "Unknown" ) ) + m_where += QString( "OR %1.%2 = '' " ).arg( tableName( tables ) ).arg( valueName( value ) ); + + m_where += " ) "; + } + + m_linkTables |= tables; +} + + +void +QueryBuilder::addMatches( int tables, const QStringList& match ) +{ + if ( !match.isEmpty() ) + { + m_where += "AND ( true "; + + for ( uint i = 0; i < match.count(); i++ ) + { + if ( tables & tabAlbum ) m_where += "OR album.name LIKE '" + escapeString( match[i] ) + "' "; + if ( tables & tabArtist ) m_where += "OR artist.name LIKE '" + escapeString( match[i] ) + "' "; + if ( tables & tabGenre ) m_where += "OR genre.name LIKE '" + escapeString( match[i] ) + "' "; + if ( tables & tabYear ) m_where += "OR year.name LIKE '" + escapeString( match[i] ) + "' "; + if ( tables & tabSong ) m_where += "OR tags.title LIKE '" + escapeString( match[i] ) + "' "; + if ( tables & tabStats ) m_where += "OR statistics.url LIKE '" + escapeString( match[i] ) + "' "; + + if ( match[i] == i18n( "Unknown" ) ) + { + if ( tables & tabAlbum ) m_where += "OR album.name = '' "; + if ( tables & tabArtist ) m_where += "OR artist.name = '' "; + if ( tables & tabGenre ) m_where += "OR genre.name = '' "; + if ( tables & tabYear ) m_where += "OR year.name = '' "; + } + } + + m_where += " ) "; + } + + m_linkTables |= tables; +} + + +void +QueryBuilder::excludeFilter( int tables, const QString& filter ) +{ + if ( !filter.isEmpty() ) + { + m_where += "AND ( true "; + if ( tables & tabAlbum ) m_where += "AND album.name <> '%" + escapeString( filter ) + "%' "; + if ( tables & tabArtist ) m_where += "AND artist.name <> '%" + escapeString( filter ) + "%' "; + if ( tables & tabGenre ) m_where += "AND genre.name <> '%" + escapeString( filter ) + "%' "; + if ( tables & tabYear ) m_where += "AND year.name <> '%" + escapeString( filter ) + "%' "; + if ( tables & tabSong ) m_where += "AND tags.title <> '%" + escapeString( filter ) + "%' "; + m_where += " ) "; + } + + m_linkTables |= tables; +} + + +void +QueryBuilder::excludeMatch( int tables, const QString& match ) +{ + if ( !match.isEmpty() ) + { + m_where += "AND ( true "; + if ( tables & tabAlbum ) m_where += "AND album.name <> '" + escapeString( match ) + "' "; + if ( tables & tabArtist ) m_where += "AND artist.name <> '" + escapeString( match ) + "' "; + if ( tables & tabGenre ) m_where += "AND genre.name <> '" + escapeString( match ) + "' "; + if ( tables & tabYear ) m_where += "AND year.name <> '" + escapeString( match ) + "' "; + if ( tables & tabSong ) m_where += "AND tags.title <> '" + escapeString( match ) + "' "; + + if ( match == i18n( "Unknown" ) ) + { + if ( tables & tabAlbum ) m_where += "AND album.name <> '' "; + if ( tables & tabArtist ) m_where += "AND artist.name <> '' "; + if ( tables & tabGenre ) m_where += "AND genre.name <> '' "; + if ( tables & tabYear ) m_where += "AND year.name <> '' "; + } + m_where += " ) "; + } + + m_linkTables |= tables; +} + + +void +QueryBuilder::exclusiveFilter( int tableMatching, int tableNotMatching, int value ) +{ + m_join += " LEFT JOIN "; + m_join += tableName( tableNotMatching ); + m_join += " ON "; + + m_join += tableName( tableMatching ) + "."; + m_join += valueName( value ); + m_join+= " = "; + m_join += tableName( tableNotMatching ) + "."; + m_join += valueName( value ); + + m_where += " AND "; + m_where += tableName( tableNotMatching ) + "."; + m_where += valueName( value ); + m_where += " IS null "; +} + + +void +QueryBuilder::setOptions( int options ) +{ + if ( options & optNoCompilations || options & optOnlyCompilations ) + m_linkTables |= tabSong; + + if ( options & optNoCompilations ) m_where += "AND tags.sampler = 0 "; + if ( options & optOnlyCompilations ) m_where += "AND tags.sampler = 1 "; + + if ( options & optRemoveDuplicates ) m_values = "DISTINCT " + m_values; + if ( options & optRandomize ) + { + if ( !m_sort.isEmpty() ) m_sort += ","; + m_sort += "RAND() "; + } +} + + +void +QueryBuilder::sortBy( int table, int value, bool descending ) +{ + //shall we sort case-sensitively? (not for integer columns!) + bool b = true; + if ( value & valID || value & valTrack || value & valScore || value & valLength || value & valBitrate || + value & valSamplerate || value & valPlayCounter || value & valAccessDate || value & valCreateDate || value & valPercentage || + table & tabYear ) + b = false; + + if ( !m_sort.isEmpty() ) m_sort += ","; + if ( b ) m_sort += "LOWER( "; + if ( table & tabYear ) m_sort += "("; + + m_sort += tableName( table ) + "."; + m_sort += valueName( value ); + + if ( table & tabYear ) m_sort += "+0)"; + + if ( b ) m_sort += " ) "; + if ( descending ) m_sort += " DESC "; + + m_linkTables |= table; +} + +void +QueryBuilder::sortByFunction( int function, int table, int value, bool descending ) +{ + // This function should be used with the equivalent addReturnFunctionValue (with the same function on same values) + // since it uses the "func(table.value) AS functablevalue" definition. + + //shall we sort case-sensitively? (not for integer columns!) + bool b = true; + if ( value & valID || value & valTrack || value & valScore || value & valLength || value & valBitrate || + value & valSamplerate || value & valPlayCounter || value & valAccessDate || value & valCreateDate || value & valPercentage || + table & tabYear ) + b = false; + + if ( !m_sort.isEmpty() ) m_sort += ","; + //m_sort += functionName( function ) + "("; + if ( b ) m_sort += "LOWER( "; + if ( table & tabYear ) m_sort += "("; + + QString columnName = functionName( function )+tableName( table )+valueName( value ); + m_sort += columnName; + + if ( table & tabYear ) m_sort += "+0)"; + if ( b ) m_sort += " ) "; + //m_sort += " ) "; + if ( descending ) m_sort += " DESC "; + + m_linkTables |= table; +} + +void +QueryBuilder::groupBy( int table, int value ) +{ + if ( !m_group.isEmpty() ) m_group += ","; + m_group += tableName( table ) + "."; + m_group += valueName( value ); + + m_linkTables |= table; +} + + +void +QueryBuilder::setLimit( int startPos, int length ) +{ + m_limit = QString( " LIMIT %1, %2 " ).arg( startPos ).arg( length ); +} + + +void +QueryBuilder::initSQLDrag() +{ + clear(); + addReturnValue( QueryBuilder::tabAlbum, QueryBuilder::valName ); + addReturnValue( QueryBuilder::tabArtist, QueryBuilder::valName ); + addReturnValue( QueryBuilder::tabGenre, QueryBuilder::valName ); + addReturnValue( QueryBuilder::tabSong, QueryBuilder::valTitle ); + addReturnValue( QueryBuilder::tabYear, QueryBuilder::valName ); + addReturnValue( QueryBuilder::tabSong, QueryBuilder::valComment ); + addReturnValue( QueryBuilder::tabSong, QueryBuilder::valTrack ); + addReturnValue( QueryBuilder::tabSong, QueryBuilder::valBitrate ); + addReturnValue( QueryBuilder::tabSong, QueryBuilder::valLength ); + addReturnValue( QueryBuilder::tabSong, QueryBuilder::valSamplerate ); + addReturnValue( QueryBuilder::tabSong, QueryBuilder::valURL ); +} + + +void +QueryBuilder::buildQuery() +{ + if ( m_query.isEmpty() ) + { + linkTables( m_linkTables ); + + m_query = "SELECT " + m_values + " FROM " + m_tables + " " + m_join + " WHERE true " + m_where; + // GROUP BY must be before ORDER BY for sqlite + if ( !m_group.isEmpty() ) m_query += " GROUP BY " + m_group; + if ( !m_sort.isEmpty() ) m_query += " ORDER BY " + m_sort; + m_query += m_limit; + } +} + +// get the builded SQL-Query (used in smartplaylisteditor soon) +QString +QueryBuilder::getQuery() +{ + if ( m_query.isEmpty()) + { + buildQuery(); + } + return m_query; +} + +QStringList +QueryBuilder::run() +{ + buildQuery(); + //debug() << m_query << endl; +// return query( m_query ); +} + + +void +QueryBuilder::clear() +{ + m_query = ""; + m_values = ""; + m_tables = ""; + m_join = ""; + m_where = ""; + m_sort = ""; + m_group = ""; + m_limit = ""; + + m_linkTables = 0; + m_returnValues = 0; +} + + +QString +QueryBuilder::tableName( int table ) +{ + QString tables; + + if ( table & tabSong ) tables += ",tags"; + if ( table & tabArtist ) tables += ",artist"; + if ( table & tabAlbum ) tables += ",album"; + if ( table & tabGenre ) tables += ",genre"; + if ( table & tabYear ) tables += ",year"; + if ( table & tabStats ) tables += ",statistics"; + + // when there are multiple tables involved, we always need table tags for linking them + return tables.mid( 1 ); +} + + +QString +QueryBuilder::valueName( int value ) +{ + QString values; + + if ( value & valID ) values += "id"; + if ( value & valName ) values += "name"; + if ( value & valURL ) values += "url"; + if ( value & valTitle ) values += "title"; + if ( value & valTrack ) values += "track"; + if ( value & valScore ) values += "percentage"; + if ( value & valComment ) values += "comment"; + if ( value & valBitrate ) values += "bitrate"; + if ( value & valLength ) values += "length"; + if ( value & valSamplerate ) values += "samplerate"; + if ( value & valPlayCounter ) values += "playcounter"; + if ( value & valAccessDate ) values += "accessdate"; + if ( value & valCreateDate ) values += "createdate"; + if ( value & valPercentage ) values += "percentage"; + if ( value & valArtistID ) values += "artist"; + if ( value & valAlbumID ) values += "album"; + if ( value & valGenreID ) values += "genre"; + if ( value & valYearID ) values += "year"; + + return values; +} + +QString +QueryBuilder::functionName( int value ) +{ + QString function; + + if ( value & funcCount ) function += "Count"; + if ( value & funcMax ) function += "Max"; + if ( value & funcMin ) function += "Min"; + if ( value & funcAvg ) function += "Avg"; + if ( value & funcSum ) function += "Sum"; + + return function; +} + diff --git a/amarok/src/database_refactor/dbenginebase.h b/amarok/src/database_refactor/dbenginebase.h new file mode 100644 index 00000000..880a34bd --- /dev/null +++ b/amarok/src/database_refactor/dbenginebase.h @@ -0,0 +1,133 @@ +/*************************************************************************** + * Copyright (C) 2004-2005 Mark Kretschmann * + * 2004 Christian Muehlhaeuser * + * 2004 Sami Nieminen * + * 2005 Ian Monroe * + * * + * 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 program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#ifndef AMAROK_DBENGINEBASE_H +#define AMAROK_DBENGINEBASE_H + +#include "plugin/plugin.h" //baseclass +#include //baseclass + + +class DbConfig +{}; + +class DbConnection : public QObject, public amaroK::Plugin +{ + public: + enum DbConnectionType { sqlite = 0, mysql = 1, postgresql = 2 }; + + DbConnection( DbConfig* /* config */ ); + virtual ~DbConnection() = 0; + + virtual QStringList query( const QString& /* statement */ ) = 0; + virtual int insert( const QString& /* statement */, const QString& /* table */ ) = 0; + const bool isInitialized() const { return m_initialized; } + virtual bool isConnected() const = 0; + virtual const QString lastError() const { return "None"; } + + protected: + bool m_initialized; + DbConfig *m_config; +}; + + +class QueryBuilder +{ + public: + //attributes: + enum qBuilderTables { tabAlbum = 1, tabArtist = 2, tabGenre = 4, tabYear = 8, tabSong = 32, tabStats = 64, tabDummy = 0 }; + enum qBuilderOptions { optNoCompilations = 1, optOnlyCompilations = 2, optRemoveDuplicates = 4, optRandomize = 8 }; + enum qBuilderValues { valID = 1, valName = 2, valURL = 4, valTitle = 8, valTrack = 16, valScore = 32, valComment = 64, + valBitrate = 128, valLength = 256, valSamplerate = 512, valPlayCounter = 1024, + valCreateDate = 2048, valAccessDate = 4096, valPercentage = 8192, valArtistID = 16384, valAlbumID = 32768, + valYearID = 65536, valGenreID = 131072, valDummy = 0 }; + enum qBuilderFunctions { funcCount = 1, funcMax = 2, funcMin = 4, funcAvg = 8, funcSum = 16 }; + + enum qBuilderFilter { modeNormal = 0, modeFuzzy = 1 }; + + QueryBuilder(); + + QString escapeString( QString string ) + { + return + #ifdef USE_MYSQL + // We have to escape "\" for mysql, but can't do so for sqlite + (m_dbConnType == DbConnection::mysql) + ? string.replace("\\", "\\\\").replace( '\'', "''" ) + : + #endif + string.replace( '\'', "''" ); + } + + void addReturnValue( int table, int value ); + void addReturnFunctionValue( int function, int table, int value); + uint countReturnValues(); + + void addURLFilters( const QStringList& filter ); + + void addFilter( int tables, const QString& filter, int mode = modeNormal ); + void addFilters( int tables, const QStringList& filter ); + void excludeFilter( int tables, const QString& filter ); + + void addMatch( int tables, const QString& match ); + void addMatch( int tables, int value, const QString& match ); + void addMatches( int tables, const QStringList& match ); + void excludeMatch( int tables, const QString& match ); + + void exclusiveFilter( int tableMatching, int tableNotMatching, int value ); + + void setOptions( int options ); + void sortBy( int table, int value, bool descending = false ); + void sortByFunction( int function, int table, int value, bool descending = false ); + void groupBy( int table, int value ); + void setLimit( int startPos, int length ); + + void initSQLDrag(); + void buildQuery(); + QString getQuery(); + QString query() { buildQuery(); return m_query; }; + void clear(); + + QStringList run(); + + private: + QString tableName( int table ); + QString valueName( int value ); + QString functionName( int value ); + + void linkTables( int tables ); + + QString m_query; + QString m_values; + QString m_tables; + QString m_join; + QString m_where; + QString m_sort; + QString m_group; + QString m_limit; + + int m_linkTables; + uint m_returnValues; +}; + + +#endif /*AMAROK_DBENGINEBASE_H*/ diff --git a/amarok/src/database_refactor/sqlite/_Makefile.am b/amarok/src/database_refactor/sqlite/_Makefile.am new file mode 100644 index 00000000..bc58791a --- /dev/null +++ b/amarok/src/database_refactor/sqlite/_Makefile.am @@ -0,0 +1,38 @@ +kde_module_LTLIBRARIES = \ + libamarok_sqlite_dbengine_plugin.la + +SUBDIRS = \ + sqlite + +INCLUDES = \ + -I$(top_srcdir)/amarok/src/database/sqlite/sqlite \ + -I$(top_srcdir)/amarok/src/database \ + -I$(top_srcdir)/amarok/src/plugin \ + -I$(top_srcdir)/amarok/src/engine \ + -I$(top_srcdir)/amarok/src/amarokcore \ + -I$(top_srcdir)/amarok/src/statusbar \ + -I$(top_srcdir)/amarok/src \ + $(all_includes) $(taglib_includes) + +libamarok_sqlite_dbengine_plugin_la_LIBADD = \ + $(top_builddir)/amarok/src/database/sqlite/sqlite/libsqlite.la \ + $(top_builddir)/amarok/src/database/libdbengine.la \ + $(top_builddir)/amarok/src/plugin/libplugin.la \ + $(LIB_KDECORE) + +libamarok_sqlite_dbengine_plugin_la_SOURCES = \ + sqlite_dbengine.cpp + +libamarok_sqlite_dbengine_plugin_la_LDFLAGS = \ + -module \ + -no-undefined \ + $(KDE_PLUGIN) \ + $(all_libraries) + +METASOURCES = \ + AUTO + +kde_services_DATA = \ + amarok_sqlite_dbengine_plugin.desktop + + diff --git a/amarok/src/database_refactor/sqlite/amarok_sqlite_dbengine_plugin.desktop b/amarok/src/database_refactor/sqlite/amarok_sqlite_dbengine_plugin.desktop new file mode 100644 index 00000000..89fce118 --- /dev/null +++ b/amarok/src/database_refactor/sqlite/amarok_sqlite_dbengine_plugin.desktop @@ -0,0 +1,103 @@ +[Desktop Entry] +Encoding=UTF-8 +Type=Service +Name=SQLite DBEngine +Name[af]=SQLite DBEnjin +Name[ar]= محرك SQLite DBEngine +Name[bn]=এসকিউ-লাইট ডিবি-ইঞ্জিন +Name[br]=Keflusker DB SQLite +Name[da]=SQLite DB-motor +Name[de]=SQLite +Name[eo]=SQLite DBIlo +Name[es]=Motor de base de datos SQLite +Name[et]=SQLite'i andmebaasimootor +Name[fi]=SQLite-tietokantajärjestelmä +Name[fr]=Moteur de base de données SQLite +Name[ga]=Inneall SQLite +Name[gl]=Motor de BBDD SQLite +Name[he]=מנוע מסד נתונים SQLite +Name[hu]=SQLite adatbázis-alrendszer +Name[is]=SQLite gagnagrunnur +Name[it]=Motore DB SQLite +Name[ja]=SQLite DB エンジン +Name[lt]=SQLite duomenų bazės variklis +Name[nds]=SQLite +Name[ne]=एसक्यु लाइट डीबी इन्जिन +Name[nn]=SQLite-databasemotor +Name[pl]=Baza danych SQLite +Name[pt]=Motor de BD SQLite +Name[pt_BR]=Mecanismo do Banco de Dados SQLite +Name[ru]=SQLite +Name[sq]=Motor SQLite DB +Name[sr]=Мотор SQLite DB +Name[sr@Latn]=Motor SQLite DB +Name[ss]=Motor SQLite DB +Name[sv]=SQLite-databasgränssnitt +Name[tg]=Муҳаррики-DB барои SQLite +Name[tr]=SQLite DBMotoru +Name[uk]=Рушій бази даних SQLite +Name[uz]=SQLite maʼlumot bazasi +Name[uz@cyrillic]=SQLite маълумот базаси +Name[wa]=Éndjin d' BD SQLite +Name[zh_CN]=SQLite 数据引擎 +Name[zh_TW]=SQLite 資料庫引擎 +X-KDE-Library=libamarok_sqlite_dbengine_plugin +Comment=Plugin for Amarok +Comment[af]=Inprop module vir Amarok +Comment[ar]= قابس ( برنامج مضاف الى) AmaroK +Comment[bg]=Приставка за Amarok +Comment[bn]=আমারক-এর জন্য প্লাগিন +Comment[br]=Lugent evit Amarok +Comment[ca]=Connector per l'Amarok +Comment[cs]=Modul pro AmaroK +Comment[de]=Modul für Amarok +Comment[el]=Πρόσθετο για το AmaroK +Comment[eo]=Kromaĵo por Amarok +Comment[es]=Extensión para Amarok +Comment[et]=Amaroki plugin +Comment[fa]=وصله برای amaroK +Comment[fi]=Amarok-liitännäinen +Comment[fr]=Module pour Amarok +Comment[ga]=Breiseán AmaroK +Comment[gl]=Extensión para Amarok +Comment[hu]=Bővítőmodul az Amarokhoz +Comment[is]=Íforrit fyrir Amarok +Comment[it]=Plugin per Amarok +Comment[ja]=Amarok のためのプラグイン +Comment[ka]=მოდული Amarok-ისთვის +Comment[km]=កម្មវិធី​ជំនួយ​សម្រាប់ Amarok +Comment[lt]=Amarok įskiepis +Comment[mk]=Приклучок за Амарок +Comment[nb]=Programtillegg for Amarok +Comment[nds]=Moduul för Amarok +Comment[ne]=अमारोकका लागि प्लगइन +Comment[nl]=Plugin voor Amarok +Comment[nn]=Programtillegg for Amarok +Comment[pa]=ਅਮਰੋਕ ਲਈ ਪਲੱਗਇਨ +Comment[pl]=Wtyczka Amaroka +Comment[pt]='Plugin' para o Amarok +Comment[pt_BR]=Plugin para o Amarok +Comment[ru]=Модуль amaroK +Comment[se]=Lassemoduvla Amarok:ii +Comment[sk]=Amarok modul +Comment[sr]=Прикључак за Amarok +Comment[sr@Latn]=Priključak za Amarok +Comment[sv]=Insticksprogram för Amarok +Comment[th]=โปรแกรมเสริมสำหรับ Amarok +Comment[tr]=Amarok için Eklenti +Comment[uk]=Втулок для Amarok +Comment[uz]=Amarok uchun plagin +Comment[uz@cyrillic]=Amarok учун плагин +Comment[wa]=Tchôke-divins po Amarok +Comment[zh_CN]=Amarok 插件 +Comment[zh_TW]=amaroK 插件 +ServiceTypes=amaroK/Plugin + +X-KDE-amaroK-plugintype=dbengine +X-KDE-amaroK-name=sqlite-dbengine +X-KDE-amaroK-authors=Mark Kretschmann, Christian Muehlhaeuser +X-KDE-amaroK-email=markey@web.de +X-KDE-amaroK-rank=255 +X-KDE-amaroK-version=1 +X-KDE-amaroK-framework-version=5 + diff --git a/amarok/src/database_refactor/sqlite/sqlite_dbengine.cpp b/amarok/src/database_refactor/sqlite/sqlite_dbengine.cpp new file mode 100644 index 00000000..3d93a84c --- /dev/null +++ b/amarok/src/database_refactor/sqlite/sqlite_dbengine.cpp @@ -0,0 +1,227 @@ +// (c) 2004 Mark Kretschmann +// (c) 2004 Christian Muehlhaeuser +// (c) 2004 Sami Nieminen +// (c) 2005 Ian Monroe +// See COPYING file for licensing information. + +#define DEBUG_PREFIX "SQLite-DBEngine" + +#include "app.h" +#include "amarok.h" +#include "amarokconfig.h" +#include "debug.h" +#include "sqlite_dbengine.h" + +#include + +#include +#include +#include + +#include //DbConnection::sqlite_power() +#include //query() +#include //usleep() + +#include "sqlite/sqlite3.h" + +AMAROK_EXPORT_PLUGIN( SqliteDbEngine ) + + +////////////////////////////////////////////////////////////////////////////////////////// +// CLASS SqliteConnection +////////////////////////////////////////////////////////////////////////////////////////// + +SqliteDbEngine::SqliteDbEngine() + : DbConnection( new SqliteConfig( "collection.db" ) ) +{ + const QCString path = QString(/*amaroK::saveLocation()+*/"collection.db").local8Bit(); + + // Open database file and check for correctness + m_initialized = false; + QFile file( path ); + if ( file.open( IO_ReadOnly ) ) + { + QString format; + file.readLine( format, 50 ); + if ( !format.startsWith( "SQLite format 3" ) ) + { + warning() << "Database versions incompatible. Removing and rebuilding database.\n"; + } + else if ( sqlite3_open( path, &m_db ) != SQLITE_OK ) + { + warning() << "Database file corrupt. Removing and rebuilding database.\n"; + sqlite3_close( m_db ); + } + else + m_initialized = true; + } + + if ( !m_initialized ) + { + // Remove old db file; create new + QFile::remove( path ); + if ( sqlite3_open( path, &m_db ) == SQLITE_OK ) + { + m_initialized = true; + } + } + if ( m_initialized ) + { + if( sqlite3_create_function(m_db, "rand", 0, SQLITE_UTF8, NULL, sqlite_rand, NULL, NULL) != SQLITE_OK ) + m_initialized = false; + if( sqlite3_create_function(m_db, "power", 2, SQLITE_UTF8, NULL, sqlite_power, NULL, NULL) != SQLITE_OK ) + m_initialized = false; + } + + //optimization for speeding up SQLite + query( "PRAGMA default_synchronous = OFF;" ); +} + + +SqliteDbEngine::~SqliteDbEngine() +{ + if ( m_db ) sqlite3_close( m_db ); +} + + +QStringList SqliteDbEngine::query( const QString& statement ) +{ + QStringList values; + int error; + const char* tail; + sqlite3_stmt* stmt; + + //compile SQL program to virtual machine + error = sqlite3_prepare( m_db, statement.utf8(), statement.length(), &stmt, &tail ); + + if ( error != SQLITE_OK ) + { + Debug::error() << k_funcinfo << " sqlite3_compile error:" << endl; + Debug::error() << sqlite3_errmsg( m_db ) << endl; + Debug::error() << "on query: " << statement << endl; + values = QStringList(); + } + else + { + int busyCnt = 0; + int number = sqlite3_column_count( stmt ); + //execute virtual machine by iterating over rows + while ( true ) + { + error = sqlite3_step( stmt ); + + if ( error == SQLITE_BUSY ) + { + if ( busyCnt++ > 20 ) { + Debug::error() << "Busy-counter has reached maximum. Aborting this sql statement!\n"; + break; + } + ::usleep( 100000 ); // Sleep 100 msec + debug() << "sqlite3_step: BUSY counter: " << busyCnt << endl; + } + if ( error == SQLITE_MISUSE ) + debug() << "sqlite3_step: MISUSE" << endl; + if ( error == SQLITE_DONE || error == SQLITE_ERROR ) + break; + + //iterate over columns + for ( int i = 0; i < number; i++ ) + { + values << QString::fromUtf8( (const char*) sqlite3_column_text( stmt, i ) ); + } + } + //deallocate vm resources + sqlite3_finalize( stmt ); + + if ( error != SQLITE_DONE ) + { + Debug::error() << k_funcinfo << "sqlite_step error.\n"; + Debug::error() << sqlite3_errmsg( m_db ) << endl; + Debug::error() << "on query: " << statement << endl; + values = QStringList(); + } + } + + return values; +} + + +int SqliteDbEngine::insert( const QString& statement, const QString& /* table */ ) +{ + int error; + const char* tail; + sqlite3_stmt* stmt; + + //compile SQL program to virtual machine + error = sqlite3_prepare( m_db, statement.utf8(), statement.length(), &stmt, &tail ); + + if ( error != SQLITE_OK ) + { + Debug::error() << k_funcinfo << " sqlite3_compile error:" << endl; + Debug::error() << sqlite3_errmsg( m_db ) << endl; + Debug::error() << "on insert: " << statement << endl; + } + else + { + int busyCnt = 0; + //execute virtual machine by iterating over rows + while ( true ) + { + error = sqlite3_step( stmt ); + + if ( error == SQLITE_BUSY ) + { + if ( busyCnt++ > 20 ) { + Debug::error() << "Busy-counter has reached maximum. Aborting this sql statement!\n"; + break; + } + ::usleep( 100000 ); // Sleep 100 msec + debug() << "sqlite3_step: BUSY counter: " << busyCnt << endl; + } + if ( error == SQLITE_MISUSE ) + debug() << "sqlite3_step: MISUSE" << endl; + if ( error == SQLITE_DONE || error == SQLITE_ERROR ) + break; + } + //deallocate vm resources + sqlite3_finalize( stmt ); + + if ( error != SQLITE_DONE ) + { + Debug::error() << k_funcinfo << "sqlite_step error.\n"; + Debug::error() << sqlite3_errmsg( m_db ) << endl; + Debug::error() << "on insert: " << statement << endl; + } + } + return sqlite3_last_insert_rowid( m_db ); +} + + +// this implements a RAND() function compatible with the MySQL RAND() (0-param-form without seed) +void SqliteDbEngine::sqlite_rand(sqlite3_context *context, int /*argc*/, sqlite3_value ** /*argv*/) +{ + //sqlite3_result_double( context, static_cast(KApplication::random()) / (RAND_MAX+1.0) ); +} + +// this implements a POWER() function compatible with the MySQL POWER() +void SqliteDbEngine::sqlite_power(sqlite3_context *context, int argc, sqlite3_value **argv) +{ + Q_ASSERT( argc==2 ); + if( sqlite3_value_type(argv[0])==SQLITE_NULL || sqlite3_value_type(argv[1])==SQLITE_NULL ) { + sqlite3_result_null(context); + return; + } + double a = sqlite3_value_double(argv[0]); + double b = sqlite3_value_double(argv[1]); + sqlite3_result_double( context, pow(a,b) ); +} + + +////////////////////////////////////////////////////////////////////////////////////////// +// CLASS SqliteConfig +////////////////////////////////////////////////////////////////////////////////////////// + +SqliteConfig::SqliteConfig( const QString& dbfile ) + : m_dbfile( dbfile ) +{} + diff --git a/amarok/src/database_refactor/sqlite/sqlite_dbengine.h b/amarok/src/database_refactor/sqlite/sqlite_dbengine.h new file mode 100644 index 00000000..d19263e0 --- /dev/null +++ b/amarok/src/database_refactor/sqlite/sqlite_dbengine.h @@ -0,0 +1,58 @@ +// (c) 2004 Mark Kretschmann +// (c) 2004 Christian Muehlhaeuser +// (c) 2004 Sami Nieminen +// See COPYING file for licensing information. + +#ifndef AMAROK_SQLITE_DBENGINE_H +#define AMAROK_SQLITE_DBENGINE_H + +#include "dbenginebase.h" +#include +#include //stack allocated +#include //baseclass +#include //baseclass +#include //stack allocated +#include //stack allocated + +class DbConfig; +class DbConnection; +class DbConnectionPool; +class CoverFetcher; +class MetaBundle; +class Scrobbler; + + +class SqliteConfig : public DbConfig +{ + public: + SqliteConfig( const QString& /* dbfile */ ); + + const QString dbFile() const { return m_dbfile; } + + private: + QString m_dbfile; +}; + + +typedef struct sqlite3 sqlite3; +typedef struct sqlite3_context sqlite3_context; +typedef struct Mem sqlite3_value; + +class SqliteDbEngine : public DbConnection +{ + public: + SqliteDbEngine(); + ~SqliteDbEngine(); + + QStringList query( const QString& /* statement */ ); + int insert( const QString& /* statement */, const QString& /* table */ ); + bool isConnected()const { return true; } + private: + static void sqlite_rand(sqlite3_context *context, int /*argc*/, sqlite3_value ** /*argv*/); + static void sqlite_power(sqlite3_context *context, int argc, sqlite3_value **argv); + + sqlite3* m_db; +}; + + +#endif /*SQLITE_DBENGINE_H*/ diff --git a/amarok/src/dbsetup.ui b/amarok/src/dbsetup.ui new file mode 100644 index 00000000..9a30eb75 --- /dev/null +++ b/amarok/src/dbsetup.ui @@ -0,0 +1,468 @@ + +DbSetup + + + DbSetup + + + + 0 + 0 + 385 + 155 + + + + Database Setup + + + + unnamed + + + 0 + + + + configStack + + + Plain + + + + SQLLite + + + 0 + + + + + MySQL + + + 1 + + + + unnamed + + + 0 + + + + mySqlFrame + + + NoFrame + + + Raised + + + 0 + + + + unnamed + + + 0 + + + + mysqlConfig + + + MySQL Configuration + + + + unnamed + + + + textLabel1 + + + Hostname: + + + + + textLabel5 + + + Database: + + + + + kcfg_MySqlPort + + + + 100 + 32767 + + + + 65535 + + + Which port mysql should connect to. + + + Which port mysql should connect to. + + + + + textLabel2 + + + Port: + + + + + kcfg_MySqlHost + + + Hostname where database lives. + + + Hostname where database lives. + + + + + kcfg_MySqlDbName + + + Name of the database. + + + Name of the database. + + + + + line1 + + + HLine + + + Sunken + + + Horizontal + + + + + layout4 + + + + unnamed + + + + textLabel3 + + + Username: + + + + + kcfg_MySqlUser + + + Username with which to connect to. + + + Username with which to connect to. + + + + + textLabel4 + + + Password: + + + + + kcfg_MySqlPassword2 + + + Password + + + Password with which to connect to. + + + Password with which to connect to. + + + + + + + + + + + + + PostgreSQL + + + 2 + + + + unnamed + + + 0 + + + + postgreSqlFrame + + + NoFrame + + + Raised + + + + unnamed + + + 0 + + + + postgresqlConfig + + + PostgreSQL Configuration + + + + unnamed + + + + textLabel1 + + + Hostname: + + + + + textLabel5 + + + Database: + + + + + kcfg_PostgresqlPort + + + + 100 + 32767 + + + + 65535 + + + Which port postgresql should connect to. + + + Which port postgresql should connect to. + + + + + textLabel2 + + + Port: + + + + + kcfg_PostgresqlHost + + + Hostname where database lives. + + + Hostname where database lives. + + + + + kcfg_PostgresqlDbName + + + Name of the database. + + + Name of the database. + + + + + line1 + + + HLine + + + Sunken + + + Horizontal + + + + + layout4 + + + + unnamed + + + + textLabel3 + + + Username: + + + + + kcfg_PostgresqlUser + + + Username with which to connect to. + + + Username with which to connect to. + + + + + textLabel4 + + + Password: + + + + + kcfg_PostgresqlPassword2 + + + Password + + + Password with which to connect to. + + + Password with which to connect to. + + + + + + + + + + + + + + textLabel1_2 + + + Database: + + + + + + SQLite + + + + databaseEngine + + + + 7 + 0 + 0 + 0 + + + + + + + + + + databaseEngine + activated(int) + configStack + raiseWidget(int) + + + databaseEngine + activated(int) + DbSetup + databaseEngine_activated(int) + + + + amarokconfig.h + config.h + debug.h + dbsetup.ui.h + + + databaseEngine_activated( int ) + + + init() + + + + knuminput.h + klineedit.h + klineedit.h + klineedit.h + klineedit.h + klineedit.h + kcombobox.h + + diff --git a/amarok/src/dbsetup.ui.h b/amarok/src/dbsetup.ui.h new file mode 100644 index 00000000..5b305e0e --- /dev/null +++ b/amarok/src/dbsetup.ui.h @@ -0,0 +1,55 @@ +#//(c) 2005 Ian Monroe see COPYING +/**************************************************************************** +** ui.h extension file, included from the uic-generated form implementation. +** +** If you want to add, delete, or rename functions or slots, use +** Qt Designer to update this file, preserving your code. +** +** You should not define a constructor or destructor in this file. +** Instead, write your code in functions called init() and destroy(). +** These will automatically be called by the form's constructor and +** destructor. +*****************************************************************************/ +#include "config.h" +#include "amarokconfig.h" +#include "collectiondb.h" + +void DbSetup::init() +{ + configStack->raiseWidget( 0 ); +#ifdef USE_MYSQL + databaseEngine->insertItem( "MySQL", -1 ); + if (AmarokConfig::databaseEngine() == QString::number(DbConnection::mysql)) + { + databaseEngine->setCurrentItem("MySQL"); + configStack->raiseWidget( 1 ); + } +#endif + +#ifdef USE_POSTGRESQL + databaseEngine->insertItem( "Postgresql", -1 ); + if (AmarokConfig::databaseEngine() == QString::number(DbConnection::postgresql)) + { + databaseEngine->setCurrentItem("Postgresql"); + configStack->raiseWidget( 2 ); + } +#endif +} + +void DbSetup::databaseEngine_activated( int item ) +{ + if( item == 0 ) + configStack->raiseWidget( 0 ); + + // If built with MySQL support, the PostgreSQL config widget is #2 + // Without MySQL it's #1 +#ifdef USE_MYSQL + else if( item == 1 ) + configStack->raiseWidget( 1 ); + else if( item == 2 ) + configStack->raiseWidget( 2 ); +#elif defined(USE_POSTGRESQL) + else if( item == 1 ) + configStack->raiseWidget( 2 ); +#endif +} diff --git a/amarok/src/debug.h b/amarok/src/debug.h new file mode 100644 index 00000000..2c83ad79 --- /dev/null +++ b/amarok/src/debug.h @@ -0,0 +1,245 @@ +// Author: Max Howell , (C) 2003-5 +// Copyright: See COPYING file that comes with this distribution +// + +#ifndef AMAROK_DEBUG_H +#define AMAROK_DEBUG_H + +#include +#include +#include +#include +#include +#include + +class QApplication; +extern QApplication *qApp; ///@see Debug::Indent + + +/** + * @namespace Debug + * @short kdebug with indentation functionality and convenience macros + * @author Max Howell + * + * Usage: + * + * #define DEBUG_PREFIX "Blah" + * #include "debug.h" + * + * void function() + * { + * Debug::Block myBlock( __PRETTY_FUNCTION__ ); + * + * debug() << "output1" << endl; + * debug() << "output2" << endl; + * } + * + * Will output: + * + * app: BEGIN: void function() + * app: [Blah] output1 + * app: [Blah] output2 + * app: END: void function(): Took 0.1s + * + * @see Block + * @see CrashHelper + * @see ListStream + */ + +namespace Debug +{ + extern QMutex mutex; // defined in app.cpp + + // we can't use a statically instantiated QCString for the indent, because + // static namespaces are unique to each dlopened library. So we piggy back + // the QCString on the KApplication instance + + #define qApp reinterpret_cast(qApp) + class Indent : QObject + { + friend QCString &modifieableIndent(); + Indent() : QObject( qApp, "DEBUG_indent" ) {} + QCString m_string; + }; + + inline QCString &modifieableIndent() + { + QObject *o = qApp ? qApp->child( "DEBUG_indent" ) : 0; + QCString &ret = (o ? static_cast( o ) : new Indent)->m_string; + return ret; + } + + inline QCString indent() + { + return QDeepCopy( modifieableIndent() ); + } + #undef qApp + + + #ifdef NDEBUG + static inline kndbgstream debug() { return kndbgstream(); } + static inline kndbgstream warning() { return kndbgstream(); } + static inline kndbgstream error() { return kndbgstream(); } + static inline kndbgstream fatal() { return kndbgstream(); } + + typedef kndbgstream DebugStream; + #else + #ifndef DEBUG_PREFIX + #define AMK_PREFIX "" + #else + #define AMK_PREFIX "[" DEBUG_PREFIX "] " + #endif + + //from kdebug.h + enum DebugLevels { + KDEBUG_INFO = 0, + KDEBUG_WARN = 1, + KDEBUG_ERROR = 2, + KDEBUG_FATAL = 3 + }; + + static inline kdbgstream debug() { mutex.lock(); QCString ind = indent(); mutex.unlock(); return kdbgstream( ind, 0, KDEBUG_INFO ) << AMK_PREFIX; } + static inline kdbgstream warning() { mutex.lock(); QCString ind = indent(); mutex.unlock(); return kdbgstream( ind, 0, KDEBUG_WARN ) << AMK_PREFIX << "[WARNING!] "; } + static inline kdbgstream error() { mutex.lock(); QCString ind = indent(); mutex.unlock(); return kdbgstream( ind, 0, KDEBUG_ERROR ) << AMK_PREFIX << "[ERROR!] "; } + static inline kdbgstream fatal() { mutex.lock(); QCString ind = indent(); mutex.unlock(); return kdbgstream( ind, 0, KDEBUG_FATAL ) << AMK_PREFIX; } + + typedef kdbgstream DebugStream; + + #undef AMK_PREFIX + #endif + + typedef kndbgstream NoDebugStream; +} + +using Debug::debug; +using Debug::warning; +using Debug::error; +using Debug::fatal; +using Debug::DebugStream; + +/// Standard function announcer +#define DEBUG_FUNC_INFO { Debug::mutex.lock(); kdDebug() << Debug::indent() << k_funcinfo << endl; Debug::mutex.unlock(); } + +/// Announce a line +#define DEBUG_LINE_INFO { Debug::mutex.lock(); kdDebug() << Debug::indent() << k_funcinfo << "Line: " << __LINE__ << endl; Debug::mutex.unlock(); } + +/// Convenience macro for making a standard Debug::Block +#define DEBUG_BLOCK Debug::Block uniquelyNamedStackAllocatedStandardBlock( __PRETTY_FUNCTION__ ); + +/// Use this to remind yourself to finish the implementation of a function +#define AMAROK_NOTIMPLEMENTED warning() << "NOT-IMPLEMENTED: " << __PRETTY_FUNCTION__ << endl; + +/// Use this to alert other developers to stop using a function +#define AMAROK_DEPRECATED warning() << "DEPRECATED: " << __PRETTY_FUNCTION__ << endl; + + +namespace Debug +{ + /** + * @class Debug::Block + * @short Use this to label sections of your code + * + * Usage: + * + * void function() + * { + * Debug::Block myBlock( "section" ); + * + * debug() << "output1" << endl; + * debug() << "output2" << endl; + * } + * + * Will output: + * + * app: BEGIN: section + * app: [prefix] output1 + * app: [prefix] output2 + * app: END: section - Took 0.1s + * + */ + + class Block + { + timeval m_start; + const char *m_label; + + public: + Block( const char *label ) + : m_label( label ) + { + mutex.lock(); + gettimeofday( &m_start, 0 ); + + kdDebug() << "BEGIN: " << label << "\n"; + Debug::modifieableIndent() += " "; + mutex.unlock(); + } + + ~Block() + { + mutex.lock(); + timeval end; + gettimeofday( &end, 0 ); + + end.tv_sec -= m_start.tv_sec; + if( end.tv_usec < m_start.tv_usec) { + // Manually carry a one from the seconds field. + end.tv_usec += 1000000; + end.tv_sec--; + } + end.tv_usec -= m_start.tv_usec; + + double duration = double(end.tv_sec) + (double(end.tv_usec) / 1000000.0); + + Debug::modifieableIndent().truncate( Debug::indent().length() - 2 ); + kdDebug() << "END__: " << m_label + << " - Took " << QString::number( duration, 'g', 2 ) << "s\n"; + mutex.unlock(); + } + }; + + + /** + * @name Debug::stamp() + * @short To facilitate crash/freeze bugs, by making it easy to mark code that has been processed + * + * Usage: + * + * { + * Debug::stamp(); + * function1(); + * Debug::stamp(); + * function2(); + * Debug::stamp(); + * } + * + * Will output (assuming the crash occurs in function2() + * + * app: Stamp: 1 + * app: Stamp: 2 + * + */ + + inline void stamp() + { + static int n = 0; + debug() << "| Stamp: " << ++n << endl; + } +} + + +#include + +namespace Debug +{ + /** + * @class Debug::List + * @short You can pass anything to this and it will output it as a list + * + * debug() << (Debug::List() << anInt << aString << aQStringList << aDouble) << endl; + */ + + typedef QValueList List; +} + +#endif diff --git a/amarok/src/deletedialog.cpp b/amarok/src/deletedialog.cpp new file mode 100644 index 00000000..1c34e7d1 --- /dev/null +++ b/amarok/src/deletedialog.cpp @@ -0,0 +1,157 @@ +/*************************************************************************** + begin : Tue Aug 31 21:59:58 EST 2004 + copyright : (C) 2004 by Michael Pyne + (C) 2006 by Ian Monroe +***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "amarok.h" +#include "playlist.h" +#include "collectiondb.h" +#include "deletedialog.h" +#include "statusbar.h" + +////////////////////////////////////////////////////////////////////////////// +// DeleteWidget implementation +////////////////////////////////////////////////////////////////////////////// + +DeleteWidget::DeleteWidget(QWidget *parent, const char *name) + : DeleteDialogBase(parent, name) +{ + KConfigGroup messageGroup(KGlobal::config(), "FileRemover"); + + bool deleteInstead = messageGroup.readBoolEntry("deleteInsteadOfTrash", false); + slotShouldDelete(deleteInstead); + ddShouldDelete->setChecked(deleteInstead); +} + +void DeleteWidget::setFiles(const KURL::List &files) +{ + ddFileList->clear(); +// ddFileList->insertStringList(files); + for( KURL::List::ConstIterator it = files.begin(); it != files.end(); it++) + { + if( (*it).isLocalFile() ) //path is nil for non-local + ddFileList->insertItem( (*it).path() ); + else + ddFileList->insertItem( (*it).url() ); + } + ddNumFiles->setText(i18n("1 file selected.", "%n files selected.", files.count())); +} + +void DeleteWidget::slotShouldDelete(bool shouldDelete) +{ + if(shouldDelete) { + ddDeleteText->setText(i18n("These items will be permanently " + "deleted from your hard disk.")); + ddWarningIcon->setPixmap(KGlobal::iconLoader()->loadIcon("messagebox_warning", + KIcon::Desktop, KIcon::SizeLarge)); + } + else { + ddDeleteText->setText(i18n("These items will be moved to the Trash Bin.")); + ddWarningIcon->setPixmap(KGlobal::iconLoader()->loadIcon("trashcan_full", + KIcon::Desktop, KIcon::SizeLarge)); + } +} + +////////////////////////////////////////////////////////////////////////////// +// DeleteDialog implementation +////////////////////////////////////////////////////////////////////////////// + +DeleteDialog::DeleteDialog(QWidget *parent, const char *name) : + KDialogBase(Swallow, WStyle_DialogBorder, parent, name, + true /* modal */, i18n("About to delete selected files"), + Ok | Cancel, Cancel /* Default */, true /* separator */), + m_trashGuiItem(i18n("&Send to Trash"), "trashcan_full") +{ + m_widget = new DeleteWidget(this, "delete_dialog_widget"); + setMainWidget(m_widget); + + m_widget->setMinimumSize(400, 300); + setMinimumSize(410, 326); + adjustSize(); + + slotShouldDelete(shouldDelete()); + connect(m_widget->ddShouldDelete, SIGNAL(toggled(bool)), SLOT(slotShouldDelete(bool))); + +} + +bool DeleteDialog::confirmDeleteList(const KURL::List& condemnedFiles) +{ + m_widget->setFiles(condemnedFiles); + + return exec() == QDialog::Accepted; +} + +void DeleteDialog::setFiles(const KURL::List &files) +{ + m_widget->setFiles(files); +} + +void DeleteDialog::accept() +{ + KConfigGroup messageGroup(KGlobal::config(), "FileRemover"); + + // Save user's preference + + messageGroup.writeEntry("deleteInsteadOfTrash", shouldDelete()); + messageGroup.sync(); + + KDialogBase::accept(); +} + +void DeleteDialog::slotShouldDelete(bool shouldDelete) +{ + setButtonGuiItem(Ok, shouldDelete ? KStdGuiItem::del() : m_trashGuiItem); +} + +bool DeleteDialog::showTrashDialog(QWidget* parent, const KURL::List& files) +{ + DeleteDialog dialog(parent); + bool doDelete = dialog.confirmDeleteList(files); + + if( doDelete ) + { + KIO::Job* job = 0; + bool shouldDelete = dialog.shouldDelete(); + if ( ( shouldDelete && (job = KIO::del( files )) ) || + ( job = Amarok::trashFiles( files ) ) ) + { + if(shouldDelete) //amarok::trashFiles already does the progress operation + Amarok::StatusBar::instance()->newProgressOperation( job ) + .setDescription( i18n("Deleting files") ); + } + + } + + return doDelete; +} +#include "deletedialog.moc" + +// vim: set et ts=4 sw=4: diff --git a/amarok/src/deletedialog.h b/amarok/src/deletedialog.h new file mode 100644 index 00000000..6127f1fb --- /dev/null +++ b/amarok/src/deletedialog.h @@ -0,0 +1,67 @@ +/*************************************************************************** + begin : Tue Aug 31 21:54:20 EST 2004 + copyright : (C) 2004 by Michael Pyne + (C) 2006 by Ian Monroe +***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#ifndef _DELETEDIALOG_H +#define _DELETEDIALOG_H + + +#include +#include +#include +#include "deletedialogbase.h" + +class QStringList; +class KListBox; +class KGuiItem; +class QLabel; +class QWidgetStack; + +class DeleteWidget : public DeleteDialogBase +{ + Q_OBJECT + +public: + DeleteWidget(QWidget *parent = 0, const char *name = 0); + + void setFiles(const KURL::List &files); + +protected slots: + virtual void slotShouldDelete(bool shouldDelete); +}; + +class DeleteDialog : public KDialogBase +{ + Q_OBJECT + +public: + DeleteDialog(QWidget *parent, const char *name = "delete_dialog"); + static bool showTrashDialog(QWidget* parent, const KURL::List &files); + + bool confirmDeleteList(const KURL::List &condemnedFiles); + void setFiles(const KURL::List &files); + bool shouldDelete() const { return m_widget->ddShouldDelete->isChecked(); } + +protected slots: + virtual void accept(); + void slotShouldDelete(bool shouldDelete); + +private: + DeleteWidget *m_widget; + KGuiItem m_trashGuiItem; +}; + +#endif + +// vim: set et ts=4 sw=4: diff --git a/amarok/src/deletedialogbase.ui b/amarok/src/deletedialogbase.ui new file mode 100644 index 00000000..768050bc --- /dev/null +++ b/amarok/src/deletedialogbase.ui @@ -0,0 +1,135 @@ + +DeleteDialogBase + + + DeleteDialogBase + + + + 0 + 0 + 542 + 374 + + + + + 420 + 320 + + + + + unnamed + + + 0 + + + + layout4 + + + + unnamed + + + + ddWarningIcon + + + + 4 + 4 + 0 + 0 + + + + Icon Placeholder, not in GUI + + + + + layout3 + + + + unnamed + + + + ddDeleteText + + + Deletion method placeholder, never shown to user. + + + WordBreak|AlignCenter + + + + + + + + + ddFileList + + + NoSelection + + + List of files that are about to be deleted. + + + This is the list of items that are about to be deleted. + + + + + ddNumFiles + + + Placeholder for number of files, not in GUI + + + AlignVCenter|AlignRight + + + + + ddShouldDelete + + + &Delete files instead of moving them to the trash + + + If checked, files will be permanently removed instead of being placed in the Trash Bin + + + <qt><p>If this box is checked, files will be <b>permanently removed</b> instead of being placed in the Trash Bin.</p> + +<p><em>Use this option with caution</em>: Most filesystems are unable to reliably undelete deleted files.</p></qt> + + + + + + + + + ddShouldDelete + toggled(bool) + DeleteDialogBase + slotShouldDelete(bool) + + + + slotShouldDelete(bool) + + + + klistbox.h + + diff --git a/amarok/src/device/Makefile.am b/amarok/src/device/Makefile.am new file mode 100644 index 00000000..9d38aa8f --- /dev/null +++ b/amarok/src/device/Makefile.am @@ -0,0 +1,3 @@ +METASOURCES = AUTO + +SUBDIRS = massstorage nfs smb diff --git a/amarok/src/device/massstorage/Makefile.am b/amarok/src/device/massstorage/Makefile.am new file mode 100644 index 00000000..69ff41a5 --- /dev/null +++ b/amarok/src/device/massstorage/Makefile.am @@ -0,0 +1,28 @@ +kde_module_LTLIBRARIES = libamarok_massstorage-device.la +kde_services_DATA = amarok_massstorage-device.desktop + +INCLUDES = \ + -I$(top_builddir)/amarok/src/amarokcore \ + -I$(top_srcdir)/amarok/src/amarokcore \ + -I$(top_builddir)/amarok/src/amarokcore \ + -I$(top_srcdir)/amarok/src/plugin \ + -I$(top_builddir)/amarok/src \ + -I$(top_srcdir)/amarok/src \ + $(all_includes) + +METASOURCES = AUTO + +libamarok_massstorage_device_la_LIBADD = \ + $(top_builddir)/amarok/src/libamarok.la \ + $(LIB_KDECORE) $(LIB_QT) + +libamarok_massstorage_device_la_LDFLAGS = \ + $(KDE_PLUGIN) \ + $(all_libraries) + +libamarok_massstorage_device_la_SOURCES = \ + massstoragedevicehandler.cpp + +noinst_HEADERS = \ + massstoragedevicehandler.h + diff --git a/amarok/src/device/massstorage/amarok_massstorage-device.desktop b/amarok/src/device/massstorage/amarok_massstorage-device.desktop new file mode 100644 index 00000000..ef48dee5 --- /dev/null +++ b/amarok/src/device/massstorage/amarok_massstorage-device.desktop @@ -0,0 +1,110 @@ +[Desktop Entry] +Encoding=UTF-8 +Type=Service +Name=Mass Storage Device +Name[af]=Massa Stoor Toestel +Name[ar]=جهاز حفظ ذو الذاكرة الضخمة +Name[bg]=Устройство за съхранение +Name[bn]=মাস স্টোরেজ ডিভাইস +Name[ca]=Dispositiu d'emmagatzemament massiu +Name[cs]=Souborové mediální zařízení +Name[da]=Masseopbevaringsenhed +Name[de]=Wechsellaufwerk +Name[el]=Συσκευή μαζικής αποθήκευσης +Name[eo]=Amasmemora Ekipaĵo +Name[es]=Dispositivo de almacenamiento masivo +Name[et]=Mass-salvestusseade +Name[fa]=دستگاه ذخیره‌گاه حجم +Name[fi]=Massamuistilaite +Name[fr]=Périphérique de stockage +Name[gl]=Dispositivo de Armacenaxe +Name[hu]=Tárolóeszköz +Name[is]=Harðir diskar o.þ.h +Name[it]=Dispositivo di memorizzazione di massa +Name[ja]=マスストレージデバイス +Name[ka]=არქივის შესანახი მოწყობილობა +Name[km]=ឧបករណ៍​ផ្ទុក​ធំ +Name[lt]=Duomenų saugojimo įrenginys +Name[mk]=Уред за складирање податоци +Name[nb]=Masselagerenhet +Name[nds]=Bültspieker-Reedschap +Name[ne]=बृहत भण्डारण यन्त्र +Name[nl]=Massa-opslag-apparaat +Name[nn]=Masselagringseining +Name[pa]=ਵੱਡਾ ਸਟੋਰੇਜ਼ ਜੰਤਰ +Name[pl]=Urządzenie przechowywania danych +Name[pt]=Dispositivo Multimédia com Armazenamento +Name[pt_BR]=Dispositivo de Armazenamento em Massa +Name[se]=Vurkenovttadat +Name[sk]=Zariadenie na masové ukladanie +Name[sr]=Уређај за масовно складиштење +Name[sr@Latn]=Uređaj za masovno skladištenje +Name[sv]=Masslagringsenhet +Name[th]=อุปกรณ์สื่อบันทึกข้อมูล +Name[tr]=Yığın Depolama Aygıtı +Name[uk]=Пристрій масового накопичувача +Name[uz]=Saqlash uskunasi +Name[uz@cyrillic]=Сақлаш ускунаси +Name[wa]=Éndjin d' wårdaedje di masse +Name[zh_CN]=大容量存储设备 +Name[zh_TW]=大型儲存裝置 +Comment=Device plugin for Amarok +Comment[af]=Inprop module vir Amarok +Comment[ar]= قابس ( برنامج مضاف الى) AmaroK +Comment[bg]=Приставка за устройство за Amarok +Comment[bn]=আমারক-এর জন্য ডিভাইস প্লাগিন +Comment[ca]=Connector de dispositiu per a l'Amarok +Comment[cs]=Modul zařízení pro AmaroK +Comment[da]=Enheds-plugin for Amarok +Comment[de]=Geräte-Modul für Amarok +Comment[el]=Πρόσθετο συσκευής για το AmaroK +Comment[eo]=Ekipaĵa kromaĵo por Amarok +Comment[es]=Plugin de dispositivos para Amarok +Comment[et]=Amaroki seadmeplugin +Comment[fa]=وصلۀ دستگاه برای Amarok +Comment[fi]=Amarok-laiteliitännäinen +Comment[fr]=Plugin de périphérique pour Amarok +Comment[ga]=Breiseán gléis le haghaidh AmaroK +Comment[gl]=Extensión de dispositivos para Amarok +Comment[hu]=Eszköz-bővítőmodul az Amarokhoz +Comment[is]=Tækja íforrit fyrir Amarok +Comment[it]=Plugin di dispositivo per Amarok +Comment[ja]=Amarok のためのデバイスプラグイン +Comment[ka]=მოწყობილობის ძრავი Amarok-ისთვის +Comment[km]=កម្មវិធី​ជំនួយ​ឧបករណ៍​សម្រាប់ Amarok +Comment[lt]=Amarok įrenginio priedas +Comment[mk]=Приклучок за уред за Амарок +Comment[ms]=Plugin peranti untuk Amarok +Comment[nb]=Amarok programtillegg for masselagerenhet +Comment[nds]=Reedschapmoduul för Amarok +Comment[ne]=अमारोकका लागि यन्त्र प्लगइन +Comment[nl]=Apparaatplugin voor Amarok +Comment[nn]=Einingstillegg for Amarok +Comment[pa]=ਅਮਰੋਕ ਲਈ ਜੰਤਰ ਪਲੱਗਇਨ +Comment[pl]=Wtyczka urządzenia dla Amaroka +Comment[pt]='Plugin' de dispositivo para o Amarok +Comment[pt_BR]=Plugin de dispositivo para o Amarok +Comment[se]=Ovttadatlassemoduvla Amarok:ii +Comment[sk]=Modul zariadenia pre Amarok +Comment[sr]=Уређајски прикључак за Amarok +Comment[sr@Latn]=Uređajski priključak za Amarok +Comment[sv]=Enhetsinsticksprogram för Amarok +Comment[th]=โปรแกรมเสริมจัดการอุปกรณ์สำหรับ Amarok +Comment[tr]=Amarok için aygıt eklentisi +Comment[uk]=Втулок пристроїв для Amarok +Comment[uz]=Amarok uchun uskuna plagini +Comment[uz@cyrillic]=Amarok учун ускуна плагини +Comment[wa]=Tchôke-divins d' éndjin po Amarok +Comment[zh_CN]=Amarok 的设备插件 +Comment[zh_TW]=amaroK 裝置插件 +ServiceTypes=Amarok/Plugin + +X-KDE-Library=libamarok_massstorage-device + +X-KDE-Amarok-authors=Maximilian Kossick +X-KDE-Amarok-email=maximilian.kossick@googlemail.com +X-KDE-Amarok-framework-version=32 +X-KDE-Amarok-name=massstorage-device +X-KDE-Amarok-plugintype=device +X-KDE-Amarok-rank=100 +X-KDE-Amarok-version=1 diff --git a/amarok/src/device/massstorage/massstoragedevicehandler.cpp b/amarok/src/device/massstorage/massstoragedevicehandler.cpp new file mode 100644 index 00000000..76ff777b --- /dev/null +++ b/amarok/src/device/massstorage/massstoragedevicehandler.cpp @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2006-2007 Maximilian Kossick + * + * 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 program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#define DEBUG_PREFIX "MassStorageDeviceHandler" + +#include "massstoragedevicehandler.h" + +AMAROK_EXPORT_PLUGIN( MassStorageDeviceHandlerFactory ) + +#include "collectiondb.h" +#include "debug.h" + +#include +#include + +#include + +MassStorageDeviceHandler::MassStorageDeviceHandler() + : DeviceHandler() + , m_deviceID( -1 ) +{ +} + +MassStorageDeviceHandler::MassStorageDeviceHandler( int deviceId, const QString &mountPoint, const QString &uuid ) + : DeviceHandler() + , m_deviceID( deviceId ) + , m_mountPoint( mountPoint ) + , m_uuid( uuid ) +{ +} + +MassStorageDeviceHandler::~MassStorageDeviceHandler() +{ +} + +bool MassStorageDeviceHandler::isAvailable() const +{ + return true; +} + + +QString MassStorageDeviceHandler::type() const +{ + return "uuid"; +} + +int MassStorageDeviceHandler::getDeviceID() +{ + return m_deviceID; +} + +const QString &MassStorageDeviceHandler::getDevicePath() const +{ + return m_mountPoint; +} + +void MassStorageDeviceHandler::getURL( KURL &absolutePath, const KURL &relativePath ) +{ + absolutePath.setPath( m_mountPoint ); + absolutePath.addPath( relativePath.path() ); + absolutePath.cleanPath(); +} + +void MassStorageDeviceHandler::getPlayableURL( KURL &absolutePath, const KURL &relativePath ) +{ + getURL( absolutePath, relativePath ); +} + +bool MassStorageDeviceHandler::deviceIsMedium( const Medium * m ) const +{ + return m_uuid == m->id(); +} + +/////////////////////////////////////////////////////////////////////////////// +// class MassStorageDeviceHandlerFactory +/////////////////////////////////////////////////////////////////////////////// + +QString MassStorageDeviceHandlerFactory::type( ) const +{ + return "uuid"; +} + +bool MassStorageDeviceHandlerFactory::canCreateFromMedium( ) const +{ + return true; +} + +bool MassStorageDeviceHandlerFactory::canCreateFromConfig( ) const +{ + return false; +} + +bool MassStorageDeviceHandlerFactory::canHandle( const Medium * m ) const +{ + return m && !m->id().isEmpty() && !excludedFilesystem( m->fsType() ); +} + +MassStorageDeviceHandlerFactory::MassStorageDeviceHandlerFactory( ) +{ +} + +MassStorageDeviceHandlerFactory::~MassStorageDeviceHandlerFactory( ) +{ +} + +DeviceHandler * MassStorageDeviceHandlerFactory::createHandler( const KConfig* ) const +{ + return 0; +} + +DeviceHandler * MassStorageDeviceHandlerFactory::createHandler( const Medium * m ) const +{ + QStringList ids = CollectionDB::instance()->query( QString( "SELECT id, label, lastmountpoint " + "FROM devices WHERE type = 'uuid' " + "AND uuid = '%1';" ).arg( m->id() ) ); + if ( ids.size() == 3 ) + { + debug() << "Found existing UUID config for ID " << ids[0] << " , uuid " << m->id() << endl; + CollectionDB::instance()->query( QString( "UPDATE devices SET lastmountpoint = '%2' WHERE " + "id = %1;" ).arg( ids[0] ).arg( m->mountPoint() ) ); + return new MassStorageDeviceHandler( ids[0].toInt(), m->mountPoint(), m->id() ); + } + else + { + int id = CollectionDB::instance()->insert( QString( "INSERT INTO devices( type, uuid, lastmountpoint ) " + "VALUES ( 'uuid', '%1', '%2' );" ) + .arg( m->id() ) + .arg( m->mountPoint() ), "devices" ); + if ( id == 0 ) + { + warning() << "Inserting into devices failed for type=uuid, uuid=" << m->id() << endl; + return 0; + } + debug() << "Created new UUID device with ID " << id << " , uuid " << m->id() << endl; + return new MassStorageDeviceHandler( id, m->mountPoint(), m->id() ); + } +} + +bool +MassStorageDeviceHandlerFactory::excludedFilesystem( const QString &fstype ) const +{ + return fstype.isEmpty() || + fstype.find( "smb" ) != -1 || + fstype.find( "cifs" ) != -1 || + fstype.find( "nfs" ) != -1 || + fstype == "udf" || + fstype == "iso9660" ; +} diff --git a/amarok/src/device/massstorage/massstoragedevicehandler.h b/amarok/src/device/massstorage/massstoragedevicehandler.h new file mode 100644 index 00000000..cb3bdf92 --- /dev/null +++ b/amarok/src/device/massstorage/massstoragedevicehandler.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2006-2007 Maximilian Kossick + * + * 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 program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef MASSSTORAGEDEVICEHANDLER_H +#define MASSSTORAGEDEVICEHANDLER_H + +#include + +class MassStorageDeviceHandlerFactory : public DeviceHandlerFactory +{ +public: + MassStorageDeviceHandlerFactory(); + virtual ~MassStorageDeviceHandlerFactory(); + + virtual bool canHandle( const Medium* m ) const; + + virtual bool canCreateFromMedium() const; + + virtual DeviceHandler* createHandler( const Medium* m ) const; + + virtual bool canCreateFromConfig() const; + + virtual DeviceHandler* createHandler( const KConfig* c ) const; + + virtual QString type() const; + +private: + bool excludedFilesystem( const QString &fstype ) const; +}; + +/** + @author Maximilian Kossick +*/ +class MassStorageDeviceHandler : public DeviceHandler +{ +public: + MassStorageDeviceHandler(); + MassStorageDeviceHandler(int deviceId, const QString &mountPoint, const QString &uuid ); + + virtual ~MassStorageDeviceHandler(); + + virtual bool isAvailable() const; + virtual QString type() const; + virtual int getDeviceID( ); + virtual const QString &getDevicePath() const; + virtual void getURL( KURL &absolutePath, const KURL &relativePath ); + virtual void getPlayableURL( KURL &absolutePath, const KURL &relativePath ); + virtual bool deviceIsMedium( const Medium *m ) const; + +private: + + int m_deviceID; + const QString m_mountPoint; + QString m_uuid; + +}; + +#endif diff --git a/amarok/src/device/nfs/Makefile.am b/amarok/src/device/nfs/Makefile.am new file mode 100644 index 00000000..7b7ab9cb --- /dev/null +++ b/amarok/src/device/nfs/Makefile.am @@ -0,0 +1,30 @@ +kde_module_LTLIBRARIES = libamarok_nfs-device.la +kde_services_DATA = amarok_nfs-device.desktop + +INCLUDES = \ + -I$(top_builddir)/amarok/src/amarokcore \ + -I$(top_srcdir)/amarok/src/amarokcore \ + -I$(top_builddir)/amarok/src/amarokcore \ + -I$(top_srcdir)/amarok/src/plugin \ + -I$(top_builddir)/amarok/src \ + -I$(top_srcdir)/amarok/src \ + $(all_includes) + +METASOURCES = AUTO + +libamarok_nfs_device_la_LIBADD = \ + $(top_builddir)/amarok/src/libamarok.la \ + $(LIB_KDECORE) $(LIB_QT) + +libamarok_nfs_device_la_LDFLAGS = \ + $(KDE_PLUGIN) \ + $(all_libraries) + + + + + + + +noinst_HEADERS = nfsdevicehandler.h +libamarok_nfs_device_la_SOURCES = nfsdevicehandler.cpp diff --git a/amarok/src/device/nfs/amarok_nfs-device.desktop b/amarok/src/device/nfs/amarok_nfs-device.desktop new file mode 100644 index 00000000..d513ea95 --- /dev/null +++ b/amarok/src/device/nfs/amarok_nfs-device.desktop @@ -0,0 +1,107 @@ +[Desktop Entry] +Encoding=UTF-8 +Type=Service +Name=NFS Device +Name[af]=NFS Toestel +Name[ar]=جهاز نظام الملفات على الشبكة NFS +Name[bg]=NFS устройство +Name[bn]=এন-এফ-এস ডিভাইস +Name[ca]=Dispositiu NFS +Name[cs]=NFS zařízení +Name[da]=NFS-enhed +Name[de]=NFS-Gerät +Name[el]=Συσκευή NFS +Name[eo]=NFS Aranĝaĵo +Name[es]=Dispositivo NFS +Name[et]=NFS-seade +Name[fa]=دستگاه NFS +Name[fi]=NFS-laite +Name[fr]=Périphérique NFS +Name[ga]=Gléas NFS +Name[gl]=Dispositivo NFS +Name[hu]=NFS-eszköz +Name[is]=NFS tæki +Name[it]=Dispositivo NFS +Name[ja]=NFS デバイス +Name[ka]=NFS მოწყობილობა +Name[km]=ឧបករណ៍ NFS +Name[lt]=NFS įrenginys +Name[mk]=NFS-уред +Name[ms]=Peranti NFS +Name[nb]=NFS-enhet +Name[nds]=NFS-Reedschap +Name[ne]=NFS यन्त्र +Name[nl]=NFS-apparaat +Name[nn]=NFS-eining +Name[pa]=NFS ਜੰਤਰ +Name[pl]=Urządzenie NFS +Name[pt]=Dispositivo NFS +Name[pt_BR]=Dispositivo NFS +Name[se]=NFS-ovttadat +Name[sk]=NFS zariadenie +Name[sr]=NFS уређај +Name[sr@Latn]=NFS uređaj +Name[sv]=NFS-enhet +Name[tr]=NFS Aygıtı +Name[uk]=Пристрій NFS +Name[uz]=NFS uskunasi +Name[uz@cyrillic]=NFS ускунаси +Name[wa]=Éndjin NFS +Name[zh_CN]=NFS 设备 +Name[zh_TW]=NFS 裝置 +Comment=Device plugin for Amarok which supports NFS +Comment[af]=Toestel inprop module vir Amarok wat NFS ondersteuning bied +Comment[bg]=Приставка за устройство за Amarok, поддържаща NFS +Comment[bn]=আমারক-এর জন্য ডিভাইস প্লাগিন যা এন-এফ-এস সমর্থন করে +Comment[ca]=Connector de dispositiu per a l'Amarok que accepta NFS +Comment[cs]=Modul zařízení pro AmaroK, které podporuje NFS +Comment[da]=Enhedsplugin for Amarok som understøtter NFS +Comment[de]=Geräte-Modul für Amarok mit Unterstützung für NFS +Comment[el]=Πρόσθετο συσκευής για το AmaroK με υποστήριξη NFS +Comment[eo]=Aranĝaĵa kromaĵo por Amarok kiu subtenas NFS-n +Comment[es]=Plugin de dispositivo con soporte NFS para Amarok +Comment[et]=Amaroki seadmeplugin NFS-i toega +Comment[fa]=وصلۀ دستگاه برای Amarok که از NFS پشتیبانی می‌کند +Comment[fi]=Amarok-liitännäinen, joka tukee NFS:ää (Network File System) +Comment[fr]=Plugin de périphérique pour Amarok qui supporte NFS +Comment[gl]=Extensión de dispositivos que soporten NFS para Amarok +Comment[hu]=NFS-t támogató eszköz-bővítőmodul az Amarokhoz +Comment[is]=Tækja íforrit fyrir Amarok með stuðning við NFS +Comment[it]=Plugin di dispositivo per Amarok con supporto NFS +Comment[ja]=NFS をサポートする Amarok のためのデバイスプラグイン +Comment[ka]=მოწყობილობის მოდული Amarok-ისთვის NFS მხარდაჭერით +Comment[km]=កម្មវិធី​ជំនួយ​ឧបករណ៍ សម្រាប់ Amarok ដែល​គាំទ្រ NFS +Comment[lt]=Amarok įrenginio priedas, palaikantis NFS +Comment[mk]=Приклучок за уред за Амарок што поддржува NFS +Comment[ms]=Plugin peranti untuk Amarok yang menyokong NFS +Comment[nb]=Amarok programtillegg for enhet som støtter NFS +Comment[nds]=Reedschapmoduul för Amarok, dat NFS ünnerstütt +Comment[ne]=अमारोकका लागि यन्त्र प्लगइन जसले NFS समर्थन गर्दछ +Comment[nl]=Apparaatplugin voor Amarok met ondersteuning voor NFS +Comment[nn]=Einingstillegg for Amarok som støttar NFS +Comment[pa]=ਅਮਰੋਕ ਲਈ ਜੰਤਰ ਪਲੱਗਇਨ, ਜੋ ਕਿ NFS ਲਈ ਸਹਾਇਕ ਹੈ +Comment[pl]=Wtyczka urządzenia dla Amaroka z obsługą NFS +Comment[pt]=Um 'plugin' de dispositivo para o Amarok que suporta o NFS +Comment[pt_BR]=Plugin de dispositivo para o Amarok, que suporta NFS +Comment[se]=Ovttadatlassemoduvla Amarokii mii doarju NFS +Comment[sk]=Modul zariadenia pre Amarok, ktorý podporuje NFS +Comment[sr]=Уређајски прикључак за Amarok који подржава NFS +Comment[sr@Latn]=Uređajski priključak za Amarok koji podržava NFS +Comment[sv]=Enhetsinsticksprogram för Amarok som stöder NFS +Comment[th]=โปรแกรมเสริมจัดการอุปกรณ์สำหรับ Amarok ที่รองรับการใช้งาน NFS +Comment[tr]=Amarok için NFS destekleyen aygıt eklentisi +Comment[uk]=Втулок пристроїв для Amarok, який підтримує NFS +Comment[wa]=Tchôke-divins d' éndjin po Amarok ki sopoite NFS +Comment[zh_CN]=Amarok 支持 NFS 的设备插件 +Comment[zh_TW]=支援 NFS 的 amaroK 裝置插件 +ServiceTypes=Amarok/Plugin + +X-KDE-Library=libamarok_nfs-device + +X-KDE-Amarok-authors=Maximilian Kossick +X-KDE-Amarok-email=maximilian.kossick@googlemail.com +X-KDE-Amarok-framework-version=32 +X-KDE-Amarok-name=nfs-device +X-KDE-Amarok-plugintype=device +X-KDE-Amarok-rank=100 +X-KDE-Amarok-version=1 diff --git a/amarok/src/device/nfs/nfsdevicehandler.cpp b/amarok/src/device/nfs/nfsdevicehandler.cpp new file mode 100644 index 00000000..274f76e5 --- /dev/null +++ b/amarok/src/device/nfs/nfsdevicehandler.cpp @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2006-2007 Maximilian Kossick + * + * 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 program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "nfsdevicehandler.h" + +AMAROK_EXPORT_PLUGIN( NfsDeviceHandlerFactory ) + +#include "debug.h" + +#include +#include + +#include + + +NfsDeviceHandler::NfsDeviceHandler( int deviceId, QString server, QString dir, QString mountPoint ) + : DeviceHandler() + , m_deviceID( deviceId ) + , m_mountPoint( mountPoint ) + , m_server( server ) + , m_dir( dir ) +{ +} + +NfsDeviceHandler::~NfsDeviceHandler() +{ +} + +bool +NfsDeviceHandler::isAvailable() const +{ + return true; +} + + +QString +NfsDeviceHandler::type() const +{ + return "nfs"; +} + +int +NfsDeviceHandler::getDeviceID() +{ + return m_deviceID; +} + +const QString & +NfsDeviceHandler::getDevicePath() const +{ + return m_mountPoint; +} + +void +NfsDeviceHandler::getURL( KURL &absolutePath, const KURL &relativePath ) +{ + absolutePath.setPath( m_mountPoint ); + absolutePath.addPath( relativePath.path() ); + absolutePath.cleanPath(); +} + +void +NfsDeviceHandler::getPlayableURL( KURL &absolutePath, const KURL &relativePath ) +{ + getURL( absolutePath, relativePath ); +} + +bool +NfsDeviceHandler::deviceIsMedium( const Medium * m ) const +{ + return m->deviceNode() == m_server + ':' + m_dir; +} + +/////////////////////////////////////////////////////////////////////////////// +// class NfsDeviceHandlerFactory +/////////////////////////////////////////////////////////////////////////////// + +QString +NfsDeviceHandlerFactory::type( ) const +{ + return "nfs"; +} + +bool +NfsDeviceHandlerFactory::canCreateFromMedium( ) const +{ + return true; +} + +bool +NfsDeviceHandlerFactory::canCreateFromConfig( ) const +{ + return false; +} + +bool +NfsDeviceHandlerFactory::canHandle( const Medium * m ) const +{ + return m && m->fsType() == "nfs" && m->isMounted(); +} + +NfsDeviceHandlerFactory::NfsDeviceHandlerFactory( ) +{ +} + +NfsDeviceHandlerFactory::~NfsDeviceHandlerFactory( ) +{ +} + +DeviceHandler * +NfsDeviceHandlerFactory::createHandler( const KConfig* ) const +{ + return 0; +} + +DeviceHandler * +NfsDeviceHandlerFactory::createHandler( const Medium * m ) const +{ + QString server = m->deviceNode().section( ":", 0, 0 ); + QString share = m->deviceNode().section( ":", 1, 1 ); + QStringList ids = CollectionDB::instance()->query( QString( "SELECT id, label, lastmountpoint " + "FROM devices WHERE type = 'nfs' " + "AND servername = '%1' AND sharename = '%2';" ) + .arg( server ) + .arg( share ) ); + if ( ids.size() == 3 ) + { + debug() << "Found existing NFS config for ID " << ids[0] << " , server " << server << " ,share " << share << endl; + CollectionDB::instance()->query( QString( "UPDATE devices SET lastmountpoint = '%2' WHERE " + "id = %1;" ).arg( ids[0] ).arg( m->mountPoint() ) ); + return new NfsDeviceHandler( ids[0].toInt(), server, share, m->mountPoint() ); + } + else + { + int id = CollectionDB::instance()->insert( QString( "INSERT INTO devices" + "( type, servername, sharename, lastmountpoint ) " + "VALUES ( 'nfs', '%1', '%2', '%3' );" ) + .arg( server ) + .arg( share ) + .arg( m->mountPoint() ), "devices" ); + if ( id == 0 ) + { + warning() << "Inserting into devices failed for type=nfs, server=" << server << ", share=" << share << endl; + return 0; + } + debug() << "Created new NFS device with ID " << id << " , server " << server << " ,share " << share << endl; + return new NfsDeviceHandler( id, server, share, m->mountPoint() ); + } +} + diff --git a/amarok/src/device/nfs/nfsdevicehandler.h b/amarok/src/device/nfs/nfsdevicehandler.h new file mode 100644 index 00000000..a17af7aa --- /dev/null +++ b/amarok/src/device/nfs/nfsdevicehandler.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2006-2007 Maximilian Kossick + * + * 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 program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef NFSDEVICEHANDLER_H +#define NFSDEVICEHANDLER_H + +#include + +class NfsDeviceHandlerFactory : public DeviceHandlerFactory +{ +public: + NfsDeviceHandlerFactory(); + virtual ~NfsDeviceHandlerFactory(); + + virtual bool canHandle( const Medium* m ) const; + + virtual bool canCreateFromMedium() const; + + virtual DeviceHandler* createHandler( const Medium* m ) const; + + virtual bool canCreateFromConfig() const; + + virtual DeviceHandler* createHandler( const KConfig* c ) const; + + virtual QString type() const; +}; + +/** + @author Maximilian Kossick +*/ +class NfsDeviceHandler : public DeviceHandler +{ +public: + NfsDeviceHandler(int deviceId, QString server, QString dir, QString mountPoint ); + + virtual ~NfsDeviceHandler(); + + virtual bool isAvailable() const; + virtual QString type() const; + virtual int getDeviceID( ); + virtual const QString &getDevicePath() const; + virtual void getURL( KURL &absolutePath, const KURL &relativePath ); + virtual void getPlayableURL( KURL &absolutePath, const KURL &relativePath ); + virtual bool deviceIsMedium( const Medium *m ) const; + +private: + + int m_deviceID; + const QString m_mountPoint; + QString m_server; + QString m_dir; + +}; + +#endif diff --git a/amarok/src/device/smb/Makefile.am b/amarok/src/device/smb/Makefile.am new file mode 100644 index 00000000..d5598a8a --- /dev/null +++ b/amarok/src/device/smb/Makefile.am @@ -0,0 +1,24 @@ +kde_module_LTLIBRARIES = libamarok_smb-device.la +kde_services_DATA = amarok_smb-device.desktop + +INCLUDES = \ + -I$(top_builddir)/amarok/src/amarokcore \ + -I$(top_srcdir)/amarok/src/amarokcore \ + -I$(top_builddir)/amarok/src/amarokcore \ + -I$(top_srcdir)/amarok/src/plugin \ + -I$(top_builddir)/amarok/src \ + -I$(top_srcdir)/amarok/src \ + $(all_includes) + +METASOURCES = AUTO + +libamarok_smb_device_la_LIBADD = \ + $(top_builddir)/amarok/src/libamarok.la \ + $(LIB_KDECORE) $(LIB_QT) + +libamarok_smb_device_la_LDFLAGS = \ + $(KDE_PLUGIN) \ + $(all_libraries) + +noinst_HEADERS = smbdevicehandler.h +libamarok_smb_device_la_SOURCES = smbdevicehandler.cpp diff --git a/amarok/src/device/smb/amarok_smb-device.desktop b/amarok/src/device/smb/amarok_smb-device.desktop new file mode 100644 index 00000000..57ee4590 --- /dev/null +++ b/amarok/src/device/smb/amarok_smb-device.desktop @@ -0,0 +1,106 @@ +[Desktop Entry] +Encoding=UTF-8 +Type=Service +Name=SMB Device +Name[af]=SMB Toestel +Name[ar]=جهاز SMB +Name[bg]=SMB устройство +Name[bn]=এস-এম‌-বি ডিভাইস +Name[ca]=Dispositiu SMB +Name[cs]=SMB zařízení +Name[da]=SMB-enhed +Name[de]=SMB-Gerät +Name[el]=Συσκευή SMB +Name[eo]=SMB Aranĝaĵo +Name[es]=Dispositivo SMB +Name[et]=SMB-seade +Name[fa]=دستگاه SMB +Name[fi]=SMB-laite +Name[fr]=Périphérique SMB +Name[ga]=Gléas SMB +Name[gl]=Dispositivo SMB +Name[hu]=SMB-eszköz +Name[is]=SMB tæki +Name[it]=Dispositivo SMB +Name[ja]=SMB デバイス +Name[ka]=SMB მოწყობილობა +Name[km]=ឧបករណ៍ SMB +Name[lt]=SMB įrenginys +Name[mk]=SMB-уред +Name[ms]=Peranti SMB +Name[nb]=SMB-enhet +Name[nds]=SMB-Reedschap +Name[ne]=SMB यन्त्र +Name[nl]=SMB-apparaat +Name[nn]=SMB-eining +Name[pa]=SMB ਜੰਤਰ +Name[pl]=Urządzenie SMB +Name[pt]=Dispositivo SMB +Name[pt_BR]=Dispositivo SMB +Name[se]=SMB-ovttadat +Name[sk]=SMB zariadenie +Name[sr]=SMB уређај +Name[sr@Latn]=SMB uređaj +Name[sv]=SMB-enhet +Name[tr]=SMB Aygıtı +Name[uk]=Пристрій SMB +Name[uz]=SMB uskunasi +Name[uz@cyrillic]=SMB ускунаси +Name[wa]=Éndjin SMB +Name[zh_CN]=SMB 设备 +Name[zh_TW]=MSB 裝置 +Comment=Device plugin for Amarok which supports SMBFS +Comment[af]=Toestel inprop module vir Amarok wat SMBFS ondersteuning bied +Comment[bg]=Приставка за устройство за Amarok, поддържаща SMBFS +Comment[bn]=আমারক-এর জন্য ডিভাইস প্লাগিন যা এসএম‌বিএফএস সমর্থন করে +Comment[ca]=Connector de dispositiu per a l'Amarok que accepta SMBFS +Comment[cs]=Modul zařízení pro AmaroK, které podporuje SMBFS +Comment[da]=Enhedsplugin for Amarok som understøtter SMBFS +Comment[de]=Geräte-Modul für Amarok mit Unterstützung für SMB-Dateisystem +Comment[el]=Πρόσθετο συσκευής για το AmaroK με υποστήριξη SMBFS +Comment[eo]=Aranĝaĵa kromaĵo por Amarok kiu subtenas SMBFS-n +Comment[es]=Plugin de dispositivo con soporte SMBFS para Amarok +Comment[et]=Amaroki seadmeplugin SMBFS-i toega +Comment[fa]=وصلۀ دستگاه برای Amarok که از SMBFS پشتیبانی می‌کند +Comment[fi]=Amarok-liitännäinen, joka tukee SMBFS:ää +Comment[fr]=Plugin de périphérique pour Amarok qui supporte SMBFS +Comment[gl]=Extensión de dispositivos que soporten SMBFS para Amarok +Comment[hu]=SMBFS-t támogató eszköz-bővítőmodul az Amarokhoz +Comment[is]=Tækja íforrit fyrir Amarok með stuðning við SMBFS +Comment[it]=Plugin di dispositivo per Amarok con supporto SMBFS +Comment[ja]=SMBFS をサポートする Amarok のためのデバイスプラグイン +Comment[km]=កម្មវិធី​ជំនួយ​ឧបករណ៍​សម្រាប់ Amarok ដែល​គាំទ្រ SMBFS +Comment[lt]=Amarok įrenginio priedas, palaikantis SMBFS +Comment[mk]=Приклучок за уред за Амарок што поддржува SMBFS +Comment[ms]=Plugmasuk peranti untuk Amarok yang menyokong SMBFS +Comment[nb]=Amarok programtillegg for enhet som støtter SMBFS +Comment[nds]=Reedschapmoduul för Amarok, dat SMBFS ünnerstütt +Comment[ne]=अमारोकका लागि यन्त्र प्लगइन जसले SMBFS समर्थन गर्दछ +Comment[nl]=Apparaatplugin voor Amarok met ondersteuning voor SMBFS +Comment[nn]=Einingstillegg for Amarok som støttar SMBFS +Comment[pa]=ਅਮਰੋਕ ਲਈ ਜੰਤਰ ਪਲੱਗਇਨ, ਜੋ ਕਿ SMBFS ਲਈ ਸਹਾਇਕ ਹੈ +Comment[pl]=Wtyczka urządzenia dla Amaroka obsługująca SMBFS +Comment[pt]=Um 'plugin' de dispositivo para o Amarok que suporta o SMBFS +Comment[pt_BR]=Plugin de dispositivo para o Amarok, que suporta SMBFS +Comment[se]=Ovttadatlassemoduvla Amarokii mii doarju SMBFS +Comment[sk]=Modul zariadenia pre Amarok, ktorý podporuje SMBFS +Comment[sr]=Уређајски прикључак за Amarok који подржава SMBFS +Comment[sr@Latn]=Uređajski priključak za Amarok koji podržava SMBFS +Comment[sv]=Enhetsinsticksprogram för Amarok som stöder SMBFS +Comment[th]=โปรแกรมเสริมจัดการอุปกรณ์สำหรับ Amarok ที่รองรับการใช้งาน SMBFS +Comment[tr]=Amarok için SMBFS destekleyen aygıt eklentisi +Comment[uk]=Втулок пристроїв для Amarok, який підтримує SMBFS +Comment[wa]=Tchôke-divins d' éndjin po Amarok ki sopoite SMBFS +Comment[zh_CN]=Amarok 支持 SMBFS 的设备插件 +Comment[zh_TW]=支援 SMBFS 的 amaroK 裝置插件 +ServiceTypes=Amarok/Plugin + +X-KDE-Library=libamarok_smb-device + +X-KDE-Amarok-authors=Maximilian Kossick +X-KDE-Amarok-email=maximilian.kossick@googlemail.com +X-KDE-Amarok-framework-version=32 +X-KDE-Amarok-name=smb-device +X-KDE-Amarok-plugintype=device +X-KDE-Amarok-rank=100 +X-KDE-Amarok-version=1 diff --git a/amarok/src/device/smb/smbdevicehandler.cpp b/amarok/src/device/smb/smbdevicehandler.cpp new file mode 100644 index 00000000..2e778fcd --- /dev/null +++ b/amarok/src/device/smb/smbdevicehandler.cpp @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2006-2007 Maximilian Kossick + * + * 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 program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "smbdevicehandler.h" + +AMAROK_EXPORT_PLUGIN( SmbDeviceHandlerFactory ) + +#include "debug.h" + +#include +#include + +#include + + +SmbDeviceHandler::SmbDeviceHandler( int deviceId, QString server, QString dir, QString mountPoint ) + : DeviceHandler() + , m_deviceID( deviceId ) + , m_mountPoint( mountPoint ) + , m_server( server ) + , m_dir( dir ) +{ +} + +SmbDeviceHandler::~SmbDeviceHandler() +{ +} + +bool +SmbDeviceHandler::isAvailable() const +{ + return true; +} + + +QString +SmbDeviceHandler::type() const +{ + return "smb"; +} + +int +SmbDeviceHandler::getDeviceID() +{ + return m_deviceID; +} + +const QString & +SmbDeviceHandler::getDevicePath() const +{ + return m_mountPoint; +} + +void +SmbDeviceHandler::getURL( KURL &absolutePath, const KURL &relativePath ) +{ + absolutePath.setPath( m_mountPoint ); + absolutePath.addPath( relativePath.path() ); + absolutePath.cleanPath(); +} + +void +SmbDeviceHandler::getPlayableURL( KURL &absolutePath, const KURL &relativePath ) +{ + getURL( absolutePath, relativePath ); +} + +bool +SmbDeviceHandler::deviceIsMedium( const Medium * m ) const +{ + return m->deviceNode() == m_server + ':' + m_dir; +} + +/////////////////////////////////////////////////////////////////////////////// +// class SmbDeviceHandlerFactory +/////////////////////////////////////////////////////////////////////////////// + +QString +SmbDeviceHandlerFactory::type( ) const +{ + return "smb"; +} + +bool +SmbDeviceHandlerFactory::canCreateFromMedium( ) const +{ + return true; +} + +bool +SmbDeviceHandlerFactory::canCreateFromConfig( ) const +{ + return false; +} + +bool +SmbDeviceHandlerFactory::canHandle( const Medium * m ) const +{ + return m && ( m->fsType().find( "smb" ) != -1 || + m->fsType().find( "cifs" ) != -1 ) + && m->isMounted(); +} + +SmbDeviceHandlerFactory::SmbDeviceHandlerFactory( ) +{ +} + +SmbDeviceHandlerFactory::~SmbDeviceHandlerFactory( ) +{ +} + +DeviceHandler * +SmbDeviceHandlerFactory::createHandler( const KConfig* ) const +{ + return 0; +} + +DeviceHandler * +SmbDeviceHandlerFactory::createHandler( const Medium * m ) const +{ + QString server = m->deviceNode().section( "/", 2, 2 ); + QString share = m->deviceNode().section( "/", 3, 3 ); + QStringList ids = CollectionDB::instance()->query( QString( "SELECT id, label, lastmountpoint " + "FROM devices WHERE type = 'smb' " + "AND servername = '%1' AND sharename = '%2';" ) + .arg( server ) + .arg( share ) ); + if ( ids.size() == 3 ) + { + debug() << "Found existing SMB config for ID " << ids[0] << " , server " << server << " ,share " << share << endl; + CollectionDB::instance()->query( QString( "UPDATE devices SET lastmountpoint = '%2' WHERE " + "id = %1;" ).arg( ids[0] ).arg( m->mountPoint() ) ); + return new SmbDeviceHandler( ids[0].toInt(), server, share, m->mountPoint() ); + } + else + { + int id = CollectionDB::instance()->insert( QString( "INSERT INTO devices" + "( type, servername, sharename, lastmountpoint ) " + "VALUES ( 'smb', '%1', '%2', '%3' );" ) + .arg( server ) + .arg( share ) + .arg( m->mountPoint() ), "devices" ); + if ( id == 0 ) + { + warning() << "Inserting into devices failed for type=smb, server=" << server << ", share=" << share << endl; + return 0; + } + debug() << "Created new SMB device with ID " << id << " , server " << server << " ,share " << share << endl; + return new SmbDeviceHandler( id, server, share, m->mountPoint() ); + } +} + diff --git a/amarok/src/device/smb/smbdevicehandler.h b/amarok/src/device/smb/smbdevicehandler.h new file mode 100644 index 00000000..e3b04cb0 --- /dev/null +++ b/amarok/src/device/smb/smbdevicehandler.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2006-2007 Maximilian Kossick + * + * 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 program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef SMBDEVICEHANDLER_H +#define SMBDEVICEHANDLER_H + +#include + +class SmbDeviceHandlerFactory : public DeviceHandlerFactory +{ +public: + SmbDeviceHandlerFactory(); + virtual ~SmbDeviceHandlerFactory(); + + virtual bool canHandle( const Medium* m ) const; + + virtual bool canCreateFromMedium() const; + + virtual DeviceHandler* createHandler( const Medium* m ) const; + + virtual bool canCreateFromConfig() const; + + virtual DeviceHandler* createHandler( const KConfig* c ) const; + + virtual QString type() const; +}; + +/** + @author Maximilian Kossick +*/ +class SmbDeviceHandler : public DeviceHandler +{ +public: + SmbDeviceHandler(int deviceId, QString server, QString dir, QString mountPoint ); + + virtual ~SmbDeviceHandler(); + + virtual bool isAvailable() const; + virtual QString type() const; + virtual int getDeviceID( ); + virtual const QString &getDevicePath() const; + virtual void getURL( KURL &absolutePath, const KURL &relativePath ); + virtual void getPlayableURL( KURL &absolutePath, const KURL &relativePath ); + virtual bool deviceIsMedium( const Medium *m ) const; + +private: + + int m_deviceID; + const QString m_mountPoint; + QString m_server; + QString m_dir; + +}; + +#endif diff --git a/amarok/src/deviceconfiguredialog.cpp b/amarok/src/deviceconfiguredialog.cpp new file mode 100644 index 00000000..c9781fe1 --- /dev/null +++ b/amarok/src/deviceconfiguredialog.cpp @@ -0,0 +1,158 @@ +// +// C++ Implementation: deviceconfiguredialog.cpp +// +// Description: +// +// +// Author: Jeff Mitchell , (C) 2006 +// Martin Aumueller , (C) 2005 +// +// Copyright: See COPYING file that comes with this distribution +// +// +#include "amarok.h" +#include "debug.h" +#include "deviceconfiguredialog.h" +#include "hintlineedit.h" +#include "mediabrowser.h" +#include "medium.h" +#include "plugin/pluginconfig.h" +#include "pluginmanager.h" +#include "scriptmanager.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +DeviceConfigureDialog::DeviceConfigureDialog( const Medium &medium ) + : KDialogBase( Amarok::mainWindow(), "deviceconfiguredialog", true, QString("Select Plugin for " + medium.name()), Ok|Cancel, Ok, false ) +{ + m_medium = new Medium( medium ); + kapp->setTopWidget( this ); + setCaption( kapp->makeStdCaption( i18n( "Configure Media Device" ) ) ); + showButtonApply( false ); + + QVBox* vbox = makeVBoxMainWidget(); + vbox->setSpacing( KDialog::spacingHint() ); + + QLabel *connectLabel = 0; + m_connectEdit = 0; + QLabel *disconnectLabel = 0; + m_disconnectEdit = 0; + m_transcodeCheck = 0; + QButtonGroup *transcodeGroup = 0; + m_transcodeAlways = 0; + m_transcodeWhenNecessary = 0; + m_transcodeRemove = 0; + + MediaDevice* device = MediaBrowser::instance()->deviceFromId( m_medium->id() ); + + if( device ) + { + device->loadConfig(); + + // pre-connect/post-disconnect (mount/umount) + connectLabel = new QLabel( vbox ); + connectLabel->setText( i18n( "Pre-&connect command:" ) ); + m_connectEdit = new HintLineEdit( device->m_preconnectcmd, vbox ); + m_connectEdit->setHint( i18n( "Example: mount %d" ) ); + connectLabel->setBuddy( m_connectEdit ); + QToolTip::add( m_connectEdit, i18n( "Set a command to be run before connecting to your device (e.g. a mount command) here.\n%d is replaced by the device node, %m by the mount point.\nEmpty commands are not executed." ) ); + + disconnectLabel = new QLabel( vbox ); + disconnectLabel->setText( i18n( "Post-&disconnect command:" ) ); + m_disconnectEdit = new HintLineEdit( device->m_postdisconnectcmd, vbox ); + disconnectLabel->setBuddy( m_disconnectEdit ); + m_disconnectEdit->setHint( i18n( "Example: eject %d" ) ); + QToolTip::add( m_disconnectEdit, i18n( "Set a command to be run after disconnecting from your device (e.g. an eject command) here.\n%d is replaced by the device node, %m by the mount point.\nEmpty commands are not executed." ) ); + + // transcode + m_transcodeCheck = new QCheckBox( vbox ); + m_transcodeCheck->setText( i18n( "&Transcode before transferring to device" ) ); + m_transcodeCheck->setChecked( device->m_transcode ); + + transcodeGroup = new QVButtonGroup( vbox ); + QString format = "mp3"; + if( !device->supportedFiletypes().isEmpty() ) + format = device->supportedFiletypes().first(); + transcodeGroup->setTitle( i18n( "Transcode to preferred format (%1) for device" ).arg( format ) ); + m_transcodeAlways = new QRadioButton( transcodeGroup ); + m_transcodeAlways->setText( i18n( "Whenever possible" ) ); + m_transcodeAlways->setChecked( device->m_transcodeAlways ); + m_transcodeWhenNecessary = new QRadioButton( transcodeGroup ); + m_transcodeWhenNecessary->setText( i18n( "When necessary" ) ); + m_transcodeWhenNecessary->setChecked( !device->m_transcodeAlways ); + connect( m_transcodeCheck, SIGNAL(toggled( bool )), + transcodeGroup, SLOT(setEnabled( bool )) ); + transcodeGroup->insert( m_transcodeAlways ); + transcodeGroup->insert( m_transcodeWhenNecessary ); + m_transcodeRemove = new QCheckBox( transcodeGroup ); + m_transcodeRemove->setText( i18n( "Remove transcoded files after transfer" ) ); + m_transcodeRemove->setChecked( device->m_transcodeRemove ); + + const ScriptManager *sm = ScriptManager::instance(); + m_transcodeCheck->setEnabled( sm->transcodeScriptRunning() != QString::null ); + transcodeGroup->setEnabled( sm->transcodeScriptRunning() != QString::null && device->m_transcode ); + if( sm->transcodeScriptRunning().isNull() ) + { + QToolTip::add( m_transcodeCheck, i18n( "For this feature, a script of type \"Transcode\" has to be running" ) ); + QToolTip::add( transcodeGroup, i18n( "For this feature, a script of type \"Transcode\" has to be running" ) ); + } + + device->addConfigElements( vbox ); + } + + m_accepted = false; +} + +DeviceConfigureDialog::~DeviceConfigureDialog() +{ + delete m_connectEdit; + delete m_disconnectEdit; + delete m_medium; +} + +void +DeviceConfigureDialog::slotCancel() +{ + KDialogBase::slotCancel( ); +} + +void +DeviceConfigureDialog::slotOk() +{ + m_accepted = true; + MediaDevice* device = MediaBrowser::instance()->deviceFromId( m_medium->id() ); + + if( device ) + { + device->m_preconnectcmd = m_connectEdit->text(); + device->setConfigString( "PreConnectCommand", device->m_preconnectcmd ); + device->m_postdisconnectcmd = m_disconnectEdit->text(); + device->setConfigString( "PostDisconnectCommand", device->m_postdisconnectcmd ); + device->setConfigBool( "Transcode", device->m_transcode ); + device->m_transcode = m_transcodeCheck->isChecked(); + device->setConfigBool( "Transcode", device->m_transcode ); + device->m_transcodeAlways = m_transcodeAlways->isChecked(); + device->setConfigBool( "TranscodeAlways", device->m_transcodeAlways ); + device->m_transcodeRemove = m_transcodeRemove->isChecked(); + device->setConfigBool( "TranscodeRemove", device->m_transcodeRemove ); + device->applyConfig(); + } + + MediaBrowser::instance()->updateButtons(); + MediaBrowser::instance()->updateStats(); + MediaBrowser::instance()->updateDevices(); + + KDialogBase::slotOk(); +} + +#include "deviceconfiguredialog.moc" diff --git a/amarok/src/deviceconfiguredialog.h b/amarok/src/deviceconfiguredialog.h new file mode 100644 index 00000000..920829e9 --- /dev/null +++ b/amarok/src/deviceconfiguredialog.h @@ -0,0 +1,55 @@ +// +// C++ Interface: deviceconfiguredialog.h +// +// Description: +// +// +// Author: Jeff Mitchell , (C) 2006 +// Martin Aumueller , (C) 2005 +// +// Copyright: See COPYING file that comes with this distribution +// +// +#ifndef DEVICECONFIGUREDIALOG_H +#define DEVICECONFIGUREDIALOG_H + +#include "hintlineedit.h" + +#include +#include + +#include + +class MediaBrowser; +class Medium; + +/** + @author Jeff Mitchell +*/ +class DeviceConfigureDialog : public KDialogBase +{ + Q_OBJECT + + public: + DeviceConfigureDialog( const Medium &medium ); + ~DeviceConfigureDialog(); + bool successful() { return m_accepted; }; + + private slots: + void slotOk(); + void slotCancel(); + + private: + bool m_accepted; + Medium* m_medium; + + HintLineEdit *m_connectEdit; + HintLineEdit *m_disconnectEdit; + QCheckBox *m_transcodeCheck; + QRadioButton *m_transcodeAlways; + QRadioButton *m_transcodeWhenNecessary; + QCheckBox *m_transcodeRemove; + +}; + +#endif diff --git a/amarok/src/devicemanager.cpp b/amarok/src/devicemanager.cpp new file mode 100644 index 00000000..e97e1274 --- /dev/null +++ b/amarok/src/devicemanager.cpp @@ -0,0 +1,284 @@ +// +// C++ Implementation: devicemanager +// +// Description: Controls device/medium object handling, providing +// helper functions for other objects +// +// +// Author: Jeff Mitchell , (C) 2006 +// Maximilian Kossick , (C) 2006 +// +// Copyright: See COPYING file that comes with this distribution +// +// + +#include "amarok.h" +#include "amarokconfig.h" +#include "debug.h" +#include "devicemanager.h" +#include "medium.h" +#include "mediumpluginmanager.h" + +#include +#include + +#include +#include +#include +#include + +typedef Medium::List MediumList; +typedef QMap::Iterator MediumIterator; + +DeviceManager* DeviceManager::instance() +{ + static DeviceManager dw; + return &dw; +} + +DeviceManager::DeviceManager() +{ + DEBUG_BLOCK + m_dc = KApplication::dcopClient(); + m_dc->setNotifications(true); + m_valid = false; + + if (!m_dc->isRegistered()) + { + debug() << "DeviceManager: DCOP Client not registered!" << endl; + } + else + { + if (!m_dc->connectDCOPSignal("kded", "mediamanager", "mediumAdded(QString)", "devices", "mediumAdded(QString)", false) || + !m_dc->connectDCOPSignal("kded", "mediamanager", "mediumRemoved(QString)", "devices", "mediumRemoved(QString)", false) || + !m_dc->connectDCOPSignal("kded", "mediamanager", "mediumChanged(QString)", "devices", "mediumChanged(QString)", false)) + { + debug() << "DeviceManager: Could not connect to signal mediumAdded/Removed/Changed!" << endl; + } + else + { + m_valid = true; + //run the DCOP query here because apparently if you don't run KDE as a DM the first call will fail + //...go figure + QByteArray data, replyData; + QCString replyType; + QDataStream arg(data, IO_WriteOnly); + QStringList result; + arg << 5; + if (!m_dc->call("kded", "mediamanager", "fullList()", data, replyType, replyData, false, -1)) + { + debug() << "During DeviceManager init, error during DCOP call" << endl; + } + reconcileMediumMap(); + debug() << "DeviceManager: connectDCOPSignal returned successfully!" << endl; + } + } +} + +DeviceManager::~DeviceManager() +{ + for( MediumIterator it = m_mediumMap.begin(); it != m_mediumMap.end(); it++ ) + delete (*it); +} + +void +DeviceManager::mediumAdded( const QString name ) +{ + DEBUG_BLOCK + if ( !m_valid ) + return; + Medium* addedMedium = getDevice(name); + if ( addedMedium != 0 ) + debug() << "[DeviceManager::mediumAdded] Obtained medium name is " << name << ", id is: " << addedMedium->id() << endl; + else + debug() << "[DeviceManager::mediumAdded] Obtained medium is null; name was " << name << endl; + emit mediumAdded( addedMedium, name ); +} + + +void +DeviceManager::mediumRemoved( const QString name ) +{ + DEBUG_BLOCK + if ( !m_valid ) + return; + Medium* removedMedium = 0; + if ( m_mediumMap.contains(name) ) + removedMedium = m_mediumMap[name]; + if ( removedMedium != 0 ) + debug() << "[DeviceManager::mediumRemoved] Obtained medium name is " << name << ", id is: " << removedMedium->id() << endl; + else + debug() << "[DeviceManager::mediumRemoved] Medium was unknown and is null; name was " << name << endl; + //if you get a null pointer from this signal, it means we did not know about the device + //before it was removed, i.e. the removal was the first event for the device received while amarok + //has been running + //There is no point in calling getDevice, since it will not be in the list anyways + emit mediumRemoved( removedMedium, name ); + if ( m_mediumMap.contains(name) ) + { + delete removedMedium; //If we are to remove it from the map, delete it first + m_mediumMap.remove(name); + } +} + + +void +DeviceManager::mediumChanged( const QString name ) +{ + DEBUG_BLOCK + if ( !m_valid ) + return; + Medium *changedMedium = getDevice(name); + if ( changedMedium != 0 ) + debug() << "[DeviceManager::mediumChanged] Obtained medium name is " << name << ", id is: " << changedMedium->id() << endl; + else + debug() << "[DeviceManager::mediumChanged] Obtained medium is null; name was " << name << endl; + emit mediumChanged( changedMedium, name ); +} + + +/* +BIG FAT WARNING: +Values returned from the below function should not be counted on being unique! +For instance, there may be a Medium object in the QMap that can be accessed through +other functions that has the same data as the Medium object returned, but is a different object. +As you should not be writing to this object, this is okay, however: + +Use the Medium's name or id, not the pointer value, for equality comparison!!! + +This function does rebuild the map every time it is called, however this should be rare enough +that it is not a problem. +*/ +Medium::List +DeviceManager::getDeviceList() +{ + return Medium::createList( getDeviceStringList() ); +} + +QStringList +DeviceManager::getDeviceStringList() +{ + DEBUG_BLOCK + MediumList currMediumList; + + if ( !m_valid ) + { + QStringList blah; + return blah; + } + + //normal kded Medium doesn't have autodetect, so decrease by 1 + int autodetect_insert = Medium::PROPERTIES_COUNT - 1; + + QByteArray data, replyData; + QCString replyType; + QDataStream arg(data, IO_WriteOnly); + QStringList result; + arg << 5; + if (!m_dc->call("kded", "mediamanager", "fullList()", data, replyType, replyData)) + { + debug() << "Error during DCOP call" << endl; + } + else + { + QDataStream reply(replyData, IO_ReadOnly); + while(!reply.atEnd()) + { + reply >> result; + } + QStringList::Iterator it; + for( it = result.begin(); it != result.end(); ++it ) + { + if (autodetect_insert == Medium::PROPERTIES_COUNT - 1) + result.insert(it, QString("true")); + autodetect_insert--; + if (autodetect_insert == -1) + autodetect_insert = Medium::PROPERTIES_COUNT - 1; + } + } + + return result; +} + +Medium* +DeviceManager::getDevice( const QString name ) +{ + DEBUG_BLOCK + if ( !m_valid ) + return 0; + debug() << "DeviceManager: getDevice called with name argument = " << name << endl; + Medium* returnedMedium = 0; + MediumList currMediumList = getDeviceList(); + + for ( Medium::List::iterator it = currMediumList.begin(); it != currMediumList.end(); ++it ) + { + if ( (*it).name() == name ) + { + MediumIterator secIt; + if ( (secIt = m_mediumMap.find( name )) != m_mediumMap.end() ) + { + //Refresh the Medium by reconstructing then copying it over. + returnedMedium = *secIt; + *returnedMedium = Medium( *it ); + } + else + { + //No previous version of this Medium - create it + returnedMedium = new Medium( *it ); + m_mediumMap[ name ] = returnedMedium; + } + break; + } + } + return returnedMedium; +} + +void +DeviceManager::reconcileMediumMap() +{ + DEBUG_BLOCK + if ( !m_valid ) + return; + + MediumList currMediumList = getDeviceList(); + + Medium::List::iterator it; + for ( it = currMediumList.begin(); it != currMediumList.end(); ++it ) + { + MediumIterator locIt; + if ( (locIt = m_mediumMap.find( (*it).name() )) != m_mediumMap.end() ) + { + Medium* mediumHolder = (*locIt); + *mediumHolder = Medium( *it ); + } + else + m_mediumMap[ (*it).name() ] = new Medium(*it); + } + + //Sanity check + if ( currMediumList.size() != m_mediumMap.size() ) + warning() << "Number of devices does not equal expected number" << endl; +} + +QString DeviceManager::convertMediaUrlToDevice( QString url ) +{ + QString device; + if ( url.startsWith( "media:" ) || url.startsWith( "system:" ) ) + { + KURL devicePath( url ); + DCOPRef mediamanager( "kded", "mediamanager" ); + DCOPReply reply = mediamanager.call( "properties(QString)", devicePath.fileName() ); + if ( reply.isValid() ) { + QStringList properties = reply; + device = properties[ 5 ]; + //kdDebug() << "DeviceManager::convertMediaUrlToDevice() munged to: " << device << "\n"; + } else + device = QString(); + } + else + device = url; + + return device; +} + +#include "devicemanager.moc" diff --git a/amarok/src/devicemanager.h b/amarok/src/devicemanager.h new file mode 100644 index 00000000..74470390 --- /dev/null +++ b/amarok/src/devicemanager.h @@ -0,0 +1,79 @@ +// +// C++ Interface: devicemanager +// +// Description: Controls device/medium object handling, providing +// helper functions for other objects +// +// +// Author: Jeff Mitchell , (C) 2006 +// Maximilian Kossick , (C) 2006 +// +// Copyright: See COPYING file that comes with this distribution +// +// + + +#ifndef AMAROK_DEVICE_MANAGER_H +#define AMAROK_DEVICE_MANAGER_H + +#include "medium.h" + +#include + +#include + +typedef QMap MediumMap; + + +//this class provides support for MountPointManager and MediaDeviceManager +//the latter is responsible for handling mediadevices (e.g. ipod) +//unless you have special requirements you should use either MountPointManager or +//MediaDeviceManager instead of this class. +class DeviceManager : public QObject +{ + + Q_OBJECT + public: + DeviceManager(); + ~DeviceManager(); + static DeviceManager *instance(); + + void mediumAdded( const QString name ); + void mediumChanged( const QString name); + void mediumRemoved( const QString name); + + MediumMap getMediumMap() { return m_mediumMap; } + Medium* getDevice( const QString name ); + // reconciles m_mediumMap to whatever kded has in it. + void reconcileMediumMap(); + + bool isValid() { return m_valid; } + + //only use getDeviceList to initialise clients + Medium::List getDeviceList(); + + //public so can be called from DCOP...but don't use this, see the + //warning about getDeviceList() + QStringList getDeviceStringList(); + + // Converts a media://media/hdc URL as provided by the KDE media + // manager on CD insert to /dev/hdc so amarok can play it. + // This method is safe to call with a device path, it returns it + // unchanged. + QString convertMediaUrlToDevice( QString url ); + + signals: + void mediumAdded( const Medium*, QString ); + void mediumChanged( const Medium*, QString ); + void mediumRemoved( const Medium*, QString ); + + private: + + DCOPClient *m_dc; + bool m_valid; + MediumMap m_mediumMap; + +}; + +#endif + diff --git a/amarok/src/directorylist.cpp b/amarok/src/directorylist.cpp new file mode 100644 index 00000000..30ae5bf5 --- /dev/null +++ b/amarok/src/directorylist.cpp @@ -0,0 +1,318 @@ +/*************************************************************************** + directorylist.cpp + ------------------- + begin : Tue Feb 4 2003 + copyright : (C) 2003 Scott Wheeler + : (C) 2004 Max Howell + : (C) 2004 Mark Kretschmann +***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#include "amarokconfig.h" +#include "directorylist.h" +#include "mountpointmanager.h" + +#include +#include + +#include +#include +#include +#include + + +CollectionSetup* CollectionSetup::s_instance; + + +CollectionSetup::CollectionSetup( QWidget *parent ) + : QVBox( parent, "CollectionSetup" ) +{ + s_instance = this; + + (new QLabel( i18n( + "These folders will be scanned for " + "media to make up your collection:"), this ))->setAlignment( Qt::WordBreak ); + + m_view = new QFixedListView( this ); + m_recursive = new QCheckBox( i18n("&Scan folders recursively"), this ); + m_monitor = new QCheckBox( i18n("&Watch folders for changes"), this ); + + QToolTip::add( m_recursive, i18n( "If selected, Amarok will read all subfolders." ) ); + QToolTip::add( m_monitor, i18n( "If selected, folders will automatically get rescanned when the content is modified, e.g. when a new file was added." ) ); + + // Read config values + //we have to detect if this is the actual first run and not get the collectionFolders in that case + //there won't be any anyway and accessing them creates a Sqlite database, even if the user wants to + //use another database + //bug 131719 131724 + if( !Amarok::config()->readBoolEntry( "First Run", true ) ) + m_dirs = MountPointManager::instance()->collectionFolders(); + + m_recursive->setChecked( AmarokConfig::scanRecursively() ); + m_monitor->setChecked( AmarokConfig::monitorChanges() ); + + m_view->addColumn( QString::null ); + m_view->setRootIsDecorated( true ); + m_view->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ); + m_view->setResizeMode( QListView::LastColumn ); + + reinterpret_cast(m_view->header())->hide(); + new Collection::Item( m_view ); + + setSpacing( 6 ); +} + + +void +CollectionSetup::writeConfig() +{ + //If we are in recursive mode then we don't need to store the names of the + //subdirectories of the selected directories + if ( recursive() ) + { + for ( QStringList::iterator it=m_dirs.begin(); it!=m_dirs.end(); ++it ) + { + QStringList::iterator jt=m_dirs.begin(); + while ( jt!=m_dirs.end() ) + { + if ( it==jt ) + { + ++jt; + continue; + } + //Note: all directories except "/" lack a trailing '/'. + //If (*jt) is a subdirectory of (*it) it is redundant. + //As all directories are subdirectories of "/", if "/" is selected, we + //can delete everything else. + if ( ( *jt ).startsWith( *it + '/' ) || *it=="/" ) + jt = m_dirs.remove( jt ); + else + ++jt; + } + } + } + + MountPointManager::instance()->setCollectionFolders( m_dirs ); + AmarokConfig::setScanRecursively( recursive() ); + AmarokConfig::setMonitorChanges( monitor() ); +} + + +////////////////////////////////////////////////////////////////////////////////////////// +// CLASS Item +////////////////////////////////////////////////////////////////////////////////////////// + +namespace Collection { + +Item::Item( QListView *parent ) + : QCheckListItem( parent, "/", QCheckListItem::CheckBox ) + , m_lister( true ) + , m_url( "file:/" ) + , m_listed( false ) + , m_fullyDisabled( false ) +{ + //Since we create the "/" checklistitem here, we need to enable it if needed + if ( CollectionSetup::instance()->m_dirs.contains( "/" ) ) + static_cast( this )->setOn(true); + m_lister.setDirOnlyMode( true ); + connect( &m_lister, SIGNAL(newItems( const KFileItemList& )), SLOT(newItems( const KFileItemList& )) ); + setOpen( true ); + setVisible( true ); +} + + +Item::Item( QListViewItem *parent, const KURL &url , bool full_disable /* default=false */ ) + : QCheckListItem( parent, url.fileName(), QCheckListItem::CheckBox ) + , m_lister( true ) + , m_url( url ) + , m_listed( false ) + , m_fullyDisabled( full_disable ) +{ + m_lister.setDirOnlyMode( true ); + setExpandable( true ); + connect( &m_lister, SIGNAL(newItems( const KFileItemList& )), SLOT(newItems( const KFileItemList& )) ); + connect( &m_lister, SIGNAL(completed()), SLOT(completed()) ); + connect( &m_lister, SIGNAL(canceled()), SLOT(completed()) ); +} + + +QString +Item::fullPath() const +{ + QString path; + + for( const QListViewItem *item = this; item != listView()->firstChild(); item = item->parent() ) + { + path.prepend( item->text( 0 ) ); + path.prepend( '/' ); + } + + return path; +} + + +void +Item::setOpen( bool b ) +{ + if ( !m_listed ) + { + m_lister.openURL( m_url, true ); + m_listed = true; + } + + QListViewItem::setOpen( b ); +} + + +void +Item::stateChange( bool b ) +{ + QStringList &cs_m_dirs = CollectionSetup::instance()->m_dirs; + + if ( isFullyDisabled() ) + return; + + if( CollectionSetup::instance()->recursive() ) + for( QListViewItem *item = firstChild(); item; item = item->nextSibling() ) + if ( dynamic_cast( item ) && !dynamic_cast( item )->isFullyDisabled() ) + static_cast(item)->QCheckListItem::setOn( b ); + + //If it is disabled, allow us to change its appearance (above code) but not add it + //to the list of folders (code below) + if ( isDisabled() ) + return; + + // Update folder list + QStringList::Iterator it = cs_m_dirs.find( m_url.path() ); + if ( isOn() ) { + if ( it == cs_m_dirs.end() ) + cs_m_dirs << m_url.path(); + + // Deselect subdirectories if we are in recursive mode as they are redundant + if ( CollectionSetup::instance()->recursive() ) + { + QStringList::Iterator diriter = cs_m_dirs.begin(); + while ( diriter != cs_m_dirs.end() ) + { + // Since the dir "/" starts with '/', we need a hack to stop it removing + // itself (it being the only path with a trailing '/') + if ( (*diriter).startsWith( m_url.path(1) ) && *diriter != "/" ) + diriter = cs_m_dirs.erase(diriter); + else + ++diriter; + } + } + } + else { + //Deselect item and recurse through children but only deselect children if they + //do not exist unless we are in recursive mode (where no children should be + //selected if the parent is being unselected) + //Note this does not do anything to the checkboxes, but they should be doing + //the same thing as we are (hopefully) + //Note: all paths lack a trailing '/' except for "/", which must be handled as a + //special case + if ( it != cs_m_dirs.end() ) + cs_m_dirs.erase( it ); + QStringList::Iterator diriter = cs_m_dirs.begin(); + while ( diriter != cs_m_dirs.end() ) + { + if ( (*diriter).startsWith( m_url.path(1) ) ) //path(1) adds a trailing '/' + { + if ( CollectionSetup::instance()->recursive() || + !QFile::exists( *diriter ) ) + { + diriter = cs_m_dirs.erase(diriter); + } + else + ++diriter; + } + else + ++diriter; + } + } + + // Redraw parent items + listView()->triggerUpdate(); +} + + +void +Item::activate() +{ + if( !isDisabled() ) + QCheckListItem::activate(); +} + + +void +Item::newItems( const KFileItemList &list ) //SLOT +{ + for( KFileItemListIterator it( list ); *it; ++it ) + { + //Fully disable (always appears off and grayed-out) if it is "/proc", "/sys" or + //"/dev" or one of their children. This is because we will never scan them, so we + //might as well show that. + //These match up with the skipped dirs in CollectionScanner::readDir. + bool fully_disable=false; + + if ( this->m_url.fileName().isEmpty() && ( ( *it )->url().fileName()=="proc" + || ( *it )->url().fileName()=="dev" || ( *it )->url().fileName()=="sys" ) ) + { + fully_disable=true; + } + + Item *item = new Item( this, (*it)->url() , fully_disable || this->isFullyDisabled() ); + + if ( !item->isFullyDisabled() ) + { + if( CollectionSetup::instance()->recursive() && isOn() || + CollectionSetup::instance()->m_dirs.contains( item->fullPath() ) ) + { + item->setOn( true ); + } + } + + item->setPixmap( 0, (*it)->pixmap( KIcon::SizeSmall ) ); + } +} + + +void +Item::paintCell( QPainter * p, const QColorGroup & cg, int column, int width, int align ) +{ + bool dirty = false; + QStringList &cs_m_dirs = CollectionSetup::instance()->m_dirs; + + // Figure out if a child folder is activated + for ( QStringList::const_iterator iter = cs_m_dirs.begin(); iter != cs_m_dirs.end(); + ++iter ) + if ( ( *iter ).startsWith( m_url.path(1) ) ) + if ( *iter != "/" ) // "/" should not match as a child of "/" + dirty = true; + + // Use a different color if this folder has an activated child folder + const QFont f = p->font(); + QColorGroup _cg = cg; + if ( dirty ) + { + _cg.setColor( QColorGroup::Text, listView()->colorGroup().link() ); + QFont font = p->font(); + font.setBold( !font.bold() ); + p->setFont( font ); + } + + QCheckListItem::paintCell( p, isDisabled() ? listView()->palette().disabled() : _cg, column, width, align ); + p->setFont( f ); +} + +} //namespace Collection + +#include "directorylist.moc" diff --git a/amarok/src/directorylist.h b/amarok/src/directorylist.h new file mode 100644 index 00000000..d6954d98 --- /dev/null +++ b/amarok/src/directorylist.h @@ -0,0 +1,100 @@ +/*************************************************************************** + directorylist.h + ------------------- + begin : Tue Feb 4 2003 + copyright : (C) 2003 Scott Wheeler + : (C) 2004 Max Howell + : (C) 2004 Mark Kretschmann +***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#ifndef AMAROK_DIRECTORYLIST_H +#define AMAROK_DIRECTORYLIST_H + +#include //inlined functions +#include //baseclass +#include //baseclass + +#include //stack allocated +#include //stack allocated + + +namespace Collection { class Item; } + +class QFixedListView : public QListView +// Reimplement sizeHint to have directorylist not being too big for "low" (1024x768 is not exactly low) resolutions +{ +public: + QFixedListView ( QWidget * parent = 0, const char * name = 0, WFlags f = 0 ) + :QListView(parent, name, f) {}; + QSize sizeHint() const + { + return QSize(400, 100); + } + +}; + +class CollectionSetup : public QVBox +{ + friend class Collection::Item; + +public: + static CollectionSetup* instance() { return s_instance; } + + CollectionSetup( QWidget* ); + void writeConfig(); + + QStringList dirs() const { return m_dirs; } + bool recursive() const { return m_recursive->isChecked(); } + bool monitor() const { return m_monitor->isChecked(); } + +private: + static CollectionSetup* s_instance; + + QFixedListView *m_view; + QStringList m_dirs; + QCheckBox *m_recursive; + QCheckBox *m_monitor; +}; + + +namespace Collection { //just to keep it out of the global namespace + +class Item : public QObject, public QCheckListItem +{ +Q_OBJECT +public: + Item( QListView *parent ); + Item( QListViewItem *parent, const KURL &url , bool full_disable=false ); + + QCheckListItem *parent() const { return static_cast( QListViewItem::parent() ); } + bool isFullyDisabled() const { return m_fullyDisabled; } + bool isDisabled() const { return isFullyDisabled() || ( CollectionSetup::instance()->recursive() && parent() && parent()->isOn() ); } + QString fullPath() const; + + void setOpen( bool b ); // reimpl. + void stateChange( bool ); // reimpl. + void activate(); // reimpl. + void paintCell( QPainter * p, const QColorGroup & cg, int column, int width, int align ); // reimpl. + +public slots: + void newItems( const KFileItemList& ); + void completed() { if( childCount() == 0 ) { setExpandable( false ); repaint(); } } + +private: + KDirLister m_lister; + KURL m_url; + bool m_listed; + bool m_fullyDisabled; +}; +} + +#endif diff --git a/amarok/src/dynamicmode.cpp b/amarok/src/dynamicmode.cpp new file mode 100644 index 00000000..e7950e49 --- /dev/null +++ b/amarok/src/dynamicmode.cpp @@ -0,0 +1,399 @@ +/*************************************************************************** + * copyright : (C) 2005 Seb Ruiz * + * copyright : (C) 2006 Gábor Lehel * + * copyright : (C) 2006 Bonne Eggleston // random func + +#include +#include + +///////////////////////////////////////////////////////////////////////////// +/// CLASS DynamicMode +//////////////////////////////////////////////////////////////////////////// + +DynamicMode::DynamicMode( const QString &name ) + : m_title( name ) + , m_cycle( true ) + , m_upcoming( 20 ) + , m_previous( 5 ) + , m_appendType( RANDOM ) +{ +} + +DynamicMode::~DynamicMode() +{} + +void +DynamicMode::deleting() +{ + if( this == Playlist::instance()->dynamicMode() ) + Playlist::instance()->disableDynamicMode(); +} + +void +DynamicMode::edit() +{ + if( this == Playlist::instance()->dynamicMode() ) + Playlist::instance()->editActiveDynamicMode(); //so the changes get noticed + else + ConfigDynamic::editDynamicPlaylist( PlaylistWindow::self(), this ); +} + +QStringList DynamicMode::items() const { return m_items; } + +QString DynamicMode::title() const { return m_title; } +bool DynamicMode::cycleTracks() const { return m_cycle; } +int DynamicMode::upcomingCount() const { return m_upcoming; } +int DynamicMode::previousCount() const { return m_previous; } +int DynamicMode::appendType() const { return m_appendType; } + +void DynamicMode::setItems( const QStringList &list ) { m_items = list; } +void DynamicMode::setCycleTracks( bool e ) { m_cycle = e; } +void DynamicMode::setUpcomingCount( int c ) { m_upcoming = c; } +void DynamicMode::setPreviousCount( int c ) { m_previous = c; } +void DynamicMode::setAppendType( int type ) { m_appendType = type; } +void DynamicMode::setTitle( const QString& title ) { m_title = title; } + +void DynamicMode::setDynamicItems( QPtrList& newList ) +{ +DEBUG_BLOCK + + QStringList strListEntries; + PlaylistBrowserEntry* entry; + QPtrListIterator it( newList ); + + while( (entry = it.current()) != 0 ) + { + ++it; + strListEntries << entry->text(0); + } + + setItems( strListEntries ); + PlaylistBrowser::instance()->saveDynamics(); + + rebuildCachedItemSet(); +} + +void DynamicMode::rebuildCachedItemSet() +{ +DEBUG_BLOCK + + m_cachedItemSet.clear(); + + if( appendType() == RANDOM || appendType() == SUGGESTION ) + { + QueryBuilder qb; + qb.setOptions( QueryBuilder::optRandomize | QueryBuilder::optRemoveDuplicates ); + qb.addReturnValue( QueryBuilder::tabSong, QueryBuilder::valURL ); + + if( appendType() == SUGGESTION ) + { + // TODO some clever stuff here for spanning across artists + QString artist = EngineController::instance()->bundle().artist(); + + if( artist.isEmpty() ) + { + PlaylistItem *currentItem = Playlist::instance()->currentItem(); + if( currentItem != 0 ) + artist = currentItem->artist(); + } + + debug() << "seeding from: " << artist << endl; + + QStringList suggestions = CollectionDB::instance()->similarArtists( artist, 16 ); + // for this artist, choose 4 suggested artists at random, to get further suggestions from + QStringList newChosen; + for( uint suggestCount = 0; suggestCount < 4; ++suggestCount ) + { + if( suggestions.isEmpty() ) + break; + + QString chosen = suggestions[ KApplication::random() % suggestions.count() ]; + + debug() << "found similar artist: " << chosen << endl; + + QStringList newSuggestions = CollectionDB::instance()->similarArtists( chosen, 10 ); + for( uint c = 0; c < 4; ++c ) // choose another 4 artists + { + if( newSuggestions.isEmpty() ) + break; + + QString s = newSuggestions[ KApplication::random() % newSuggestions.count() ]; + + debug() << "found extended similar artist: " << s << endl; + newChosen += s; + newSuggestions.remove( s ); + } + suggestions.remove( chosen ); + } + if ( !newChosen.isEmpty() ) + suggestions += newChosen; + qb.addMatches( QueryBuilder::tabArtist, suggestions ); + } + + qb.setLimit( 0, CACHE_SIZE ); + debug() << "Using SQL: " << qb.query() << endl; + + QStringList urls = qb.run(); + + foreach( urls ) //we have to run setPath on all raw paths + { + KURL current; + current.setPath( *it ); + m_cachedItemSet += current; + } + } + + else + { + PlaylistBrowser *pb = PlaylistBrowser::instance(); + QPtrList dynamicEntries = pb->dynamicEntries(); + if( !dynamicEntries.count() ) + { + Amarok::StatusBar::instance()->longMessage( i18n( "This dynamic playlist has no sources set." ), + KDE::StatusBar::Sorry ); + return; + } + // Create an array of the sizes of each of the playlists + QValueVector trackCount(dynamicEntries.count()) ; + int trackCountTotal = 0; + + for( uint i=0; i < dynamicEntries.count(); i++ ){ + trackCount[i] = 0; + + if ( QListViewItem *item = dynamicEntries.at( i ) ){ + if( item->rtti() == PlaylistEntry::RTTI ) + trackCount[i] = static_cast(item)->tracksURL().count(); + else if( item->rtti() == SmartPlaylist::RTTI ) + trackCount[i] = static_cast(item)->length(); + + trackCountTotal += trackCount[i]; + } + } + + + PlaylistBrowserEntry* entry; + QPtrListIterator it( dynamicEntries ); + + //const int itemsPerSource = CACHE_SIZE / dynamicEntries.count() != 0 ? CACHE_SIZE / dynamicEntries.count() : 1; + + int i = 0; + while( (entry = it.current()) != 0 ) + { + ++it; + //trackCountTotal might be 0 + int itemsForThisSource = trackCountTotal ? CACHE_SIZE * trackCount[i] / trackCountTotal : 1; + if (itemsForThisSource == 0) + itemsForThisSource = 1; + debug() << "this source will return " << itemsForThisSource << " entries" << endl; + + if( entry->rtti() == PlaylistEntry::RTTI ) + { + KURL::List t = tracksFromStaticPlaylist( static_cast(entry), itemsForThisSource); + m_cachedItemSet += t; + } + + else if( entry->rtti() == SmartPlaylist::RTTI ) + { + KURL::List t = tracksFromSmartPlaylist( static_cast(entry), itemsForThisSource); + m_cachedItemSet += t; + } + i++; + } + } +} + +KURL::List DynamicMode::tracksFromStaticPlaylist( PlaylistEntry *item, uint songCount ) +{ +DEBUG_BLOCK + + KURL::List trackList = item->tracksURL(); + KURL::List returnList; + + for( uint i=0; i < songCount; ) + { + if( trackList.isEmpty() ) + break; + + KURL::List::Iterator urlIt = trackList.at( KApplication::random() % trackList.count() ); + if( (*urlIt).isValid() ) + { + returnList << (*urlIt).path(); + ++i; + } + trackList.remove( urlIt ); + } + + debug() << "Returning " << returnList.count() << " tracks from " << item->text(0) << endl; + + return returnList; +} + +KURL::List DynamicMode::tracksFromSmartPlaylist( SmartPlaylist *item, uint songCount ) +{ +DEBUG_BLOCK + if( !item || !songCount ) + return KURL::List(); + + bool useDirect = true; + const bool hasTimeOrder = item->isTimeOrdered(); + debug() << "The smart playlist: " << item->text(0) << ", time order? " << hasTimeOrder << endl; + + QString sql = item->query(); + + // FIXME: All this SQL magic out of collectiondb is not a good thing + + // if there is no ordering, add random ordering + if ( sql.find( QString("ORDER BY"), false ) == -1 ) + { + QRegExp limit( "(LIMIT.*)?;$" ); + sql.replace( limit, QString(" ORDER BY %1 LIMIT %2 OFFSET 0;") + .arg( CollectionDB::instance()->randomFunc() ) + .arg( songCount ) ); + } + else + { + uint limit=0, offset=0; + + QRegExp limitSearch( "LIMIT.*(\\d+).*OFFSET.*(\\d+)" ); + int findLocation = limitSearch.search( sql, 0 ); + if( findLocation == -1 ) //not found, let's find out the higher limit the hard way + { + QString counting( sql ); + counting.replace( QRegExp( "SELECT.*FROM" ), "SELECT COUNT(*) FROM" ); + // Postgres' grouping rule doesn't like the following clause + counting.replace( QRegExp( "ORDER BY.*" ), "" ); + QStringList countingResult = CollectionDB::instance()->query( counting ); + limit = countingResult[0].toInt(); + } + else + { // There's a Limit, we have to respect it. + // capturedTexts() gives us the strings that were matched by each subexpression + offset = limitSearch.capturedTexts()[2].toInt(); + limit = limitSearch.capturedTexts()[1].toInt(); + } + + // we must be ordering by some other arbitrary query. + // we can scrap it, since it won't affect our result + if( !hasTimeOrder ) + { + // We can mess with the limits if the smart playlist is not orderd by a time criteria + // Why? We can have a smart playlist which is ordered by name or by some other quality which + // is meaningless in dynamic mode + QRegExp orderLimit( "(ORDER BY.*)?;$" ); + + sql.replace( orderLimit, QString(" ORDER BY %1 LIMIT %2 OFFSET 0;") + .arg( CollectionDB::instance()->randomFunc() ) + .arg( songCount ) ); + } + else // time ordered criteria, only mess with the limits + { + debug() << "time based criteria used!" << endl; + if ( limit <= songCount ) // The list is even smaller than the number of songs we want :-( + songCount = limit; + else + // Let's get a random limit, repecting the original one. + offset += KApplication::random() % (limit - songCount); + + if( findLocation == -1 ) // there is no limit + { + QRegExp queryEnd( ";$" ); // find the end of the query an add a limit + sql.replace( queryEnd, QString(" LIMIT %1 OFFSET %2;" ).arg( songCount*5 ).arg( offset ) ); + useDirect = false; + } + else // there is a limit, so find it and replace it + sql.replace( limitSearch, QString(" LIMIT %1 OFFSET %2;" ).arg( songCount ).arg( offset ) ); + } + } + + // only return the fields that we need + sql.replace( QRegExp( "SELECT.*FROM" ), "SELECT tags.url, tags.deviceid FROM" ); + QStringList queryResult = CollectionDB::instance()->query( sql ); + QStringList items; + + debug() << "Smart Playlist: adding urls from query: " << sql << endl; + if ( !item->query().isEmpty() ) + //We have to filter all the un-needed results from query( sql ) + for( uint x=0; x < queryResult.count() ; x += 2 ) + items << MountPointManager::instance()->getAbsolutePath( queryResult[x+1].toInt(), queryResult[x] ); + else + items = queryResult; + + + KURL::List urls; + foreach( items ) //we have to run setPath on all raw paths + { + KURL tmp; + tmp.setPath( *it ); + urls << tmp; + } + KURL::List addMe; + + // we have to randomly select tracks from the returned query since we can't have + // ORDER BY RAND() for some statements + if( !useDirect ) + { + for( uint i=0; i < songCount && urls.count(); i++ ) + { + KURL::List::iterator newItem = urls.at( KApplication::random() % urls.count() ); + addMe << (*newItem); + urls.remove( newItem ); + } + } + + useDirect ? + debug() << "Returning " << urls.count() << " tracks from " << item->text(0) << endl: + debug() << "Returning " << addMe.count() << " tracks from " << item->text(0) << endl; + + return useDirect ? urls : addMe; +} + + +KURL::List DynamicMode::retrieveTracks( const uint trackCount ) +{ +DEBUG_BLOCK + KURL::List retrieval; + + // always rebuild with suggested mode since the artists will be changing + if( m_cachedItemSet.count() <= trackCount || appendType() == SUGGESTION ) + rebuildCachedItemSet(); + + for( uint i=0; i < trackCount; i++ ) + { + if( m_cachedItemSet.isEmpty() ) + break; + const int pos = KApplication::random() % m_cachedItemSet.count(); + KURL::List::iterator newItem = m_cachedItemSet.at( pos ); + if( QFile::exists( (*newItem).path() ) ) + retrieval << (*newItem); + m_cachedItemSet.remove( newItem ); + } + + return retrieval; +} diff --git a/amarok/src/dynamicmode.h b/amarok/src/dynamicmode.h new file mode 100644 index 00000000..8ae8a544 --- /dev/null +++ b/amarok/src/dynamicmode.h @@ -0,0 +1,124 @@ +/*************************************************************************** + * copyright : (C) 2005 Seb Ruiz * + * copyright : (C) 2006 Gábor Lehel * + ***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +/*************************************************************************** + * Infiltrations of Dynamic Mode * + * Dynamic mode is a complex playlist handling mechanism - acting * + * basically on the concept of a 'rotating' playlist. The playlist can * + * be modelled as a queuing system, FIFO. As a track is advanced, * + * the first track in the playlist is removed, and another appended to * + * the end. The type of addition is selected by the user during * + * configuration. * + * * + * Due to the nature of this type of handling, the status of dynamicmode * + * must be determined, as many function require alternate handling. * + * Examples include: * + * - Context Menus * + * - Double clicking on an item -> requires moving the item to * + * front of the queue * + * - Undo/Redo states, to reinit history items * + * Please be aware of these when working with dynamic mode. * + ***************************************************************************/ + +#ifndef AMAROK_DYNAMIC_H +#define AMAROK_DYNAMIC_H + +#include //KURL::List + +class QString; +class QStringList; +template class QPtrList; +class PlaylistBrowserEntry; +class PlaylistEntry; +class SmartPlaylist; + +class DynamicMode +{ + public: + DynamicMode( const QString &name ); + virtual ~DynamicMode(); + enum Type { RANDOM=0, SUGGESTION=1, CUSTOM=2 }; + + void edit(); + void deleting(); + void setDynamicItems( QPtrList& newList ); + + /** + * Retrieves \p tracks from the cache, \p m_cachedItemSet + */ + KURL::List retrieveTracks( const uint trackCount ); + + /** + * Creates a list of \p CACHE_SIZE urls, stored in \p m_cachedItemSet in order + * to increase efficiency of dynamic mode population. This should be called + * when the dynamic sources are changed, or the cache runs out of items + */ + void rebuildCachedItemSet(); + + QString title() const; + QStringList items() const; + bool cycleTracks() const; + int upcomingCount() const; + int previousCount() const; + int appendType() const; + + void setAppendType( int type ); + void setCycleTracks( bool cycle ); + void setItems( const QStringList &list ); + void setUpcomingCount( int count ); + void setPreviousCount( int count ); + void setTitle( const QString& title ); + + private: + static const int CACHE_SIZE = 200; ///< the number of items to store in the cached set + + /** + * Returns a list of \p songCount urls from \p item - to be stored as part of + * the dynamic element cache, \p m_cachedItemSet + * + * This function will alter the sql statement of the item in order to return an + * adequate subset of data after execution. Limits and ordering attributes + * within the statement will be respected (to a certain extent). + */ + KURL::List tracksFromSmartPlaylist( SmartPlaylist *item, uint songCount ); + + /** + * Returns a list of \p songCount urls from \p item - to be stored as part of + * the dynamic element cache, \p m_cachedItemSet + * + * This function will return a random selection of elements from within the + * playlist given, in order to give some diversity when rebuilding the cache. + */ + KURL::List tracksFromStaticPlaylist( PlaylistEntry *item, uint songCount ); + + /** + * A list of urls which satisfy at least one of the dynamic mode sources. As tracks + * are added to the playlist, they are removed from the cache. When the cache expires, + * it should be rebuilt. + * + * The cache is used to reduce the number of database queries required when adding and + * removing tracks from the playlist when using dynamic mode. + */ + KURL::List m_cachedItemSet; + + QStringList m_items; + + QString m_title; + bool m_cycle; + int m_upcoming; + int m_previous; + int m_appendType; +}; + +#endif //AMAROK_DYNAMIC_H diff --git a/amarok/src/editfilterdialog.cpp b/amarok/src/editfilterdialog.cpp new file mode 100644 index 00000000..75764317 --- /dev/null +++ b/amarok/src/editfilterdialog.cpp @@ -0,0 +1,791 @@ +// (c) 2006 Giovanni Venturi +// See COPYING file for licensing information. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "amarokcore/amarokconfig.h" +#include "collectiondb.h" +#include "debug.h" +#include "editfilterdialog.h" +#include "metabundle.h" + +EditFilterDialog::EditFilterDialog( QWidget* parent, bool metaBundleKeywords, const QString &text ) + : KDialogBase( Plain, i18n("Edit Filter"), User1|User2|Default|Ok|Cancel, + Cancel, parent, "editfilter", /*modal*/true, /*separator*/false ), + m_minMaxRadio(0), + m_filterText(text) +{ + // Redefine "Default" button + KGuiItem defaultButton( i18n("&Append"), "add" ); + setButtonWhatsThis( Default, i18n( "

    By clicking here you can add the defined condition. The \"OK\" button will " + "close the dialog and apply the defined filter. With this button you can add more than " + "one condition to create a more complex filtering condition.

    " ) ); + setButtonTip(Default, i18n( "Add this filter condition to the list" ) ); + setButtonGuiItem( Default, defaultButton ); + + // define "User1" button + KGuiItem user1Button( i18n("&Clear"), "remove" ); + setButtonWhatsThis( User1, i18n( "

    By clicking here you will clear the filter. If you intend to " + "undo the last appending just click on the \"Undo\" button.

    " ) ); + setButtonTip(User1, i18n( "Clear the filter" ) ); + setButtonGuiItem( User1, user1Button ); + + // define "User2" button + KGuiItem user2Button( i18n("this \"undo\" will undo the last appended filter... be careful how you will translate it " + "to avoid two buttons (\"Cancel\" and \"Undo\") with same label in the same dialog", "&Undo"), "undo" ); + setButtonWhatsThis( User2, i18n( "

    Clicking here will remove the last appended filter. " + "You cannot undo more than one action.

    " ) ); + setButtonTip(User2, i18n( "Remove last appended filter" ) ); + setButtonGuiItem( User2, user2Button ); + + m_mainLay = new QVBoxLayout( plainPage() ); + m_mainLay->activate(); + + // no filter rule available + m_appended = false; + + // text explanation of this dialog + QLabel *label1 = new QLabel( plainPage(), "label1" ); + label1->setText( i18n("

    Edit the filter for finding tracks with specific attributes" + ", e.g. you can look for a track that has a length of three minutes.

    ") ); + m_mainLay->addWidget( label1 ); + m_mainLay->addItem( new QSpacerItem( 10, 10, QSizePolicy::Expanding, QSizePolicy::Minimum ) ); + + // choosing keyword filtering + QHBoxLayout *keywordLayout = new QHBoxLayout( plainPage() ); + QLabel *label3 = new QLabel( i18n("Attribute:"), plainPage(), "label3" ); + QWhatsThis::add( label3, + i18n("you can translate the keyword as you will do for the combobox", + "

    Here you can choose to Simple Search directly or to use " + "some keywords to specify some attributes, such as the artist name " + "and so on. The keywords selectable are divided by their specific value. " + "Some keywords are numeric and others are alphanumeric. You do not need " + "to know it directly. When a keyword is numeric it will be used to search " + "the numeric data for each track.

    The alphanumeric " + "keywords are the following: album, artist, filename " + " (including path), mountpoint (e.g. /home/user1), filetype " + " (you can specify mp3, ogg, flac, ... and the file extensions will be matched), " + "genre, comment, composer, directory, lyrics, " + "title, and label.

    " + "

    The numeric keywords are: bitrate, disc/discnumber, " + "length (expressed in seconds), playcount, rating, " + "samplerate, score, size/filesize (expressed in bytes, " + "kbytes, and megabytes as specified in the unit for the filesize keyword), " + "track (i.e. the track number), and year.

    ") ); + keywordLayout->addWidget( label3 ); + keywordLayout->addItem( new QSpacerItem( 5, 10, QSizePolicy::Minimum, QSizePolicy::Minimum ) ); + m_comboKeyword = new QComboBox( plainPage(), "keywordComboBox"); + QToolTip::add( m_comboKeyword, i18n("Select an attribute for the filter") ); + label3->setBuddy( m_comboKeyword ); + + m_comboKeyword->insertItem( i18n("Simple Search") ); + m_vector.push_back("Simple Search"); + if( metaBundleKeywords ) + { + for( int i=0; i < MetaBundle::NUM_COLUMNS; ++i ) + { + if( i == MetaBundle::Mood ) + continue; + if( !AmarokConfig::useRatings() && i == MetaBundle::Rating ) + continue; + if( !AmarokConfig::useScores() && i == MetaBundle::Score ) + continue; + + m_comboKeyword->insertItem( MetaBundle::prettyColumnName( i ) ); + m_vector.push_back( MetaBundle::exactColumnName( i ).lower() ); + } + } + else + { + m_comboKeyword->insertItem( i18n("Album") ); + m_vector.push_back( "album" ); + m_comboKeyword->insertItem( i18n("Artist") ); + m_vector.push_back( "artist" ); + m_comboKeyword->insertItem( i18n("Bitrate") ); + m_vector.push_back( "bitrate" ); + m_comboKeyword->insertItem( i18n("BPM") ); + m_vector.push_back( "bpm" ); + m_comboKeyword->insertItem( i18n("Comment") ); + m_vector.push_back( "comment" ); + m_comboKeyword->insertItem( i18n("Composer") ); + m_vector.push_back( "composer" ); + m_comboKeyword->insertItem( i18n("Directory") ); + m_vector.push_back( "directory" ); + m_comboKeyword->insertItem( i18n("Disc Number") ); + m_vector.push_back( "disc" ); + m_comboKeyword->insertItem( i18n("Filename") ); + m_vector.push_back( "filename" ); + m_comboKeyword->insertItem( i18n("Mount Point") ); + m_vector.push_back( "mountpoint" ); + m_comboKeyword->insertItem( i18n("Filetype") ); + m_vector.push_back( "filetype" ); + m_comboKeyword->insertItem( i18n("Genre") ); + m_vector.push_back( "genre" ); + m_comboKeyword->insertItem( i18n("Length") ); + m_vector.push_back( "length" ); + m_comboKeyword->insertItem( i18n("Label") ); + m_vector.push_back( "label" ); + m_comboKeyword->insertItem( i18n("Lyrics") ); + m_vector.push_back( "lyrics" ); + m_comboKeyword->insertItem( i18n("Play Count") ); + m_vector.push_back( "playcount" ); + if( AmarokConfig::useRatings() ) + { + m_comboKeyword->insertItem( i18n("Rating") ); + m_vector.push_back( "rating" ); + } + m_comboKeyword->insertItem( i18n("Sample Rate") ); + m_vector.push_back( "samplerate" ); + if( AmarokConfig::useScores() ) + { + m_comboKeyword->insertItem( i18n("Score") ); + m_vector.push_back( "score" ); + } + m_comboKeyword->insertItem( i18n("File Size") ); + m_vector.push_back( "size" ); + m_comboKeyword->insertItem( i18n("Title") ); + m_vector.push_back( "title" ); + m_comboKeyword->insertItem( i18n("Track") ); + m_vector.push_back( "track" ); + m_comboKeyword->insertItem( i18n("Year") ); + m_vector.push_back( "year" ); + } + + // the "Simple Search" text is selected in the comboKeyword + m_selectedIndex = 0; + + keywordLayout->addWidget( m_comboKeyword ); + keywordLayout->addItem( new QSpacerItem( 5, 10, QSizePolicy::Minimum, QSizePolicy::Minimum ) ); + m_editKeyword = new KLineEdit( plainPage(), "editKeywordBox" ); + QWhatsThis::add( m_editKeyword, i18n("

    Type the attribute value or the text to look for here.

    ") ); + keywordLayout->addWidget( m_editKeyword ); + m_mainLay->addLayout( keywordLayout ); + m_mainLay->addItem( new QSpacerItem( 10, 10, QSizePolicy::Expanding, QSizePolicy::Minimum ) ); + connect(m_comboKeyword, SIGNAL(activated(int)), this, SLOT(selectedKeyword(int))); + + // group of options on numeric attribute keywords: a value <,>,= ... or a value between Min and Max + m_groupBox = new QGroupBox( plainPage(), "groupBox" ); + m_groupBox->setTitle( i18n( "Attribute value is" ) ); + m_mainLay->addWidget( m_groupBox ); + m_mainLay->addItem( new QSpacerItem( 10, 10, QSizePolicy::Minimum, QSizePolicy::Minimum ) ); + + QVBoxLayout *vertLayout = new QVBoxLayout( m_groupBox, 15, 5 ); + + // choose other keyword parameters: smaller than, greater than, equal to... + QHBoxLayout *paramLayout = new QHBoxLayout( vertLayout ); + + m_comboCondition = new QComboBox( m_groupBox, "valuecondition"); + m_comboCondition->insertItem( i18n("smaller than") ); + m_comboCondition->insertItem( i18n("larger than") ); + m_comboCondition->insertItem( i18n("equal to") ); + m_comboCondition->insertItem( i18n("between") ); + paramLayout->addWidget( m_comboCondition ); + paramLayout->addItem( new QSpacerItem( 5, 10, QSizePolicy::Fixed, QSizePolicy::Minimum ) ); + + m_spinMin1 = new QSpinBox( m_groupBox, "minimum1" ); + paramLayout->addWidget( m_spinMin1 ); + paramLayout->addItem( new QSpacerItem( 5, 10, QSizePolicy::Minimum, QSizePolicy::Minimum ) ); + + m_spinMin2 = new QSpinBox( m_groupBox, "minimum2" ); + paramLayout->addWidget( m_spinMin2 ); + paramLayout->addItem( new QSpacerItem( 5, 10, QSizePolicy::Minimum, QSizePolicy::Minimum ) ); + + connect(m_spinMin1, SIGNAL(valueChanged(int)), this, SLOT(minSpinChanged(int))); + + m_andLabel = new QLabel( i18n("and"), m_groupBox, "andLabel"); + paramLayout->addWidget( m_andLabel ); + paramLayout->addItem( new QSpacerItem( 5, 10, QSizePolicy::Minimum, QSizePolicy::Minimum ) ); + + m_spinMax1 = new QSpinBox( m_groupBox, "maximum1" ); + paramLayout->addWidget( m_spinMax1 ); + paramLayout->addItem( new QSpacerItem( 5, 10, QSizePolicy::Minimum, QSizePolicy::Minimum ) ); + + m_spinMax2 = new QSpinBox( m_groupBox, "maximum2" ); + paramLayout->addWidget( m_spinMax2 ); + + connect(m_spinMax1, SIGNAL(valueChanged(int)), this, SLOT(maxSpinChanged(int))); + + QHBoxLayout *filesizeLayout = new QHBoxLayout( vertLayout ); + filesizeLayout->setAlignment( AlignLeft ); + m_filesizeLabel = new QLabel( i18n("Unit:"), m_groupBox, "filesizeLabel"); + filesizeLayout->addWidget( m_filesizeLabel ); + filesizeLayout->addItem( new QSpacerItem( 5, 10, QSizePolicy::Fixed, QSizePolicy::Minimum ) ); + m_comboUnitSize = new QComboBox( m_groupBox, "comboUnitSize" ); + m_filesizeLabel->setBuddy( m_comboUnitSize ); + m_comboUnitSize->insertItem( i18n("B (1 Byte)") ); + m_comboUnitSize->insertItem( i18n("KB (1024 Bytes)") ); + m_comboUnitSize->insertItem( i18n("MB (1024 KB)") ); + filesizeLayout->addWidget( m_comboUnitSize ); + + // type text selected + textWanted(); + + // check the "One Value Choosing" by default + chooseOneValue(); + + connect( m_comboCondition, SIGNAL(activated(int)), SLOT(chooseCondition(int)) ); + + QHBoxLayout *otherOptionsLayout = new QHBoxLayout( plainPage() ); + otherOptionsLayout->setAlignment( AlignHCenter ); + m_mainLay->addLayout( otherOptionsLayout ); + + // the groupbox to select the action filter + m_groupBox2 = new QGroupBox( plainPage(), "groupBox2" ); + m_groupBox2->setTitle( i18n( "Filter action" ) ); + otherOptionsLayout->addWidget( m_groupBox2 ); + + QVBoxLayout* ratioLay = new QVBoxLayout( m_groupBox2, 15, 0 ); + + m_checkALL = new QRadioButton( i18n("Match all words"), m_groupBox2, "checkall" ); + QToolTip::add( m_checkALL, + i18n("

    Check this box to look for the tracks that contain all the words you typed " + "in the related Simple Search edit box

    ")); + ratioLay->addWidget( m_checkALL ); + + m_checkAtLeastOne = new QRadioButton( i18n("Match any word"), m_groupBox2, "checkor"); + QToolTip::add( m_checkAtLeastOne, + i18n("

    Check this box to look for the tracks that contain at least one of the words " + "you typed in the related Simple Search edit box

    ")); + ratioLay->addWidget( m_checkAtLeastOne ); + + m_checkExactly = new QRadioButton( i18n("Exact match"), m_groupBox2, "checkexactly"); + QToolTip::add( m_checkExactly, + i18n("

    Check this box to look for all the tracks that contain exactly the words you typed " + "in the related Simple Search edit box

    ")); + ratioLay->addWidget( m_checkExactly ); + + m_checkExclude = new QRadioButton( i18n("Exclude"), m_groupBox2, "checkexclude"); + QToolTip::add( m_checkExclude, + i18n("

    Check this box to look for all the tracks that do not contain the words you typed " + "in the related Simple Search edit box

    ")); + ratioLay->addWidget( m_checkExclude ); + + m_actionCheck << m_checkALL; + m_actionCheck << m_checkAtLeastOne; + m_actionCheck << m_checkExactly; + m_actionCheck << m_checkExclude; + + connect( m_checkALL, SIGNAL(clicked()), this, SLOT(slotCheckAll()) ); + connect( m_checkAtLeastOne, SIGNAL(clicked()), this, SLOT(slotCheckAtLeastOne()) ); + connect( m_checkExactly, SIGNAL(clicked()), this, SLOT(slotCheckExactly()) ); + connect( m_checkExclude, SIGNAL(clicked()), this, SLOT(slotCheckExclude()) ); + + // check "select all words" as default + slotCheckAll(); + + // some vertical space + otherOptionsLayout->addItem( new QSpacerItem( 50, 5, QSizePolicy::Minimum, QSizePolicy::Minimum ) ); + + QVBoxLayout* verticalCondLay = new QVBoxLayout( otherOptionsLayout, 15, 0 ); + + m_groupBox3 = new QGroupBox( plainPage(), "groupBox3" ); + m_groupBox3->setTitle( i18n( "Appending condition" ) ); + verticalCondLay->addWidget( m_groupBox3 ); + + QVBoxLayout* ratioLay2 = new QVBoxLayout( m_groupBox3, 15, 0 ); + + m_checkAND = new QRadioButton( i18n("AND logic condition", "AND"), m_groupBox3, "checkAND" ); + QToolTip::add( m_checkAND, + i18n("

    Check this box if you want to add another condition and you want that the filter " + "to match both the previous conditions and this new one

    ")); + ratioLay2->addWidget( m_checkAND ); + + m_checkOR = new QRadioButton( i18n("OR logic condition", "OR"), m_groupBox3, "checkOR" ); + QToolTip::add( m_checkOR, + i18n("

    Check this box if you want to add another condition and you want that the filter " + "to match either the previous conditions or this new one

    ")); + ratioLay2->addWidget( m_checkOR ); + + otherOptionsLayout->addItem( new QSpacerItem( 10, 10, QSizePolicy::Minimum, QSizePolicy::Minimum ) ); + + m_prefixNOT = new QCheckBox( i18n("Invert condition"), plainPage(), "prefixNOT" ); + QToolTip::add( m_prefixNOT, + i18n("Check this box to negate the defined filter condition")); + QWhatsThis::add( m_prefixNOT, + i18n("

    If this option is checked the defined filter condition will be negated. " + "This means that, for example, you can define a filter that looks for all " + "tracks that are not of a specific album, artist, and so on.

    ")); + verticalCondLay->addWidget( m_prefixNOT ); + m_prefixNOT->setEnabled( false ); + + connect(m_prefixNOT, SIGNAL(clicked()), SLOT(assignPrefixNOT())); + + m_mainLay->addItem( new QSpacerItem( 10, 20, QSizePolicy::Minimum, QSizePolicy::Minimum ) ); + + // you need to append at least one filter condition to specify if do + // an "AND" or an "OR" with the next condition if the filter is empty + if (m_filterText.isEmpty()) + m_groupBox3->setEnabled( false ); + + connect( m_checkAND, SIGNAL(clicked()), SLOT(slotCheckAND()) ); + connect( m_checkOR, SIGNAL(clicked()), SLOT(slotCheckOR()) ); + + // check "AND" condition as default + slotCheckAND(); + + // setup Min Max Value spin + setMinMaxValueSpins(); +} + +EditFilterDialog::~EditFilterDialog() +{ + delete m_editKeyword; +} + +QString EditFilterDialog::filter() const +{ + return m_filterText; +} + +void EditFilterDialog::exclusiveSelectOf( int which ) +{ + int size = static_cast( m_actionCheck.count() ); + + for ( int i = 0; i < size; i++ ) + if ( i != which ) + m_actionCheck[i]->setChecked( false ); + else + m_actionCheck[i]->setChecked( true ); +} + +QString EditFilterDialog::keywordConditionString(const QString& keyword) const +{ + // this member is called when there is a keyword that needs numeric attributes + QString result, unit; + + if (m_vector.at(m_selectedIndex) == "size") + switch (m_comboUnitSize->currentItem()) + { + case 1: + // kbytes + unit = "k"; + break; + case 2: + // mbytes + unit = "m"; + break; + } + + switch(m_comboCondition->currentItem()) + { + case 0: + // less than... + result = m_strPrefixNOT + keyword + ":<"; + if (keyword == "length") + result += QString::number( m_spinMin1->value() * 60 + m_spinMin2->value() ) + unit; + else + result += m_spinMin1->text() + unit; + break; + case 1: + // greater than... + result = m_strPrefixNOT + keyword + ":>"; + if (keyword == "length") + result += QString::number( m_spinMin1->value() * 60 + m_spinMin2->value() ) + unit; + else + result += m_spinMin1->text() + unit; + break; + case 2: + // equal to... + if (keyword == "length") + result = m_strPrefixNOT + "length:" + QString::number( m_spinMin1->value() * 60 + + m_spinMin2->value() ) + unit; + else + { + if (m_strPrefixNOT.isEmpty()) + result = keyword + ":>" + QString::number(m_spinMin1->value() - 1) + unit + + " " + keyword + ":<" + QString::number(m_spinMin1->value() + 1) + unit; + else + result = keyword + ":<" + QString::number(m_spinMin1->value()) + unit + + " OR " + keyword + ":>" + QString::number(m_spinMin1->value()) + unit; + } + break; + case 3: + // between... + if (keyword == "length") + { + if (m_strPrefixNOT.isEmpty()) + result = "length:>" + QString::number( m_spinMin1->value() * 60 + m_spinMin2->value() - 1) + unit + + " length:<" + QString::number( m_spinMax1->value() * 60 + m_spinMax2->value() + 1) + unit; + else + result = "length:<" + QString::number( m_spinMin1->value() * 60 + m_spinMin2->value()) + unit + + " OR length:>" + QString::number( m_spinMax1->value() * 60 + m_spinMax2->value()) + unit; + } + else + { + if (m_strPrefixNOT.isEmpty()) + result = keyword + ":>" + QString::number(m_spinMin1->value() - 1) + unit + + " " + keyword + ":<" + QString::number(m_spinMax1->value() + 1) + unit; + else + result = keyword + ":<" + QString::number(m_spinMin1->value() - 1) + unit + + " OR " + keyword + ":>" + QString::number(m_spinMax1->value() + 1) + unit; + } + break; + } + + return result; +} + +void EditFilterDialog::setMinMaxValueSpins() +{ + // setting some spin box options and limit values + m_spinMin1->setValue( 0 ); + m_spinMin1->setMinValue( 0 ); + m_spinMin1->setMaxValue( 100000000 ); + + m_spinMin2->setMinValue( 0 ); + m_spinMin2->setMaxValue( 59 ); + m_spinMin2->hide(); + + m_spinMax1->setValue( 0 ); + m_spinMax1->setMinValue( 0 ); + m_spinMax1->setMaxValue( 100000000 ); + + m_spinMax2->setMinValue( 0 ); + m_spinMax2->setMaxValue( 59 ); + m_spinMax2->hide(); + + // fix tooltip + QToolTip::add( m_spinMin1, "" ); + QToolTip::add( m_spinMin2, i18n("Seconds") ); + + QToolTip::add( m_spinMax1, "" ); + QToolTip::add( m_spinMax2, i18n("Seconds") ); +} + +// SLOTS +void EditFilterDialog::selectedKeyword(int index) // SLOT +{ + debug() << "you selected index " << index << ": '" << m_comboKeyword->text(index) << "'" << endl; + m_groupBox2->setEnabled( false ); + m_comboUnitSize->setEnabled( false ); + m_filesizeLabel->setEnabled( false ); + m_prefixNOT->setEnabled( true ); + + setMinMaxValueSpins(); + + const QString key = m_vector[index]; + if( index == 0 ) + { + // Simple Search + m_groupBox2->setEnabled( true ); + m_prefixNOT->setEnabled( false ); + textWanted(); + } + else if( key=="bitrate" ) + { + // bitrate: set useful values for the spinboxes + m_spinMin1->setValue( 128 ); + m_spinMax1->setValue( 384 ); + valueWanted(); + } + else if( key=="samplerate" ) + { + // samplerate: set useful values for the spinboxes + m_spinMin1->setValue( 8000 ); + m_spinMax1->setValue( 48000 ); + valueWanted(); + } + else if( key=="length" ) + { + // length: set useful values for the spinboxes + m_spinMin2->show(); + m_spinMax2->show(); + m_spinMin1->setValue( 1 ); + m_spinMax1->setValue( 5 ); + QToolTip::add( m_spinMin1, i18n("Minutes") ); + QToolTip::add( m_spinMax1, i18n("Minutes") ); + + // fix the maximum values to reduce spinboxes size + m_spinMin1->setMaxValue( 240 ); + m_spinMax1->setMaxValue( 240 ); + + valueWanted(); + } + else if( key=="size" || key=="filesize" ) + { + // size: set useful values for the spinboxes + m_filesizeLabel->setEnabled( true ); + m_comboUnitSize->setEnabled( true ); + m_spinMin1->setValue( 1 ); + m_spinMax1->setValue( 3 ); + m_comboUnitSize->setCurrentItem( 2 ); + valueWanted(); + } + else if( key=="year" ) + { + // year: set useful values for the spinboxes + m_spinMin1->setValue( 1900 ); + m_spinMax1->setValue( QDate::currentDate().year() ); + valueWanted(); + } + else if( key=="track" || key=="disc" || key=="discnumber" ) + { + // track/disc: set useful values for the spinboxes + m_spinMin1->setValue( 1 ); + m_spinMax1->setValue( 15 ); + valueWanted(); + } + else if( key=="playcount" + || key=="lastplayed" + || key=="rating" + || key=="score" + || key=="bpm" ) + { + valueWanted(); + } + else if( key=="label" ) + textWanted( CollectionDB::instance()->labelList() ); + else if( key=="album" ) + textWanted( CollectionDB::instance()->albumList() ); + else if( key=="artist" ) + textWanted( CollectionDB::instance()->artistList() ); + else if( key=="composer" ) + textWanted( CollectionDB::instance()->composerList() ); + else if( key=="genre" ) + textWanted( CollectionDB::instance()->genreList() ); + else if( key=="type" || key=="filetype" ) + { + QStringList types; + types << "mp3" << "flac" << "ogg" << "aac" << "m4a" << "mp4" << "mp2" << "ac3" + << "wav" << "asf" << "wma"; + textWanted( types ); + } + else + textWanted(); + + // assign the correct value to the m_strPrefixNOT + assignPrefixNOT(); + + // assign the right index + m_selectedIndex = index; +} + +void EditFilterDialog::minSpinChanged(int value) // SLOT +{ + if (value > m_spinMax1->value()) + m_spinMax1->setValue(value); +} + +void EditFilterDialog::maxSpinChanged(int value) // SLOT +{ + if (m_spinMin1->value() > value) + m_spinMin1->setValue(value); +} + +void EditFilterDialog::textWanted() // SLOT +{ + m_editKeyword->setEnabled( true ); + m_groupBox->setEnabled( false ); + + m_editKeyword->completionObject()->clear(); +} + +void EditFilterDialog::textWanted( const QStringList &completion ) // SLOT +{ + m_editKeyword->setEnabled( true ); + m_groupBox->setEnabled( false ); + + m_editKeyword->completionObject()->clear(); + m_editKeyword->completionObject()->insertItems( completion ); + m_editKeyword->completionObject()->setIgnoreCase( true ); + m_editKeyword->setCompletionMode( KGlobalSettings::CompletionPopup ); +} + +void EditFilterDialog::valueWanted() // SLOT +{ + m_editKeyword->setEnabled( false ); + m_groupBox->setEnabled( true ); +} + +void EditFilterDialog::chooseCondition( int condition ) // SLOT +{ + if( condition == 3 ) // included between + chooseMinMaxValue(); + else + chooseOneValue(); +} + +void EditFilterDialog::chooseOneValue() // SLOT +{ + m_andLabel->setEnabled( false); + m_spinMax1->setEnabled( false ); + m_spinMax2->setEnabled( false ); +} + +void EditFilterDialog::chooseMinMaxValue() // SLOT +{ + m_andLabel->setEnabled( true ); + m_spinMax1->setEnabled( true ); + m_spinMax2->setEnabled( true ); +} + +void EditFilterDialog::slotCheckAll() // SLOT +{ + exclusiveSelectOf( 0 ); +} + +void EditFilterDialog::slotCheckAtLeastOne() // SLOT +{ + exclusiveSelectOf( 1 ); +} + +void EditFilterDialog::slotCheckExactly() // SLOT +{ + exclusiveSelectOf( 2 ); +} + +void EditFilterDialog::slotCheckExclude() // SLOT +{ + exclusiveSelectOf( 3 ); +} + +void EditFilterDialog::slotCheckAND() // SLOT +{ + m_checkAND->setChecked( true ); + m_checkOR->setChecked( false ); +} + +void EditFilterDialog::slotCheckOR() // SLOT +{ + m_checkAND->setChecked( false ); + m_checkOR->setChecked( true ); +} + +void EditFilterDialog::assignPrefixNOT() // SLOT +{ + if (m_prefixNOT->isChecked()) + m_strPrefixNOT = "-"; + else + m_strPrefixNOT = ""; +} + +void EditFilterDialog::slotDefault() // SLOT +{ + // now append the filter rule if not empty + if (m_editKeyword->text().isEmpty() && (m_selectedIndex == 0)) + { + KMessageBox::sorry( 0, i18n("

    Sorry but the filter rule cannot be set. The text field is empty. " + "Please type something into it and retry.

    "), i18n("Empty Text Field")); + m_editKeyword->setFocus(); + return; + } + if (!m_appended) + { + // it's the first rule + m_appended = true; + m_groupBox3->setEnabled( true ); + } + + m_previousFilterText = m_filterText; + if (!m_filterText.isEmpty()) + { + m_filterText += " "; + if (m_checkOR->isChecked()) + m_filterText += "OR "; + } + QStringList list = QStringList::split( " ", m_editKeyword->text() ); + const QString key = m_vector[m_selectedIndex]; + if( m_selectedIndex == 0 ) + { + // Simple Search + debug() << "selected text: '" << m_editKeyword->text() << "'" << endl; + if (m_actionCheck[0]->isChecked()) + { + // all words + m_filterText += m_editKeyword->text(); + } + else if (m_actionCheck[1]->isChecked()) + { + // at least one word + m_filterText += *(list.begin()); + for ( QStringList::Iterator it = ++list.begin(); it != list.end(); ++it ) + m_filterText += " OR " + *it; + } + else if (m_actionCheck[2]->isChecked()) + { + // exactly the words + m_filterText += "\"" + m_editKeyword->text() + "\""; + } + else if (m_actionCheck[3]->isChecked()) + { + // exclude words + for ( QStringList::Iterator it = list.begin(); it != list.end(); ++it ) + m_filterText += " -" + *it; + } + } + else if( key=="bitrate" + || key=="disc" || key=="discnumber" + || key=="length" + || key=="playcount" + || key=="rating" + || key=="samplerate" + || key=="score" + || key=="filesize" || key=="size" + || key=="track" + || key=="year" ) + { + m_filterText += keywordConditionString( m_vector[m_selectedIndex] ); + } + else + { + m_filterText += m_vector[m_selectedIndex] + ":\"" + m_editKeyword->text() + "\""; + } + emit filterChanged( m_filterText ); + + m_editKeyword->clear(); +} + +void EditFilterDialog::slotUser1() // SLOT +{ + m_previousFilterText = m_filterText; + m_filterText = ""; + + // no filter appended cause all cleared + m_appended = false; + m_groupBox3->setEnabled( false ); + + emit filterChanged( m_filterText ); +} + +void EditFilterDialog::slotUser2() // SLOT +{ + m_filterText = m_previousFilterText; + if (m_filterText.isEmpty()) + { + // no filter appended cause all cleared + m_appended = false; + m_groupBox3->setEnabled( false ); + } + emit filterChanged( m_filterText ); +} + +void EditFilterDialog::slotOk() // SLOT +{ + // If there's a filter typed in but unadded, add it. + // This makes it easier to just add one condition - you only need to press OK. + if ( !m_editKeyword->text().isEmpty() ) + slotDefault(); + + // Don't let OK do anything if they haven't set any filters. + if (m_appended) + accept(); +} + +#include "editfilterdialog.moc" diff --git a/amarok/src/editfilterdialog.h b/amarok/src/editfilterdialog.h new file mode 100644 index 00000000..c6a0fabd --- /dev/null +++ b/amarok/src/editfilterdialog.h @@ -0,0 +1,107 @@ +// (c) 2006 Giovanni Venturi +// See COPYING file for licensing information. + +#ifndef AMAROK_EDITFILTERDIALOG_H +#define AMAROK_EDITFILTERDIALOG_H + +#include +#include + +#include + +class QWidget; +class QVBoxLayout; +class QComboBox; +class QCheckBox; +class QLineEdit; +class QRadioButton; +class QGroupBox; +class QSpinBox; +class QStringList; +class KComboBox; + +class EditFilterDialog : public KDialogBase +{ + Q_OBJECT + public: + EditFilterDialog( QWidget* parent, bool metaBundleKeywords, const QString &text = "" ); + ~EditFilterDialog(); + + QString filter() const; + + signals: + void filterChanged( const QString &filter ); + + private: + QVBoxLayout *m_mainLay; + + QCheckBox *m_prefixNOT; + QComboBox *m_comboKeyword; + KLineEdit *m_editKeyword; + + QGroupBox *m_groupBox; + + QComboBox *m_comboCondition; + QLabel *m_filesizeLabel; + QComboBox *m_comboUnitSize; + + QRadioButton *m_minMaxRadio; + QSpinBox *m_spinMin1, *m_spinMin2; + QLabel *m_andLabel; + QSpinBox *m_spinMax1, *m_spinMax2; + + QGroupBox *m_groupBox2; + QRadioButton *m_checkALL; + QRadioButton *m_checkAtLeastOne; + QRadioButton *m_checkExactly; + QRadioButton *m_checkExclude; + QValueList m_actionCheck; + + QGroupBox *m_groupBox3; + QRadioButton *m_checkAND; + QRadioButton *m_checkOR; + + bool m_appended; // true if a filter appended + int m_selectedIndex; // the position of the selected keyword in the combobox + QValueVector m_vector; // the vector of the amarok filter keyword + QString m_filterText; // the resulting filter string + QString m_previousFilterText; // the previous resulting filter string + QString m_strPrefixNOT; // is empty if no NOT prefix is needed else it's "-" + + private: + void exclusiveSelectOf( int which ); + QString keywordConditionString(const QString& keyword) const; + void setMinMaxValueSpins(); + + private slots: + void selectedKeyword(int index); + + void minSpinChanged(int value); + void maxSpinChanged(int value); + + void textWanted(); + void textWanted( const QStringList &completions ); + void valueWanted(); + + void chooseCondition(int index); + void chooseOneValue(); + void chooseMinMaxValue(); + + void slotCheckAll(); + void slotCheckAtLeastOne(); + void slotCheckExactly(); + void slotCheckExclude(); + + void slotCheckAND(); + void slotCheckOR(); + + void assignPrefixNOT(); + + protected slots: + virtual void slotDefault(); + virtual void slotUser1(); + virtual void slotUser2(); + virtual void slotOk(); +}; + +#endif /* AMAROK_EDITFILTERDIALOG_H */ diff --git a/amarok/src/engine/ENGINE_TODO b/amarok/src/engine/ENGINE_TODO new file mode 100644 index 00000000..0755bf28 --- /dev/null +++ b/amarok/src/engine/ENGINE_TODO @@ -0,0 +1,13 @@ +TODO for the next engine framework: + + * emit stateChanged() with no parameter and force controller to read the state from + EngineBase::state() instead, as this makes things more likely to be consistent + * combine load() and play() as it just complicates things for engine development (eg + crossfade is a bitch for me due to the separation + * setVolumeSW( double ), to give more resolution for setting volume + * Make EngineObserver system use engineStateChanged( oldState, newState ) + * Don't do above function for Play on every track? Is this ever useful? + * Would be useful if engineNewMetaData and stateChanged for Play weren't separate? + * setVolumeSW uses a log function, volume() doesn't so you get different values back to + what you expect. Really we should provide a volumeToLog( int ) function for engines to + use internally as some engines may already apply a log function anyway. diff --git a/amarok/src/engine/Makefile.am b/amarok/src/engine/Makefile.am new file mode 100644 index 00000000..926d54ae --- /dev/null +++ b/amarok/src/engine/Makefile.am @@ -0,0 +1,27 @@ +#if with_gst10 +# GST10_ENGINE_SUBDIR = gst10 +#endif + +if with_nmm + NMM_ENGINE_SUBDIR = nmm +endif + +if with_xine + XINE_ENGINE_SUBDIR = xine +endif + +if with_helix + HELIX_ENGINE_SUBDIR = helix +endif + +if with_yauap + YAUAP_ENGINE_SUBDIR = yauap +endif + +SUBDIRS = . \ + void \ + $(XINE_ENGINE_SUBDIR) \ + $(NMM_ENGINE_SUBDIR) \ + $(HELIX_ENGINE_SUBDIR) \ + $(YAUAP_ENGINE_SUBDIR) + diff --git a/amarok/src/engine/akode/Makefile.am b/amarok/src/engine/akode/Makefile.am new file mode 100644 index 00000000..8414cd64 --- /dev/null +++ b/amarok/src/engine/akode/Makefile.am @@ -0,0 +1,15 @@ +kde_module_LTLIBRARIES = libamarok_aKode-engine.la +kde_services_DATA = amarok_aKode-engine.desktop + +INCLUDES = -I$(top_srcdir)/amarok/src $(CFLAGS_AKODE) $(all_includes) + +libamarok_aKode_engine_la_LIBADD = \ + $(top_builddir)/amarok/src/libamarok.la \ + $(top_builddir)/amarok/src/plugin/libplugin.la \ + -lkdecore \ + $(LIBS_AKODE) + +libamarok_aKode_engine_la_SOURCES = akode-engine.cpp akode-scope.cpp +libamarok_aKode_engine_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries) + +METASOURCES = AUTO diff --git a/amarok/src/engine/akode/akode-engine.cpp b/amarok/src/engine/akode/akode-engine.cpp new file mode 100644 index 00000000..429f0172 --- /dev/null +++ b/amarok/src/engine/akode/akode-engine.cpp @@ -0,0 +1,196 @@ +/*************************************************************************** + * Copyright (C) 2005 Max Howell * + * * + * 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. * + * * + ***************************************************************************/ + +#include +#include +#include +#include +#include + +AMAROK_EXPORT_PLUGIN( AkodeEngine ) + + +namespace Amarok +{ + class Manager : public aKode::Player::Manager + { + AkodeEngine *m_engine; + + /// Called for all stateChanges + virtual void stateChangeEvent( aKode::Player::State ) + { + QApplication::postEvent( m_engine, new QCustomEvent( 3000 ) ); + } + + /// Called when a decoder reaches end of file + virtual void eofEvent() + { + QApplication::postEvent( m_engine, new QCustomEvent( 3001 ) ); + } + + /// Called when a decoder encounters a fatal error + virtual void errorEvent() + { + QApplication::postEvent( m_engine, new QCustomEvent( 3002 ) ); + } + + public: + Manager( AkodeEngine *engine ) : m_engine( engine ) {} + }; +} + + +AkodeEngine::AkodeEngine() + : m_player( 0 ) +{} + +AkodeEngine::~AkodeEngine() +{ + if( m_player ) + m_player->close(); +} + +bool +AkodeEngine::init() +{ +// startTimer( 20 ); + + m_player = new aKode::Player(); + m_player->setManager( new Amarok::Manager( this ) ); + m_player->setMonitor( &m_scope ); + + return m_player->open( "auto" ); +} + +bool +AkodeEngine::load( const KURL &url, bool isStream ) +{ + Engine::Base::load( url, isStream ); + + return m_player->load( url.path().local8Bit().data() ); +} + +bool +AkodeEngine::play( uint /*offset*/ ) +{ + //FIXME this seemed to crash Amarok + //m_player->decoder()->seek( offset ); + m_player->play(); + + return true; +} + +void +AkodeEngine::unpause() +{ + m_player->play(); +} + +bool +AkodeEngine::canDecode( const KURL &url ) const +{ + const QString ext = url.path().right( 4 ).lower(); + + return ext == ".mp3" || ext == ".ogg" || ext == ".wav" || ext ==".mpc" || ext == "flac"; +} + +uint +AkodeEngine::position() const +{ + if( !m_player->decoder() ) + return 0; + + const int pos = m_player->decoder()->position(); + + return pos >= 0 ? pos : 0; +} + +void +AkodeEngine::stop() +{ + m_player->stop(); + m_player->unload(); +} + +void +AkodeEngine::pause() +{ + switch( m_player->state() ) { + case aKode::Player::Playing: m_player->pause(); break; + case aKode::Player::Paused: m_player->play(); break; + default: ; + } +} + +void +AkodeEngine::setVolumeSW( uint v ) +{ + m_player->setVolume( (float)v / 100.0 ); +} + +void +AkodeEngine::seek( uint ms ) +{ + m_player->decoder()->seek( ms ); +} + +Engine::State +AkodeEngine::state() const +{ + switch( m_player->state() ) + { + case aKode::Player::Open: + case aKode::Player::Closed: return Engine::Empty; + default: + case aKode::Player::Loaded: return Engine::Idle; + case aKode::Player::Playing: return Engine::Playing; + case aKode::Player::Paused: return Engine::Paused; + } +} + +bool +AkodeEngine::event( QEvent *e ) +{ + switch( e->type() ) + { + /* + case QEvent::Timer: + if( m_player->decoder() && m_player->decoder()->eof() ) { + m_player->stop(); + emit trackEnded(); + } + break; + */ + case 3000: + emit stateChanged( state() ); + break; + + case 3001: + m_player->stop(); + emit trackEnded(); + break; + + case 3002: + m_player->stop(); + emit trackEnded(); + emit infoMessage( i18n("Unable to decode %1").arg( m_url.prettyURL()) ); + break; + + default: + return false; + } + + return true; +} + +const Engine::Scope& AkodeEngine::scope() +{ + return m_scope.scope(); +} diff --git a/amarok/src/engine/akode/akode-engine.h b/amarok/src/engine/akode/akode-engine.h new file mode 100644 index 00000000..a5fc41be --- /dev/null +++ b/amarok/src/engine/akode/akode-engine.h @@ -0,0 +1,42 @@ +/*************************************************************************** + * Copyright (C) 2005 Max Howell * + * * + * 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. * + * * + ***************************************************************************/ + +#include "enginebase.h" +#include "akode-scope.h" + +namespace aKode { class Player; } + +class AkodeEngine : public Engine::Base +{ + virtual bool init(); + virtual bool canDecode( const KURL& ) const; + virtual uint position() const; + virtual bool load( const KURL&, bool ); + virtual bool play( uint ); + virtual void stop(); + virtual void pause(); + virtual void unpause(); + virtual void setVolumeSW( uint ); + virtual void seek( uint ); + + virtual Engine::State state() const; + virtual const Engine::Scope &scope(); + + virtual bool event( QEvent* ); + + aKode::Player *m_player; + aKodeScope m_scope; + +protected: + ~AkodeEngine(); + +public: + AkodeEngine(); +}; diff --git a/amarok/src/engine/akode/akode-scope.cpp b/amarok/src/engine/akode/akode-scope.cpp new file mode 100644 index 00000000..41b80891 --- /dev/null +++ b/amarok/src/engine/akode/akode-scope.cpp @@ -0,0 +1,91 @@ +/* aKode: aKodeScope + + Copyright (C) 2005 Allan Sandfeld Jensen + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include + +#include +#include +#include "akode-scope.h" + +#include +//#include +using aKode::AudioFrame; + +// Copies fromFrame to toFrame fast (Actually a swap happens to save malloc and frees). +static inline void takeover(AudioFrame* toFrame, AudioFrame* fromFrame) +{ + AudioFrame tmpFrame; + + tmpFrame = *toFrame; + *toFrame = *fromFrame; + *fromFrame = tmpFrame; + tmpFrame.data = 0; +} + +struct aKodeScope::private_data +{ + private_data() : length(512), convert(16) {}; + int length; + aKode::AudioFrame frame; + aKode::AudioFrame frame1; + aKode::Converter convert; + std::vector scope; +}; + +aKodeScope::aKodeScope() +{ + d = new private_data; +} + +aKodeScope::~aKodeScope() +{ + delete d; +} + +void aKodeScope::writeFrame(AudioFrame* frame) +{ + takeover(&d->frame, frame); +} +/* +void aKodeScope::setLength(length) +{ + d->length = length; +} + +int aKodeScope::length() const +{ + return d->length; +}*/ + +const Engine::Scope& aKodeScope::scope() +{ + d->convert.doFrame(&d->frame, &d->frame1); + + int length = d->frame1.length; + int channels = d->frame1.channels; + if (length > 512) length = 512; + d->scope.resize(length*channels); + int16_t **data = (int16_t**)d->frame1.data; + for(int j=0 ; j < length; j++) + for (int i = 0; i < channels; i++) + d->scope[j*channels + i] = data[i][j]; + + return d->scope; +} diff --git a/amarok/src/engine/akode/akode-scope.h b/amarok/src/engine/akode/akode-scope.h new file mode 100644 index 00000000..940af5c7 --- /dev/null +++ b/amarok/src/engine/akode/akode-scope.h @@ -0,0 +1,45 @@ +/* aKode: scope + + Copyright (C) 2005 Allan Sandfeld Jensen + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef _AKODE_SCOPE_H +#define _AKODE_SCOPE_H + +#include +#include "enginebase.h" + +namespace aKode { +class AudioFrame; +} + +class aKodeScope : public aKode::Player::Monitor +{ +public: + aKodeScope(); + ~aKodeScope(); + + void writeFrame(aKode::AudioFrame *frame); + + const Engine::Scope& scope(); + struct private_data; +private: + private_data *d; +}; + +#endif diff --git a/amarok/src/engine/akode/amarok_aKode-engine.desktop b/amarok/src/engine/akode/amarok_aKode-engine.desktop new file mode 100644 index 00000000..8c87826f --- /dev/null +++ b/amarok/src/engine/akode/amarok_aKode-engine.desktop @@ -0,0 +1,120 @@ +[Desktop Entry] +Encoding=UTF-8 +Type=Service +Name=aKode Engine +Name[af]=aKode Enjin +Name[ar]=محرك aKode +Name[bg]=aKode +Name[bn]=অ্যাকোড ইঞ্জিন +Name[br]=Keflusker aKode +Name[ca]=Motor aKode +Name[cs]=aKode +Name[da]=aKode-motor +Name[de]=aKode +Name[el]=Μηχανή aKode +Name[eo]=aKode Ilo +Name[es]=Motor aKode +Name[et]=aKode mootor +Name[fa]=موتور aKode +Name[fi]=aKode +Name[fr]=Moteur aKode +Name[ga]=Inneall aKode +Name[gl]=Motor aKode +Name[he]=מנוע שמע aKode +Name[hu]=aKode alrendszer +Name[is]=aKode vél +Name[it]=Motore aKode +Name[ja]=aKode エンジン +Name[ka]=aKode ძრავა +Name[km]=ម៉ាស៊ីន aKode +Name[lt]=aKode variklis +Name[mk]=aKode-машина +Name[ms]=Enjin aKode +Name[nb]=aKode-motor +Name[nds]=aKode +Name[ne]=एकोड इन्जिन +Name[nl]=aKode-engine +Name[nn]=aKode-motor +Name[pa]=aKode ਇੰਜਣ +Name[pl]=Wyjście aKode +Name[pt]=Motor aKode +Name[pt_BR]=Mecanismo aKode +Name[ru]=aKode +Name[se]=aKode-mohtor +Name[sl]=Pogon aKode +Name[sq]=Motor aKode +Name[sr]=Мотор aKode +Name[sr@Latn]=Motor aKode +Name[ss]=Motor aKode +Name[sv]=aKode-gränssnitt +Name[ta]=aKode என்ஜின் +Name[tg]=Муҳаррики aKode +Name[tr]=aKode Motoru +Name[uk]=Рушій aKode +Name[uz]=aKode tizimi +Name[uz@cyrillic]=aKode тизими +Name[wa]=Éndjin aKode +Name[zh_CN]=aKode 引擎 +Name[zh_TW]=aKode 解碼引擎 +ServiceTypes=Amarok/Plugin +X-KDE-Library=libamarok_aKode-engine +Comment=aKode audio-engine for Amarok +Comment[af]=aKode oudio enjin vir Amarok +Comment[ar]=محرك صوتي aKode ل- AmaroK +Comment[bg]=Аудио система aKode за Amarok +Comment[bn]=আমারক-এর জন্য অ্যাকোড অডিও ইঞ্জিন +Comment[br]=Keflusker klevet aKode evit Amarok +Comment[ca]=Motor de so aKode per l'Amarok +Comment[cs]=Zvukový modul aKode pro Amarok +Comment[da]=aKode lyd-motor for Amarok +Comment[de]=aKode-Ausgabe-Modul für Amarok +Comment[el]=Μηχανή ήχου aKode για το AmaroK +Comment[eo]=aKode aŭdmotoro por Amarok +Comment[es]=Motor de audio aKode para Amarok +Comment[et]=Amaroki aKode audiomootor +Comment[fa]=موتور صوتی aKode برای Amarok +Comment[fi]=aKode-toistojärjestelmä Amarok-liitännäinen +Comment[fr]=Moteur audio aKode pour Amarok +Comment[ga]=Inneall fuaime aKode le haghaidh AmaroK +Comment[gl]=Motor de áudio aKode para Amarok +Comment[hu]=aKode hang-alrendszer az Amarokhoz +Comment[is]=aKode hljóðvél fyrir Amarok +Comment[it]=motore audio aKode per Amarok +Comment[ja]=Amarok のための aKode オーディオエンジン +Comment[ka]=aKode აუდიო ძრავი Amarok-ისთვის +Comment[km]=ម៉ាស៊ីន​អូឌីយ៉ូ aKode សម្រាប់ Amarok +Comment[lt]=Amarok skirtas aKode audio variklis +Comment[mk]=aKode аудио-машина за Амарок +Comment[ms]=Enjin-audio aKode untuk Amarok +Comment[nb]=aKode lydmotor for Amarok +Comment[nds]=aKode-Klangmaschien för Amarok +Comment[ne]=अमारोकका लागि एकोड अडियो इन्जिन +Comment[nl]=aKode audio-engine voor Amarok +Comment[nn]=aKode lydmotor for Amarok +Comment[pa]=ਅਮਰੋਕ ਲਈ aKode ਆਡੀਓ-ਇੰਜਣ +Comment[pl]=Wtyczka wyjścia audio aKode dla Amaroka +Comment[pt]=Motor de áudio do aKode para o Amarok +Comment[pt_BR]=Mecanismo de áudio aKode para o Amarok +Comment[ru]=Модуль вывода aKode для amaroK +Comment[se]=aKode jietnamohtor Amarok:ii +Comment[sk]=aKode audio-engine pre Amarok +Comment[sr]=Аудио мотор aKode за Amarok +Comment[sr@Latn]=Audio motor aKode za Amarok +Comment[sv]=aKode ljudgränssnitt för Amarok +Comment[th]=โปรแกรมประมวลผลเสียง aKode สำหรับ Amarok +Comment[tr]=Amarok için aKode ses-motoru eklentisi +Comment[uk]=Аудіо-рушій aKode для Amarok +Comment[uz]=Amarok uchun aKode audio-tizimi +Comment[uz@cyrillic]=Amarok учун aKode аудио-тизими +Comment[wa]=Éndjin d' son aKode po-z Amarok +Comment[zh_CN]=Amarok 的 aKode 音频引擎 +Comment[zh_TW]=AmaroK 的 aKode 解碼引擎 + +X-KDE-Amarok-plugintype=engine +X-KDE-Amarok-name=akode-engine +X-KDE-Amarok-authors=Allan Sandfeld, Max Howell +X-KDE-Amarok-email=kde@carewolf.com +X-KDE-Amarok-rank=0 +X-KDE-Amarok-version=1 +X-KDE-Amarok-framework-version=32 + diff --git a/amarok/src/engine/helix/COPYING b/amarok/src/engine/helix/COPYING new file mode 100644 index 00000000..0fc8a215 --- /dev/null +++ b/amarok/src/engine/helix/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/amarok/src/engine/helix/Makefile.am b/amarok/src/engine/helix/Makefile.am new file mode 100644 index 00000000..f48927b9 --- /dev/null +++ b/amarok/src/engine/helix/Makefile.am @@ -0,0 +1,44 @@ +kde_module_LTLIBRARIES = \ + libamarok_helixengine_plugin.la + +SUBDIRS = \ + helix-sp \ + config + +INCLUDES = \ + -I$(top_srcdir)/amarok/src \ + -I$(top_builddir)/amarok/src/amarokcore \ + $(all_includes) + +AM_CPPFLAGS = \ + -D_UNIX \ + -Wall -Wreturn-type \ + -fno-exceptions \ + -I$(top_srcdir)/amarok/src/engine/helix/helix-sp \ + -I$(top_srcdir)/amarok/src/engine/helix/config \ + -I$(top_srcdir)/amarok/src/ \ + -include $(top_srcdir)/amarok/src/engine/helix/helix-sp/helixdefines.h \ + $(all_includes) + +libamarok_helixengine_plugin_la_SOURCES = \ + helix-engine.cpp \ + helix-errors.cpp \ + helix-configdialog.cpp \ + hxplayercontrol.cpp + +libamarok_helixengine_plugin_la_LIBADD = \ + $(top_builddir)/amarok/src/libamarok.la \ + $(top_builddir)/amarok/src/engine/helix/helix-sp/libhelix-sp.la \ + $(top_builddir)/amarok/src/engine/helix/config/libhelixconfig.la \ + $(top_builddir)/amarok/src/plugin/libplugin.la \ + -lkdeui -lkdecore $(ALSALIB_LIBS) + +libamarok_helixengine_plugin_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries) + +METASOURCES = \ + AUTO + +kde_services_DATA = \ + amarok_helixengine_plugin.desktop + +KDE_OPTIONS=nofinal diff --git a/amarok/src/engine/helix/Makefile.test b/amarok/src/engine/helix/Makefile.test new file mode 100644 index 00000000..0fc64129 --- /dev/null +++ b/amarok/src/engine/helix/Makefile.test @@ -0,0 +1,119 @@ +DEFINES=-DTEST_APP -D_REENTRANT_ -DQT_THREAD_SUPPORT + +CXX=g++ +MOC=/usr/lib/qt-3.3/bin/moc + +CXXFLAGS=-g -fpic -pipe -Wall -Wreturn-type -fno-exceptions --permissive -fno-rtti -Wno-ctor-dtor-privacy -march=pentium -mcpu=pentium -O2 $(INCLUDES) $(DEFINES) + +SRCS=main.cpp hxsplay.cpp +LIBSRCS=helix-engine.cpp helix-config.cpp hxsplay.cpp +MOCSRCS=helix-engine.moc helix-config.moc +MOCHDRS=$(MOCSRCS:.moc=.h) + +OBJS=$(SRCS:.cpp=.o) +LIBOBJS=$(LIBSRCS:.cpp=.o) + +INCLUDES=\ + -I/usr/lib/qt-3.3/include \ + -I/usr/include/kde \ + -I./helix-sp \ + -I../.. \ + -I. \ + -I/usr/X11R6/include \ + -Ihelix-sp/helix-include/runtime \ + -Ihelix-sp/helix-include/common/include \ + -Ihelix-sp/helix-include/client/include \ + -Ihelix-sp/helix-include/common/container \ + -Ihelix-sp/helix-include/common/system \ + -Ihelix-sp/helix-include/common/dbgtool \ + -Ihelix-sp/helix-include/common/util + +LIBRARIES=-Lhelix-sp -lhelix-sp --lstdc++ -ldl -lm -L/usr/lib/qt-3.3/lib -lqt-mt + +.SUFFIXES: .cpp .so .h .moc + +.c.o: + $(CC) $(CCFLAGS) -o $@ -c $< + +.cpp.o: + $(CXX) $(CXXFLAGS) -o $@ -c $< + +.h.moc: + $(MOC) $< -o $@ + + +PROGRAM=splay +SLIBRARY=libamarok_helixengine_plugin.so + +all: $(MOCSRCS) $(SLIBRARY) $(OBJS) $(LIBOBJS) $(PROGRAM) + +$(PROGRAM): $(OBJS) + $(CXX) -o $(PROGRAM) $(OBJS) $(LIBRARIES) + +$(SLIBRARY): $(LIBOBJS) + $(CXX) -shared -o $(SLIBRARY) $(LIBOBJS) -L/usr/X11R6/lib $(LIBRARIES) + +$(OBJS): $(SRCS) + +$(MOCSRCS): $(MOCHDRS) + +install: + install -p -C -m 755 $(SLIBRARY) /usr/lib/kde3/$(SLIBRARY) + install -p -C -m 755 $(SLIBRARY:.so=.la) /usr/lib/kde3/$(SLIBRARY:.so=.la) + install -p -C -m 644 amarok_helixengine_plugin.desktop /usr/share/services/amarok_helixengine_plugin.desktop +# kbuildsycoca + +uninstall: + rm -f /usr/lib/kde3/$(SLIBRARY:.so=.la) + rm -f /usr/lib/kde3/$(SLIBRARY) + rm -f /usr/share/services/amarok_helixengine_plugin.desktop + +clean: + rm -f $(PROGRAM) $(SLIBRARY) $(LIBOBJS) $(OBJS) helix-engine.moc *~ + +depend: + makedepend $(DEFINES) $(INCLUDES) -o.o -- $(SRCS) + +# DO NOT DELETE THIS LINE -- make depend depends on it + +main.o: /usr/include/dlfcn.h /usr/include/features.h /usr/include/sys/cdefs.h +main.o: /usr/include/gnu/stubs.h +main.o: /usr/lib/gcc/i386-redhat-linux/3.4.3/include/stddef.h +main.o: /usr/include/bits/dlfcn.h hxsplay.h /usr/lib/qt-3.3/include/qthread.h +main.o: /usr/lib/qt-3.3/include/qwindowdefs.h +main.o: /usr/lib/qt-3.3/include/qobjectdefs.h +main.o: /usr/lib/qt-3.3/include/qglobal.h /usr/lib/qt-3.3/include/qstring.h +main.o: /usr/lib/qt-3.3/include/qcstring.h +main.o: /usr/lib/qt-3.3/include/qmemarray.h /usr/lib/qt-3.3/include/qgarray.h +main.o: /usr/lib/qt-3.3/include/qshared.h +main.o: /usr/lib/qt-3.3/include/qwinexport.h /usr/include/string.h +main.o: /usr/include/limits.h +main.o: /usr/lib/gcc/i386-redhat-linux/3.4.3/include/limits.h +main.o: /usr/lib/qt-3.3/include/qnamespace.h /usr/lib/qt-3.3/include/qmutex.h +main.o: /usr/lib/qt-3.3/include/qsemaphore.h +main.o: /usr/lib/qt-3.3/include/qwaitcondition.h +hxsplay.o: /usr/include/dlfcn.h /usr/include/features.h +hxsplay.o: /usr/include/sys/cdefs.h /usr/include/gnu/stubs.h +hxsplay.o: /usr/lib/gcc/i386-redhat-linux/3.4.3/include/stddef.h +hxsplay.o: /usr/include/bits/dlfcn.h /usr/include/sys/param.h +hxsplay.o: /usr/include/limits.h +hxsplay.o: /usr/lib/gcc/i386-redhat-linux/3.4.3/include/limits.h +hxsplay.o: /usr/include/linux/limits.h /usr/include/linux/param.h +hxsplay.o: /usr/include/asm/param.h /usr/include/unistd.h +hxsplay.o: /usr/include/bits/posix_opt.h /usr/include/bits/types.h +hxsplay.o: /usr/include/bits/wordsize.h /usr/include/bits/typesizes.h +hxsplay.o: /usr/include/bits/confname.h /usr/include/sys/types.h +hxsplay.o: /usr/include/time.h /usr/lib/qt-3.3/include/qthread.h +hxsplay.o: /usr/lib/qt-3.3/include/qwindowdefs.h +hxsplay.o: /usr/lib/qt-3.3/include/qobjectdefs.h +hxsplay.o: /usr/lib/qt-3.3/include/qglobal.h +hxsplay.o: /usr/lib/qt-3.3/include/qstring.h +hxsplay.o: /usr/lib/qt-3.3/include/qcstring.h +hxsplay.o: /usr/lib/qt-3.3/include/qmemarray.h +hxsplay.o: /usr/lib/qt-3.3/include/qgarray.h +hxsplay.o: /usr/lib/qt-3.3/include/qshared.h +hxsplay.o: /usr/lib/qt-3.3/include/qwinexport.h /usr/include/string.h +hxsplay.o: /usr/lib/qt-3.3/include/qnamespace.h +hxsplay.o: /usr/lib/qt-3.3/include/qmutex.h +hxsplay.o: /usr/lib/qt-3.3/include/qsemaphore.h +hxsplay.o: /usr/lib/qt-3.3/include/qwaitcondition.h hxsplay.h hxsplay.moc diff --git a/amarok/src/engine/helix/TODO b/amarok/src/engine/helix/TODO new file mode 100644 index 00000000..c940a4e0 --- /dev/null +++ b/amarok/src/engine/helix/TODO @@ -0,0 +1,3 @@ +- fix timesync with my alsa device implementation, so scope looks better when using it +- support esd as someone actually asked for it on the channel +- fade on stop diff --git a/amarok/src/engine/helix/amarok_helixengine_plugin.desktop b/amarok/src/engine/helix/amarok_helixengine_plugin.desktop new file mode 100644 index 00000000..705749b0 --- /dev/null +++ b/amarok/src/engine/helix/amarok_helixengine_plugin.desktop @@ -0,0 +1,118 @@ +[Desktop Entry] +Encoding=UTF-8 +Type=Service +Name=Helix Engine +Name[af]=Helix Enjin +Name[ar]=محرك Helix +Name[bg]=Helix +Name[bn]=হেলিক্স ইঞ্জিন +Name[br]=Keflusker Helix +Name[ca]=Motor Helix +Name[da]=Helix motor +Name[de]=Helix +Name[el]=Μηχανή Helix +Name[eo]=Helix Ilo +Name[es]=Motor Helix +Name[et]=Helixi mootor +Name[eu]=Helix motorea +Name[fa]=موتور Helix +Name[fi]=Helix +Name[fr]=Moteur Helix +Name[ga]=Inneall Helix +Name[gl]=Motor Helix +Name[he]=מנוע שמע Helix +Name[hu]=Helix alrendszer +Name[is]=Helix vél +Name[it]=Motore Helix +Name[ja]=Helix エンジン +Name[ka]=Helix dრავა +Name[km]=ម៉ាស៊ីន Helix +Name[lt]=Helix variklis +Name[mk]=Helix-машина +Name[ms]=Enjin Helix +Name[nb]=Helix-motor +Name[nds]=Helix +Name[ne]=हेलिक्स इन्जिन +Name[nl]=Helix-engine +Name[nn]=Helix-motor +Name[pa]=ਹੀਲਿਕਸ ਇੰਜਣ +Name[pl]=Wyjście Helix +Name[pt]=Motor Helix +Name[pt_BR]=Mecanismo Helix +Name[ru]=Helix +Name[se]=Helix-mohtor +Name[sq]=Motor Helix +Name[sr]=Мотор Helix +Name[sr@Latn]=Motor Helix +Name[ss]=Motor Helix +Name[sv]=Helix-gränssnitt +Name[tg]=Муҳаррики Helix +Name[tr]=Helix Motoru +Name[uk]=Рушій Helix +Name[uz]=Helix tizimi +Name[uz@cyrillic]=Helix тизими +Name[wa]=Éndjin Helix +Name[zh_CN]=Helix 引擎 +Name[zh_TW]=Helix 解碼引擎 +X-KDE-Library=libamarok_helixengine_plugin +Comment=Plugin for Amarok +Comment[af]=Inprop module vir Amarok +Comment[ar]= قابس ( برنامج مضاف الى) AmaroK +Comment[bg]=Приставка за Amarok +Comment[bn]=আমারক-এর জন্য প্লাগিন +Comment[br]=Lugent evit Amarok +Comment[ca]=Connector per l'Amarok +Comment[cs]=Modul pro AmaroK +Comment[de]=Modul für Amarok +Comment[el]=Πρόσθετο για το AmaroK +Comment[eo]=Kromaĵo por Amarok +Comment[es]=Extensión para Amarok +Comment[et]=Amaroki plugin +Comment[fa]=وصله برای amaroK +Comment[fi]=Amarok-liitännäinen +Comment[fr]=Module pour Amarok +Comment[ga]=Breiseán AmaroK +Comment[gl]=Extensión para Amarok +Comment[hu]=Bővítőmodul az Amarokhoz +Comment[is]=Íforrit fyrir Amarok +Comment[it]=Plugin per Amarok +Comment[ja]=Amarok のためのプラグイン +Comment[ka]=მოდული Amarok-ისთვის +Comment[km]=កម្មវិធី​ជំនួយ​សម្រាប់ Amarok +Comment[lt]=Amarok įskiepis +Comment[mk]=Приклучок за Амарок +Comment[nb]=Programtillegg for Amarok +Comment[nds]=Moduul för Amarok +Comment[ne]=अमारोकका लागि प्लगइन +Comment[nl]=Plugin voor Amarok +Comment[nn]=Programtillegg for Amarok +Comment[pa]=ਅਮਰੋਕ ਲਈ ਪਲੱਗਇਨ +Comment[pl]=Wtyczka Amaroka +Comment[pt]='Plugin' para o Amarok +Comment[pt_BR]=Plugin para o Amarok +Comment[ru]=Модуль amaroK +Comment[se]=Lassemoduvla Amarok:ii +Comment[sk]=Amarok modul +Comment[sr]=Прикључак за Amarok +Comment[sr@Latn]=Priključak za Amarok +Comment[sv]=Insticksprogram för Amarok +Comment[th]=โปรแกรมเสริมสำหรับ Amarok +Comment[tr]=Amarok için Eklenti +Comment[uk]=Втулок для Amarok +Comment[uz]=Amarok uchun plagin +Comment[uz@cyrillic]=Amarok учун плагин +Comment[wa]=Tchôke-divins po Amarok +Comment[zh_CN]=Amarok 插件 +Comment[zh_TW]=amaroK 插件 +ServiceTypes=Amarok/Plugin + +X-KDE-Amarok-plugintype=engine +X-KDE-Amarok-name=helix-engine +X-KDE-Amarok-authors=Paul Cifarelli +X-KDE-Amarok-email=paul@cifarelli.net +X-KDE-Amarok-rank=75 +X-KDE-Amarok-version=1 +X-KDE-Amarok-framework-version=32 + + + diff --git a/amarok/src/engine/helix/config/Makefile.am b/amarok/src/engine/helix/config/Makefile.am new file mode 100644 index 00000000..fb077794 --- /dev/null +++ b/amarok/src/engine/helix/config/Makefile.am @@ -0,0 +1,15 @@ +noinst_LTLIBRARIES = \ + libhelixconfig.la + +libhelixconfig_la_SOURCES = \ + dummy.cpp \ + helixconfig.kcfgc + +INCLUDES = \ + $(all_includes) + +kde_kcfg_DATA = \ + helixconfig.kcfg + + + diff --git a/amarok/src/engine/helix/config/dummy.cpp b/amarok/src/engine/helix/config/dummy.cpp new file mode 100644 index 00000000..e69de29b diff --git a/amarok/src/engine/helix/config/helixconfig.kcfg b/amarok/src/engine/helix/config/helixconfig.kcfg new file mode 100644 index 00000000..f94bbfe9 --- /dev/null +++ b/amarok/src/engine/helix/config/helixconfig.kcfg @@ -0,0 +1,44 @@ + + + + + config.h + + + + + This is the directory where clntcore.so is located + HELIX_LIBS "/common" + + + + This is the directory where, for example, vorbisrend.so is located + HELIX_LIBS "/plugins" + + + + This is the directory where, for example, cvt1.so is located + HELIX_LIBS "/codecs" + + + + OSS vs ALSA + "oss" + + + + ALSA Device + "default" + + + + Is the device selected + false + + + + + diff --git a/amarok/src/engine/helix/config/helixconfig.kcfgc b/amarok/src/engine/helix/config/helixconfig.kcfgc new file mode 100644 index 00000000..c0f6079d --- /dev/null +++ b/amarok/src/engine/helix/config/helixconfig.kcfgc @@ -0,0 +1,7 @@ +# Code generation options for kconfig_compiler +File=helixconfig.kcfg +ClassName=HelixConfig +Singleton=true +Mutators=true +MemberVariables=private + diff --git a/amarok/src/engine/helix/helix-configdialog.cpp b/amarok/src/engine/helix/helix-configdialog.cpp new file mode 100644 index 00000000..15895db4 --- /dev/null +++ b/amarok/src/engine/helix/helix-configdialog.cpp @@ -0,0 +1,482 @@ +/*************************************************************************** + * Copyright (C) 2005 Paul Cifarelli * + * * + * 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. * + * * + ***************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "helix-configdialog.h" +#include "helix-engine.h" + +#include "config/helixconfig.h" +#include + +#include + +#define DEBUG_PREFIX "helix-engine" +#define indent helix_indent + +#include "debug.h" + +using namespace std; + +HelixConfigDialogBase *HelixConfigDialog::instance = NULL; + +HelixConfigEntry::HelixConfigEntry( QWidget *parent, + Amarok::PluginConfig *pluginConfig, + int row, + const QString & description, + const char *defaultvalue, + const QString & tooltip) + : m_w(0) + , m_valueChanged( false ) + , m_stringValue( defaultvalue ) +{ + QGridLayout *grid = (QGridLayout*)parent->layout(); + + m_w = new KLineEdit( m_stringValue, parent ); + connect( (QWidget *) m_w, SIGNAL(textChanged( const QString& )), this, SLOT(slotStringChanged( const QString& )) ); + connect( (QWidget *) m_w, SIGNAL(textChanged( const QString& )), pluginConfig, SIGNAL(viewChanged()) ); + + QToolTip::add( (QWidget *) m_w, "" + tooltip ); + + QLabel* d = new QLabel( description + ':', parent ); + d->setAlignment( QLabel::WordBreak | QLabel::AlignVCenter ); + + grid->addWidget( (QWidget *) m_w, row, 1 ); + grid->addWidget( d, row, 0 ); +} + +HelixConfigEntry::HelixConfigEntry( QWidget *parent, + QString &str, + Amarok::PluginConfig *pluginConfig, + int row, + const QString & description, + const char *defaultvalue, + const QString & tooltip) + : m_w(0) + , m_valueChanged( false ) + , m_stringValue( defaultvalue ) +{ + QGridLayout *grid = (QGridLayout*)parent->layout(); + + m_key = str; + + m_w = new KLineEdit( str, parent ); + connect( m_w, SIGNAL(textChanged( const QString& )), this, SLOT(slotStringChanged( const QString& )) ); + connect( m_w, SIGNAL(textChanged( const QString& )), pluginConfig, SIGNAL(viewChanged()) ); + + QToolTip::add( m_w, "" + tooltip ); + + QLabel* d = new QLabel( description + ':', parent ); + d->setAlignment( QLabel::WordBreak | QLabel::AlignVCenter ); + + grid->addWidget( m_w, row, 1 ); + grid->addWidget( d, row, 0 ); +} + + +inline void +HelixConfigEntry::slotStringChanged( const QString& ) +{ + m_stringValue = m_w->text(); + m_valueChanged = true; +} + +HelixSoundDevice::HelixSoundDevice( QWidget *parent, + Amarok::PluginConfig *pluginConfig, + int &row, + HelixEngine *engine ) + : deviceComboBox(0), checkBox_outputDevice(0), lineEdit_outputDevice(0), m_changed(false), m_engine(engine) +{ + QGridLayout *grid = (QGridLayout*)parent->layout(); + + deviceComboBox = new KComboBox( false, parent, "deviceComboBox" ); + deviceComboBox->insertItem("oss"); // I believe these are not subject to translation (they don't seem to be in xine, +#ifdef USE_HELIX_ALSA + deviceComboBox->insertItem("alsa"); // and neither are the equivalents in gst (osssink and alsasink) +#endif + deviceComboBox->setCurrentItem(HelixConfig::outputplugin()); + QLabel* op = new QLabel( i18n("Output plugin:"), parent ); + op->setAlignment( QLabel::WordBreak | QLabel::AlignVCenter ); + grid->addWidget( op, row, 0 ); + grid->addWidget( deviceComboBox, row, 1); + connect( (QWidget *)deviceComboBox, SIGNAL( activated( const QString& ) ), this, SLOT( slotNewDevice( const QString& )) ); + connect( (QWidget *)deviceComboBox, SIGNAL( activated( const QString& )), pluginConfig, SIGNAL(viewChanged()) ); + + ++row; + + checkBox_outputDevice = new QCheckBox( parent, "checkBox_outputDevice" ); + checkBox_outputDevice->setSizePolicy( QSizePolicy( (QSizePolicy::SizeType)5, (QSizePolicy::SizeType)5, 0, 0, checkBox_outputDevice->sizePolicy().hasHeightForWidth() ) ); + grid->addWidget( checkBox_outputDevice, row, 0 ); + checkBox_outputDevice->setText( i18n( "Device:" ) ); + + lineEdit_outputDevice = new KLineEdit( HelixConfig::device(), parent ); + connect( (QWidget *) lineEdit_outputDevice, SIGNAL(textChanged( const QString& )), this, SLOT(slotStringChanged( const QString& )) ); + connect( (QWidget *) lineEdit_outputDevice, SIGNAL( textChanged( const QString& )), pluginConfig, SIGNAL(viewChanged()) ); + connect( checkBox_outputDevice, SIGNAL( toggled(bool) ), lineEdit_outputDevice, SLOT( setEnabled(bool) ) ); + connect( checkBox_outputDevice, SIGNAL( toggled(bool) ), pluginConfig, SIGNAL(viewChanged()) ); + + connect( checkBox_outputDevice, SIGNAL( toggled(bool) ), this, SLOT( slotDeviceChecked(bool) ) ); + grid->addWidget( (QWidget *) lineEdit_outputDevice, row, 1 ); + + if (HelixConfig::deviceenabled()) + { + checkBox_outputDevice->setChecked( true ); + lineEdit_outputDevice->setEnabled( true ); + } + else + { + checkBox_outputDevice->setChecked( false ); + lineEdit_outputDevice->setEnabled( false ); + } + + if (HelixConfig::outputplugin() == "oss") + { + checkBox_outputDevice->setEnabled( false ); + lineEdit_outputDevice->setEnabled( false ); + } +} + +void +HelixSoundDevice::slotNewDevice( const QString &dev ) +{ + if (dev == "oss") + { + checkBox_outputDevice->setEnabled( false ); + lineEdit_outputDevice->setEnabled(false); + } + else + { + checkBox_outputDevice->setEnabled( true ); + if (checkBox_outputDevice->isChecked()) + lineEdit_outputDevice->setEnabled( true ); + else + lineEdit_outputDevice->setEnabled( false ); + } + + m_changed = true; +} + +void +HelixSoundDevice::slotStringChanged( const QString& ) +{ + m_changed = true; +} + +void +HelixSoundDevice::slotDeviceChecked( bool checked ) +{ + checkBox_outputDevice->setChecked( checked ); + if (checked) + lineEdit_outputDevice->setEnabled( true ); + else + lineEdit_outputDevice->setEnabled( false ); + m_changed = true; +} + +bool +HelixSoundDevice::save() +{ + if (m_changed) + { + HelixConfig::setOutputplugin(deviceComboBox->currentText()); + if (deviceComboBox->currentText() == "oss") + m_engine->setOutputSink(HelixSimplePlayer::OSS); + else + m_engine->setOutputSink(HelixSimplePlayer::ALSA); + + HelixConfig::setDevice( lineEdit_outputDevice->text() ); + if (checkBox_outputDevice->isChecked()) + m_engine->setDevice( lineEdit_outputDevice->text().utf8() ); + else + m_engine->setDevice("default"); + HelixConfig::setDeviceenabled( checkBox_outputDevice->isChecked() ); + } + + return m_changed; +} + +void HelixSoundDevice::setSoundSystem( int api ) +{ + switch (api) + { + case HelixSimplePlayer::OSS: + deviceComboBox->setCurrentItem("oss"); + checkBox_outputDevice->setEnabled( false ); + lineEdit_outputDevice->setEnabled(false); + break; + + case HelixSimplePlayer::ALSA: + deviceComboBox->setCurrentItem("alsa"); + checkBox_outputDevice->setEnabled( true ); + if (checkBox_outputDevice->isChecked()) + lineEdit_outputDevice->setEnabled( true ); + else + lineEdit_outputDevice->setEnabled( false ); + break; + }; + HelixConfig::setOutputplugin(deviceComboBox->currentText()); + HelixConfig::writeConfig(); +} + +void HelixConfigDialogBase::setSoundSystem( int api ) +{ + m_device->setSoundSystem(api); +} + + +HelixConfigDialogBase::HelixConfigDialogBase( HelixEngine *engine, Amarok::PluginConfig *config, QWidget *p ) + : QTabWidget( p ) + , m_core(0) + , m_plugin(0) + , m_codec(0) + , m_device(0) + , m_engine( engine ) +{ + int row = 0; + QString currentPage; + QWidget *parent = 0; + QGridLayout *grid = 0; + QScrollView *sv = 0; + + QString pageName( i18n("Main") ); + + addTab( sv = new QScrollView, pageName ); + parent = new QWidget( sv->viewport() ); + + sv->setResizePolicy( QScrollView::AutoOneFit ); + sv->setHScrollBarMode( QScrollView::AlwaysOff ); + sv->setFrameShape( QFrame::NoFrame ); + sv->addChild( parent ); + + grid = new QGridLayout( parent, /*rows*/20, /*cols*/2, /*margin*/10, /*spacing*/10 ); + grid->setColStretch( 0, 1 ); + grid->setColStretch( 1, 1 ); + + if( sv ) + sv->setMinimumWidth( grid->sizeHint().width() + 20 ); + + engine->m_coredir = HelixConfig::coreDirectory(); + m_core = new HelixConfigEntry( parent, engine->m_coredir, + config, row, + i18n("Helix/Realplay core directory"), + HelixConfig::coreDirectory().utf8(), + i18n("This is the directory where clntcore.so is located")); + ++row; + engine->m_pluginsdir = HelixConfig::pluginDirectory(); + m_plugin = new HelixConfigEntry( parent, engine->m_pluginsdir, + config, row, + i18n("Helix/Realplay plugins directory"), + HelixConfig::pluginDirectory().utf8(), + i18n("This is the directory where, for example, vorbisrend.so is located")); + ++row; + engine->m_codecsdir = HelixConfig::codecsDirectory(); + m_codec = new HelixConfigEntry( parent, engine->m_codecsdir, + config, row, + i18n("Helix/Realplay codecs directory"), + HelixConfig::codecsDirectory().utf8(), + i18n("This is the directory where, for example, cvt1.so is located")); + ++row; + grid->addMultiCellWidget( new KSeparator( KSeparator::Horizontal, parent ), row, row, 0, 1 ); + + ++row; + m_device = new HelixSoundDevice( parent, config, row, engine ); + + // lets find the logo if we can + QPixmap *pm = 0; + QString logo = HelixConfig::coreDirectory(); + if (logo.isEmpty()) + logo = HELIX_LIBS "/common"; + + logo.append("/../share/"); + + QString tmp = logo; + tmp.append("hxplay/logo.png"); + if (QFileInfo(tmp).exists()) + { + logo = tmp; + pm = new QPixmap(logo); + } + else + { + tmp = logo; + tmp.append("realplay/logo.png"); + if (QFileInfo(tmp).exists()) + { + logo = tmp; + pm = new QPixmap(logo); + } + } + + if (pm) + { + QLabel *l = new QLabel(parent); + l->setPixmap(*pm); + grid->addMultiCellWidget( l, 20, 20, 1, 1, Qt::AlignRight ); + } + + entries.setAutoDelete( true ); + + pageName = i18n("Plugins"); + + addTab( sv = new QScrollView, pageName ); + parent = new QWidget( sv->viewport() ); + + sv->setResizePolicy( QScrollView::AutoOneFit ); + sv->addChild( parent ); + + QTextEdit *le = new QTextEdit( parent ); + if( sv ) + sv->setMinimumWidth( le->sizeHint().width() ); + + grid = new QGridLayout( parent, /*rows*/1, /*cols*/1, /*margin*/2, /*spacing*/1 ); + grid->addMultiCellWidget( le, 0, 1, 0, 1, 0 ); + le->setWordWrap(QTextEdit::NoWrap); + + int n = engine->numPlugins(); + const char *description, *copyright, *moreinfourl; + row = 0; + for (int i=0; igetPluginInfo(i, description, copyright, moreinfourl)) + { + le->append(QString(description)); + le->append(QString(copyright)); + le->append(QString(moreinfourl)); + le->append(QString(" ")); + } + } + + le->setReadOnly(true); + le->setContentsPos(0,0); +} + +HelixConfigDialogBase::~HelixConfigDialogBase() +{ + delete m_core; + delete m_plugin; + delete m_codec; + delete m_device; +} + +bool +HelixConfigDialogBase::hasChanged() const +{ + for( QPtrListIterator it( entries ); *it != 0; ++it ) + if ( (*it)->isChanged() ) + return true; + if (m_core->isChanged() || m_plugin->isChanged() || m_codec->isChanged() || m_device->isChanged()) + return true; + + return false; +} + +bool +HelixConfigDialogBase::isDefault() const +{ + return false; +} + +void +HelixConfigDialogBase::save() +{ + bool writeIt = false; + + if (m_core->isChanged()) + { + m_engine->m_coredir = m_core->stringValue(); + HelixConfig::setCoreDirectory(m_engine->m_coredir); + writeIt = true; + } + + if (m_plugin->isChanged()) + { + m_engine->m_pluginsdir = m_plugin->stringValue(); + HelixConfig::setPluginDirectory(m_engine->m_pluginsdir); + writeIt = true; + } + + if (m_codec->isChanged()) + { + m_engine->m_codecsdir = m_codec->stringValue(); + HelixConfig::setCodecsDirectory(m_engine->m_codecsdir); + writeIt = true; + } + + writeIt |= m_device->save(); + + // not really doing anything here yet + for( HelixConfigEntry *entry = entries.first(); entry; entry = entries.next() ) + { + if( entry->isChanged() ) + { + entry->setUnchanged(); + } + } + + if (m_device->isChanged()) + { + m_device->setUnchanged(); + writeIt = true; + } + + if (writeIt) + { + HelixConfig::writeConfig(); + + // reinit... + m_engine->init(); + } + +} + +HelixConfigDialog::HelixConfigDialog( HelixEngine *engine, QWidget *p ) : Amarok::PluginConfig() +{ + if (!instance) + instance = new HelixConfigDialogBase( engine, this, p ); +} + +HelixConfigDialog::~HelixConfigDialog() +{ + delete instance; + instance = 0; +} + +int HelixConfigDialog::setSoundSystem( int api ) +{ + if (instance) + { + instance->setSoundSystem(api); + return 0; + } + else + { + HelixConfig::setOutputplugin(api ? "alsa" : "oss"); + HelixConfig::writeConfig(); + return 1; + } +} + +#include "helix-configdialog.moc" diff --git a/amarok/src/engine/helix/helix-configdialog.h b/amarok/src/engine/helix/helix-configdialog.h new file mode 100644 index 00000000..0e9487c7 --- /dev/null +++ b/amarok/src/engine/helix/helix-configdialog.h @@ -0,0 +1,121 @@ +/*************************************************************************** + * Copyright (C) 2005 Paul Cifarelli * + * * + * 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. * + * * + ***************************************************************************/ + +#ifndef _HELIX_CONFIG_H_ +#define _HELIX_CONFIG_H_ + +#include "plugin/pluginconfig.h" +#include +#include +#include + +class QGridLayout; +class KComboBox; +class QCheckBox; +class KLineEdit; +class HelixEngine; + +// since many preferences can be set in Helix, I'm planning on more config items later +// for now I'll just get the location of the Helix core/plugins for initializing +// the Helix core +class HelixConfigEntry : public QObject +{ +Q_OBJECT +public: + HelixConfigEntry( QWidget *parent, Amarok::PluginConfig*, + int row, const QString & description, const char *defaultvalue, const QString & tooltip ); + HelixConfigEntry( QWidget *parent, QString &str, Amarok::PluginConfig*, + int row, const QString & description, const char *defaultvalue, const QString & tooltip ); + + bool isChanged() const { return m_valueChanged; } + void setUnchanged() { m_valueChanged = false; } + const QString& key() const { return m_key; } + QString stringValue() const { return m_stringValue; } + int numValue() const { return m_numValue; } + +private slots: + void slotStringChanged( const QString& ); + +private: + KLineEdit *m_w; + bool m_valueChanged; + int m_numValue; + QString m_key; + QString m_stringValue; +}; + +class HelixSoundDevice : public QObject +{ +Q_OBJECT +public: + HelixSoundDevice( QWidget *parent, Amarok::PluginConfig *config, int &row, HelixEngine *engine ); + bool save(); + void setSoundSystem( int api ); + bool isChanged() const { return m_changed; } + void setUnchanged() { m_changed = false; } + +private slots: + void slotNewDevice( const QString& ); + void slotStringChanged( const QString& ); + void slotDeviceChecked( bool ); + +private: + KComboBox* deviceComboBox; + QCheckBox* checkBox_outputDevice; + KLineEdit* lineEdit_outputDevice; + bool m_changed; + HelixEngine *m_engine; +}; + + +class HelixConfigDialogBase : public QTabWidget +{ +public: + HelixConfigDialogBase( HelixEngine *engine, Amarok::PluginConfig *config, QWidget *parent = 0 ); + ~HelixConfigDialogBase(); + + virtual QWidget *view() { return this; } + virtual bool hasChanged() const; + virtual bool isDefault() const; + + /** Save view state into configuration */ + virtual void save(); + + void setSoundSystem( int api ); + void setEngine(HelixEngine *e) { m_engine = e; } + +private: + QPtrList entries; + HelixConfigEntry *m_core; + HelixConfigEntry *m_plugin; + HelixConfigEntry *m_codec; + HelixSoundDevice *m_device; + HelixEngine *m_engine; +}; + +class HelixConfigDialog : public Amarok::PluginConfig +{ +public: + HelixConfigDialog( HelixEngine *engine, QWidget *parent = 0 ); + ~HelixConfigDialog(); + + virtual QWidget *view() { return instance->view(); } + virtual bool hasChanged() const { return instance->hasChanged(); } + virtual bool isDefault() const { return instance->isDefault(); } + + virtual void save() { instance->save(); } + static int setSoundSystem( int api ); + +private: + static HelixConfigDialogBase *instance; +}; + + +#endif diff --git a/amarok/src/engine/helix/helix-engine.cpp b/amarok/src/engine/helix/helix-engine.cpp new file mode 100644 index 00000000..f8c8b26a --- /dev/null +++ b/amarok/src/engine/helix/helix-engine.cpp @@ -0,0 +1,897 @@ +/*************************************************************************** + * Copyright (C) 2005 Paul Cifarelli * + * * + * 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. * + * * + ***************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include "debug.h" + +#include +#include + +#include +#include +#include + +#include "helix-engine.h" +#include "helix-configdialog.h" +#include "config/helixconfig.h" +#include "helix-errors.h" +#include "helix-sp.h" +#include "hxplayercontrol.h" +#include "amarokconfig.h" + +AMAROK_EXPORT_PLUGIN( HelixEngine ) + +#define DEBUG_PREFIX "helix-engine" + +using namespace std; + +extern "C" +{ + #include +} + +#define HELIX_ENGINE_TIMER 10 // 10 ms timer +#define SCOPE_MAX_BEHIND 200 // 200 postmix buffers + + +#ifndef LLONG_MAX +#define LLONG_MAX 9223372036854775807LL +#endif + + +///returns the configuration we will use +static inline QCString configPath() { return QFile::encodeName( QDir::homeDirPath() + "/.helix/config" ); } + + +HelixEngine::HelixEngine() + : EngineBase(), PlayerControl(), + m_state(Engine::Empty), + m_coredir(HELIX_LIBS "/common"), + m_pluginsdir(HELIX_LIBS "/plugins"), + m_codecsdir(HELIX_LIBS "/codecs"), + m_inited(false), + m_scopeplayerlast(0), + m_sfps(0.0), + m_scopedelta(0), + m_sframes(0), + m_lframes(0) +{ + addPluginProperty( "HasConfigure", "true" ); + addPluginProperty( "HasEqualizer", "true" ); + addPluginProperty( "HasCrossfade", "true" ); + addPluginProperty( "HasCDDA", "false"); + + memset(&m_md, 0, sizeof(m_md)); + memset(hscope, 0, 2*sizeof(HelixScope)); + memset(&m_scopetm, 0, sizeof(struct timeval)); + memset(m_pfade, 0, 2*sizeof(FadeTrack)); +} + +HelixEngine::~HelixEngine() +{ + m_mimes.clear(); +} + +int HelixEngine::print2stdout(const char *fmt, ...) +{ + va_list args; + char buf[1024]; + + va_start(args, fmt); + + int ret = vsprintf(buf, fmt, args); + debug() << buf; + + va_end(args); + + return ret; +} + + +int HelixEngine::print2stderr(const char *fmt, ...) +{ + va_list args; + char buf[1024]; + + va_start(args, fmt); + + int ret = vsprintf(buf, fmt, args); + debug() << buf; + + va_end(args); + + return ret; +} + + +void HelixEngine::notifyUser(unsigned long code, const char *moreinfo, const char *moreinfourl) +{ + QString *err = HelixErrors::errorText(code); + if (err) + emit statusText(i18n("Helix Core returned error: %1 %2 %3").arg(QString(*err)).arg(QString(moreinfo)).arg(QString(moreinfourl))); + else + emit statusText(i18n("Helix Core returned error: ")); +} + +void HelixEngine::interruptUser(unsigned long code, const char *moreinfo, const char *moreinfourl) +{ + QString *err = HelixErrors::errorText(code); + if (err) + emit infoMessage(i18n("Helix Core returned error: %1 %1 %1").arg(QString(*err)).arg(QString(moreinfo)).arg(QString(moreinfourl))); + else + emit infoMessage(i18n("Helix Core returned error: ")); + + // since this is a serious error, emit trackEnded so amarok knows to move on + play_finished( m_current ); +} + + +void HelixEngine::onContacting(const char *host) +{ + emit statusText( i18n("Contacting: %1").arg( QString(host) ) ); +} + +void HelixEngine::onBuffering(int pcnt) +{ + if (pcnt != 100) // let's not report that... + emit statusText( i18n( "Buffering %1%" ).arg( pcnt ) ); +} + + +Amarok::PluginConfig* +HelixEngine::configure() const +{ + debug() << "Starting HelixConfigDialog\n"; + return new HelixConfigDialog( (HelixEngine *)this ); +} + +int HelixEngine::fallbackToOSS() +{ + KMessageBox::information( 0, i18n("The helix library you have configured does not support ALSA, the helix-engine has fallen back to OSS") ); + debug() << "Falling back to OSS\n"; + return (HelixConfigDialog::setSoundSystem( (int) HelixSimplePlayer::OSS )); +} + +bool +HelixEngine::init() +{ + debug() << "Initializing HelixEngine\n"; + struct stat s; + bool exists = false; + stop(); + m_state = Engine::Empty; + + m_numPlayers = 2; + m_current = 1; + + m_coredir = HelixConfig::coreDirectory(); + if (m_coredir.isEmpty()) + m_coredir = HELIX_LIBS "/common"; + + m_pluginsdir = HelixConfig::pluginDirectory(); + if (m_pluginsdir.isEmpty()) + m_pluginsdir = HELIX_LIBS "/plugins"; + + m_codecsdir = HelixConfig::codecsDirectory(); + if (m_codecsdir.isEmpty()) + m_codecsdir = HELIX_LIBS "/codecs"; + + if (HelixConfig::outputplugin() == "oss") + setOutputSink( HelixSimplePlayer::OSS ); + else + { + setOutputSink( HelixSimplePlayer::ALSA ); + if (HelixConfig::deviceenabled()) + setDevice( HelixConfig::device().utf8() ); + else + setDevice("default"); + } + + + if (!stat(m_coredir.utf8(), &s) && !stat(m_pluginsdir.utf8(), &s) && !stat(m_codecsdir.utf8(), &s)) + { + long vol=0; + bool eqenabled=false; + int savedpreamp=0; + QValueList savedequalizerGains; + + if (m_inited) + { + vol = PlayerControl::getVolume(); + eqenabled = PlayerControl::isEQenabled(); + for (unsigned int i=0; i < m_equalizerGains.size(); i++) + savedequalizerGains.append(m_equalizerGains[i]); + savedpreamp = m_preamp; + PlayerControl::tearDown(); + } + + PlayerControl::init(m_coredir.utf8(), m_pluginsdir.utf8(), m_codecsdir.utf8(), 2); + if (PlayerControl::initDirectSS()) + { + fallbackToOSS(); + + PlayerControl::initDirectSS(); + } + + if (m_inited) + { + PlayerControl::setVolume(vol); + setEqualizerEnabled(eqenabled); + setEqualizerParameters(savedpreamp, savedequalizerGains); + } + + m_inited = exists = true; + } + + + if (!exists || PlayerControl::getError()) + { + KMessageBox::error( 0, i18n("The Helix Engine requires the RealPlayer(tm) or HelixPlayer libraries to be installed. Please make sure one is installed, and adjust the paths in \"Amarok Settings\" -> \"Engine\"") ); + // we need to return true here so that the user has an oppportunity to change the directory + //return false; + return true; + } + + // create a list of mime types and ext for use in canDecode() + m_mimes.resize( getMimeListLen() ); + int i = 0; + const MimeList *ml = getMimeList(); + MimeEntry *entry; + while (ml) + { + QString mt = ml->mimetypes; + QString me = ml->mimeexts; + + entry = new MimeEntry; + entry->type = QStringList::split('|', mt); + entry->ext = QStringList::split('|', me); + m_mimes[i] = *entry; + + debug() << ml->mimetypes << endl; + + i++; + ml = ml->fwd; + } + + debug() << "Succussful init\n"; + + return true; +} + + +bool +HelixEngine::load( const KURL &url, bool isStream ) +{ + debug() << "In load " << url.url() << endl; + + if (!m_inited) + return false; + + if (!canDecode(url)) + { + const QString path = url.path(); + const QString ext = path.mid( path.findRev( '.' ) + 1 ).lower(); + emit statusText( i18n("No plugin found for the %1 format").arg(ext) ); + return false; + } + + debug() << "xfadeLength is " << m_xfadeLength << endl; + if( m_xfadeLength > 0 && m_state == Engine::Playing && !isStream && + ( m_xfadeNextTrack || //set by engine controller when switching tracks automatically + (uint) AmarokConfig::crossfadeType() == 0 || //crossfade always + (uint) AmarokConfig::crossfadeType() == 2 ) ) //crossfade when switching tracks manually) + { + //set m_xfadeNextTrack true here regardless to play() will work correctly; disable in there + m_xfadeNextTrack = true; + int nextPlayer = m_current ? 0 : 1; + + // prepare the next player + PlayerControl::stop(nextPlayer); + resetScope(nextPlayer); + memset(&hscope[nextPlayer], 0, sizeof(HelixScope)); + memset(&m_pfade[nextPlayer], 0, sizeof(FadeTrack)); + + if (isPlaying(m_current)) + { + m_pfade[m_current].m_fadeactive = true; + m_pfade[m_current].m_startfadetime = PlayerControl::where(m_current); + setFadeout(true, m_xfadeLength, m_current); + } + Engine::Base::load( url, false ); // we don't crossfade streams ?? do we load the base here ?? + PlayerControl::setURL( QFile::encodeName( url.url() ), nextPlayer, !isStream ); + m_isStream = false; + } + else + cleanup(); + + m_isStream = isStream; + int nextPlayer; + + nextPlayer = m_current ? 0 : 1; + + Engine::Base::load( url, isStream || url.protocol() == "http" ); + m_state = Engine::Idle; + emit stateChanged( Engine::Idle ); + m_url = url; + + if (url.isLocalFile()) + PlayerControl::setURL( QFile::encodeName( url.url() ), nextPlayer, !m_isStream ); + else + { + m_isStream = true; + PlayerControl::setURL( QFile::encodeName( url.url() ), nextPlayer, !m_isStream ); + } + + return true; +} + +bool +HelixEngine::play( uint offset ) +{ + debug() << "In play" << endl; + int nextPlayer; + + if (!m_inited) + return false; + + if (m_state != Engine::Playing) + { + struct timezone tz; + memset(&tz, 0, sizeof(struct timezone)); + gettimeofday(&m_scopetm, &tz); + startTimer(HELIX_ENGINE_TIMER); + } + + nextPlayer = m_current ? 0 : 1; + + if (m_xfadeLength && m_xfadeNextTrack && !offset && isPlaying(m_current)) + { + m_xfadeNextTrack = false; + PlayerControl::start(nextPlayer, true, m_xfadeLength); + } + else + PlayerControl::start(nextPlayer); + + if (offset) + PlayerControl::seek( offset, nextPlayer ); + + if (!PlayerControl::getError()) + { + if (m_state != Engine::Playing) + { + m_state = Engine::Playing; + emit stateChanged( Engine::Playing ); + } + + m_current = nextPlayer; + return true; + } + + cleanup(); + m_state = Engine::Empty; + emit stateChanged( Engine::Empty ); + + return false; +} + +void +HelixEngine::cleanup() +{ + if (!m_inited) + return; + + m_url = KURL(); + PlayerControl::stop(); // stop all players + resetScope(0); + resetScope(1); + killTimers(); + m_isStream = false; + memset(&m_md, 0, sizeof(m_md)); + memset(hscope, 0, 2*sizeof(HelixScope)); + memset(m_pfade, 0, 2*sizeof(FadeTrack)); +} + +void +HelixEngine::stop() +{ + if (!m_inited) + return; + + debug() << "In stop where=" << where(m_current) << " duration=" << duration(m_current) << endl; + + if( AmarokConfig::fadeout() && !m_pfade[m_current].m_fadeactive && state() == Engine::Playing ) + { + debug() << "fading out...\n"; + m_state = Engine::Empty; + emit stateChanged( Engine::Empty ); // tell the controller not to bother you anymore + + m_pfade[m_current].m_fadeactive = true; + m_pfade[m_current].m_stopfade = true; + m_pfade[m_current].m_startfadetime = PlayerControl::where(m_current); + setFadeout(true, AmarokConfig::fadeoutLength(), m_current); + } + else + { + debug() << "Stopping immediately\n"; + cleanup(); + cleanUpStream(m_current); + m_state = Engine::Empty; + emit stateChanged( m_state ); + } +} + + +void HelixEngine::play_finished(int playerIndex) +{ + debug() << "Ok, finished playing the track\n"; + cleanUpStream(playerIndex); + resetScope(playerIndex); + memset(&hscope[playerIndex], 0, sizeof(HelixScope)); + memset(&m_pfade[playerIndex], 0, sizeof(FadeTrack)); + if (playerIndex == m_current && !m_pfade[playerIndex].m_stopfade && !m_pfade[playerIndex].m_fadeactive) + { + m_state = Engine::Idle; + emit stateChanged( m_state ); + emit trackEnded(); + } +} + +void +HelixEngine::pause() +{ + if (!m_inited) + return; + + // TODO: PAUSE in XFADE + debug() << "In pause\n"; + if( m_state == Engine::Playing ) + { + PlayerControl::pause(m_current); + m_state = Engine::Paused; + emit stateChanged( Engine::Paused ); + } +} + +void +HelixEngine::unpause() +{ + if (!m_inited) + return; + + // TODO: PAUSE in XFADE + debug() << "In unpause\n"; + if ( m_state == Engine::Paused ) + { + PlayerControl::resume(m_current); + m_state = Engine::Playing; + emit stateChanged( Engine::Playing ); + } +} + +Engine::State +HelixEngine::state() const +{ + //debug() << "In state, state is " << m_state << endl; + + if (!m_inited || m_url.isEmpty()) + return (Engine::Empty); + + return m_state; +} + +uint +HelixEngine::position() const +{ + if (!m_inited) + return 0; + + return PlayerControl::where(m_current); +} + +uint +HelixEngine::length() const +{ + if (!m_inited) + return 0; + + return PlayerControl::duration(m_current); +} + +void +HelixEngine::seek( uint ms ) +{ + if (!m_inited) + return; + + debug() << "In seek\n"; + resetScope(0); + resetScope(1); + PlayerControl::seek(ms, m_current); +} + +void +HelixEngine::setVolumeSW( uint vol ) +{ + if (!m_inited) + return; + + debug() << "In setVolumeSW\n"; + PlayerControl::setVolume(vol); // set the volume in all players! +} + + +bool +HelixEngine::canDecode( const KURL &url ) const +{ + if (!m_inited) + return false; + + debug() << "In canDecode " << url.prettyURL() << endl; + + if (url.protocol() == "http" || url.protocol() == "rtsp") + return true; + + const QString path = url.path(); + const QString ext = path.mid( path.findRev( '.' ) + 1 ).lower(); + + if (ext != "txt") + for (int i=0; i<(int)m_mimes.size(); i++) + { + if (m_mimes[i].type.grep("audio").count() || + m_mimes[i].type.grep("video").count() || + m_mimes[i].type.grep("application").count()) + if (m_mimes[i].ext.grep(ext).count()) + { + return true; + } + } + + return false; +} + +void +HelixEngine::timerEvent( QTimerEvent * ) +{ + PlayerControl::dispatch(); // dispatch the players + if ( m_xfadeLength <= 0 && m_state == Engine::Playing && PlayerControl::done(m_current) ) + play_finished(m_current); + else if ( m_xfadeLength > 0 || AmarokConfig::fadeout() ) + { + if ( m_state == Engine::Playing && isPlaying(m_current?0:1) && PlayerControl::done(m_current?0:1) ) + hscope[m_current?0:1].m_lasttime = 0; + + // fade on stop finished + if ( m_pfade[m_current].m_stopfade && m_pfade[m_current].m_fadeactive && + (PlayerControl::where(m_current) > m_pfade[m_current].m_startfadetime + (unsigned)AmarokConfig::fadeoutLength() || + PlayerControl::done(m_current)) ) + { + debug() << "Stop fade end\n"; + stop(); + } + + // crossfade finished + if ( m_pfade[m_current?0:1].m_fadeactive && + PlayerControl::where(m_current?0:1) > m_pfade[m_current?0:1].m_startfadetime + (unsigned)m_xfadeLength) + play_finished(m_current?0:1); + } + + // prune the scope(s) + prune(); + + struct timeval tm; + struct timezone tz; + memset(&tz, 0, sizeof(struct timezone)); + gettimeofday(&tm, &tz); + m_scopedelta = (tm.tv_sec - m_scopetm.tv_sec) * 1000 + (tm.tv_usec - m_scopetm.tv_usec) / 1000; // ms + m_scopetm.tv_sec = tm.tv_sec; + m_scopetm.tv_usec = tm.tv_usec; + hscope[m_current].m_lasttime += m_scopedelta; + + HelixSimplePlayer::metaData *md = getMetaData(m_current); + if (m_isStream && + (strcmp(m_md.title, md->title) || strcmp(m_md.artist, md->artist))) + { + memcpy(&m_md, md, sizeof(m_md)); + + debug() << "{Title}: " << md->title << " {Artist}: " << md->artist << " {Bitrate}: " << md->bitrate << endl; + + /* Real Radio One (and Rhapsody?) streams have their own format, where title is: + * clipinfo:title=|artist name=<artist>|Album name=<album>|Artist:Next artist=<next artist>| \ + * ordinal=<some number>|duration=<in secs>|Track:Rhapsody Track Id=<some number> + * + * for all other streams helix sends the title of the song in the artist string. + * this prevents context lookup, so we split it here (the artist and title are separated by a '-' + * we'll put the 'title' in album instead... + */ + Engine::SimpleMetaBundle bndl; + bndl.album = QString::fromUtf8( m_md.title ); + if ( bndl.album.startsWith( QString("clipinfo:") ) ) + { + bndl.album = bndl.album.remove(0, 9); + QStringList sl = QStringList::split('|', bndl.album); + for ( QStringList::Iterator it = sl.begin(); it != sl.end(); ++it ) + { + if ((*it).startsWith("title=")) + bndl.title = (*it).section('=', 1, 1); + if ((*it).startsWith("artist name=")) + bndl.artist = (*it).section('=', 1, 1); + if ((*it).startsWith("Album name=")) + bndl.album = (*it).section('=', 1, 1); + if ((*it).startsWith("duration=")) + bndl.length = (*it).section('=', 1, 1); + } + + //debug() << "Title: " << bndl.title << endl; + //debug() << "Artist: " << bndl.artist << endl; + //debug() << "Album: " << bndl.album << endl; + //debug() << "length: " << bndl.length << endl; + } + else + { + char c,*tmp = strchr(m_md.artist, '-'); + if (tmp) + { + tmp--; + c = *tmp; + *tmp = '\0'; + bndl.artist = QString::fromUtf8( m_md.artist ); + *tmp = c; + tmp+=3; + bndl.title = QString::fromUtf8( tmp ); + bndl.album = QString::fromUtf8( m_md.title ); + } + else // just copy them as is... + { + bndl.title = QString::fromUtf8( m_md.title ); + bndl.artist = QString::fromUtf8( m_md.artist ); + } + } + bndl.bitrate = QString::number( m_md.bitrate / 1000 ); + emit EngineBase::metaData( bndl ); + } +} + + +int HelixEngine::prune() +{ + int err = 0; + + err |= prune(0); + err |= prune(1); + + return err; +} + +int HelixEngine::prune(int playerIndex) +{ + // + // this bit is to help us keep more accurate time than helix provides + ///////////////////////////////////////////////////////////////////// + unsigned long hpos = PlayerControl::where(playerIndex); + + if (hpos != hscope[playerIndex].m_lastpos + && hpos - hscope[playerIndex].m_lastpos < hscope[playerIndex].m_lasttime - hscope[playerIndex].m_lastpos) + hscope[playerIndex].m_lasttime = hpos; + + if (hpos > hscope[playerIndex].m_lasttime) + { + hscope[playerIndex].m_w = hpos; + hscope[playerIndex].m_lasttime = hpos; + } + else + hscope[playerIndex].m_w = hscope[playerIndex].m_lasttime; + + hscope[playerIndex].m_lastpos = hpos; + + if ( getScopeCount(playerIndex) > SCOPE_MAX_BEHIND ) // protect against naughty streams + { + resetScope(playerIndex); + return 0; + } + + if (!hscope[playerIndex].m_w || !hscope[playerIndex].m_item) + return 0; + + // prune, unless the player is still starting + while (hpos && hscope[playerIndex].m_item && hscope[playerIndex].m_w > hscope[playerIndex].m_item->etime) + { + //debug() << "pruning " << hpos << "," << hscope[playerIndex].m_w << "," << hscope[playerIndex].m_lasttime + // << "," << hscope[playerIndex].m_item->time << ":" << hscope[playerIndex].m_item->etime << endl; + + if (hscope[playerIndex].m_item && hscope[playerIndex].m_item->allocd) + delete hscope[playerIndex].m_item; + hscope[playerIndex].m_item = getScopeBuf(playerIndex); + } + + if (!hscope[playerIndex].m_item) + return 0; + + if (hscope[playerIndex].m_w < hscope[playerIndex].m_item->time) // wait for the player to catchup + { + //debug() << "waiting for player to catchup " << hpos << "," << hscope[playerIndex].m_w << "," << hscope[playerIndex].m_lasttime + // << "," << hscope[playerIndex].m_item->time << ":" << hscope[playerIndex].m_item->etime << endl; + return 0; + } + + return 1; +} + +const Engine::Scope &HelixEngine::scope() +{ + if (isPlaying(0) && isPlaying(1)) // crossfading + { + if (m_scopeplayerlast) + scope(m_current); + else + scope(m_current?0:1); + + m_scopeplayerlast = !m_scopeplayerlast; + } + else + scope(m_current); + + return m_scope; +} + +int HelixEngine::scope(int playerIndex) +{ + int i; + unsigned long t; + + if (!m_inited) + return 0; + + if (!hscope[playerIndex].m_item && !peekScopeTime(t, playerIndex)) + hscope[playerIndex].m_item = getScopeBuf(playerIndex); + + if (!prune(playerIndex)) + return 0; + + if (hscope[playerIndex].m_item->nchan > 2) + return 0; + + int j,k=0; + short int *pint; + unsigned char b[4]; + + // calculate the starting offset into the buffer + int off = (hscope[playerIndex].m_item->spb * (hscope[playerIndex].m_w - hscope[playerIndex].m_item->time) / + (hscope[playerIndex].m_item->etime - hscope[playerIndex].m_item->time)) * + hscope[playerIndex].m_item->nchan * hscope[playerIndex].m_item->bps; + k = off; + while (hscope[playerIndex].m_item && hscope[playerIndex].m_scopeindex < SCOPESIZE) + { + while (k < (int) hscope[playerIndex].m_item->len) + { + for (j=0; j<hscope[playerIndex].m_item->nchan; j++) + { + switch (hscope[playerIndex].m_item->bps) + { + case 1: + b[1] = 0; + b[0] = hscope[playerIndex].m_item->buf[k]; + break; + case 2: + b[1] = hscope[playerIndex].m_item->buf[k+1]; + b[0] = hscope[playerIndex].m_item->buf[k]; + break; + } + + pint = (short *) &b[0]; + + if (hscope[playerIndex].m_item->nchan == 1) // duplicate mono samples + { + hscope[playerIndex].m_currentScope[hscope[playerIndex].m_scopeindex] = *pint; + hscope[playerIndex].m_scopeindex++; + hscope[playerIndex].m_currentScope[hscope[playerIndex].m_scopeindex] = *pint; + hscope[playerIndex].m_scopeindex++; + } + else + { + hscope[playerIndex].m_currentScope[hscope[playerIndex].m_scopeindex] = *pint; + hscope[playerIndex].m_scopeindex++; + } + + k += hscope[playerIndex].m_item->bps; + } + + if (hscope[playerIndex].m_scopeindex >= SCOPESIZE) + { + hscope[playerIndex].m_scopeindex = SCOPESIZE; + break; + } + } + // as long as we know there's another buffer...otherwise we need to wait for another + if (hscope[playerIndex].m_scopeindex < SCOPESIZE) + { + if (hscope[playerIndex].m_item && hscope[playerIndex].m_item->allocd) + delete hscope[playerIndex].m_item; + hscope[playerIndex].m_item = getScopeBuf(playerIndex); + + k = 0; + + if (!hscope[playerIndex].m_item) + return 0; // wait until there are some more buffers available + } + else + { + if (k >= (int) hscope[playerIndex].m_item->len) + { + if (hscope[playerIndex].m_item && hscope[playerIndex].m_item->allocd) + delete hscope[playerIndex].m_item; + hscope[playerIndex].m_item = getScopeBuf(playerIndex); + } + break; // done with the scope buffer, so hand it off + } + } + + // ok, we must have a full buffer here, give it to the scope + for (i=0; i<SCOPESIZE; i++) + m_scope[i] = hscope[playerIndex].m_currentScope[i]; + hscope[playerIndex].m_scopeindex = 0; + + return 1; +} + +void +HelixEngine::resetScope(int playerIndex) +{ + if (playerIndex >=0 && playerIndex < numPlayers()) + { + // make sure the scope is clear of old buffers + clearScopeQ(playerIndex); + hscope[playerIndex].m_scopeindex = 0; + if (hscope[playerIndex].m_item && hscope[playerIndex].m_item->allocd) + delete hscope[playerIndex].m_item; + hscope[playerIndex].m_w = 0; + hscope[playerIndex].m_item = 0; + } +} + + +void +HelixEngine::setEqualizerEnabled( bool enabled ) //SLOT +{ + enableEQ(enabled); +} + + +// ok, this is lifted from gst... but why mess with what works? +void +HelixEngine::setEqualizerParameters( int preamp, const QValueList<int>& bandGains ) //SLOT +{ + m_preamp = ( preamp + 100 ) / 2; + + m_equalizerGains.resize( bandGains.count() ); + for ( uint i = 0; i < bandGains.count(); i++ ) + m_equalizerGains[i] = ( *bandGains.at( i ) + 100 ) / 2; + + updateEQgains(); +} + + +namespace Debug +{ + #undef helix_indent + QCString helix_indent; +} + +#include "helix-engine.moc" diff --git a/amarok/src/engine/helix/helix-engine.h b/amarok/src/engine/helix/helix-engine.h new file mode 100644 index 00000000..e7282d3b --- /dev/null +++ b/amarok/src/engine/helix/helix-engine.h @@ -0,0 +1,131 @@ +/*************************************************************************** + * Copyright (C) 2005-2006 Paul Cifarelli <paul@cifarelli.net> * + * * + * 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. * + * * + ***************************************************************************/ + +#ifndef _HELIX_ENGINE_H_ +#define _HELIX_ENGINE_H_ + +#include "enginebase.h" +#include <qobject.h> +#include <sys/types.h> +#include <hxplayercontrol.h> + +class QStringList; +struct timeval; + +class HelixEngine : public Engine::Base, public PlayerControl +{ + Q_OBJECT + +public: + HelixEngine(); + ~HelixEngine(); + + virtual bool init(); + virtual bool canDecode( const KURL& ) const; + virtual uint position() const; + virtual uint length() const; + virtual Engine::State state() const; + + virtual void play_finished(int playerIndex); + virtual const Engine::Scope &scope(); + + virtual Amarok::PluginConfig *configure() const; + + virtual void onContacting(const char *host); + virtual void onBuffering(int pcnt); + + virtual int fallbackToOSS(); + +public slots: + virtual bool load( const KURL &url, bool stream ); + virtual bool play( uint = 0 ); + virtual void stop(); + virtual void pause(); + virtual void unpause(); + virtual void seek( uint ); + + virtual void setEqualizerEnabled( bool ); + virtual void setEqualizerParameters( int preamp, const QValueList<int>& ); + + +protected: + virtual void setVolumeSW( uint ); + +private: + Engine::State m_state; + KURL m_url; + + QString m_coredir; + QString m_pluginsdir; + QString m_codecsdir; + bool m_inited; + + int m_numPlayers; + int m_current; // the current player + + bool m_isStream; + HelixSimplePlayer::metaData m_md; + + int scope(int playerIndex); + int prune(); + int prune(int playerIndex); + bool m_scopeplayerlast; + float m_sfps; + struct timeval m_scopetm; + unsigned long m_scopedelta; + int m_sframes; + int m_lframes; + struct HelixScope + { + DelayQueue *m_item; + unsigned long m_lasttime; + unsigned long m_lastpos; + unsigned short m_currentScope[SCOPESIZE]; + int m_scopeindex; + unsigned long m_w; // more accurate position estimate for the player + } hscope[2]; + + typedef struct MimeEntry + { + QStringList type; + QStringList ext; + }; + + std::vector<MimeEntry> m_mimes; + + struct FadeTrack + { + unsigned long m_startfadetime; + bool m_fadeactive; + bool m_stopfade; + } m_pfade[2]; + + void cleanup(); + void timerEvent( QTimerEvent * ); + void resetScope(int playerIndex); + + int print2stdout(const char *fmt, ...) +#ifdef __GNUC__ + __attribute__ ((format (printf, 2, 3))) +#endif + ; + int print2stderr(const char *fmt, ...) +#ifdef __GNUC__ + __attribute__ ((format (printf, 2, 3))) +#endif + ; + void notifyUser(unsigned long code, const char *moreinfo, const char *moreinfourl); + void interruptUser(unsigned long code, const char *moreinfo, const char *moreinfourl); + + friend class HelixConfigDialogBase; +}; + + +#endif diff --git a/amarok/src/engine/helix/helix-errors.cpp b/amarok/src/engine/helix/helix-errors.cpp new file mode 100644 index 00000000..e4412c7b --- /dev/null +++ b/amarok/src/engine/helix/helix-errors.cpp @@ -0,0 +1,457 @@ +/*************************************************************************** + * Copyright (C) 2005 Paul Cifarelli * + * * + * 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. * + * * + ***************************************************************************/ +#include <qdict.h> +#include <config.h> +#include <iostream> +#include "debug.h" +#include <klocale.h> +#include <map> + +using namespace std; + +#include "helix-errors.h" + +struct HelixCoreErrors +{ + unsigned long code; + QString error_string; +} helixErrors[500] = + +{ {0x80040004, i18n("Invalid Operation")}, + {0x80040005, i18n("Invalid Version")}, + {0x80040006, i18n("Invalid Revision")}, + {0x80040007, i18n("Not Initialized")}, + {0x80040008, i18n("Doc Missing")}, + {0x80040009, i18n("Unexpected")}, + {0x8004000c, i18n("Incomplete")}, + {0x8004000d, i18n("Buffertoosmall")}, + {0x8004000e, i18n("Unsupported Video")}, + {0x8004000f, i18n("Unsupported Audio")}, + {0x80040010, i18n("Invalid Bandwidth")}, + {0x80040011, i18n("No Fileformat")}, + {0x80040011, i18n("No Fileformat")}, + {0x80040011, i18n("Missing Components")}, + {0x00040012, i18n("Element Not Found")}, + {0x00040013, i18n("Noclass")}, + {0x00040014, i18n("Class Noaggregation")}, + {0x80040015, i18n("Not Licensed")}, + {0x80040016, i18n("No Filesystem")}, + {0x80040017, i18n("Request Upgrade")}, + {0x80040018, i18n("Check Rights")}, + {0x80040019, i18n("Restore Server Denied")}, + {0x8004001a, i18n("Debugger Detected")}, + {0x8004005c, i18n("Restore Server Connect")}, + {0x8004005d, i18n("Restore Server Timeout")}, + {0x8004005e, i18n("Revoke Server Connect")}, + {0x8004005f, i18n("Revoke Server Timeout")}, + {0x800401cd, i18n("View Rights Nodrm")}, + {0x800401d3, i18n("Vsrc Nodrm")}, + {0x80040024, i18n("Wm Opl Not Supported")}, + {0x8004001b, i18n("Restoration Complete")}, + {0x8004001c, i18n("Backup Complete")}, + {0x8004001d, i18n("Tlc Not Certified")}, + {0x8004001e, i18n("Corrupted Backup File")}, + {0x8004001f, i18n("Awaiting License")}, + {0x80040020, i18n("Already Initialized")}, + {0x80040021, i18n("Not Supported")}, + {0x00040022, i18n("False")}, + {0x00040023, i18n("Warning")}, + {0x00040040, i18n("Buffering")}, + {0x00040041, i18n("Paused")}, + {0x00040042, i18n("No Data")}, + {0x00040043, i18n("Stream Done")}, + {0x80040043, i18n("Net Socket Invalid")}, + {0x80040044, i18n("Net Connect")}, + {0x80040045, i18n("Bind")}, + {0x80040046, i18n("Socket Create")}, + {0x80040047, i18n("Invalid Host")}, + {0x80040048, i18n("Net Read")}, + {0x80040049, i18n("Net Write")}, + {0x8004004a, i18n("Net Udp")}, + {0x8004004b, i18n("Retry")}, + {0x8004004c, i18n("Server Timeout")}, + {0x8004004d, i18n("Server Disconnected")}, + {0x8004004e, i18n("Would Block")}, + {0x8004004f, i18n("General Nonet")}, + {0x80040050, i18n("Block Canceled")}, + {0x80040051, i18n("Multicast Join")}, + {0x80040052, i18n("General Multicast")}, + {0x80040053, i18n("Multicast Udp")}, + {0x80040054, i18n("At Interrupt")}, + {0x80040055, i18n("Msg Toolarge")}, + {0x80040056, i18n("Net Tcp")}, + {0x80040057, i18n("Try Autoconfig")}, + {0x80040058, i18n("Notenough Bandwidth")}, + {0x80040059, i18n("Http Connect")}, + {0x8004005a, i18n("Port In Use")}, + {0x8004005b, i18n("Loadtest Not Supported")}, + {0x00040060, i18n("Tcp Connect")}, + {0x00040061, i18n("Tcp Reconnect")}, + {0x80040062, i18n("Tcp Failed")}, + {0x80040063, i18n("Authentication Socket Create Failure")}, + {0x80040064, i18n("Authentication Tcp Connect Failure")}, + {0x80040065, i18n("Authentication Tcp Connect Timeout")}, + {0x80040066, i18n("Authentication Failure")}, + {0x80040067, i18n("Authentication Required Parameter Missing")}, + {0x80040068, i18n("Dns Resolve Failure")}, + {0x00040068, i18n("Authentication Succeeded")}, + {0x80040069, i18n("Pull Authentication Failed")}, + {0x8004006a, i18n("Bind Error")}, + {0x8004006b, i18n("Pull Ping Timeout")}, + {0x8004006c, i18n("Authentication Tcp Failed")}, + {0x8004006d, i18n("Unexpected Stream End")}, + {0x8004006e, i18n("Authentication Read Timeout")}, + {0x8004006f, i18n("Authentication Connection Failure")}, + {0x80040070, i18n("Blocked")}, + {0x80040071, i18n("Notenough Predecbuf")}, + {0x80040072, i18n("End With Reason")}, + {0x80040073, i18n("Socket Nobufs")}, + {0x00040080, i18n("At End")}, + {0x80040081, i18n("Invalid File")}, + {0x80040082, i18n("Invalid Path")}, + {0x80040083, i18n("Record")}, + {0x80040084, i18n("Record Write")}, + {0x80040085, i18n("Temporary File")}, + {0x80040086, i18n("Already Open")}, + {0x80040087, i18n("Seek Pending")}, + {0x80040088, i18n("Cancelled")}, + {0x80040089, i18n("File Not Found")}, + {0x8004008a, i18n("Write Error")}, + {0x8004008b, i18n("File Exists")}, + {0x8004008c, i18n("File Not Open")}, + {0x0004008d, i18n("Advise Prefer Linear")}, + {0x8004008e, i18n("Parse Error")}, + {0x0004008f, i18n("Advise Noasync Seek")}, + {0x80040090, i18n("Header Parse Error")}, + {0x80040091, i18n("Corrupt File")}, + {0x800400c0, i18n("Bad Server")}, + {0x800400c1, i18n("Advanced Server")}, + {0x800400c2, i18n("Old Server")}, + {0x000400c3, i18n("Redirection")}, + {0x800400c4, i18n("Server Alert")}, + {0x800400c5, i18n("Proxy")}, + {0x800400c6, i18n("Proxy Response")}, + {0x800400c7, i18n("Advanced Proxy")}, + {0x800400c8, i18n("Old Proxy")}, + {0x800400c9, i18n("Invalid Protocol")}, + {0x800400ca, i18n("Invalid Url Option")}, + {0x800400cb, i18n("Invalid Url Host")}, + {0x800400cc, i18n("Invalid Url Path")}, + {0x800400cd, i18n("Http Content Not Found")}, + {0x800400ce, i18n("Not Authorized")}, + {0x800400cf, i18n("Unexpected Msg")}, + {0x800400d0, i18n("Bad Transport")}, + {0x800400d1, i18n("No Session Id")}, + {0x800400d2, i18n("Proxy Dnr")}, + {0x800400d3, i18n("Proxy Net Connect")}, + {0x800400d4, i18n("Aggregate Operation Not Allowed")}, + {0x800400d5, i18n("Rights Expired")}, + {0x800400d6, i18n("Not Modified")}, + {0x800400d7, i18n("Forbidden")}, + {0x80040100, i18n("Audio Driver Error")}, + {0x80040101, i18n("Late Packet")}, + {0x80040102, i18n("Overlapped Packet")}, + {0x80040103, i18n("Outoforder Packet")}, + {0x80040104, i18n("Noncontiguous Packet")}, + {0x80040140, i18n("Open Not Processed")}, + {0x80040141, i18n("Windraw Exception")}, + {0x80040180, i18n("Expired")}, + {0x80040fc0, i18n("Invalid Interleaver")}, + {0x80040fc1, i18n("Bad Format")}, + {0x80040fc2, i18n("Chunk Missing")}, + {0x80040fc3, i18n("Invalid Stream")}, + {0x80040fc4, i18n("Dnr")}, + {0x80040fc5, i18n("Open Driver")}, + {0x80040fc6, i18n("Upgrade")}, + {0x80040fc7, i18n("Notification")}, + {0x80040fc8, i18n("Not Notified")}, + {0x80040fc9, i18n("Stopped")}, + {0x80040fca, i18n("Closed")}, + {0x80040fcb, i18n("Invalid Wav File")}, + {0x80040fcc, i18n("No Seek")}, + {0x80040200, i18n("Decode Inited")}, + {0x80040201, i18n("Decode Not Found")}, + {0x80040202, i18n("Decode Invalid")}, + {0x80040203, i18n("Decode Type Mismatch")}, + {0x80040204, i18n("Decode Init Failed")}, + {0x80040205, i18n("Decode Not Inited")}, + {0x80040206, i18n("Decode Decompress")}, + {0x80040207, i18n("Obsolete Version")}, + {0x00040208, i18n("Decode At End")}, + {0x80040240, i18n("Encode File Too Small")}, + {0x80040241, i18n("Encode Unknown File")}, + {0x80040242, i18n("Encode Bad Channels")}, + {0x80040243, i18n("Encode Bad Sampsize")}, + {0x80040244, i18n("Encode Bad Samprate")}, + {0x80040245, i18n("Encode Invalid")}, + {0x80040246, i18n("Encode No Output File")}, + {0x80040247, i18n("Encode No Input File")}, + {0x80040248, i18n("Encode No Output Permissions")}, + {0x80040249, i18n("Encode Bad Filetype")}, + {0x8004024a, i18n("Encode Invalid Video")}, + {0x8004024b, i18n("Encode Invalid Audio")}, + {0x8004024c, i18n("Encode No Video Capture")}, + {0x8004024d, i18n("Encode Invalid Video Capture")}, + {0x8004024e, i18n("Encode No Audio Capture")}, + {0x8004024f, i18n("Encode Invalid Audio Capture")}, + {0x80040250, i18n("Encode Too Slow For Live")}, + {0x80040251, i18n("Encode Engine Not Initialized")}, + {0x80040252, i18n("Encode Codec Not Found")}, + {0x80040253, i18n("Encode Codec Not Initialized")}, + {0x80040254, i18n("Encode Invalid Input Dimensions")}, + {0x80040255, i18n("Encode Message Ignored")}, + {0x80040256, i18n("Encode No Settings")}, + {0x80040257, i18n("Encode No Output Types")}, + {0x80040258, i18n("Encode Improper State")}, + {0x80040259, i18n("Encode Invalid Server")}, + {0x8004025a, i18n("Encode Invalid Temp Path")}, + {0x8004025b, i18n("Encode Merge Fail")}, + {0x0004025c, i18n("Binary Data Not Found")}, + {0x0004025d, i18n("Binary End Of Data")}, + {0x8004025e, i18n("Binary Data Purged")}, + {0x8004025f, i18n("Binary Full")}, + {0x80040260, i18n("Binary Offset Past End")}, + {0x80040261, i18n("Encode No Encoded Data")}, + {0x80040262, i18n("Encode Invalid Dll")}, + {0x80040263, i18n("Not Indexable")}, + {0x80040264, i18n("Encode No Browser")}, + {0x80040265, i18n("Encode No File To Server")}, + {0x80040266, i18n("Encode Insufficient Disk Space")}, + {0x00040267, i18n("Encode Sample Discarded")}, + {0x80040268, i18n("Encode Rv10 Frame Too Large")}, + {0x00040269, i18n("Not Handled")}, + {0x0004026a, i18n("End Of Stream")}, + {0x0004026b, i18n("Jobfile Incomplete")}, + {0x0004026c, i18n("Nothing To Serialize")}, + {0x8004026d, i18n("Sizenotset")}, + {0x8004026e, i18n("Already Committed")}, + {0x8004026f, i18n("Buffers Outstanding")}, + {0x80040270, i18n("Not Committed")}, + {0x80040271, i18n("Sample Time Not Set")}, + {0x80040272, i18n("Timeout")}, + {0x80040273, i18n("Wrongstate")}, + {0x800403c1, i18n("Remote Usage Error")}, + {0x800403c2, i18n("Remote Invalid Endtime")}, + {0x800403c3, i18n("Remote Missing Input File")}, + {0x800403c4, i18n("Remote Missing Output File")}, + {0x800403c5, i18n("Remote Input Equals Output File")}, + {0x800403c6, i18n("Remote Unsupported Audio Version")}, + {0x800403c7, i18n("Remote Different Audio")}, + {0x800403c8, i18n("Remote Different Video")}, + {0x800403c9, i18n("Remote Paste Missing Stream")}, + {0x800403ca, i18n("Remote End Of Stream")}, + {0x800403cb, i18n("Remote Image Map Parse Error")}, + {0x800403cc, i18n("Remote Invalid Imagemap File")}, + {0x800403cd, i18n("Remote Event Parse Error")}, + {0x800403ce, i18n("Remote Invalid Event File")}, + {0x800403cf, i18n("Remote Invalid Output File")}, + {0x800403d0, i18n("Remote Invalid Duration")}, + {0x800403d1, i18n("Remote No Dump Files")}, + {0x800403d2, i18n("Remote No Event Dump File")}, + {0x800403d3, i18n("Remote No Imap Dump File")}, + {0x800403d4, i18n("Remote No Data")}, + {0x800403d5, i18n("Remote Empty Stream")}, + {0x800403d6, i18n("Remote Read Only File")}, + {0x800403d7, i18n("Remote Paste Missing Audio Stream")}, + {0x800403d8, i18n("Remote Paste Missing Video Stream")}, + {0x800403d9, i18n("Remote Encrypted Content")}, + {0x80040281, i18n("Property Not Found")}, + {0x80040282, i18n("Property Not Composite")}, + {0x80040283, i18n("Property Duplicate")}, + {0x80040284, i18n("Property Type Mismatch")}, + {0x80040285, i18n("Property Active")}, + {0x80040286, i18n("Property Inactive")}, + {0x80040287, i18n("Property Value Underflow")}, + {0x80040288, i18n("Property Value Overflow")}, + {0x80040289, i18n("Property Value less than Lower bound")}, + {0x8004028a, i18n("Property Value greater than Upper bound")}, + {0x0004028b, i18n("Property Delete Pending")}, + {0x800401c1, i18n("Could not initialize core")}, + {0x800401c2, i18n("Perfectplay Not Supported")}, + {0x800401c3, i18n("No Live Perfectplay")}, + {0x800401c4, i18n("Perfectplay Not Allowed")}, + {0x800401c5, i18n("No Codecs")}, + {0x800401c6, i18n("Slow Machine")}, + {0x800401c7, i18n("Force Perfectplay")}, + {0x800401c8, i18n("Invalid Http Proxy Host")}, + {0x800401c9, i18n("Invalid Metafile")}, + {0x800401ca, i18n("Browser Launch")}, + {0x800401cb, i18n("View Source Noclip")}, + {0x800401cc, i18n("View Source Disabled")}, + {0x800401ce, i18n("Timeline Suspended")}, + {0x800401cf, i18n("Buffer Not Available")}, + {0x800401d0, i18n("Could Not Display")}, + {0x800401d1, i18n("Vsrc Disabled")}, + {0x800401d2, i18n("Vsrc Noclip")}, + {0x80040301, i18n("Resource Not Cached")}, + {0x80040302, i18n("Resource Not Found")}, + {0x80040303, i18n("Resource Close File First")}, + {0x80040304, i18n("Resource Nodata")}, + {0x80040305, i18n("Resource Badfile")}, + {0x80040306, i18n("Resource Partialcopy")}, + {0x800402c0, i18n("PayPerView No User")}, + {0x800402c1, i18n("PayPerView Guid Read Only")}, + {0x800402c2, i18n("PayPerView Guid Collision")}, + {0x800402c3, i18n("Register Guid Exists")}, + {0x800402c4, i18n("PayPerView Authorization Failed")}, + {0x800402c5, i18n("PayPerView Old Player")}, + {0x800402c6, i18n("PayPerView Account Locked")}, + {0x800402c8, i18n("Xr PayPerView Protocol Ignores")}, + {0x800402c9, i18n("PayPerView User Already Exists")}, + {0x80040340, i18n("Upg Auth Failed")}, + {0x80040341, i18n("Upg Cert Auth Failed")}, + {0x80040342, i18n("Upg Cert Expired")}, + {0x80040343, i18n("Upg Cert Revoked")}, + {0x80040344, i18n("Upg Rup Bad")}, + {0x80040345, i18n("Upg System Busy")}, + {0x80041800, i18n("Autocfg Success")}, + {0x80041901, i18n("No Error")}, + {0x80041902, i18n("Invalid Version")}, + {0x80041903, i18n("Invalid Format")}, + {0x80041904, i18n("Invalid Bandwidth")}, + {0x80041905, i18n("Invalid Path")}, + {0x80041906, i18n("Unknown Path")}, + {0x80041907, i18n("Invalid Protocol")}, + {0x80041908, i18n("Invalid Player Addr")}, + {0x80041909, i18n("Local Streams Prohibited")}, + {0x8004190a, i18n("Server Full")}, + {0x8004190b, i18n("Remote Streams Prohibited")}, + {0x8004190c, i18n("Event Streams Prohibited")}, + {0x8004190d, i18n("Invalid Host")}, + {0x8004190e, i18n("No Codec")}, + {0x8004190f, i18n("Livefile Invalid Bwn")}, + {0x80041910, i18n("Unable To Fulfill")}, + {0x80041911, i18n("Multicast Delivery Only")}, + {0x80041912, i18n("License Exceeded")}, + {0x80041913, i18n("License Unavailable")}, + {0x80041914, i18n("Invalid Loss Correction")}, + {0x80041915, i18n("Protocol Failure")}, + {0x80041916, i18n("Realvideo Streams Prohibited")}, + {0x80041917, i18n("Realaudio Streams Prohibited")}, + {0x80041918, i18n("Datatype Unsupported")}, + {0x80041919, i18n("Datatype Unlicensed")}, + {0x8004191a, i18n("Restricted Player")}, + {0x8004191b, i18n("Stream Initializing")}, + {0x8004191c, i18n("Invalid Player")}, + {0x8004191d, i18n("Player Plus Only")}, + {0x8004191e, i18n("No Embedded Players")}, + {0x8004191f, i18n("Pna Prohibited")}, + {0x80041920, i18n("Authentication Unsupported")}, + {0x80041921, i18n("Max Failed Authentications")}, + {0x80041922, i18n("Authentication Access Denied")}, + {0x80041923, i18n("Authentication Uuid Read Only")}, + {0x80041924, i18n("Authentication Uuid Not Unique")}, + {0x80041925, i18n("Authentication No Such User")}, + {0x80041926, i18n("Authentication Registration Succeeded")}, + {0x80041927, i18n("Authentication Registration Failed")}, + {0x80041928, i18n("Authentication Registration Guid Required")}, + {0x80041929, i18n("Authentication Unregistered Player")}, + {0x8004192a, i18n("Authentication Time Expired")}, + {0x8004192b, i18n("Authentication No Time Left")}, + {0x8004192c, i18n("Authentication Account Locked")}, + {0x8004192d, i18n("Authentication Invalid Server Cfg")}, + {0x8004192e, i18n("No Mobile Download")}, + {0x8004192f, i18n("No More Multi Addr")}, + {0x80041930, i18n("Proxy Max Connections")}, + {0x80041931, i18n("Proxy Max Gw Bandwidth")}, + {0x80041932, i18n("Proxy Max Bandwidth")}, + {0x80041933, i18n("Bad Loadtest Password")}, + {0x80041934, i18n("Pna Not Supported")}, + {0x80041935, i18n("Proxy Origin Disconnected")}, + {0x80041936, i18n("Internal Error")}, + {0x80041937, i18n("Max Value")}, + {0x80040600, i18n("Socket Intr")}, + {0x80040601, i18n("Socket Badf")}, + {0x80040602, i18n("Socket Acces")}, + {0x80040603, i18n("Socket Fault")}, + {0x80040604, i18n("Socket Inval")}, + {0x80040605, i18n("Socket Mfile")}, + {0x80040606, i18n("Socket Wouldblock")}, + {0x80040607, i18n("Socket Inprogress")}, + {0x80040608, i18n("Socket Already")}, + {0x80040609, i18n("Socket Notsock")}, + {0x8004060a, i18n("Socket Destaddrreq")}, + {0x8004060b, i18n("Socket Msgsize")}, + {0x8004060c, i18n("Socket Prototype")}, + {0x8004060d, i18n("Socket Noprotoopt")}, + {0x8004060e, i18n("Socket Protonosupport")}, + {0x8004060f, i18n("Socket Socktnosupport")}, + {0x80040610, i18n("Socket Opnotsupp")}, + {0x80040611, i18n("Socket Pfnosupport")}, + {0x80040612, i18n("Socket Afnosupport")}, + {0x80040613, i18n("Socket Addrinuse")}, + {0x80040614, i18n("Socket Address Not Available")}, + {0x80040615, i18n("Socket Net Down")}, + {0x80040616, i18n("Socket Net Unreachable")}, + {0x80040617, i18n("Socket Net Reset")}, + {0x80040618, i18n("Socket Connection Aborted")}, + {0x80040619, i18n("Socket Connection Reset")}, + {0x8004061a, i18n("Socket No buffers")}, + {0x8004061b, i18n("Socket Isconnected")}, + {0x8004061c, i18n("Socket Notconn")}, + {0x8004061d, i18n("Socket Shutdown")}, + {0x8004061e, i18n("Socket Too Many References")}, + {0x8004061f, i18n("Socket Timedout")}, + {0x80040620, i18n("Socket Connection Refused")}, + {0x80040621, i18n("Socket Loop")}, + {0x80040622, i18n("Socket Name too long")}, + {0x80040623, i18n("Socket Hostdown")}, + {0x80040624, i18n("Socket Hostunreach")}, + {0x80040625, i18n("Socket Pipe")}, + {0x80040626, i18n("Socket Endstream")}, + {0x00040627, i18n("Socket Buffered")}, + {0x80040640, i18n("Resolve Noname")}, + {0x80040641, i18n("Resolve Nodata")}, + {0, 0} + }; + + +class HelixErrorsBase +{ +public: + HelixErrorsBase(); + ~HelixErrorsBase(); + + QString *errorText(unsigned long code); + +private: + std::map<unsigned long, QString *> m_errors; + int m_nerrors; +}; + + +HelixErrorsBase *HelixErrors::m_base = new HelixErrorsBase(); + +QString *HelixErrors::errorText(unsigned long code) +{ + return m_base->errorText(code); +} + +HelixErrorsBase::HelixErrorsBase() : m_nerrors(0) +{ + while (helixErrors[m_nerrors].code) m_nerrors++; + + for (int i=0; i<m_nerrors; i++) + m_errors[helixErrors[i].code] = new QString(helixErrors[i].error_string); +} + +HelixErrorsBase::~HelixErrorsBase() +{ + for (int i=0; i<m_nerrors; i++) + delete m_errors[helixErrors[i].code]; +} + +QString *HelixErrorsBase::errorText(unsigned long code) +{ + if (m_errors.count(code)) + return m_errors[code]; + else + return 0; +} diff --git a/amarok/src/engine/helix/helix-errors.h b/amarok/src/engine/helix/helix-errors.h new file mode 100644 index 00000000..f53f6deb --- /dev/null +++ b/amarok/src/engine/helix/helix-errors.h @@ -0,0 +1,27 @@ +/*************************************************************************** + * Copyright (C) 2005 Paul Cifarelli * + * * + * 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. * + * * + ***************************************************************************/ +#ifndef _HELIX_ERRORS_INCLUDED +#define _HELIX_ERRORS_INCLUDED + +class QString; +class HelixErrorsBase; + +class HelixErrors +{ +public: + static QString *errorText(unsigned long code); + +private: + HelixErrors(); + static HelixErrorsBase *m_base; +}; + + +#endif diff --git a/amarok/src/engine/helix/helix-sp/Makefile.am b/amarok/src/engine/helix/helix-sp/Makefile.am new file mode 100644 index 00000000..436410a9 --- /dev/null +++ b/amarok/src/engine/helix/helix-sp/Makefile.am @@ -0,0 +1,38 @@ +noinst_LTLIBRARIES = libhelix-sp.la + +# must disable HX_LOG_SUBSYSTEM before commiting!! +# -DHX_LOG_SUBSYSTEM + +AM_CPPFLAGS = \ + -D_UNIX \ + -Wall -Wreturn-type -DHELIX_CONFIG_DISABLE_ATOMIC_OPERATORS \ + -I$(top_srcdir)/amarok/src/engine \ + -I$(top_srcdir)/amarok/src/ \ + -I$(srcdir)/helix-include/runtime \ + -I$(srcdir)/helix-include/audio/fixptutil \ + -I$(srcdir)/helix-include/common/include \ + -I$(srcdir)/helix-include/client/include \ + -I$(srcdir)/helix-include/common/container \ + -I$(srcdir)/helix-include/common/system \ + -I$(srcdir)/helix-include/common/dbgtool \ + -I$(srcdir)/helix-include/common/util \ + -I$(srcdir)/helix-include/common/log \ + -include $(srcdir)/helixdefines.h \ + $(all_includes) + +libhelix_sp_la_SOURCES = \ + hspcontext.cpp \ + hspadvisesink.cpp \ + hsperror.cpp \ + hspauthmgr.cpp \ + hsphook.cpp \ + iids.cpp \ + utils.cpp \ + gain.cpp \ + hspalsadevice.cpp \ + helix-sp.cpp + +METASOURCES = \ + AUTO + +KDE_OPTIONS=nofinal \ No newline at end of file diff --git a/amarok/src/engine/helix/helix-sp/Makefile.test b/amarok/src/engine/helix/helix-sp/Makefile.test new file mode 100644 index 00000000..6c49f458 --- /dev/null +++ b/amarok/src/engine/helix/helix-sp/Makefile.test @@ -0,0 +1,388 @@ +DEFINES=-D_UNIX -DTEST_APP + +RM=rm -f + +RM_DIR=rm -rf + +MAKE_DEP= + +MAKE_DEP_FLAGS=$(INCLUDES) $(DEFINES) + +RANLIB=ranlib + +CP=cp + +MAKE=make + +CC=gcc + +AR=ar + +CCFLAGS=-pipe -Wall -Wreturn-type -fno-exceptions -march=pentium -mcpu=pentium -O2 $(INCLUDES) $(DEFINES) + +CXX=g++ + +CXXFLAGS=-g -fpic -pipe -Wall -Wreturn-type -fno-exceptions --permissive -fno-rtti -Wno-ctor-dtor-privacy -march=pentium -mcpu=pentium -O2 $(INCLUDES) $(DEFINES) + +COMMON_SRCS=hspcontext.cpp hspadvisesink.cpp hsperror.cpp hspauthmgr.cpp iids.cpp print.cpp utils.cpp +LIB_SRCS=helix-sp.cpp $(COMMON_SRCS) +TESTAPP_SRCS =helix-sp.cpp $(COMMON_SRCS) + +# sources for make depend: +SRC=$(LIB_SRCS) + +COMMON_OBJS=$(COMMON_SRCS:.cpp=.o) +LIB_OBJS=$(LIB_SRCS:.cpp=.o) +TESTAPP_OBJS=splay.o + +COMPILED_OBJS=splay.o helix-sp.o $(COMMON_SRCS:.cpp=.o) + +INCLUDES= \ + -I/usr/X11R6/include \ + -Ihelix-include/runtime \ + -Ihelix-include/common/include \ + -Ihelix-include/client/include \ + -Ihelix-include/common/container \ + -Ihelix-include/common/system \ + -Ihelix-include/common/dbgtool \ + -Ihelix-include/common/util \ + -I. -include helixdefines.h + + +DYNAMIC_LIBS=-lstdc++ -lX11 -ldl -lm -lpthread + +.SUFFIXES: .cpp .so + +.c.o: + $(CC) $(CCFLAGS) -o $@ -c $< + +.cpp.o: + $(CXX) $(CXXFLAGS) -o $@ -c $< + + +PROGRAM=splay +LIBRARY=libhelix-sp.a +#SLIBRARY=libhelix-simpleplayer.so + +all: $(LIBRARY) $(PROGRAM) $(COMPILED_OBJS) + +#$(SLIBRARY): $(LIB_OBJS) $(STATIC_LIBS) +# $(CXX) -shared -o $(SLIBRARY) $(LIB_OBJS) $(STATIC_LIBS) -L/usr/X11R6/lib $(DYNAMIC_LIBS) + +$(LIBRARY): $(LIB_OBJS) $(STATIC_LIBS) + $(AR) r $(LIBRARY) $(LIB_OBJS) + +$(PROGRAM): $(TESTAPP_OBJS) $(STATIC_LIBS) + $(CXX) -o $(PROGRAM) $(TESTAPP_OBJS) $(LIBRARY) -L/usr/X11R6/lib $(DYNAMIC_LIBS) + +helix-sp.o: helix-sp.cpp + $(CXX) $(CXXFLAGS) -o helix-sp.o -c helix-sp.cpp + +splay.o: helix-sp.cpp + $(CXX) $(CXXFLAGS) -DTEST_APP -o splay.o -c helix-sp.cpp + +$(COMMON_OBJ): $(COMMON_SRC) + +clean: + $(RM) $(PROGRAM) $(SLIBRARY) $(LIBRARY) $(COMPILED_OBJS) *~ + +depend: + makedepend $(DEFINES) $(INCLUDES) -o.o -- $(SRC) + +# DO NOT DELETE THIS LINE -- make depend depends on it + +helix-sp.o: helixdefines.h /usr/include/stdlib.h /usr/include/features.h +helix-sp.o: /usr/include/sys/cdefs.h /usr/include/gnu/stubs.h +helix-sp.o: /usr/lib/gcc/i386-redhat-linux/3.4.3/include/stddef.h +helix-sp.o: /usr/include/bits/waitflags.h /usr/include/bits/waitstatus.h +helix-sp.o: /usr/include/endian.h /usr/include/bits/endian.h +helix-sp.o: /usr/include/xlocale.h /usr/include/sys/types.h +helix-sp.o: /usr/include/bits/types.h /usr/include/bits/wordsize.h +helix-sp.o: /usr/include/bits/typesizes.h /usr/include/time.h +helix-sp.o: /usr/include/sys/select.h /usr/include/bits/select.h +helix-sp.o: /usr/include/bits/sigset.h /usr/include/bits/time.h +helix-sp.o: /usr/include/sys/sysmacros.h /usr/include/bits/pthreadtypes.h +helix-sp.o: /usr/include/bits/sched.h /usr/include/alloca.h +helix-sp.o: helix-include/common/include/hxcomm.h +helix-sp.o: helix-include/common/include/hxengin.h +helix-sp.o: helix-include/common/include/hxcom.h +helix-sp.o: helix-include/common/include/hxtypes.h /usr/include/sys/param.h +helix-sp.o: /usr/include/limits.h +helix-sp.o: /usr/lib/gcc/i386-redhat-linux/3.4.3/include/limits.h +helix-sp.o: /usr/include/bits/posix1_lim.h /usr/include/bits/local_lim.h +helix-sp.o: /usr/include/linux/limits.h /usr/include/bits/posix2_lim.h +helix-sp.o: /usr/include/bits/xopen_lim.h /usr/include/bits/stdio_lim.h +helix-sp.o: /usr/include/linux/param.h /usr/include/asm/param.h +helix-sp.o: /usr/include/unistd.h /usr/include/bits/posix_opt.h +helix-sp.o: /usr/include/bits/environments.h /usr/include/bits/confname.h +helix-sp.o: /usr/include/getopt.h helix-include/common/include/hxresult.h +helix-sp.o: helix-include/runtime/hlxclib/memory.h /usr/include/memory.h +helix-sp.o: /usr/include/string.h helix-include/common/include/atomicbase.h +helix-sp.o: helix-include/common/include/ihxpckts.h +helix-sp.o: helix-include/common/include/hxvalue.h +helix-sp.o: helix-include/common/include/hxccf.h +helix-sp.o: helix-include/common/include/hxcore.h /usr/include/sys/time.h +helix-sp.o: helix-include/common/include/hxwin.h +helix-sp.o: helix-include/client/include/hxclsnk.h +helix-sp.o: helix-include/common/include/hxerror.h +helix-sp.o: helix-include/common/include/hxauth.h +helix-sp.o: helix-include/common/include/hxprefs.h +helix-sp.o: helix-include/common/util/hxstrutl.h +helix-sp.o: helix-include/runtime/hlxclib/string.h /usr/include/strings.h +helix-sp.o: helix-include/runtime/hlxclib/stdlib.h +helix-sp.o: helix-include/runtime/safestring.h /usr/include/ctype.h +helix-sp.o: helix-include/common/include/hxvsrc.h hspadvisesink.h hsperror.h +helix-sp.o: hspauthmgr.h hspcontext.h print.h /usr/X11R6/include/X11/Xlib.h +helix-sp.o: /usr/X11R6/include/X11/X.h /usr/X11R6/include/X11/Xfuncproto.h +helix-sp.o: /usr/X11R6/include/X11/Xosdefs.h /usr/include/dlfcn.h +helix-sp.o: /usr/include/bits/dlfcn.h /usr/include/errno.h +helix-sp.o: /usr/include/bits/errno.h /usr/include/linux/errno.h +helix-sp.o: /usr/include/asm/errno.h helix-include/common/include/hxausvc.h +helix-sp.o: helix-include/common/system/dllpath.h +helix-sp.o: helix-include/common/container/hxmap.h +helix-sp.o: helix-include/common/container/chxmapptrtoptr.h +helix-sp.o: helix-include/common/container/carray.h +helix-sp.o: helix-include/common/dbgtool/hxassert.h +helix-sp.o: helix-include/runtime/hlxclib/assert.h /usr/include/assert.h +helix-sp.o: helix-include/runtime/hlxclib/limits.h +helix-sp.o: helix-include/runtime/hlxclib/stdio.h /usr/include/stdio.h +helix-sp.o: /usr/include/libio.h /usr/include/_G_config.h +helix-sp.o: /usr/include/wchar.h /usr/include/bits/wchar.h +helix-sp.o: /usr/include/gconv.h +helix-sp.o: /usr/lib/gcc/i386-redhat-linux/3.4.3/include/stdarg.h +helix-sp.o: /usr/include/bits/sys_errlist.h /usr/include/signal.h +helix-sp.o: /usr/include/bits/signum.h /usr/include/bits/siginfo.h +helix-sp.o: /usr/include/bits/sigaction.h /usr/include/bits/sigcontext.h +helix-sp.o: /usr/include/asm/sigcontext.h /usr/include/bits/sigstack.h +helix-sp.o: /usr/include/sys/ucontext.h /usr/include/bits/sigthread.h +helix-sp.o: helix-include/common/container/hxstring.h +helix-sp.o: helix-include/common/container/hxmaputils.h +helix-sp.o: helix-include/common/container/chxmapbuckets.h +helix-sp.o: helix-include/common/container/chxmapstringtoob.h +helix-sp.o: helix-include/common/container/chxmapstringtostring.h +helix-sp.o: helix-include/common/container/chxmaplongtoobj.h helix-sp.h +helix-sp.o: hspvoladvise.h utils.h +hspcontext.o: helixdefines.h helix-include/common/container/hxbuffer.h +hspcontext.o: helix-include/common/include/ihxpckts.h +hspcontext.o: helix-include/common/include/hxvalue.h +hspcontext.o: helix-include/common/include/hxcom.h +hspcontext.o: helix-include/common/include/hxtypes.h /usr/include/sys/param.h +hspcontext.o: /usr/include/limits.h /usr/include/features.h +hspcontext.o: /usr/include/sys/cdefs.h /usr/include/gnu/stubs.h +hspcontext.o: /usr/lib/gcc/i386-redhat-linux/3.4.3/include/limits.h +hspcontext.o: /usr/include/bits/posix1_lim.h /usr/include/bits/local_lim.h +hspcontext.o: /usr/include/linux/limits.h /usr/include/bits/posix2_lim.h +hspcontext.o: /usr/include/bits/xopen_lim.h /usr/include/bits/stdio_lim.h +hspcontext.o: /usr/include/linux/param.h /usr/include/asm/param.h +hspcontext.o: /usr/include/unistd.h /usr/include/bits/posix_opt.h +hspcontext.o: /usr/include/bits/environments.h /usr/include/bits/types.h +hspcontext.o: /usr/include/bits/wordsize.h +hspcontext.o: /usr/lib/gcc/i386-redhat-linux/3.4.3/include/stddef.h +hspcontext.o: /usr/include/bits/typesizes.h /usr/include/bits/confname.h +hspcontext.o: /usr/include/getopt.h /usr/include/sys/types.h +hspcontext.o: /usr/include/time.h /usr/include/endian.h +hspcontext.o: /usr/include/bits/endian.h /usr/include/sys/select.h +hspcontext.o: /usr/include/bits/select.h /usr/include/bits/sigset.h +hspcontext.o: /usr/include/bits/time.h /usr/include/sys/sysmacros.h +hspcontext.o: /usr/include/bits/pthreadtypes.h /usr/include/bits/sched.h +hspcontext.o: helix-include/common/include/hxresult.h +hspcontext.o: helix-include/runtime/hlxclib/memory.h /usr/include/memory.h +hspcontext.o: /usr/include/string.h /usr/include/xlocale.h +hspcontext.o: helix-include/common/include/atomicbase.h +hspcontext.o: helix-include/common/container/hxstring.h +hspcontext.o: helix-include/common/dbgtool/hxassert.h +hspcontext.o: helix-include/runtime/hlxclib/assert.h /usr/include/assert.h +hspcontext.o: helix-include/runtime/hlxclib/limits.h +hspcontext.o: helix-include/runtime/hlxclib/stdio.h /usr/include/stdio.h +hspcontext.o: /usr/include/libio.h /usr/include/_G_config.h +hspcontext.o: /usr/include/wchar.h /usr/include/bits/wchar.h +hspcontext.o: /usr/include/gconv.h +hspcontext.o: /usr/lib/gcc/i386-redhat-linux/3.4.3/include/stdarg.h +hspcontext.o: /usr/include/bits/sys_errlist.h /usr/include/signal.h +hspcontext.o: /usr/include/bits/signum.h /usr/include/bits/siginfo.h +hspcontext.o: /usr/include/bits/sigaction.h /usr/include/bits/sigcontext.h +hspcontext.o: /usr/include/asm/sigcontext.h /usr/include/bits/sigstack.h +hspcontext.o: /usr/include/sys/ucontext.h /usr/include/bits/sigthread.h +hspcontext.o: /usr/include/stdlib.h /usr/include/bits/waitflags.h +hspcontext.o: /usr/include/bits/waitstatus.h /usr/include/alloca.h +hspcontext.o: helix-include/runtime/hlxclib/string.h /usr/include/strings.h +hspcontext.o: helix-include/common/util/hxmangle.h +hspcontext.o: helix-include/client/include/hxclsnk.h +hspcontext.o: helix-include/common/include/hxerror.h +hspcontext.o: helix-include/common/include/hxprefs.h hspadvisesink.h +hspcontext.o: hsperror.h hspauthmgr.h helix-include/common/include/hxauth.h +hspcontext.o: hspcontext.h helix-include/common/include/hxausvc.h helix-sp.h +hspcontext.o: utils.h +hspadvisesink.o: helixdefines.h /usr/include/stdio.h /usr/include/features.h +hspadvisesink.o: /usr/include/sys/cdefs.h /usr/include/gnu/stubs.h +hspadvisesink.o: /usr/lib/gcc/i386-redhat-linux/3.4.3/include/stddef.h +hspadvisesink.o: /usr/include/bits/types.h /usr/include/bits/wordsize.h +hspadvisesink.o: /usr/include/bits/typesizes.h /usr/include/libio.h +hspadvisesink.o: /usr/include/_G_config.h /usr/include/wchar.h +hspadvisesink.o: /usr/include/bits/wchar.h /usr/include/gconv.h +hspadvisesink.o: /usr/lib/gcc/i386-redhat-linux/3.4.3/include/stdarg.h +hspadvisesink.o: /usr/include/bits/stdio_lim.h +hspadvisesink.o: /usr/include/bits/sys_errlist.h +hspadvisesink.o: helix-include/common/include/hxcomm.h +hspadvisesink.o: helix-include/common/include/hxengin.h +hspadvisesink.o: helix-include/common/include/hxcom.h +hspadvisesink.o: helix-include/common/include/hxtypes.h +hspadvisesink.o: /usr/include/sys/param.h /usr/include/limits.h +hspadvisesink.o: /usr/lib/gcc/i386-redhat-linux/3.4.3/include/limits.h +hspadvisesink.o: /usr/include/bits/posix1_lim.h /usr/include/bits/local_lim.h +hspadvisesink.o: /usr/include/linux/limits.h /usr/include/bits/posix2_lim.h +hspadvisesink.o: /usr/include/bits/xopen_lim.h /usr/include/linux/param.h +hspadvisesink.o: /usr/include/asm/param.h /usr/include/unistd.h +hspadvisesink.o: /usr/include/bits/posix_opt.h +hspadvisesink.o: /usr/include/bits/environments.h +hspadvisesink.o: /usr/include/bits/confname.h /usr/include/getopt.h +hspadvisesink.o: /usr/include/sys/types.h /usr/include/time.h +hspadvisesink.o: /usr/include/endian.h /usr/include/bits/endian.h +hspadvisesink.o: /usr/include/sys/select.h /usr/include/bits/select.h +hspadvisesink.o: /usr/include/bits/sigset.h /usr/include/bits/time.h +hspadvisesink.o: /usr/include/sys/sysmacros.h +hspadvisesink.o: /usr/include/bits/pthreadtypes.h /usr/include/bits/sched.h +hspadvisesink.o: helix-include/common/include/hxresult.h +hspadvisesink.o: helix-include/runtime/hlxclib/memory.h /usr/include/memory.h +hspadvisesink.o: /usr/include/string.h /usr/include/xlocale.h +hspadvisesink.o: helix-include/common/include/atomicbase.h +hspadvisesink.o: helix-include/common/include/ihxpckts.h +hspadvisesink.o: helix-include/common/include/hxvalue.h +hspadvisesink.o: helix-include/common/include/hxccf.h +hspadvisesink.o: helix-include/common/include/hxmon.h +hspadvisesink.o: helix-include/runtime/hlxclib/limits.h +hspadvisesink.o: helix-include/common/include/hxcore.h +hspadvisesink.o: /usr/include/sys/time.h helix-include/common/include/hxwin.h +hspadvisesink.o: helix-include/client/include/hxclsnk.h hspadvisesink.h +hspadvisesink.o: print.h helix-include/common/include/hxausvc.h helix-sp.h +hspadvisesink.o: utils.h +hsperror.o: helixdefines.h helix-include/common/include/hxcomm.h +hsperror.o: helix-include/common/include/hxengin.h +hsperror.o: helix-include/common/include/hxcom.h +hsperror.o: helix-include/common/include/hxtypes.h /usr/include/sys/param.h +hsperror.o: /usr/include/limits.h /usr/include/features.h +hsperror.o: /usr/include/sys/cdefs.h /usr/include/gnu/stubs.h +hsperror.o: /usr/lib/gcc/i386-redhat-linux/3.4.3/include/limits.h +hsperror.o: /usr/include/bits/posix1_lim.h /usr/include/bits/local_lim.h +hsperror.o: /usr/include/linux/limits.h /usr/include/bits/posix2_lim.h +hsperror.o: /usr/include/bits/xopen_lim.h /usr/include/bits/stdio_lim.h +hsperror.o: /usr/include/linux/param.h /usr/include/asm/param.h +hsperror.o: /usr/include/unistd.h /usr/include/bits/posix_opt.h +hsperror.o: /usr/include/bits/environments.h /usr/include/bits/types.h +hsperror.o: /usr/include/bits/wordsize.h +hsperror.o: /usr/lib/gcc/i386-redhat-linux/3.4.3/include/stddef.h +hsperror.o: /usr/include/bits/typesizes.h /usr/include/bits/confname.h +hsperror.o: /usr/include/getopt.h /usr/include/sys/types.h +hsperror.o: /usr/include/time.h /usr/include/endian.h +hsperror.o: /usr/include/bits/endian.h /usr/include/sys/select.h +hsperror.o: /usr/include/bits/select.h /usr/include/bits/sigset.h +hsperror.o: /usr/include/bits/time.h /usr/include/sys/sysmacros.h +hsperror.o: /usr/include/bits/pthreadtypes.h /usr/include/bits/sched.h +hsperror.o: helix-include/common/include/hxresult.h +hsperror.o: helix-include/runtime/hlxclib/memory.h /usr/include/memory.h +hsperror.o: /usr/include/string.h /usr/include/xlocale.h +hsperror.o: helix-include/common/include/atomicbase.h +hsperror.o: helix-include/common/include/ihxpckts.h +hsperror.o: helix-include/common/include/hxvalue.h +hsperror.o: helix-include/common/include/hxccf.h +hsperror.o: helix-include/common/include/hxerror.h +hsperror.o: helix-include/common/include/hxcore.h /usr/include/sys/time.h +hsperror.o: helix-include/common/include/hxwin.h +hsperror.o: helix-include/common/container/hxbuffer.h +hsperror.o: helix-include/common/container/hxstring.h +hsperror.o: helix-include/common/dbgtool/hxassert.h +hsperror.o: helix-include/runtime/hlxclib/assert.h /usr/include/assert.h +hsperror.o: helix-include/runtime/hlxclib/limits.h +hsperror.o: helix-include/runtime/hlxclib/stdio.h /usr/include/stdio.h +hsperror.o: /usr/include/libio.h /usr/include/_G_config.h +hsperror.o: /usr/include/wchar.h /usr/include/bits/wchar.h +hsperror.o: /usr/include/gconv.h +hsperror.o: /usr/lib/gcc/i386-redhat-linux/3.4.3/include/stdarg.h +hsperror.o: /usr/include/bits/sys_errlist.h /usr/include/signal.h +hsperror.o: /usr/include/bits/signum.h /usr/include/bits/siginfo.h +hsperror.o: /usr/include/bits/sigaction.h /usr/include/bits/sigcontext.h +hsperror.o: /usr/include/asm/sigcontext.h /usr/include/bits/sigstack.h +hsperror.o: /usr/include/sys/ucontext.h /usr/include/bits/sigthread.h +hsperror.o: /usr/include/stdlib.h /usr/include/bits/waitflags.h +hsperror.o: /usr/include/bits/waitstatus.h /usr/include/alloca.h +hsperror.o: helix-include/runtime/hlxclib/string.h /usr/include/strings.h +hsperror.o: hsperror.h print.h helix-include/common/include/hxausvc.h +hsperror.o: helix-sp.h utils.h +hspauthmgr.o: helixdefines.h /usr/include/stdio.h /usr/include/features.h +hspauthmgr.o: /usr/include/sys/cdefs.h /usr/include/gnu/stubs.h +hspauthmgr.o: /usr/lib/gcc/i386-redhat-linux/3.4.3/include/stddef.h +hspauthmgr.o: /usr/include/bits/types.h /usr/include/bits/wordsize.h +hspauthmgr.o: /usr/include/bits/typesizes.h /usr/include/libio.h +hspauthmgr.o: /usr/include/_G_config.h /usr/include/wchar.h +hspauthmgr.o: /usr/include/bits/wchar.h /usr/include/gconv.h +hspauthmgr.o: /usr/lib/gcc/i386-redhat-linux/3.4.3/include/stdarg.h +hspauthmgr.o: /usr/include/bits/stdio_lim.h /usr/include/bits/sys_errlist.h +hspauthmgr.o: helix-include/common/include/hxcom.h +hspauthmgr.o: helix-include/common/include/hxtypes.h /usr/include/sys/param.h +hspauthmgr.o: /usr/include/limits.h +hspauthmgr.o: /usr/lib/gcc/i386-redhat-linux/3.4.3/include/limits.h +hspauthmgr.o: /usr/include/bits/posix1_lim.h /usr/include/bits/local_lim.h +hspauthmgr.o: /usr/include/linux/limits.h /usr/include/bits/posix2_lim.h +hspauthmgr.o: /usr/include/bits/xopen_lim.h /usr/include/linux/param.h +hspauthmgr.o: /usr/include/asm/param.h /usr/include/unistd.h +hspauthmgr.o: /usr/include/bits/posix_opt.h /usr/include/bits/environments.h +hspauthmgr.o: /usr/include/bits/confname.h /usr/include/getopt.h +hspauthmgr.o: /usr/include/sys/types.h /usr/include/time.h +hspauthmgr.o: /usr/include/endian.h /usr/include/bits/endian.h +hspauthmgr.o: /usr/include/sys/select.h /usr/include/bits/select.h +hspauthmgr.o: /usr/include/bits/sigset.h /usr/include/bits/time.h +hspauthmgr.o: /usr/include/sys/sysmacros.h /usr/include/bits/pthreadtypes.h +hspauthmgr.o: /usr/include/bits/sched.h +hspauthmgr.o: helix-include/common/include/hxresult.h +hspauthmgr.o: helix-include/runtime/hlxclib/memory.h /usr/include/memory.h +hspauthmgr.o: /usr/include/string.h /usr/include/xlocale.h +hspauthmgr.o: helix-include/common/include/atomicbase.h +hspauthmgr.o: helix-include/common/include/hxauth.h hspauthmgr.h +hspauthmgr.o: /usr/include/ctype.h print.h +hspauthmgr.o: helix-include/common/include/hxausvc.h helix-sp.h utils.h +iids.o: helixdefines.h helix-include/common/include/hxtypes.h +iids.o: /usr/include/sys/param.h /usr/include/limits.h +iids.o: /usr/include/features.h /usr/include/sys/cdefs.h +iids.o: /usr/include/gnu/stubs.h +iids.o: /usr/lib/gcc/i386-redhat-linux/3.4.3/include/limits.h +iids.o: /usr/include/bits/posix1_lim.h /usr/include/bits/local_lim.h +iids.o: /usr/include/linux/limits.h /usr/include/bits/posix2_lim.h +iids.o: /usr/include/bits/xopen_lim.h /usr/include/bits/stdio_lim.h +iids.o: /usr/include/linux/param.h /usr/include/asm/param.h +iids.o: /usr/include/unistd.h /usr/include/bits/posix_opt.h +iids.o: /usr/include/bits/environments.h /usr/include/bits/types.h +iids.o: /usr/include/bits/wordsize.h +iids.o: /usr/lib/gcc/i386-redhat-linux/3.4.3/include/stddef.h +iids.o: /usr/include/bits/typesizes.h /usr/include/bits/confname.h +iids.o: /usr/include/getopt.h /usr/include/sys/types.h /usr/include/time.h +iids.o: /usr/include/endian.h /usr/include/bits/endian.h +iids.o: /usr/include/sys/select.h /usr/include/bits/select.h +iids.o: /usr/include/bits/sigset.h /usr/include/bits/time.h +iids.o: /usr/include/sys/sysmacros.h /usr/include/bits/pthreadtypes.h +iids.o: /usr/include/bits/sched.h helix-include/common/include/hxcom.h +iids.o: helix-include/common/include/hxresult.h +iids.o: helix-include/runtime/hlxclib/memory.h /usr/include/memory.h +iids.o: /usr/include/string.h /usr/include/xlocale.h +iids.o: helix-include/common/include/atomicbase.h +iids.o: helix-include/common/include/hxiids.h +iids.o: helix-include/common/include/hxpiids.h +print.o: helixdefines.h /usr/lib/gcc/i386-redhat-linux/3.4.3/include/stdarg.h +print.o: /usr/include/stdio.h /usr/include/features.h +print.o: /usr/include/sys/cdefs.h /usr/include/gnu/stubs.h +print.o: /usr/lib/gcc/i386-redhat-linux/3.4.3/include/stddef.h +print.o: /usr/include/bits/types.h /usr/include/bits/wordsize.h +print.o: /usr/include/bits/typesizes.h /usr/include/libio.h +print.o: /usr/include/_G_config.h /usr/include/wchar.h +print.o: /usr/include/bits/wchar.h /usr/include/gconv.h +print.o: /usr/include/bits/stdio_lim.h /usr/include/bits/sys_errlist.h +print.o: print.h +utils.o: helixdefines.h /usr/include/stdio.h /usr/include/features.h +utils.o: /usr/include/sys/cdefs.h /usr/include/gnu/stubs.h +utils.o: /usr/lib/gcc/i386-redhat-linux/3.4.3/include/stddef.h +utils.o: /usr/include/bits/types.h /usr/include/bits/wordsize.h +utils.o: /usr/include/bits/typesizes.h /usr/include/libio.h +utils.o: /usr/include/_G_config.h /usr/include/wchar.h +utils.o: /usr/include/bits/wchar.h /usr/include/gconv.h +utils.o: /usr/lib/gcc/i386-redhat-linux/3.4.3/include/stdarg.h +utils.o: /usr/include/bits/stdio_lim.h /usr/include/bits/sys_errlist.h +utils.o: /usr/include/string.h /usr/include/xlocale.h utils.h diff --git a/amarok/src/engine/helix/helix-sp/gain.cpp b/amarok/src/engine/helix/helix-sp/gain.cpp new file mode 100644 index 00000000..c219955b --- /dev/null +++ b/amarok/src/engine/helix/helix-sp/gain.cpp @@ -0,0 +1,517 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * + * This software is released under the provisions of the GPL version 2. + * see file "COPYING". If that file is not available, the full statement + * of the license can be found at + * + * http://www.fsf.org/licensing/licenses/gpl.txt + * + * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved. + * Copyright (c) 2005 Paul Cifarelli All Rights Reserved. + * + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the + * portions it created. + * + * This file, and the files included with this file, is distributed + * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY + * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS + * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET + * ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * ***** END LICENSE BLOCK ***** */ + +#include <stdlib.h> +#include <math.h> +#include <string.h> +#include <iostream> +#include "hxassert.h" + +#include "gain.h" + +using namespace std; + +#define INT8_CEILING 255 +#define INT16_CEILING 32767 +#define INT32_CEILING 65535 + +struct GAIN_STATE +{ + int sampleRate; + int nChannels; + int bytesPerSample; + bool isMute; + float instGain; /* gain applied right now */ + float tgtGain; /* in a smooth gain change, the gain we are aiming for */ + float decay; +}; + +GAIN_STATE* gainInit(int sampleRate, int nChannels, int bytesPerSample) +{ + GAIN_STATE* g = (GAIN_STATE*) calloc(1,sizeof(GAIN_STATE)) ; + if (g) + { + g->sampleRate = sampleRate; + g->nChannels = nChannels; + g->bytesPerSample = bytesPerSample; + gainSetTimeConstant(0.1f, g); + } + + return g ; +} + +void gainFree(GAIN_STATE* g) +{ + if (g) free(g) ; +} + +float gainSetSmoothdB(float dB, GAIN_STATE* g) +{ + float gain = pow(10.0, 0.05*dB) ; + + if (g) + { + g->isMute = false; + g->tgtGain = gain ; + } + + return dB ; +} + +float gainSetImmediatedB(float dB, GAIN_STATE* g) +{ + dB = gainSetSmoothdB(dB, g) ; + + if (g) + g->instGain = g->tgtGain ; // make it instantaneous + + return dB ; +} + +float gainSetSmooth(float percent, GAIN_STATE* g) +{ + float gaintop = pow(10.0, 0.05*GAIN_MAX_dB) ; + float gainbottom = pow(10.0, 0.05*GAIN_MIN_dB) ; + float gain = percent * (gaintop - gainbottom) + gainbottom; + + if (g) + { + g->isMute = false; + g->tgtGain = gain ; + } + + return gain; +} + +float gainSetImmediate(float percent, GAIN_STATE* g) +{ + float gain = gainSetSmooth(percent, g) ; + + if (g) + g->instGain = g->tgtGain ; // make it instantaneous + + return gain; +} + +void gainSetMute(GAIN_STATE* g) +{ + if (g) + { + g->isMute = true; + g->instGain = g->tgtGain = 0.0; // mute is immediate + } +} + +int gainSetTimeConstant(float millis, GAIN_STATE* g) +{ + if (!g) + return 0; + + // we define the time constant millis so that the signal has decayed to 1/2 (-6dB) after + // millis milliseconds have elapsed. + // Let T[sec] = millis/1000 = time constant in units of seconds + // + // => (1-2^-s)^(T[sec]*sr) = 1/2 + // => 1-2^-s = (1/2)^(1/(T[sec]*sr)) + // => 2^-s = 1 - (1/2)^(1/(T[sec]*sr)) + // => s = -log2(1 - (1/2)^(1 / (T[sec]*sr))) + + // first 0.5 is rounding constant + int shift; + shift = (int)(0.5 - 1.0/log(2.0)*log(1.0 - pow(0.5, 1000.0/(millis * g->sampleRate)))) ; + if (shift < 1) + shift = 1 ; + if (shift > 31) + shift = 31 ; + + g->decay = ::pow(2.0, (float) shift); + + return 1 ; // OK +} + +static void gainFeedMono(unsigned char* signal, unsigned char *outsignal, int len, GAIN_STATE *g) +{ + if (!g) + return; + + float tgtGain = g->tgtGain ; + float gain = g->instGain ; + unsigned char *bufferEnd = signal + len; + + if (gain == tgtGain) + { // steady state + while (signal < bufferEnd) + { + switch (g->bytesPerSample) + { + case 1: + { + short int res; + char *s = (char *) signal; + char *o = (char *) outsignal; + res = (short int) (*s * gain); + *o = (char) (res > INT8_CEILING ? INT8_CEILING : res); + } + break; + case 2: + { + long res; + short int *s = (short int *) signal; + short int *o = (short int *) outsignal; + res = (long) (*s * gain); + *o = (short int) (res > INT16_CEILING ? INT16_CEILING : res); + } + break; + case 4: + { + long long res; + long *s = (long *) signal; + long *o = (long *) outsignal; + res = (long long) (*s * gain); + *o = (long) (res > INT32_CEILING ? INT32_CEILING : res); + } + break; + default: + return; + } + signal += g->bytesPerSample; + outsignal += g->bytesPerSample; + } + } + else + { // while we are still ramping the gain + while (signal < bufferEnd) + { + switch (g->bytesPerSample) + { + case 1: + { + short int res; + char *s = (char *) signal; + char *o = (char *) outsignal; + res = (short int) (*s * gain); + *o = (char) (res > INT8_CEILING ? INT8_CEILING : res); + } + break; + case 2: + { + long res; + short int *s = (short int *) signal; + short int *o = (short int *) outsignal; + res = (long) (*s * gain); + *o = (short int) (res > INT16_CEILING ? INT16_CEILING : res); + } + break; + case 4: + { + long long res; + long *s = (long *) signal; + long *o = (long *) outsignal; + res = (long long) (*s * gain); + *o = (long) (res > INT32_CEILING ? INT32_CEILING : res); + } + break; + default: + return; + } + signal += g->bytesPerSample; + outsignal += g->bytesPerSample; + gain += ((tgtGain-gain) / g->decay); + } + g->instGain = gain ; + } +} + +static void gainFeedStereo(unsigned char* signal, unsigned char *outsignal, int len, GAIN_STATE *g) +{ + if (!g) + return; + + float tgtGain = g->tgtGain ; + float gain = g->instGain ; + unsigned char *bufferEnd = signal + len; + + if (gain == tgtGain) + { // steady state + while (signal < bufferEnd) + { + switch (g->bytesPerSample) + { + case 1: + { + short int res; + char *s = (char *) signal; + char *o = (char *) outsignal; + res = (short int) (*s * gain); + *o = (char) (res > INT8_CEILING ? INT8_CEILING : res); + s++; + o++; + res = (short int) (*s * gain); + *o = (char) (res > INT8_CEILING ? INT8_CEILING : res); + } + break; + case 2: + { + long res; + short int *s = (short int *) signal; + short int *o = (short int *) outsignal; + res = (long) (*s * gain); + *o = (short int) (res > INT16_CEILING ? INT16_CEILING : res); + s++; + o++; + res = (long) (*s * gain); + *o = (short int) (res > INT16_CEILING ? INT16_CEILING : res); + } + break; + case 4: + { + long long res; + long *s = (long *) signal; + long *o = (long *) outsignal; + res = (long long) (*s * gain); + *o = (long) (res > INT32_CEILING ? INT32_CEILING : res); + s++; + o++; + res = (long long) (*s * gain); + *o = (long) (res > INT32_CEILING ? INT32_CEILING : res); + } + break; + default: + return; + } + signal += 2 * g->bytesPerSample; + outsignal += 2 * g->bytesPerSample; + } + } + else + { // while we are still ramping the gain + while (signal < bufferEnd) + { + switch (g->bytesPerSample) + { + case 1: + { + short int res; + char *s = (char *) signal; + char *o = (char *) outsignal; + res = (short int) (*s * gain); + *o = (char) (res > INT8_CEILING ? INT8_CEILING : res); + s++; + o++; + res = (short int) (*s * gain); + *o = (char) (res > INT8_CEILING ? INT8_CEILING : res); + } + break; + case 2: + { + long res; + short int *s = (short int *) signal; + short int *o = (short int *) outsignal; + res = (long) (*s * gain); + *o = (short int) (res > INT16_CEILING ? INT16_CEILING : res); + s++; + o++; + res = (long) (*s * gain); + *o = (short int) (res > INT16_CEILING ? INT16_CEILING : res); + } + break; + case 4: + { + long long res; + long *s = (long *) signal; + long *o = (long *) outsignal; + res = (long long) (*s * gain); + *o = (long) (res > INT32_CEILING ? INT32_CEILING : res); + s++; + o++; + res = (long long) (*s * gain); + *o = (long) (res > INT32_CEILING ? INT32_CEILING : res); + } + break; + default: + return; + } + signal += 2 * g->bytesPerSample; + outsignal += 2 * g->bytesPerSample; + gain += ((tgtGain-gain) / g->decay); + } + g->instGain = gain ; + } +} + +static void gainFeedMulti(unsigned char* signal, unsigned char *outsignal, int len, GAIN_STATE *g) +{ + if (!g) + return; + + float tgtGain = g->tgtGain ; + float gain = g->instGain ; + unsigned char *bufferEnd = signal + len; + + if (gain == tgtGain) + { // steady state + while (signal < bufferEnd) + { + switch (g->bytesPerSample) + { + case 1: + { + short int res; + int i ; + char *s = (char *) signal; + char *o = (char *) outsignal; + for (i = 0 ; i < g->nChannels ; i++) + { + res = (short int) (*s * gain); + *o = (char) (res > INT8_CEILING ? INT8_CEILING : res); + s++; + o++; + } + } + break; + case 2: + { + long res; + int i ; + short int *s = (short int *) signal; + short int *o = (short int *) outsignal; + for (i = 0 ; i < g->nChannels ; i++) + { + res = (long) (*s * gain); + *o = (short int) (res > INT16_CEILING ? INT16_CEILING : res); + s++; + o++; + } + } + break; + case 4: + { + long long res; + int i ; + long *s = (long *) signal; + long *o = (long *) outsignal; + for (i = 0 ; i < g->nChannels ; i++) + { + res = (long long) (*s * gain); + *o = (long) (res > INT32_CEILING ? INT32_CEILING : res); + s++; + o++; + } + } + break; + default: + return; + } + signal += g->nChannels * g->bytesPerSample; + outsignal += g->nChannels * g->bytesPerSample; + } + } + else + { // while we are still ramping the gain + while (signal < bufferEnd) + { + int i ; + + switch (g->bytesPerSample) + { + case 1: + { + short int res; + char *s = (char *) signal; + char *o = (char *) outsignal; + for (i = 0 ; i < g->nChannels ; i++) + { + res = (short int) (*s * gain); + *o = (char) (res > INT8_CEILING ? INT8_CEILING : res); + s++; + o++; + } + } + break; + case 2: + { + long res; + short int *s = (short int *) signal; + short int *o = (short int *) outsignal; + for (i = 0 ; i < g->nChannels ; i++) + { + res = (long) (*s * gain); + *o = (short int) (res > INT16_CEILING ? INT16_CEILING : res); + s++; + o++; + } + } + case 4: + { + long long res; + long *s = (long *) signal; + long *o = (long *) outsignal; + for (i = 0 ; i < g->nChannels ; i++) + { + res = (long long) (*s * gain); + *o = (long) (res > INT32_CEILING ? INT32_CEILING : res); + s++; + o++; + } + } + break; + } + signal += g->nChannels * g->bytesPerSample; + outsignal += g->nChannels * g->bytesPerSample; + gain += ((tgtGain-gain) / g->decay); + } + g->instGain = gain ; + } +} + +void gainFeed(unsigned char* signal, unsigned char *outsignal, int len, GAIN_STATE* g) +{ + if (!g) + return; + + /* if the gain is 0dB, and we are not currently ramping, shortcut. */ + if (g->instGain == 1.0 && g->instGain == g->tgtGain) + { + if (signal != outsignal) + memcpy(outsignal, signal, len); + + return ; + } + switch (g->nChannels) + { + case 1: + gainFeedMono(signal, outsignal, len, g) ; + break ; + case 2: + gainFeedStereo(signal, outsignal, len, g) ; + break ; + default: + gainFeedMulti(signal, outsignal, len, g) ; + break ; + } +} diff --git a/amarok/src/engine/helix/helix-sp/gain.h b/amarok/src/engine/helix/helix-sp/gain.h new file mode 100644 index 00000000..d120a134 --- /dev/null +++ b/amarok/src/engine/helix/helix-sp/gain.h @@ -0,0 +1,58 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * + * This software is released under the provisions of the GPL version 2. + * see file "COPYING". If that file is not available, the full statement + * of the license can be found at + * + * http://www.fsf.org/licensing/licenses/gpl.txt + * + * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved. + * Copyright (c) 2005 Paul Cifarelli All Rights Reserved. + * + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the + * portions it created. + * + * This file, and the files included with this file, is distributed + * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY + * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS + * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET + * ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef _GAIN_H_ +#define _GAIN_H_ + +#include "hxtypes.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define GAIN_MAX_dB 0.0 +#define GAIN_MIN_dB -27.0 + +struct GAIN_STATE; +typedef struct GAIN_STATE GAIN_STATE; + +GAIN_STATE* gainInit(int sampleRate, int nChannels, int bytePerSample) ; +void gainFree(GAIN_STATE*) ; +float gainSetImmediatedB(float dB, GAIN_STATE*) ; +float gainSetSmoothdB(float dB, GAIN_STATE*); +float gainSetImmediate(float dB, GAIN_STATE*) ; +float gainSetSmooth(float dB, GAIN_STATE*); +void gainSetMute(GAIN_STATE* g); +int gainSetTimeConstant(float millis, GAIN_STATE*) ; +void gainFeed(unsigned char* signal, unsigned char *outsignal, int len, GAIN_STATE* g) ; + +#ifdef __cplusplus +} +#endif + +#endif /* _GAIN_H_ */ diff --git a/amarok/src/engine/helix/helix-sp/helix-include/client/include/hxclsnk.h b/amarok/src/engine/helix/helix-sp/helix-include/client/include/hxclsnk.h new file mode 100644 index 00000000..e79f7e90 --- /dev/null +++ b/amarok/src/engine/helix/helix-sp/helix-include/client/include/hxclsnk.h @@ -0,0 +1,230 @@ + +/* + * + * This software is released under the provisions of the GPL version 2. + * see file "COPYING". If that file is not available, the full statement + * of the license can be found at + * + * http://www.fsf.org/licensing/licenses/gpl.txt + * + * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved. + * + */ + +#ifndef _HXCLSNK_H_ +#define _HXCLSNK_H_ + +/* + * Forward declarations of some interfaces defined or used here-in. + */ +typedef _INTERFACE IHXClientAdviseSink IHXClientAdviseSink; +typedef _INTERFACE IHXRequest IHXRequest; + + +/**************************************************************************** + * + * Interface: + * + * IHXClientAdviseSink + * + * Purpose: + * + * Interface supplied by client to core to receive notifications of + * status changes. + * + * IID_IHXClientAdviseSink: + * + * {00000B00-0901-11d1-8B06-00A024406D59} + * + */ + +DEFINE_GUID(IID_IHXClientAdviseSink, 0x00000B00, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXClientAdviseSink + +DECLARE_INTERFACE_(IHXClientAdviseSink, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXClientAdviseSink methods + */ + + /************************************************************************ + * Method: + * IHXClientAdviseSink::OnPosLength + * Purpose: + * Called to advise the client that the position or length of the + * current playback context has changed. + */ + STDMETHOD(OnPosLength) (THIS_ + UINT32 ulPosition, + UINT32 ulLength) PURE; + + /************************************************************************ + * Method: + * IHXClientAdviseSink::OnPresentationOpened + * Purpose: + * Called to advise the client a presentation has been opened. + */ + STDMETHOD(OnPresentationOpened) (THIS) PURE; + + /************************************************************************ + * Method: + * IHXClientAdviseSink::OnPresentationClosed + * Purpose: + * Called to advise the client a presentation has been closed. + */ + STDMETHOD(OnPresentationClosed) (THIS) PURE; + + /************************************************************************ + * Method: + * IHXClientAdviseSink::OnStatisticsChanged + * Purpose: + * Called to advise the client that the presentation statistics + * have changed. + */ + STDMETHOD(OnStatisticsChanged) (THIS) PURE; + + /************************************************************************ + * Method: + * IHXClientAdviseSink::OnPreSeek + * Purpose: + * Called by client engine to inform the client that a seek is + * about to occur. The render is informed the last time for the + * stream's time line before the seek, as well as the first new + * time for the stream's time line after the seek will be completed. + * + */ + STDMETHOD(OnPreSeek) (THIS_ + ULONG32 ulOldTime, + ULONG32 ulNewTime) PURE; + + /************************************************************************ + * Method: + * IHXClientAdviseSink::OnPostSeek + * Purpose: + * Called by client engine to inform the client that a seek has + * just occurred. The render is informed the last time for the + * stream's time line before the seek, as well as the first new + * time for the stream's time line after the seek. + * + */ + STDMETHOD(OnPostSeek) (THIS_ + ULONG32 ulOldTime, + ULONG32 ulNewTime) PURE; + + /************************************************************************ + * Method: + * IHXClientAdviseSink::OnStop + * Purpose: + * Called by client engine to inform the client that a stop has + * just occurred. + * + */ + STDMETHOD(OnStop) (THIS) PURE; + + /************************************************************************ + * Method: + * IHXClientAdviseSink::OnPause + * Purpose: + * Called by client engine to inform the client that a pause has + * just occurred. The render is informed the last time for the + * stream's time line before the pause. + * + */ + STDMETHOD(OnPause) (THIS_ + ULONG32 ulTime) PURE; + + /************************************************************************ + * Method: + * IHXClientAdviseSink::OnBegin + * Purpose: + * Called by client engine to inform the client that a begin or + * resume has just occurred. The render is informed the first time + * for the stream's time line after the resume. + * + */ + STDMETHOD(OnBegin) (THIS_ + ULONG32 ulTime) PURE; + + /************************************************************************ + * Method: + * IHXClientAdviseSink::OnBuffering + * Purpose: + * Called by client engine to inform the client that buffering + * of data is occurring. The render is informed of the reason for + * the buffering (start-up of stream, seek has occurred, network + * congestion, etc.), as well as percentage complete of the + * buffering process. + * + */ + STDMETHOD(OnBuffering) (THIS_ + ULONG32 ulFlags, + UINT16 unPercentComplete) PURE; + + /************************************************************************ + * Method: + * IHXClientAdviseSink::OnContacting + * Purpose: + * Called by client engine to inform the client is contacting + * hosts(s). + * + */ + STDMETHOD(OnContacting) (THIS_ + const char* pHostName) PURE; +}; + + +// $Private: + +/**************************************************************************** + * + * Interface: + * + * IHXClientRequestSink + * + * Purpose: + * + * Enables top level clients to get notified of new URLs + * + * + * IID_IHXClientRequestSink + * + * {00000B01-0901-11d1-8B06-00A024406D59} + * + */ + +DEFINE_GUID(IID_IHXClientRequestSink, 0x00000B01, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXClientRequestSink + +DECLARE_INTERFACE_(IHXClientRequestSink, IUnknown) +{ + /************************************************************************ + * Method: + * IHXClientRequestSink::OnNewRequest + * Purpose: + * Inform TLC of the new request. The TLC may choose to + * modify RequestHeaders at this time. + */ + + STDMETHOD(OnNewRequest) (THIS_ IHXRequest* pNewRequest) PURE; +}; + +// $EndPrivate. + +#endif /* _HXCLSNK_H_ */ diff --git a/amarok/src/engine/helix/helix-sp/helix-include/common/container/carray.h b/amarok/src/engine/helix/helix-sp/helix-include/common/container/carray.h new file mode 100644 index 00000000..962efe2b --- /dev/null +++ b/amarok/src/engine/helix/helix-sp/helix-include/common/container/carray.h @@ -0,0 +1,358 @@ + +/* + * + * This software is released under the provisions of the GPL version 2. + * see file "COPYING". If that file is not available, the full statement + * of the license can be found at + * + * http://www.fsf.org/licensing/licenses/gpl.txt + * + * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved. + * + */ + +#ifndef CARRAY_H_ +#define CARRAY_H_ + +#include "hxcom.h" +#include "hxassert.h" + +class HXEXPORT_CLASS CHXPtrArray { +public: + CHXPtrArray(); + ~CHXPtrArray(); + + // return num elements == 0 + HXBOOL IsEmpty() const; + // return number of elements + int GetSize() const; + // return largest index + int GetUpperBound() const; + // set size and grow by size + void SetSize(int nelems, int growSize=-1); + + + // free un-assigned slots + void FreeExtra(); + // free the entire array + void RemoveAll(); + // return the value at the given index + void* GetAt(int index) const; + // set the value at the given index + void SetAt(int index, void* value); + // return reference to value at given index + void*& ElementAt(int index); + // set value, grow array if needed + void SetAtGrow(int index, void* value); + // add the element, ret index of added element + int Add(void* value); + // append another array + void Append(const CHXPtrArray& other); + + // add the element if not already in array + HXBOOL AddIfUnique(void* value); + // same as GetAt() + void* operator[](int index) const; + // same as ElementAt() + void*& operator[](int index); + + // insert value at index + void InsertAt(int index, void* value, int repeat=1); + // insert array at index + void InsertAt(int index, CHXPtrArray* pPtrArray); + // remove value(s) at index + void RemoveAt(int index, int repeat=1); + + // search for value in array + inline HXBOOL Find(void* value, int* index=NULL); + // search for and remove first occurrence + HXBOOL FindAndRemoveOne(void* value); + // search for and remove all occurrences + HXBOOL FindAndRemoveAll(void* value); + + class Iterator + { + friend class CHXPtrArray; + public: + Iterator(); + Iterator& operator++(); + HXBOOL operator==(const Iterator& iter) const; + HXBOOL operator!=(const Iterator& iter) const; + void* operator*(); + + private: + Iterator(CHXPtrArray* pArray, int idx); + + CHXPtrArray* m_pArray; + int m_idx; + }; + + Iterator Begin(); + Iterator End(); + +private: + // not implemented + CHXPtrArray(const CHXPtrArray&); + // not implemented + void operator=(const CHXPtrArray&); + + // resize the array to given size + void Resize(int size); + // get the size to grow array by + int GetGrowSize(int newSize); + // common code for insertions + void InsertCommon(int index, int len); + + + // total slots allocated + int m_size; + // number of elements in array + int m_nelems; + // use set grow size for resizing ops + int m_userGrowSize; + // default grow size if user does not set + int m_defGrowSize; + // data array + void** m_pData; + +}; + +/// +/// IsEmpty() const +/// +/// return num elements == 0 +/// +inline HXBOOL +CHXPtrArray::IsEmpty() const +{ + return m_nelems == 0; +} + +/// +/// GetSize() const +/// +/// return size of the array +/// +inline int +CHXPtrArray::GetSize() const +{ + return m_nelems; +} + +/// +/// GetUpperBound() const +/// +/// return largest index +/// +inline int +CHXPtrArray::GetUpperBound() const +{ + return m_nelems - 1; +} + +/// +/// GetAt(int index) const +/// +/// return the value at the given index +/// +inline void* +CHXPtrArray::GetAt(int index) const +{ + HX_ASSERT(index >= 0 && index < m_nelems); + return m_pData[index]; +} + +/// +/// SetAt(int index, void* value) +/// +/// set the value at the given index +/// +inline void +CHXPtrArray::SetAt(int index, void* value) +{ + HX_ASSERT(index >= 0 && index < m_nelems); + m_pData[index] = value; +} + +/// +/// ElementAt(int index) +/// +/// return reference to value at given index +/// +inline void*& +CHXPtrArray::ElementAt(int index) +{ + HX_ASSERT(index >= 0 && index < m_nelems); + return m_pData[index]; +} + +/// +/// Add(void* value) +/// +/// append the element to the array +/// +inline int +CHXPtrArray::Add(void* value) +{ + int ret = m_nelems; + SetAtGrow(m_nelems, value); + return ret; +} + +/// +/// void* operator[] +/// +/// same as GetAt() +/// +inline void* +CHXPtrArray::operator[] (int index) const +{ + return GetAt(index); +} + +/// +/// void*& operator[] +/// +/// same as ElementAt() +/// +inline void*& +CHXPtrArray::operator[] (int index) +{ + return ElementAt(index); +} + +/// +/// AddIfUnique(void* value) +/// +/// add the element if not already in array +/// +inline HXBOOL +CHXPtrArray::AddIfUnique(void* value) +{ + int index; + if (Find(value, &index)) return FALSE; + Add(value); + return TRUE; +} + +/// +/// Find(void* value, int* index=NULL) +/// +/// search for value in array +/// +inline HXBOOL +CHXPtrArray::Find(void* value, int* index) +{ + int i = 0; + for (void** cur = m_pData; i < m_nelems; ++i, ++cur) + { + if (*cur == value) + { + if (index) *index = i; + return TRUE; + } + } + return FALSE; +} + +/// +/// FindAndRemoveOne(void* value) +/// +/// search for and remove first occurrence +/// +inline HXBOOL +CHXPtrArray::FindAndRemoveOne(void* value) +{ + int index = -1; + if (Find(value, &index) && index >= 0) + { + RemoveAt(index, 1); + return TRUE; + } + return FALSE; +} + +/// +/// FindAndRemoveAll(void* value) +/// +/// search for and remove all occurrences +/// +inline HXBOOL +CHXPtrArray::FindAndRemoveAll(void* value) +{ + void** src = m_pData; + void** dest = m_pData; + + for (int i = 0; i < m_nelems; ++i, ++src) + if (value == *src) *dest++ = *src; + + if (src != dest) + { + SetSize(dest - m_pData); + return TRUE; + } + + return FALSE; +} + +inline CHXPtrArray::Iterator +CHXPtrArray::Begin() +{ + return Iterator(this, 0); +} + +inline CHXPtrArray::Iterator +CHXPtrArray::End() +{ + return Iterator(this, GetSize()); +} + +/// +/// CHXPtrArray::Iterator methods +/// +inline +CHXPtrArray::Iterator::Iterator() + : m_pArray(0), + m_idx(0) +{ +} + +inline +CHXPtrArray::Iterator::Iterator(CHXPtrArray* pArray, int idx) + : m_pArray(pArray), + m_idx(idx) +{ +} + +inline CHXPtrArray::Iterator& +CHXPtrArray::Iterator::operator++() +{ + HX_ASSERT(m_pArray); + HX_ASSERT(!m_pArray->IsEmpty()); + HX_ASSERT(m_idx < m_pArray->GetSize()); + ++m_idx; + return *this; +} + +inline HXBOOL +CHXPtrArray::Iterator::operator==(const Iterator& iter) const +{ + return m_pArray == iter.m_pArray && m_idx == iter.m_idx; +} + +inline HXBOOL +CHXPtrArray::Iterator::operator!=(const Iterator& iter) const +{ + return !operator==(iter); +} + +inline void* +CHXPtrArray::Iterator::operator*() +{ + HX_ASSERT(m_pArray); + HX_ASSERT(!m_pArray->IsEmpty()); + HX_ASSERT(m_idx < m_pArray->GetSize()); + return m_pArray->GetAt(m_idx); +} + +#endif /* CARRAY_H_ */ diff --git a/amarok/src/engine/helix/helix-sp/helix-include/common/container/chxmapbuckets.h b/amarok/src/engine/helix/helix-sp/helix-include/common/container/chxmapbuckets.h new file mode 100644 index 00000000..d52e0cd0 --- /dev/null +++ b/amarok/src/engine/helix/helix-sp/helix-include/common/container/chxmapbuckets.h @@ -0,0 +1,69 @@ + +/* + * + * This software is released under the provisions of the GPL version 2. + * see file "COPYING". If that file is not available, the full statement + * of the license can be found at + * + * http://www.fsf.org/licensing/licenses/gpl.txt + * + * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved. + * + */ + +#ifndef _CHLXMAPBUCKETS_H_ +#define _CHLXMAPBUCKETS_H_ + +#include "hxmaputils.h" + +class CHlxMapBuckets +{ +public: + typedef HlxMap::IntVec_t ITEM; + + CHlxMapBuckets() : m_items(0), m_size(0) + { + } + + CHlxMapBuckets(UINT16 num) : m_items(0), m_size(0) + { + // Nasty new in constructor makes it hard to detect OOM errors, + // but I don't think this constructor is actually used by anybody. + m_items = new ITEM[num]; + m_size = num; + } + + ~CHlxMapBuckets() { HX_VECTOR_DELETE(m_items); } + + inline ITEM& operator[] (int idx) + { + return m_items[idx]; + } + + inline const ITEM& operator[] (int idx) const + { + return m_items[idx]; + } + + inline bool empty () const { return !m_items; } + + inline UINT16 size() const { return m_size; } + + inline HX_RESULT Init (UINT16 num) + { + HX_VECTOR_DELETE(m_items); + m_items = new ITEM[num]; + if( !m_items ) + { + return HXR_OUTOFMEMORY; + } + m_size = num; + return HXR_OK; + } + +private: + ITEM* m_items; + UINT16 m_size; +}; + +#endif // _CHLXMAPBUCKETS_H_ diff --git a/amarok/src/engine/helix/helix-sp/helix-include/common/container/chxmaplongtoobj.h b/amarok/src/engine/helix/helix-sp/helix-include/common/container/chxmaplongtoobj.h new file mode 100644 index 00000000..18a2a8b1 --- /dev/null +++ b/amarok/src/engine/helix/helix-sp/helix-include/common/container/chxmaplongtoobj.h @@ -0,0 +1,286 @@ + +/* + * + * This software is released under the provisions of the GPL version 2. + * see file "COPYING". If that file is not available, the full statement + * of the license can be found at + * + * http://www.fsf.org/licensing/licenses/gpl.txt + * + * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved. + * + */ + +#ifndef _CHXMAPLONGTOOBJ_H_ +#define _CHXMAPLONGTOOBJ_H_ + +// Notes... +// +// Since we aren't using templates, we get to copy the same basic code all +// over the place. So, if you change something in this class, chances are +// that the other CHXMap*To* classes may need the change as well. +// XXXSAB: Need to better abstract out the common code... +// +// This implementation has a few dynamically resized vectors - their +// "chunk sizes" (number of elements added to size when a new element +// addition requires a reallocation) can be adjusted via the following +// accessors. +// +// m_items - This is the vector of actual key/value pairs (along with a +// boolean "free" flag) where the data for the map is stored. It's +// chunk size is controlled via the optional argument to the map +// ctor. And the default value for that is controlled by the +// static SetDefaultChunkSize() method. +// +// m_buckets - This is the vector of hash buckets. Each hash bucket is +// a vector of int indices into the m_items vector. The number of +// buckets doesn't change over time and is controlled via the +// InitHashTable() method (which has the effect of resetting the +// map) and it defaults to z_defaultNumBuckets (101 at the moment). +// The chunk size of the individual hash buckets is set by the +// SetBucketChunkSize() method and the default for that is set by +// the SetDefaultBucketChunkSize() method. +// + +#include "hxtypes.h" +#include "carray.h" +#include "hxstring.h" + +#include "hxmaputils.h" +#include "chxmapbuckets.h" + +class CHXMapLongToObj +{ +public: + typedef LONG32 key_type; + typedef LONG32 key_arg_type; + typedef LONG32 key_ref_type; + inline static key_type key_nil() { return 0; } + + typedef void* value_type; + typedef void* value_arg_type; + typedef void*& value_ref_type; + typedef void* value_const_ref_type; + inline static value_ref_type val_nil() { static const value_type p = 0; return (value_ref_type)p; } + + struct Item + { + Item (key_arg_type key_ = key_nil(), + value_arg_type val_ = val_nil(), + bool bFree_ = true) : + key(key_), val(val_), bFree(bFree_) + {} + + key_type key; + value_type val; + bool bFree; + }; + DECLARE_ITEMVEC(ItemVec_t,Item,Item(),0,0); + + class Iterator + { + public: + typedef key_type iter_key_type; + friend class CHXMapLongToObj; + + // NOTE: (item == -1) is used to mean "set to end of pItems". + Iterator(ItemVec_t* pItems = NULL, + int item = -1); + + // NOTE: Values of 'next' copied into iterator...since this + // iterator is caching key/value and doesn't return a + // value_type&, it can't be used to modify the values in the + // map. + Iterator& operator++(); + Iterator operator++(int); // XXXSAB: tested? + + HXBOOL operator==(const Iterator&) const; + HXBOOL operator!=(const Iterator&) const; + value_type operator*(); // returns the 'value' + iter_key_type get_key (); // returns the 'key' + + private: + void GotoValid(); + + ItemVec_t* m_pItems; + int m_item; + + // cached key/value + iter_key_type m_key; + value_type m_val; + }; + +private: + +#if defined(HELIX_CONFIG_NOSTATICS) + static const ULONG32 z_defaultNumBuckets; + static const ULONG32 z_defaultChunkSize; + static const ULONG32 z_defaultBucketChunkSize; +#else + static ULONG32 z_defaultNumBuckets; + static ULONG32 z_defaultChunkSize; + static ULONG32 z_defaultBucketChunkSize; +#endif + + +public: + + // Construction + + // NOTE: Chunk size is the number of key/value pairs to grow by each + // time one of the hash buckets needs to be grown. + CHXMapLongToObj(int chunkSize = z_defaultChunkSize); + ~CHXMapLongToObj(); + + // Attributes + inline int GetCount() const; + inline HXBOOL IsEmpty() const; + + HXBOOL Lookup(key_arg_type key, value_arg_type& value) const; + POSITION Lookup(key_arg_type key) const; + + // XXXSAB: I added GetKeyAt() and GetAt() since there was previously + // no easy way to get those data without advancing the + // POSITION. + key_ref_type GetKeyAt(POSITION pos) const; + value_const_ref_type GetAt(POSITION pos) const; + value_ref_type GetAt(POSITION pos); + + // Lookup & add if not there + value_ref_type operator[](key_arg_type key); + + // add a new (key, value) pair + POSITION SetAt(key_arg_type key, value_arg_type value); + + // remove existing (key, ?) pair + POSITION Remove(key_arg_type key); + + HXBOOL RemoveKey(key_arg_type key); + + void RemoveAll(); + + // Iteration + POSITION GetStartPosition() const; + void GetNextAssoc (POSITION& pos, key_arg_type& key, value_arg_type& value) const; + + Iterator Begin(); + Iterator End(); + Iterator Erase(Iterator it); + // XXXSAB: Added Find() command to parallel STL style method + Iterator Find(key_arg_type key); + + // Returns the number of hash buckets + inline ULONG32 GetHashTableSize() const; + + // This will reset the internal storage so that any the map will be + // empty when this returns. + HX_RESULT InitHashTable(ULONG32 numBuckets = z_defaultNumBuckets, + HXBOOL bAlloc = TRUE); + + typedef ULONG32 (*HashFunc_t) (key_arg_type key); + static ULONG32 DefaultHashFunc (key_arg_type key); + inline HashFunc_t SetHashFunc (HashFunc_t hf = DefaultHashFunc); // XXXSAB: tested??? + + // Overrideables: special non-virtual (XXXSAB: Huh?) + inline ULONG32 HashKey(key_arg_type key) const; + + inline static void SetDefaultNumBuckets (ULONG32 numBuckets); + inline static void SetDefaultChunkSize (ULONG32 chunkSize); + inline static void SetDefaultBucketChunkSize (ULONG32 chunkSize); + inline void SetBucketChunkSize (ULONG32 chunkSize); + + // In _DEBUG mode, this does a bunch of DPRINTF's... + void Dump() const; + +private: + inline HXBOOL Lookup(key_arg_type key, int& retItem) const; + HXBOOL LookupInBucket(ULONG32 bucket, key_arg_type key, int& retItem) const; + Item* LookupItem(ULONG32 bucket, key_arg_type key); + inline const Item* LookupItem(ULONG32 bucket, key_arg_type key) const + { + return ((CHXMapLongToObj*)this)->LookupItem(bucket, key); + } + + // Internal function - key already verified not to exist + HXBOOL AddToBucket(ULONG32 bucket, key_arg_type key, value_arg_type value, int& retItem); + + + inline POSITION Item2Pos(int item) const; + inline int Pos2Item(POSITION pos) const; + +private: + + HashFunc_t m_hf; + + ItemVec_t m_items; + HlxMap::IntVec_t m_free; + + CHlxMapBuckets m_buckets; + ULONG32 m_numBuckets; + ULONG32 m_chunkSize; + ULONG32 m_bucketChunkSize; + + // Members specific to the type of key and/or value goes below here. + void ConstructTypeSpecifics(); + inline HXBOOL IsKeyMatch (key_arg_type k1, key_arg_type k2) const + { + return (k1 == k2) ? TRUE : FALSE; + } +}; + +int CHXMapLongToObj::GetCount() const +{ + return m_items.size() - m_free.size(); +} + +HXBOOL CHXMapLongToObj::IsEmpty() const +{ + return GetCount() == 0; +} + +ULONG32 CHXMapLongToObj::GetHashTableSize() const +{ + return m_numBuckets; +} + +CHXMapLongToObj::HashFunc_t CHXMapLongToObj::SetHashFunc ( + CHXMapLongToObj::HashFunc_t hf) +{ + HashFunc_t old = m_hf; + m_hf = hf; + return old; +} + +ULONG32 CHXMapLongToObj::HashKey (key_arg_type key) const +{ + if (m_hf) return m_hf(key); + return DefaultHashFunc(key); +} + +void CHXMapLongToObj::SetDefaultNumBuckets (ULONG32 numBuckets) +{ +#if !defined(HELIX_CONFIG_NOSTATICS) + z_defaultNumBuckets = numBuckets; +#endif +} + +void CHXMapLongToObj::SetDefaultChunkSize (ULONG32 chunkSize) +{ +#if !defined(HELIX_CONFIG_NOSTATICS) + z_defaultChunkSize = chunkSize; +#endif +} + +void CHXMapLongToObj::SetDefaultBucketChunkSize (ULONG32 chunkSize) +{ +#if !defined(HELIX_CONFIG_NOSTATICS) + z_defaultBucketChunkSize = chunkSize; +#endif +} + +void CHXMapLongToObj::SetBucketChunkSize (ULONG32 chunkSize) +{ + m_bucketChunkSize = chunkSize; +} + +#endif // _CHXMAPLONGTOOBJ_H_ diff --git a/amarok/src/engine/helix/helix-sp/helix-include/common/container/chxmapptrtoptr.h b/amarok/src/engine/helix/helix-sp/helix-include/common/container/chxmapptrtoptr.h new file mode 100644 index 00000000..186dd50b --- /dev/null +++ b/amarok/src/engine/helix/helix-sp/helix-include/common/container/chxmapptrtoptr.h @@ -0,0 +1,285 @@ + +/* + * + * This software is released under the provisions of the GPL version 2. + * see file "COPYING". If that file is not available, the full statement + * of the license can be found at + * + * http://www.fsf.org/licensing/licenses/gpl.txt + * + * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved. + * + */ + +#ifndef _CHXMAPPTRTOPTR_H_ +#define _CHXMAPPTRTOPTR_H_ + +// Notes... +// +// Since we aren't using templates, we get to copy the same basic code all +// over the place. So, if you change something in this class, chances are +// that the other CHXMap*To* classes may need the change as well. +// XXXSAB: Need to better abstract out the common code... +// +// This implementation has a few dynamically resized vectors - their +// "chunk sizes" (number of elements added to size when a new element +// addition requires a reallocation) can be adjusted via the following +// accessors. +// +// m_items - This is the vector of actual key/value pairs (along with a +// boolean "free" flag) where the data for the map is stored. It's +// chunk size is controlled via the optional argument to the map +// ctor. And the default value for that is controlled by the +// static SetDefaultChunkSize() method. +// +// m_buckets - This is the vector of hash buckets. Each hash bucket is +// a vector of int indices into the m_items vector. The number of +// buckets doesn't change over time and is controlled via the +// InitHashTable() method (which has the effect of resetting the +// map) and it defaults to z_defaultNumBuckets (101 at the moment). +// The chunk size of the individual hash buckets is set by the +// SetBucketChunkSize() method and the default for that is set by +// the SetDefaultBucketChunkSize() method. +// + +#include "hxtypes.h" +#include "carray.h" +#include "hxstring.h" + +#include "hxmaputils.h" +#include "chxmapbuckets.h" + +class CHXMapPtrToPtr +{ +public: + typedef void* key_type; + typedef void* key_arg_type; + typedef void* key_ref_type; + inline static key_type key_nil() { return NULL; } + + typedef void* value_type; + typedef void* value_arg_type; + typedef void*& value_ref_type; + typedef void* value_const_ref_type; + inline static value_ref_type val_nil() { static const value_type p = 0; return (value_ref_type)p; } + + struct Item + { + Item (key_arg_type key_ = key_nil(), + value_arg_type val_ = val_nil(), + bool bFree_ = true) : + key(key_), val(val_), bFree(bFree_) + {} + + key_type key; + value_type val; + bool bFree; + }; + DECLARE_ITEMVEC(ItemVec_t,Item,Item(),0,0); + + class Iterator + { + public: + typedef key_type iter_key_type; + friend class CHXMapPtrToPtr; + + // NOTE: (item == -1) is used to mean "set to end of pItems". + Iterator(ItemVec_t* pItems = NULL, + int item = -1); + + // NOTE: Values of 'next' copied into iterator...since this + // iterator is caching key/value and doesn't return a + // value_type&, it can't be used to modify the values in the + // map. + Iterator& operator++(); + Iterator operator++(int); // XXXSAB: tested? + + HXBOOL operator==(const Iterator&) const; + HXBOOL operator!=(const Iterator&) const; + value_type operator*(); // returns the 'value' + iter_key_type get_key (); // returns the 'key' + + private: + void GotoValid(); + + ItemVec_t* m_pItems; + int m_item; + + // cached key/value + iter_key_type m_key; + value_type m_val; + }; + +private: + +#if defined(HELIX_CONFIG_NOSTATICS) + static const ULONG32 z_defaultNumBuckets; + static const ULONG32 z_defaultChunkSize; + static const ULONG32 z_defaultBucketChunkSize; +#else + static ULONG32 z_defaultNumBuckets; + static ULONG32 z_defaultChunkSize; + static ULONG32 z_defaultBucketChunkSize; +#endif + +public: + + // Construction + + // NOTE: Chunk size is the number of key/value pairs to grow by each + // time one of the hash buckets needs to be grown. + CHXMapPtrToPtr(int chunkSize = z_defaultChunkSize); + ~CHXMapPtrToPtr(); + + // Attributes + inline int GetCount() const; + inline HXBOOL IsEmpty() const; + + HXBOOL Lookup(key_arg_type key, value_arg_type& value) const; + POSITION Lookup(key_arg_type key) const; + + // XXXSAB: I added GetKeyAt() and GetAt() since there was previously + // no easy way to get those data without advancing the + // POSITION. + key_ref_type GetKeyAt(POSITION pos) const; + value_const_ref_type GetAt(POSITION pos) const; + value_ref_type GetAt(POSITION pos); + + // Lookup & add if not there + value_ref_type operator[](key_arg_type key); + + // add a new (key, value) pair + POSITION SetAt(key_arg_type key, value_arg_type value); + + // remove existing (key, ?) pair + POSITION Remove(key_arg_type key); + + HXBOOL RemoveKey(key_arg_type key); + + void RemoveAll(); + + // Iteration + POSITION GetStartPosition() const; + void GetNextAssoc (POSITION& pos, key_arg_type& key, value_arg_type& value) const; + + Iterator Begin(); + Iterator End(); + Iterator Erase(Iterator it); + // XXXSAB: Added Find() command to parallel STL style method + Iterator Find(key_arg_type key); + + // Returns the number of hash buckets + inline ULONG32 GetHashTableSize() const; + + // This will reset the internal storage so that any the map will be + // empty when this returns. + HX_RESULT InitHashTable(ULONG32 numBuckets = z_defaultNumBuckets, + HXBOOL bAlloc = TRUE); + + typedef ULONG32 (*HashFunc_t) (key_arg_type key); + static ULONG32 DefaultHashFunc (key_arg_type key); + inline HashFunc_t SetHashFunc (HashFunc_t hf = DefaultHashFunc); // XXXSAB: tested??? + + // Overrideables: special non-virtual (XXXSAB: Huh?) + inline ULONG32 HashKey(key_arg_type key) const; + + inline static void SetDefaultNumBuckets (ULONG32 numBuckets); + inline static void SetDefaultChunkSize (ULONG32 chunkSize); + inline static void SetDefaultBucketChunkSize (ULONG32 chunkSize); + inline void SetBucketChunkSize (ULONG32 chunkSize); + + // In _DEBUG mode, this does a bunch of DPRINTF's... + void Dump() const; + +private: + inline HXBOOL Lookup(key_arg_type key, int& retItem) const; + HXBOOL LookupInBucket(ULONG32 bucket, key_arg_type key, int& retItem) const; + Item* LookupItem(ULONG32 bucket, key_arg_type key); + inline const Item* LookupItem(ULONG32 bucket, key_arg_type key) const + { + return ((CHXMapPtrToPtr*)this)->LookupItem(bucket, key); + } + + // Internal function - key already verified not to exist + HXBOOL AddToBucket(ULONG32 bucket, key_arg_type key, value_arg_type value, int& retItem); + + + inline POSITION Item2Pos(int item) const; + inline int Pos2Item(POSITION pos) const; + +private: + + HashFunc_t m_hf; + + ItemVec_t m_items; + HlxMap::IntVec_t m_free; + + CHlxMapBuckets m_buckets; + ULONG32 m_numBuckets; + ULONG32 m_chunkSize; + ULONG32 m_bucketChunkSize; + + // Members specific to the type of key and/or value goes below here. + void ConstructTypeSpecifics(); + inline HXBOOL IsKeyMatch (key_arg_type k1, key_arg_type k2) const + { + return (k1 == k2) ? TRUE : FALSE; + } +}; + +int CHXMapPtrToPtr::GetCount() const +{ + return m_items.size() - m_free.size(); +} + +HXBOOL CHXMapPtrToPtr::IsEmpty() const +{ + return GetCount() == 0; +} + +ULONG32 CHXMapPtrToPtr::GetHashTableSize() const +{ + return m_numBuckets; +} + +CHXMapPtrToPtr::HashFunc_t CHXMapPtrToPtr::SetHashFunc ( + CHXMapPtrToPtr::HashFunc_t hf) +{ + HashFunc_t old = m_hf; + m_hf = hf; + return old; +} + +ULONG32 CHXMapPtrToPtr::HashKey (key_arg_type key) const +{ + if (m_hf) return m_hf(key); + return DefaultHashFunc(key); +} + +void CHXMapPtrToPtr::SetDefaultNumBuckets (ULONG32 numBuckets) +{ +#if !defined(HELIX_CONFIG_NOSTATICS) + z_defaultNumBuckets = numBuckets; +#endif +} + +void CHXMapPtrToPtr::SetDefaultChunkSize (ULONG32 chunkSize) +{ +#if !defined(HELIX_CONFIG_NOSTATICS) + z_defaultChunkSize = chunkSize; +#endif +} + +void CHXMapPtrToPtr::SetDefaultBucketChunkSize (ULONG32 chunkSize) +{ +#if !defined(HELIX_CONFIG_NOSTATICS) + z_defaultBucketChunkSize = chunkSize; +#endif +} + +void CHXMapPtrToPtr::SetBucketChunkSize (ULONG32 chunkSize) +{ + m_bucketChunkSize = chunkSize; +} + +#endif // _CHXMAPPTRTOPTR_H_ diff --git a/amarok/src/engine/helix/helix-sp/helix-include/common/container/chxmapstringtoob.h b/amarok/src/engine/helix/helix-sp/helix-include/common/container/chxmapstringtoob.h new file mode 100644 index 00000000..5d3ff12e --- /dev/null +++ b/amarok/src/engine/helix/helix-sp/helix-include/common/container/chxmapstringtoob.h @@ -0,0 +1,322 @@ + +/* + * + * This software is released under the provisions of the GPL version 2. + * see file "COPYING". If that file is not available, the full statement + * of the license can be found at + * + * http://www.fsf.org/licensing/licenses/gpl.txt + * + * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved. + * + */ + +#ifndef _CHXMAPSTRINGTOOB_H_ +#define _CHXMAPSTRINGTOOB_H_ + +// Notes... +// +// Since we aren't using templates, we get to copy the same basic code all +// over the place. So, if you change something in this class, chances are +// that the other CHXMap*To* classes may need the change as well. +// XXXSAB: Need to better abstract out the common code... +// +// This implementation has a few dynamically resized vectors - their +// "chunk sizes" (number of elements added to size when a new element +// addition requires a reallocation) can be adjusted via the following +// accessors. +// +// m_items - This is the vector of actual key/value pairs (along with a +// boolean "free" flag) where the data for the map is stored. It's +// chunk size is controlled via the optional argument to the map +// ctor. And the default value for that is controlled by the +// static SetDefaultChunkSize() method. +// +// m_buckets - This is the vector of hash buckets. Each hash bucket is +// a vector of int indices into the m_items vector. The number of +// buckets doesn't change over time and is controlled via the +// InitHashTable() method (which has the effect of resetting the +// map) and it defaults to z_defaultNumBuckets (101 at the moment). +// The chunk size of the individual hash buckets is set by the +// SetBucketChunkSize() method and the default for that is set by +// the SetDefaultBucketChunkSize() method. +// + +#include "hxtypes.h" +#include "carray.h" +#include "hxstring.h" + +#include "hxmaputils.h" +#include "chxmapbuckets.h" + +#include "hlxclib/string.h" // strcasecmp() + +class CHXMapStringToOb +{ +public: + typedef const char* key_type; + typedef const char* key_arg_type; + typedef const char* key_ref_type; + + inline static CHXString& key_nil() { return (CHXString&)HXEmptyString; } + + typedef void* value_type; + typedef void* value_arg_type; + typedef void*& value_ref_type; + typedef void* value_const_ref_type; + inline static value_ref_type val_nil() { static const value_type p = 0; return (value_ref_type)p; } + + struct Item + { + Item (key_arg_type key_ = key_nil(), + value_arg_type val_ = val_nil(), + bool bFree_ = true) : + key(key_), val(val_), bFree(bFree_) + {} + + CHXString key; + value_type val; + bool bFree; + }; + DECLARE_ITEMVEC(ItemVec_t,Item,Item(),0,0); + + class Iterator + { + public: + typedef key_type iter_key_type; + friend class CHXMapStringToOb; + + // NOTE: (item == -1) is used to mean "set to end of pItems". + Iterator(ItemVec_t* pItems = NULL, + int item = -1); + + // NOTE: Values of 'next' copied into iterator...since this + // iterator is caching key/value and doesn't return a + // value_type&, it can't be used to modify the values in the + // map. + Iterator& operator++(); + Iterator operator++(int); // XXXSAB: tested? + + HXBOOL operator==(const Iterator&) const; + HXBOOL operator!=(const Iterator&) const; + value_type operator*(); // returns the 'value' + iter_key_type get_key (); // returns the 'key' + + private: + void GotoValid(); + + ItemVec_t* m_pItems; + int m_item; + + // cached key/value + CHXString m_key; + value_type m_val; + }; + +private: + +#if defined(HELIX_CONFIG_NOSTATICS) + static const ULONG32 z_defaultNumBuckets; + static const ULONG32 z_defaultChunkSize; + static const ULONG32 z_defaultBucketChunkSize; +#else + static ULONG32 z_defaultNumBuckets; + static ULONG32 z_defaultChunkSize; + static ULONG32 z_defaultBucketChunkSize; +#endif + + +public: + // Construction + + // NOTE: Chunk size is the number of key/value pairs to grow by each + // time one of the hash buckets needs to be grown. + CHXMapStringToOb(int chunkSize = z_defaultChunkSize); + ~CHXMapStringToOb(); + + // Attributes + inline int GetCount() const; + inline HXBOOL IsEmpty() const; + + HXBOOL Lookup(key_arg_type key, value_arg_type& value) const; + POSITION Lookup(key_arg_type key) const; + + // XXXSAB: I added GetKeyAt() and GetAt() since there was previously + // no easy way to get those data without advancing the + // POSITION. + key_ref_type GetKeyAt(POSITION pos) const; + value_const_ref_type GetAt(POSITION pos) const; + value_ref_type GetAt(POSITION pos); + + // Lookup & add if not there + value_ref_type operator[](key_arg_type key); + + // add a new (key, value) pair + POSITION SetAt(key_arg_type key, value_arg_type value); + + // remove existing (key, ?) pair + POSITION Remove(key_arg_type key); + + HXBOOL RemoveKey(key_arg_type key); + + void RemoveAll(); + + // Iteration + POSITION GetStartPosition() const; + void GetNextAssoc (POSITION& pos, key_arg_type& key, value_arg_type& value) const; + + // CHXString Helper... + void GetNextAssoc (POSITION& pos, CHXString& key, value_arg_type& value) const + { + const char* sz = NULL; + GetNextAssoc (pos, sz, value); + if (sz) key = sz; + } + + Iterator Begin(); + Iterator End(); + Iterator Erase(Iterator it); + // XXXSAB: Added Find() command to parallel STL style method + Iterator Find(key_arg_type key); + + // Returns the number of hash buckets + inline ULONG32 GetHashTableSize() const; + + // This will reset the internal storage so that any the map will be + // empty when this returns. + // NOTE: This function always allocates some data - the bAlloc flag is + // for compatibility with an old interface and is ignored. + HX_RESULT InitHashTable(ULONG32 numBuckets = z_defaultNumBuckets, + HXBOOL bAlloc = TRUE); + + // defaults to on. Set this before you add anything! And make sure + // that if you set a hash function that it's behavior matches the case + // sensitivity flag. + void SetCaseSensitive(HXBOOL bCaseSens) { m_bCaseSens = bCaseSens ? true : false; } + + typedef ULONG32 (*HashFunc_t) (key_arg_type key); + static ULONG32 DefaultHashFunc (key_arg_type key) + { + return HlxMap::StrHashFunc(key, true); + } + static ULONG32 DefaultNoCaseHashFunc (key_arg_type key) + { + return HlxMap::StrHashFunc(key, false); + } + inline HashFunc_t SetHashFunc (HashFunc_t hf = DefaultHashFunc); // XXXSAB: tested??? + + // Overrideables: special non-virtual (XXXSAB: Huh?) + inline ULONG32 HashKey(key_arg_type key) const; + + inline static void SetDefaultNumBuckets (ULONG32 numBuckets); + inline static void SetDefaultChunkSize (ULONG32 chunkSize); + inline static void SetDefaultBucketChunkSize (ULONG32 chunkSize); + inline void SetBucketChunkSize (ULONG32 chunkSize); + + // In _DEBUG mode, this does a bunch of DPRINTF's... + void Dump() const; + +private: + inline HXBOOL Lookup(key_arg_type key, int& retItem) const; + HXBOOL LookupInBucket(ULONG32 bucket, key_arg_type key, int& retItem) const; + Item* LookupItem(ULONG32 bucket, key_arg_type key); + inline const Item* LookupItem(ULONG32 bucket, key_arg_type key) const + { + return ((CHXMapStringToOb*)this)->LookupItem(bucket, key); + } + + // Internal function - key already verified not to exist + HXBOOL AddToBucket(ULONG32 bucket, key_arg_type key, value_arg_type value, int& retItem); + + inline POSITION Item2Pos(int item) const; + inline int Pos2Item(POSITION pos) const; + +private: + + HashFunc_t m_hf; + + ItemVec_t m_items; + HlxMap::IntVec_t m_free; + + CHlxMapBuckets m_buckets; + ULONG32 m_numBuckets; + ULONG32 m_chunkSize; + ULONG32 m_bucketChunkSize; + + // Members specific to the type of key and/or value goes below here. + void ConstructTypeSpecifics(); + inline HXBOOL IsKeyMatch (key_arg_type k1, key_arg_type k2) const + { + HXBOOL bRet; + + if (m_bCaseSens) bRet = (strcmp(k1, k2) == 0); + else bRet = (strcasecmp(k1, k2) == 0); + +#ifdef XXXSAB + printf ("IsKeyMatch(\"%s\", \"%s\") -> %s\n", + k1, k2, + bRet ? "true" : "false"); +#endif /* XXXSAB */ + + return bRet; + } + + bool m_bCaseSens; +}; + +int CHXMapStringToOb::GetCount() const +{ + return m_items.size() - m_free.size(); +} + +HXBOOL CHXMapStringToOb::IsEmpty() const +{ + return GetCount() == 0; +} + +ULONG32 CHXMapStringToOb::GetHashTableSize() const +{ + return m_numBuckets; +} + +CHXMapStringToOb::HashFunc_t CHXMapStringToOb::SetHashFunc ( + CHXMapStringToOb::HashFunc_t hf) +{ + HashFunc_t old = m_hf; + m_hf = hf; + return old; +} + +ULONG32 CHXMapStringToOb::HashKey (key_arg_type key) const +{ + if (m_hf) return m_hf(key); + return m_bCaseSens ? DefaultHashFunc(key) : DefaultNoCaseHashFunc(key); +} + +void CHXMapStringToOb::SetDefaultNumBuckets (ULONG32 numBuckets) +{ +#if !defined(HELIX_CONFIG_NOSTATICS) + z_defaultNumBuckets = numBuckets; +#endif +} + +void CHXMapStringToOb::SetDefaultChunkSize (ULONG32 chunkSize) +{ +#if !defined(HELIX_CONFIG_NOSTATICS) + z_defaultChunkSize = chunkSize; +#endif +} + +void CHXMapStringToOb::SetDefaultBucketChunkSize (ULONG32 chunkSize) +{ +#if !defined(HELIX_CONFIG_NOSTATICS) + z_defaultBucketChunkSize = chunkSize; +#endif +} + +void CHXMapStringToOb::SetBucketChunkSize (ULONG32 chunkSize) +{ + m_bucketChunkSize = chunkSize; +} + +#endif // _CHXMAPSTRINGTOOB_H_ diff --git a/amarok/src/engine/helix/helix-sp/helix-include/common/container/chxmapstringtostring.h b/amarok/src/engine/helix/helix-sp/helix-include/common/container/chxmapstringtostring.h new file mode 100644 index 00000000..5d679d77 --- /dev/null +++ b/amarok/src/engine/helix/helix-sp/helix-include/common/container/chxmapstringtostring.h @@ -0,0 +1,316 @@ + +/* + * + * This software is released under the provisions of the GPL version 2. + * see file "COPYING". If that file is not available, the full statement + * of the license can be found at + * + * http://www.fsf.org/licensing/licenses/gpl.txt + * + * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved. + * + */ + +#ifndef _CHXMAPSTRINGTOSTRING_H_ +#define _CHXMAPSTRINGTOSTRING_H_ + +// Notes... +// +// Since we aren't using templates, we get to copy the same basic code all +// over the place. So, if you change something in this class, chances are +// that the other CHXMap*To* classes may need the change as well. +// XXXSAB: Need to better abstract out the common code... +// +// This implementation has a few dynamically resized vectors - their +// "chunk sizes" (number of elements added to size when a new element +// addition requires a reallocation) can be adjusted via the following +// accessors. +// +// m_items - This is the vector of actual key/value pairs (along with a +// boolean "free" flag) where the data for the map is stored. It's +// chunk size is controlled via the optional argument to the map +// ctor. And the default value for that is controlled by the +// static SetDefaultChunkSize() method. +// +// m_buckets - This is the vector of hash buckets. Each hash bucket is +// a vector of int indices into the m_items vector. The number of +// buckets doesn't change over time and is controlled via the +// InitHashTable() method (which has the effect of resetting the +// map) and it defaults to z_defaultNumBuckets (101 at the moment). +// The chunk size of the individual hash buckets is set by the +// SetBucketChunkSize() method and the default for that is set by +// the SetDefaultBucketChunkSize() method. +// + +#include "hxtypes.h" +#include "carray.h" +#include "hxstring.h" + +#include "hxmaputils.h" +#include "chxmapbuckets.h" + +#include "hlxclib/string.h" // strcasecmp() + +class CHXMapStringToString +{ +public: + typedef const char* key_type; + typedef const char* key_arg_type; + typedef const char* key_ref_type; + + inline static CHXString& key_nil() { return (CHXString&)HXEmptyString; } + + typedef const char* value_type; + typedef const char* value_arg_type; + typedef CHXString& value_ref_type; + typedef const CHXString& value_const_ref_type; + + inline static CHXString& val_nil() { return (CHXString&)HXEmptyString; } + + struct Item + { + Item (key_arg_type key_ = key_nil(), + value_arg_type val_ = val_nil(), + bool bFree_ = true) : + key(key_), val(val_), bFree(bFree_) + {} + + CHXString key; + CHXString val; + bool bFree; + }; + DECLARE_ITEMVEC(ItemVec_t,Item,Item(),0,0);//HXEmptyString); + + class Iterator + { + public: + typedef key_type iter_key_type; + friend class CHXMapStringToString; + + // NOTE: (item == -1) is used to mean "set to end of pItems". + Iterator(ItemVec_t* pItems = NULL, + int item = -1); + + // NOTE: Values of 'next' copied into iterator...since this + // iterator is caching key/value and doesn't return a + // value_type&, it can't be used to modify the values in the + // map. + Iterator& operator++(); + Iterator operator++(int); // XXXSAB: tested? + + HXBOOL operator==(const Iterator&) const; + HXBOOL operator!=(const Iterator&) const; + value_type operator*(); // returns the 'value' + iter_key_type get_key (); // returns the 'key' + + private: + void GotoValid(); + + ItemVec_t* m_pItems; + int m_item; + + // cached key/value + CHXString m_key; + CHXString m_val; + }; + +private: + +#if defined(HELIX_CONFIG_NOSTATICS) + static const ULONG32 z_defaultNumBuckets; + static const ULONG32 z_defaultChunkSize; + static const ULONG32 z_defaultBucketChunkSize; +#else + static ULONG32 z_defaultNumBuckets; + static ULONG32 z_defaultChunkSize; + static ULONG32 z_defaultBucketChunkSize; +#endif + + +public: + + // Construction + + // NOTE: Chunk size is the number of key/value pairs to grow by each + // time one of the hash buckets needs to be grown. + CHXMapStringToString(int chunkSize = z_defaultChunkSize); + ~CHXMapStringToString(); + + // Attributes + inline int GetCount() const; + inline HXBOOL IsEmpty() const; + + HXBOOL Lookup(key_arg_type key, CHXString& value) const; + POSITION Lookup(key_arg_type key) const; + + // XXXSAB: I added GetKeyAt() and GetAt() since there was previously + // no easy way to get those data without advancing the + // POSITION. + key_ref_type GetKeyAt(POSITION pos) const; + value_const_ref_type GetAt(POSITION pos) const; + value_ref_type GetAt(POSITION pos); + + // Lookup & add if not there + value_ref_type operator[](key_arg_type key); + + // add a new (key, value) pair + POSITION SetAt(key_arg_type key, value_arg_type value); + + // remove existing (key, ?) pair + POSITION Remove(key_arg_type key); + + HXBOOL RemoveKey(key_arg_type key); + + void RemoveAll(); + + // Iteration + POSITION GetStartPosition() const; + void GetNextAssoc (POSITION& pos, CHXString& key, CHXString& value) const; + + Iterator Begin(); + Iterator End(); + Iterator Erase(Iterator it); + // XXXSAB: Added Find() command to parallel STL style method + Iterator Find(key_arg_type key); + + // Returns the number of hash buckets + inline ULONG32 GetHashTableSize() const; + + // This will reset the internal storage so that any the map will be + // empty when this returns. + // NOTE: This function always allocates some data - the bAlloc flag is + // for compatibility with an old interface and is ignored. + HX_RESULT InitHashTable(ULONG32 numBuckets = z_defaultNumBuckets, + HXBOOL bAlloc = TRUE); + + // defaults to on. Set this before you add anything! And make sure + // that if you set a hash function that it's behavior matches the case + // sensitivity flag. + void SetCaseSensitive(HXBOOL bCaseSens) { m_bCaseSens = bCaseSens ? true : false; } + + typedef ULONG32 (*HashFunc_t) (key_arg_type key); + static ULONG32 DefaultHashFunc (key_arg_type key) + { + return HlxMap::StrHashFunc(key, true); + } + static ULONG32 DefaultNoCaseHashFunc (key_arg_type key) + { + return HlxMap::StrHashFunc(key, false); + } + inline HashFunc_t SetHashFunc (HashFunc_t hf = DefaultHashFunc); // XXXSAB: tested??? + + // Overrideables: special non-virtual (XXXSAB: Huh?) + inline ULONG32 HashKey(key_arg_type key) const; + + inline static void SetDefaultNumBuckets (ULONG32 numBuckets); + inline static void SetDefaultChunkSize (ULONG32 chunkSize); + inline static void SetDefaultBucketChunkSize (ULONG32 chunkSize); + inline void SetBucketChunkSize (ULONG32 chunkSize); + + // In _DEBUG mode, this does a bunch of DPRINTF's... + void Dump() const; + +private: + inline HXBOOL Lookup(key_arg_type key, int& retItem) const; + HXBOOL LookupInBucket(ULONG32 bucket, key_arg_type key, int& retItem) const; + Item* LookupItem(ULONG32 bucket, key_arg_type key); + inline const Item* LookupItem(ULONG32 bucket, key_arg_type key) const + { + return ((CHXMapStringToString*)this)->LookupItem(bucket, key); + } + + // Internal function - key already verified not to exist + HXBOOL AddToBucket(ULONG32 bucket, key_arg_type key, value_arg_type value, int& retItem); + + inline POSITION Item2Pos(int item) const; + inline int Pos2Item(POSITION pos) const; + +private: + + HashFunc_t m_hf; + + ItemVec_t m_items; + HlxMap::IntVec_t m_free; + + CHlxMapBuckets m_buckets; + ULONG32 m_numBuckets; + ULONG32 m_chunkSize; + ULONG32 m_bucketChunkSize; + + // Members specific to the type of key and/or value goes below here. + void ConstructTypeSpecifics(); + inline HXBOOL IsKeyMatch (key_arg_type k1, key_arg_type k2) const + { + HXBOOL bRet; + + if (m_bCaseSens) bRet = (strcmp(k1, k2) == 0); + else bRet = (strcasecmp(k1, k2) == 0); + +#ifdef XXXSAB + printf ("IsKeyMatch(\"%s\", \"%s\") -> %s\n", + k1, k2, + bRet ? "true" : "false"); +#endif /* XXXSAB */ + + return bRet; + } + + bool m_bCaseSens; +}; + +int CHXMapStringToString::GetCount() const +{ + return m_items.size() - m_free.size(); +} + +HXBOOL CHXMapStringToString::IsEmpty() const +{ + return GetCount() == 0; +} + +ULONG32 CHXMapStringToString::GetHashTableSize() const +{ + return m_numBuckets; +} + +CHXMapStringToString::HashFunc_t CHXMapStringToString::SetHashFunc ( + CHXMapStringToString::HashFunc_t hf) +{ + HashFunc_t old = m_hf; + m_hf = hf; + return old; +} + +ULONG32 CHXMapStringToString::HashKey (key_arg_type key) const +{ + if (m_hf) return m_hf(key); + return m_bCaseSens ? DefaultHashFunc(key) : DefaultNoCaseHashFunc(key); +} + +void CHXMapStringToString::SetDefaultNumBuckets (ULONG32 numBuckets) +{ +#if !defined(HELIX_CONFIG_NOSTATICS) + z_defaultNumBuckets = numBuckets; +#endif +} + +void CHXMapStringToString::SetDefaultChunkSize (ULONG32 chunkSize) +{ +#if !defined(HELIX_CONFIG_NOSTATICS) + z_defaultChunkSize = chunkSize; +#endif +} + +void CHXMapStringToString::SetDefaultBucketChunkSize (ULONG32 chunkSize) +{ +#if !defined(HELIX_CONFIG_NOSTATICS) + z_defaultBucketChunkSize = chunkSize; +#endif +} + +void CHXMapStringToString::SetBucketChunkSize (ULONG32 chunkSize) +{ + m_bucketChunkSize = chunkSize; +} + +#endif // _CHXMAPSTRINGTOSTRING_H_ diff --git a/amarok/src/engine/helix/helix-sp/helix-include/common/container/hxbuffer.h b/amarok/src/engine/helix/helix-sp/helix-include/common/container/hxbuffer.h new file mode 100644 index 00000000..bcda2595 --- /dev/null +++ b/amarok/src/engine/helix/helix-sp/helix-include/common/container/hxbuffer.h @@ -0,0 +1,206 @@ + +/* + * + * This software is released under the provisions of the GPL version 2. + * see file "COPYING". If that file is not available, the full statement + * of the license can be found at + * + * http://www.fsf.org/licensing/licenses/gpl.txt + * + * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved. + * + */ + +#ifndef _HXBUFFER_H_ +#define _HXBUFFER_H_ + +#include "ihxpckts.h" +#include "hxvalue.h" +//#include "hxheap.h" +#include "hxstring.h" + +typedef struct +{ + UCHAR* m_pData; + ULONG32 m_ulLength; + unsigned char m_FreeWithMallocInterfaceIfAvail; +} _BigData; + +// This determines the length of the built in buffer that is used if the +// data length is small enough, to save us from allocating so many little +// pieces of data. +#define PnBufferShort +#ifdef PnBufferShort +// XXXNH: This value was originally 15 and was chosen after some research as +// an optimal size for bulk of the small strings we deal with in our buffers. +// However, since the size of the structure is larger than 15 bytes on +// 64-bit systems we are now using this compile-time size calculation to +// ensure that the structure is of sufficient size. +const int MaxPnbufShortDataLen = HX_MAX(sizeof(_BigData), 15); +#endif + +#define NUM_ALLOCATION_EACH_TIME 25 +/**************************************************************************** + * + * Class: + * + * CHXBuffer + * + * Purpose: + * + * PN implementation of a basic buffer. + * + */ +class CHXBuffer + : public IHXBuffer +{ +protected: + + LONG32 m_lRefCount; + ULONG32 m_ulAllocLength; + HXBOOL m_bJustPointToExistingData; + +#if !defined(HELIX_CONFIG_NOSTATICS) + // Interface for optional allocator + static IMalloc* m_zMallocInterface; +#endif + + // number of CHXBuffer allocated at a time to be placed in freeStore + static CHXBuffer* s_pFreeStore; + static const int s_iBufferChunk; + + virtual ~CHXBuffer(); + + HXBOOL FreeWithMallocInterface() const; + +#ifdef PnBufferShort + // buffer for small amounts of data + //UCHAR m_ShortData[MaxPnbufShortDataLen + 1]; +#endif + + enum { BigDataTag = 0xEE }; + + union + { + _BigData m_BigData; + + UCHAR m_ShortData[MaxPnbufShortDataLen + 1]; + }; + + bool IsShort() const; + HX_RESULT SetSize(ULONG32 ulLength, HXBOOL copyExistingData); + + UCHAR* Allocate(UINT32 size) const; + UCHAR* Reallocate(UCHAR*, UINT32 oldSize, UINT32 newSize) const; + void Deallocate(UCHAR*) const; + + +public: + CHXBuffer(); + CHXBuffer(UCHAR* pData, UINT32 ulLength, HXBOOL bOwnBuffer = TRUE); + +#if 0 +#ifndef __MWERKS__ +#if defined (_DEBUG) && defined (_WIN32) && 0 + void * operator new( + unsigned int, + int, + const char *, + int + ); +#else + void * operator new (size_t size); +#endif /*defined (_DEBUG) && defined (_WIN32) */ + void operator delete(void *p, size_t size); +#endif /*__MWERKS__*/ +#endif /*0*/ + + inline CHXBuffer& operator=(const char* psz); + inline CHXBuffer& operator=(const unsigned char* psz); + inline CHXBuffer& operator=(const CHXString &str); + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj); + + STDMETHOD_(ULONG32,AddRef) (THIS); + + STDMETHOD_(ULONG32,Release) (THIS); + + /* + * IHXBuffer methods + */ + STDMETHOD(Get) (THIS_ + REF(UCHAR*) pData, + REF(ULONG32) ulLength); + + STDMETHOD(Set) (THIS_ + const UCHAR* pData, + ULONG32 ulLength); + + STDMETHOD(SetSize) (THIS_ + ULONG32 ulLength); + + STDMETHOD_(ULONG32,GetSize) (THIS); + + STDMETHOD_(UCHAR*,GetBuffer) + (THIS); + + +public: + static HX_RESULT FromCharArray + ( + const char* szIn, + IHXBuffer** ppbufOut + ); + static HX_RESULT FromCharArray + ( + const char* szIn, + UINT32 ulLength, + IHXBuffer** ppbufOut + ); + static void SetAllocator(IMalloc* pMalloc); + static void ReleaseAllocator(); +}; + +CHXBuffer& CHXBuffer::operator=(const char* psz) +{ + Set((const unsigned char*)psz, strlen(psz)+1); + return(*this); +} + +CHXBuffer& CHXBuffer::operator=(const unsigned char* psz) +{ + Set(psz, strlen((const char*)psz)+1); + return(*this); +} + +CHXBuffer& CHXBuffer::operator=(const CHXString& str) +{ + Set((const unsigned char*)(const char *)str, str.GetLength()+1); + return(*this); +} + + +// This class was created in order to be able to have a buffer that consists of +// a subset of another existing buffer without allocating any new data or +// copying data over. The way to use this class is to instantiate it with 3 +// parameters: +// 1) A pointer to the superset buffer, +// 2) The pointer to the point in the the buffer that represents the start of +// the subset buffer, and +// 3) The length of the subset buffer. +// +class CHXBufferFragment : public CHXBuffer +{ +public : + CHXBufferFragment(IHXBuffer * pWrappedBuffer, UCHAR* pModFrameStart, ULONG32 ulFragLen) : CHXBuffer( pModFrameStart, ulFragLen, FALSE ), m_pHXBufferPointedTo(pWrappedBuffer){ if(pWrappedBuffer) {pWrappedBuffer->AddRef();} }; + ~CHXBufferFragment(){ HX_RELEASE(m_pHXBufferPointedTo);} + +protected : + IHXBuffer * m_pHXBufferPointedTo; +}; + +#endif diff --git a/amarok/src/engine/helix/helix-sp/helix-include/common/container/hxmap.h b/amarok/src/engine/helix/helix-sp/helix-include/common/container/hxmap.h new file mode 100644 index 00000000..797b04ae --- /dev/null +++ b/amarok/src/engine/helix/helix-sp/helix-include/common/container/hxmap.h @@ -0,0 +1,22 @@ + +/* + * + * This software is released under the provisions of the GPL version 2. + * see file "COPYING". If that file is not available, the full statement + * of the license can be found at + * + * http://www.fsf.org/licensing/licenses/gpl.txt + * + * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved. + * + */ + +#ifndef _HXMAP_H_ +#define _HXMAP_H_ + +#include "chxmapptrtoptr.h" +#include "chxmapstringtoob.h" +#include "chxmapstringtostring.h" +#include "chxmaplongtoobj.h" + +#endif // _HXMAP_H_ diff --git a/amarok/src/engine/helix/helix-sp/helix-include/common/container/hxmaputils.h b/amarok/src/engine/helix/helix-sp/helix-include/common/container/hxmaputils.h new file mode 100644 index 00000000..48faa7c3 --- /dev/null +++ b/amarok/src/engine/helix/helix-sp/helix-include/common/container/hxmaputils.h @@ -0,0 +1,211 @@ + +/* + * + * This software is released under the provisions of the GPL version 2. + * see file "COPYING". If that file is not available, the full statement + * of the license can be found at + * + * http://www.fsf.org/licensing/licenses/gpl.txt + * + * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved. + * + */ + +#ifndef _HLXMAPUTILS_H_ +#define _HLXMAPUTILS_H_ + +#include "hxtypes.h" +#include "hxassert.h" + +typedef void* POSITION; // XXXSAB: where does this belong? + +#if defined(HELIX_CONFIG_LOW_HEAP_HASH_TABLE) +// The default values to the hash class result in terrible heap consumption. +// Using absolute minimal values here. Not much of a hash table now though! +#define CHUNK_INIT 1 +#else +#define CHUNK_INIT 16 +#endif + +#define DECLARE_ITEMVEC(CLASS,ITEM,NIL,CHUNK,INIT) \ + class CLASS \ + { \ + public: \ + CLASS(); \ + CLASS(int num);\ + CLASS(int num, const ITEM& item); \ + CLASS(const CLASS& from); \ + CLASS& operator= (const CLASS& from); \ + ~CLASS(); \ + \ + inline ITEM& operator[] (int idx) \ + { \ + return m_items[idx]; \ + /* return (idx >= 0 && idx < m_used ? m_items[idx] : nil()); */ \ + } \ + \ + inline const ITEM& operator[] (int idx) const \ + { \ + return m_items[idx]; \ + /* return (idx >= 0 && idx < m_used ? m_items[idx] : nil()); */ \ + } \ + \ + inline bool empty () const { return m_used <= 0; } \ + inline int size () const { return m_used; } \ + inline void resize (int s) \ + { \ + resize(s, INIT); \ + } \ + \ + void resize (int s, const ITEM& item); \ + \ + inline int capacity () const { return m_alloc; } \ +\ + void reserve (int s); \ +\ + inline void SetChunkSize (int chunk) { m_chunkSize = chunk; } \ + void GrowBy (int by); \ + \ + CLASS& push_back (const ITEM& item); \ + \ + inline CLASS& pop_back () \ + { \ + HX_ASSERT (m_used > 0); \ + if (m_used > 0) --m_used; \ + return *this; \ + } \ + \ + inline ITEM& back() \ + { \ + HX_ASSERT (m_items); HX_ASSERT (m_used > 0); \ + return m_items[m_used-1]; \ + } \ + \ + void zap (int idx, int numToZap = 1); \ + \ + private: \ + ITEM* m_items; \ + int m_alloc; \ + int m_used; \ + UINT16 m_chunkSize; \ + } + +#define DECLARE_ITEMVEC_IMP(PARENT, CLASS,ITEM,NIL,CHUNK,INIT) \ + PARENT::CLASS::CLASS() : \ + m_items(0), m_alloc(0), m_used(0), m_chunkSize(CHUNK) \ + { \ + } \ + \ + PARENT::CLASS::CLASS(int num) : \ + m_items(0), m_alloc(0), m_used(0), m_chunkSize(CHUNK) \ + { \ + if (num > 0) \ + { \ + m_items = new ITEM[num]; \ + m_used = m_alloc = num; \ + for (int i = 0; i < num; ++i) m_items[i] = INIT; \ + } \ + } \ + \ + PARENT::CLASS::CLASS(int num, const ITEM& item) : \ + m_items(0), m_alloc(0), m_used(0), m_chunkSize(CHUNK) \ + { \ + if (num > 0) \ + { \ + m_items = new ITEM[num]; \ + m_used = m_alloc = num; \ + for (int i = 0; i < num; ++i) m_items[i] = item; \ + } \ + } \ + \ + PARENT::CLASS::CLASS(const PARENT::CLASS& from) : \ + m_items(0), m_alloc(0), m_used(0), m_chunkSize(CHUNK) \ + { \ + m_used = from.m_used; \ + m_alloc = from.m_alloc; \ + m_items = new ITEM[m_alloc]; \ + for (int i = 0; i < m_used; ++i) m_items[i] = from.m_items[i]; \ + } \ + \ + PARENT::CLASS& PARENT::CLASS::operator= (const PARENT::CLASS& from) \ + { \ + if (m_items != from.m_items) \ + { \ + HX_VECTOR_DELETE(m_items); \ + m_used = from.m_used; \ + m_alloc = from.m_alloc; \ + m_items = new ITEM[m_alloc]; \ + for (int i = 0; i < m_used; ++i) m_items[i] = from.m_items[i]; \ + } \ + return *this; \ + } \ + \ + PARENT::CLASS::~CLASS() { HX_VECTOR_DELETE(m_items); } \ + \ + \ + void PARENT::CLASS::resize (int s, const ITEM& item) \ + { \ + reserve(s); \ + for (int i = m_used; i < s; ++i) m_items[i] = item; \ + m_used = s; \ + } \ + \ + void PARENT::CLASS::reserve (int s) \ + { \ + if (s > m_alloc) \ + { \ + ITEM* newItems = new ITEM[s]; \ + if( newItems ){ \ + for (int i = 0; i < m_used; ++i) newItems[i] = m_items[i]; \ + HX_VECTOR_DELETE (m_items); \ + m_items = newItems; \ + m_alloc = s; \ + } \ + } \ + } \ + \ + void PARENT::CLASS::GrowBy (int by) \ + { \ + /* If no chunkSize specified, \ + use the larger of 16 and the currently allocated amount. \ + */ \ + \ + int chunk = m_chunkSize > 0 ? m_chunkSize : MAX (m_alloc, CHUNK_INIT); \ + int newAlloc = m_alloc + ((by + chunk - 1) / chunk) * chunk; \ + reserve (newAlloc); \ + } \ + \ + PARENT::CLASS& PARENT::CLASS::push_back (const ITEM& item) \ + { \ + if (m_used == m_alloc) GrowBy (1); \ + HX_ASSERT (m_used < m_alloc); \ + m_items[m_used++] = item; \ + return *this; \ + } \ + \ + void PARENT::CLASS::zap (int idx, int numToZap) \ + { \ + HX_ASSERT (idx >= 0 && idx < m_used); \ + \ + if ((idx + numToZap) >= m_used) \ + { \ + m_used = idx; \ + } \ + else \ + { \ + int src = idx + numToZap; \ + int dest = idx; \ + for (; src < m_used; ++src, ++dest) \ + m_items[dest] = m_items[src]; \ + m_used -= numToZap; \ + } \ + } + +struct HlxMap +{ + DECLARE_ITEMVEC(IntVec_t, int, 0, 0, 0); + + static ULONG32 StrHashFunc (const char* key, bool bCaseSens); +}; + +#endif // _HLXMAPUTILS_H_ diff --git a/amarok/src/engine/helix/helix-sp/helix-include/common/container/hxstring.h b/amarok/src/engine/helix/helix-sp/helix-include/common/container/hxstring.h new file mode 100644 index 00000000..6a0657c3 --- /dev/null +++ b/amarok/src/engine/helix/helix-sp/helix-include/common/container/hxstring.h @@ -0,0 +1,559 @@ + +/* + * + * This software is released under the provisions of the GPL version 2. + * see file "COPYING". If that file is not available, the full statement + * of the license can be found at + * + * http://www.fsf.org/licensing/licenses/gpl.txt + * + * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved. + * + */ + +#ifndef HXSTRING_H +#define HXSTRING_H + +#include "hxtypes.h" +#include "hxassert.h" +#include "hlxclib/limits.h" +#include "hlxclib/string.h" + +#if defined(HELIX_CONFIG_NOSTATICS) +# include "globals/hxglobalchxstring.h" +#endif + + +typedef INT32 (*StringGrowthFunc)(INT32 currentSize, INT32 sizeNeeded); + +class CHXStringRep +{ +public: + CHXStringRep(INT32 bufSize = 1, bool bSetLength = false); + CHXStringRep(const char* pStr); + CHXStringRep(const char* pStr, INT32 bufSize); + CHXStringRep(char ch, INT32 bufSize); + ~CHXStringRep(); + + void AddRef(); + void Release(); + + char* GetBuffer(); + INT32 GetStringSize() const; + INT32 GetBufferSize() const; + + void SetStringSize(INT32 strSize); + void Resize(INT32 newSize); + void ResizeAndCopy(INT32 newSize, bool bSetLength = false); + + void Copy(const char* pStr, INT32 size); + + bool IsShared() const; + +private: + CHXStringRep(const CHXStringRep&); + CHXStringRep& operator=(const CHXStringRep&); + + INT32 m_refCount; + INT32 m_strSize; + INT32 m_bufSize; + char* m_pData; +}; + +class HXEXPORT_CLASS CHXString +{ +public: + CHXString(StringGrowthFunc pGrowthFunc = 0); + CHXString(const CHXString& rhs); + CHXString(char ch, int length = 1, StringGrowthFunc pGrowthFunc = 0); + CHXString(const char* pStr, StringGrowthFunc pGrowthFunc = 0); + CHXString(const char* pStr, int length, StringGrowthFunc pGrowthFunc = 0); + CHXString(const unsigned char* pStr, StringGrowthFunc pGrowthFunc = 0); + ~CHXString(); + + // Attributes & Operations + // as an array of characters + UINT32 GetLength() const; + HXBOOL IsEmpty() const; + void Empty(); + + char GetAt(INT32 i) const; + char operator[](short i) const; + char operator[](unsigned short i) const; + char operator[](int i) const; + char operator[](unsigned int i) const; + char operator[](long i) const; + char operator[](unsigned long i) const; + + void SetAt(INT32 i, char ch); + operator const char*() const; + + bool operator>(const CHXString& rhs) const; + bool operator>(const char* pStr) const; + bool operator>(const unsigned char* pStr) const; + bool operator>=(const CHXString& rhs) const; + bool operator>=(const char* pStr) const; + bool operator>=(const unsigned char* pStr) const; + bool operator==(const CHXString& rhs) const; + bool operator==(const char* pStr) const; + bool operator==(const unsigned char* pStr) const; + bool operator!=(const CHXString& rhs) const; + bool operator!=(const char* pStr) const; + bool operator!=(const unsigned char* pStr) const; + bool operator<=(const CHXString& rhs) const; + inline bool operator<=(const char* pStr) const; + inline bool operator<=(const unsigned char* pStr) const; + bool operator<(const CHXString& rhs) const; + inline bool operator<(const char* pStr) const; + inline bool operator<(const unsigned char* pStr) const; + + const CHXString& operator=(const CHXString& rhs); + const CHXString& operator=(char ch); + const CHXString& operator=(const char* pStr); + const CHXString& operator=(const unsigned char* pStr); + + const CHXString& operator+=(const CHXString& rhs); + const CHXString& operator+=(char ch); + const CHXString& operator+=(const char* pStr); + + friend CHXString operator+(const CHXString& strA, + const CHXString& strB); + + friend CHXString operator+(const CHXString& str, char ch); + friend CHXString operator+(char ch , const CHXString& str); + friend CHXString operator+(const CHXString& strA, const char* pStrB); + friend CHXString operator+(const char* pStrA, const CHXString& strB); + + INT32 Compare(const char* pStr) const; + INT32 CompareNoCase(const char* pStr) const; + + void Center(short length); + CHXString Mid(INT32 i, INT32 length) const; + CHXString Mid(INT32 i) const; + CHXString Left(INT32 length) const; + CHXString Right(INT32 length) const; + + ULONG32 CountFields(char delim) const; + CHXString GetNthField(char delim, ULONG32 i, UINT64& state) const; + CHXString NthField(char delim, ULONG32 i) const; + + CHXString SpanIncluding(const char* pCharSet) const; + CHXString SpanExcluding(const char* pCharSet) const; + + void MakeUpper(); + void MakeLower(); + + void TrimRight(); + void TrimLeft(); + + INT32 Find(char ch) const; + INT32 ReverseFind(char ch) const; + HXBOOL FindAndReplace(const char* pSearch , const char* pReplace, + HXBOOL bReplaceAll = FALSE); + + INT32 Find(const char* pStr) const; + + void Format(const char* pFmt, ...); + + void AppendULONG(ULONG32 value); + + void AppendEndOfLine(); + + char* GetBuffer(INT32 minSize); + void ReleaseBuffer(INT32 newSize = -1); + char* GetBufferSetLength(INT32 newSize); + void FreeExtra(); + + INT32 GetAllocLength() const; + INT32 SetMinBufSize(INT32 minSize); + +#if defined(_MACINTOSH) || defined(_MAC_UNIX) + const CHXString& SetFromStr255(const Str255 ); + + const CHXString& AppendFromStr255(const Str255 ); + const CHXString& InsertFromStr255(const Str255 ); + + const CHXString& SetFromIndString(short , short ); + + const CHXString& operator =(FSSpec ); + operator const FSSpec(void); + + HX_RESULT MakeStr255(Str255& ) const; + +#if !defined(_CARBON) && !defined(_MAC_UNIX) + operator Str255* (void); + operator const Str255* (void) const; + operator ConstStr255Param (void) const; +#else + const CHXString& operator =(const FSRef& ); + // ConstStr255Param operator disallowed since + // it's not implemented in Carbon + //operator ConstStr255Param (void) const; + operator const FSRef(void); + + const CHXString& operator =(CFStringRef ); + + HX_RESULT SetFromCFString(CFStringRef , CFStringEncoding ); + + HX_RESULT SetFromHFSUniStr255(const HFSUniStr255& , CFStringEncoding ); + HX_RESULT MakeHFSUniStr255(HFSUniStr255& , CFStringEncoding ) const; +#endif /* !defined(_CARBON) */ + +#endif /* _MACINTOSH */ + +protected: + + void Init(const char* pStr, UINT32 size = UINT_MAX ); + void Nuke(); + void ConcatInPlace(const char* pStr, const UINT32 size); + void EnsureUnique(); // Make sure that m_pRep is not shared + void Release(); + static UINT32 SafeStrlen(const char* ); + +//#define CHXSMEMCHK +#ifdef CHXSMEMCHK + INT32 m_memchkCopied; + INT32 m_memchkChanged; + INT32 m_memchkDataLength; + static INT32 g_memchkCopiedNotChanged; + static INT32 g_memchkCopiedChanged; + static INT32 g_memchkCounter; + static INT32 g_memchkHighCount; + static INT32 g_memchkTotalBytesNotChanged; + char* m_memchkData; + static memchkWhatever Dummy; + + static void memchkLogStats(void); +#endif /* CHXSMEMCHK */ + +private: + static INT32 MinimalGrowth(INT32 currentSize, INT32 sizeNeeded); + static INT32 DoublingGrowth(INT32 currentSize, INT32 sizeNeeded); + + void Append(const char* pStr, INT32 size); + void Grow(INT32 newSize); + + CHXStringRep* m_pRep; + StringGrowthFunc m_pGrowthFunc; +}; + +#if !defined(HELIX_CONFIG_NOSTATICS) +extern const CHXString HXEmptyString; +#else +extern const char* const _g_emptyString; +#define HXEmptyString HXGlobalCHXString::Get(&_g_emptyString) +#endif + +inline +char* CHXStringRep::GetBuffer() +{ + return m_pData; +} + +inline +INT32 CHXStringRep::GetStringSize() const +{ + return m_strSize; +} + +inline +INT32 CHXStringRep::GetBufferSize() const +{ + return m_bufSize; +} + +inline +void CHXStringRep::SetStringSize(INT32 strSize) +{ + HX_ASSERT(strSize >= 0); + HX_ASSERT(strSize < m_bufSize); + HX_ASSERT((size_t)strSize == strlen(m_pData)); + m_strSize = strSize; +} + +inline +bool CHXStringRep::IsShared() const +{ + return (m_refCount > 1); +} + +inline +UINT32 CHXString::GetLength() const +{ + return (m_pRep) ? m_pRep->GetStringSize() : 0; +} + +inline +HXBOOL CHXString::IsEmpty() const +{ + return (GetLength() == 0); +} + +inline +char CHXString::GetAt(INT32 i) const +{ + HX_ASSERT(m_pRep); + HX_ASSERT(i >= 0); + HX_ASSERT(i < m_pRep->GetBufferSize()); + return m_pRep->GetBuffer()[i]; +} + +inline +char CHXString::operator[](short i) const +{ + HX_ASSERT(m_pRep); + HX_ASSERT(i >= 0); + HX_ASSERT(i < m_pRep->GetBufferSize()); + return m_pRep->GetBuffer()[i]; +} + +inline +char CHXString::operator[](unsigned short i) const +{ + HX_ASSERT(m_pRep); + HX_ASSERT((INT32)i < m_pRep->GetBufferSize()); + return m_pRep->GetBuffer()[i]; +} + +inline +char CHXString::operator[](int i) const +{ + HX_ASSERT(m_pRep); + HX_ASSERT(i >= 0); + HX_ASSERT(i < m_pRep->GetBufferSize()); + return m_pRep->GetBuffer()[i]; +} + +inline +char CHXString::operator[](unsigned int i) const +{ + HX_ASSERT(m_pRep); + HX_ASSERT(i < (unsigned int)m_pRep->GetBufferSize()); + return m_pRep->GetBuffer()[i]; +} + +inline +char CHXString::operator[](long i) const +{ + HX_ASSERT(m_pRep); + HX_ASSERT(i >= 0); + HX_ASSERT(i < m_pRep->GetBufferSize()); + return m_pRep->GetBuffer()[i]; +} + +inline +char CHXString::operator[](unsigned long i) const +{ + HX_ASSERT(m_pRep); + HX_ASSERT(i < (unsigned long)m_pRep->GetBufferSize()); + return m_pRep->GetBuffer()[i]; +} + +inline +CHXString::operator const char*() const +{ + return (m_pRep) ? m_pRep->GetBuffer() : (const char*)(&m_pRep); +} + +inline +INT32 CHXString::Compare(const char* pStr) const +{ + return strcmp((const char*)(*this), pStr); +} + +inline +INT32 CHXString::CompareNoCase(const char* pStr) const +{ + return strcasecmp((const char*)(*this), pStr); +} + +inline +bool CHXString::operator>(const char* pStr) const +{ + return (Compare(pStr) > 0); +} + +inline +bool CHXString::operator>(const CHXString& rhs) const +{ + return (*this > ((const char*)rhs)); +} + +inline +bool CHXString::operator>(const unsigned char* pStr) const +{ + return (*this > ((const char*)pStr)); +} + +inline +bool operator>(const char* pA, const CHXString& b) +{ + return (b < pA); +} + +inline +bool operator>(const unsigned char* pA, const CHXString& b) +{ + return (b < pA); +} + +inline +bool CHXString::operator>=(const char* pStr) const +{ + return (Compare(pStr) >= 0); +} + +inline +bool CHXString::operator>=(const CHXString& rhs) const +{ + return (*this >= ((const char*)rhs)); +} + +inline +bool CHXString::operator>=(const unsigned char* pStr) const +{ + return (*this >= ((const char*)pStr)); +} + +inline +bool operator>=(const char* pA, const CHXString& b) +{ + return (b <= pA); +} + +inline +bool operator>=(const unsigned char* pA, const CHXString& b) +{ + return (b <= pA); +} + +inline +bool CHXString::operator==(const char* pStr) const +{ + return (strcmp(((const char*)*this), pStr) == 0); +} + +inline +bool CHXString::operator==(const CHXString& rhs) const +{ + return ((m_pRep == rhs.m_pRep) || + ((GetLength() == rhs.GetLength()) && + (*this == ((const char*)rhs)))); +} + +inline +bool CHXString::operator==(const unsigned char* pStr) const +{ + return (*this == ((const char*)pStr)); +} + +inline +bool operator==(const char* pA, const CHXString& b) +{ + return (b == pA); +} + +inline +bool operator==(const unsigned char* pA, const CHXString& b) +{ + return (b == pA); +} + +inline +bool CHXString::operator!=(const char* pStr) const +{ + return (strcmp(((const char*)*this), pStr) != 0); +} + +inline +bool CHXString::operator!=(const CHXString& rhs) const +{ + return ((m_pRep != rhs.m_pRep) && + ((GetLength() != rhs.GetLength()) || + (*this != ((const char*)rhs)))); +} + +inline +bool CHXString::operator!=(const unsigned char* pStr) const +{ + return (*this != ((const char*)pStr)); +} + +inline +bool operator!=(const char* pA, const CHXString& b) +{ + return (b != pA); +} + +inline +bool operator!=(const unsigned char* pA, const CHXString& b) +{ + return (b != pA); +} + +inline +bool CHXString::operator<=(const char* pStr) const +{ + return (Compare(pStr) <= 0); +} + +inline +bool CHXString::operator<=(const CHXString& rhs) const +{ + return (*this <= ((const char*)rhs)); +} + +inline +bool CHXString::operator<=(const unsigned char* pStr) const +{ + return (*this <= ((const char*)pStr)); +} + +inline +bool operator<=(const char* pA, const CHXString& b) +{ + return (b >= pA); +} + +inline +bool operator<=(const unsigned char* pA, const CHXString& b) +{ + return (b >= pA); +} + +inline +bool CHXString::operator<(const char* pStr) const +{ + return (Compare(pStr) < 0); +} + +inline +bool CHXString::operator<(const CHXString& rhs) const +{ + return (*this < ((const char*)rhs)); +} + +inline +bool CHXString::operator<(const unsigned char* pStr) const +{ + return (*this < ((const char*)pStr)); +} + +inline +bool operator<(const char* pA, const CHXString& b) +{ + return (b > pA); +} + +inline +bool operator<(const unsigned char* pA, const CHXString& b) +{ + return (b > pA); +} + +inline +UINT32 CHXString::SafeStrlen(const char* pStr) +{ + return (pStr) ? strlen(pStr) : 0; +} +#endif /* HXSTRING_H */ diff --git a/amarok/src/engine/helix/helix-sp/helix-include/common/dbgtool/hxassert.h b/amarok/src/engine/helix/helix-sp/helix-include/common/dbgtool/hxassert.h new file mode 100644 index 00000000..dd841fe2 --- /dev/null +++ b/amarok/src/engine/helix/helix-sp/helix-include/common/dbgtool/hxassert.h @@ -0,0 +1,727 @@ + +/* + * + * This software is released under the provisions of the GPL version 2. + * see file "COPYING". If that file is not available, the full statement + * of the license can be found at + * + * http://www.fsf.org/licensing/licenses/gpl.txt + * + * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved. + * + */ + +///////////////////////////////////////////////////////////////////////////// +// HXASSERT.H +// +// Debugging support header. +// +// HX_ASSERT() - asserts an expression is TRUE. Compiles to no-ops in +// retail builds. Provides message box or other UI when +// expression fails. +// +// HX_ASSERT_VALID_PTR() - asserts that a pointer is valid. Performs more +// rigid verification specifically appropriate for pointers. +// +// HX_VERIFY() - verifies an expression is TRUE. Expression or code DOES NOT +// compile away in retail builds, but UI of failure is removed. +// In debug builds provides message box or other UI when +// expression fails. +// +// HX_TRACE() - Similar to DEBUGPRINTF() but no buffer is required. +// Compiles to no-ops in retail builds. +// + +#ifndef _HXASSERT_H_ +#define _HXASSERT_H_ + +#include "hlxclib/assert.h" + +#ifndef ASSERT +#if defined (DEBUG) || defined (_DEBUG) +#if defined (_OSF1) && defined (_NATIVE_COMPILER) +# define ASSERT(x) assert(((long)(x)) != 0L) +#else +# define ASSERT(x) assert(x) +#endif +#else +# define ASSERT(x) /* x */ +#endif /* DEBUG */ +#endif /* ndef ASSERT */ + +#include "hlxclib/limits.h" +#include "hxtypes.h" +#include "hxresult.h" // for HX_RESULT +#include "hlxclib/stdio.h" // for sprintf + +#ifdef _SUN +#include <stddef.h> // for size_t +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined (_SOLARIS) || defined (_IRIX) || defined (_DECALPHA) +#ifndef __inline +#define __inline inline +#endif +#endif + +#if defined (_OSF1) && defined (_NATIVE_COMPILER) +#if defined __cplusplus +#define __inline inline +#else +#define __inline static +#endif /* __cplusplus */ +#endif + +#ifdef _HPUX +#if defined __cplusplus +#define __inline inline +#else +#define __inline +#endif /* __cplusplus */ +#endif + +#if defined _AIX +#if defined __cplusplus +#define __inline inline +#else +#define __inline +#endif /* __cplusplus */ +#endif /* _AIX */ + +#ifdef _UNIX +#include <signal.h> /* For kill() */ +#include <unistd.h> /* For getpid() */ +#include <stdio.h> +void HXUnixDebugBreak(); +#endif + + +///////////////////////////////////////////////////////////////////////////// +// Diagnostic support + +// For _MAX_PATH +#if defined ( _MACINTOSH ) +#include "platform/mac/maclibrary.h" +#elif defined (_UNIX) +#include <stdlib.h> +#if !defined(_VXWORKS) +#include <sys/param.h> +#endif +#define _MAX_PATH MAXPATHLEN +#elif defined (_WINDOWS) +#include <stdlib.h> +#endif + +#ifdef _SYMBIAN +# include <unistd.h> +# define _MAX_PATH MAXPATHLEN +#endif + +#ifdef _OPENWAVE +#ifndef _MAX_PATH +#define _MAX_PATH 256 +#endif +#endif + +#if defined (DEBUG) || defined (_DEBUG) + +#ifdef _MACINTOSH +# define MAX_TRACE_OUTPUT 255 +#else +# ifdef _UNIX +# define MAX_TRACE_OUTPUT 255 +# elif _WIN16 +# define MAX_TRACE_OUTPUT 255 +# elif _SYMBIAN +# define MAX_TRACE_OUTPUT 255 +# else +# define MAX_TRACE_OUTPUT (_MAX_PATH*2 + 20) +# endif +#endif + + +///////////////////////////////////////////////////////////////////////////// +// +// HXDebugOptionEnabled: +// Determine if the given debug option is enabled. +// A lookup is done to the registry key, and if it's present +// and set to '1', TRUE is returned otherwise FALSE +// Similar to HXWantTraceMessages, except not specific to one option. +#ifdef _WIN16 +// The STDMETHODCALLTYPE includes the _export keyword, even though this is +// a static, not dll, library. Seems to work on win32 ok, but not a win16 +// .exe. rpapp would see this as duplicate symbol, until the _export was +// removed, and all the libraries that rpapp linked with rebuilt. There +// may be a define for STDMETHODCALLTYPE without the _export that should be +// used here. XXXTW. april 98. +HXBOOL far _cdecl HXDebugOptionEnabled(const char* szOption); +#else +HXBOOL STDMETHODCALLTYPE HXDebugOptionEnabled(const char* szOption); +#endif + +///////////////////////////////////////////////////////////////////////////// +// +// HXWantTraceMessages: +// Helper function used to determine if the system has asked for trace +// messages. +// +HXBOOL STDMETHODCALLTYPE HXWantTraceMessages(); + +///////////////////////////////////////////////////////////////////////////// +// +// HXOutputDebugString: +// Helper function used by DEBUGOUTSTR(). This is better than +// OutputDebugString, because it will check to see if output +// tracing is turned off in the registry. This prevents the massive +// slew of output messages. +// +void STDMETHODCALLTYPE HXOutputDebugString(const char* pString); + +///////////////////////////////////////////////////////////////////////////// +// +// HXTrace: Helper function used by HX_TRACE() +// +void STDMETHODVCALLTYPE HXTrace(const char* pszFormat, ...); + + +///////////////////////////////////////////////////////////////////////////// +// +// HXAssertFailedLine: Helper function used by HX_ASSERT() +// +#ifdef _WIN16 +// The STDMETHODCALLTYPE includes the _export keyword, even though this is +// a static, not dll, library. Seems to work on win32 ok, but not a win16 +// .exe. rpapp would see this as duplicate symbol, until the _export was +// removed, and all the libraries that rpapp linked with rebuilt. There +// may be a define for STDMETHODCALLTYPE without the _export that should be +// used here. XXXTW. april 98. +HXBOOL far _cdecl HXAssertFailedLine(const char* pszExpression, const char* pszFileName, int nLine); +#else +HXBOOL STDMETHODCALLTYPE HXAssertFailedLine(const char* pszExpression, const char* pszFileName, int nLine); +#endif + +///////////////////////////////////////////////////////////////////////////// +// +// HXAssertValidPointer, HXIsValidAddress: Helper functions used by +// HX_ASSERT_VALID_PTR() +// +void STDMETHODCALLTYPE HXAssertValidPointer(const void* pVoid, const char* pszFileName, int nLine); +#ifdef __cplusplus +#ifdef _WIN16 + // see note above on the problem with STDMETHODCALLTYPE on win16 + HXBOOL far _cdecl HXIsValidAddress(const void* lp, ULONG32 nBytes = 1, HXBOOL bReadWrite = TRUE); +#else + HXBOOL STDMETHODCALLTYPE HXIsValidAddress(const void* lp, ULONG32 nBytes = 1, HXBOOL bReadWrite = TRUE); +#endif +#else +#ifdef _WIN16 + // see note above on the problem with STDMETHODCALLTYPE on win16 + HXBOOL far _cdecl HXIsValidAddress(const void* lp, ULONG32 nBytes, HXBOOL bReadWrite); +#else + HXBOOL STDMETHODCALLTYPE HXIsValidAddress(const void* lp, ULONG32 nBytes, HXBOOL bReadWrite); +#endif +#endif + +#ifdef _WIN16 +HXBOOL far _cdecl HXIsValidString(const char* lpsz, int nLength); +#else +HXBOOL STDMETHODCALLTYPE HXIsValidString(const char* lpsz, int nLength); +#endif + +///////////////////////////////////////////////////////////////////////////// +// +// HXDebugBreak: used to break into debugger at critical times +// +#ifndef HXDebugBreak +// by default, debug break is asm int 3, or a call to DebugBreak, or nothing + +#if defined(_WINDOWS) +#if !defined(_M_IX86) +#include "windows.h" +#define HXDebugBreak() DebugBreak() +#else +#define HXDebugBreak() _asm { int 3 } +#endif // _M_ALPHA +#elif defined( _MACINTOSH ) +#define HXDebugBreak() Debugger() +#elif defined(_OPENWAVE) +#if defined(_OPENWAVE_SIMULATOR) // windows app... +#define HXDebugBreak() _asm { int 3 } +#else +void HXDebugBreak(); +#endif +#elif defined(_SYMBIAN) +#if defined(__WINS__) +#define HXDebugBreak() _asm { int 3 } +#else +void HXDebugBreak(); +#endif //_SYMBIAN +#elif defined(_UNIX) +void HXDebugBreak(); +#elif defined(_VXWORKS) +#include <taskLib.h> +#define HXDebugBreak() (taskSuspend(taskIdSelf())) + +#endif // end of#if defined(_WIN32) || defined(_WINDOWS) + +#endif // end of#ifndef HXDebugBreak + +#ifdef _UNIX +#include "signal.h" +#include "stdlib.h" + +#define HX_ENABLE_JIT_DEBUGGING() \ +do { \ + signal (SIGSEGV, (void (*)(int))HXDebugBreak); \ + signal (SIGBUS, (void (*)(int))HXDebugBreak); \ + signal (SIGFPE, (void (*)(int))HXDebugBreak); \ + if (!getenv("PROCESS_NAME")) \ + { \ + char *progname = new char[strlen(argv[0]) + 30]; \ + sprintf(progname, "PROCESS_NAME=%s", argv[0]); /* Flawfinder: ignore */ \ + putenv(progname); \ + } \ +} while (0); +#else +#define HX_ENABLE_JIT_DEBUGGING() +#endif + +///////////////////////////////////////////////////////////////////////////// +// +// HXAbort: used to shut down the application at critical times +// +#ifndef HXAbort + +#if (defined(_WIN32) || defined(_WINDOWS)) && !defined(WIN32_PLATFORM_PSPC) +# define HXAbort() abort() +#elif defined(WIN32_PLATFORM_PSPC) +# define HXAbort() exit(1) +#elif defined(_SYMBIAN) +# define HXAbort() exit(1) +#elif defined(_OPENWAVE) +// XXXSAB is this right?? +# define HXAbort() exit(1) +#elif defined ( _MACINTOSH ) +# define HXAbort() DebugStr("\pHXAbort: Please exit this program.") +#elif defined ( _UNIX ) +# define HXAbort() printf("\npnabort: Please exit this program.\n") +#endif // end of#if defined(_WIN32) || defined(_WINDOWS) + +#endif // end of#ifndef HXAbort + + +///////////////////////////////////////////////////////////////////////////// +// +// HX_TRACE: see above. +// +#define HX_TRACE ::HXTrace + +///////////////////////////////////////////////////////////////////////////// +// +// HX_ASSERT: see above. +// +#define HX_ASSERT(f) \ + do \ + { \ + if (!(f) && HXAssertFailedLine(#f, __FILE__, __LINE__)) \ + HXDebugBreak(); \ + } while (0) \ + + +#define REQUIRE_REPORT(targ,file,line) HXAssertFailedLine(targ,file,line) + + + + +///////////////////////////////////////////////////////////////////////////// +// +// Macros Defined for logging messges in the player. +// +// STARTLOGSINK: Query for an IHXErrorSink +// +// LOGINFO: Report the error to the error sink +// +// STOPLOGSINK: Shutdown and Release the error sink. +// +///////////////////////////////////////////////////////////////////////////// +#ifndef _GOLD + +#ifndef _INTERFACE +#define _INTERFACE struct +#endif + +typedef _INTERFACE IHXErrorMessages IHXErrorMessages; + + const char* FileAndLine(const char* pFile, UINT32 ulLine); + HX_RESULT HXLogf(IHXErrorMessages *pErr, UINT8 sev, HX_RESULT err, UINT32 usr, const char* pURL, const char* pUsrStr, ...); + HX_RESULT HXLog(IHXErrorMessages *pErr, UINT8 sev, HX_RESULT err, UINT32 usr, const char* pStr, const char* pURL); + +#define THIS_LINE() FileAndLine(__FILE__, __LINE__) + +#define HX_STARTLOG(__IUnknown, __ErrorMessages) \ + __IUnknown->QueryInterface(IID_IHXErrorMessages, (void **)&__ErrorMessages); \ + HX_ASSERT(__ErrorMessages != NULL && "Must have IHXErrorMessages interface to start logging") + +#define HX_LOG HXLog +#define HX_LOGF HXLogf + +#define HX_STOPLOG(__ErrorMessages) HX_RELEASE(__ErrorMessages) + +#else // _GOLD +#define HX_STARTLOG(x,y) (void)0 +#define HX_LOG 1 ? (void)0 : HXLog +#define HX_LOGF 1 ? (void)0 : HXLogf +#define HX_STOPLOG(x) (void)0 +#endif + +///////////////////////////////////////////////////////////////////////////// +// +// HX_SAFESIZE_T +// +#define HX_SAFESIZE_T(f) \ + ( ((ULONG32)(f) <= (size_t)-1)?((size_t)(f)): \ + (HXAssertFailedLine("size_t overflow",__FILE__, __LINE__), (size_t)(f)) ) \ + +///////////////////////////////////////////////////////////////////////////// +// +// HX_SAFEINT +// +#ifndef _VXWORKS +#define HX_SAFEINT(f) \ + ( ((LONG32)(f) <= LONG_MAX && ((LONG32)(f)) >= ((LONG32)(LONG_MIN)))?((int)(f)): \ + (HXAssertFailedLine("Integer Size Overflow",__FILE__, __LINE__), (int)(f)) ) \ + +#else + +#ifdef __cplusplus +extern "C" +#endif +int safe_int_func_call(LONG32 f); +#define HX_SAFEINT(f) safe_int_func_call((LONG32)(f)) +#endif + +#define HX_SAFEUINT(f) \ + ( ((ULONG32)(f) <= ULONG_MAX && (LONG32)(f) >= 0)?((unsigned int)(f)): \ + (HXAssertFailedLine("Unsigned Integer Size Overflow",__FILE__, __LINE__), (unsigned int)(f)) ) \ + +#define HX_SAFEINT16(f) \ + ( ((LONG32)(f) <= SHRT_MAX && (LONG32)(f) >= SHRT_MIN)?((short)(f)): \ + (HXAssertFailedLine("Short Integer Size Overflow",__FILE__, __LINE__), (short)(f)) ) \ + +#define HX_SAFEUINT16(f) \ + ( ((ULONG32)(f) <= USHRT_MAX && (LONG32)(f) >= 0)?((unsigned short)(f)): \ + (HXAssertFailedLine("Unsigned Short Integer Size Overflow",__FILE__, __LINE__), (unsigned short)(f)) ) \ + +#define HX_SAFEINT8(f) \ + ( ((LONG32)(f) <= SCHAR_MAX && (LONG32)(f) >= SCHAR_MIN)?((char)(f)): \ + (HXAssertFailedLine("Signed Char Size Overflow",__FILE__, __LINE__), (char)(f)) ) \ + +#define HX_SAFEUINT8(f) \ + ( ((ULONG32)(f) <= UCHAR_MAX && (LONG32)(f) >= 0)?((unsigned char)(f)): \ + (HXAssertFailedLine("Unsigned Char Size Overflow",__FILE__, __LINE__), (unsigned char)(f)) ) \ + + + + +///////////////////////////////////////////////////////////////////////////// +// +// HX_SAFE_VOID2HANDLE +// +#if defined(_WINDOWS) + +#ifdef _WIN32 +#define HX_SAFE_VOID2HANDLE(f) ((HANDLE)(f)) +#else // !_WIN23 +#define HX_SAFE_VOID2HANDLE(f) ((HANDLE)LOWORD(f)) +// this doesn't work most of the time since the assignment of a handle (near *) to a +// void * sets the high word to the SS. +// ( ( (0xFFFF0000 & ((ULONG32)(f))) == 0)?((HANDLE)LOWORD(f)): +// (HXAssertFailedLine("HANDLE HIWORD NOT NULL",__FILE__, __LINE__), (HANDLE)LOWORD(f)) ) + +#endif + +#elif defined ( __MWERKS__ ) +#define HX_SAFE_VOID2HANDLE(f) (f) +#elif defined ( _UNIX ) +#define HX_SAFE_VOID2HANDLE(f) (f) +#endif // end of#if defined(_WIN32) || defined(_WINDOWS) + +///////////////////////////////////////////////////////////////////////////// +// +// HX_VERIFY: see above. +// +#define HX_VERIFY(f) HX_ASSERT(f) + +///////////////////////////////////////////////////////////////////////////// +// +// HX_ASSERT_VALID_PTR: see above. +// +#define HX_ASSERT_VALID_PTR(pOb) (::HXAssertValidPointer((const void*)pOb, __FILE__, __LINE__)) +#define HX_ASSERT_VALID_READ_PTR(pOb) HX_ASSERT(::HXIsValidAddress(pOb, 1, FALSE)) + +///////////////////////////////////////////////////////////////////////////// +// +// _SAFESTRING: Similar to HX_TRACE() except that it assume you are +// using a single format string, with a single parameter string, this will +// ensure that the length of the resulting format is less than the maximum +// trace string, and gracefully handle the situation... +// +#define HX_TRACE_SAFESTRING(f,s)\ + if ((strlen(f) + strlen(s)) < (size_t)MAX_TRACE_OUTPUT)\ + {\ + HX_TRACE(f,s);\ + }\ + else\ + {\ + HX_TRACE(f,"Some really big URL");\ + }\ + +#else // _DEBUG + +#ifndef _DEBUG + +#ifndef HX_ENABLE_JIT_DEBUGGING +#define HX_ENABLE_JIT_DEBUGGING() +#endif + +#ifdef HXAbort +#undef HXAbort +#endif // end of#ifdef HXAbort + +#define HXAbort() + +#endif // _DEBUG + +#ifndef _DEBUG + +#ifdef HXDebugBreak +#undef HXDebugBreak +#endif // end of#ifdef HXDebugBreak + +#define HXDebugBreak() + +#endif // _DEBUG || DEBUG + +///////////////////////////////////////////////////////////////////////////// +// +// HX_SAFEINT +// +#define HX_SAFEINT(f) ((int)(f)) +#define HX_SAFEUINT(f) ((unsigned int)(f)) +#define HX_SAFEINT16(f) ((short)(f)) +#define HX_SAFEUINT16(f) ((unsigned short)(f)) +#define HX_SAFEINT8(f) ((char)(f)) +#define HX_SAFEUINT8(f) ((unsigned char)(f)) + +#define HX_ASSERT(f) ((void)0) +#define HX_SAFESIZE_T(f) ((size_t)(f)) +#define HX_SAFEINT(f) ((int)(f)) +#define HX_SAFE_VOID2HANDLE(f) ((HANDLE)(ULONG32)(f)) +#define HX_ASSERT_VALID_PTR(pOb) ((void)0) +#define HX_ASSERT_VALID_READ_PTR(pOb) ((void)0) +#define HXOutputDebugString(f) ((void)0) + +#define REQUIRE_REPORT(targ,file,line) ((int)0) + +#if defined (_MACINTOSH) +// this is the proper release version of HX_VERIFY that preserves the syntactic +// role of the debug version; it's necessary to quiet warnings on the Mac +#define HX_VERIFY(f) do { if (!(f)) {} } while (0) +#else +#define HX_VERIFY(f) ((void)(f)) +#endif + +#if defined (_WINDOWS) +__inline void __cdecl HXTrace(const char* x, ...) { } +#else +static __inline void HXTrace(const char* /* x */, ...) {} +#endif + +#define HX_TRACE 1 ? (void)0 : ::HXTrace +#define HX_TRACE_SAFESTRING HX_TRACE + +#endif // !_DEBUG + +#ifdef __cplusplus +} // end extern "C" +#endif + +#ifdef __cplusplus +///////////////////////////////////////////////////////////////////////////// +// +// Helper for loginfo for LOGINFO() +// +#ifndef _INTERFACE +#define _INTERFACE struct +#endif + +typedef _INTERFACE IHXErrorSink IHXErrorSink; + +class LogInfo +{ + public: + static void STDMETHODCALLTYPE FileandLine(const char* file, int nLine); + static void STDMETHODCALLTYPE Report(IHXErrorSink* mySink, const char* pUserInfo, ...); + private: + static char m_pFile[_MAX_PATH]; /* Flawfinder: ignore */ +}; + +#endif + + +///////////////////////////////////////////////////////////////////////////// +// CHECK/REQUIRE MACROS +// +// These macros are always valid (debug and release builds) +// +// + +// +// REQUIRE and REQUIRE_ACTION _always_ emit code for the goto if the statement is false +// +// REQUIRE_REPORT only generates code in Debug builds +// +// The do {} while (0) construct ensures this is legal wherever a statement is legal +// + +#define CHECK(stmt) HX_ASSERT(stmt) + +#define REQUIRE_VOID_RETURN(stmt) \ + do { if ((stmt) == 0) { if (REQUIRE_REPORT(#stmt,__FILE__,__LINE__)) HXDebugBreak(); return; } } while (0) +#define REQUIRE_RETURN(stmt,returned) \ + do { if ((stmt) == 0) { if (REQUIRE_REPORT(#stmt,__FILE__,__LINE__)) HXDebugBreak(); return (returned); } } while (0) +#define REQUIRE_VOID_RETURN_QUIET(stmt) \ + do { if ((stmt) == 0) { return; } } while (0) +#define REQUIRE_RETURN_QUIET(stmt,returned) \ + do { if ((stmt) == 0) { return (returned); } } while (0) +#define REQUIRE(stmt,target) \ + do { if ((stmt) == 0) { REQUIRE_REPORT(#target,__FILE__,__LINE__); goto target; } } while (0) +#define REQUIRE_ACTION(stmt,target,action) \ + do { if ((stmt) == 0) { REQUIRE_REPORT(#target,__FILE__,__LINE__); {{action;} goto target;} } } while (0) +#define REQUIRE_QUIET(stmt,target) \ + do { if ((stmt) == 0) goto target; } while (0) +#define REQUIRE_ACTION_QUIET(stmt,target,action) \ + do { if ((stmt) == 0) {{action;} goto target;} } while (0) +#define PRE_REQUIRE_RETURN(stmt,returned) \ + REQUIRE_RETURN(stmt,returned) +#define PRE_REQUIRE_VOID_RETURN(stmt) \ + REQUIRE_VOID_RETURN(stmt) +#define POST_REQUIRE_RETURN(stmt,returned) \ + REQUIRE_RETURN(stmt,returned) +#define POST_REQUIRE_VOID_RETURN(stmt) \ + REQUIRE_VOID_RETURN(stmt) + +#define REQUIRE_SUCCESS_RETURN_QUIET(expr) \ + do { register HX_RESULT const res = expr; if (FAILED (res)) return res; } while (0) +#define REQUIRE_SUCCESS_RETURN(expr) \ + do { register HX_RESULT const res = expr; if (FAILED (res)) { REQUIRE_REPORT("False condition, Aborting...",__FILE__,__LINE__); return res; } } while (0) + +// +// REQUIRE_SUCCESS reports the error if an expected result failed +// Ideally, this should report the status value as well +// + +#define CHECK_SUCCESS(stat) HX_ASSERT(((unsigned long)(stat)>>31) == 0) + +#define REQUIRE_SUCCESS(stat,target) \ + do { if (((unsigned long)(stat)>>31) != 0) { REQUIRE_REPORT(#target,__FILE__,__LINE__); goto target; } } while (0) +#define REQUIRE_SUCCESS_ACTION(stat,target,action) \ + do { if (((unsigned long)(stat)>>31) != 0) { REQUIRE_REPORT(#target,__FILE__,__LINE__); {{action;} goto target;} } } while (0) +#define REQUIRE_SUCCESS_QUIET(stat,target) \ + do { if (((unsigned long)(stat)>>31) != 0) goto target; } while (0) +#define REQUIRE_SUCCESS_ACTION_QUIET(stat,target,action) \ + do { if (((unsigned long)(stat)>>31) != 0) {{action;} goto target;} } while (0) + +// +// REQUIRE_NOERR reports the error if the error value is non-zero +// Ideally, this should report the error value as well +// + +#define CHECK_NOERR(err) HX_ASSERT((err) == 0) + +#define REQUIRE_NOERR_RETURN(err,returned) \ + do { if ((err) != 0) { REQUIRE_REPORT("Toolbox error, Aborting...",__FILE__,__LINE__); return (returned); } } while (0) +#define REQUIRE_NOERR(err,target) \ + do { if ((err) != 0) { REQUIRE_REPORT(#target,__FILE__,__LINE__); goto target; } } while (0) +#define REQUIRE_NOERR_ACTION(err,target,action) \ + do { if ((err) != 0) { REQUIRE_REPORT(#target,__FILE__,__LINE__); {{action;} goto target;} } } while (0) +#define REQUIRE_NOERR_QUIET(err,target) \ + do { if ((err) != 0) goto target; } while (0) +#define REQUIRE_NOERR_ACTION_QUIET(err,target,action) \ + do { if ((err) != 0) {{action;} goto target;} } while (0) + +// +// REQUIRE_NONNULL reports the error if the ptr value is null +// Ideally, this should report the error value as well +// +#define CHECK_NONNULL(ptr) HX_ASSERT((ptr) != 0L) +#define CHECK_NULL(ptr) HX_ASSERT((ptr) == 0L) + +#define REQUIRE_NONNULL_VOID_RETURN(ptr) \ + do { if ((ptr) == 0L) { REQUIRE_REPORT(#ptr" is nil, Aborting...",__FILE__,__LINE__); return; } } while (0) +#define REQUIRE_NONNULL_RETURN(ptr,returned) \ + do { if ((ptr) == 0L) { REQUIRE_REPORT(#ptr" is nil, Aborting...",__FILE__,__LINE__); return (returned); } } while (0) +#define REQUIRE_NONNULL(ptr,target) \ + do { if ((ptr) == 0L) { REQUIRE_REPORT(#target,__FILE__,__LINE__); goto target; } } while (0) +#define REQUIRE_NONNULL_ACTION(ptr,target,action) \ + do { if ((ptr) == 0L) { REQUIRE_REPORT(#target,__FILE__,__LINE__); {{action;} goto target;} } } while (0) +#define REQUIRE_NONNULL_QUIET(ptr,target) \ + do { if ((ptr) == 0L) goto target; } while (0) +#define REQUIRE_NONNULL_ACTION_QUIET(ptr,target,action) \ + do { if ((ptr) == 0L) {{action;} goto target;} } while (0) +// lower case versions make source code more readable + +#if defined(_CARBON) || defined(_MAC_MACHO) +#undef check +#undef require +#undef require_action +#undef require_quiet +#undef check_noerr +#undef require_action_quiet +#undef require_noerr +#undef require_noerr_action +#undef require_noerr_quiet +#undef require_noerr_action_quiet +#endif + +#define check(stmt) CHECK(stmt) + +#define require_void_return(stmt) REQUIRE_VOID_RETURN(stmt) +#define require_return_void(stmt) REQUIRE_VOID_RETURN(stmt) +#define require_return(stmt,returned) REQUIRE_RETURN(stmt,returned) +#define require(stmt,target) REQUIRE(stmt,target) +#define require_action(stmt,target,action) REQUIRE_ACTION(stmt,target,action) +#define require_quiet(stmt,target) REQUIRE_QUIET(stmt,target) +#define require_action_quiet(stmt,target,action) REQUIRE_ACTION_QUIET(stmt,target,action) + +#define check_success(stat) CHECK_SUCCESS(stat) + +#define require_success(stat,target) REQUIRE_SUCCESS(stat,target) +#define require_success_action(stat,target,action) REQUIRE_SUCCESS_ACTION(stat,target,action) +#define require_success_quiet(stat,target) REQUIRE_SUCCESS_QUIET(stat,target) +#define require_success_action_quiet(stat,target,action) REQUIRE_SUCCESS_ACTION_QUIET(stat,target,action) + +#define check_noerr(err) CHECK_NOERR(err) + +#define require_noerr_return(err,returned) REQUIRE_NOERR_RETURN(err,returned) +#define require_noerr(err,target) REQUIRE_NOERR(err,target) +#define require_noerr_action(err,target,action) REQUIRE_NOERR_ACTION(err,target,action) +#define require_noerr_quiet(err,target) REQUIRE_NOERR_QUIET(err,target) +#define require_noerr_action_quiet(err,target,action) REQUIRE_NOERR_ACTION_QUIET(err,target,action) + +#define check_nonnull(ptr) CHECK_NONNULL(ptr) +#define check_null(ptr) CHECK_NULL(ptr) + +#define require_nonnull_void_return(ptr) REQUIRE_NONNULL_VOID_RETURN(ptr) +#define require_nonnull_return(ptr,returned) REQUIRE_NONNULL_RETURN(ptr,returned) +#define require_nonnull(ptr,target) REQUIRE_NONNULL(ptr,target) +#define require_nonnull_action(ptr,target,action) REQUIRE_NONNULL_ACTION(ptr,target,action) +#define require_nonnull_quiet(ptr,target) REQUIRE_NONNULL_QUIET(ptr,target) +#define require_nonnull_action_quiet(ptr,target,action) REQUIRE_NONNULL_ACTION_QUIET(ptr,target,action) + + +#endif // !_HXASSERT_H_ diff --git a/amarok/src/engine/helix/helix-sp/helix-include/common/include/atomicbase.h b/amarok/src/engine/helix/helix-sp/helix-include/common/include/atomicbase.h new file mode 100644 index 00000000..ba2748f7 --- /dev/null +++ b/amarok/src/engine/helix/helix-sp/helix-include/common/include/atomicbase.h @@ -0,0 +1,1566 @@ + +/* + * + * This software is released under the provisions of the GPL version 2. + * see file "COPYING". If that file is not available, the full statement + * of the license can be found at + * + * http://www.fsf.org/licensing/licenses/gpl.txt + * + * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved. + * + */ + + +/*********************************************************************** + * THIS CODE IS HIGHLY CRITICAL TO THE SERVER'S STABILITY!!! + * DO NOT MAKE CHANGES TO THE ATOMIC OPERATORS OR TO THE + * MUTEX CODE WITHOUT A SERVER TEAM CODE-REVIEW! (dev@helix-server) + */ + + +/**************************************************************************** + * $Id: atomicbase.h 587223 2006-09-21 23:14:40Z aoliveira $ + * + * atomicbase.h - Defines several atomic operations + * + * See server/common/util/pub/servatomic.h for broader platform support. + * Also conditionally overrides InterlockedIncrement/Decrement + * via USE_HX_ATOMIC_INTERLOCKED_INC_DEC. + * + * + *********************************************************************** + * + * Defines: + * + * void HXAtomicIncINT32(INT32* p) -- Increment *p + * void HXAtomicDecINT32(INT32* p) -- Decrement *p + * void HXAtomicAddINT32(INT32* p, INT32 n) -- Increment *p by n + * void HXAtomicSubINT32(INT32* p, INT32 n) -- Decrement *p by n + * INT32 HXAtomicIncRetINT32(INT32* p) -- Increment *p and return it + * INT32 HXAtomicDecRetINT32(INT32* p) -- Decrement *p and return it + * INT32 HXAtomicAddRetINT32(INT32* p, INT32 n)-- Increment *p by n, return it + * INT32 HXAtomicSubRetINT32(INT32* p, INT32 n)-- Increment *p by n, return it + * + * + * There are also UINT32 versions: + * + * void HXAtomicIncUINT32(UINT32* p) + * void HXAtomicDecUINT32(UINT32* p) + * void HXAtomicAddUINT32(UINT32* p, UINT32 n) + * void HXAtomicSubUINT32(UINT32* p, UINT32 n) + * UINT32 HXAtomicIncRetUINT32(UINT32* p) + * UINT32 HXAtomicDecRetUINT32(UINT32* p) + * UINT32 HXAtomicAddRetUINT32(UINT32* p, UINT32 n) + * UINT32 HXAtomicSubRetUINT32(UINT32* p, UINT32 n) + * + *********************************************************************** + * + * TODO: + * Add INT64 versions + * Obsolete the 0x80000000-based Solaris implementation entirely. + * + ***********************************************************************/ +#ifndef _ATOMICBASE_H_ +#define _ATOMICBASE_H_ + + +/*********************************************************************** + * Sun Solaris / SPARC (Native compiler) + * + * Implementation Notes: + * This uses inline assembly from server/common/util/platform/solaris/atomicops.il + * Note: Sparc/gcc is in include/atomicbase.h + */ +#if defined (_SOLARIS) && !defined (__GNUC__) + +#if defined(__cplusplus) +extern "C" { +#endif + //UINT32 _HXAtomicIncRetUINT32 (UINT32* pNum); + //UINT32 _HXAtomicDecRetUINT32 (UINT32* pNum); + UINT32 _HXAtomicAddRetUINT32 (UINT32* pNum, UINT32 ulNum); + UINT32 _HXAtomicSubRetUINT32 (UINT32* pNum, UINT32 ulNum); +#if defined(__cplusplus) +} +#endif + + +#define HXAtomicIncUINT32(p) _HXAtomicAddRetUINT32((p),(UINT32)1) +#define HXAtomicDecUINT32(p) _HXAtomicSubRetUINT32((p),(UINT32)1) +#define HXAtomicIncRetUINT32(p) _HXAtomicAddRetUINT32((p),(UINT32)1) +#define HXAtomicDecRetUINT32(p) _HXAtomicSubRetUINT32((p),(UINT32)1) +#define HXAtomicAddUINT32(p,n) _HXAtomicAddRetUINT32((p),(n)) +#define HXAtomicSubUINT32(p,n) _HXAtomicSubRetUINT32((p),(n)) +#define HXAtomicAddRetUINT32(p,n) _HXAtomicAddRetUINT32((p),(n)) +#define HXAtomicSubRetUINT32(p,n) _HXAtomicSubRetUINT32((p),(n)) + +inline void HXAtomicIncINT32(INT32* p) { HXAtomicIncUINT32((UINT32*)p); } +inline void HXAtomicDecINT32(INT32* p) { HXAtomicDecUINT32((UINT32*)p); } +inline void HXAtomicAddINT32(INT32* p, INT32 n) { HXAtomicAddUINT32((UINT32*)p, (UINT32)n); } +inline void HXAtomicSubINT32(INT32* p, INT32 n) { HXAtomicSubUINT32((UINT32*)p, (UINT32)n); } +inline INT32 HXAtomicIncRetINT32(INT32* p) { return HXAtomicIncRetUINT32((UINT32*)p); } +inline INT32 HXAtomicDecRetINT32(INT32* p) { return HXAtomicDecRetUINT32((UINT32*)p); } +inline INT32 HXAtomicAddRetINT32(INT32* p, INT32 n) { return HXAtomicAddRetUINT32((UINT32*)p, (UINT32)n); } +inline INT32 HXAtomicSubRetINT32(INT32* p, INT32 n) { return HXAtomicSubRetUINT32((UINT32*)p, (UINT32)n); } + + + +/*********************************************************************** + * Sun Solaris / SPARC (gcc) + * + * Implementation Notes: + * The sparc method of pipelining and use of "delay slots" requires + * the nop's. Be extra careful modifying these routines! + * + * This implementation sacrifices being able to store the value + * 0x800000000 in the INT32 value, which is a special "busy" marker value. + * Since these are intended for use primarily with AddRef/Release and + * resource usage counters, this should be acceptable for now. If a counter + * is incremented to the point it would conflict with the flag, it is + * incremented one more to hop over it. The same in reverse for decrement. + * This is far from ideal, however... See the inline-assembly file + * server/common/util/platform/solaris/mutex_setbit.il for *much* + * better implementations using newer sparc assembly operators. + * + * Basic design of the flag-based implementation: + * 1. Load a register with 0x80000000 + * 2. _atomically_ swap it with the INT32 (critical!) + * 3. Compare what we got with 0x80000000 + * 4. Branch if equal to #2 + * 5. Increment (or decrement) the result + * 6. Compare to 0x80000000 + * 7. Branch if equal to #5 + * 8. Save the new value to the INT32's location in memory + * 9. Return new INT32 result if required + * + * This implementation primarily exists due to limitations in the ancient + * version of gcc we used to use on Solaris (2.7.2.3), and more modern + * gcc's can probably handle assembly more like what's used in Sun's + * Native compiler version. + * + */ +#elif defined (__sparc__) && defined (__GNUC__) + +/* Increment by 1 */ +inline void +HXAtomicIncUINT32(UINT32* pNum) +{ + __asm__ __volatile__(\ +"1: swap [%0], %2; ! Swap *pNum and %2\n" +" nop; ! delay slot...\n" +" cmp %2, %1; ! Is someone else using pNum?\n" +" be 1b; ! If so, retry...\n" +" nop; ! delay slot...yawn\n" +"2: inc %2; ! Increment %2\n" +" cmp %2, %1; ! check for overflow\n" +" be 2b; ! if so, inc again\n" +" nop; ! but this means a delay, sigh\n" +" st %2, [%0]; ! Save new value into *pNum\n" + : /* no output */ + : "r" (pNum), "r" (0x80000000), "r" (0x80000000) + : "cc", "memory" + ); +} + +/* Decrement by 1 */ +inline void +HXAtomicDecUINT32(UINT32* pNum) +{ + __asm__ __volatile__( +"1: swap [%0], %2; ! Swap *pNum and %2\n" +" nop; ! delay slot...\n" +" cmp %2, %1; ! Is someone else using pNum?\n" +" be 1b; ! If so, retry...\n" +" nop; ! delay slot...yawn\n" +"2: dec %2; ! Increment %2\n" +" cmp %2, %1; ! check for overflow\n" +" be 2b; ! if so, dec again\n" +" nop; ! but this means a delay, sigh\n" +" st %2, [%0]; ! Save new value into *pNum\n" + : /* no output */ + : "r" (pNum), "r" (0x80000000), "r" (0x80000000) + : "cc", "memory" + ); +} + +/* Increment by 1 and return new value */ +inline UINT32 +HXAtomicIncRetUINT32(UINT32* pNum) +{ + volatile UINT32 ulRet; + __asm__ __volatile__( +" mov %2, %0; ! Copy %2 to %0 \n" +"1: swap [%1], %0; ! Swap *pNum and %0\n" +" nop; ! delay slot...\n" +" cmp %0, %2; ! Is someone else using pNum?\n" +" be 1b; ! If so, retry...\n" +" nop; ! delay slot...yawn\n" +"2: inc %0; ! Increment %0\n" +" cmp %0, %2; ! check for overflow\n" +" be 2b; ! if so, inc again\n" +" nop; ! but this means a delay, sigh\n" +" st %0, [%1]; ! Save new value into *pNum\n" + : "=r" (ulRet) + : "r" (pNum), "r" (0x80000000), "0" (ulRet) + : "cc", "memory" + ); + return ulRet; +} + +/* Decrement by 1 and return new value */ +inline UINT32 +HXAtomicDecRetUINT32(UINT32* pNum) +{ volatile UINT32 ulRet; + __asm__ __volatile__( +" mov %2, %0; ! Copy %2 to %0 \n" +"1: swap [%1], %0; ! Swap *pNum and %0\n" +" nop; ! delay slot...\n" +" cmp %0, %2; ! Is someone else using pNum?\n" +" be 1b; ! If so, retry...\n" +" nop; ! delay slot...yawn\n" +"2: dec %0; ! Decrement %0\n" +" cmp %0, %2; ! check for overflow\n" +" be 2b; ! if so, dec again\n" +" nop; ! but this means a delay, sigh\n" +" st %0, [%1]; ! Save new value into *pNum\n" + : "=r" (ulRet) + : "r" (pNum), "r" (0x80000000), "0" (ulRet) + : "cc", "memory" + ); + return ulRet; +} + +/* Add n */ +inline void +HXAtomicAddUINT32(UINT32* pNum, UINT32 ulNum) +{ + __asm__ __volatile__( +"1: swap [%0], %2; ! Swap *pNum and %2\n" +" nop; ! delay slot...\n" +" cmp %2, %1; ! Is someone else using pNum?\n" +" be 1b; ! If so, retry...\n" +" nop; ! delay slot...yawn\n" +" add %2, %3, %2; ! Add ulNum to %2\n" +" cmp %2, %1; ! check for overflow\n" +" bne 2f; ! if not, skip to the end\n" +" nop; ! but this means a delay, sigh\n" +" inc %2; ! skip marker value\n" +"2: st %2, [%0]; ! Save new value into *pNum\n" + : /* no output */ + : "r" (pNum), "r" (0x80000000), "r" (0x80000000), "r" (ulNum) + : "cc", "memory" + ); +} + +/* Subtract n */ +inline void +HXAtomicSubUINT32(UINT32* pNum, UINT32 ulNum) +{ + __asm__ __volatile__( +"1: swap [%0], %2; ! Swap *pNum and %2\n" +" nop; ! delay slot...\n" +" cmp %2, %1; ! Is someone else using pNum?\n" +" be 1b; ! If so, retry...\n" +" nop; ! delay slot...yawn\n" +" sub %2, %3, %2; ! Subtract ulNum to %2\n" +" cmp %2, %1; ! check for overflow\n" +" bne 2f; ! if not, skip to the end\n" +" nop; ! but this means a delay, sigh\n" +" inc %2; ! skip marker value\n" +"2: st %2, [%0]; ! Save new value into *pNum\n" + : /* no output */ + : "r" (pNum), "r" (0x80000000), "r" (0x80000000), "r" (ulNum) + : "cc", "memory" + ); +} + +/* Add n and return new value */ +inline UINT32 +HXAtomicAddRetUINT32(UINT32* pNum, UINT32 ulNum) +{ + volatile UINT32 ulRet; \ + __asm__ __volatile__( +" mov %2, %0 ! Copy %2 to %0 \n" +"1: swap [%1], %0; ! Swap *pNum and %0\n" +" nop; ! delay slot...\n" +" cmp %0, %2; ! Is someone else using pNum?\n" +" be 1b; ! If so, retry...\n" +" nop; ! delay slot...yawn\n" +" add %0, %3, %0; ! Add ulNum to %0\n" +" cmp %0, %2; ! check for overflow\n" +" bne 2f; ! if not, skip to the end\n" +" nop; ! but this means a delay, sigh\n" +" inc %0; ! skip marker value\n" +"2: st %0, [%1]; ! Save new value into *pNum\n" + : "=r" (ulRet) + : "r" (pNum), "r" (0x80000000), "r" (ulNum), "0" (ulRet) + : "cc", "memory" + ); + return ulRet; +} + +/* Subtract n and return new value */ +inline UINT32 +HXAtomicSubRetUINT32(UINT32* pNum, UINT32 ulNum) +{ volatile UINT32 ulRet; + __asm__ __volatile__( +" mov %2, %0 ! Copy %2 to %0 \n" +"1: swap [%1], %0; ! Swap *pNum and %0\n" +" nop; ! delay slot...\n" +" cmp %0, %2; ! Is someone else using pNum?\n" +" be 1b; ! If so, retry...\n" +" nop; ! delay slot...yawn\n" +" sub %0, %3, %0; ! Sub ulNum from %0\n" +" cmp %0, %2; ! check for overflow\n" +" bne 2f; ! if not, skip to the end\n" +" nop; ! but this means a delay, sigh\n" +" dec %0; ! skip marker value\n" +"2: st %0, [%1]; ! Save new value into *pNum\n" + : "=r" (ulRet) + : "r" (pNum), "r" (0x80000000), "r" (ulNum), "0" (ulRet) + : "cc", "memory" + ); + return ulRet; +} + +inline void HXAtomicIncINT32(INT32* p) { HXAtomicIncUINT32((UINT32*)p); } +inline void HXAtomicDecINT32(INT32* p) { HXAtomicDecUINT32((UINT32*)p); } +inline void HXAtomicAddINT32(INT32* p, INT32 n) { HXAtomicAddUINT32((UINT32*)p, (UINT32)n); } +inline void HXAtomicSubINT32(INT32* p, INT32 n) { HXAtomicSubUINT32((UINT32*)p, (UINT32)n); } +inline INT32 HXAtomicIncRetINT32(INT32* p) { return HXAtomicIncRetUINT32((UINT32*)p); } +inline INT32 HXAtomicDecRetINT32(INT32* p) { return HXAtomicDecRetUINT32((UINT32*)p); } +inline INT32 HXAtomicAddRetINT32(INT32* p, INT32 n) { return HXAtomicAddRetUINT32((UINT32*)p, (UINT32)n); } +inline INT32 HXAtomicSubRetINT32(INT32* p, INT32 n) { return HXAtomicSubRetUINT32((UINT32*)p, (UINT32)n); } + + + +/*********************************************************************** + * Windows / x86 (Visual C/C++) + * + * Implementation Notes: + * 'xadd' is only available in the 486 series and later, not the 386. + * There is no 'xsub' counterpart, you have to negate the operand + * and use 'xadd'. Note the use of the 'lock' prefix to ensure + * certain operations occur atomically. + */ +#elif defined (_M_IX86) /* && _M_IX86 > 300 XXX wschildbach: disabled until the build system delivers the correct value */ + +/* Increment by 1 */ +static __inline void +HXAtomicIncUINT32(UINT32* pNum) +{ + // register usage summary: + // eax - pointer to the value we're modifying + _asm + { + mov eax, pNum ; Load the pointer into a register + lock inc dword ptr [eax] ; Atomically increment *pNum + } +} + +/* Decrement by 1 */ +static __inline void +HXAtomicDecUINT32(UINT32* pNum) +{ + // register usage summary: + // eax - pointer to the value we're modifying + _asm + { + mov eax, pNum ; Load the pointer into a register + lock dec dword ptr [eax] ; Atomically decrement *pNum + } +} + +/* Increment by 1 and return new value */ +static __inline UINT32 +HXAtomicIncRetUINT32(UINT32* pNum) +{ + volatile UINT32 ulRet; + // register usage summary: + // eax - pointer to the value we're modifying + // ebx - work register + _asm + { + mov eax, pNum ; Load the pointer into a register + mov ebx, 0x1 ; Load increment amount into a register + lock xadd dword ptr [eax], ebx ; Increment *pNum; ebx gets old value + inc ebx ; Increment old value + mov ulRet, ebx ; Set the return value + } + return ulRet; +} + +/* Decrement by 1 and return new value */ +static __inline UINT32 +HXAtomicDecRetUINT32(UINT32* pNum) +{ + volatile UINT32 ulRet; + // register usage summary: + // eax - pointer to the value we're modifying + // ebx - work register + // note: we increment by 0xffffffff to decrement by 1 + _asm + { + mov eax, pNum ; Load the pointer into a register + mov ebx, 0xffffffff ; Load decrement amount into a register + lock xadd dword ptr [eax], ebx ; Decrement *pNum; ebx gets old value + dec ebx ; decrement old value + mov ulRet, ebx ; Set the return value + } + return ulRet; +} + +/* Add n */ +static __inline void +HXAtomicAddUINT32(UINT32* pNum, UINT32 ulNum) +{ + // register usage summary: + // eax - pointer to the value we're modifying + // ebx - work register + _asm + { + mov eax, pNum ; Load the pointer into a register + mov ebx, ulNum ; Load increment amount into a register + lock add dword ptr [eax], ebx ; Increment *pNum by ulNum + } +} + +/* Subtract n */ +static __inline void +HXAtomicSubUINT32(UINT32* pNum, UINT32 ulNum) +{ + // register usage summary: + // eax - pointer to the value we're modifying + // ebx - work register + _asm + { + mov eax, pNum ; Load the pointer into a register + mov ebx, ulNum ; Load increment amount into a register + lock sub dword ptr [eax], ebx ; Atomically decrement *pNum by ulNum + } +} + +/* Add n and return new value */ +static __inline UINT32 +HXAtomicAddRetUINT32(UINT32* pNum, UINT32 ulNum) +{ + volatile UINT32 ulRet; + // register usage summary: + // eax - pointer to the value we're modifying + // ebx - work register + // ecx - work register #2 + _asm + { + mov eax, pNum ; Load the pointer into a register + mov ebx, ulNum ; Load increment amount into a register + mov ecx, ebx ; copy ebx into ecx + lock xadd dword ptr [eax], ecx ; Increment *pNum; ecx gets old value + add ecx, ebx ; Add ulNum to it + mov ulRet, ecx ; save result in ulRet + } + return ulRet; +} + +/* Subtract n and return new value */ +static __inline UINT32 +HXAtomicSubRetUINT32(UINT32* pNum, UINT32 ulNum) +{ + volatile UINT32 ulRet; + // register usage summary: + // eax - pointer to the value we're modifying + // ebx - work register + // ecx - work register #2 + _asm + { + mov eax, pNum ; Load the pointer into a register + mov ebx, ulNum ; Load increment amount into a register + mov ecx, 0x0 ; zero out ecx + sub ecx, ebx ; compute -(ulNum), saving in ecx + lock xadd dword ptr [eax], ecx ; Decrement *pNum; ecx gets old value + sub ecx, ebx ; subtract ulNum from it + mov ulRet, ecx ; save result in ulRet + } + return ulRet; +} + +static __inline void HXAtomicIncINT32(INT32* p) { HXAtomicIncUINT32((UINT32*)p); } +static __inline void HXAtomicDecINT32(INT32* p) { HXAtomicDecUINT32((UINT32*)p); } +static __inline void HXAtomicAddINT32(INT32* p, INT32 n) { HXAtomicAddUINT32((UINT32*)p, (UINT32)n); } +static __inline void HXAtomicSubINT32(INT32* p, INT32 n) { HXAtomicSubUINT32((UINT32*)p, (UINT32)n); } +static __inline INT32 HXAtomicIncRetINT32(INT32* p) { return HXAtomicIncRetUINT32((UINT32*)p); } +static __inline INT32 HXAtomicDecRetINT32(INT32* p) { return HXAtomicDecRetUINT32((UINT32*)p); } +static __inline INT32 HXAtomicAddRetINT32(INT32* p, INT32 n) { return HXAtomicAddRetUINT32((UINT32*)p, (UINT32)n); } +static __inline INT32 HXAtomicSubRetINT32(INT32* p, INT32 n) { return HXAtomicSubRetUINT32((UINT32*)p, (UINT32)n); } + + + +/*********************************************************************** + * Intel x86 (gcc) / Unix -- i486 and higher - 32-bit + * + * Implementation Notes: + * 'xadd' is only available in the 486 series and later, not the 386. + * There is no 'xsub' counterpart, you have to negate the operand + * and use 'xadd'. Note the use of the 'lock' prefix to ensure + * certain operations occur atomically. + * + * OpenBSD is excluded since the standard assembler on x86 systems + * can't handle the xadd instruction. + * + */ +#elif defined(__GNUC__) && !defined(_OPENBSD) && \ + (__GNUC__>2 || (__GNUC__==2 && __GNUC_MINOR__>=95)) && \ + ( defined (__i486__) || defined (__i586__) || defined (__i686__) || \ + defined (__pentium__) || defined (__pentiumpro__)) + +/* Increment by 1 */ +static __inline__ void +HXAtomicIncUINT32(UINT32* pNum) +{ + __asm__ __volatile__( + "lock incl (%0);" // atomically add 1 to *pNum + : /* no output */ + : "r" (pNum) + : "cc", "memory" + ); +} + +/* Decrement by 1 */ +static __inline__ void +HXAtomicDecUINT32(UINT32* pNum) +{ + __asm__ __volatile__( + "lock decl (%0);" // atomically add -1 to *pNum + : /* no output */ + : "r" (pNum) + : "cc", "memory" + ); +} + +/* Increment by 1 and return new value */ +static __inline__ UINT32 +HXAtomicIncRetUINT32(UINT32* pNum) +{ + volatile UINT32 ulRet; + __asm__ __volatile__( + "lock xaddl %0, (%1);" // atomically add 1 to *pNum + " inc %0;" // old value in %0, increment it + : "=r" (ulRet) + : "r" (pNum), "0" (0x1) + : "cc", "memory" + ); + return ulRet; +} + +/* Decrement by 1 and return new value */ +static __inline__ UINT32 +HXAtomicDecRetUINT32(UINT32* pNum) +{ + volatile UINT32 ulRet; + __asm__ __volatile__( + "lock xaddl %0, (%1);" // atomically add -1 to *pNum + " dec %0;" // old value in %0, decrement it + : "=r" (ulRet) + : "r" (pNum), "0" (-1) + : "cc", "memory" + ); + return ulRet; +} + +/* Add n */ +static __inline__ void +HXAtomicAddUINT32(UINT32* pNum, UINT32 ulNum) +{ + __asm__ __volatile__( + "lock addl %1, (%0);" // atomically add ulNum to *pNum + : /* no output */ + : "r" (pNum), "r" (ulNum) + : "cc", "memory" + ); +} + +/* Subtract n */ +static __inline__ void +HXAtomicSubUINT32(UINT32* pNum, UINT32 ulNum) +{ + __asm__ __volatile__( + "lock subl %1, (%0);" // atomically add ulNum to *pNum + : /* no output */ + : "r" (pNum), "r" (ulNum) + : "cc", "memory" + ); +} + +/* Add n and return new value */ +static __inline__ UINT32 +HXAtomicAddRetUINT32(UINT32* pNum, UINT32 ulNum) +{ + volatile UINT32 ulRet; + __asm__ __volatile__( + " mov %2, %0;" // copy ulNum into %0 + "lock xaddl %0, (%1);" // atomically add ulNum to *pNum + " add %2, %0;" // old value in %0, add ulNum + : "=r" (ulRet) + : "r" (pNum), "r" (ulNum), "0" (0) + : "cc", "memory" + ); + return ulRet; +} + +/* Subtract n and return new value */ +static __inline__ UINT32 +HXAtomicSubRetUINT32(UINT32* pNum, UINT32 ulNum) +{ + volatile UINT32 ulRet; + __asm__ __volatile__( + " sub %2, %0;" // negate ulNum, saving in %0 + "lock xaddl %0, (%1);" // atomically add -(ulNum) to *pNum + " sub %2, %0;" // old value in %0, subtract ulNum + : "=r" (ulRet) + : "r" (pNum), "r" (ulNum), "0" (0) + : "cc", "memory" + ); + return ulRet; +} + + +static __inline__ void HXAtomicIncINT32(INT32* p) { HXAtomicIncUINT32((UINT32*)p); } +static __inline__ void HXAtomicDecINT32(INT32* p) { HXAtomicDecUINT32((UINT32*)p); } +static __inline__ void HXAtomicAddINT32(INT32* p, INT32 n) { HXAtomicAddUINT32((UINT32*)p, (UINT32)n); } +static __inline__ void HXAtomicSubINT32(INT32* p, INT32 n) { HXAtomicSubUINT32((UINT32*)p, (UINT32)n); } +static __inline__ INT32 HXAtomicIncRetINT32(INT32* p) { return HXAtomicIncRetUINT32((UINT32*)p); } +static __inline__ INT32 HXAtomicDecRetINT32(INT32* p) { return HXAtomicDecRetUINT32((UINT32*)p); } +static __inline__ INT32 HXAtomicAddRetINT32(INT32* p, INT32 n) { return HXAtomicAddRetUINT32((UINT32*)p, (UINT32)n); } +static __inline__ INT32 HXAtomicSubRetINT32(INT32* p, INT32 n) { return HXAtomicSubRetUINT32((UINT32*)p, (UINT32)n); } + + + +/*********************************************************************** + * Intel x86/amd64/x86_64 (gcc) / Unix -- 64-bit + * + * Implementation Notes: + * + */ +#elif defined(__GNUC__) && (defined (__amd64__) || defined (__x86_64__)) + +/* Increment by 1 */ +static __inline__ void +HXAtomicIncUINT32(UINT32* pNum) +{ + __asm__ __volatile__( + "lock incl (%%rax);" // atomically add 1 to *pNum + : /* no output */ + : "a" (pNum) + : "cc", "memory" + ); +} + +/* Decrement by 1 */ +static __inline__ void +HXAtomicDecUINT32(UINT32* pNum) +{ + __asm__ __volatile__( + "lock decl (%%rax);" // atomically add -1 to *pNum + : /* no output */ + : "a" (pNum) + : "cc", "memory" + ); +} + +/* Increment by 1 and return new value */ +static __inline__ UINT32 +HXAtomicIncRetUINT32(UINT32* pNum) +{ + volatile UINT32 ulRet; + __asm__ __volatile__( + "lock xaddl %%ebx, (%%rax);" // atomically add 1 to *pNum + " incl %%ebx;" // old value in %%ebx, increment it + : "=b" (ulRet) + : "a" (pNum), "b" (0x1) + : "cc", "memory" + ); + return ulRet; +} + +/* Decrement by 1 and return new value */ +static __inline__ UINT32 +HXAtomicDecRetUINT32(UINT32* pNum) +{ + volatile UINT32 ulRet; + __asm__ __volatile__( + "lock xaddl %%ebx, (%%rax);" // atomically add -1 to *pNum + " decl %%ebx;" // old value in %%ebx, decrement it + : "=b" (ulRet) + : "a" (pNum), "b" (-1) + : "cc", "memory" + ); + return ulRet; +} + +/* Add n */ +static __inline__ void +HXAtomicAddUINT32(UINT32* pNum, UINT32 ulNum) +{ + __asm__ __volatile__( + "lock addl %%ebx, (%%rax);" // atomically add ulNum to *pNum + : /* no output */ + : "a" (pNum), "b" (ulNum) + : "cc", "memory" + ); +} + +/* Subtract n */ +static __inline__ void +HXAtomicSubUINT32(UINT32* pNum, UINT32 ulNum) +{ + __asm__ __volatile__( + "lock subl %%ebx, (%%rax);" // atomically add ulNum to *pNum + : /* no output */ + : "a" (pNum), "b" (ulNum) + : "cc", "memory" + ); +} + +/* Add n and return new value */ +static __inline__ UINT32 +HXAtomicAddRetUINT32(UINT32* pNum, UINT32 ulNum) +{ + volatile UINT32 ulRet; + __asm__ __volatile__( + " movl %%ebx, %%ecx;" // copy ulNum into %0 + "lock xaddl %%ecx, (%%rax);" // atomically add ulNum to *pNum + " addl %%ebx, %%ecx;" // old value in %%ecx, add ulNum + : "=c" (ulRet) + : "a" (pNum), "b" (ulNum), "c" (0) + : "cc", "memory" + ); + return ulRet; +} + +/* Subtract n and return new value */ +static __inline__ UINT32 +HXAtomicSubRetUINT32(UINT32* pNum, UINT32 ulNum) +{ + volatile UINT32 ulRet; + __asm__ __volatile__( + " subl %%ebx, %%ecx;" // negate ulNum, saving in %0 + "lock xaddl %%ecx, (%%rax);" // atomically add -(ulNum) to *pNum + " subl %%ebx, %%ecx;" // old value in %%ecx, subtract ulNum + : "=c" (ulRet) + : "a" (pNum), "b" (ulNum), "c" (0) + : "cc", "memory" + ); + return ulRet; +} + + +static __inline__ void HXAtomicIncINT32(INT32* p) { HXAtomicIncUINT32((UINT32*)p); } +static __inline__ void HXAtomicDecINT32(INT32* p) { HXAtomicDecUINT32((UINT32*)p); } +static __inline__ void HXAtomicAddINT32(INT32* p, INT32 n) { HXAtomicAddUINT32((UINT32*)p, (UINT32)n); } +static __inline__ void HXAtomicSubINT32(INT32* p, INT32 n) { HXAtomicSubUINT32((UINT32*)p, (UINT32)n); } +static __inline__ INT32 HXAtomicIncRetINT32(INT32* p) { return HXAtomicIncRetUINT32((UINT32*)p); } +static __inline__ INT32 HXAtomicDecRetINT32(INT32* p) { return HXAtomicDecRetUINT32((UINT32*)p); } +static __inline__ INT32 HXAtomicAddRetINT32(INT32* p, INT32 n) { return HXAtomicAddRetUINT32((UINT32*)p, (UINT32)n); } +static __inline__ INT32 HXAtomicSubRetINT32(INT32* p, INT32 n) { return HXAtomicSubRetUINT32((UINT32*)p, (UINT32)n); } + + + +/*********************************************************************** + * HP-UX / IA64 (Native compiler) + * + * Implementation Notes: + * A work-in-progress... + */ +#elif defined(_HPUX) && defined(_IA64) + +#if defined(__cplusplus) +extern "C" { +#endif + UINT32 _HXAtomicIncRetUINT32 (UINT32* pNum); + UINT32 _HXAtomicDecRetUINT32 (UINT32* pNum); + UINT32 _HXAtomicAddRetUINT32 (UINT32* pNum, UINT32 ulNum); + UINT32 _HXAtomicSubRetUINT32 (UINT32* pNum, UINT32 ulNum); +#if defined(__cplusplus) +} +#endif + +#define HXAtomicIncINT32(p) _HXAtomicIncRetUINT32((UINT32*)(p)) +#define HXAtomicDecINT32(p) _HXAtomicDecRetUINT32((UINT32*)(p)) +#define HXAtomicIncRetINT32(p) _HXAtomicIncRetUINT32((UINT32*)(p)) +#define HXAtomicDecRetINT32(p) _HXAtomicDecRetUINT32((UINT32*)(p)) +#define HXAtomicAddINT32(p,n) _HXAtomicAddRetUINT32((UINT32*)(p),(INT32)(n)) +#define HXAtomicSubINT32(p,n) _HXAtomicSubRetUINT32((UINT32*)(p),(INT32)(n)) +#define HXAtomicAddRetINT32(p,n) _HXAtomicAddRetUINT32((UINT32*)(p),(INT32)(n)) +#define HXAtomicSubRetINT32(p,n) _HXAtomicSubRetUINT32((UINT32*)(p),(INT32)(n)) + +#define HXAtomicIncUINT32(p) _HXAtomicIncRetUINT32((p)) +#define HXAtomicDecUINT32(p) _HXAtomicDecRetUINT32((p)) +#define HXAtomicIncRetUINT32(p) _HXAtomicIncRetUINT32((p)) +#define HXAtomicDecRetUINT32(p) _HXAtomicDecRetUINT32((p)) +#define HXAtomicAddUINT32(p,n) _HXAtomicAddRetUINT32((p),(n)) +#define HXAtomicSubUINT32(p,n) _HXAtomicSubRetUINT32((p),(n)) +#define HXAtomicAddRetUINT32(p,n) _HXAtomicAddRetUINT32((p),(n)) +#define HXAtomicSubRetUINT32(p,n) _HXAtomicSubRetUINT32((p),(n)) + + + +/*********************************************************************** + * Tru64 (OSF1) / Alpha (Native compiler) + * + * Implementation Notes: + * + * The Alpha CPU provides instructions to load-lock a value, + * modify it, and attempt to write it back. If the value has + * been modified by someone else since the load-lock occurred, + * the write will fail and you can check the status code to + * know whether you need to retry or not. + * + */ +#elif defined (__alpha) + +#include <c_asm.h> + +/* Increment by 1 and return new value */ +inline INT32 +HXAtomicIncRetINT32(INT32* pNum) +{ + return asm ( + "10: ldl_l %t0, (%a0);" // Load-lock value into a register + " addl %t0, 1, %t0;" // Increment value + " or %t0, %zero, %v0;" // set new value for return. + " stl_c %t0, (%a0);" // Save new value into *pNum + " beq %t0, 10b;" // Retry if sequence failed + , pNum); +} + +/* Decrement by 1 and return new value */ +inline INT32 +HXAtomicDecRetINT32(INT32* pNum) +{ + return asm ( + "10: ldl_l %t0, (%a0);" // Load-lock value into a register + " subl %t0, 1, %t0;" // Decrement value + " or %t0, %zero, %v0;" // set new value for return. + " stl_c %t0, (%a0);" // Save new value into *pNum + " beq %t0, 10b;" // Retry if sequence failed + , pNum); +} + +/* Add n and return new value */ +inline INT32 +HXAtomicAddRetINT32(INT32* pNum, INT32 n) +{ + return asm ( + "10: ldl_l %t0, (%a0);" // Load-lock value into a register + " addl %t0, %a1, %t0;" // Add n to value + " or %t0, %zero, %v0;" // set new value for return. + " stl_c %t0, (%a0);" // Save new value into *pNum + " beq %t0, 10b;" // Retry if sequence failed + , pNum, n); +} + +/* Subtract n and return new value */ +inline INT32 +HXAtomicSubRetINT32(INT32* pNum, INT32 n) +{ + return asm ( + "10: ldl_l %t0, (%a0);" // Load-lock value into a register + " subl %t0, %a1, %t0;" // Subtract n from value + " or %t0, %zero, %v0;" // set new value for return. + " stl_c %t0, (%a0);" // Save new value into *pNum + " beq %t0, 10b;" // Retry if sequence failed + , pNum, n); +} + +/* Increment by 1 and return new value */ +inline UINT32 +HXAtomicIncRetUINT32(UINT32* pNum) +{ + return asm ( + "10: ldl_l %t0, (%a0);" // Load-lock value into a register + " addl %t0, 1, %t0;" // Increment value + " or %t0, %zero, %v0;" // set new value for return. + " stl_c %t0, (%a0);" // Save new value into *pNum + " beq %t0, 10b;" // Retry if sequence failed + , pNum); +} + +/* Decrement by 1 and return new value */ +inline UINT32 +HXAtomicDecRetUINT32(UINT32* pNum) +{ + return asm ( + "10: ldl_l %t0, (%a0);" // Load-lock value into a register + " subl %t0, 1, %t0;" // Decrement value + " or %t0, %zero, %v0;" // set new value for return. + " stl_c %t0, (%a0);" // Save new value into *pNum + " beq %t0, 10b;" // Retry if sequence failed + , pNum); +} + +/* Add n and return new value */ +inline UINT32 +HXAtomicAddRetUINT32(UINT32* pNum, UINT32 n) +{ + return asm ( + "10: ldl_l %t0, (%a0);" // Load-lock value into a register + " addl %t0, %a1, %t0;" // Add n to value + " or %t0, %zero, %v0;" // set new value for return. + " stl_c %t0, (%a0);" // Save new value into *pNum + " beq %t0, 10b;" // Retry if sequence failed + , pNum, n); +} + +/* Subtract n and return new value */ +inline UINT32 +HXAtomicSubRetUINT32(UINT32* pNum, UINT32 n) +{ + return asm ( + "10: ldl_l %t0, (%a0);" // Load-lock value into a register + " subl %t0, %a1, %t0;" // Subtract n from value + " or %t0, %zero, %v0;" // set new value for return. + " stl_c %t0, (%a0);" // Save new value into *pNum + " beq %t0, 10b;" // Retry if sequence failed + , pNum, n); +} + +#define HXAtomicIncINT32(p) HXAtomicIncRetINT32((p)) +#define HXAtomicDecINT32(p) HXAtomicDecRetINT32((p)) +#define HXAtomicAddINT32(p,n) HXAtomicAddRetINT32((p),(n)) +#define HXAtomicSubINT32(p,n) HXAtomicSubRetINT32((p),(n)) + +#define HXAtomicIncUINT32(p) HXAtomicIncRetUINT32((p)) +#define HXAtomicDecUINT32(p) HXAtomicDecRetUINT32((p)) +#define HXAtomicAddUINT32(p,n) HXAtomicAddRetUINT32((p),(n)) +#define HXAtomicSubUINT32(p,n) HXAtomicSubRetUINT32((p),(n)) + + + +/*********************************************************************** + * AIX / PowerPC (Native compiler) + * + * Implementation Notes: + * + * XXXDC: The xlc compiler is able to do inline asm for C but when I do + * it for C++ it crashes, so for now I have resorted to putting + * the asm in a separate assembler routine. The way you inline with + * xlc/xlC is difficult to use, requiring the use of "#pragma mc_func". + */ +#elif defined (_AIX) + +//defined in common/util/platform/aix/atomicops.s +#if defined(__cplusplus) +extern "C" { +#endif + INT32 _HXAtomicAddRetINT32 (INT32* pNum, INT32 lNum); + INT32 _HXAtomicSubRetINT32 (INT32* pNum, INT32 lNum); + UINT32 _HXAtomicAddRetUINT32 (UINT32* pNum, UINT32 ulNum); + UINT32 _HXAtomicSubRetUINT32 (UINT32* pNum, UINT32 ulNum); +#if defined(__cplusplus) +} +#endif + +#define HXAtomicIncINT32(p) _HXAtomicAddRetINT32((p),(INT32)1) +#define HXAtomicDecINT32(p) _HXAtomicSubRetINT32((p),(INT32)1) +#define HXAtomicIncRetINT32(p) _HXAtomicAddRetINT32((p),(INT32)1) +#define HXAtomicDecRetINT32(p) _HXAtomicSubRetINT32((p),(INT32)1) +#define HXAtomicAddINT32(p,n) _HXAtomicAddRetINT32((p),(n)) +#define HXAtomicSubINT32(p,n) _HXAtomicSubRetINT32((p),(n)) +#define HXAtomicAddRetINT32(p,n) _HXAtomicAddRetINT32((p),(n)) +#define HXAtomicSubRetINT32(p,n) _HXAtomicSubRetINT32((p),(n)) + +#define HXAtomicIncUINT32(p) _HXAtomicAddRetUINT32((p),(UINT32)1) +#define HXAtomicDecUINT32(p) _HXAtomicSubRetUINT32((p),(UINT32)1) +#define HXAtomicIncRetUINT32(p) _HXAtomicAddRetUINT32((p),(UINT32)1) +#define HXAtomicDecRetUINT32(p) _HXAtomicSubRetUINT32((p),(UINT32)1) +#define HXAtomicAddUINT32(p,n) _HXAtomicAddRetUINT32((p),(n)) +#define HXAtomicSubUINT32(p,n) _HXAtomicSubRetUINT32((p),(n)) +#define HXAtomicAddRetUINT32(p,n) _HXAtomicAddRetUINT32((p),(n)) +#define HXAtomicSubRetUINT32(p,n) _HXAtomicSubRetUINT32((p),(n)) + + +/*********************************************************************** + * MAC / PowerPC (CW) + * + * Implementation Notes: + * + * This will need to be rewritten, probably, once we move away from CW to PB. + * + * Note: This is an imcompletely-defined platform, be aware that + * not all standard HXAtomic operators are defined! + * + */ +#elif defined(_MACINTOSH) && defined(__MWERKS__) + +inline UINT32 +HXAtomicIncRetUINT32(register UINT32* pNum) +{ + register UINT32 zeroOffset = 0; + register UINT32 temp; + + asm + { + again: + lwarx temp, zeroOffset, pNum + addi temp, temp, 1 + stwcx. temp, zeroOffset, pNum + bne- again + } + + return temp; +} + +inline UINT32 +HXAtomicDecRetUINT32(register UINT32* pNum) +{ + register UINT32 zeroOffset = 0; + register UINT32 temp; + + asm + { + again: + lwarx temp, zeroOffset, pNum + subi temp, temp, 1 + stwcx. temp, zeroOffset, pNum + bne- again + } + + return temp; +} + + +/*********************************************************************** + * MAC - PowerPC (PB or XCode) / Linux - PowerPC + * + * Implementation Notes: + * + * Use PowerPC load exclusive and store exclusive instructions + * + */ +#elif defined(_MAC_UNIX) || (defined(_LINUX) && defined(__powerpc__)) + +// could also probably be defined(__GNUC__) && defined(__powerpc) + +static inline UINT32 +HXAtomicIncRetUINT32(UINT32* pNum) +{ + volatile UINT32 result; + + __asm__ __volatile__ ( +"1: lwarx %0, %3, %2;\n" +" addi %0, %0, 1;\n" +" stwcx. %0, %3, %2;\n" +" bne- 1b;" + : "=b" (result) + : "0" (result), "b" (pNum), "b" (0x0) + : "cc", "memory" + ); + + return result; +} + +static inline UINT32 +HXAtomicDecRetUINT32(UINT32* pNum) +{ + volatile UINT32 result; + + __asm__ __volatile__ ( +"1: lwarx %0, %3, %2;\n" +" subi %0, %0, 1;\n" +" stwcx. %0, %3, %2;\n" +" bne- 1b;" + : "=b" (result) + : "0" (result), "b" (pNum), "b" (0x0) + : "cc", "memory" + ); + + return result; +} + + +static inline UINT32 +HXAtomicAddRetUINT32(UINT32* pNum, UINT32 ulNum) +{ + volatile UINT32 result; + + __asm__ __volatile__ ( +"1: lwarx %0, %3, %2;\n" +" add %0, %0, %4;\n" +" stwcx. %0, %3, %2;\n" +" bne- 1b;" + : "=b" (result) + : "0" (result), "b" (pNum), "b" (0x0), "b" (ulNum) + : "cc", "memory" + ); + + return result; +} + + +static inline UINT32 +HXAtomicSubRetUINT32(UINT32* pNum, UINT32 ulNum) +{ + volatile UINT32 result; + + __asm__ __volatile__ ( +"1: lwarx %0, %3, %2;\n" +" sub %0, %0, %4;\n" +" stwcx. %0, %3, %2;\n" +" bne- 1b;" + : "=b" (result) + : "0" (result), "b" (pNum), "b" (0x0), "b" (ulNum) + : "cc", "memory" + ); + + return result; +} + +// the rest of these atomic operations can be implemented in terms of the four above. + +static inline void HXAtomicIncINT32(INT32* p) { (void)HXAtomicIncRetUINT32((UINT32*)p); } +static inline void HXAtomicDecINT32(INT32* p) { (void)HXAtomicDecRetUINT32((UINT32*)p); } +static inline void HXAtomicAddINT32(INT32* p, INT32 n) { (void)HXAtomicAddRetUINT32((UINT32*)p, (UINT32)n); } +static inline void HXAtomicSubINT32(INT32* p, INT32 n) { (void)HXAtomicSubRetUINT32((UINT32*)p, (UINT32)n); } +static inline INT32 HXAtomicIncRetINT32(INT32* p) { return (INT32)HXAtomicIncRetUINT32((UINT32*)p); } +static inline INT32 HXAtomicDecRetINT32(INT32* p) { return (INT32)HXAtomicDecRetUINT32((UINT32*)p); } +static inline INT32 HXAtomicAddRetINT32(INT32* p, INT32 n) { return (INT32)HXAtomicAddRetUINT32((UINT32*)p, (UINT32)n); } +static inline INT32 HXAtomicSubRetINT32(INT32* p, INT32 n) { return (INT32)HXAtomicSubRetUINT32((UINT32*)p, (UINT32)n); } +static inline void HXAtomicIncUINT32(UINT32* p) { (void)HXAtomicIncRetUINT32(p); } +static inline void HXAtomicDecUINT32(UINT32* p) { (void)HXAtomicDecRetUINT32(p); } +static inline void HXAtomicAddUINT32(UINT32* p, UINT32 n) { (void)HXAtomicAddRetUINT32(p, n); } +static inline void HXAtomicSubUINT32(UINT32* p, UINT32 n) { (void)HXAtomicSubRetUINT32(p, n); } + + +/*********************************************************************** + * Generic + * + * Implementation Notes: + * + * This should work on any platform with a HXMutex-style mutex. + * It allocates a pool of mutexes and hashes the int pointers + * to one of the mutexes. Since the mutexes are held for + * such a short time, only long enough to increment an int, + * collisions should be extremely rare and this should work fine, + * although it is probably less fast than the extra-high-performance + * atomic operators provided above. You need to link in atomic.cpp + * to get HXAtomic::m_pLocks defined. + * + * Basic design of the mutex-based lock-pool implementation: + * At startup, allocate an array of N mutexes (where N is a power of 2). + * When a method is called, hash the int pointer to one of the locks. + * Lock this mutex. + * Modify the value. + * Unlock this mutex. + * + * + * Platform-specific notes: + * Any platforms that use this should be documented here! + * Why are you using the generic operators for this platform? + * + * HP-UX / HP-PA: + * This is used on the HP-PA processor since it doesn't provide the + * necessary assembler operators to implement proper atomic updates + * of ints. HP's mutex primitive seems pretty fast however, resulting + * in a workable solution. + * + * OpenBSD: + * The standard assembler on x86 can't handle the gcc/asm operators + * defined above, so we're using the lock-pool approach for now. + * This approach also makes it possible to support non-x86 OpenBSD + * builds more easily (someday). + * + */ +#elif defined(_HPUX) || defined(_OPENBSD) + +#if defined(__cplusplus) +#include "microsleep.h" +#include "hxcom.h" +#include "hxmutexlock.h" + +class HXAtomic +{ +public: + HXAtomic(); + ~HXAtomic(); + void InitLockPool(); + + /* Users of the HXAtomic routines should *NEVER* call these directly. + * They should *ALWAYS* use the HXAtomicAddRetINT32-style macros instead. + */ + INT32 _AddRetINT32 (INT32* pNum, INT32 nNum); + UINT32 _AddRetUINT32 (UINT32* pNum, UINT32 ulNum); + INT32 _SubRetINT32 (INT32* pNum, INT32 nNum); + UINT32 _SubRetUINT32 (UINT32* pNum, UINT32 ulNum); + +private: + void Lock (HX_MUTEX pLock); + void Unlock (HX_MUTEX pLock); + + HX_MUTEX* m_pLocks; +}; + +extern HXAtomic g_AtomicOps; //in common/util/atomicops.cpp + +#define HXAtomicIncINT32(p) g_AtomicOps._AddRetINT32((p),(INT32)1) +#define HXAtomicDecINT32(p) g_AtomicOps._SubRetINT32((p),(INT32)1) +#define HXAtomicIncRetINT32(p) g_AtomicOps._AddRetINT32((p),(INT32)1) +#define HXAtomicDecRetINT32(p) g_AtomicOps._SubRetINT32((p),(INT32)1) + +#define HXAtomicAddRetINT32(p,n) g_AtomicOps._AddRetINT32((p),(n)) +#define HXAtomicSubRetINT32(p,n) g_AtomicOps._SubRetINT32((p),(n)) +#define HXAtomicAddINT32(p,n) g_AtomicOps._AddRetINT32((p),(n)) +#define HXAtomicSubINT32(p,n) g_AtomicOps._SubRetINT32((p),(n)) + +#define HXAtomicIncUINT32(p) g_AtomicOps._AddRetUINT32((p),(UINT32)1) +#define HXAtomicDecUINT32(p) g_AtomicOps._SubRetUINT32((p),(UINT32)1) +#define HXAtomicIncRetUINT32(p) g_AtomicOps._AddRetUINT32((p),(UINT32)1) +#define HXAtomicDecRetUINT32(p) g_AtomicOps._SubRetUINT32((p),(UINT32)1) + +#define HXAtomicAddRetUINT32(p,n) g_AtomicOps._AddRetUINT32((p),(n)) +#define HXAtomicSubRetUINT32(p,n) g_AtomicOps._SubRetUINT32((p),(n)) +#define HXAtomicAddUINT32(p,n) g_AtomicOps._AddRetUINT32((p),(n)) +#define HXAtomicSubUINT32(p,n) g_AtomicOps._SubRetUINT32((p),(n)) +#endif + + + +/*********************************************************************** + * SYMBIAN + * + * Implementation Notes: + * + * Note: This is an imcompletely-defined platform, be aware that + * not all standard HXAtomic operators are defined! + * + */ +#elif defined(_SYMBIAN) + +/* Increment by 1 and return new value */ +inline INT32 +HXAtomicIncRetINT32(INT32* pNum) +{ + return User::LockedInc(*((TInt*)pNum)) + 1; +} + +/* Decrement by 1 and return new value */ +inline INT32 +HXAtomicDecRetINT32(INT32* pNum) +{ + return User::LockedDec(*((TInt*)pNum)) - 1; +} + +/* Increment by 1 and return new value */ +inline UINT32 +HXAtomicIncRetUINT32(UINT32* pNum) +{ + return ((UINT32)User::LockedInc(*((TInt*)pNum))) + 1; +} + +/* Decrement by 1 and return new value */ +inline UINT32 +HXAtomicDecRetUINT32(UINT32* pNum) +{ + return ((UINT32)User::LockedDec(*((TInt*)pNum))) - 1; +} + +#define HXAtomicIncINT32(p) HXAtomicIncRetINT32((p)) +#define HXAtomicDecINT32(p) HXAtomicDecRetINT32((p)) +#define HXAtomicIncUINT32(p) HXAtomicIncRetUINT32((p)) +#define HXAtomicDecUINT32(p) HXAtomicDecRetUINT32((p)) + +#if 0 + +/* + * Add and subtract operations are not implemented + * at this time because there isn't an easy way to + * do it using the facilities provided by Symbian. + * Assembly will likely be needed. + */ + +/* Add n and return new value */ +inline INT32 +HXAtomicAddRetINT32(INT32* pNum, INT32 n) +{ + +} + +/* Subtract n and return new value */ +inline INT32 +HXAtomicSubRetINT32(INT32* pNum, INT32 n) +{ + +} + +/* Add n and return new value */ +inline UINT32 +HXAtomicAddRetUINT32(UINT32* pNum, UINT32 n) +{ + +} + +/* Subtract n and return new value */ +inline UINT32 +HXAtomicSubRetUINT32(UINT32* pNum, UINT32 n) +{ + +} + +#define HXAtomicAddINT32(p,n) HXAtomicAddRetINT32((p),(n)) +#define HXAtomicSubINT32(p,n) HXAtomicSubRetINT32((p),(n)) + +#define HXAtomicAddUINT32(p,n) HXAtomicAddRetUINT32((p),(n)) +#define HXAtomicSubUINT32(p,n) HXAtomicSubRetUINT32((p),(n)) + +#endif + +/*********************************************************************** + * Linux / ARM (gcc) + * + * Implementation Notes: + * + * This implementation sacrifices being able to store the value + * 0x800000000 in the INT32 value, which is a special "busy" marker value. + * Since these are intended for use primarily with AddRef/Release and + * resource usage counters, this should be acceptable for now. If a counter + * is incremented to the point it would conflict with the flag, it is + * incremented one more to hop over it. The same in reverse for decrement. + * + * Basic design of the flag-based implementation: + * 1. Load a register with 0x80000000 + * 2. _atomically_ swap it with the INT32 (critical!) + * 3. Compare what we got with 0x80000000 + * 4. Branch if equal to #2 + * 5. Increment (or decrement) the result + * 6. Compare to 0x80000000 + * 7. Increment (or decrement) again if equal + * 8. Save the new value to the INT32's location in memory + * 9. Return new INT32 result if required + * + */ +#elif defined (_ARM) && defined (__GNUC__) + +/* Increment by 1 */ +inline void +HXAtomicIncUINT32(UINT32* pNum) +{ + UINT32 ulTmp; + __asm__ __volatile__( +" mov %0, #0x80000000;\n" /* Set ulTmp to 0x800000000 */ +"1: swp %0, %0, [%1];\n" /* Swap *pNum and ulTmp */ +" cmp %0, #0x80000000;\n" /* Is someone else using pNum? */ +" beq 1;\n" /* If so, retry... */ +" add %0, %0, #1;\n" /* Increment ulTmp */ +" cmp %0, #0x80000000;\n" /* check for overflow */ +" addeq %0, %0, #1;\n" /* if so, increment again */ +" str %0, [%1];\n" /* Save new value into *pNum */ + : /* no output */ + : "r" (ulTmp), "r" (pNum) + : "cc", "memory" + ); +} + +/* Decrement by 1 */ +inline void +HXAtomicDecUINT32(UINT32* pNum) +{ + UINT32 ulTmp; + __asm__ __volatile__( +" mov %0, #0x80000000;\n" /* Set ulTmp to 0x800000000 */ +"1: swp %0, %0, [%1];\n" /* Swap *pNum and ulTmp */ +" cmp %0, #0x80000000;\n" /* Is someone else using pNum? */ +" beq 1;\n" /* If so, retry... */ +" sub %0, %0, #1;\n" /* Decrement ulTmp */ +" cmp %0, #0x80000000;\n" /* check for overflow */ +" subeq %0, %0, #1;\n" /* if so, decrement again */ +" str %0, [%1];\n" /* Save new value into *pNum */ + : /* no output */ + : "r" (ulTmp), "r" (pNum) + : "cc", "memory" + ); +} + +/* Increment by 1 and return new value */ +inline UINT32 +HXAtomicIncRetUINT32(UINT32* pNum) +{ + volatile UINT32 ulRet; + __asm__ __volatile__( +" mov %0, #0x80000000;\n" /* Set ulRet to 0x80000000 */ +"1: swp %0, %0, [%1];\n" /* Swap *pNum and ulRet */ +" cmp %0, #0x80000000;\n" /* Is someone else using pNum? */ +" beq 1;\n" /* If so, retry... */ +" add %0, %0, #1;\n" /* Increment ulRet */ +" cmp %0, #0x80000000;\n" /* check for overflow */ +" addeq %0, %0, #1;\n" /* if so, increment again */ +" str %0, [%1];\n" /* Save new value into *pNum */ + : "=&r" (ulRet) + : "r" (pNum) + : "cc", "memory" + ); + return ulRet; +} + +/* Decrement by 1 and return new value */ +inline UINT32 +HXAtomicDecRetUINT32(UINT32* pNum) +{ + volatile UINT32 ulRet; + __asm__ __volatile__( +" mov %0, #0x80000000;\n" /* Set ulRet to 0x80000000 */ +"1: swp %0, %0, [%1];\n" /* Swap *pNum and ulRet */ +" cmp %0, #0x80000000;\n" /* Is someone else using pNum? */ +" beq 1;\n" /* If so, retry... */ +" sub %0, %0, #1;\n" /* Decrement ulRet */ +" cmp %0, #0x80000000;\n" /* check for overflow */ +" subeq %0, %0, #1;\n" /* if so, decrement again */ +" str %0, [%1];\n" /* Save new value into *pNum */ + : "=&r" (ulRet) + : "r" (pNum) + : "cc", "memory" + ); + return ulRet; +} + +/* Add n */ +inline void +HXAtomicAddUINT32(UINT32* pNum, UINT32 ulNum) +{ + UINT32 ulTmp; + __asm__ __volatile__( +" mov %0, #0x80000000;\n" /* Set ulTmp to 0x800000000 */ +"1: swp %0, %0, [%1];\n" /* Swap *pNum and ulTmp */ +" cmp %0, #0x80000000;\n" /* Is someone else using pNum? */ +" beq 1;\n" /* If so, retry... */ +" add %0, %0, %2;\n" /* Add ulNum to ulTmp */ +" cmp %0, #0x80000000;\n" /* check for overflow */ +" addeq %0, %0, #1;\n" /* if so, increment again */ +" str %0, [%1];\n" /* Save new value into *pNum */ + : /* no output */ + : "r" (ulTmp), "r" (pNum), "r" (ulNum) + : "cc", "memory" + ); +} + +/* Subtract n */ +inline void +HXAtomicSubUINT32(UINT32* pNum, UINT32 ulNum) +{ + UINT32 ulTmp; + __asm__ __volatile__( +" mov %0, #0x80000000;\n" /* Set ulTmp to 0x800000000 */ +"1: swp %0, %0, [%1];\n" /* Swap *pNum and ulTmp */ +" cmp %0, #0x80000000;\n" /* Is someone else using pNum? */ +" beq 1;\n" /* If so, retry... */ +" sub %0, %0, %2;\n" /* Subtract ulNum from ulTmp */ +" cmp %0, #0x80000000;\n" /* check for overflow */ +" subeq %0, %0, #1;\n" /* if so, decrement again */ +" str %0, [%1];\n" /* Save new value into *pNum */ + : /* no output */ + : "r" (ulTmp), "r" (pNum), "r" (ulNum) + : "cc", "memory" + ); +} + +/* Add n and return new value */ +inline UINT32 +HXAtomicAddRetUINT32(UINT32* pNum, UINT32 ulNum) +{ + volatile UINT32 ulRet; + __asm__ __volatile__( +" mov %0, #0x80000000;\n" /* Set ulRet to 0x80000000 */ +"1: swp %0, %0, [%1];\n" /* Swap *pNum and ulRet */ +" cmp %0, #0x80000000;\n" /* Is someone else using pNum? */ +" beq 1;\n" /* If so, retry... */ +" add %0, %0, %2;\n" /* Add ulNum to ulRet */ +" cmp %0, #0x80000000;\n" /* check for overflow */ +" addeq %0, %0, #1;\n" /* if so, increment again */ +" str %0, [%1];\n" /* Save new value into *pNum */ + : "=&r" (ulRet) + : "r" (pNum) , "r" (ulNum) + : "cc", "memory" + ); + return ulRet; +} + +/* Subtract n and return new value */ +inline UINT32 +HXAtomicSubRetUINT32(UINT32* pNum, UINT32 ulNum) +{ + volatile UINT32 ulRet; + __asm__ __volatile__( +" mov %0, #0x80000000;\n" /* Set ulRet to 0x80000000 */ +"1: swp %0, %0, [%1];\n" /* Swap *pNum and ulRet */ +" cmp %0, #0x80000000;\n" /* Is someone else using pNum? */ +" beq 1;\n" /* If so, retry... */ +" sub %0, %0, %2;\n" /* Subtract ulNum from ulRet */ +" cmp %0, #0x80000000;\n" /* check for overflow */ +" subeq %0, %0, #1;\n" /* if so, decrement again */ +" str %0, [%1];\n" /* Save new value into *pNum */ + : "=&r" (ulRet) + : "r" (pNum), "r" (ulNum) + : "cc", "memory" + ); + return ulRet; +} + +inline void HXAtomicIncINT32(INT32* p) { HXAtomicIncUINT32((UINT32*)p); } +inline void HXAtomicDecINT32(INT32* p) { HXAtomicDecUINT32((UINT32*)p); } +inline void HXAtomicAddINT32(INT32* p, INT32 n) { HXAtomicAddUINT32((UINT32*)p, (UINT32)n); } +inline void HXAtomicSubINT32(INT32* p, INT32 n) { HXAtomicSubUINT32((UINT32*)p, (UINT32)n); } +inline INT32 HXAtomicIncRetINT32(INT32* p) { return HXAtomicIncRetUINT32((UINT32*)p); } +inline INT32 HXAtomicDecRetINT32(INT32* p) { return HXAtomicDecRetUINT32((UINT32*)p); } +inline INT32 HXAtomicAddRetINT32(INT32* p, INT32 n) { return HXAtomicAddRetUINT32((UINT32*)p, (UINT32)n); } +inline INT32 HXAtomicSubRetINT32(INT32* p, INT32 n) { return HXAtomicSubRetUINT32((UINT32*)p, (UINT32)n); } + +/*********************************************************************** + * Add new platforms above here + */ +#else + +// +// Unsupported platform +// + +#ifndef HELIX_CONFIG_DISABLE_ATOMIC_OPERATORS +// Defining HELIX_CONFIG_DISABLE_ATOMIC_OPERATORS will use the ++ and -- +// operators in place of atomic operators in some places in the code. These +// operators are not thread-safe, and should only be used in the intermediary +// stages of porting. +# error "You need to create atomic dec/inc opers for your platform or #define HELIX_CONFIG_DISABLE_ATOMIC_OPERATORS" +#endif + +#endif + + + +/*************************************************************************/ + +/* + * Conditional override of InterlockedIncrement/Decrement + * + * Place this in your Umakefil/.pcf file to turn off atomic + * InterlockedIncrement/Decrement on a per-module basis, + * or place it in your umake profile for system-wide scope. + * If this is defined you'll still have access to the underlying + * HXAtomicxxx operators (if they exist for your platform), + * just that the specific InterlockedIncrement/InterlockedDecrement + * macros won't be defined to use them. + */ +#if !defined (HELIX_CONFIG_DISABLE_ATOMIC_OPERATORS) + +#undef InterlockedIncrement +#undef InterlockedDecrement + +// Since many classes (incorrectly) implement their refcount using LONG32 +// rather than the proper ULONG32, we have to use the typecast for things +// to build on many platforms. +#define InterlockedIncrement(p) HXAtomicIncRetUINT32((UINT32*)(p)) +#define InterlockedDecrement(p) HXAtomicDecRetUINT32((UINT32*)(p)) + +#if !defined(HAVE_INTERLOCKED_INCREMENT) +#define HAVE_INTERLOCKED_INCREMENT //so hxcom.h doesn't redefine these to ++/-- +#endif // /HAVE_INTERLOCKED_INCREMENT. + +#endif /* !defined(HELIX_CONFIG_DISABLE_ATOMIC_OPERATORS) */ + +#endif /* _ATOMICBASE_H_ */ diff --git a/amarok/src/engine/helix/helix-sp/helix-include/common/include/hxausvc.h b/amarok/src/engine/helix/helix-sp/helix-include/common/include/hxausvc.h new file mode 100644 index 00000000..a366ca3d --- /dev/null +++ b/amarok/src/engine/helix/helix-sp/helix-include/common/include/hxausvc.h @@ -0,0 +1,1729 @@ + +/* + * + * This software is released under the provisions of the GPL version 2. + * see file "COPYING". If that file is not available, the full statement + * of the license can be found at + * + * http://www.fsf.org/licensing/licenses/gpl.txt + * + * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved. + * + */ + +#ifndef _HXAUSVC_H_ +#define _HXAUSVC_H_ + +#define HX_MAX_VOLUME 100 +#define HX_INIT_VOLUME 50 +#define HX_MIN_VOLUME 0 + +/**************************************************************************** + * + * Forward declarations of some interfaces defined here-in. + */ +typedef _INTERFACE IHXAudioPlayer IHXAudioPlayer; +typedef _INTERFACE IHXAudioPlayerResponse IHXAudioPlayerResponse; +typedef _INTERFACE IHXAudioStream IHXAudioStream; +typedef _INTERFACE IHXAudioStream2 IHXAudioStream2; +typedef _INTERFACE IHXAudioDevice IHXAudioDevice; +typedef _INTERFACE IHXAudioDeviceResponse IHXAudioDeviceResponse; +typedef _INTERFACE IHXAudioHook IHXAudioHook; +typedef _INTERFACE IHXAudioDeviceHookManager IHXAudioDeviceHookManager; +typedef _INTERFACE IHXAudioStreamInfoResponse IHXAudioStreamInfoResponse; +// $Private: +typedef _INTERFACE IHXMultiPlayPauseSupport IHXMultiPlayPauseSupport; +typedef _INTERFACE IHXAudioDeviceManager2 IHXAudioDeviceManager2; +typedef _INTERFACE IHXAudioResampler IHXAudioResampler; +typedef _INTERFACE IHXAudioResamplerManager IHXAudioResamplerManager; +typedef _INTERFACE IHXAudioPushdown2 IHXAudioPushdown2; +// $EndPrivate. +typedef _INTERFACE IHXVolume IHXVolume; +typedef _INTERFACE IHXVolumeAdviseSink IHXVolumeAdviseSink; +typedef _INTERFACE IHXDryNotification IHXDryNotification; +typedef _INTERFACE IHXBuffer IHXBuffer; +typedef _INTERFACE IHXValues IHXValues; + +/**************************************************************************** + * + * Audio Services Data Structures + */ +typedef struct _HXAudioFormat +{ + UINT16 uChannels; /* Num. of Channels (1=Mono, 2=Stereo, etc. */ + UINT16 uBitsPerSample; /* 8 or 16 */ + UINT32 ulSamplesPerSec;/* Sampling Rate */ + UINT16 uMaxBlockSize; /* Max Blocksize */ +} HXAudioFormat; + +typedef enum _AudioStreamType +{ + STREAMING_AUDIO = 0, + INSTANTANEOUS_AUDIO = 1, + TIMED_AUDIO = 2, + STREAMING_INSTANTANEOUS_AUDIO = 3 +} AudioStreamType; + +typedef struct _HXAudioData +{ + IHXBuffer* pData; /* Audio data */ + ULONG32 ulAudioTime; /* Start time in milliseconds */ + AudioStreamType uAudioStreamType; +} HXAudioData; + +typedef enum _AudioDeviceHookType +{ + READ_ONLY_EARLY = 0, + WRITABLE = 127, + READ_ONLY_LATE = 255 +} AudioDeviceHookType; + +/**************************************************************************** + * + * Interface: + * + * IHXAudioPlayer + * + * Purpose: + * + * This interface provides access to the Audio Player services. Use this + * interface to create audio streams, "hook" post-mixed audio data, and to + * control volume levels. + * + * IID_IHXAudioPlayer: + * + * {00000700-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXAudioPlayer, 0x00000700, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + + +#undef INTERFACE +#define INTERFACE IHXAudioPlayer + +DECLARE_INTERFACE_(IHXAudioPlayer, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXAudioPlayer methods + */ + /************************************************************************ + * Method: + * IHXAudioPlayer::CreateAudioStream + * Purpose: + * Call this to create an audio stream. + */ + STDMETHOD(CreateAudioStream) (THIS_ + IHXAudioStream** /*OUT*/ pAudioStream + ) PURE; + + /************************************************************************ + * Method: + * IHXAudioPlayer::AddPostMixHook + * Purpose: + * Call this to hook audio data after all audio streams in this + * have been mixed. + */ + STDMETHOD(AddPostMixHook) (THIS_ + IHXAudioHook* /*IN*/ pHook, + const HXBOOL /*IN*/ bDisableWrite, + const HXBOOL /*IN*/ bFinal + ) PURE; + + /************************************************************************ + * Method: + * IHXAudioPlayer::RemovePostMixHook + * Purpose: + * Call this to remove an already added post hook. + */ + STDMETHOD(RemovePostMixHook) (THIS_ + IHXAudioHook* /*IN*/ pHook + ) PURE; + + /************************************************************************ + * Method: + * IHXAudioPlayer::GetAudioStreamCount + * Purpose: + * Get the number of audio streams currently active in the + * audio player. Since streams can be added mid-presentation + * this function may return different values on different calls. + * If the user needs to know about all the streams as they get + * get added to the player, IHXAudioStreamInfoResponse should + * be implemented and passed in SetStreamInfoResponse. + */ + STDMETHOD_(UINT16,GetAudioStreamCount) (THIS) PURE; + + /************************************************************************ + * Method: + * IHXAudioPlayer::GetAudioStream + * Purpose: + * Get an audio stream at position given. + */ + STDMETHOD_(IHXAudioStream*,GetAudioStream) (THIS_ + UINT16 /*IN*/ uIndex + ) PURE; + + /************************************************************************ + * Method: + * IHXAudioPlayer::SetStreamInfoResponse + * Purpose: + * Set a stream info response interface. A client must implement + * an IHXAudioStreamInfoResponse and then call this method with + * the IHXAudioStreamInfoResponse as the parameter. The audio + * player will call IHXAudioStreamInfoResponse::OnStreamsReady + * with the total number of audio streams associated with this + * audio player. + */ + STDMETHOD(SetStreamInfoResponse) (THIS_ + IHXAudioStreamInfoResponse* /*IN*/ pResponse + ) PURE; + + /************************************************************************ + * Method: + * IHXAudioPlayer::RemoveStreamInfoResponse + * Purpose: + * Remove stream info response that was added earlier + */ + STDMETHOD(RemoveStreamInfoResponse) (THIS_ + IHXAudioStreamInfoResponse* /*IN*/ pResponse + ) PURE; + + /************************************************************************ + * Method: + * IHXAudioPlayer::GetAudioVolume + * Purpose: + * Get the audio player's volume interface. This volume controls + * the volume level of all the mixed audio streams for this + * audio player. + */ + STDMETHOD_(IHXVolume*,GetAudioVolume) (THIS) PURE; + + /************************************************************************ + * Method: + * IHXAudioPlayer::GetDeviceVolume + * Purpose: + * Get the audio device volume interface. This volume controls + * the audio device volume levels. + */ + STDMETHOD_(IHXVolume*,GetDeviceVolume) (THIS) PURE; + +}; + +/**************************************************************************** + * + * Interface: + * + * IHXAudioPlayerResponse + * + * Purpose: + * + * This interface provides access to the Audio Player Response. Use this + * to receive audio player playback notifications. Your implementation of + * OnTimeSync() is called with the current audio playback time (millisecs). + * This interface is currently to be used ONLY by the RMA engine internally. + * + * IID_IHXAudioPlayerResponse: + * + * {00000701-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXAudioPlayerResponse, 0x00000701, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + + +#undef INTERFACE +#define INTERFACE IHXAudioPlayerResponse + +DECLARE_INTERFACE_(IHXAudioPlayerResponse, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXAudioPlayerResponse methods + */ + + /************************************************************************ + * Method: + * IHXAudioPlayerResponse::OnTimeSync + * Purpose: + * This method is called with the current audio playback time. + */ + STDMETHOD(OnTimeSync) (THIS_ + ULONG32 /*IN*/ ulTimeEnd + ) PURE; + +}; + + +/**************************************************************************** + * + * Interface: + * + * IHXAudioStream + * + * Purpose: + * + * This interface provides access to an Audio Stream. Use this to play + * audio, "hook" audio stream data, and to get audio stream information. + * + * IID_IHXAudioStream: + * + * {00000702-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXAudioStream, 0x00000702, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); +#undef INTERFACE +#define INTERFACE IHXAudioStream + +DECLARE_INTERFACE_(IHXAudioStream, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXAudioStream methods + */ + + /************************************************************************ + * Method: + * IHXAudioStream::Init + * Purpose: + * Initialize an audio stream with the given audio format. The + * IHXValues contains stream identification information. + */ + STDMETHOD(Init) (THIS_ + const HXAudioFormat* /*IN*/ pAudioFormat, + IHXValues* /*IN*/ pValues + ) PURE; + + /************************************************************************ + * Method: + * IHXAudioStream::Write + * Purpose: + * Write audio data to Audio Services. + * + * NOTE: If the renderer loses packets and there is no loss + * correction, then the renderer should write the next packet + * using a meaningful start time. Audio Services will play + * silence where packets are missing. + */ + STDMETHOD(Write) (THIS_ + HXAudioData* /*IN*/ pAudioData + ) PURE; + + /************************************************************************ + * Method: + * IHXAudioStream::AddPreMixHook + * Purpose: + * Use this to "hook" audio stream data prior to the mixing. + * Set bDisableWrite to TRUE to prevent this audio stream data + * from being mixed with other audio stream data associated + * with this audio player. + */ + STDMETHOD(AddPreMixHook) (THIS_ + IHXAudioHook* /*IN*/ pHook, + const HXBOOL /*IN*/ bDisableWrite + ) PURE; + + /************************************************************************ + * Method: + * IHXAudioStream::RemovePreMixHook + * Purpose: + * Use this to remove an already added "hook". + */ + STDMETHOD(RemovePreMixHook) (THIS_ + IHXAudioHook* /*IN*/ pHook + ) PURE; + + /************************************************************************ + * Method: + * IHXAudioStream::AddDryNotification + * Purpose: + * Use this to add a notification response object to get + * notifications when audio stream is running dry. + */ + STDMETHOD(AddDryNotification) (THIS_ + IHXDryNotification* /*IN*/ pNotification + ) PURE; + + /************************************************************************ + * Method: + * IHXAudioStream::GetStreamInfo + * Purpose: + * Use this to get information specific to this audio stream. + */ + STDMETHOD_(IHXValues*,GetStreamInfo) (THIS) PURE; + + /************************************************************************ + * Method: + * IHXAudioStream::GetAudioVolume + * Purpose: + * Get the audio stream's volume interface. This volume controls + * the volume level for this audio stream. + */ + STDMETHOD_(IHXVolume*,GetAudioVolume) (THIS) PURE; +}; + +/**************************************************************************** + * + * Interface: + * + * IHXAudioDevice + * + * Purpose: + * + * Object that exports audio device API + * This interface is currently to be used ONLY by the RMA engine + * internally. + * + * IID_IHXAudioDevice: + * + * {00000703-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXAudioDevice, 0x00000703, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXAudioDevice + +DECLARE_INTERFACE_(IHXAudioDevice, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXAudioDevice methods + */ + + /************************************************************************ + * Method: + * IHXAudioDevice::Open + * Purpose: + * The caller calls this to open the audio device using the audio + * format given. + */ + STDMETHOD(Open) (THIS_ + const HXAudioFormat* /*IN*/ pAudioFormat, + IHXAudioDeviceResponse* /*IN*/ pStreamResponse) PURE; + + /************************************************************************ + * Method: + * IHXAudioDevice::Close + * Purpose: + * The caller calls this to close the audio device. + */ + STDMETHOD(Close) (THIS_ + const HXBOOL /*IN*/ bFlush ) PURE; + + /************************************************************************ + * Method: + * IHXAudioDevice::Resume + * Purpose: + * The caller calls this to start or resume audio playback. + */ + STDMETHOD(Resume) (THIS) PURE; + + /************************************************************************ + * Method: + * IHXAudioDevice::Pause + * Purpose: + * The caller calls this to pause the audio device. If bFlush is + * TRUE, any buffers in the audio device will be flushed; otherwise, + * the buffers are played. + */ + STDMETHOD(Pause) (THIS) PURE; + + /************************************************************************ + * Method: + * IHXAudioDevice::Write + * Purpose: + * The caller calls this to write an audio buffer. + */ + STDMETHOD(Write) (THIS_ + const HXAudioData* /*IN*/ pAudioData) PURE; + + /************************************************************************ + * Method: + * IHXAudioDevice::InitVolume + * Purpose: + * The caller calls this to inform the audio stream of the client's + * volume range. The audio stream maps the client's volume range + * into the audio device volume range. + * NOTE: This function returns TRUE if volume is supported by this + * audio device. + */ + STDMETHOD_(HXBOOL,InitVolume) (THIS_ + const UINT16 /*IN*/ uMinVolume, + const UINT16 /*IN*/ uMaxVolume) PURE; + + /************************************************************************ + * Method: + * IHXAudioDevice::SetVolume + * Purpose: + * The caller calls this to set the audio device volume level. + */ + STDMETHOD(SetVolume) (THIS_ + const UINT16 /*IN*/ uVolume) PURE; + + /************************************************************************ + * Method: + * IHXAudioDevice::GetVolume + * Purpose: + * The caller calls this to get the audio device volume level. + */ + STDMETHOD_(UINT16,GetVolume) (THIS) PURE; + + /************************************************************************ + * Method: + * IHXAudioDevice::Reset + * Purpose: + * The caller calls this to reset the audio device. + */ + STDMETHOD(Reset) (THIS) PURE; + + /************************************************************************ + * Method: + * IHXAudioDevice::Drain + * Purpose: + * The caller calls this to drain the audio device. + */ + STDMETHOD(Drain) (THIS) PURE; + + /************************************************************************ + * Method: + * IHXAudioDevice::CheckFormat + * Purpose: + * The caller calls this to check the input format with the + * audio device format. + */ + STDMETHOD(CheckFormat) (THIS_ + const HXAudioFormat* /*IN*/ pAudioFormat ) PURE; + + /************************************************************************ + * Method: + * IHXAudioDevice::GetCurrentAudioTime + * Purpose: + * The caller calls this to get current system audio time. + */ + STDMETHOD(GetCurrentAudioTime) (THIS_ + REF(ULONG32) /*OUT*/ ulCurrentTime) PURE; +}; + +/**************************************************************************** + * + * Interface: + * IHXAudioDeviceResponse + * + * Purpose: + * + * Object that exports audio device Response API + * This interface is currently to be used ONLY by the RMA engine + * internally. + * + * IID_IHXAudioDeviceResponse: + * + * {00000704-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXAudioDeviceResponse, 0x00000704, 0x901, 0x11d1, 0x8b, 0x6, + 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXAudioDeviceResponse + +DECLARE_INTERFACE_(IHXAudioDeviceResponse, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXAudioDeviceResponse methods + */ + + /************************************************************************ + * Method: + * IHXAudioDeviceResponse::OnTimeSync + * Purpose: + * Notification interface provided by users of the IHXAudioDevice + * interface. This method is called by the IHXAudioDevice when + * audio playback occurs. + */ + STDMETHOD(OnTimeSync) (THIS_ + ULONG32 /*IN*/ ulTimeEnd) PURE; +}; + +/**************************************************************************** + * + * Interface: + * + * IHXAudioHook + * + * Purpose: + * + * Clients must implement this interface to access pre- or post-mixed + * audio data. Use this interface to get post processed audio buffers and + * their associated audio format. + * + * IID_IHXAudioHook: + * + * {00000705-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXAudioHook, 0x00000705, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXAudioHook + +DECLARE_INTERFACE_(IHXAudioHook, IUnknown) +{ + /* + * IUnknown methods! + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXAudioHook methods + */ + /************************************************************************ + * Method: + * IHXAudioHook::OnInit + * Purpose: + * Audio Services calls OnInit() with the audio data format of the + * audio data that will be provided in the OnBuffer() method. + */ + STDMETHOD(OnInit) (THIS_ + HXAudioFormat* /*IN*/ pFormat) PURE; + + /************************************************************************ + * Method: + * IHXAudioHook::OnBuffer + * Purpose: + * Audio Services calls OnBuffer() with audio data packets. The + * renderer should not modify the data in the IHXBuffer part of + * pAudioInData. If the renderer wants to write a modified + * version of the data back to Audio Services, then it should + * create its own IHXBuffer, modify the data and then associate + * this buffer with the pAudioOutData->pData member. + */ + STDMETHOD(OnBuffer) (THIS_ + HXAudioData* /*IN*/ pAudioInData, + HXAudioData* /*OUT*/ pAudioOutData) PURE; +}; + +/**************************************************************************** + * + * Interface: + * + * IHXAudioDeviceHookManager + * + * Purpose: + * + * Allows setting audio hooks in the audio device itself. + * + * IID_IHXAudioDeviceHookManager: + * + * {00000715-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXAudioDeviceHookManager, 0x00000715, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXAudioDeviceHookManager + +DECLARE_INTERFACE_(IHXAudioDeviceHookManager, IUnknown) +{ + /* + * IUnknown methods! + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXAudioDeviceHookManager methods + */ + /************************************************************************ + * Method: + * IHXAudioDeviceHookManager::AddAudioDeviceHook + * Purpose: + * Last chance to modify data being written to the audio device. + */ + STDMETHOD(AddAudioDeviceHook) (THIS_ + IHXAudioHook* /*IN*/ pHook, + AudioDeviceHookType /*IN*/ type + ) PURE; + + /************************************************************************ + * Method: + * IHXAudioDeviceHookManager::RemoveAudioDeviceHook + * Purpose: + * Removes the audio device hook that was set with AddAudioDeviceHook. + */ + STDMETHOD(RemoveAudioDeviceHook) (THIS_ + IHXAudioHook* /*IN*/ pHook + ) PURE; + + /************************************************************************ + * Method: + * IHXAudioDeviceHookManager::ProcessHooks + * Purpose: + * Called by audio device implementations to process the hooks on a + * given audio buffer + */ + STDMETHOD(ProcessAudioDeviceHooks) (THIS_ + IHXBuffer*& /*IN/OUT*/ pBuffer, + HXBOOL& /*OUT*/ bChanged + ) PURE; +}; + +/**************************************************************************** + * + * Interface: + * + * IHXAudioStreamInfoResponse + * + * Purpose: + * + * Clients must implement this interface when interested in receiving + * notification of the total number of streams associated with this + * audio player. + * + * IID_IHXAudioStreamInfoResponse: + * + * {00000706-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXAudioStreamInfoResponse, 0x00000706, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXAudioStreamInfoResponse + +DECLARE_INTERFACE_(IHXAudioStreamInfoResponse, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXAudioStreamInfoResponse methods + */ + + /************************************************************************ + * Method: + * IHXAudioStreamInfoResponse::OnStream + * Purpose: + * The client implements this to get notification of streams + * associated with this player. Use + * AudioPlayer::SetStreamInfoResponse() to register your + * implementation with the AudioPlayer. Once player has been + * initialized, it will call OnStream() multiple times to pass all + * the streams. Since a stream can be added mid-presentation, + * IHXAudioStreamInfoResponse object should be written to handle + * OnStream() in the midst of the presentation as well. + */ + STDMETHOD(OnStream) (THIS_ + IHXAudioStream* /*IN*/ pAudioStream) PURE; +}; + +/**************************************************************************** + * + * Interface: + * + * IHXVolume + * + * Purpose: + * + * This interface provides access to Audio Services volume control. Use this + * interface to get, set, or receive notifications of volume changes. Audio + * Services implements IHXVolume for IHXAudioPlayer, IHXAudioStream and + * for the audio device. Clients can use the IHXVolume interface to get/set + * volume levels of each audio stream, to get/set volume levels for the + * audio player's mixed data, or to get/set the volume levels of the audio + * device. See AudioStream::GetStreamVolume() (TBD), AudioPlayer:: + * GetAudioVolume() and AudioPlayer::GetDeviceVolume(). + * + * IID_IHXVolume: + * + * {00000707-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXVolume, 0x00000707, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXVolume + +DECLARE_INTERFACE_(IHXVolume, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXVolume methods + */ + /************************************************************************ + * Method: + * IHXVolume::SetVolume + * Purpose: + * Call this to set the volume level. + */ + STDMETHOD(SetVolume) (THIS_ + const UINT16 /*IN*/ uVolume ) PURE; + + /************************************************************************ + * Method: + * IHXVolume::GetVolume + * Purpose: + * Call this to get the current volume level. + */ + STDMETHOD_(UINT16,GetVolume) (THIS) PURE; + + /************************************************************************ + * Method: + * IHXVolume::SetMute + * Purpose: + * Call this to mute the volume. + */ + STDMETHOD(SetMute) (THIS_ + const HXBOOL /*IN*/ bMute ) PURE; + + /************************************************************************ + * Method: + * IHXVolume::GetMute + * Purpose: + * Call this to determine if the volume is muted. + * + */ + STDMETHOD_(HXBOOL,GetMute) (THIS) PURE; + + /************************************************************************ + * Method: + * IHXVolume::AddAdviseSink + * Purpose: + * Call this to register an IHXVolumeAdviseSink. The advise sink + * methods: OnVolumeChange() and OnMuteChange() are called when + * ever IHXVolume::SetVolume() and IHXVolume::SetMute() are + * called. + */ + STDMETHOD(AddAdviseSink) (THIS_ + IHXVolumeAdviseSink* /*IN*/ pSink + ) PURE; + + /************************************************************************ + * Method: + * IHXVolume::RemoveAdviseSink + * Purpose: + * Call this to unregister an IHXVolumeAdviseSink. Use this when + * you are no longer interested in receiving volume or mute change + * notifications. + */ + STDMETHOD(RemoveAdviseSink) (THIS_ + IHXVolumeAdviseSink* /*IN*/ pSink + ) PURE; +}; + +/**************************************************************************** + * + * Interface: + * + * IHXVolumeAdviseSink + * + * Purpose: + * + * This interface provides access to notifications of volume changes. A + * client must implement this interface if they are interested in receiving + * notifications of volume level changes or mute state changes. A client must + * register their volume advise sink using IHXVolume::AddAdviseSink(). + * See the IHXVolume interface. + * + * IID_IHXVolumeAdviseSink: + * + * {00000708-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXVolumeAdviseSink, 0x00000708, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXVolumeAdviseSink + +DECLARE_INTERFACE_(IHXVolumeAdviseSink, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXVolumeAdviseSink methods + */ + + /************************************************************************ + * Method: + * IHXVolumeAdviseSink::OnVolumeChange + * Purpose: + * This interface is called whenever the associated IHXVolume + * SetVolume() is called. + */ + STDMETHOD(OnVolumeChange) (THIS_ + const UINT16 uVolume + ) PURE; + + /************************************************************************ + * Method: + * IHXVolumeAdviseSink::OnMuteChange + * Purpose: + * This interface is called whenever the associated IHXVolume + * SetMute() is called. + * + */ + STDMETHOD(OnMuteChange) (THIS_ + const HXBOOL bMute + ) PURE; +}; + +/**************************************************************************** + * + * Interface: + * + * IHXAudioLevelNormalization + * + * IID_IHXAudioLevelNormalization: + * + * {00000716-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXAudioLevelNormalization, 0x00000716, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXAudioLevelNormalization + +DECLARE_INTERFACE_(IHXAudioLevelNormalization, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXAudioLevelNormalization methods + */ + STDMETHOD(SetSoundLevelOffset) (THIS_ INT16 nOffset) PURE; + STDMETHOD_(INT16, GetSoundLevelOffset)(THIS) PURE; +}; + +/**************************************************************************** + * + * Interface: + * + * IHXDryNotification + * + * Purpose: + * + * Audio Renderer should implement this if it needs notification when the + * audio stream is running dry. + * + * IID_IHXDryNotification: + * + * {00000709-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXDryNotification, 0x00000709, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXDryNotification + +DECLARE_INTERFACE_(IHXDryNotification, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXDryNotification methods + */ + + /************************************************************************ + * Method: + * IHXDryNotification::OnDryNotification + * Purpose: + * This function is called when it is time to write to audio device + * and there is not enough data in the audio stream. The renderer can + * then decide to add more data to the audio stream. This should be + * done synchronously within the call to this function. + * It is OK to not write any data. Silence will be played instead. + */ + STDMETHOD(OnDryNotification) (THIS_ + UINT32 /*IN*/ ulCurrentStreamTime, + UINT32 /*IN*/ ulMinimumDurationRequired + ) PURE; +}; + + +/**************************************************************************** + * + * Interface: + * + * IHXAudioDeviceManager + * + * Purpose: + * + * Allows the default audio device to be replaced. + * + * IID_IHXAudioDeviceManager: + * + * {0000070A-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXAudioDeviceManager, 0x0000070A, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXAudioDeviceManager + +DECLARE_INTERFACE_(IHXAudioDeviceManager, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXAudioDeviceManager methods + */ + + /********************************************************************** + * Method: + * IHXAudioDeviceManager::Replace + * Purpose: + * This is used to replace the default implementation of the audio + * device by the given audio device interface. + */ + STDMETHOD(Replace) (THIS_ + IHXAudioDevice* /*IN*/ pAudioDevice) PURE; + + /********************************************************************** + * Method: + * IHXAudioDeviceManager::Remove + * Purpose: + * This is used to remove the audio device given to the manager in + * the earlier call to Replace. + */ + STDMETHOD(Remove) (THIS_ + IHXAudioDevice* /*IN*/ pAudioDevice) PURE; + + /************************************************************************ + * Method: + * IHXAudioDeviceManager::AddFinalHook + * Purpose: + * One last chance to modify data being written to the audio device. + * This hook allows the user to change the audio format that + * is to be written to the audio device. This can be done in call + * to OnInit() in IHXAudioHook. + */ + STDMETHOD(SetFinalHook) (THIS_ + IHXAudioHook* /*IN*/ pHook + ) PURE; + + /************************************************************************ + * Method: + * IHXAudioDeviceManager::RemoveFinalHook + * Purpose: + * Remove final hook + */ + STDMETHOD(RemoveFinalHook) (THIS_ + IHXAudioHook* /*IN*/ pHook + ) PURE; + + /************************************************************************ + * Method: + * IHXAudioDeviceManager::GetAudioFormat + * Purpose: + * Returns the audio format in which the audio device is opened. + * This function will fill in the pre-allocated HXAudioFormat + * structure passed in. + */ + STDMETHOD(GetAudioFormat) (THIS_ + HXAudioFormat* /*IN/OUT*/pAudioFormat) PURE; +}; + +/**************************************************************************** + * + * Interface: + * + * IHXAudioCrossFade + * + * Purpose: + * + * This interface can be used to cross-fade two audio streams. It is exposed + * by IHXAudioPlayer + * + * IID_IHXAudioCrossFade: + * + * {0000070B-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXAudioCrossFade, 0x0000070B, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXAudioCrossFade + +DECLARE_INTERFACE_(IHXAudioCrossFade, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXAudioCrossFade methods + */ + + /************************************************************************ + * Method: + * IHXAudioCrossFade::CrossFade + * Purpose: + * Cross-fade two audio streams. + * pStreamFrom - Stream to be cross faded from + * pStreamTo - Stream to be cross faded to + * ulFromCrossFadeStartTime- "From" Stream time when cross fade is + * to be started + * ulToCrossFadeStartTime - "To" Stream time when cross fade is to + * be started + * ulCrossFadeDuration - Duration over which cross-fade needs + * to be done + * + */ + STDMETHOD(CrossFade) (THIS_ + IHXAudioStream* pStreamFrom, + IHXAudioStream* pStreamTo, + UINT32 ulFromCrossFadeStartTime, + UINT32 ulToCrossFadeStartTime, + UINT32 ulCrossFadeDuration) PURE; +}; + +/**************************************************************************** + * + * Interface: + * + * IHXAudioStream2 + * + * Purpose: + * + * This interface contains some last-minute added audio stream functions + * + * IID_IHXAudioStream2: + * + * {0000070C-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXAudioStream2, 0x0000070C, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXAudioStream2 + +DECLARE_INTERFACE_(IHXAudioStream2, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXAudioStream2 methods + */ + /************************************************************************ + * Method: + * IHXAudioStream2::RemoveDryNotification + * Purpose: + * Use this to remove itself from the notification response object + * during the stream switching. + */ + STDMETHOD(RemoveDryNotification) (THIS_ + IHXDryNotification* /*IN*/ pNotification + ) PURE; + + /************************************************************************ + * Method: + * IHXAudioStream2::GetAudioFormat + * Purpose: + * Returns the input audio format of the data written by the + * renderer. This function will fill in the pre-allocated + * HXAudioFormat structure passed in. + */ + STDMETHOD(GetAudioFormat) (THIS_ + HXAudioFormat* /*IN/OUT*/pAudioFormat) PURE; +}; + + +/**************************************************************************** + * + * Interface: + * + * IHXAudioPushdown + * + * Purpose: + * + * This interface can be used to setup the audio pushdown time. + * + * IID_IHXAudioPushdown: + * + * {0000070D-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXAudioPushdown, 0x0000070D, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXAudioPushdown + +DECLARE_INTERFACE_(IHXAudioPushdown, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXAudioPushdown methods + */ + /************************************************************************ + * Method: + * IHXAudioPushdown::SetAudioPushdown + * Purpose: + * Use this to set the minimum audio pushdown value in ms. + * This is the amount of audio data that is being written + * to the audio device before starting playback. + */ + STDMETHOD(SetAudioPushdown) (THIS_ + UINT32 /*IN*/ ulAudioPushdown + ) PURE; +}; + + +/**************************************************************************** + * + * Interface: + * + * IHXAudioHookManager + * + * Purpose: + * + * This interface can be used to add a hook at the audio device layer. + * + * IID_IHXAudioHookManager: + * + * {0000070E-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXAudioHookManager, 0x0000070E, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXAudioHookManager + +DECLARE_INTERFACE_(IHXAudioHookManager, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXAudioHookManager methods + */ + /************************************************************************ + * Method: + * IHXAudioHookManager::AddHook + * Purpose: + * Use this to add a hook + */ + STDMETHOD(AddHook) (THIS_ + IHXAudioHook* /*IN*/ pHook + ) PURE; + + /************************************************************************ + * Method: + * IHXAudioHookManager::RemoveHook + * Purpose: + * Use this to remove a hook + */ + STDMETHOD(RemoveHook) (THIS_ + IHXAudioHook* /*IN*/ pHook + ) PURE; +}; + +// $Private: +/**************************************************************************** + * + * Interface: + * + * IHXMultiPlayPauseSupport + * + * Purpose: + * + * This interface can be used to control whether audio services handles multi-player pause / rewind support + * + * IID_IHXMultiPlayPauseSupport: + * + * {0000070F-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXMultiPlayPauseSupport, + 0x0000070F, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXMultiPlayPauseSupport + +DECLARE_INTERFACE_(IHXMultiPlayPauseSupport, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXMultiPlayPauseSupport methods + */ + STDMETHOD_(HXBOOL,GetDisableMultiPlayPauseSupport) (THIS) PURE; +}; + +/**************************************************************************** + * + * Interface: + * + * IHXAudioDeviceManager2 + * + * Purpose: + * + * Audio Device Manager extension + * + * IID_IHXAudioDeviceManager2: + * + * {00000710-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXAudioDeviceManager2, 0x00000710, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXAudioDeviceManager2 + +DECLARE_INTERFACE_(IHXAudioDeviceManager2, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXAudioDeviceManager2 methods + */ + + /********************************************************************** + * Method: + * IHXAudioDeviceManager2::IsReplacedDevice + * Purpose: + * This is used to determine if the audio device has been replaced. + */ + STDMETHOD_(HXBOOL, IsReplacedDevice) (THIS) PURE; + +}; + + +/**************************************************************************** + * + * Interface: + * + * IHXAudioResampler + * + * Purpose: + * + * Audio Resampler + * + * IID_IHXAudioResampler: + * + * {00000711-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXAudioResampler, 0x00000711, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXAudioResampler + +DECLARE_INTERFACE_(IHXAudioResampler, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXAudioResampler methods + */ + + /********************************************************************** + * Method: + * IHXAudioResampler::Resample + * Purpose: + * Will produce 1 output frame for every (upFactor/downFactor) inputs + * frames, straddling if not an integer. Works down to 1 sample/call. + * + * Returns actual number of output frames. + ***********************************************************************/ + + STDMETHOD_(UINT32, Resample) ( THIS_ + UINT16* pInput, + UINT32 ulInputBytes, + UINT16* pOutput) PURE; + + /********************************************************************** + * Method: + * IHXAudioResampler::Requires + * Purpose: + * Returns number of input frames required to produce this number + * of output frames, given the current state of the filter. + */ + + STDMETHOD_(UINT32, Requires) ( THIS_ + UINT32 ulOutputFrames) PURE; +}; + +/**************************************************************************** + * + * Interface: + * + * IHXAudioResamplerManager + * + * Purpose: + * + * Audio Resampler Manager + * + * IID_IHXAudioResamplerManager: + * + * {00000712-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXAudioResamplerManager, 0x00000712, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXAudioResamplerManager + +DECLARE_INTERFACE_(IHXAudioResamplerManager, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXAudioResamplerManager methods + * + */ + STDMETHOD(CreateResampler) (THIS_ + HXAudioFormat inAudioFormat, + REF(HXAudioFormat) outAudioFormat, + REF(IHXAudioResampler*) pResampler) PURE; +}; + +/**************************************************************************** + * + * Interface: + * + * IHXAudioPushdown2 + * + * Purpose: + * + * Audio PushDown access methods + * + * IID_IHXAudioPushdown2: + * + * {00000713-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXAudioPushdown2, 0x00000713, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXAudioPushdown2 + +DECLARE_INTERFACE_(IHXAudioPushdown2, IHXAudioPushdown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /************************************************************************ + * Method: + * IHXAudioPushdown::SetAudioPushdown + * Purpose: + * Use this to set the minimum audio pushdown value in ms. + * This is the amount of audio data that is being written + * to the audio device before starting playback. + */ + STDMETHOD(SetAudioPushdown) (THIS_ + UINT32 /*IN */ ulAudioPushdown) PURE; + + /************************************************************************ + * Method: + * IHXAudioPushdown2::GetAudioPushdown + * Purpose: + * Use this to get the minimum audio pushdown value in ms. + * This is the amount of audio data that is being written + * to the audio device before starting playback. + */ + STDMETHOD(GetAudioPushdown) (THIS_ + REF(UINT32) /*OUT*/ ulAudioPushdown) PURE; + + /************************************************************************ + * Method: + * IHXAudioPushdown2::GetCurrentAudioDevicePushdown + * Purpose: + * Use this to get the audio pushed down to the audio device and haven't + * been played yet + */ + STDMETHOD(GetCurrentAudioDevicePushdown) (THIS_ + REF(UINT32) /*OUT*/ ulAudioPusheddown) PURE; +}; + +/**************************************************************************** + * + * Interface: + * + * IHXAudioMultiChannel + * + * Purpose: + * + * Multi-channel audio support + * + * IID_IHXAudioMultiChannel: + * + * {00000714-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXAudioMultiChannel, 0x00000714, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXAudioMultiChannel + +DECLARE_INTERFACE_(IHXAudioMultiChannel, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXAudioMultiChannel methods + */ + STDMETHOD_(HXBOOL,GetMultiChannelSupport) (THIS) PURE; +}; +// $EndPrivate. + + +#if defined(HELIX_FEATURE_TIMELINE_WATCHER) + +// Timeline watcher. +// {211A3CAE-F1DA-4678-84D5-0F12E7B1D8C6} +DEFINE_GUID(IID_IHXTimelineWatcher, + 0x211a3cae, 0xf1da, 0x4678, 0x84, 0xd5, 0xf, 0x12, 0xe7, 0xb1, 0xd8, 0xc6); +#undef INTERFACE +#define INTERFACE IHXTimelineWatcher +DECLARE_INTERFACE_(IHXTimelineWatcher, IUnknown) +{ + STDMETHOD(QueryInterface) (THIS_ REFIID riid, void** ppvObj) PURE; + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + STDMETHOD(OnPause) (THIS) PURE; + STDMETHOD(OnResume) (THIS) PURE; + STDMETHOD(OnClose) (THIS) PURE; + STDMETHOD(OnTimeSync) (THIS_ UINT32 currentTime ) PURE; +}; + +// TimelineManager +// {9ED91BC3-9E92-46bb-A094-6C8B9416CFB6} +DEFINE_GUID(IID_IHXTimelineManager, + 0x9ed91bc3, 0x9e92, 0x46bb, 0xa0, 0x94, 0x6c, 0x8b, 0x94, 0x16, 0xcf, 0xb6); +#undef INTERFACE +#define INTERFACE IHXTimelineManager +DECLARE_INTERFACE_(IHXTimelineManager, IUnknown) +{ + STDMETHOD(QueryInterface) (THIS_ REFIID riid, void** ppvObj) PURE; + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + STDMETHOD(AddTimelineWatcher) (THIS_ IHXTimelineWatcher* ) PURE; + STDMETHOD(RemoveTimelineWatcher) (THIS_ IHXTimelineWatcher* ) PURE; +}; +#endif /* #if defined(HELIX_FEATURE_TIMELINE_WATCHER) */ + + +#endif /* _HXAUSVC_H_ */ diff --git a/amarok/src/engine/helix/helix-sp/helix-include/common/include/hxauth.h b/amarok/src/engine/helix/helix-sp/helix-include/common/include/hxauth.h new file mode 100644 index 00000000..903c835f --- /dev/null +++ b/amarok/src/engine/helix/helix-sp/helix-include/common/include/hxauth.h @@ -0,0 +1,385 @@ + +/* + * + * This software is released under the provisions of the GPL version 2. + * see file "COPYING". If that file is not available, the full statement + * of the license can be found at + * + * http://www.fsf.org/licensing/licenses/gpl.txt + * + * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved. + * + */ + +#ifndef _HXAUTH_H_ +#define _HXAUTH_H_ + +/* + * Forward declarations of some interfaces defined or used here-in. + */ +typedef _INTERFACE IHXAuthenticator IHXAuthenticator; +typedef _INTERFACE IHXAuthenticatorResponse IHXAuthenticatorResponse; +typedef _INTERFACE IHXAuthenticatorRequest IHXAuthenticatorRequest; +typedef _INTERFACE IHXPassword IHXPassword; +typedef _INTERFACE IHXAuthenticationManagerResponse IHXAuthenticationManagerResponse; +typedef _INTERFACE IHXValues IHXValues; +typedef _INTERFACE IHXBuffer IHXBuffer; + +/**************************************************************************** + * + * Interface: + * + * IHXAuthenticator + * + * Purpose: + * + * Provide a means of authenticating users. + * + * IID_IHXAuthenticator: + * + * {00001800-0901-11d1-8B06-00A024406D59} + * + */ + +DEFINE_GUID(IID_IHXAuthenticator, 0x00001800, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#define CLSID_IHXAuthenticator IID_IHXAuthenticator + +#undef INTERFACE +#define INTERFACE IHXAuthenticator + +DECLARE_INTERFACE_(IHXAuthenticator, IUnknown) +{ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * InitAuthenticator is called by the creator of the Authenticator + * object in order to pass it an IHXAuthenticatorRequest object, + * usually implemented by the creator itself. + */ + STDMETHOD(InitAuthenticator) (THIS_ + IHXAuthenticatorRequest* pRequest) PURE; + /* + * Authenticate is called by a file object (and others??) + * when it wants to ask the creator, presumably an FS Manager, + * for authorization to open it's file. + * + * Authenticate will call IHXAuthenticateResponse::AuthenticateDone + * when done with HXR_OK or an error. + * + * File objects will presumably perform the Authenticate response as + * part of their Init() call, and not call InitDone until they receive + * a response one way or the other. + */ + STDMETHOD(Authenticate) (THIS_ + IHXValues* pValues, + IHXAuthenticatorResponse* pResponse) PURE; + + /* GenerateAuthRequest is called by the creator of this object + * when they want to send an authentication request to someone. + */ + STDMETHOD(GenerateAuthRequest) (THIS_ + UINT32 authType, + REF(IHXValues*) pValues) PURE; + /* + * AuthValuesReady is called by IHXAuthenticatorRequest when it + * is ready to respond to a GetAuthValues request. + */ + STDMETHOD(AuthValuesReady) (THIS_ + HX_RESULT result, + IHXValues* pValues) PURE; +}; + + +/**************************************************************************** + * + * Interface: + * + * IHXAuthenticatorResponse + * + * Purpose: + * + * Response object for the Authenticator class. + * + * IID_IHXAuthenticatorResponse: + * + * {00001801-0901-11d1-8B06-00A024406D59} + * + */ + +DEFINE_GUID(IID_IHXAuthenticatorResponse, 0x00001801, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXAuthenticatorResponse + +DECLARE_INTERFACE_(IHXAuthenticatorResponse, IUnknown) +{ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* AuthenticateDone is called by an IHXAuthenticator when it has + * finished it's authorization steps. If the result is HXR_OK, + * then the values contain authorization information as generated by + * IHXPassword. + */ + STDMETHOD(AuthenticateDone) (THIS_ HX_RESULT result, + IHXValues* pAuthResponseValues) PURE; +}; + + +/**************************************************************************** + * + * Interface: + * + * IHXAuthenticatorRequest + * + * Purpose: + * + * Request object for the Authenticator class. + * + * IID_IHXAuthenticatorRequest: + * + * {00001802-0901-11d1-8B06-00A024406D59} + * + */ + +DEFINE_GUID(IID_IHXAuthenticatorRequest, 0x00001802, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXAuthenticatorRequest + +DECLARE_INTERFACE_(IHXAuthenticatorRequest, IUnknown) +{ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* GetAuthValues is called by the Authenticator object when it + * needs to know the authorization info for this transaction. + * + * This object should call AuthValuesReady when ready. + */ + STDMETHOD(GetAuthValues) (THIS_ IHXValues* pOrigValues) PURE; +}; + + +/**************************************************************************** + * + * Interface: + * + * IHXPassword + * + * Purpose: + * + * Provides a general password facility for storing of passwords in + * an encrypted form and a facility for verifying passwords securely + * over the network. + * + * IID_IHXPassword: + * + * {00001700-0901-11d1-8B06-00A024406D59} + * + */ + +#define HX_AUTH_BASIC 1 +#define HX_AUTH_DIGEST 2 +#define PN_AUTH_HX5 3 +#define HX_AUTH_NTLM 4 + +DEFINE_GUID(IID_IHXPassword, 0x00001700, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXPassword + +DECLARE_INTERFACE_(IHXPassword, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXPassword methods + */ + + STDMETHOD(Crypt) (THIS_ IHXValues* pAuthentication) PURE; + STDMETHOD(Verify) (THIS_ IHXValues* pAuth1, IHXValues* pAuth2) PURE; + + STDMETHOD(AsString) (THIS_ IHXValues* pAuth, REF(IHXBuffer*) pBuffer) PURE; + STDMETHOD(AsValues) (THIS_ const char* str, IHXValues* pValues) PURE; + + /* + * CreateBuffer is provided for the convenince of external users, + * who would otherwise have to get a context and common class factory + * just to create IHXBuffers. This method can be used instead, but + * is not advisable if other means are available. + */ + STDMETHOD(CreateBuffer) (THIS_ REF(IHXBuffer*) pBuffer) PURE; + + /* + * Ditto for CreateValues + */ + STDMETHOD(CreateValues) (THIS_ REF(IHXValues*) pValues) PURE; +}; + + +/**************************************************************************** + * + * Interface: + * + * IHXAuthenticationManager + * + * Purpose: + * + * Provide a means of authenticating users. + * + * IID_IHXAuthenticator: + * + * {00001a00-0901-11d1-8B06-00A024406D59} + * + */ + +DEFINE_GUID(IID_IHXAuthenticationManager, 0x00001a00, 0x901, 0x11d1, + 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXAuthenticationManager + +DECLARE_INTERFACE_(IHXAuthenticationManager, IUnknown) +{ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* HandleAuthenticationRequest is called when the core wants us to get + * a username and password. + */ + STDMETHOD(HandleAuthenticationRequest) ( + THIS_ IHXAuthenticationManagerResponse* pResponse) PURE; +}; + + +/**************************************************************************** + * + * Interface: + * + * IHXAuthenticationManager2 + * + * Purpose: + * + * Provide a means of authenticating users. + * + * Includes sending an IHXValues list to the + * authentication manager, for support of proxy + * authentication, for example, which may include + * a "pseudonym" header or something. + * + * IID_IHXAuthenticator2: + * + * {34e171d2-a8f0-4832-bc7d-06dfe3ae58fd} + * + */ + +DEFINE_GUID(IID_IHXAuthenticationManager2, + 0x34e171d2, 0xa8f0, 0x4832, 0xbc, 0x7d, 0x06, + 0xdf, 0xe3, 0xae, 0x58, 0xfd); + +#undef INTERFACE +#define INTERFACE IHXAuthenticationManager2 + +DECLARE_INTERFACE_(IHXAuthenticationManager2, IUnknown) +{ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* HandleAuthenticationRequest2 is called when the core wants us to get + * a username and password. + */ + STDMETHOD(HandleAuthenticationRequest2) ( + THIS_ + IHXAuthenticationManagerResponse* pResponse, + IHXValues* pHeader) PURE; +}; + + +/**************************************************************************** + * + * Interface: + * + * IHXAuthenticationManagerResponse + * + * Purpose: + * + * Response object for IHXAuthenticationManager. + * + * IID_IHXAuthenticator: + * + * {00001a01-0901-11d1-8B06-00A024406D59} + * + */ + +DEFINE_GUID(IID_IHXAuthenticationManagerResponse, 0x00001a01, 0x901, 0x11d1, + 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59); + + +#undef INTERFACE +#define INTERFACE IHXAuthenticationManagerResponse + +DECLARE_INTERFACE_(IHXAuthenticationManagerResponse, IUnknown) +{ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* HandleAuthenticationRequest is called when the core wants us to get + * a username and password. + */ + STDMETHOD(AuthenticationRequestDone) (THIS_ + HX_RESULT result, + const char* pUserName, + const char* pPassword) PURE; +}; + +#ifdef _MACINTOSH +#pragma export on +#endif + +STDAPI CreatePassword(IUnknown** /* OUT */ ppIUnknown); + +#ifdef _MACINTOSH +#pragma export off +#endif + +#endif /* _HXAUTH_H_ */ diff --git a/amarok/src/engine/helix/helix-sp/helix-include/common/include/hxccf.h b/amarok/src/engine/helix/helix-sp/helix-include/common/include/hxccf.h new file mode 100644 index 00000000..b99d4bdb --- /dev/null +++ b/amarok/src/engine/helix/helix-sp/helix-include/common/include/hxccf.h @@ -0,0 +1,106 @@ + +/* + * + * This software is released under the provisions of the GPL version 2. + * see file "COPYING". If that file is not available, the full statement + * of the license can be found at + * + * http://www.fsf.org/licensing/licenses/gpl.txt + * + * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved. + * + */ + +#ifndef _HXCCF_H_ +#define _HXCCF_H_ + +/* + * Forward declarations of some interfaces defined here-in. + */ + +typedef _INTERFACE IHXCommonClassFactory IHXCommonClassFactory; + + +/**************************************************************************** + * + * Interface: + * + * IHXCommonClassFactory + * + * Purpose: + * + * RMA interface that manages the creation of common RMA classes. + * + * IID_IHXCommonClassFactory: + * + * {00000000-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXCommonClassFactory, 0x00000000, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXCommonClassFactory + +DECLARE_INTERFACE_(IHXCommonClassFactory, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXCommonClassFactory methods + */ + + /************************************************************************ + * Method: + * IHXCommonClassFactory::CreateInstance + * Purpose: + * Creates instances of common objects supported by the system, + * like IHXBuffer, IHXPacket, IHXValues, etc. + * + * This method is similar to Window's CoCreateInstance() in its + * purpose, except that it only creates objects of a well known + * types. + * + * NOTE: Aggregation is never used. Therefore and outer unknown is + * not passed to this function, and you do not need to code for this + * situation. + */ + STDMETHOD(CreateInstance) (THIS_ + REFCLSID /*IN*/ rclsid, + void** /*OUT*/ ppUnknown) PURE; + + /************************************************************************ + * Method: + * IHXController::CreateInstanceAggregatable + * Purpose: + * Creates instances of common objects that can be aggregated + * supported by the system, like IHXSiteWindowed + * + * This method is similar to Window's CoCreateInstance() in its + * purpose, except that it only creates objects of a well known + * types. + * + * NOTE 1: Unlike CreateInstance, this method will create internal + * objects that support Aggregation. + * + * NOTE 2: The output interface is always the non-delegating + * IUnknown. + */ + STDMETHOD(CreateInstanceAggregatable) + (THIS_ + REFCLSID /*IN*/ rclsid, + REF(IUnknown*) /*OUT*/ ppUnknown, + IUnknown* /*IN*/ pUnkOuter) PURE; +}; + + +#endif /*_HXCCF_H_*/ diff --git a/amarok/src/engine/helix/helix-sp/helix-include/common/include/hxcom.h b/amarok/src/engine/helix/helix-sp/helix-include/common/include/hxcom.h new file mode 100644 index 00000000..9a5f6891 --- /dev/null +++ b/amarok/src/engine/helix/helix-sp/helix-include/common/include/hxcom.h @@ -0,0 +1,862 @@ + +/* + * + * This software is released under the provisions of the GPL version 2. + * see file "COPYING". If that file is not available, the full statement + * of the license can be found at + * + * http://www.fsf.org/licensing/licenses/gpl.txt + * + * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved. + * + */ + +#ifndef _HXCOM_H_ +#define _HXCOM_H_ + +#include "hxtypes.h" /* Needed for most type definitions */ + +// have to use the double expansion to get the prescan level + +#define STATCONCAT1(w,x,y,z) STATCONCAT2(w,x,y,z) +#define STATCONCAT2(w,x,y,z) w##x##y##z + +#ifdef _STATICALLY_LINKED +#ifndef _PLUGINNAME +#define ENTRYPOINT(func) STATCONCAT1(entrypoint_error_symbol_should_not_be_needed,_PLUGINNAME,_,func) +#else /* _PLUGINNAME */ +#define ENTRYPOINT(func) STATCONCAT1(entrypoint_for_,_PLUGINNAME,_,func) +#endif +#else /* _STATICALLY_LINKED */ +#define ENTRYPOINT(func) func +#endif + +/* + * We include objbase.h when building for windows so that hxcom.h can + * easily be used in any windows code. + */ +#ifdef _WIN32 +#include "hlxclib/sys/socket.h" +#include <objbase.h> +#endif /* _WIN32 */ + + +#include "hxresult.h" + +/* + * REF: + * Use this for reference parameters, so that C users can + * use the interface as well. + */ +#if defined(__cplusplus) +#define REF(type) type& +#else +#define REF(type) const type * const +#endif + +/* + * CONSTMETHOD: + * Use this for constant methods in an interface + * Compiles away under C + */ +#if !defined( CONSTMETHOD ) + +#if defined(__cplusplus) +#define CONSTMETHOD const +#else +#define CONSTMETHOD +#endif + +#endif +/* + * CALL: + * + * Used by C users to easily call a function through an interface + * + * EXAMPLE: + * + * pIFooObject->CALL(IFoo,DoSomething)(bar); + * + */ +#if !defined(__cplusplus) || defined(CINTERFACE) +#define CALL(iface, func) iface##Vtbl->func +#endif + + +#define _INTERFACE struct + +/* + * If using windows.h or the windows implementation of COM + * these defines are not needed. + */ +#if !defined( _OBJBASE_H_ ) + +#ifdef _WIN16 +typedef unsigned int MMRESULT; +#define FAR _far +#else +#define FAR +#endif /* WIN16 */ +#define PASCAL _pascal +#define CDECL _cdecl + +/* + * EXTERN_C + */ +#ifndef EXTERN_C +#ifdef __cplusplus +#define EXTERN_C extern "C" +#else +#define EXTERN_C extern +#endif +#endif + +#ifdef OLDERRORCODES +#ifndef MAKE_HRESULT +#define MAKE_HRESULT(sev,fac,code) ((HRESULT) (((unsigned long)(sev)<<31) | ((unsigned long)(fac)<<16) | ((unsigned long)(code)))) +#endif /*MAKE_HRESULT*/ +#endif /* OLDERRORCODES */ + +/* + * STDMETHODCALLTYPE + */ +#ifndef STDMETHODCALLTYPE +#if defined(_WIN32) || defined(_MPPC_) +#ifdef _MPPC_ +#define STDMETHODCALLTYPE __cdecl +#else +#define STDMETHODCALLTYPE __stdcall +#endif +#elif defined(_WIN16) +#define STDMETHODCALLTYPE __export FAR CDECL +#else +#define STDMETHODCALLTYPE +#endif +#endif + +/* + * STDMETHODVCALLTYPE + */ +#ifndef STDMETHODVCALLTYPE +#if defined(_WINDOWS) || defined(_MPPC_) +#define STDMETHODVCALLTYPE __cdecl +#else +#define STDMETHODVCALLTYPE +#endif +#endif + +/* + * STDAPICALLTYPE + */ +#ifndef STDAPICALLTYPE +#if defined(_WIN32) || defined(_MPPC_) +#define STDAPICALLTYPE __stdcall +#elif defined(_WIN16) +#define STDAPICALLTYPE __export FAR PASCAL +#else +#define STDAPICALLTYPE +#endif +#endif + +/* + * STDAPIVCALLTYPE + */ +#ifndef STDAPIVCALLTYPE +#if defined(_WINDOWS) || defined(_MPPC_) +#define STDAPIVCALLTYPE __cdecl +#else +#define STDAPIVCALLTYPE +#endif +#endif + +/* + * Standard API defines: + * + * NOTE: the 'V' versions allow Variable Argument lists. + * + * STDAPI + * STDAPI_(type) + * STDAPIV + * STDAPIV_(type) + */ +#ifndef STDAPI +#define STDAPI EXTERN_C HX_RESULT STDAPICALLTYPE +#endif +#ifndef STDAPI_ +#define STDAPI_(type) EXTERN_C type STDAPICALLTYPE +#endif +#ifndef STDAPIV +#define STDAPIV EXTERN_C HX_RESULT STDAPIVCALLTYPE +#endif +#ifndef STDAPIV_ +#define STDAPIV_(type) EXTERN_C type STDAPIVCALLTYPE +#endif + + +/* + * Standard Interface Method defines: + * + * NOTE: the 'V' versions allow Variable Argument lists. + * + * STDMETHODIMP + * STDMETHODIMP_(type) + * STDMETHODIMPV + * STDMETHODIMPV_(type) + */ +#ifndef STDMETHODIMP +#define STDMETHODIMP HX_RESULT STDMETHODCALLTYPE +#endif +#ifndef STDMETHODIMP_ +#define STDMETHODIMP_(type) type STDMETHODCALLTYPE +#endif +#ifndef STDMETHODIMPV +#define STDMETHODIMPV HX_RESULT STDMETHODVCALLTYPE +#endif +#ifndef STDMETHODIMPV_ +#define STDMETHODIMPV_(type) type STDMETHODVCALLTYPE +#endif + + +#if defined(__cplusplus) && !defined(CINTERFACE) +#define _INTERFACE struct +#define STDMETHOD(method) virtual HX_RESULT STDMETHODCALLTYPE method +#define STDMETHOD_(type,method) virtual type STDMETHODCALLTYPE method +#define PURE = 0 +#define THIS_ +#define THIS void + +#if defined(_WINDOWS) && defined(EXPORT_CLASSES) +#define DECLARE_INTERFACE(iface) _INTERFACE HXEXPORT_CLASS iface +#define DECLARE_INTERFACE_(iface, baseiface) _INTERFACE HXEXPORT_CLASS iface : public baseiface +#else +#define DECLARE_INTERFACE(iface) _INTERFACE iface +#define DECLARE_INTERFACE_(iface, baseiface) _INTERFACE iface : public baseiface +#endif // defined(_WINDOWS) && defined(EXPORT_CLASSES) + +#if !defined(BEGIN_INTERFACE) +#if defined(_MPPC_) && \ + ( (defined(_MSC_VER) || defined(__SC__) || defined(__MWERKS__)) && \ + !defined(NO_NULL_VTABLE_ENTRY) ) + #define BEGIN_INTERFACE virtual void a() {} + #define END_INTERFACE +#else + #define BEGIN_INTERFACE + #define END_INTERFACE +#endif +#endif + + +#else + +#define _INTERFACE struct + +#define STDMETHOD(method) HX_RESULT (STDMETHODCALLTYPE * method) +#define STDMETHOD_(type,method) type (STDMETHODCALLTYPE * method) + +#if !defined(BEGIN_INTERFACE) +#if defined(_MPPC_) + #define BEGIN_INTERFACE void *b; + #define END_INTERFACE +#else + #define BEGIN_INTERFACE + #define END_INTERFACE +#endif +#endif + + +#define PURE +#define THIS_ INTERFACE FAR* This, +#define THIS INTERFACE FAR* This +#ifdef CONST_VTABLE +#undef CONST_VTBL +#define CONST_VTBL const +#define DECLARE_INTERFACE(iface) typedef _INTERFACE iface { \ + const struct iface##Vtbl FAR* lpVtbl; \ + } iface; \ + typedef const struct iface##Vtbl iface##Vtbl; \ + const struct iface##Vtbl +#else +#undef CONST_VTBL +#define CONST_VTBL +#define DECLARE_INTERFACE(iface) typedef _INTERFACE iface { \ + struct iface##Vtbl FAR* lpVtbl; \ + } iface; \ + typedef struct iface##Vtbl iface##Vtbl; \ + struct iface##Vtbl +#endif +#define DECLARE_INTERFACE_(iface, baseiface) DECLARE_INTERFACE(iface) + +#endif + +/* + * COMMON TYPES + */ + +#if !defined(HELIX_FEATURE_FULLGUID) + +#define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) + +#define DEFINE_GUID_ENUM(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ + name = l + w1 * 3 + w2 * 5 + b1 * 7 + b2 * 11 + b3 * 13 + b4 * 17 + \ + b5 * 19 + b6 * 23 + b7 * 29 + b8 * 31, + +#ifndef GUID_DEFINED +#define GUID_DEFINED +typedef enum _GUIDNoConflict +{ + GUID_NULL, + DEFINE_GUID_ENUM(IID_IUnknown, 0x00000000, 0x0000, 0x0000, 0xC0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46) + DEFINE_GUID_ENUM(IID_IMalloc, 0x00000002, 0x0000, 0x0000, 0xC0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46) +#include "hxiids.h" +#include "hxpiids.h" + NUM_GUIDS +} GUID; +#endif + +#else /* #if !defined(HELIX_FEATURE_FULLGUID) */ +#ifndef GUID_DEFINED +#define GUID_DEFINED +typedef struct _GUID + { + ULONG32 Data1; + UINT16 Data2; + UINT16 Data3; + UCHAR Data4[ 8 ]; + } GUID; +#endif +#endif /* #if !defined(HELIX_FEATURE_FULLGUID) #else */ + +#if !defined( __IID_DEFINED__ ) +#define __IID_DEFINED__ +typedef GUID IID; +#define IID_NULL GUID_NULL +typedef GUID CLSID; +#define CLSID_NULL GUID_NULL + +#if defined(__cplusplus) +#ifndef _REFGUID_DEFINED +#define _REFGUID_DEFINED +#define REFGUID const GUID & +#endif + +#ifndef _REFIID_DEFINED +#define _REFIID_DEFINED +#define REFIID const IID & +#endif + +#ifndef _REFCLSID_DEFINED +#define _REFCLSID_DEFINED +#define REFCLSID const CLSID & +#endif + +#else +#ifndef _REFGUID_DEFINED +#define _REFGUID_DEFINED +#define REFGUID const GUID * const +#endif +#ifndef _REFIID_DEFINED +#define _REFIID_DEFINED +#define REFIID const IID * const +#endif +#ifndef _REFCLSID_DEFINED +#define _REFCLSID_DEFINED +#define REFCLSID const CLSID * const +#endif +#endif +#endif + + +#if defined(HELIX_FEATURE_FULLGUID) + +#if !defined (INITGUID) || (defined (_STATICALLY_LINKED) && !defined(NCIHACK)) + +#define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ + EXTERN_C const GUID FAR name + +#define DEFINE_GUID_ENUM(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ + EXTERN_C const GUID FAR name; + +#else /* #if !defined (INITGUID) || (defined (_STATICALLY_LINKED) && !defined(NCIHACK)) */ + +#define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ + EXTERN_C const GUID name \ + = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } } + +#define DEFINE_GUID_ENUM(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ + EXTERN_C const GUID name \ + = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } }; + +#endif /* #if !defined (INITGUID) || (defined (_STATICALLY_LINKED) && !defined(NCIHACK)) #else */ + +#endif /* #if defined(HELIX_FEATURE_FULLGUID) */ + +#include "hlxclib/memory.h" /* for memcmp */ + +#ifdef __cplusplus +inline HXBOOL IsEqualGUID(REFGUID rguid1, REFGUID rguid2) +{ +#if defined(HELIX_FEATURE_FULLGUID) + return !memcmp(&rguid1, &rguid2, sizeof(GUID)); +#else // HELIX_FEATURE_FULLGUID + return (rguid1 == rguid2); +#endif // HELIX_FEATURE_FULLGUID + +} + +inline void SetGUID(GUID& rguid1, REFGUID rguid2) +{ +#if defined(HELIX_FEATURE_FULLGUID) + memcpy(&rguid1, &rguid2, sizeof(GUID)); /* Flawfinder: ignore */ +#else // HELIX_FEATURE_FULLGUID + rguid1 = rguid2; +#endif // HELIX_FEATURE_FULLGUID +} +#else // __cplusplus +#if defined(HELIX_FEATURE_FULLGUID) +#define IsEqualGUID(rguid1, rguid2) (!memcmp(rguid1, rguid2, sizeof(GUID))) +#define SetGUID(rguid1, rguid2) (memcpy(rguid1, rguid2, sizeof(GUID))) /* Flawfinder: ignore */ +#else // HELIX_FEATURE_FULLGUID +#define IsEqualGUID(rguid1, rguid2) ((rguid1) == (rguid2)) +#define SetGUID(rguid1, rguid2) ((rguid1) = (rguid2)) +#endif // HELIX_FEATURE_FULLGUID +#endif // __cplusplus + +#define IsEqualIID(riid1, riid2) IsEqualGUID(riid1, riid2) +#define IsEqualCLSID(rclsid1, rclsid2) IsEqualGUID(rclsid1, rclsid2) + +#define SetIID(riid1, riid2) SetGUID(riid1, riid2) +#define SetCLSID(rclsid1, rclsid2) SetGUID(rclsid1, rclsid2) + +#ifdef __cplusplus + +/* + * Because GUID is defined elsewhere in WIN32 land, the operator == and != + * are moved outside the class to global scope. + */ +#if defined(HELIX_FEATURE_FULLGUID) +inline HXBOOL operator==(const GUID& guidOne, const GUID& guidOther) +{ + return !memcmp(&guidOne,&guidOther,sizeof(GUID)); +} + +inline HXBOOL operator!=(const GUID& guidOne, const GUID& guidOther) +{ + return !(guidOne == guidOther); +} +#endif // HELIX_FEATURE_FULLGUID + +#endif + + +/**************************************************************************** + * + * Interface: + * + * IUnknown + * + * Purpose: + * + * Base class of all interfaces. Defines life time management and + * support for dynamic cast. + * + * IID_IUnknown: + * + * {00000000-0000-0000-C000000000000046} + * + */ +DEFINE_GUID(IID_IUnknown, 0x00000000, 0x0000, 0x0000, 0xC0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x46); + +#undef INTERFACE +#define INTERFACE IUnknown + +DECLARE_INTERFACE(IUnknown) +{ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; +}; + + +/**************************************************************************** + * + * Interface: + * + * IMalloc + * + * Purpose: + * + * Basic memory management interface. + * + * IID_IMalloc: + * + * {00000002-0000-0000-C000000000000046} + * + */ +DEFINE_GUID(IID_IMalloc, 00000002, 0x0000, 0x0000, 0xC0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x46); + +#undef INTERFACE +#define INTERFACE IMalloc + +DECLARE_INTERFACE_(IMalloc, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IMalloc methods + */ + STDMETHOD_(void*,Alloc) (THIS_ + UINT32 /*IN*/ count) PURE; + + STDMETHOD_(void*,Realloc) (THIS_ + void* /*IN*/ pMem, + UINT32 /*IN*/ count) PURE; + + STDMETHOD_(void,Free) (THIS_ + void* /*IN*/ pMem) PURE; + + STDMETHOD_(UINT32,GetSize) (THIS_ + void* /*IN*/ pMem) PURE; + + STDMETHOD_(HXBOOL,DidAlloc) (THIS_ + void* /*IN*/ pMem) PURE; + + STDMETHOD_(void,HeapMinimize) (THIS) PURE; +}; + +#else /* else case of !defined( _OBJBASE_H_ ) && !defined( _COMPOBJ_H_ ) */ + +// For now, we always use full guids on Windows +#ifndef HELIX_FEATURE_FULLGUID +#define HELIX_FEATURE_FULLGUID +#endif /* HELIX_FEATURE_FULLGUID */ + +#ifdef DEFINE_GUID +#undef DEFINE_GUID +#endif + +#if !defined (INITGUID) || (defined (_STATICALLY_LINKED) && !defined(NCIHACK)) +#define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ + EXTERN_C const GUID FAR name +#define DEFINE_GUID_ENUM(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ + EXTERN_C const GUID FAR name; +#else +#define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ + EXTERN_C const GUID name \ + = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } } +#define DEFINE_GUID_ENUM(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ + EXTERN_C const GUID name \ + = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } }; +#endif + + +/* Even in windows we want these GUID's defined... */ + +#if !(defined(INITGUID) && defined(USE_IUNKNOWN_AND_IMALLOC_FROM_UUID_LIB)) +DEFINE_GUID(IID_IUnknown, 0x00000000, 0x0000, 0x0000, 0xC0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x46); + +DEFINE_GUID(IID_IMalloc, 00000002, 0x0000, 0x0000, 0xC0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x46); +#endif + +#include <memory.h> /* for memcmp */ + +#ifdef __cplusplus +inline void SetGUID(REFGUID rguid1, REFGUID rguid2) +{ + memcpy((void*)&rguid1, (void*)&rguid2, sizeof(GUID)); /* Flawfinder: ignore */ +} +#else +#define SetGUID(rguid1, rguid2) (memcpy((void*)rguid1, (void*)rguid2, sizeof(GUID))) /* Flawfinder: ignore */ +#endif +#define SetIID(riid1, riid2) SetGUID(riid1, riid2) +#define SetCLSID(rclsid1, rclsid2) SetGUID(rclsid1, rclsid2) + +#endif /* !defined( _OBJBASE_H_ ) && !defined( _COMPOBJ_H_ )*/ + +#ifdef __cplusplus + +/* + * This operator is defined for all platforms + */ +#if defined(HELIX_FEATURE_FULLGUID) +inline HXBOOL operator<(const GUID& lhs, const GUID& rhs) +{ + return memcmp(&lhs, &rhs, sizeof(GUID)) < 0; +} +#endif // HELIX_FEATURE_FULLGUID + +#endif // __cplusplus + + +#ifdef IsEqualIID +#undef IsEqualIID +#endif + +#ifdef IsEqualCLSID +#undef IsEqualCLSID +#endif + +#define IsEqualIID(riid1, riid2) HXIsEqualGUID(riid1, riid2) +#define IsEqualCLSID(rclsid1, rclsid2) HXIsEqualGUID(rclsid1, rclsid2) + +#ifdef __cplusplus +inline HXBOOL HXIsEqualGUID(REFGUID rguid1, REFGUID rguid2) +{ +#if defined(HELIX_FEATURE_FULLGUID) + return (((UINT32*) &rguid1)[0] == ((UINT32*) &rguid2)[0] && + ((UINT32*) &rguid1)[1] == ((UINT32*) &rguid2)[1] && + ((UINT32*) &rguid1)[2] == ((UINT32*) &rguid2)[2] && + ((UINT32*) &rguid1)[3] == ((UINT32*) &rguid2)[3]); +#else + return( (rguid1) == (rguid2) ); +#endif +} +#else +#if defined(HELIX_FEATURE_FULLGUID) +#define HXIsEqualGUID(rguid1, rguid2) \ + (((UINT32*) &rguid1)[0] == ((UINT32*) &rguid2)[0] && \ + ((UINT32*) &rguid1)[1] == ((UINT32*) &rguid2)[1] && \ + ((UINT32*) &rguid1)[2] == ((UINT32*) &rguid2)[2] && \ + ((UINT32*) &rguid1)[3] == ((UINT32*) &rguid2)[3]); +#else +#define HXIsEqualGUID(rguid1, rguid2) \ + ( (rguid1) == (rguid2) ); +#endif +#endif + +/**************************************************************************** + * + * QueryInterface size reduction structs and functions. + * + * Example Usage: + * + * QInterfaceList qiList[] = + * { + * { GET_IIDHANDLE(IID_IUnknown), (IUnknown*) (IHXPlugin*) this}, + * { GET_IIDHANDLE(IID_IHXPlugin), (IHXPlugin*) this}, + * { GET_IIDHANDLE(IID_IHXFileFormatObject), (IHXFileFormatObject*) this}, + * { GET_IIDHANDLE(IID_IHXAtomizerResponse), (IHXAtomizerResponse*) this}, + * { GET_IIDHANDLE(IID_IHXAtomizationCommander), (IHXAtomizationCommander*) this}, + * { GET_IIDHANDLE(IID_IHXASMSource), (IHXASMSource*) this}, + * { GET_IIDHANDLE(IID_IHXPacketFormat), (IHXPacketFormat*) this}, + * { GET_IIDHANDLE(IID_IHXFileSwitcher), (IHXFileSwitcher*) m_pFileSwitcher}, + * { GET_IIDHANDLE(IID_IHXCommonClassFactory), m_pClassFactory}, + * { GET_IIDHANDLE(IID_IHXScheduler), m_pScheduler} + * }; + * + * return QIFind(qiList, QILISTSIZE(qiList), riid, ppvObj); + */ +#if !defined(HELIX_FEATURE_FULLGUID) + +#define IIDHANDLE IID +#define GET_IIDHANDLE(x) (x) +#define DREF_IIDHANDLE(x) (x) + +#else // HELIX_FEATURE_FULLGUID + +#define IIDHANDLE const IID* +#define GET_IIDHANDLE(x) (&(x)) +#define DREF_IIDHANDLE(x) (*(x)) + +#endif // HELIX_FEATURE_FULLGUID + +typedef struct +{ + IIDHANDLE hiid; + void* pIFace; +} QInterfaceList; + +#define QILISTSIZE(x) sizeof(x)/sizeof(QInterfaceList) + +#if !defined(INITGUID) || (defined(_STATICALLY_LINKED) && !defined(NCIHACK)) +EXTERN_C HX_RESULT QIFind(QInterfaceList* qiList, UINT32 ulqiListSize, + REFIID riid, void** ppvObj); +#else // !INITGUID || (_STATICALLY_LINKED && NCIHACK) +EXTERN_C HX_RESULT QIFind(QInterfaceList* qiList, UINT32 ulqiListSize, + REFIID riid, void** ppvObj) +{ + do + { + if (IsEqualIID(DREF_IIDHANDLE(qiList->hiid), riid)) + { + *ppvObj = (qiList->pIFace); + if (*ppvObj) + { + ((IUnknown*) (*ppvObj))->AddRef(); + return HXR_OK; + } + return HXR_NOINTERFACE; + } + qiList++; + } while ((--ulqiListSize) != 0); + + *ppvObj = NULL; + return HXR_NOINTERFACE; +} +#endif // !INITGUID || (_STATICALLY_LINKED && NCIHACK) + + +/**************************************************************************** + * + * Putting the following macro in the definition of your class will overload + * new and delete for that object. New will then take an IMalloc* from + * which to allocate memory from and store it in the beginning of the + * memory which it will return. Delete will grab this IMalloc* from + * the beginning of the mem and use this pointer to deallocate the mem. + * + * Example useage: + * class A + * { + * public: + * A(int); + * ~A(); + * + * IMALLOC_MEM + * }; + * + * IMalloc* pMalloc; + * m_pContext->QueryInterface(IID_IMalloc, (void**)&pMalloc); + * A* p = new(pMalloc) A(0); + * pMalloc->Release(); + * delete p; + */ + +#define IMALLOC_MEM\ + void* operator new(size_t size, IMalloc* pMalloc)\ + {\ + void* pMem = pMalloc->Alloc(size + sizeof(IMalloc*));\ + *(IMalloc**)pMem = pMalloc;\ + pMalloc->AddRef();\ + return ((unsigned char*)pMem + sizeof(IMalloc*));\ + }\ +\ + void operator delete(void* pMem)\ + {\ + pMem = (unsigned char*)pMem - sizeof(IMalloc*);\ + IMalloc* pMalloc = *(IMalloc**)pMem;\ + pMalloc->Free(pMem);\ + pMalloc->Release();\ + }\ + + +/**************************************************************************** + * + * By default, we attempt to use atomic InterlockedIncrement/Decrement + * implementations. Add HELIX_FEATURE_DISABLE_INTERLOCKED_INC_DEC to + * your profile's .pf or to your Umakefil/.pcf file to use non-threadsafe + * implementations. + */ +#include "atomicbase.h" + +#if !defined HAVE_INTERLOCKED_INCREMENT +#undef InterlockedIncrement +#undef InterlockedDecrement +#define InterlockedIncrement(plong) (++(*(plong))) +#define InterlockedDecrement(plong) (--(*(plong))) +#endif /* !defined HAVE_INTERLOCKED_INCREMENT */ + + +#if defined(HELIX_CONFIG_COMPACT_COM_MACROS) + +EXTERN_C void HX_AddRefFunc(IUnknown** pUnk); +EXTERN_C void HX_ReleaseFunc(IUnknown** pUnk); + +#if defined(INITGUID) && (!defined(_STATICALLY_LINKED) || defined(NCIHACK)) + +void HX_AddRefFunc(IUnknown** pUnk) +{ + if (*pUnk) + { + (*pUnk)->AddRef(); + } +} + +void HX_ReleaseFunc(IUnknown** pUnk) +{ + if (*pUnk) + { + (*pUnk)->Release(); + *pUnk = 0; + } +} + +#endif // INITGUID && (!_STATICALLY_LINKED || NCIHACK) + +#if defined(HELIX_CONFIG_TYPE_CHECK_COM_MACROS) + +#if defined(HELIX_CONFIG_TYPE_CHECK_COM_MACROS_IMPLICIT_CAST) + +#define HX_RELEASE(x) (HX_ReleaseFunc(&x)) +#define HX_ADDREF(x) (HX_AddRefFunc(&x)) + +#else // defined(HELIX_CONFIG_TYPE_CHECK_COM_MACROS_IMPLICIT_CAST) + +// we don't want to build with this; this should be compiled only +// to ensure that we have a COM object in the code base + +#define HX_RELEASE(x) \ +{ \ + if (x) \ + { \ + IUnknown* pUnk; \ + (x)->QueryInterface(IID_IUnknown, (void**)&pUnk); \ + if (pUnk) \ + { \ + pUnk->Release(); \ + } \ + (x)->Release(); \ + (x) = 0; \ + } \ +} \ + +#define HX_ADDREF(x) \ +{ \ + if (x) \ + { \ + IUnknown* pUnk; \ + (x)->QueryInterface(IID_IUnknown, (void**)&pUnk); \ + if (pUnk) \ + { \ + pUnk->Release(); \ + } \ + (x)->AddRef(); \ + } \ +} \ + +#endif // defined(HELIX_CONFIG_TYPE_CHECK_COM_MACROS_IMPLICIT_CAST) + +#else // defined(HELIX_CONFIG_TYPE_CHECK_COM_MACROS) + +#define HX_RELEASE(x) (HX_ReleaseFunc((IUnknown**)&(x))) +#define HX_ADDREF(x) (HX_AddRefFunc((IUnknown**)&(x))) + +#endif // defined(HELIX_CONFIG_TYPE_CHECK_COM_MACROS) + +#else // defined(HELIX_CONFIG_COMPACT_COM_MACROS) + +#define HX_RELEASE(x) ((x) ? ((x)->Release(), (x) = 0) : 0) +#define HX_ADDREF(x) ((x) ? ((x)->AddRef()) : 0) + +#endif // defined(HELIX_CONFIG_COMPACT_COM_MACROS) + + + +#endif /* _HXCOM_H_ */ diff --git a/amarok/src/engine/helix/helix-sp/helix-include/common/include/hxcomm.h b/amarok/src/engine/helix/helix-sp/helix-include/common/include/hxcomm.h new file mode 100644 index 00000000..dc4c8ea4 --- /dev/null +++ b/amarok/src/engine/helix/helix-sp/helix-include/common/include/hxcomm.h @@ -0,0 +1,625 @@ + +/* + * + * This software is released under the provisions of the GPL version 2. + * see file "COPYING". If that file is not available, the full statement + * of the license can be found at + * + * http://www.fsf.org/licensing/licenses/gpl.txt + * + * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved. + * + */ + +#ifndef _HXCOMM_H_ +#define _HXCOMM_H_ + +#include "hxengin.h" /* For HXTimeval */ +#include "hxccf.h" /* For IHXCommonClassFactory. Formerly declared in this file. */ + +/* + * Forward declarations of some interfaces defined here-in. + */ + +typedef _INTERFACE IHXStatistics IHXStatistics; +typedef _INTERFACE IHXRegistryID IHXRegistryID; +typedef _INTERFACE IHXServerFork IHXServerFork; +typedef _INTERFACE IHXServerControl IHXServerControl; +typedef _INTERFACE IHXReconfigServerResponse IHXReconfigServerResponse; +typedef _INTERFACE IHXBuffer IHXBuffer; +typedef _INTERFACE IHXWantServerReconfigNotification + IHXWantServerReconfigNotification; +typedef _INTERFACE IHXFastAlloc IHXFastAlloc; + + +/**************************************************************************** + * + * Interface: + * + * IHXStatistics + * + * Purpose: + * + * This interface allows update of the client statistics. + * + * IID_IHXStatistics: + * + * {00000001-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXStatistics, 0x00000001, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXStatistics + +DECLARE_INTERFACE_(IHXStatistics, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXStatistics methods + */ + + /************************************************************************ + * Method: + * IHXStatistics::Init + * Purpose: + * Pass registry ID to the caller + * + */ + STDMETHOD(InitializeStatistics) (THIS_ + UINT32 /*IN*/ ulRegistryID) PURE; + + /************************************************************************ + * Method: + * IHXStatistics::Update + * Purpose: + * Notify the client to update its statistics stored in the registry + * + */ + STDMETHOD(UpdateStatistics) (THIS) PURE; +}; + + +/**************************************************************************** + * + * Interface: + * + * IHXRegistryID + * + * Purpose: + * + * This interface is implemented by IHXPlayer, IHXStreamSource, + * and IHXStream. It allows the user to get the registry Base ID, + * for an object that you have a pointer to. + * + * IID_IHXRegistryID: + * + * {00000002-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXRegistryID, 0x00000002, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXRegistryID + +DECLARE_INTERFACE_(IHXRegistryID, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXRegistryID methods + */ + + /************************************************************************ + * Method: + * IHXRegistryID::GetID + * Purpose: + * Get the registry ID of the object. + * + */ + STDMETHOD(GetID) (THIS_ + REF(UINT32) /*OUT*/ ulRegistryID) PURE; +}; + +/**************************************************************************** + * + * Interface: + * + * IHXServerFork + * + * Purpose: + * + * This interface is implemented by the server context on Unix + * platforms. This interface allows your plugin to fork off a + * process. Note that the process that is forked off cannot use + * any RMA APIs. The fork() system call is prohibited from within + * a RMA plugin. + * + * IID_IHXServerFork: + * + * {00000003-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXServerFork, 0x00000003, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXServerFork + +DECLARE_INTERFACE_(IHXServerFork, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXServerFork methods + */ + + /************************************************************************ + * Method: + * IHXServerFork::Fork + * Purpose: + * Fork off a child process. The child process cannot use any RMA + * APIs. Upon successful completion, Fork returns 0 to the child + * process and the PID of the child to the parent. A return value + * of -1 indicates an error. + * + * Note: The child process should *NOT* Release any interfaces. + * The cleanup of the IHXServerFork() interface and other + * RMA interfaces is done by the parent. + * + */ + STDMETHOD_(INT32, Fork) (THIS) PURE; +}; + +/* + * + * Interface: + * + * IHXServerControl + * + * Purpose: + * + * This interface provides access to the RealMedia server's controls + * for shutting down (for now). + * + * Note: This registry is not related to the Windows system registry. + * + * IID_IHXServerControl: + * + * {00000004-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXServerControl, 0x00000004, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#define CLSID_IHXServerControl IID_IHXServerControl + +#undef INTERFACE +#define INTERFACE IHXServerControl + +DECLARE_INTERFACE_(IHXServerControl, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXServerControl methods + */ + + /************************************************************************ + * Method: + * IHXServerControl::ShutdownServer + * Purpose: + * Shutdown the server. + */ + STDMETHOD(ShutdownServer) (THIS_ + UINT32 status) PURE; +}; + +/* + * + * Interface: + * + * IHXServerControl2 + * + * Purpose: + * + * Interface for extended server control methods. + * + * + * IID_IHXServerControl2: + * + * {00000005-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXServerControl2, 0x00000005, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + + +#undef INTERFACE +#define INTERFACE IHXServerControl2 + +DECLARE_INTERFACE_(IHXServerControl2, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXServerControl2 methods + */ + + /************************************************************************ + * IHXServerControl2::RestartServer + * + * Purpose: + * + * Completely shutdown the server, then restart. Mainly used to + * cause not hot setting config var changes to take effect. + */ + STDMETHOD(RestartServer) (THIS) PURE; + + /************************************************************************ + * IHXServerControl2::ReconfigServer + * + * Purpose: + * + * Used to cause the server to re-read in config from file or registry + * (however it was started) and attempt to use the values. + */ + STDMETHOD(ReconfigServer) (THIS_ IHXReconfigServerResponse* pResp) PURE; + +}; + +/* + * + * Interface: + * + * IHXReconfigServerResponse + * + * Purpose: + * + * Response interface for IHXServerControl2::ReconfigServer + * + * + * IID_IHXReconfigServerResponse: + * + * {00000006-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXReconfigServerResponse, 0x00000006, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + + +#undef INTERFACE +#define INTERFACE IHXReconfigServerResponse + +DECLARE_INTERFACE_(IHXReconfigServerResponse, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /************************************************************************ + * IHXReconfigServerResponse::ReconfigServerDone + * + * Purpose: + * + * Notification that reconfiguring the server is done. + */ + STDMETHOD(ReconfigServerDone) (THIS_ + HX_RESULT res, + IHXBuffer** pInfo, + UINT32 ulNumInfo) PURE; +}; + +/* + * + * Interface: + * + * IHXServerReconfigNotification + * + * Purpose: + * + * Register with the server that you want notification when a reconfig + * request comes in and want/need to take part in the reconfiguration. This + * is used when you have configuration info outside the server config file + * which needs to be re-initialized. + * + * + * IID_IHXServerReconfigNotification: + * + * {00000007-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXServerReconfigNotification, 0x00000007, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + + +#undef INTERFACE +#define INTERFACE IHXServerReconfigNotification + +DECLARE_INTERFACE_(IHXServerReconfigNotification, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /************************************************************************ + * IHXServerReconfigNotification::WantReconfigNotification + * + * Purpose: + * + * Tell the server that you want reconfig notification. + */ + STDMETHOD(WantReconfigNotification) (THIS_ + IHXWantServerReconfigNotification* pResponse) PURE; + + /************************************************************************ + * IHXServerReconfigNotification::CancelReconfigNotification + * + * Purpose: + * + * Tell the server that you no longer want reconfig notification. + */ + STDMETHOD(CancelReconfigNotification) (THIS_ + IHXWantServerReconfigNotification* pResponse) PURE; + +}; + +/* + * + * Interface: + * + * IHXWantServerReconfigNotification + * + * Purpose: + * + * Tell user that the server got a reconfig request and it is time to + * do your reconfiguration. NOTE: You should not need this if all of your + * configuration is stored in the config file; that is taken care of through + * IHXActiveRegistry. + * + * IID_IHXWantServerReconfigNotification: + * + * {00000008-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXWantServerReconfigNotification, 0x00000008, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + + +#undef INTERFACE +#define INTERFACE IHXWantServerReconfigNotification + +DECLARE_INTERFACE_(IHXWantServerReconfigNotification, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /************************************************************************ + * IHXWantServerReconfigNotification::ServerReconfig + * + * Purpose: + * + * Notify user that a server reconfig request had come in and it + * is now your turn to do external (not server config) reconfiguration.* + */ + STDMETHOD(ServerReconfig) (THIS_ + IHXReconfigServerResponse* pResponse) PURE; + +}; + +// $Private: + +DEFINE_GUID(IID_IHXResolverExec, 0x00000009, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + + +#undef INTERFACE +#define INTERFACE IHXResolverExec + +DECLARE_INTERFACE_(IHXResolverExec, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + STDMETHOD(ResolverExec) (THIS_ int readfd, int writefd) PURE; + +}; + + +/**************************************************************************** + * + * Interface: + * + * IHXFastAlloc + * + * Purpose: + * + * Basic memory management interface. + * + * IID_IHXFastAlloc: + * + * {0000000a-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXFastAlloc, 0x0000000a, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXFastAlloc + +DECLARE_INTERFACE_(IHXFastAlloc, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXFastAlloc methods + */ + STDMETHOD_(void*,FastAlloc) (THIS_ + UINT32 /*IN*/ count) PURE; + + STDMETHOD_(void,FastFree) (THIS_ + void* /*IN*/ pMem) PURE; +}; + +#define FAST_CACHE_MEM\ + void* operator new(size_t size, IHXFastAlloc* pMalloc = NULL)\ + {\ + void* pMem;\ + if (pMalloc)\ + {\ + pMem = pMalloc->FastAlloc(size + sizeof(IHXFastAlloc*));\ + }\ + else\ + {\ + pMem = (void *)::new char [size + sizeof(IHXFastAlloc*)];\ + }\ + *(IHXFastAlloc**)pMem = pMalloc;\ + return ((unsigned char*)pMem + sizeof(IHXFastAlloc*));\ + }\ +\ + void operator delete(void* pMem)\ + {\ + pMem = (unsigned char*)pMem - sizeof(IHXFastAlloc*);\ + IHXFastAlloc* pMalloc = *(IHXFastAlloc**)pMem;\ + if (pMalloc)\ + {\ + pMalloc->FastFree((char *)pMem);\ + }\ + else\ + {\ + delete[] (char *)pMem;\ + }\ + }\ + + +/**************************************************************************** + * + * Interface: + * + * IHXAccurateClock + * + * Purpose: + * + * High Accuracy, Cheap (no system-call) gettimeofday() + * [ Only available on some Unix platforms, except QI can fail!! ] + * + * IID_IHXAccurateClock: + * + * {0000000b-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXAccurateClock, 0x0000000b, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXAccurateClock + +DECLARE_INTERFACE_(IHXAccurateClock, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXAccurateClock methods + */ + STDMETHOD_(HXTimeval,GetTimeOfDay) (THIS) PURE; +}; + +// $EndPrivate. + +#endif /*_HXCOMM_H_*/ diff --git a/amarok/src/engine/helix/helix-sp/helix-include/common/include/hxcore.h b/amarok/src/engine/helix/helix-sp/helix-include/common/include/hxcore.h new file mode 100644 index 00000000..e76f85c4 --- /dev/null +++ b/amarok/src/engine/helix/helix-sp/helix-include/common/include/hxcore.h @@ -0,0 +1,2110 @@ + +/* + * + * This software is released under the provisions of the GPL version 2. + * see file "COPYING". If that file is not available, the full statement + * of the license can be found at + * + * http://www.fsf.org/licensing/licenses/gpl.txt + * + * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved. + * + */ + +#ifndef _HXCORE_H_ +#define _HXCORE_H_ + +/* + * Forward declarations of some interfaces defined or used here-in. + */ +typedef _INTERFACE IUnknown IUnknown; +typedef _INTERFACE IHXStream IHXStream; +typedef _INTERFACE IHXStream2 IHXStream2; +typedef _INTERFACE IHXStreamSource IHXStreamSource; +typedef _INTERFACE IHXPlayer IHXPlayer; +typedef _INTERFACE IHXClientEngine IHXClientEngine; +typedef _INTERFACE IHXScheduler IHXScheduler; +typedef _INTERFACE IHXClientAdviseSink IHXClientAdviseSink; +typedef _INTERFACE IHXValues IHXValues; +typedef _INTERFACE IHXBuffer IHXBuffer; +typedef _INTERFACE IHXPacket IHXPacket; +// $Private: +typedef _INTERFACE IHXPersistenceManager IHXPersistenceManager; +typedef _INTERFACE IHXRendererAdviseSink IHXRendererAdviseSink; +typedef _INTERFACE IHXLayoutSite IHXLayoutSite; +typedef _INTERFACE IHXProtocolValidator IHXProtocolValidator; +typedef _INTERFACE IHXUpdateProperties IHXUpdateProperties; +typedef _INTERFACE IHXUpdateProperties2 IHXUpdateProperties2; +typedef _INTERFACE IHXPersistentComponentManager IHXPersistentComponentManager; +typedef _INTERFACE IHXPersistentComponent IHXPersistentComponent; +typedef _INTERFACE IHXPersistentRenderer IHXPersistentRenderer; +typedef _INTERFACE IHXCoreMutex IHXCoreMutex; +typedef _INTERFACE IHXMacBlitMutex IHXMacBlitMutex; +// $EndPrivate. +typedef _INTERFACE IHXRenderer IHXRenderer; +typedef _INTERFACE IHXPlayer2 IHXPlayer2; +typedef _INTERFACE IHXRequest IHXrequest; +typedef _INTERFACE IHXPlayerNavigator IHXPlayerNavigator; +typedef _INTERFACE IHXGroupSink IHXGroupSink; + +typedef struct _PersistentComponentInfo PersistentComponentInfo; +typedef struct _HXxEvent HXxEvent; + + +#ifdef _MAC_CFM +#pragma export on +#endif + +#if defined _UNIX && !(defined _VXWORKS) +/* Includes needed for select() stuff */ +#include <sys/time.h> +#include <sys/types.h> +#include <unistd.h> +#endif + +#ifdef _BEOS // fd_set stuff +#include <net/socket.h> +#endif + +#include "hxwin.h" + +/* Used in renderer and advise sink interface */ +enum BUFFERING_REASON +{ + BUFFERING_START_UP = 0, + BUFFERING_SEEK, + BUFFERING_CONGESTION, + BUFFERING_LIVE_PAUSE +}; + +/**************************************************************************** + * + * Function: + * + * CreateEngine() + * + * Purpose: + * + * Function implemented by the RMA core to return a pointer to the + * client engine. This function would be run by top level clients. + */ +STDAPI CreateEngine + ( + IHXClientEngine** /*OUT*/ ppEngine + ); + +/**************************************************************************** + * + * Function: + * + * CloseEngine() + * + * Purpose: + * + * Function implemented by the RMA core to close the engine which + * was returned in CreateEngine(). + */ +STDAPI CloseEngine + ( + IHXClientEngine* /*IN*/ pEngine + ); + +#ifdef _MAC_CFM +#pragma export off +#endif + +/* + * Definitions of Function Pointers to CreateEngine() and Close Engine(). + * These types are provided as a convenince to authors of top level clients. + */ +typedef HX_RESULT (HXEXPORT_PTR FPRMCREATEENGINE)(IHXClientEngine** ppEngine); +typedef HX_RESULT (HXEXPORT_PTR FPRMCLOSEENGINE) (IHXClientEngine* pEngine); + + +/**************************************************************************** + * + * Interface: + * + * IHXStream + * + * Purpose: + * + * Interface provided by the client engine to the renderers. This + * interface allows access to stream related information and properties. + * + * IID_IHXStream: + * + * {00000400-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXStream, 0x00000400, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXStream + +DECLARE_INTERFACE_(IHXStream, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXStream methods + */ + + /************************************************************************ + * Method: + * IHXStream::GetSource + * Purpose: + * Get the interface to the source object of which the stream is + * a part of. + * + */ + STDMETHOD(GetSource) (THIS_ + REF(IHXStreamSource*) pSource) PURE; + + /************************************************************************ + * Method: + * IHXStream::GetStreamNumber + * Purpose: + * Get the stream number for this stream relative to the source + * object of which the stream is a part of. + * + */ + STDMETHOD_(UINT16,GetStreamNumber) (THIS) PURE; + + /************************************************************************ + * Method: + * IHXStream::GetStreamType + * Purpose: + * Get the MIME type for this stream. NOTE: The returned string is + * assumed to be valid for the life of the IHXStream from which it + * was returned. + * + */ + STDMETHOD_(const char*,GetStreamType) (THIS) PURE; + + /************************************************************************ + * Method: + * IHXStream::GetHeader + * Purpose: + * Get the header for this stream. + * + */ + STDMETHOD_(IHXValues*,GetHeader) (THIS) PURE; + + /************************************************************************ + * Method: + * IHXStream::ReportQualityOfService + * Purpose: + * Call this method to report to the playback context that the + * quality of service for this stream has changed. The unQuality + * should be on a scale of 0 to 100, where 100 is the best possible + * quality for this stream. Although the transport engine can + * determine lost packets and report these through the user + * interface, only the renderer of this stream can determine the + * "real" perceived damage associated with this loss. + * + * NOTE: The playback context may use this value to indicate loss + * in quality to the user interface. When the effects of a lost + * packet are eliminated the renderer should call this method with + * a unQuality of 100. + * + */ + STDMETHOD(ReportQualityOfService) (THIS_ + UINT8 unQuality) PURE; + + /************************************************************************ + * Method: + * IHXStream::ReportRebufferStatus + * Purpose: + * Call this method to report to the playback context that the + * available data has dropped to a critically low level, and that + * rebuffering should occur. The renderer should call back into this + * interface as it receives additional data packets to indicate the + * status of its rebuffering effort. + * + * NOTE: The values of unNeeded and unAvailable are used to indicate + * the general status of the rebuffering effort. For example, if a + * renderer has "run dry" and needs 5 data packets to play smoothly + * again, it should call ReportRebufferStatus() with 5,0 then as + * packet arrive it should call again with 5,1; 5,2... and eventually + * 5,5. + * + */ + STDMETHOD(ReportRebufferStatus) (THIS_ + UINT8 unNeeded, + UINT8 unAvailable) PURE; + + /************************************************************************ + * Method: + * IHXStream::SetGranularity + * Purpose: + * Sets the desired Granularity for this stream. The actual + * granularity will be the lowest granularity of all streams. + * Valid to call before stream actually begins. Best to call during + * IHXRenderer::OnHeader(). + */ + STDMETHOD(SetGranularity) (THIS_ + ULONG32 ulGranularity) PURE; + + /************************************************************************ + * Method: + * IHXStream::GetRendererCount + * Purpose: + * Returns the current number of renderer instances supported by + * this stream instance. + */ + STDMETHOD_(UINT16, GetRendererCount)(THIS) PURE; + + /************************************************************************ + * Method: + * IHXStream::GetRenderer + * Purpose: + * Returns the Nth renderer instance supported by this stream. + */ + STDMETHOD(GetRenderer) (THIS_ + UINT16 nIndex, + REF(IUnknown*) pUnknown) PURE; +}; + +/**************************************************************************** + * + * Interface: + * + * IHXStream2 + * + * Purpose: + * + * Interface provided by the client engine to the renderers. This + * interface allows access to stream related information and properties. + * + * IID_IHXStream: + * + * {00000400-0901-11d1-8B06-00A024406D5a} + * + */ +DEFINE_GUID(IID_IHXStream2, 0x00000400, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x5a); + +#undef INTERFACE +#define INTERFACE IHXStream2 + +DECLARE_INTERFACE_(IHXStream2, IHXStream) +{ + /************************************************************************ + * Method: + * IHXStream2::ReportAudioRebufferStatus + * Purpose: + * For audio only, when it's called, the rebuffer will only occur when + * there aren't any packets in the transport and the amount of audio in + * audio device falls below the minimum startup audio pushdown(1000ms + * by default) + * + * Non-audio renderers should still call ReportRebufferStatus(), the + * rebuffer will occur when the core drains out all the packets from + * the transport buffer + * + * The rest semantic are the same between the 2 calls. + */ + STDMETHOD(ReportAudioRebufferStatus) (THIS_ + UINT8 unNeeded, + UINT8 unAvailable) PURE; +}; + +/**************************************************************************** + * + * Interface: + * + * IHXStreamSource + * + * Purpose: + * + * Interface provided by the client engine to the renderers. This + * interface allows access to source related information and properties. + * + * IID_IHXStreamSource: + * + * {00000401-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXStreamSource, 0x00000401, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXStreamSource + +DECLARE_INTERFACE_(IHXStreamSource, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXStreamSource methods + */ + + /************************************************************************ + * Method: + * IHXStreamSource::IsLive + * Purpose: + * Ask the source whether it is live + * + */ + STDMETHOD_ (HXBOOL,IsLive) (THIS) PURE; + + /************************************************************************ + * Method: + * IHXStreamSource::GetPlayer + * Purpose: + * Get the interface to the player object of which the source is + * a part of. + * + */ + STDMETHOD(GetPlayer) (THIS_ + REF(IHXPlayer*) pPlayer) PURE; + + /************************************************************************ + * Method: + * IHXStreamSource::GetURL + * Purpose: + * Get the URL for this source. NOTE: The returned string is + * assumed to be valid for the life of the IHXStreamSource from which + * it was returned. + * + */ + STDMETHOD_(const char*,GetURL) (THIS) PURE; + + /************************************************************************ + * Method: + * IHXStreamSource::GetStreamCount + * Purpose: + * Returns the current number of stream instances supported by + * this source instance. + */ + STDMETHOD_(UINT16, GetStreamCount)(THIS) PURE; + + /************************************************************************ + * Method: + * IHXStreamSource::GetStream + * Purpose: + * Returns the Nth stream instance supported by this source. + */ + STDMETHOD(GetStream) (THIS_ + UINT16 nIndex, + REF(IUnknown*) pUnknown) PURE; +}; + + +/**************************************************************************** + * + * Interface: + * + * IHXPlayer + * + * Purpose: + * + * Interface provided by the client engine to the renderers. This + * interface allows access to player related information, properties, + * and operations. + * + * IID_IHXPlayer: + * + * {00000402-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXPlayer, 0x00000402, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXPlayer + +DECLARE_INTERFACE_(IHXPlayer, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXPlayer methods + */ + + /************************************************************************ + * Method: + * IHXPlayer::GetClientEngine + * Purpose: + * Get the interface to the client engine object of which the + * player is a part of. + * + */ + STDMETHOD(GetClientEngine) (THIS_ + REF(IHXClientEngine*) pEngine) PURE; + + /************************************************************************ + * Method: + * IHXPlayer::IsDone + * Purpose: + * Ask the player if it is done with the current presentation + * + */ + STDMETHOD_(HXBOOL,IsDone) (THIS) PURE; + + /************************************************************************ + * Method: + * IHXPlayer::IsLive + * Purpose: + * Ask the player whether it contains the live source + * + */ + STDMETHOD_(HXBOOL,IsLive) (THIS) PURE; + + /************************************************************************ + * Method: + * IHXPlayer::GetCurrentPlayTime + * Purpose: + * Get the current time on the Player timeline + * + */ + STDMETHOD_(ULONG32,GetCurrentPlayTime) (THIS) PURE; + + /************************************************************************ + * Method: + * IHXPlayer::OpenURL + * Purpose: + * Tell the player to begin playback of all its sources. + * + */ + STDMETHOD(OpenURL) (THIS_ + const char* pURL) PURE; + + /************************************************************************ + * Method: + * IHXPlayer::Begin + * Purpose: + * Tell the player to begin playback of all its sources. + * + */ + STDMETHOD(Begin) (THIS) PURE; + + /************************************************************************ + * Method: + * IHXPlayer::Stop + * Purpose: + * Tell the player to stop playback of all its sources. + * + */ + STDMETHOD(Stop) (THIS) PURE; + + /************************************************************************ + * Method: + * IHXPlayer::Pause + * Purpose: + * Tell the player to pause playback of all its sources. + * + */ + STDMETHOD(Pause) (THIS) PURE; + + /************************************************************************ + * Method: + * IHXPlayer::Seek + * Purpose: + * Tell the player to seek in the playback timeline of all its + * sources. + * + */ + STDMETHOD(Seek) (THIS_ + ULONG32 ulTime) PURE; + + /************************************************************************ + * Method: + * IHXPlayer::GetSourceCount + * Purpose: + * Returns the current number of source instances supported by + * this player instance. + */ + STDMETHOD_(UINT16, GetSourceCount)(THIS) PURE; + + /************************************************************************ + * Method: + * IHXPlayer::GetSource + * Purpose: + * Returns the Nth source instance supported by this player. + */ + STDMETHOD(GetSource) (THIS_ + UINT16 nIndex, + REF(IUnknown*) pUnknown) PURE; + + /************************************************************************ + * Method: + * IHXPlayer::SetClientContext + * Purpose: + * Called by the client to install itself as the provider of client + * services to the core. This is traditionally called by the top + * level client application. + */ + STDMETHOD(SetClientContext) (THIS_ + IUnknown* pUnknown) PURE; + + /************************************************************************ + * Method: + * IHXPlayer::GetClientContext + * Purpose: + * Called to get the client context for this player. This is + * set by the top level client application. + */ + STDMETHOD(GetClientContext) (THIS_ + REF(IUnknown*) pUnknown) PURE; + + /************************************************************************ + * Method: + * IHXPlayer::AddAdviseSink + * Purpose: + * Call this method to add a client advise sink. + * + */ + STDMETHOD(AddAdviseSink) (THIS_ + IHXClientAdviseSink* pAdviseSink) PURE; + + /************************************************************************ + * Method: + * IHXPlayer::RemoveAdviseSink + * Purpose: + * Call this method to remove a client advise sink. + */ + STDMETHOD(RemoveAdviseSink) (THIS_ + IHXClientAdviseSink* pAdviseSink) PURE; +}; + + +/**************************************************************************** + * + * Interface: + * + * IHXClientEngine + * + * Purpose: + * + * Interface to the basic client engine. Provided to the renderers and + * other client side components. + * + * IID_IHXClientEngine: + * + * {00000403-0901-11d1-8B06-00A024406D59} + */ +DEFINE_GUID(IID_IHXClientEngine, 0x00000403, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXClientEngine + +DECLARE_INTERFACE_(IHXClientEngine, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXClientEngine methods + */ + + /************************************************************************ + * Method: + * IHXClientEngine::CreatePlayer + * Purpose: + * Creates a new IHXPlayer instance. + * + */ + STDMETHOD(CreatePlayer) (THIS_ + REF(IHXPlayer*) pPlayer) PURE; + + /************************************************************************ + * Method: + * IHXClientEngine::ClosePlayer + * Purpose: + * Called by the client when it is done using the player... + * + */ + STDMETHOD(ClosePlayer) (THIS_ + IHXPlayer* pPlayer) PURE; + + /************************************************************************ + * Method: + * IHXClientEngine::GetPlayerCount + * Purpose: + * Returns the current number of IHXPlayer instances supported by + * this client engine instance. + */ + STDMETHOD_(UINT16, GetPlayerCount)(THIS) PURE; + + /************************************************************************ + * Method: + * IHXClientEngine::GetPlayer + * Purpose: + * Returns the Nth IHXPlayer instances supported by this client + * engine instance. + */ + STDMETHOD(GetPlayer) (THIS_ + UINT16 nPlayerNumber, + REF(IUnknown*) pUnknown) PURE; + + /************************************************************************ + * Method: + * IHXClientEngine::EventOccurred + * Purpose: + * Clients call this to pass OS events to all players. HXxEvent + * defines a cross-platform event. + */ + STDMETHOD(EventOccurred) (THIS_ + HXxEvent* /*IN*/ pEvent) PURE; + +}; + +// $Private: +/**************************************************************************** + * + * Interface: + * + * IHXClientEngineMapper + * + * Purpose: + * + * Interface to the basic client engine. Provided to the renderers and + * other client side components. + * + * IID_IHXClientEngineMapper: + * + * {0000040A-0901-11d1-8B06-00A024406D59} + */ +DEFINE_GUID(IID_IHXClientEngineMapper, 0x0000040A, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXClientEngineMapper + +DECLARE_INTERFACE_(IHXClientEngineMapper, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXClientEngineMapper methods + */ + + /************************************************************************ + * Method: + * IHXClientEngineMapper::GetPlayerBySite + * Purpose: + * Returns the IHXPlayer instance supported by this client + * engine instance that contains the specified IHXSite. + */ + STDMETHOD(GetPlayerBySite) (THIS_ + IHXSite* pSite, + REF(IUnknown*) pUnknown) PURE; +}; +// $EndPrivate: + +#if defined _UNIX && !defined (_VXWORKS) +DEFINE_GUID(IID_IHXClientEngineSelector, 0x00000404, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXClientEngineSelector + +DECLARE_INTERFACE_(IHXClientEngineSelector, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /************************************************************************ + * Method: + * IHXClientEngine::Select + * Purpose: + * Top level clients under Unix should use this instead of + * select() to select for events. + */ + STDMETHOD_(INT32, Select) (THIS_ + INT32 n, + fd_set *readfds, + fd_set *writefds, + fd_set *exceptfds, + struct timeval* timeout) PURE; +}; +#endif /* _UNIX */ + +/**************************************************************************** + * + * Interface: + * + * IHXClientEngineSetup + * + * Purpose: + * + * Interface to the basic client engine. Provided to the renderers and + * other client side components. + * + * IID_IHXClientEngineSetup: + * + * {00000405-0901-11d1-8B06-00A024406D59} + */ +DEFINE_GUID(IID_IHXClientEngineSetup, 0x00000405, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXClientEngineSetup + +DECLARE_INTERFACE_(IHXClientEngineSetup, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXClientEngineSetup methods + */ + + /************************************************************************ + * Method: + * IHXClientEngineSetup::Setup + * Purpose: + * Top level clients use this interface to over-ride certain basic + * interfaces implemented by the core. Current over-ridable + * interfaces are: IHXPreferences, IHXHyperNavigate + */ + STDMETHOD(Setup) (THIS_ + IUnknown* pContext) PURE; +}; + + +/**************************************************************************** + * + * Interface: + * + * IHXInfoLogger + * + * Purpose: + * + * Interface to send any logging information back to the server. + * This information will appear in the server's access log. + * + * IID_IHXInfoLogger: + * + * {00000409-0901-11d1-8B06-00A024406D59} + */ +DEFINE_GUID(IID_IHXInfoLogger, 0x00000409, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXInfoLogger + +DECLARE_INTERFACE_(IHXInfoLogger, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXInfoLogger methods + */ + + /************************************************************************ + * Method: + * IHXInfoLogger::LogInformation + * Purpose: + * Logs any user defined information in form of action and + * associated data. + */ + STDMETHOD(LogInformation) (THIS_ + const char* /*IN*/ pAction, + const char* /*IN*/ pData) PURE; +}; + + +// $Private: +/**************************************************************************** + * + * Interface: + * + * IHXPersistenceManager + * + * Purpose: + * + * + * IID_IHXPersistenceManager: + * + * {0000040B-0901-11d1-8B06-00A024406D59} + */ +DEFINE_GUID(IID_IHXPersistenceManager, 0x0000040B, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXPersistenceManager + +DECLARE_INTERFACE_(IHXPersistenceManager, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXPersistenceManager methods + */ + /************************************************************************ + * Method: + * IHXPersistenceManager::AddPersistentComponent + * Purpose: + * We currently allow ONLY IHXRenderer to register itself as a + * persistent component. + * Renderer registers itself as a persistent renderer if it wants + * to live across group boundaries. + * It will not be unloaded until + * a) Someone opens the next URL. + * b) All the groups within the current presentation have been + * played. + */ + STDMETHOD(AddPersistentComponent) (THIS_ + IUnknown* /*IN*/ pComponent) PURE; + + /************************************************************************ + * Method: + * IHXPersistenceManager::RemovePersistentComponent + * Purpose: + * Remove an earlier registered persistent component. + */ + STDMETHOD(RemovePersistentComponent) (THIS_ + IUnknown* /*IN*/ pComponent) PURE; + + /************************************************************************ + * Method: + * IHXPersistenceManager::GetPersistentComponent + * Purpose: + * Return an earlier registered persistent component. + */ + STDMETHOD(GetPersistentComponent) (THIS_ + REF(IUnknown*) /*OUT*/ pComponent) PURE; + +}; + +/**************************************************************************** + * + * Interface: + * + * IHXDriverStreamManager + * + * Purpose: + * Methods to notify/update driver stream renderer + * + * + * IID_IHXDriverStreamManager + * + * {0000040C-0901-11d1-8B06-00A024406D59} + */ +DEFINE_GUID(IID_IHXDriverStreamManager, 0x0000040C, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXDriverStreamManager + +DECLARE_INTERFACE_(IHXDriverStreamManager, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXDriverStreamManager methods + */ + /************************************************************************ + * Method: + * IHXDriverStreamManager::AddRendererAdviseSink + * Purpose: + * Add a renderer advise sink + * + */ + STDMETHOD(AddRendererAdviseSink) (THIS_ + IHXRendererAdviseSink* pSink) PURE; + + /************************************************************************ + * Method: + * IHXDriverStreamManager::SetDriverStreamManager + * Purpose: + * Remove an advise sink + * + */ + STDMETHOD(RemoveRendererAdviseSink) (THIS_ + IHXRendererAdviseSink* pSink) PURE; +}; + + +/**************************************************************************** + * + * Interface: + * IHXRendererAdviseSink + * + * Purpose: + * Provides access to notifications of initialization/changes to + * renderers in the player. + * + * IID_IHXRendererAdviseSink + * + * {0000040D-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXRendererAdviseSink, 0x0000040D, 0x901, + 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXRendererAdviseSink + +DECLARE_INTERFACE_(IHXRendererAdviseSink, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXRendererAdviseSink methods + */ + + STDMETHOD(TrackDurationSet) (THIS_ + UINT32 ulGroupIndex, + UINT32 ulTrackIndex, + UINT32 ulDuration, + UINT32 ulDelay, + HXBOOL bIsLive) PURE; + + STDMETHOD(RepeatedTrackDurationSet) (THIS_ + const char* pID, + UINT32 ulDuration, + HXBOOL bIsLive) PURE; + + STDMETHOD(TrackUpdated) (THIS_ + UINT32 ulGroupIndex, + UINT32 ulTrackIndex, + IHXValues* pValues) PURE; + + /************************************************************************ + * Method: + * IHXRendererAdviseSink::RendererInitialized + * Purpose: + * Notification of renderer initialization + * + */ + STDMETHOD(RendererInitialized) (THIS_ + IHXRenderer* pRenderer, + IUnknown* pStream, + IHXValues* pInfo) PURE; + + /************************************************************************ + * Method: + * IHXRendererAdviseSink::RendererClosed + * Purpose: + * Notification of renderer close + * + */ + STDMETHOD(RendererClosed) (THIS_ + IHXRenderer* pRenderer, + IHXValues* pInfo) PURE; +}; + + +/**************************************************************************** + * + * Interface: + * + * IHXLayoutStream + * + * Purpose: + * + * Interface that allows access/updates to stream properties + * + * IID_IHXLayoutStream: + * + * {0000040E-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXLayoutStream, 0x0000040E, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXLayoutStream + +DECLARE_INTERFACE_(IHXLayoutStream, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXLayoutStream methods + */ + + /************************************************************************ + * Method: + * IHXLayoutStream::GetProperty + * Purpose: + * Get layout stream property + * + * + */ + STDMETHOD(GetProperties) (THIS_ + REF(IHXValues*) pValue) PURE; + + /************************************************************************ + * Method: + * IHXLayoutStream::SetProperty + * Purpose: + * Set layout stream property + * + */ + STDMETHOD(SetProperties) (THIS_ + IHXValues* pValue) PURE; +}; + +/**************************************************************************** + * + * Interface: + * + * IHXRendererUpgrade + * + * Purpose: + * + * Interface that tells the player to upgrade a particular set of renderers + * + * IID_IHXRendererUpgrade: + * + * {00000410-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXRendererUpgrade, 0x00000410, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXRendererUpgrade + +DECLARE_INTERFACE_(IHXRendererUpgrade, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXRendererUpgrade methods + */ + + /************************************************************************ + * Method: + * IHXRendererUpgrade::IsRendererAvailable + * Purpose: + * Find out if the renderer is already loaded + * + * + */ + STDMETHOD_(HXBOOL,IsRendererAvailable) (THIS_ + const char* pMimeType) PURE; + + /************************************************************************ + * Method: + * IHXRendererUpgrade::ForceUpgrade + * Purpose: + * Force an upgrade for any unloaded renderers + * + */ + STDMETHOD(ForceUpgrade) (THIS) PURE; +}; + +/**************************************************************************** + * + * Interface: + * + * IHXValidator + * + * Purpose: + * + * Interface that provides validation + * + * IID_IHXValidator: + * + * {00000412-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXValidator, 0x00000412, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXValidator + +DECLARE_INTERFACE_(IHXValidator, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXValidator methods + */ + + /************************************************************************ + * Method: + * IHXValidator::ValidateProtocol + * Purpose: + * Find out if the protocol is valid + * + * + */ + STDMETHOD_(HXBOOL,ValidateProtocol) (THIS_ + char* pProtocol) PURE; + + /************************************************************************ + * Method: + * IHXValidator::ValidateMetaFile + * Purpose: + * Find out if it is a meta file + * + * + */ + STDMETHOD(ValidateMetaFile) (THIS_ + IHXRequest* pRequest, + IHXBuffer* pContent) PURE; +}; + +/**************************************************************************** + * + * Interface: + * + * IHXPrivateStreamSource + * + * Purpose: + * This interface is being added for the sole purpose of implementing + * IsSaveAllowed on our stream source objects. We need to get this in + * for alpha-3 of the player, since it would be very bad to put out a + * player version that allowed recording of content that was supposed to + * be unrecordable. This method should be moved into IHXStreamSource + * as soon as is convenient. + * + * IHXPrivateStreamSource: + * + * {57DFD0E2-C76E-11d1-8B5C-006008065552} + * + */ + +DEFINE_GUID(IID_IHXPrivateStreamSource, 0x57dfd0e2, 0xc76e, 0x11d1, 0x8b, 0x5c, + 0x0, 0x60, 0x8, 0x6, 0x55, 0x52); + +#undef INTERFACE +#define INTERFACE IHXPrivateStreamSource + +DECLARE_INTERFACE_(IHXPrivateStreamSource, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXPrivateStreamSource methods + */ + + + STDMETHOD_ (HXBOOL,IsSaveAllowed) (THIS) PURE; + +}; + +// $EndPrivate. + +/**************************************************************************** + * + * Interface: + * + * IHXPlayer2 + * + * Purpose: + * + * Extra methods in addition to IHXPlayer + * + * IID_IHXPlayer2: + * + * {00000411-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXPlayer2, 0x00000411, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXPlayer2 + +DECLARE_INTERFACE_(IHXPlayer2, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /************************************************************************ + * Method: + * IID_IHXPlayer2::SetMinimumPreroll + * Purpose: + * Call this method to set the minimum preroll of this clip + */ + STDMETHOD(SetMinimumPreroll) (THIS_ + UINT32 ulMinPreroll) PURE; + + /************************************************************************ + * Method: + * IID_IHXPlayer2::GetMinimumPreroll + * Purpose: + * Call this method to get the minimum preroll of this clip + */ + STDMETHOD(GetMinimumPreroll) (THIS_ + REF(UINT32) ulMinPreroll) PURE; + + /************************************************************************ + * Method: + * IID_IHXPlayer2::OpenRequest + * Purpose: + * Call this method to open the IHXRequest + */ + STDMETHOD(OpenRequest) (THIS_ + IHXRequest* pRequest) PURE; + + /************************************************************************ + * Method: + * IID_IHXPlayer2::GetRequest + * Purpose: + * Call this method to get the IHXRequest + */ + STDMETHOD(GetRequest) (THIS_ + REF(IHXRequest*) pRequest) PURE; +}; + +// $Private: +/**************************************************************************** + * + * Interface: + * + * IHXUpdateProperties + * + * Purpose: + * + * update any offset related stuff + * + * IID_IHXUpdateProperties: + * + * {00000413-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXUpdateProperties, 0x00000413, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXUpdateProperties + +DECLARE_INTERFACE_(IHXUpdateProperties, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /************************************************************************ + * Method: + * IHXUpdateProperties::UpdatePacketTimeOffset + * Purpose: + * Call this method to update the timestamp offset of cached packets + */ + STDMETHOD(UpdatePacketTimeOffset) (THIS_ + INT32 lTimeOffset) PURE; + + /************************************************************************ + * Method: + * IHXUpdateProperties::UpdatePlayTimes + * Purpose: + * Call this method to update properties + */ + STDMETHOD(UpdatePlayTimes) (THIS_ + IHXValues* pProps) PURE; +}; + +/**************************************************************************** + * + * Interface: + * + * IHXUpdateProperties + * + * Purpose: + * + * update any offset related stuff + * + * IID_IHXUpdateProperties: + * + * {00000413-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXUpdateProperties2, 0x00000413, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x5a); + +#undef INTERFACE +#define INTERFACE IHXUpdateProperties2 + +DECLARE_INTERFACE_(IHXUpdateProperties2, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /************************************************************************ + * Method: + * IHXUpdateProperties::UpdateHeader + * Purpose: + * Call this method to update the stream header + */ + STDMETHOD(UpdateHeader) (THIS_ + IHXValues* pProps) PURE; +}; +// $EndPrivate. + +/**************************************************************************** + * + * Interface: + * + * IHXPlayerNavigator + * + * Purpose: + * + * navigate player objects + * + * IID_IHXPlayerNavigator: + * + * {00000414-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXPlayerNavigator, 0x00000414, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXPlayerNavigator + +DECLARE_INTERFACE_(IHXPlayerNavigator, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /************************************************************************ + * Method: + * IHXPlayerNavigator::AddChildPlayer + * Purpose: + * Add child player to the current player + */ + STDMETHOD(AddChildPlayer) (THIS_ + IHXPlayer* pPlayer) PURE; + + /************************************************************************ + * Method: + * IHXPlayerNavigator::RemoveChildPlayer + * Purpose: + * Remove child player from the current player + */ + STDMETHOD(RemoveChildPlayer) (THIS_ + IHXPlayer* pPlayer) PURE; + + /************************************************************************ + * Method: + * IHXPlayerNavigator::GetNumChildPlayer + * Purpose: + * Get number of the child players + */ + STDMETHOD_(UINT16, GetNumChildPlayer) (THIS) PURE; + + /************************************************************************ + * Method: + * IHXPlayerNavigator::GetChildPlayer + * Purpose: + * Get Nth child player + */ + STDMETHOD(GetChildPlayer) (THIS_ + UINT16 uPlayerIndex, + REF(IHXPlayer*) pPlayer) PURE; + + /************************************************************************ + * Method: + * IHXPlayerNavigator::SetParentPlayer + * Purpose: + * Set the parent player + */ + STDMETHOD(SetParentPlayer) (THIS_ + IHXPlayer* pPlayer) PURE; + + /************************************************************************ + * Method: + * IHXPlayerNavigator::RemoveParentPlayer + * Purpose: + * Remove the parent player + */ + STDMETHOD(RemoveParentPlayer) (THIS_ + IHXPlayer* pPlayer) PURE; + + /************************************************************************ + * Method: + * IHXPlayerNavigator::GetParentPlayer + * Purpose: + * Get the parent player + */ + STDMETHOD(GetParentPlayer) (THIS_ + REF(IHXPlayer*) pPlayer) PURE; +}; + +// $Private: +/**************************************************************************** + * + * Interface: + * + * IHXPersistentComponentManager + * + * Purpose: + * + * persistent component manager + * + * IID_IHXPersistentComponentManager + * + * {00000415-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXPersistentComponentManager, 0x00000415, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXPersistentComponentManager + +DECLARE_INTERFACE_(IHXPersistentComponentManager, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /************************************************************************ + * Method: + * IHXPersistentComponentManager::CreatePersistentComponent + * Purpose: + * create persistent component + */ + STDMETHOD(CreatePersistentComponent) (THIS_ + REF(IHXPersistentComponent*) pPersistentComponent) PURE; + + + /************************************************************************ + * Method: + * IHXPersistentComponentManager::AddPersistentComponent + * Purpose: + * add persistent component + */ + STDMETHOD(AddPersistentComponent) (THIS_ + IHXPersistentComponent* pPersistentComponent) PURE; + + /************************************************************************ + * Method: + * IHXPersistentComponentManager::RemovePersistentComponent + * Purpose: + * remove persistent component + */ + STDMETHOD(RemovePersistentComponent) (THIS_ + UINT32 ulPersistentComponentID) PURE; + + /************************************************************************ + * Method: + * IHXPersistentComponentManager::GetPersistentComponentInfo + * Purpose: + * get persistent component information + */ + STDMETHOD(GetPersistentComponent) (THIS_ + UINT32 ulPersistentComponentID, + REF(IHXPersistentComponent*) pPersistentComponent) PURE; + + /************************************************************************ + * Method: + * IHXPersistentComponentManager::AttachPersistentComponentLayout + * Purpose: + * get persistent component information + */ + STDMETHOD(AttachPersistentComponentLayout) (THIS_ + IUnknown* pLSG, + IHXValues* pProps) PURE; +}; + +/**************************************************************************** + * + * Interface: + * + * IHXPersistentComponent + * + * Purpose: + * + * persistent component + * + * IID_IHXPersistentComponent + * + * {00000416-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXPersistentComponent, 0x00000416, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXPersistentComponent + +DECLARE_INTERFACE_(IHXPersistentComponent, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /************************************************************************ + * Method: + * IHXPersistentComponent::Init + * Purpose: + * initialize persistent component + */ + STDMETHOD(Init) (THIS_ + IHXPersistentRenderer* pPersistentRenderer) PURE; + + /************************************************************************ + * Method: + * IHXPersistentComponent::AddRendererAdviseSink + * Purpose: + * add renderer advise sink + */ + STDMETHOD(AddRendererAdviseSink) (THIS_ + IHXRendererAdviseSink* pSink) PURE; + + /************************************************************************ + * Method: + * IHXPersistentComponent::RemoveRendererAdviseSink + * Purpose: + * remove renderer advise sink + */ + STDMETHOD(RemoveRendererAdviseSink) (THIS_ + IHXRendererAdviseSink* pSink) PURE; + + /************************************************************************ + * Method: + * IHXPersistentComponent::AddGroupSink + * Purpose: + * add renderer advise sink + */ + STDMETHOD(AddGroupSink) (THIS_ + IHXGroupSink* pSink) PURE; + + /************************************************************************ + * Method: + * IHXPersistentComponent::RemoveGroupSink + * Purpose: + * remove renderer advise sink + */ + STDMETHOD(RemoveGroupSink) (THIS_ + IHXGroupSink* pSink) PURE; + + /************************************************************************ + * Method: + * IHXPersistentComponent::GetPersistentRenderer + * Purpose: + * get persistent renderer + */ + STDMETHOD(GetPersistentRenderer) (THIS_ + REF(IHXPersistentRenderer*) pPersistentRenderer) PURE; + + /************************************************************************ + * Method: + * IHXPersistentComponent::GetPersistentProperties + * Purpose: + * get persistent component properties + */ + STDMETHOD(GetPersistentProperties) (THIS_ + REF(IHXValues*) pProperties) PURE; +}; + +/**************************************************************************** + * + * Interface: + * + * IID_IHXClientStatisticsGranularity + * + * Purpose: + * + * Enables users to set the Granularity at which statistics are + * gathered. This allows machines under high load to still run + * efficiently. + * + * + * IID_IHXClientStatisticsGranularity + * + * {00000416-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXClientStatisticsGranularity, 0x00000417, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXClientStatisticsGranularity + +DECLARE_INTERFACE_(IHXClientStatisticsGranularity, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /************************************************************************ + * Method: + * IHXClientStatisticsGranularity::SetStatsGranularity + * Purpose: + * Set the granularity + */ + STDMETHOD(SetStatsGranularity) (THIS_ ULONG32 ulGranularity) PURE; +}; + +// $EndPrivate. + +// $Private: + +/**************************************************************************** + * + * Interface: + * + * IID_IHXSourceBufferingStats + * + * Purpose: + * + * Enables users to get the current buffering status of the + * given source. + * + * + * IID_IHXSourceBufferingStats + * + * {00000418-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXSourceBufferingStats, 0x00000418, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXSourceBufferingStats + +DECLARE_INTERFACE_(IHXSourceBufferingStats, IUnknown) +{ + /************************************************************************ + * Method: + * IHXSourceBufferingStats::GetCurrentBuffering + * Purpose: + * Get the current buffering information + */ + + STDMETHOD(GetCurrentBuffering) (THIS_ UINT16 uStreamNumber, + REF(INT64) llLowestTimestamp, + REF(INT64) llHighestTimestamp, + REF(UINT32) ulNumBytes, + REF(HXBOOL) bDone) PURE; +}; + + +/**************************************************************************** + * + * Interface: + * + * IID_IHXSourceBufferingStats2 + * + * Purpose: + * + * Enables users to get the current buffering status of the + * given source. + * + * + * IID_IHXSourceBufferingStats2 + * + * {00000418-0901-11d1-8B06-00A024406D5A} + * + */ +DEFINE_GUID(IID_IHXSourceBufferingStats2, 0x00000418, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x5a); + +#undef INTERFACE +#define INTERFACE IHXSourceBufferingStats2 + +DECLARE_INTERFACE_(IHXSourceBufferingStats2, IHXSourceBufferingStats) +{ + /************************************************************************ + * Method: + * IHXSourceBufferingStats2::GetCurrentBuffering + * Purpose: + * Get the amount of buffering in the transport + */ + STDMETHOD(GetCurrentBuffering) (THIS_ UINT16 uStreamNumber, + REF(INT64) llLowestTimestamp, + REF(INT64) llHighestTimestamp, + REF(UINT32) ulNumBytes, + REF(HXBOOL) bDone) PURE; + + /************************************************************************ + * Method: + * IHXSourceBufferingStats2::GetTotalBuffering + * Purpose: + * Get the total amount of data buffered for a stream. + * This includes what is in the transport buffer and in + * the renderer + */ + + STDMETHOD(GetTotalBuffering) (THIS_ UINT16 uStreamNumber, + REF(INT64) llLowestTimestamp, + REF(INT64) llHighestTimestamp, + REF(UINT32) ulNumBytes, + REF(HXBOOL) bDone) PURE; +}; + +// $EndPrivate. + +/**************************************************************************** + * + * Interface: + * + * IID_IHXSourceLatencyStats + * + * Purpose: + * + * + * + * IHXSourceLatencyStats + * + * {7A4D7872-E5A9-11D8-ABE7-000A95BEFE6C} + * + */ +DEFINE_GUID(IID_IHXSourceLatencyStats, 0x7A4D7872, 0xE5A9, 0x11D8, 0xAB, 0xE7, 0x00, + 0x0A, 0x95, 0xBE, 0xFE, 0x6C); + +#undef INTERFACE +#define INTERFACE IHXSourceLatencyStats + +DECLARE_INTERFACE_(IHXSourceLatencyStats, IUnknown) +{ + /************************************************************************ + * Method: + * IHXSourceLatencyStats::SetLiveSyncOffset + * Purpose: + * set the live sync start time + */ + STDMETHOD(SetLiveSyncOffset) (THIS_ UINT32 ulLiveSyncStartTime) PURE; + + /************************************************************************ + * Method: + * IHXSourceLatencyStats::NewPacketTimeStamp + * Purpose: + * call this for each arriving packet! + */ + + STDMETHOD(NewPacketTimeStamp) (THIS_ UINT32 ulDueTimeStamp) PURE; + + /************************************************************************ + * Method: + * IHXSourceLatencyStats::GetLatencyStats + * Purpose: + * call this for each arriving packet! + */ + + STDMETHOD(GetLatencyStats) (THIS_ + REF(UINT32) ulAverageLatency, + REF(UINT32) ulMinimumLatency, + REF(UINT32) ulMaximumLatency ) PURE; + + + /************************************************************************ + * Method: + * IHXSourceLatencyStats::ResetLatencyStats + * Purpose: + * call this for each arriving packet! + */ + + STDMETHOD(ResetLatencyStats) (THIS_ ) PURE; + + +}; + + +// $Private: + +/**************************************************************************** + * + * Interface: + * + * IID_IHXPlayerPresentation + * + * Purpose: + * + * Control over the player's current presentation + * + * IID_IHXPlayerPresentation + * + * {6DE011A7-EF05-417b-9367-6FE0E54302D3} + * + */ +DEFINE_GUID(IID_IHXPlayerPresentation, 0x6de011a7, 0xef05, 0x417b, 0x93, 0x67, 0x6f, 0xe0, 0xe5, 0x43, 0x2, 0xd3); + +#undef INTERFACE +#define INTERFACE IHXPlayerPresentation + +DECLARE_INTERFACE_(IHXPlayerPresentation, IUnknown) +{ + /************************************************************************ + * Method: + * IHXPlayerPresentation::ClosePresentation + * Purpose: + * Call this method to close the player's current presentation. This will free + * all resources associated with the current presentation. + */ + STDMETHOD(ClosePresentation) (THIS) PURE; +}; + +// $EndPrivate. + + + +// $Private: + +/**************************************************************************** + * + * Interface: + * + * IID_IHXCoreMutex + * + * Purpose: + * + * Access the core mutex + * + * IID_IHXCoreMutex + * + * {6DE011A7-EF05-417b-9367-6FE0E44404D4} + * + */ +DEFINE_GUID(IID_IHXCoreMutex, 0x6de011a7, 0xef05, 0x417b, 0x93, 0x67, 0x6f, 0xe0, 0xe4, 0x44, 0x4, 0xd4); + +#undef INTERFACE +#define INTERFACE IHXCoreMutex + +DECLARE_INTERFACE_(IHXCoreMutex, IUnknown) +{ + /************************************************************************ + * Method: + * IHXCoreMutex::LockCoreMutex + * Purpose: + * Call this method to lock the client engine's core mutex. + */ + STDMETHOD(LockCoreMutex) (THIS) PURE; + + /************************************************************************ + * Method: + * IHXCoreMutex::UnlockCoreMutex + * Purpose: + * Call this method to unlock the client engine's core mutex. + */ + STDMETHOD(UnlockCoreMutex) (THIS) PURE; +}; + + +/**************************************************************************** + * + * Interface: + * + * IID_IHXMacBlitMutex + * + * Purpose: + * + * Access the Mac blitting mutex + * + * Used for all Mac drawing, in both the core and the tlc. This + * mutex prevents quickdraw (and/or the image compression manager) + * from re-entering which causes crashes on OS X. + * + * IID_IHXCoreMutex + * + * {294e6de4-fbc6-4c06-bb94-95a969373b4d} + * + */ +DEFINE_GUID(IID_IHXMacBlitMutex, 0x294e6de4, 0xfbc6, 0x4c06, 0xbb, 0x94, 0x95, 0xa9, 0x69, 0x37, 0x3b, 0x4d); + +#undef INTERFACE +#define INTERFACE IHXMacBlitMutex + +DECLARE_INTERFACE_(IHXMacBlitMutex, IUnknown) +{ + /************************************************************************ + * Method: + * IHXMacBlitMutex::LockMacBlitMutex + * Purpose: + * Call this method to lock the client engine's core mutex. + */ + STDMETHOD(LockMacBlitMutex) (THIS) PURE; + + /************************************************************************ + * Method: + * IHXMacBlitMutex::UnlockMacBlitMutex + * Purpose: + * Call this method to unlock the client engine's core mutex. + */ + STDMETHOD(UnlockMacBlitMutex) (THIS) PURE; +}; + +// $EndPrivate. + + + +#endif /* _HXCORE_H_ */ + diff --git a/amarok/src/engine/helix/helix-sp/helix-include/common/include/hxengin.h b/amarok/src/engine/helix/helix-sp/helix-include/common/include/hxengin.h new file mode 100644 index 00000000..cc4d1b49 --- /dev/null +++ b/amarok/src/engine/helix/helix-sp/helix-include/common/include/hxengin.h @@ -0,0 +1,2812 @@ + +/* + * + * This software is released under the provisions of the GPL version 2. + * see file "COPYING". If that file is not available, the full statement + * of the license can be found at + * + * http://www.fsf.org/licensing/licenses/gpl.txt + * + * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved. + * + */ + +#ifndef _HXENGIN_H_ +#define _HXENGIN_H_ + +#include "hxcom.h" +#include "ihxpckts.h" + +/* + * Forward declarations of some interfaces used here-in. + */ + +typedef _INTERFACE IHXValues IHXValues; +typedef _INTERFACE IHXBuffer IHXBuffer; +typedef _INTERFACE IHXCallback IHXCallback; +typedef _INTERFACE IHXScheduler IHXScheduler; +typedef _INTERFACE IHXTCPResponse IHXTCPResponse; +typedef _INTERFACE IHXLBoundTCPSocket IHXLBoundTCPSocket; +typedef _INTERFACE IHXTCPSocket IHXTCPSocket; +typedef _INTERFACE IHXListenResponse IHXListenResponse; +typedef _INTERFACE IHXListenSocket IHXListenSocket; +typedef _INTERFACE IHXNetworkServices IHXNetworkServices; +typedef _INTERFACE IHXNetworkServices2 IHXNetworkServices2; +typedef _INTERFACE IHXUDPResponse IHXUDPResponse; +typedef _INTERFACE IHXUDPSocket IHXUDPSocket; +typedef _INTERFACE IHXResolver IHXResolver; +typedef _INTERFACE IHXResolverResponse IHXResolverResponse; +typedef _INTERFACE IHXInterruptSafe IHXInterruptSafe; +typedef _INTERFACE IHXAsyncIOSelection IHXAsyncIOSelection; +typedef _INTERFACE IHXUDPMulticastInit IHXUDPMulticastInit; +typedef _INTERFACE IHXInterruptState IHXInterruptState; +typedef _INTERFACE IHXOptimizedScheduler IHXOptimizedScheduler; +// $Private: +typedef _INTERFACE IHXThreadSafeScheduler IHXThreadSafeScheduler; +typedef _INTERFACE IHXBufferedSocket IHXBufferedSocket; +typedef _INTERFACE IHXNetInterfaces IHXNetInterfaces; +typedef _INTERFACE IHXNetInterfacesAdviseSink IHXNetInterfacesAdviseSink; +// $EndPrivate. +typedef _INTERFACE IHXNetworkInterfaceEnumerator IHXNetworkInterfaceEnumerator; +typedef _INTERFACE IHXUDPConnectedSocket IHXUDPConnectedSocket; +typedef _INTERFACE IHXAutoBWDetection IHXAutoBWDetection; +typedef _INTERFACE IHXAutoBWDetectionAdviseSink IHXAutoBWDetectionAdviseSink; +typedef _INTERFACE IHXAutoBWCalibration IHXAutoBWCalibration; +typedef _INTERFACE IHXAutoBWCalibrationAdviseSink IHXAutoBWCalibrationAdviseSink; +typedef _INTERFACE IHXPreferredTransport IHXPreferredTransport; + +/* + * Address flags starting with PNR are deprecated. + */ +#define HXR_INADDR_ANY (UINT32)0x00000000 //THIS FLAG IS DEPRECATED +#define HX_INADDR_ANY (UINT32)0x00000000 + +/* + * 255.255.255.254 + * + * Bind to all ports in IPBindings list from + * server config. + */ +#define HXR_INADDR_IPBINDINGS (UINT32)0xfffffffe //THIS FLAG IS DEPRECATED +#define HX_INADDR_IPBINDINGS (UINT32)0xfffffffe + + +/* Async IO Selection Type (Unix Only) */ + +#define PNAIO_READ 1 +#define PNAIO_WRITE 2 +#define PNAIO_EXCEPTION 4 + +/**************************************************************************** + * + * Interface: + * + * IHXCallback + * + * Purpose: + * + * This interface defines a simple callback which will be used in + * various interfaces such as IHXScheduler. + * + * IID_IHXCallback: + * + * {00000100-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXCallback, 0x00000100, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXCallback + +DECLARE_INTERFACE_(IHXCallback, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXCallback methods + */ + + /************************************************************************ + * Method: + * IHXCallback::Func + * Purpose: + * This is the function that will be called when a callback is + * to be executed. + */ + STDMETHOD(Func) (THIS) PURE; +}; + + +/**************************************************************************** + * + * Interface: + * + * IHXScheduler + * + * Purpose: + * + * This interface provides the user with a way of scheduling callbacks + * that will be executed at some time in the future. + * + * IID_IHXScheduler: + * + * {00000101-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXScheduler, 0x00000101, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXScheduler + +typedef ULONG32 CallbackHandle; + +typedef struct _HXTimeval +{ + UINT32 tv_sec; + UINT32 tv_usec; +} HXTimeval; + +DECLARE_INTERFACE_(IHXScheduler, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXScheduler methods + */ + + /************************************************************************ + * Method: + * IHXScheduler::RelativeEnter + * Purpose: + * Schedule a callback to be executed "ms" milliseconds from now + * This function is less percise then AbsoluteEnter and should only + * be used when accurate timing is not critical. + */ + STDMETHOD_(CallbackHandle,RelativeEnter) (THIS_ + IHXCallback* pCallback, + UINT32 ms) PURE; + + /************************************************************************ + * Method: + * IHXScheduler::AbsoluteEnter + * Purpose: + * Schedule a callback to be executed at time "tVal". + */ + STDMETHOD_(CallbackHandle,AbsoluteEnter) (THIS_ + IHXCallback* pCallback, + HXTimeval tVal) PURE; + + /************************************************************************ + * Method: + * IHXScheduler::Remove + * Purpose: + * Remove a callback from the scheduler. + */ + STDMETHOD(Remove) (THIS_ + CallbackHandle Handle) PURE; + + /************************************************************************ + * Method: + * IHXScheduler::GetCurrentSchedulerTime + * Purpose: + * Gives the current time (in the timeline of the scheduler). + */ + STDMETHOD_(HXTimeval,GetCurrentSchedulerTime) (THIS) PURE; +}; + + +/**************************************************************************** + * + * Interface: + * + * IHXTCPResponse + * + * Purpose: + * + * This is the response interface for the asynchronous TCP networking + * interface. + * + * IID_IHXTCPResponse: + * + * {00000102-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXTCPResponse, 0x00000102, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXTCPResponse + +DECLARE_INTERFACE_(IHXTCPResponse, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXTCPResponse methods + */ + + /************************************************************************ + * Method: + * IHXTCPResponse::ConnectDone + * Purpose: + * A Connect operation has been completed or an error has occurred. + */ + STDMETHOD(ConnectDone) (THIS_ + HX_RESULT status) PURE; + + /************************************************************************ + * Method: + * IHXTCPResponse::ReadDone + * Purpose: + * A Read operation has been completed or an error has occurred. + * The data is returned in the IHXBuffer. + */ + STDMETHOD(ReadDone) (THIS_ + HX_RESULT status, + IHXBuffer* pBuffer) PURE; + + /************************************************************************ + * Method: + * IHXTCPResponse::WriteReady + * Purpose: + * This is the response method for WantWrite. + * If HX_RESULT is ok, then the TCP channel is ok to Write to. + */ + STDMETHOD(WriteReady) (THIS_ + HX_RESULT status) PURE; + + /************************************************************************ + * Method: + * IHXTCPResponse::Closed + * Purpose: + * This method is called to inform you that the TCP channel has + * been closed by the peer or closed due to error. + */ + STDMETHOD(Closed) (THIS_ + HX_RESULT status) PURE; +}; + + +/**************************************************************************** + * + * Interface: + * + * IHXTCPSocket + * + * Purpose: + * + * Provides the user with an asynchronous TCP networking interface. + * + * IID_IHXTCPSocket: + * + * {00000103-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXTCPSocket, 0x00000103, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXTCPSocket + +DECLARE_INTERFACE_(IHXTCPSocket, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXTCPSocket methods + * + * Network addresses and ports are in native byte order + * + */ + + STDMETHOD(Init) (THIS_ + IHXTCPResponse* /*IN*/ pTCPResponse) PURE; + + STDMETHOD(SetResponse) (THIS_ + IHXTCPResponse* pTCPResponse) PURE; + + STDMETHOD(Bind) (THIS_ + UINT32 ulLocalAddr, + UINT16 nPort) PURE; + + /* + * pDestination is a string containing host name or dotted-ip notation + */ + STDMETHOD(Connect) (THIS_ + const char* pDestination, + UINT16 nPort) PURE; + + STDMETHOD(Read) (THIS_ + UINT16 Size) PURE; + + STDMETHOD(Write) (THIS_ + IHXBuffer* pBuffer) PURE; + + /************************************************************************ + * Method: + * IHXTCPSocket::WantWrite + * Purpose: + * This method is called when you wish to write a large amount of + * data. If you are only writing small amounts of data, you can + * just call Write (all data not ready to be transmitted will be + * buffered on your behalf). When the TCP channel is ready to be + * written to, the response interfaces WriteReady method will be + * called. + */ + STDMETHOD(WantWrite) (THIS) PURE; + + /************************************************************************ + * Method: + * IHXTCPSocket::GetForeignAddress + * Purpose: + * Returns the address of the other end of the TCP socket as a + * ULONG32 in local host order + */ + STDMETHOD(GetForeignAddress) (THIS_ + REF(ULONG32) lAddress) PURE; + + STDMETHOD(GetLocalAddress) (THIS_ + REF(ULONG32) lAddress) PURE; + + /************************************************************************ + * Method: + * IHXTCPSocket::GetForeignPort + * Purpose: + * Returns the port of the other end of the TCP socket in local + * host order. + */ + STDMETHOD(GetForeignPort) (THIS_ + REF(UINT16) port) PURE; + + STDMETHOD(GetLocalPort) (THIS_ + REF(UINT16) port) PURE; +}; + +// $Private: +/**************************************************************************** + * + * Interface: + * + * IHXTCPSecureSocket + * + * Purpose: + * + * When an IHXTCPSocket also supports this interface, + * it allows you to say it's secure so it tries to use + * SSL. + * + */ +DEFINE_GUID(IID_IHXTCPSecureSocket, 0x00000203, 0x911, 0x21d1, 0x8c, 0x4, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x54); + +#undef INTERFACE +#define INTERFACE IHXTCPSecureSocket + +DECLARE_INTERFACE_(IHXTCPSecureSocket, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXTCPSecureSocket method + */ + STDMETHOD(SetSecure) (THIS_ + HXBOOL bSecure) PURE; +}; +// $EndPrivate. + + + +// $Private: +/**************************************************************************** + * + * Interface: + * + * IHXSSL + * + * Purpose: + * + * This is the interface to an SSL library. + * + */ +DEFINE_GUID(IID_IHXSSL, 0x34e171d4, 0xa8f0, + 0x4832, 0xbc, 0x7d, 0x06, 0xdf, 0xe3, 0xae, 0x58, 0xfd); + +DECLARE_INTERFACE_(IHXSSL, IUnknown) +{ + /* + * IUnknown methods + */ + + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32, AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32, Release) (THIS) PURE; + + /* + * + */ + + STDMETHOD(Initialize) (THIS) PURE; + + STDMETHOD(Shutdown) (THIS) PURE; + + STDMETHOD(PostConnect) (THIS_ + LONG32 nSocketNumber) PURE; + + STDMETHOD_(LONG32, Read) (THIS_ + LONG32 nSocketNumber, + void* buff, + LONG32 buffLen) PURE; + + STDMETHOD_(LONG32, Write) (THIS_ + LONG32 nSocketNumber, + void* buff, + LONG32 buffLen) PURE; + + STDMETHOD(Close) (THIS_ + LONG32 nSocketNumber) PURE; + + STDMETHOD(SetCallbacks) (THIS_ + void* readCallback, + void* writeCallback, + void* closeCallback) PURE; + + +}; +// $EndPrivate. + + + + + +// $Private: +/**************************************************************************** + * + * Interface: + * + * IHXBufferedSocket + * + * Purpose: + * + * This provdies a method for doing for doing more optimal + * TCP delivery using desired packet size and writev. + * + * IID_IHXTCPSocket: + * + * {00001402-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXBufferedSocket, + 0x00001402, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXBufferedSocket + +DECLARE_INTERFACE_(IHXBufferedSocket, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXTCPSocket methods + */ + + STDMETHOD(BufferedWrite) (THIS_ + IHXBuffer* pBuffer) PURE; + + STDMETHOD(FlushWrite) (THIS) PURE; + + STDMETHOD(SetDesiredPacketSize) (THIS_ + UINT32 ulPacketSize) PURE; + +}; +// $EndPrivate. + + +/**************************************************************************** + * + * Interface: + * + * IHXListenResponse + * + * Purpose: + * + * This is the response interface for the asynchronous TCP listening + * socket interface. + * + * IID_IHXListenResponse: + * + * {00000104-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXListenResponse, 0x00000104, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXListenResponse + +DECLARE_INTERFACE_(IHXListenResponse, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXListenResponse methods + */ + + STDMETHOD(NewConnection) (THIS_ + HX_RESULT status, + IHXTCPSocket* pTCPSocket) PURE; +}; + +/**************************************************************************** + * + * Interface: + * + * IHXListenSocket + * + * Purpose: + * + * This interfaces allows you to asynchronously listen on a port for + * TCP connections. + * + * IID_IHXListenSocket: + * + * {00000105-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXListenSocket, 0x00000105, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXListenSocket + +DECLARE_INTERFACE_(IHXListenSocket, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXListenSocket methods + */ + + STDMETHOD(Init) (THIS_ + UINT32 ulLocalAddr, + UINT16 port, + IHXListenResponse* /*IN*/ pListenResponse + ) PURE; +}; + + +/**************************************************************************** + * + * Interface: + * + * IHXNetworkServices + * + * Purpose: + * + * This is a factory interface for the various types of networking + * interfaces described above. + * + * IID_IHXNetworkServices: + * + * {00000106-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXNetworkServices, 0x00000106, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXNetworkServices + +DECLARE_INTERFACE_(IHXNetworkServices, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXNetworkServices methods + */ + + /************************************************************************ + * Method: + * IHXNetworkServices::CreateTCPSocket + * Purpose: + * Create a new TCP socket. + */ + STDMETHOD(CreateTCPSocket) (THIS_ + IHXTCPSocket** /*OUT*/ ppTCPSocket) PURE; + + /************************************************************************ + * Method: + * IHXNetworkServices::CreateUDPSocket + * Purpose: + * Create a new UDP socket. + */ + STDMETHOD(CreateUDPSocket) (THIS_ + IHXUDPSocket** /*OUT*/ ppUDPSocket) PURE; + + /************************************************************************ + * Method: + * IHXNetworkServices::CreateListenSocket + * Purpose: + * Create a new TCP socket that will listen for connections on a + * particular port. + */ + STDMETHOD(CreateListenSocket) (THIS_ + IHXListenSocket** /*OUT*/ ppListenSocket + ) PURE; + + /************************************************************************ + * Method: + * IHXNetworkServices::CreateResolver + * Purpose: + * Create a new resolver that can lookup host names + */ + STDMETHOD(CreateResolver) (THIS_ + IHXResolver** /*OUT*/ ppResolver) PURE; +}; + + +/**************************************************************************** + * + * Interface: + * + * IHXNetworkServices2 + * + * Purpose: + * + * This is a factory interface for the various types of networking + * interfaces described above. + * + * IID_IHXNetworkServices: + * + * {17951551-5683-11d3-B6BA-00C0F031C237} + * + */ + +// {17951551-5683-11d3-B6BA-00C0F031C237} +DEFINE_GUID(IID_IHXNetworkServices2, 0x17951551, 0x5683, 0x11d3, 0xb6, 0xba, 0x0, 0xc0, 0xf0, 0x31, 0xc2, 0x37); + +#undef INTERFACE +#define INTERFACE IHXNetworkServices2 + +DECLARE_INTERFACE_(IHXNetworkServices2, IHXNetworkServices) +{ + /************************************************************************ + * Method: + * IHXNetworkServices2::CreateLBoundTCPSocket + * Purpose: + * Create a new local bound TCP socket. + */ + STDMETHOD(CreateLBoundTCPSocket) (THIS_ + IHXTCPSocket** /*OUT*/ ppTCPSocket) PURE; +}; + + + +/**************************************************************************** + * + * Interface: + * + * IHXUDPResponse + * + * Purpose: + * + * This is the response interface for the asynchronous UDP networking + * interface. + * + * IID_IHXUDPResponse: + * + * {00000107-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXUDPResponse, 0x00000107, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXUDPResponse + +DECLARE_INTERFACE_(IHXUDPResponse, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXUDPResponse methods + */ + + STDMETHOD(ReadDone) (THIS_ + HX_RESULT status, + IHXBuffer* pBuffer, + ULONG32 ulAddr, + UINT16 nPort) PURE; +}; + + +/**************************************************************************** + * + * Interface: + * + * IHXUDPSocket + * + * Purpose: + * + * Provides the user with an asynchronous UDP networking interface. + * + * IID_IHXUDPSocket: + * + * {00000108-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXUDPSocket, 0x00000108, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXUDPSocket + +DECLARE_INTERFACE_(IHXUDPSocket, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXUDPSocket methods + * + * Network addresses and ports are in native byte order + */ + + STDMETHOD(Init) (THIS_ + ULONG32 ulAddr, + UINT16 nPort, + IHXUDPResponse* pUDPResponse) PURE; + + STDMETHOD(Bind) (THIS_ + UINT32 ulLocalAddr, + UINT16 nPort) PURE; + + STDMETHOD(Read) (THIS_ + UINT16 Size) PURE; + + STDMETHOD(Write) (THIS_ + IHXBuffer* pBuffer) PURE; + + STDMETHOD(WriteTo) (THIS_ + ULONG32 ulAddr, + UINT16 nPort, + IHXBuffer* pBuffer) PURE; + + STDMETHOD(GetLocalPort) (THIS_ + REF(UINT16) port) PURE; + + STDMETHOD(JoinMulticastGroup) (THIS_ + ULONG32 ulMulticastAddr, + ULONG32 ulInterfaceAddr) PURE; + + STDMETHOD(LeaveMulticastGroup) (THIS_ + ULONG32 ulMulticastAddr, + ULONG32 ulInterfaceAddr) PURE; +}; + + +/**************************************************************************** + * + * Interface: + * + * IHXResolver + * + * Purpose: + * + * This interface allows you to asynchronously resolve hostnames. + * + * IID_IHXResolver: + * + * {00000109-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXResolver, 0x00000109, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXResolver + +DECLARE_INTERFACE_(IHXResolver, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXResolver methods + */ + + STDMETHOD(Init) (THIS_ + IHXResolverResponse* pResponse) PURE; + + STDMETHOD(GetHostByName) (THIS_ + const char* pHostName) PURE; +}; + + +/**************************************************************************** + * + * Interface: + * + * IHXResolverResponse + * + * Purpose: + * + * This is the response interface for the asynchronous DNS hostname + * resolver. + * + * IID_IHXResolverResponse: + * + * {0000010A-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXResolverResponse, 0x0000010A, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXResolverResponse + +DECLARE_INTERFACE_(IHXResolverResponse, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXResolverResponse methods + */ + + STDMETHOD(GetHostByNameDone) (THIS_ + HX_RESULT status, + ULONG32 ulAddr) PURE; +}; + + +/**************************************************************************** + * + * Interface: + * + * IHXInterruptSafe + * + * Purpose: + * + * This interface is used in Macintosh implementations of callback + * functions, renderers, etc... to determine if interrupt time execution + * is supported. If this interface is not implemented then it is assumed + * that interrupt time execution is NOT supported. There are restrictions + * on what may be executed at interrupt time; please consult the Macintosh + * Deferred Task Manager tech notes from Apple. + * + * IID_IHXInterruptSafe: + * + * {0000010B-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXInterruptSafe, 0x0000010B, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXInterruptSafe + +DECLARE_INTERFACE_(IHXInterruptSafe, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXInterruptSafe methods + */ + + /************************************************************************ + * Method: + * IHXInterruptSafe::IsInterruptSafe + * Purpose: + * This is the function that will be called to determine if + * interrupt time execution is supported. + */ + STDMETHOD_(HXBOOL,IsInterruptSafe) (THIS) PURE; +}; + + +/**************************************************************************** + * + * Interface: + * + * IHXAsyncIOSelection + * + * Purpose: + * + * This interface is implemented by the server/player context on Unix + * platforms. This interface allows your plugin to get callbacks based + * I/O events that are normally handled by select(). This interface + * allows you to setup callbacks which will be executed when a file + * descriptor is ready for reading, writing, or has an exception. + * + * IID_IHXAsyncIOSelection: + * + * {0000010C-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXAsyncIOSelection, 0x0000010C, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXAsyncIOSelection + +DECLARE_INTERFACE_(IHXAsyncIOSelection, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXAsyncIOSelection methods + */ + + /************************************************************************ + * Method: + * IHXAsyncIOSelection::Add + * Purpose: + * This function will allow you to receive a callback when the + * given descriptor is ready for read, write, or has an + * exception. This function is only available on Unix, and is + * intended to replace the functionality of select(). + */ + STDMETHOD(Add) (THIS_ + IHXCallback* pCallback, + INT32 lFileDescriptor, + UINT32 ulType) PURE; + + /************************************************************************ + * Method: + * IHXAsyncIOSelection::Remove + * Purpose: + * This function will allow you remove the callback associated + * with the given descriptor from the event handler. + * This function is only available on Unix, and is intended to + * replace the functionality of select(). + */ + STDMETHOD(Remove) (THIS_ + INT32 lFileDescriptor, + UINT32 ulType) PURE; +}; + + +/**************************************************************************** + * + * Interface: + * + * IHXUDPMulticastInit + * + * Purpose: + * + * Provides the user with a way to set the TTL for outgoing multicast + * UDP packets. Usually shared with IHXUDPSocket. + * + * IID_IHXUDPMulticastInit: + * + * {0000010D-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXUDPMulticastInit, 0x0000010D, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXUDPMulticastInit + +DECLARE_INTERFACE_(IHXUDPMulticastInit, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXUDPMulticastInit methods + * + */ + + /************************************************************************ + * Method: + * IHXUDPMulticastInit::InitMulticast + * Purpose: + * This function will set the TTL (time to live) for the UDP socket + * so it can be used as a multicast socket, sending packets across + * the number of routers specified in the ulTTL parameter. + */ + + STDMETHOD(InitMulticast) (THIS_ + UINT8 chTTL) PURE; +}; + +/**************************************************************************** + * + * Interface: + * + * IHXInterruptState + * + * Purpose: + * + * This interface is used in Macintosh implementations to inform the + * the client engine when entering & leaving an interupt task. It is + * also used to determine if it is currently at interrupt time. + * Please consult the Macintosh Deferred Task Manager tech notes from Apple + * for information on interrupt tasks. + * + * IID_IHXInterruptState: + * + * {0000010E-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXInterruptState, 0x0000010E, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXInterruptState + +DECLARE_INTERFACE_(IHXInterruptState, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXInterruptState methods + */ + + /************************************************************************ + * Method: + * IHXInterruptState::AtInterruptTime + * Purpose: + * This function is called to determine if we are currently at + * interrupt task time. + */ + STDMETHOD_(HXBOOL,AtInterruptTime) (THIS) PURE; + + /************************************************************************ + * Method: + * IHXInterruptState::EnterInterruptState + * Purpose: + * This function is called when starting a deferred/interrupt task + */ + STDMETHOD(EnterInterruptState) (THIS) PURE; + + /************************************************************************ + * Method: + * IHXInterruptState::LeaveInterruptState + * Purpose: + * This function is called when leaving a deferred/interrupt task + */ + STDMETHOD(LeaveInterruptState) (THIS) PURE; + + /************************************************************************ + * Method: + * IHXInterruptState::EnableInterrupt + * Purpose: + * This function can be called to enable/disable interrupt time + * processsing + */ + STDMETHOD(EnableInterrupt) (THIS_ + HXBOOL bEnable) PURE; + + /************************************************************************ + * Method: + * IHXInterruptState::IsInterruptEnabled + * Purpose: + * This function can be called to find if the core is currently + * interrupt enabled. + */ + STDMETHOD_(HXBOOL, IsInterruptEnabled) (THIS) PURE; +}; + + +/**************************************************************************** + * + * Interface: + * + * IHXOptimizedScheduler + * + * Purpose: + * + * This interface provides the user with a way of scheduling callbacks + * that will be executed at some time in the future. + * + * This interface should ONLY be used if you need accurately timed + * callbacks. These callbacks should be efficient and should not consume + * much time/CPU. This is not a thread safe interface. The user has to + * take care of synchronization in their callbacks. + * + * IID_IHXOptimizedScheduler: + * + * {0000010F-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXOptimizedScheduler, 0x0000010F, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXOptimizedScheduler + +DECLARE_INTERFACE_(IHXOptimizedScheduler, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXOptimizedScheduler methods + */ + + /************************************************************************ + * Method: + * IHXOptimizedScheduler::RelativeEnter + * Purpose: + * Schedule a callback to be executed "ms" milliseconds from now + * This function is less percise then AbsoluteEnter and should only + * be used when accurate timing is not critical. + */ + STDMETHOD_(CallbackHandle,RelativeEnter) (THIS_ + IHXCallback* pCallback, + UINT32 ms) PURE; + + /************************************************************************ + * Method: + * IHXOptimizedScheduler::AbsoluteEnter + * Purpose: + * Schedule a callback to be executed at time "tVal". + */ + STDMETHOD_(CallbackHandle,AbsoluteEnter) (THIS_ + IHXCallback* pCallback, + HXTimeval tVal) PURE; + + /************************************************************************ + * Method: + * IHXOptimizedScheduler::Remove + * Purpose: + * Remove a callback from the scheduler. + */ + STDMETHOD(Remove) (THIS_ + CallbackHandle Handle) PURE; + + /************************************************************************ + * Method: + * IHXOptimizedScheduler::GetCurrentSchedulerTime + * Purpose: + * Gives the current time (in the timeline of the scheduler). + */ + STDMETHOD_(HXTimeval,GetCurrentSchedulerTime) (THIS) PURE; +}; + +/**************************************************************************** + * + * Interface: + * + * IHXThreadSafeScheduler + * + * Purpose: + * + * This interface provides the user with a way of scheduling callbacks + * that will be executed at some time in the future. This is identical + * to IHXScheduler except the scheduler events are considered thread-safe. + * + * IID_IHXThreadSafeScheduler: + * + * {00000120-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXThreadSafeScheduler, 0x00000120, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXThreadSafeScheduler + +DECLARE_INTERFACE_(IHXThreadSafeScheduler, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXThreadSafeScheduler methods + */ + + /************************************************************************ + * Method: + * IHXThreadSafeScheduler::RelativeEnter + * Purpose: + + * Schedule a callback to be executed "ms" milliseconds from now + * This function is less percise then AbsoluteEnter and should only + * be used when accurate timing is not critical. + */ + STDMETHOD_(CallbackHandle,RelativeEnter) (THIS_ + IHXCallback* pCallback, + UINT32 ms) PURE; + + /************************************************************************ + * Method: + * IHXThreadSafeScheduler::AbsoluteEnter + * Purpose: + * Schedule a callback to be executed at time "tVal". + */ + STDMETHOD_(CallbackHandle,AbsoluteEnter) (THIS_ + IHXCallback* pCallback, + HXTimeval tVal) PURE; + + /************************************************************************ + * Method: + * IHXThreadSafeScheduler::Remove + * Purpose: + * Remove a callback from the scheduler. + */ + STDMETHOD(Remove) (THIS_ + CallbackHandle Handle) PURE; +}; + +/**************************************************************************** + * + * Interface: + * + * IHXProcessEntryPoint + * + * Purpose: + * + * This interface is the entry point for an IHXProcess + * + * IID_IHXProcessEntryPoint + * + * {00000123-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXProcessEntryPoint, 0x00000123, 0x901, 0x11d1, 0x8b, 0x6, + 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXProcessEntryPoint + +DECLARE_INTERFACE_(IHXProcessEntryPoint, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + STDMETHOD(Func) (THIS_ + IUnknown* pContext) PURE; +}; + +/**************************************************************************** + * + * Interface: + * + * IHXProcess + * + * Purpose: + * + * This interface allows you to create new server processes and specify + * an entry point. It is queried off the context. + * + * IID_IHXProcess + * + * {00000122-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXProcess, 0x00000122, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXProcess + +DECLARE_INTERFACE_(IHXProcess, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + STDMETHOD(Start) (THIS_ + const char* pProcessName, + IHXProcessEntryPoint* pEntryPoint) PURE; +}; + +/**************************************************************************** + * + * Interface: + * + * IHXLoadBalancedListen + * + * Purpose: + * + * This interface is queried off of IHXListenSocket. It allows + * a plugin to specify that it wants the server to load balance + * multiple instances of itself. The server will instantiate multiple + * instances of the plugin as needed based on socket / descriptor limits. + * Each plugin instance should attempt to listen on the same port as + * other instances (they will share the port). + * + * IID_IHXLoadBalancedListen: + * + * {00000110-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXLoadBalancedListen, 0x00000110, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXLoadBalancedListen + +DECLARE_INTERFACE_(IHXLoadBalancedListen, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXLoadBalancedListen methods + */ + + /************************************************************************ + * Method: + * IHXLoadBalancedListen::SetID + * Purpose: + * This function set's the unique ID for this listen socket. This + * ID is used to determine whether or not different instances of + * a plugin trying to listen on a single port are actually the + * same plugin. Without this function, it would be possible for + * two completely different plugins to listen on the same port using + * the load balanced listener. + */ + STDMETHOD(SetID) (THIS_ + REFIID ID) PURE; + + /************************************************************************ + * Method: + * IHXLoadBalancedListen::SetReserveLimit + * Purpose: + * Sets the reserve limit for descriptors / sockets. If less + * than reserve limit descriptors / sockets are left then a new + * instance of the plugin will be created. + */ + STDMETHOD(SetReserveLimit) (THIS_ + UINT32 ulDescriptors, + UINT32 ulSockets) PURE; +}; + + +/**************************************************************************** + * + * Interface: + * + * IHXOverrideDefaultServices + * + * Purpose: + * + * This interface is queried off of the context. It allows + * a plugin to override any default services provided by the G2 system. + * Currently, it is supported only on the client side. + * You may currently override IHXNetworkServices using this interface + * You can use the same interface to later restore back the overridden services. + * This is done by calling the same OverrideServices() function with the + * original service QIed before the initial override. + * + * IID_IHXOverrideDefaultServices: + * + * {00000111-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXOverrideDefaultServices, 0x00000111, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXOverrideDefaultServices + +DECLARE_INTERFACE_(IHXOverrideDefaultServices, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXOverrideDefaultServices methods + */ + + /************************************************************************ + * Method: + * IHXOverrideDefaultServices::OverrideServices + * Purpose: + * Override default services provided by the G2 system. + * + */ + STDMETHOD(OverrideServices) (THIS_ + IUnknown* pContext) PURE; +}; + +typedef enum _HX_SOCKET_OPTION +{ + HX_SOCKOPT_REUSE_ADDR, + HX_SOCKOPT_REUSE_PORT, + HX_SOCKOPT_BROADCAST, + HX_SOCKOPT_SET_RECVBUF_SIZE, + HX_SOCKOPT_SET_SENDBUF_SIZE, + HX_SOCKOPT_MULTICAST_IF, + HX_SOCKOPT_IP_TOS +} HX_SOCKET_OPTION; + +/**************************************************************************** + * + * Interface: + * + * IHXSetSocketOption + * + * Purpose: + * + * Set sockt option + * + * IID_IHXSetSocketOption: + * + * IID_IHXSetSocketOption: {00000114-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXSetSocketOption, + 0x00000114, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXSetSocketOption +DECLARE_INTERFACE_(IHXSetSocketOption, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXListenSocket methods + */ + + STDMETHOD(SetOption) (THIS_ + HX_SOCKET_OPTION option, + UINT32 ulValue) PURE; +}; + +#define HX_THREADSAFE_METHOD_FF_GETPACKET 0x00000001 +/* + * FileFormat::GetPacket() only calls: + * CCF->CI(Buffer), CCF->CI(Packet), CCF->CI(Values), *Alloc, *Free, + * FS->Read(), FS->Close(), FS->Seek(), + * FFR->PacketReady(), FFR->StreamDone() + * Context->Scheduler->*, + * CCF->CI(Mutex), Mutex->* + * Context->ErrorMessages + * + * XXXSMPNOW + */ + +#define HX_THREADSAFE_METHOD_FS_READ 0x00000002 +/* + * FileSystem::Read()/Seek()/Close() only calls: + * CCF->CI(Buffer), CCF->CI(Packet), CCF->CI(Values), *Alloc, *Free, + * FS->Read(), FS->Close(), FS->Seek(), + * Context->Scheduler->*, + * CCF->CI(Mutex), Mutex->* + * Context->ErrorMessages + * + * XXXSMPNOW + */ +#define HX_THREADSAFE_METHOD_FSR_READDONE 0x00000004 +/* + * FileFormat::ReadDone()/SeekDone()/CloseDone() only calls: + * CCF->CI(Buffer), CCF->CI(Packet), CCF->CI(Values), *Alloc, *Free, + * FS->Read(), FS->Close(), FS->Seek(), + * FFR->PacketReady(), FFR->StreamDone() + * Context->Scheduler->*, + * CCF->CI(Mutex), Mutex->* + * Context->ErrorMessages + * + * XXXSMPNOW + */ +#define HX_THREADSAFE_METHOD_CACHE_FILE 0x00000008 +/* + * FileSystem::Read()/Seek()/Close() only calls: + * CCF->CI(Buffer), CCF->CI(Packet), CCF->CI(Values), *Alloc, *Free, + * FS->Read(), FS->Close(), FS->Seek(), + * IHXCacheFile->*, IHXCacheFileResponse->*, + * Context->Scheduler->*, + * CCF->CI(Mutex), Mutex->* + * Context->ErrorMessages + * + * XXXSMPNOW + */ +#define HX_THREADSAFE_METHOD_CACHE_FILE_RESPONSE 0x00000010 +/* + * FileSystem::Read()/Seek()/Close() only calls: + * CCF->CI(Buffer), CCF->CI(Packet), CCF->CI(Values), *Alloc, *Free, + * FS->Read(), FS->Close(), FS->Seek(), + * IHXCacheFile->*, IHXCacheFileResponse->*, + * Context->Scheduler->*, + * CCF->CI(Mutex), Mutex->* + * Context->ErrorMessages + * + * XXXSMPNOW + */ + +/* + * Thread Safe flags for IHXDataConvert + */ +#define HX_THREADSAFE_METHOD_CONVERT_HEADERS 0x00000020 +/* + * IHXDataConvert::ConvertXXX()/CtrlBufferReady() only calls: + * CCF->CI(Buffer), CCF->CI(Packet), CCF->CI(Values), *Alloc, *Free, + * IHXDataConvertResponse->* + * Context->Scheduler->*, + * CCF->CI(Mutex), Mutex->* + * Context->ErrorMessages + * + * XXXSMPNOW + */ +#define HX_THREADSAFE_METHOD_CONVERT_DATA 0x00000040 +/* + * IHXDataConvert::ConvertXXX()/CtrlBufferReady() only calls: + * CCF->CI(Buffer), CCF->CI(Packet), CCF->CI(Values), *Alloc, *Free, + * IHXDataConvertResponse->* + * Context->Scheduler->*, + * CCF->CI(Mutex), Mutex->* + * Context->ErrorMessages + * + * XXXSMPNOW + */ +#define HX_THREADSAFE_METHOD_CONVERT_CTRL_BUFFER_READY 0x00000080 +/* + * IHXDataConvert::ConvertXXX()/CtrlBufferReady() only calls: + * CCF->CI(Buffer), CCF->CI(Packet), CCF->CI(Values), *Alloc, *Free, + * IHXDataConvertResponse->* + * Context->Scheduler->*, + * CCF->CI(Mutex), Mutex->* + * Context->ErrorMessages + * + * XXXSMPNOW + */ +#define HX_THREADSAFE_METHOD_SOCKET_READDONE 0x00000100 + +#define HX_THREADSAFE_METHOD_ALL (~0) + +/**************************************************************************** + * + * Interface: + * + * IHXThreadSafeMethods + * + * Purpose: + * + * XXXSMPNOW + * + * IID_IHXThreadSafeMethods: + * + * {00000115-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXThreadSafeMethods, 0x00000115, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXThreadSafeMethods + +DECLARE_INTERFACE_(IHXThreadSafeMethods, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXThreadSafeMethods methods + */ + + /************************************************************************ + * Method: + * IHXThreadSafeMethods::IsThreadSafe + * Purpose: + * XXXSMPNOW + */ + STDMETHOD_(UINT32,IsThreadSafe) (THIS) PURE; +}; + + +/**************************************************************************** + * + * Interface: + * + * IHXMutex + * + * Purpose: + * + * XXXSMPNOW + * + * IID_IHXMutex: + * + * {00000116-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXMutex, 0x00000116, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXMutex + +/* + * The IHXCommonClassFactory supports creating an instance + * of this object. + */ +#define CLSID_IHXMutex IID_IHXMutex + +DECLARE_INTERFACE_(IHXMutex, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXMutex methods + */ + + /* XXXSMPNOW Comments */ + STDMETHOD(Lock) (THIS) PURE; + + STDMETHOD(TryLock) (THIS) PURE; + + STDMETHOD(Unlock) (THIS) PURE; +}; + +// $Private: +/**************************************************************************** + * + * Interface: + * + * IHXFastPathNetWrite + * + * Purpose: + * + * Private interface for high speed UDP output. + * + * IID_IHXFastPathNetWrite: + * + * {00000117-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXFastPathNetWrite, 0x00000117, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXFastPathNetWrite + +DECLARE_INTERFACE_(IHXFastPathNetWrite, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXFastPathNetWrite methods + * + */ + + STDMETHOD(FastWrite) (THIS_ + const UINT8* pBuffer, UINT32 ulLen) PURE; +}; + +/**************************************************************************** + * + * Interface: + * + * IHXWouldBlockResponse + * + * Purpose: + * + * Get notifications of EWOULDBLOCK conditions. + * + * IID_IHXWouldBlockResponse: + * + * {00000118-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXWouldBlockResponse, 0x00000118, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXWouldBlockResponse + +DECLARE_INTERFACE_(IHXWouldBlockResponse, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXWouldBlockResponse methods + * + */ + + + /* + * WouldBlock + * + * Return HXR_OK to go into blocked mode, causing a future + * WouldBlockCleared call. HXR_anythingelse to ignore. + */ + + STDMETHOD(WouldBlock) (THIS_ UINT32 id) PURE; + STDMETHOD(WouldBlockCleared)(THIS_ UINT32 id) PURE; + +}; + +/**************************************************************************** + * + * Interface: + * + * IHXWouldBlock + * + * Purpose: + * + * Notifier for EWOULDBLOCK conditions. + * + * IID_IHXWouldBlock: + * + * {00000119-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXWouldBlock, 0x00000119, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXWouldBlock + +DECLARE_INTERFACE_(IHXWouldBlock, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXWouldBlock methods + * + */ + + STDMETHOD(WantWouldBlock) (THIS_ + IHXWouldBlockResponse*, UINT32 id) PURE; +}; + +/**************************************************************************** + * + * Interface: + * + * IHXSharedUDPServices + * + * Purpose: + * + * Private interface for tying a UDP socket (via IHXUDPSocketContext) to + * a shared UDP resend port. Used to send UDP packet resend requests to one + * shared UDP port per streamer. + * + * IID_IHXSharedUDPServices + * + * {00000123-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXSharedUDPServices, 0x00000124, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXSharedUDPServices + +DECLARE_INTERFACE_(IHXSharedUDPServices, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXSharedUDPServices methods + */ + STDMETHOD(RegisterSharedResponse) (THIS_ + IHXUDPResponse* response, + UINT16 sPortEnum) PURE; + + STDMETHOD(UnregisterSharedResponse) (THIS) PURE; + + STDMETHOD_(UINT16, GetSharedPort) (THIS) PURE; + +}; + +/**************************************************************************** + * + * Interface: + * + * IHXThreadLocal + * + * Purpose: + * + * Thread-local information, namely the procnum. + * + * IID_IHXThreadLocal + * + * {00000125-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXThreadLocal, 0x00000125, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXThreadLocal + +#define CLSID_IHXThreadLocal IID_IHXThreadLocal + +DECLARE_INTERFACE_(IHXThreadLocal, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXThreadLocal methods + */ + + /* + * IHXThreadLocal::GetMaxThreads() + * + * Maximum number of threads on the system (MAX_THREADS on server) + */ + STDMETHOD_(int, GetMaxThreads) (THIS) PURE; + + /* + * IHXThreadLocal::GetThreadNumber() + * + * This thread's number (< MAX_THREADS) + */ + STDMETHOD_(int, GetThreadNumber) (THIS) PURE; +}; + +/**************************************************************************** + * + * Interface: + * + * IHXMemoryServices + * + * Purpose: + * + * Exposes server memory functions + * + * IID_IHXMemoryServices + * + * {00000126-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXMemoryServices, 0x00000126, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXMemoryServices + +#define CLSID_IHXMemoryServices IID_IHXMemoryServices + +DECLARE_INTERFACE_(IHXMemoryServices, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXMemoryServices methods + */ + + /* + * IHXMemoryServices::ValidateMemory() + * + * Make consistency checks on the server shared memory space. + * + * lStartPage, lPages + * + * Specifies a page range to allow smaller searches, if you have a + * repro case and want to fail as soon as possible after the scribble. + * Use lPages == 0 to check to the last page. (Pages in the shared + * space are numbered starting from zero. You can decide on a range + * based on the printout of previous errors.) + * + * ulFlags + * + * 0x00000001 fail -- abort() -- on finding an error. + * 0x00000002 do rudimentary checks on internal SharedMemory data arrays. + * + */ + STDMETHOD(ValidateMemory) (THIS_ + INT32 lStartPage, + INT32 lPages, + UINT32 ulFlags) PURE; +}; + + +typedef enum _HX_PRIVATE_SOCKET_OPTION +{ + HX_PRIVATE_SOCKOPT_IGNORE_WSAECONNRESET +} HX_PRIVATE_SOCKET_OPTION; + +/**************************************************************************** + * + * Interface: + * + * IHXSetPrivateSocketOption + * + * Purpose: + * + * Set private sockt option + * + * IID_IHXSetPrivateSocketOption: + * + * {00000127-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXSetPrivateSocketOption, + 0x00000127, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXSetPrivateSocketOption +DECLARE_INTERFACE_(IHXSetPrivateSocketOption, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXSetPrivateSocketOption methods + */ + + STDMETHOD(SetOption) (THIS_ + HX_PRIVATE_SOCKET_OPTION option, + UINT32 ulValue) PURE; +}; + +/**************************************************************************** + * + * Interface: + * + * IHXNetInterfaces + * + * Purpose: + * + * Network Interfaces + * + * IID_IHXNetInterfaces: + * + * {00000128-0901-11d1-8B06-00A024406D59} + * + */ +typedef enum +{ + NI_UNKNOWN, + NI_ETHERNET, + NI_TOKENRING, + NI_FDDI, + NI_PPP, + NI_LOOPBACK, + NI_SLIP, + NI_TUNNEL +} NIType; + +// Operational status of the interface using the +// values defined in RFC 2863. +typedef enum +{ + NI_OPER_STATUS_NON_OPERATIONAL= 0, + NI_OPER_STATUS_UNREACHABLE, + NI_OPER_STATUS_DISCONNECTED, + NI_OPER_STATUS_CONNECTING, + NI_OPER_STATUS_CONNECTED, + NI_OPER_STATUS_OPERATIONAL +} NIStatus; + +typedef enum +{ + NI_ADDR_UNKNOWN, + NI_ADDR_IPv4, + NI_ADDR_IPv6 +} NIAddressType; + +struct NIAddressInfo +{ + NIAddressType type; + IHXBuffer* pAddress; + IHXBuffer* pSubnet; + UINT32 ulSubnetPrefix; + NIAddressInfo* next; + + NIAddressInfo() + { + type = NI_ADDR_UNKNOWN; + pAddress = NULL; + pSubnet = NULL; + ulSubnetPrefix = 0; + next = NULL; + }; + + ~NIAddressInfo() + { + HX_RELEASE(pAddress); + HX_RELEASE(pSubnet); + next = NULL; + }; +}; + +struct NIInfo +{ + NIType type; + IHXBuffer* pDescription; + NIStatus status; + UINT32 ulIPv4Index; + UINT32 ulIPv6Index; + UINT32 ulMTU; + NIAddressInfo* pAddressInfo; + + NIInfo() + { + type = NI_UNKNOWN; + pDescription = NULL; + status = NI_OPER_STATUS_NON_OPERATIONAL; + ulIPv4Index = 0; + ulIPv6Index = 0; + ulMTU = 0; + pAddressInfo = NULL; + }; + + ~NIInfo() + { + HX_RELEASE(pDescription); + + NIAddressInfo* pTemp = pAddressInfo; + while (pTemp) + { + pAddressInfo = pAddressInfo->next; + HX_DELETE(pTemp); + pTemp = pAddressInfo; + } + }; +}; + +DEFINE_GUID(IID_IHXNetInterfaces, + 0x00000128, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXNetInterfaces +DECLARE_INTERFACE_(IHXNetInterfaces, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXNetworkInterfaces methods + */ + STDMETHOD(UpdateNetInterfaces) (THIS) PURE; + + STDMETHOD_(UINT32, GetNumOfNetInterfaces) (THIS) PURE; + + STDMETHOD(GetNetInterfaces) (THIS_ + UINT16 lIndex, + REF(NIInfo*) pNIInfo) PURE; + + STDMETHOD(AddAdviseSink) (THIS_ + IHXNetInterfacesAdviseSink* pSink) PURE; + + STDMETHOD(RemoveAdviseSink) (THIS_ + IHXNetInterfacesAdviseSink* pSink) PURE; +}; + +/**************************************************************************** + * + * Interface: + * + * IHXNetInterfacesAdviseSink + * + * Purpose: + * + * Network Interfaces Advise Sink + * + * IID_IHXNetInterfaces: + * + * {00000129-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXNetInterfacesAdviseSink, + 0x00000129, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXNetInterfacesAdviseSink +DECLARE_INTERFACE_(IHXNetInterfacesAdviseSink, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXNetInterfacesAdviseSink methods + */ + STDMETHOD(NetInterfacesUpdated) (THIS) PURE; +}; + +// $EndPrivate. + +/**************************************************************************** + * + * Interface: + * + * IHXNetworkInterfaceEnumerator + * + * Purpose: + * + * Enumerate interfaces on a box. + * + * IID_IHXNetworkInterfaceEnumerator; + * + * {00000121-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXNetworkInterfaceEnumerator, 0x00000121, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXNetworkInterfaceEnumerator + +DECLARE_INTERFACE_(IHXNetworkInterfaceEnumerator, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + + /************************************************************************ + * Method: + * IHXNetworkInterfaceEnumerator::EnumerateInterfaces + * Purpose: + * returns a list of local interfaces + * Usage: + * If a buffer passed in is too small, it will return + * HXR_BUFFERTOOSMALL with ulNumInterfaces updated. + */ + STDMETHOD(EnumerateInterfaces) (THIS_ + REF(UINT32*) pulInterfaces, REF(UINT32) ulNumInterfaces) PURE; +}; + +/**************************************************************************** + * + * Interface: + * + * IHXUDPConnectedSocket + * + * Purpose: + * + * Connect and disconnect a UDP socket + * + * IID_IHXUDPConnectedSocket; + * + * {0000012A-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXUDPConnectedSocket, 0x0000012a, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXUDPConnectedSocket + +DECLARE_INTERFACE_(IHXUDPConnectedSocket, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + + /************************************************************************ + * Method: + * IHXUDPConnectedSocket::UDPConnect + * Purpose: + * Connect the udp socket + * Usage: + * Connect to the foreign addr and port the socket already knows + * about; this is implementation-dependent. + */ + STDMETHOD(UDPConnect) (THIS) PURE; + + /************************************************************************ + * Method: + * IHXUDPConnectedSocket::UDPConnect(ULONG32 ulAddr, UINT16 nPort); + * Purpose: + * Connect the udp socket + * Usage: + * Specify the host-ordered foreign addr and port to connect to. + */ + STDMETHOD(UDPConnect) (THIS_ ULONG32 ulAddr, UINT16 nPort) PURE; + + /************************************************************************ + * Method: + * IHXUDPConnectedSocket::UDPDisconnect + * Purpose: + * Disconnect the udp socket + */ + STDMETHOD(UDPDisconnect) (THIS) PURE; + + /************************************************************************ + * Method: + * IHXUDPConnectedSocket::IsUDPConnected + * Purpose: + * Return whether the socket is connected. + */ + STDMETHOD_(HXBOOL, IsUDPConnected) (THIS) PURE; + + /************************************************************************ + * Method: + * IHXUDPConnectedSocket::IsUDPConnected(REF(ULONG32) ulAddr, + * REF(UINT16) nPort) + * Purpose: + * Return whether the socket is connected, and the connected addr/port. + * Usage: + * Return the foreign addr/port the socket knows about, regardless of + * whether it's connected. This is the foreign addr that is connected + * to (if TRUE) or that will be used if Connect(void) called (if FALSE). + */ + STDMETHOD_(HXBOOL, IsUDPConnected) (THIS_ REF(ULONG32) ulAddr, + REF(UINT16) nPort) PURE; +}; + +/**************************************************************************** + * + * Interface: + * + * IHXAutoBWDetection + * + * Purpose: + * + * Auto Bandwidth Detection interface + * + * IID_IHXAutoBWDetection; + * + * {0000012b-0901-11d1-8B06-00A024406D59} + * + */ + +DEFINE_GUID(IID_IHXAutoBWDetection, + 0x0000012b, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXAutoBWDetection +DECLARE_INTERFACE_(IHXAutoBWDetection, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXAutoBWDetection methods + */ + STDMETHOD(InitAutoBWDetection) (THIS_ + HXBOOL bEnabled) PURE; + STDMETHOD(AddAutoBWDetectionSink) (THIS_ + IHXAutoBWDetectionAdviseSink* pSink) PURE; + STDMETHOD(RemoveAutoBWDetectionSink) (THIS_ + IHXAutoBWDetectionAdviseSink* pSink) PURE; +}; + +/**************************************************************************** + * + * Interface: + * + * IHXAutoBWDetectionAdviseSink + * + * Purpose: + * + * Auto Bandwidth Detection callback interface + * + * IID_IHXAutoBWDetectionAdviseSink: + * + * {0000012c-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXAutoBWDetectionAdviseSink, + 0x0000012c, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXAutoBWDetectionAdviseSink +DECLARE_INTERFACE_(IHXAutoBWDetectionAdviseSink, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXAutoBWDetectionAdviseSink methods + */ + STDMETHOD(AutoBWDetectionDone) (THIS_ + HX_RESULT status, + UINT32 ulBW) PURE; +}; + +/**************************************************************************** + * + * Interface: + * + * IHXAutoBWCalibration + * + * Purpose: + * + * Auto Bandwidth Calibration interface + * + * IID_IHXAutoBWDetection; + * + * {0000012d-0901-11d1-8B06-00A024406D59} + * + */ + +DEFINE_GUID(IID_IHXAutoBWCalibration, + 0x0000012d, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXAutoBWCalibration +DECLARE_INTERFACE_(IHXAutoBWCalibration, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXAutoBWCalibration methods + */ + STDMETHOD(InitAutoBWCalibration) (THIS_ + IHXValues* pValues) PURE; + STDMETHOD(StartAutoBWCalibration) (THIS) PURE; + STDMETHOD(StopAutoBWCalibration) (THIS) PURE; + STDMETHOD(AddAutoBWCalibrationSink) (THIS_ + IHXAutoBWCalibrationAdviseSink* pSink) PURE; + STDMETHOD(RemoveAutoBWCalibrationSink) (THIS_ + IHXAutoBWCalibrationAdviseSink* pSink) PURE; +}; + +/**************************************************************************** + * + * Interface: + * + * IHXAutoBWCalibrationAdviseSink + * + * Purpose: + * + * Auto Bandwidth Calibration callback interface + * + * IID_IHXAutoBWCalibrationAdviseSink: + * + * {0000012e-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXAutoBWCalibrationAdviseSink, + 0x0000012e, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXAutoBWCalibrationAdviseSink +DECLARE_INTERFACE_(IHXAutoBWCalibrationAdviseSink, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXAutoBWCalibrationAdviseSink methods + */ + STDMETHOD(AutoBWCalibrationStarted) (THIS_ + const char* pszServer) PURE; + STDMETHOD(AutoBWCalibrationDone) (THIS_ + HX_RESULT status, + UINT32 ulBW) PURE; +}; + +/**************************************************************************** + * + * Interface: + * + * IHXConnectionBWAdviseSink + * + * Purpose: + * + * Manages + * + * IID_IHXConnectionBWAdviseSink + * + * {7568B47F-0C1A-4099-B84B-D425C9746737} + * + */ +DEFINE_GUID(IID_IHXConnectionBWAdviseSink, +0x7568b47f, 0xc1a, 0x4099, 0xb8, 0x4b, 0xd4, 0x25, 0xc9, 0x74, 0x67, 0x37); +#undef INTERFACE +#define INTERFACE IHXConnectionBWAdviseSink +DECLARE_INTERFACE_(IHXConnectionBWAdviseSink, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXConnectionBWAdviseSink methods + */ + STDMETHOD(NewConnectionBW)(THIS_ UINT32 uConnectionBW) PURE; +}; + + +/**************************************************************************** + * + * Interface: + * + * IHXConnectionBWInfo + * + * Purpose: + * + * Manages + * + * IID_IHXConnectionBWInfo + * + * {9D1EDFB0-7A10-43f1-B008-8D0E00CA279F} + * + */ +DEFINE_GUID(IID_IHXConnectionBWInfo, +0x9d1edfb0, 0x7a10, 0x43f1, 0xb0, 0x8, 0x8d, 0xe, 0x0, 0xca, 0x27, 0x9f); + +#undef INTERFACE +#define INTERFACE IHXConnectionBWInfo +DECLARE_INTERFACE_(IHXConnectionBWInfo, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXConnectionBWInfo methods + */ + STDMETHOD(AddABDInfo)(THIS_ IHXAutoBWDetection* pABD, + IHXPreferredTransport* pPrefTransport) PURE; + STDMETHOD(RemoveABDInfo)(THIS_ IHXAutoBWDetection* pABD) PURE; + + /* + * IHXConnectionBWInfo::GetConnectionBW() + * + * Gets the current connection bandwidth estimate. + * + * Parameters: + * uBW : The connection BW is stored in this parameter on return + * bDetectedBWOnly : Specifies that only values derived from BW + * detection are considered OK. + * + * Returns: + * HXR_OK : uBW is set to the connection BW + * HXR_INCOMPLETE : This is returned if bDetectedBWOnly is set + * and there isn't any BW detection data available. + * uBW will contain a connection BW based on user + * preferences + * HXR_FAILED : No connection BW is available at this time + * + */ + STDMETHOD(GetConnectionBW)(THIS_ REF(UINT32) uBw, HXBOOL bDetectedBWOnly) PURE; + + STDMETHOD(AddSink)(THIS_ IHXConnectionBWAdviseSink* pSink) PURE; + STDMETHOD(RemoveSink)(THIS_ IHXConnectionBWAdviseSink* pSink) PURE; +}; + +#endif /* _HXENGIN_H_ */ diff --git a/amarok/src/engine/helix/helix-sp/helix-include/common/include/hxerror.h b/amarok/src/engine/helix/helix-sp/helix-include/common/include/hxerror.h new file mode 100644 index 00000000..b3d318c7 --- /dev/null +++ b/amarok/src/engine/helix/helix-sp/helix-include/common/include/hxerror.h @@ -0,0 +1,272 @@ + +/* + * + * This software is released under the provisions of the GPL version 2. + * see file "COPYING". If that file is not available, the full statement + * of the license can be found at + * + * http://www.fsf.org/licensing/licenses/gpl.txt + * + * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved. + * + */ + +#ifndef _HXERROR_H_ +#define _HXERROR_H_ + +/* + * Forward declarations of some interfaces defined here-in. + */ +typedef _INTERFACE IHXBuffer IHXBuffer; +typedef _INTERFACE IHXErrorSinkControl IHXErrorSinkControl; + + +/* Message Severity values */ + +enum { + HXLOG_EMERG = 0, /* A panic condition. Server / Player will halt or + restart. */ + + HXLOG_ALERT = 1, /* A condition that should be corrected immediately. + Needs user intervention to prevent problems. */ + + HXLOG_CRIT = 2, /* Critical conditions. */ + + HXLOG_ERR = 3, /* Errors. */ + + HXLOG_WARNING = 4, /* Warning messages. */ + + HXLOG_NOTICE = 5, /* Conditions that are not error conditions, but + should possibly be handled specially. */ + + HXLOG_INFO = 6, /* Informational messages. */ + + HXLOG_DEBUG = 7 /* Messages that contain information normally of use + only when debugging a program. */ +}; + + +/**************************************************************************** + * + * Interface: + * + * IHXErrorMessages + * + * Purpose: + * + * Error, event, and status message reporting interface + * + * IID_IHXErrorMessages: + * + * {00000800-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXErrorMessages, 0x00000800, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXErrorMessages + +DECLARE_INTERFACE_(IHXErrorMessages, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXErrorMessages methods + */ + + /************************************************************************ + * Method: + * IHXErrorMessages::Report + * Purpose: + * Call this method to report an error, event, or status message. + * Parameters: + * + * const UINT8 unSeverity + * Type of report. This value will impact how the player, tool, or + * server will react to the report. Possible values are described + * above. Depending on the error type, an error message with the + * HXR code, anda string translation of that code will be displayed. + * The error dialog includes a "more info" section that displays the + * user code and string, and a link to the more info URL. In the + * server these messages are logged to the log file. + * + * const ULONG32 ulHXCode + * Well known HXR error code. This will be translated to a text + * representation for display in an error dialog box or log file. + * + * const ULONG32 ulUserCode + * User specific error code. This will NOT be translated to a text + * representation. This can be any value the caller wants, it will + * be logged or displayed but not interpretted. + * + * const char* pUserString + * User specific error string. This will NOT be translated or + * modified. This can be any value the caller wants, it will + * be logged or displayed but not interpretted. + * + * const char* pMoreInfoURL + * User specific more info URL string. + * + */ + STDMETHOD(Report) (THIS_ + const UINT8 unSeverity, + HX_RESULT ulHXCode, + const ULONG32 ulUserCode, + const char* pUserString, + const char* pMoreInfoURL) PURE; + + /************************************************************************ + * Method: + * IHXErrorMessages::GetErrorText + * Purpose: + * Call this method to get the text description of a HXR error code. + * Parameters: + * HX_RESULT ulHXCode (A HXR error code) + * Return Value: + * IHXBuffer* containing error text. + */ + STDMETHOD_(IHXBuffer*, GetErrorText) (THIS_ + HX_RESULT ulHXCode) PURE; +}; + + +/**************************************************************************** + * + * Interface: + * + * IHXErrorSink + * + * Purpose: + * + * Error Sink Interface + * + * IID_IHXErrorSink: + * + * {00000801-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXErrorSink, 0x00000801, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXErrorSink + +DECLARE_INTERFACE_(IHXErrorSink, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXErrorSink methods + */ + + /************************************************************************ + * Method: + * IHXErrorSink::ErrorOccurred + * Purpose: + * After you have registered your error sink with an + * IHXErrorSinkControl (either in the server or player core) this + * method will be called to report an error, event, or status message. + * + * The meaning of the arguments is exactly as described in + * hxerror.h + */ + STDMETHOD(ErrorOccurred) (THIS_ + const UINT8 unSeverity, + const ULONG32 ulHXCode, + const ULONG32 ulUserCode, + const char* pUserString, + const char* pMoreInfoURL + ) PURE; +}; + + +/**************************************************************************** + * + * Interface: + * + * IHXErrorSinkControl + * + * Purpose: + * + * Error Sink Control Interface + * + * IID_IHXErrorSinkControl: + * + * {00000802-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXErrorSinkControl, 0x00000802, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXErrorSinkControl + + +DECLARE_INTERFACE_(IHXErrorSinkControl, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXErrorSinkControl methods + */ + + /************************************************************************ + * Method: + * IHXErrorSinkControl::AddErrorSink + * Purpose: + * Call this method to tell the sink controller to handle an error + * sink. + * + * This method also allows you to set a range of severity levels which + * you will receive reports for. + * + * Note: You should specify an invalid range (Low = 1, High = 0 for + * example) if you don't want to receive any errors. + * + * The default severity range is HXLOG_EMERG to HXLOG_INFO (0-6). + */ + STDMETHOD(AddErrorSink) (THIS_ + IHXErrorSink* pErrorSink, + const UINT8 unLowSeverity, + const UINT8 unHighSeverity) PURE; + + /************************************************************************ + * Method: + * IHXErrorSinkControl::AddErrorSink + * Purpose: + * Call this method to remove an error sink. + */ + STDMETHOD(RemoveErrorSink) (THIS_ + IHXErrorSink* pErrorSink) PURE; + +}; + +#endif /* _HXERROR_H_ */ diff --git a/amarok/src/engine/helix/helix-sp/helix-include/common/include/hxfiles.h b/amarok/src/engine/helix/helix-sp/helix-include/common/include/hxfiles.h new file mode 100644 index 00000000..f516af6b --- /dev/null +++ b/amarok/src/engine/helix/helix-sp/helix-include/common/include/hxfiles.h @@ -0,0 +1,2573 @@ + +/* + * + * This software is released under the provisions of the GPL version 2. + * see file "COPYING". If that file is not available, the full statement + * of the license can be found at + * + * http://www.fsf.org/licensing/licenses/gpl.txt + * + * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved. + * + */ + +#ifndef _HXFILES_H_ +#define _HXFILES_H_ + +/* + * Forward declarations of some interfaces defined here-in. + */ +typedef _INTERFACE IHXFileObject IHXFileObject; +typedef _INTERFACE IHXFileObjectExt IHXFileObjectExt; +typedef _INTERFACE IHXFileResponse IHXFileResponse; +typedef _INTERFACE IHXFileSystemObject IHXFileSystemObject; +typedef _INTERFACE IHXFileStat IHXFileStat; +typedef _INTERFACE IHXFileStatResponse IHXFileStatResponse; + +typedef _INTERFACE IHXFileSystemManager IHXFileSystemManager; +typedef _INTERFACE IHXFileSystemManagerResponse IHXFileSystemManagerResponse; +typedef _INTERFACE IHXFileExists IHXFileExists; +typedef _INTERFACE IHXFileExistsResponse IHXFileExistsResponse; +typedef _INTERFACE IHXFileMimeMapper IHXFileMimeMapper; +typedef _INTERFACE IHXFileMimeMapperResponse IHXFileMimeMapperResponse; +typedef _INTERFACE IHXBroadcastMapper IHXBroadcastMapper; +typedef _INTERFACE IHXBroadcastMapperResponse IHXBroadcastMapperResponse; +typedef _INTERFACE IHXGetFileFromSamePoolResponse IHXGetFileFromSamePoolResponse; +typedef _INTERFACE IHXBuffer IHXBuffer; +typedef _INTERFACE IHXPacket IHXPacket; +typedef _INTERFACE IHXValues IHXValues; +typedef _INTERFACE IHXMetaCreation IHXMetaCreation; + +typedef _INTERFACE IHXAuthenticator IHXAuthenticator; +typedef _INTERFACE IHXRequest IHXRequest; +typedef _INTERFACE IHXFileRename IHXFileRename; +typedef _INTERFACE IHXFileMove IHXFileMove; +typedef _INTERFACE IHXDirHandler IHXDirHandler; +typedef _INTERFACE IHXDirHandlerResponse IHXDirHandlerResponse; +typedef _INTERFACE IHXFileRemove IHXFileRemove; +// $Private: +typedef _INTERFACE IHXFastFileFactory IHXFastFileFactory; +typedef _INTERFACE IHXHTTPPostObject IHXHTTPPostObject; +typedef _INTERFACE IHXHTTPPostResponse IHXHTTPPostResponse; +typedef _INTERFACE IHXHTTPRedirect IHXHTTPRedirect; +typedef _INTERFACE IHXHTTPRedirectResponse IHXHTTPRedirectResponse; +typedef _INTERFACE IHXRM2Converter2 IHXRM2Converter2; +typedef _INTERFACE IHXRM2Converter2Response IHXRM2Converter2Response; +typedef _INTERFACE IHXPoolPathAdjustment IHXPoolPathAdjustment; +typedef _INTERFACE IHXPostDataHandler IHXPostDataHandler; +// $EndPrivate. + +/**************************************************************************** + * Defines: + * HX_FILE_XXXX + * Purpose: + * Flags for opening file objects + */ +#define HX_FILE_READ 1 +#define HX_FILE_WRITE 2 +#define HX_FILE_BINARY 4 +#define HX_FILE_NOTRUNC 8 +#define HX_FILE_NOPAC 16 + +/**************************************************************************** + * Defines: + * HX_FILEADVISE_XXXX + * Purpose: + * Flags for file object Advise method + */ +#define HX_FILEADVISE_RANDOMACCESS 1 +#define HX_FILEADVISE_SYNCACCESS 2 +#define HX_FILEADVISE_ASYNCACCESS 3 +#define HX_FILEADVISE_RANDOMACCESSONLY 4 +#define HX_FILEADVISE_ANYACCESS 5 + +/**************************************************************************** + * Defines: + * HX_FILERESPONSEADVISE_XXXX + * Purpose: + * Flags for file response Advise method + */ +#define HX_FILERESPONSEADVISE_REQUIREFULLREAD 1 + + +#if defined(_UNIX) || defined(_WINDOWS) +#include "hlxclib/sys/stat.h" +/* + * This is a subset of standard stat()/fstat() values that both Unix and + * Windows support (or at least define). + * + * These flags are returned from IHXFileStatResponse::StatDone() in the + * ulMode argument. + */ +#define HX_S_IFMT S_IFMT +#define HX_S_IFDIR S_IFDIR +#define HX_S_IFCHR S_IFCHR +#define HX_S_IFIFO S_IFIFO +#define HX_S_IFREG S_IFREG +#else +/* Macintosh */ +#define HX_S_IFMT 0170000 +#define HX_S_IFDIR 0040000 +#define HX_S_IFCHR 0020000 +#define HX_S_IFIFO 0010000 +#define HX_S_IFREG 0100000 +#endif + + +/**************************************************************************** + * + * Interface: + * + * IHXFileObject + * + * Purpose: + * + * Object that exports file control API + * + * IID_IHXFileObject: + * + * {00000200-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXFileObject, 0x00000200, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXFileObject + +DECLARE_INTERFACE_(IHXFileObject, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXFileObject methods + */ + + /************************************************************************ + * Method: + * IHXFileObject::Init + * Purpose: + * Associates a file object with the file response object it should + * notify of operation completness. This method should also check + * for validity of the object (for example by opening it if it is + * a local file). + */ + STDMETHOD(Init) (THIS_ + ULONG32 /*IN*/ ulFlags, + IHXFileResponse* /*IN*/ pFileResponse) PURE; + + /************************************************************************ + * Method: + * IHXFileObject::GetFilename + * Purpose: + * Returns the filename (without any path information) associated + * with a file object. + * + * Note: The returned pointer's lifetime expires as soon as the + * caller returns from a function which was called from the RMA + * core (i.e. when you return control to the RMA core) + * + */ + STDMETHOD(GetFilename) (THIS_ + REF(const char*) /*OUT*/ pFilename) PURE; + + /************************************************************************ + * Method: + * IHXFileObject::Close + * Purpose: + * Closes the file resource and releases all resources associated + * with the object. + */ + STDMETHOD(Close) (THIS) PURE; + + /************************************************************************ + * Method: + * IHXFileObject::Read + * Purpose: + * Reads a buffer of data of the specified length from the file + * and asynchronously returns it to the caller via the + * IHXFileResponse interface passed in to Init. + */ + STDMETHOD(Read) (THIS_ + ULONG32 ulCount) PURE; + + /************************************************************************ + * Method: + * IHXFileObject::Write + * Purpose: + * Writes a buffer of data to the file and asynchronously notifies + * the caller via the IHXFileResponse interface passed in to Init, + * of the completeness of the operation. + */ + STDMETHOD(Write) (THIS_ + IHXBuffer* pBuffer) PURE; + + /************************************************************************ + * Method: + * IHXFileObject::Seek + * Purpose: + * Seeks to an offset in the file and asynchronously notifies + * the caller via the IHXFileResponse interface passed in to Init, + * of the completeness of the operation. + * If the bRelative flag is TRUE, it is a relative seek; else + * an absolute seek. + */ + STDMETHOD(Seek) (THIS_ + ULONG32 ulOffset, + HXBOOL bRelative) PURE; + + /************************************************************************ + * Method: + * IHXFileObject::Advise + * Purpose: + * To pass information to the File Object advising it about usage + * heuristics. + */ + STDMETHOD(Advise) (THIS_ + ULONG32 ulInfo) PURE; +}; + + +/**************************************************************************** + * + * Interface: + * + * IHXFileObjectExt + * + * Purpose: + * + * Object that exports file control API + * + * IID_IHXFileObjectExt: + * + * {96DD5EB5-7EFD-4084-95CD-4D192A9036AF} + * + */ + DEFINE_GUID(IID_IHXFileObjectExt, 0x96dd5eb5, 0x7efd, 0x4084, 0x95, 0xcd, 0x4d, + 0x19, 0x2a, 0x90, 0x36, 0xaf); + +#undef INTERFACE +#define INTERFACE IHXFileObjectExt + +DECLARE_INTERFACE_(IHXFileObjectExt, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /************************************************************************ + * Method: + * IHXFileObjectExt::GetFullFilename + * Purpose: + * Returns the filename, with path information, associated + * with a file object. + * + * Note: The returned pointer's lifetime expires as soon as the + * caller returns from a function which was called from the RMA + * core (i.e. when you return control to the RMA core) + * + */ + STDMETHOD(GetFullFilename) (THIS_ + REF(IHXBuffer*) /*OUT*/ pFullFilename) PURE; +}; + + +/**************************************************************************** + * + * Interface: + * + * IHXFileResponse + * + * Purpose: + * + * Object that exports file response API + * + * IID_IHXFileResponse: + * + * {00000201-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXFileResponse, 0x00000201, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXFileResponse + +DECLARE_INTERFACE_(IHXFileResponse, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXFileResponse methods + */ + + /************************************************************************ + * Method: + * IHXFileResponse::InitDone + * Purpose: + * Notification interface provided by users of the IHXFileObject + * interface. This method is called by the IHXFileObject when the + * initialization of the file is complete. If the file is not valid + * for the file system, the status HXR_FAILED should be + * returned. + */ + STDMETHOD(InitDone) (THIS_ + HX_RESULT status) PURE; + + /************************************************************************ + * Method: + * IHXFileResponse::CloseDone + * Purpose: + * Notification interface provided by users of the IHXFileObject + * interface. This method is called by the IHXFileObject when the + * close of the file is complete. + */ + STDMETHOD(CloseDone) (THIS_ + HX_RESULT status) PURE; + + /************************************************************************ + * Method: + * IHXFileResponse::ReadDone + * Purpose: + * Notification interface provided by users of the IHXFileObject + * interface. This method is called by the IHXFileObject when the + * last read from the file is complete and a buffer is available. + */ + STDMETHOD(ReadDone) (THIS_ + HX_RESULT status, + IHXBuffer* pBuffer) PURE; + + /************************************************************************ + * Method: + * IHXFileResponse::WriteDone + * Purpose: + * Notification interface provided by users of the IHXFileObject + * interface. This method is called by the IHXFileObject when the + * last write to the file is complete. + */ + STDMETHOD(WriteDone) (THIS_ + HX_RESULT status) PURE; + + /************************************************************************ + * Method: + * IHXFileResponse::SeekDone + * Purpose: + * Notification interface provided by users of the IHXFileObject + * interface. This method is called by the IHXFileObject when the + * last seek in the file is complete. + */ + STDMETHOD(SeekDone) (THIS_ + HX_RESULT status) PURE; +}; + +/**************************************************************************** + * + * Interface: + * + * IHXAdvise + * + * Purpose: + * + * Allow IHXFileObject to query its IHXFileResponse interface + * + * IID_IHXAdvise: + * + * {43C3A3B8-8F76-4394-A4F8-07AA9091A0CA} + * + */ +DEFINE_GUID(IID_IHXAdvise, 0x43c3a3b8, 0x8f76, 0x4394, 0xa4, 0xf8, 0x7, + 0xaa, 0x90, 0x91, 0xa0, 0xca); + +#undef INTERFACE +#define INTERFACE IHXAdvise + +DECLARE_INTERFACE_(IHXAdvise, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXAdvise methods + */ + + /************************************************************************ + * Method: + * IHXAdvise::Advise + * Purpose: + * Allows IHXFileObject to query its IHXFileResponse about + * usage heuristics. It should pass HX_FILERESPONSEADVISE_xxx + * flags into this method. + */ + STDMETHOD(Advise) (THIS_ ULONG32 ulInfo) PURE; +}; + + +/**************************************************************************** + * + * Interface: + * + * IHXFileSystemObject + * + * Purpose: + * + * Object that allows a Controller to communicate with a specific + * File System plug-in session + * + * IID_IHXFileSystemObject: + * + * {00000202-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXFileSystemObject, 0x00000202, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXFileSystemObject + +DECLARE_INTERFACE_(IHXFileSystemObject, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXFileSystemObject methods + */ + + /************************************************************************ + * Method: + * IHXFileSystemObject::GetFileSystemInfo + * Purpose: + * Returns information vital to the instantiation of file system + * plugin. + * + * pShortName should be a short, human readable name in the form + * of "company-fsname". For example: pShortName = "pn-local". + */ + STDMETHOD(GetFileSystemInfo) (THIS_ + REF(const char*) /*OUT*/ pShortName, + REF(const char*) /*OUT*/ pProtocol) PURE; + + STDMETHOD(InitFileSystem) (THIS_ + IHXValues* pOptions) PURE; + + STDMETHOD(CreateFile) (THIS_ + IUnknown** /*OUT*/ ppFileObject) PURE; + + /* + * The following method is deprecated and should return HXR_NOTIMPL + */ + + STDMETHOD(CreateDir) (THIS_ + IUnknown** /*OUT*/ ppDirObject) PURE; + +}; + + +/**************************************************************************** + * + * Interface: + * + * IHXFileStat + * + * Purpose: + * + * Gets information about a specific File object + * + * IID_IHXFileStat: + * + * {00000205-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXFileStat, 0x00000205, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXFileStat + +DECLARE_INTERFACE_(IHXFileStat, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXFileStat methods + */ + + STDMETHOD(Stat) (THIS_ + IHXFileStatResponse* pFileStatResponse + ) PURE; +}; + + +/**************************************************************************** + * + * Interface: + * + * IHXFileStatResponse + * + * Purpose: + * + * Returns information about a specific File object + * + * IID_IHXFileStatResponse: + * + * {00000206-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXFileStatResponse, 0x00000206, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXFileStatResponse + +DECLARE_INTERFACE_(IHXFileStatResponse, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXFileStat methods + */ + + STDMETHOD(StatDone) (THIS_ + HX_RESULT status, + UINT32 ulSize, + UINT32 ulCreationTime, + UINT32 ulAccessTime, + UINT32 ulModificationTime, + UINT32 ulMode) PURE; +}; + +/**************************************************************************** + * + * Interface: + * + * IHXFileSystemManager + * + * Purpose: + * + * Gives out File Objects based on URLs + * + * IID_IHXFileSystemManager: + * + * {00000207-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXFileSystemManager, 0x00000207, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXFileSystemManager + +#define CLSID_IHXFileSystemManager IID_IHXFileSystemManager + +DECLARE_INTERFACE_(IHXFileSystemManager, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXFileSystemManager methods + */ + + STDMETHOD(Init) (THIS_ + IHXFileSystemManagerResponse* /*IN*/ pFileManagerResponse + ) PURE; + + /* GetFileObject attempts to locate an existing file via the DoesExist + * method in each file system's objects, and returns that object through + * FSManagerResponse->FileObjectReady + */ + STDMETHOD(GetFileObject) (THIS_ + IHXRequest* pRequest, + IHXAuthenticator* pAuthenticator) PURE; + + /* GetNewFileObject is similar to GetFileObject except that no DoesExist + * checks are done. The first file system that matches the mount point + * or protocol for the path in the request object creates the file + * which is then returned through FileObjectReady. This is especially + * useful for those who wish to open a brand new file for writing. + */ + STDMETHOD(GetNewFileObject) (THIS_ + IHXRequest* pRequest, + IHXAuthenticator* pAuthenticator) PURE; + + STDMETHOD(GetRelativeFileObject) (THIS_ + IUnknown* pOriginalObject, + const char* pPath) PURE; + + /* + * The following method is deprecated and should return HXR_NOTIMPL + */ + + STDMETHOD(GetDirObjectFromURL) (THIS_ + const char* pURL) PURE; +}; + + +/**************************************************************************** + * + * Interface: + * + * IHXFileSystemManagerResponse + * + * Purpose: + * + * Gives out File System objects based on URLs + * + * IID_IHXFileSystemManagerResponse: + * + * {00000208-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXFileSystemManagerResponse, 0x00000208, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXFileSystemManagerResponse + +DECLARE_INTERFACE_(IHXFileSystemManagerResponse, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXFileSystemManagerResponse methods + */ + + /************************************************************************ + * Method: + * IHXFileSystemManagerResponse::InitDone + * Purpose: + */ + STDMETHOD(InitDone) (THIS_ + HX_RESULT status) PURE; + + STDMETHOD(FileObjectReady) (THIS_ + HX_RESULT status, + IUnknown* pObject) PURE; + + /* + * The following method is deprecated and should return HXR_NOTIMPL + */ + + STDMETHOD(DirObjectReady) (THIS_ + HX_RESULT status, + IUnknown* pDirObject) PURE; +}; + + +/**************************************************************************** + * + * Interface: + * + * IHXFileExists + * + * Purpose: + * + * Checks for the existense of a file. Must be implemented. + * + * IID_IHXFileExists: + * + * {00000209-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXFileExists, 0x00000209, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXFileExists + +DECLARE_INTERFACE_(IHXFileExists, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXFileExists methods + */ + + /************************************************************************ + * Method: + * IHXFileExists::DoesExist + * Purpose: + */ + STDMETHOD(DoesExist) (THIS_ + const char* /*IN*/ pPath, + IHXFileExistsResponse* /*IN*/ pFileResponse) PURE; +}; + + +/**************************************************************************** + * + * Interface: + * + * IHXFileExistsResponse + * + * Purpose: + * + * Response interface for IHXFileExists. Must be implemented. + * + * IID_IHXFileExistsResponse: + * + * {0000020A-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXFileExistsResponse, 0x0000020a, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXFileExists + +DECLARE_INTERFACE_(IHXFileExistsResponse, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXFileExistsResponse methods + */ + + STDMETHOD(DoesExistDone) (THIS_ + HXBOOL bExist) PURE; +}; + + +/**************************************************************************** + * + * Interface: + * + * IHXFileMimeMapper + * + * Purpose: + * + * Allows you to specify a mime type for a specific file. + * Optional interface. + * + * IID_IHXFileMimeMapper: + * + * {0000020B-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXFileMimeMapper, 0x0000020b, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXFileMimeMapper + +DECLARE_INTERFACE_(IHXFileMimeMapper, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXFileMimeMapper methods + */ + + /************************************************************************ + * Method: + * IHXFileMimeMapper::FindMimeType + * Purpose: + */ + STDMETHOD(FindMimeType) (THIS_ + const char* /*IN*/ pURL, + IHXFileMimeMapperResponse* /*IN*/ pMimeMapperResponse + ) PURE; +}; + + +/**************************************************************************** + * + * Interface: + * + * IHXFileMimeMapperResponse + * + * Purpose: + * + * Response interface for IHXFileMimeMapper. + * Optional interface. + * + * IID_IHXFileMimeMapperResponse: + * + * {0000020C-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXFileMimeMapperResponse, 0x0000020c, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXFileMimeMapperResponse + +DECLARE_INTERFACE_(IHXFileMimeMapperResponse, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXFileMimeMapperResponse methods + */ + + /************************************************************************ + * Method: + * IHXFileMimeMapperResponse::MimeTypeFound + * Purpose: + * Notification interface provided by users of the IHXFileMimeMapper + * interface. This method is called by the IHXFileObject when the + * initialization of the file is complete, and the Mime type is + * available for the request file. If the file is not valid for the + * file system, the status HXR_FAILED should be returned, + * with a mime type of NULL. If the file is valid but the mime type + * is unknown, then the status HXR_OK should be returned with + * a mime type of NULL. + * + */ + STDMETHOD(MimeTypeFound) (THIS_ + HX_RESULT status, + const char* pMimeType) PURE; +}; + + +/**************************************************************************** + * + * Interface: + * + * IHXBroadcastMapper + * + * Purpose: + * + * Associates a file with a broadcast format plugin. + * Implementation only required by broadcast plugin file systems. + * + * IID_IHXBroadcastMapper: + * + * {0000020D-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXBroadcastMapper, 0x0000020d, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXBroadcastMapper + +DECLARE_INTERFACE_(IHXBroadcastMapper, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXBroadcastMapper methods + */ + + /************************************************************************ + * Method: + * IHXBroadcastMapper::FindBroadcastType + * Purpose: + */ + STDMETHOD(FindBroadcastType) (THIS_ + const char* /*IN*/ pURL, + IHXBroadcastMapperResponse* /*IN*/ pBroadcastMapperResponse) PURE; +}; + + +/**************************************************************************** + * + * Interface: + * + * IHXBroadcastMapperResponse + * + * Purpose: + * + * Response interface for IHXBroadcastMapper. + * Implementation only required by broadcast plugin file systems. + * + * IID_IHXBroadcastMapperResponse: + * + * {0000020E-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXBroadcastMapperResponse, 0x0000020e, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXBroadcastMapperResponse + +DECLARE_INTERFACE_(IHXBroadcastMapperResponse, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXBroadcastMapperResponse methods + */ + + /************************************************************************ + * Method: + * IHXBroadcastMapperResponse::BroadcastTypeFound + * Purpose: + * Notification interface provided by users of the IHXBroadcastMapper + * interface. This method is called by the File Object when the + * initialization of the file is complete, and the broadcast type is + * available for the request file. If the file is not valid for the + * file system, the status HXR_FAILED should be returned, + * with the broadcast type set to NULL. + * + */ + STDMETHOD(BroadcastTypeFound) (THIS_ + HX_RESULT status, + const char* pBroadcastType) PURE; +}; + + +/**************************************************************************** + * + * Interface: + * + * IHXGetFileFromSamePool + * + * Purpose: + * + * Gives out File Objects based on filenames and relative "paths" + * + * IID_IHXGetFileFromSamePool: + * + * {0000020f-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXGetFileFromSamePool, 0x0000020f, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); +#undef INTERFACE +#define INTERFACE IHXGetFileFromSamePool + +#define CLSID_IHXGetFileFromSamePool IID_IHXGetFileFromSamePool + +DECLARE_INTERFACE_(IHXGetFileFromSamePool, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + + /* + * IHXGetFileFromSamePool method + */ + /************************************************************************ + * Method: + * IHXGetFileFromSamePool::GetFileObjectFromPool + * Purpose: + * To get another FileObject from the same pool. + */ + STDMETHOD(GetFileObjectFromPool) (THIS_ + IHXGetFileFromSamePoolResponse*) PURE; +}; + + +/**************************************************************************** + * + * Interface: + * + * IHXGetFileFromSamePoolResponse + * + * Purpose: + * + * Gives out File Objects based on filenames and relative "paths" + * + * IID_IHXGetFileFromSamePoolResponse: + * + * {00000210-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXGetFileFromSamePoolResponse, 0x00000210, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); +#undef INTERFACE +#define INTERFACE IHXGetFileFromSamePoolResponse + +#define CLSID_IHXGetFileFromSamePoolResponse IID_IHXGetFileFromSamePoolResponse + +DECLARE_INTERFACE_(IHXGetFileFromSamePoolResponse, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXGetFileFromSamePoolResponse method + */ + /************************************************************************ + * Method: + * IHXGetFileFromSamePoolResponse::FileObjectReady + * Purpose: + * To return another FileObject from the same pool. + */ + STDMETHOD(FileObjectReady) (THIS_ + HX_RESULT status, + IUnknown* ppUnknown) PURE; +}; + + +/**************************************************************************** + * + * Interface: + * + * IHXFileAuthenticator + * + * Purpose: + * + * Set and Get a file object's authenticator object. + * + * IID_IHXFileAuthenticator: + * + * {00000211-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXFileAuthenticator, 0x00000211, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); +#undef INTERFACE +#define INTERFACE IHXFileAuthenticator + +#define CLSID_IHXFileAuthenticator IID_IHXFileAuthenticator + +DECLARE_INTERFACE_(IHXFileAuthenticator, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXFileAuthenticator methods + */ + STDMETHOD(SetAuthenticator) (THIS_ + IHXAuthenticator* pAuthenticator) PURE; + + STDMETHOD(GetAuthenticator) (THIS_ + REF(IHXAuthenticator*) pAuthenticator) PURE; +}; + + +/**************************************************************************** + * + * Interface: + * + * IHXRequestHandler + * + * Purpose: + * + * Object to manage IHXRequest objects + * + * IID_IHXRequestHandler: + * + * {00000212-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXRequestHandler, 0x00000212, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); +#undef INTERFACE +#define INTERFACE IHXRequestHandler + +#define CLSID_IHXRequestHandler IID_IHXRequestHandler + +DECLARE_INTERFACE_(IHXRequestHandler, IUnknown) +{ + /* + * IUnknown methods + */ + + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /************************************************************************ + * Method: + * IHXRequestHandler::SetRequest + * Purpose: + * Associates an IHXRequest with an object + */ + STDMETHOD(SetRequest) (THIS_ + IHXRequest* /*IN*/ pRequest) PURE; + + /************************************************************************ + * Method: + * IHXRequestHandler::GetRequest + * Purpose: + * Gets the IHXRequest object associated with an object + */ + STDMETHOD(GetRequest) (THIS_ + REF(IHXRequest*) /*OUT*/ pRequest) PURE; + +}; + + +/**************************************************************************** + * + * Interface: + * + * IHXRequestContext + * + * Purpose: + * + * Object to manage the context of the Request + * + * IID_IHXRequestContext: + * + * {00000217-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXRequestContext, 0x00000217, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); +#undef INTERFACE +#define INTERFACE IHXRequestContext + +#define CLSID_IHXRequestContext IID_IHXRequestContext + +DECLARE_INTERFACE_(IHXRequestContext, IUnknown) +{ + /* + * IUnknown methods + */ + + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXRequestContext methods + */ + + /************************************************************************ + * Method: + * IHXRequestContext::SetUserContext + * Purpose: + * Sets the Authenticated users Context. + */ + STDMETHOD(SetUserContext) + ( + THIS_ + IUnknown* pIUnknownNewContext + ) PURE; + + /************************************************************************ + * Method: + * IHXRequestContext::GetUserContext + * Purpose: + * Gets the Authenticated users Context. + */ + STDMETHOD(GetUserContext) + ( + THIS_ + REF(IUnknown*) pIUnknownCurrentContext + ) PURE; + + /************************************************************************ + * Method: + * IHXRequestContext::SetRequester + * Purpose: + * Sets the Object that made the request. + */ + STDMETHOD(SetRequester) + ( + THIS_ + IUnknown* pIUnknownNewRequester + ) PURE; + + /************************************************************************ + * Method: + * IHXRequestContext::GetRequester + * Purpose: + * Gets the Object that made the request. + */ + STDMETHOD(GetRequester) + ( + THIS_ + REF(IUnknown*) pIUnknownCurrentRequester + ) PURE; +}; + +/**************************************************************************** + * + * Interface: + * + * IHXRequest + * + * Purpose: + * + * Object to manage the RFC822 headers sent by the client + * + * IID_IHXRequest: + * + * {00000213-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXRequest, 0x00000213, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); +#undef INTERFACE +#define INTERFACE IHXRequest + +#define CLSID_IHXRequest IID_IHXRequest + +DECLARE_INTERFACE_(IHXRequest, IUnknown) +{ + /* + * IUnknown methods + */ + + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXRequest methods + */ + + /************************************************************************ + * Method: + * IHXRequest::SetRequestHeaders + * Purpose: + * Sets the headers that will be sent in the RFC822 header section + * of the request message + */ + STDMETHOD(SetRequestHeaders) (THIS_ + IHXValues* pRequestHeaders) PURE; + + /************************************************************************ + * Method: + * IHXRequest::GetRequestHeaders + * Purpose: + * Gets the headers that were sent in the RFC822 header section + * of the request message + */ + STDMETHOD(GetRequestHeaders) (THIS_ + REF(IHXValues*) pRequestHeaders) PURE; + + /************************************************************************ + * Method: + * IHXRequest::SetResponseHeaders + * Purpose: + * Sets the headers that will be returned in the RFC822 header + * section of the response message + */ + STDMETHOD(SetResponseHeaders) (THIS_ + IHXValues* pResponseHeaders) PURE; + + /************************************************************************ + * Method: + * IHXRequest::GetResponseHeaders + * Purpose: + * Gets the headers that were returned in the RFC822 header section + * of the response message + */ + STDMETHOD(GetResponseHeaders) (THIS_ + REF(IHXValues*) pResponseHeaders) PURE; + + /************************************************************************ + * Method: + * IHXRequest::SetURL + * Purpose: + * Sets the fully qualified path associated with a file object. + * Note: On the server, this path does not include the file system + * mount point. + */ + STDMETHOD(SetURL) (THIS_ + const char* pURL) PURE; + + /************************************************************************ + * Method: + * IHXRequest::GetURL + * Purpose: + * Returns the fully qualified path associated with a file object. + * Note: On the server, this path does not include the file system + * mount point. + * + * Note: The returned pointer's lifetime expires as soon as the + * caller returns from a function which was called from the RMA + * core (i.e. when you return control to the RMA core) + */ + STDMETHOD(GetURL) (THIS_ + REF(const char*) pURL) PURE; +}; + +/**************************************************************************** + * + * Interface: + * + * IHXFileRename + * + * Purpose: + * + * Interface to allow renaming of files. Query off of the File Object. + * Not all filesystem plugins implement this feature. + * + * IID_IHXFileRename: + * + * {00000214-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXFileRename, 0x00000214, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); +#undef INTERFACE +#define INTERFACE IHXFileRename + +DECLARE_INTERFACE_(IHXFileRename, IUnknown) +{ + /* + * IUnknown methods + */ + + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXFileRename methods + */ + + /************************************************************************ + * Method: + * IHXFileRename::Rename + * Purpose: + * Renames a file to a new name. + */ + STDMETHOD(Rename) (THIS_ + const char* pNewFileName) PURE; +}; + + +/**************************************************************************** + * + * Interface: + * + * IHXFileMove + * + * Purpose: + * + * Interface to allow removing of files. Query off of the File Object. + * Not all filesystem plugins implement this feature. + * + * IID_IHXFileMove: + * + * {23E72FB0-DE0E-11d5-AA9A-00010251B340} + * + */ +DEFINE_GUID(IID_IHXFileMove, 0x23e72fb0, 0xde0e, 0x11d5, 0xaa, 0x9a, 0x0, + 0x1, 0x2, 0x51, 0xb3, 0x40); + +#undef INTERFACE +#define INTERFACE IHXFileMove + +DECLARE_INTERFACE_(IHXFileMove, IUnknown) +{ + /* + * IUnknown methods + */ + + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXFileMove methods + */ + + /************************************************************************ + * Method: + * IHXFileMove::Move + * Purpose: + * Moves a file to a different location in the file system + */ + STDMETHOD(Move) (THIS_ + const char* pNewFilePathName) PURE; +}; + + +/**************************************************************************** + * + * Interface: + * + * IHXDirHandler + * + * Purpose: + * + * Object that exports directory handler API + * + * IID_IHXDirHandler: + * + * {00000215-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXDirHandler, 0x00000215, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXDirHandler + +DECLARE_INTERFACE_(IHXDirHandler, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXDirHandler methods + */ + + /************************************************************************ + * Method: + * IHXDirHandler::InitDirHandler + * Purpose: + * Associates a directory handler with the directory handler + * response, it should notify of operation completness. + */ + STDMETHOD(InitDirHandler) (THIS_ + IHXDirHandlerResponse* /*IN*/ pDirResponse) PURE; + + /************************************************************************ + * Method: + * IHXDirHandler::CloseDirHandler + * Purpose: + * Closes the directory handler resource and releases all resources + * associated with the object. + */ + STDMETHOD(CloseDirHandler) (THIS) PURE; + + /************************************************************************ + * Method: + * IHXDirHandler::MakeDir + * Purpose: + * Create the directory + */ + STDMETHOD(MakeDir) (THIS) PURE; + + /************************************************************************ + * Method: + * IHXDirHandler::ReadDir + * Purpose: + * Get a dump of the directory + */ + STDMETHOD(ReadDir) (THIS) PURE; +}; + + +/**************************************************************************** + * + * Interface: + * + * IHXDirHandlerResponse + * + * Purpose: + * + * Object that exports the directory handler response API + * + * IID_IHXDirHandlerResponse: + * + * {00000216-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXDirHandlerResponse, 0x00000216, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXDirHandlerResponse + +DECLARE_INTERFACE_(IHXDirHandlerResponse, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXDirHandlerResponse methods + */ + + /************************************************************************ + * Method: + * IHXDirHandlerResponse::InitDirHandlerDone + * Purpose: + * Notification interface provided by users of the IHXDirHandler + * interface. This method is called by the IHXDirHandler when the + * initialization of the object is complete. + */ + STDMETHOD(InitDirHandlerDone) (THIS_ + HX_RESULT status) PURE; + + /************************************************************************ + * Method: + * IHXDirHandlerResponse::CloseDirHandlerDone + * Purpose: + * Notification interface provided by users of the IHXDirHandler + * interface. This method is called by the IHXDirHandler when the + * close of the directory is complete. + */ + STDMETHOD(CloseDirHandlerDone) (THIS_ + HX_RESULT status) PURE; + + /************************************************************************ + * Method: + * IHXDirHandler::MakeDirDone + * Purpose: + * Notification interface provided by users of the IHXDirHandler + * interface. This method is called by the IHXDirHandler when the + * attempt to create the directory is complete. + */ + STDMETHOD(MakeDirDone) (THIS_ + HX_RESULT status) PURE; + + /************************************************************************ + * Method: + * IHXDirHandler::ReadDirDone + * Purpose: + * Notification interface provided by users of the IHXDirHandler + * interface. This method is called by the IHXDirHandler when the + * read from the directory is complete and a buffer is available. + */ + STDMETHOD(ReadDirDone) (THIS_ + HX_RESULT status, + IHXBuffer* pBuffer) PURE; +}; + + +// $Private: +/**************************************************************************** + * + * Interface: + * + * IHXGetRecursionLevel + * + * Purpose: + * + * Set's Recursion Level + * + * IID_GetRecursionLevel: + * + * {00000218-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXGetRecursionLevel, 0x00000218, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXGetRecursionLevel + +DECLARE_INTERFACE_(IHXGetRecursionLevel, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXGetRecursionLevel methods + */ + + STDMETHOD_(UINT32, GetRecursionLevel) (THIS) PURE; +}; + +/**************************************************************************** + * + * Interface: + * + * IHXFileRestrictor + * + * Purpose: + * + * Allows restrictions on per file basis. This will only get called for + * HTTP and it will only send in the localport. Maybe some day..... + * + * IID_IHXFileRestrictor: + * + * {00000219-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXFileRestrictor, 0x00000219, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXFileRestrictor + +DECLARE_INTERFACE_(IHXFileRestrictor, IUnknown) +{ + /* IUnknown methods */ + STDMETHOD(QueryInterface) (THIS_ REFIID riid, void** ppvObj) PURE; + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* IHXFileRestrictor methods */ + STDMETHOD_(HXBOOL, IsAllowed) (THIS_ const char* url, + const char* pLocalAddr, + const char* pLocalPort, + const char* pPeerAddr, + const char* pPeerPort) PURE; +}; + +// $EndPrivate. + +/**************************************************************************** + * + * Interface: + * + * IHXFileRemove + * + * Purpose: + * + * Interface to allow removing of files. Query off of the File Object. + * Not all filesystem plugins implement this feature. + * + * IID_IHXFileRemove: + * + * {0000021A-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXFileRemove, 0x0000021A, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); +#undef INTERFACE +#define INTERFACE IHXFileRemove + +DECLARE_INTERFACE_(IHXFileRemove, IUnknown) +{ + /* + * IUnknown methods + */ + + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXFileRemove methods + */ + + /************************************************************************ + * Method: + * IHXFileRemove::Remove + * Purpose: + * Removes a file from the file system. + */ + STDMETHOD(Remove) (THIS) PURE; +}; + + +// $Private: +/**************************************************************************** + * + * Interface: + * + * IHXFastFileFactory + * + * Purpose: + * + * Interface to allow wrapping file objects with a buffer and block-sharing + * sceme. Query off the server class factory. + * + * IID_IHXFastFileFactory: + * + * {0000021C-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXFastFileFactory, 0x0000021C, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#define CLSID_IHXFastFileFactory IID_IHXFastFileFactory + +#undef INTERFACE +#define INTERFACE IHXFastFileFactory + +DECLARE_INTERFACE_(IHXFastFileFactory, IUnknown) +{ + /* + * IUnknown methods + */ + + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXFastFileFactory methods + */ + + /************************************************************************ + * Method: + * IHXFastFileFactory::Wrap + * Purpose: + * Return an instantiated wrapper around an existing (but + * uninitialized) file object. + * + */ + STDMETHOD(Wrap) (THIS_ + REF(IUnknown*) /*OUT*/ pWrapper, + IUnknown* /*IN*/ pFileObj, + UINT32 /*IN*/ ulBlockSize, + HXBOOL /*IN*/ bAlignReads, + HXBOOL /*IN*/ bCacheStats) PURE; +}; + + +/**************************************************************************** + * + * Interface: + * + * IHXFastFileFactory2 + * + * Purpose: + * + * Interface to allow wrapping file objects with a buffer and block-sharing + * sceme. Query off the server class factory. + * + * IID_IHXFastFileFactory2: + * + * {0000021F-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXFastFileFactory2, 0x0000021F, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#define CLSID_IHXFastFileFactory2 IID_IHXFastFileFactory2 + +#undef INTERFACE +#define INTERFACE IHXFastFileFactory2 + +DECLARE_INTERFACE_(IHXFastFileFactory2, IUnknown) +{ + /************************************************************************ + * Method: + * IHXFastFileFactory2::Wrap + * Purpose: + * Return an instantiated wrapper around an existing (but + * uninitialized) file object. + * + */ + STDMETHOD(Wrap) (THIS_ + REF(IUnknown*) /*OUT*/ pWrapper, + IUnknown* /*IN*/ pFileObj, + UINT32 /*IN*/ ulBlockSize, + HXBOOL /*IN*/ bAlignReads, + HXBOOL /*IN*/ bCacheStats, + UINT32 /*IN*/ ulMaxBlockSize) PURE; +}; + + +/**************************************************************************** + * + * Interface: + * + * IHXFilePlacementRead + * + * Purpose: + * + * Reads into the passed buffer + * + * IID_IHXFilePlacementRead + * + * {0000021D-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXFilePlacementRead, 0x0000021D, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXFilePlacementRead + +DECLARE_INTERFACE_(IHXFilePlacementRead, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXFilePlacementRead methods + */ + STDMETHOD(Read) (THIS_ + ULONG32 ulAmount, + ULONG32 ulOffset, + char* pBuffer, + HXBOOL bOffsetBuffer) PURE; + + STDMETHOD_(ULONG32,AlignmentBoundary) (THIS) PURE; +}; + +// $EndPrivate. + +/**************************************************************************** + * + * Interface: + * + * IHXFastFileStats + * + * Purpose: + * + * Interface to allow file objects to request that at close they be + * informed how many bytes have been "fast cached" on their behalf if any. + * + * ulFastFileBytesSaved is the number of bytes that would have been read + * from the file object had FastFile not been in use (this includes some + * lookahead + * + * ulFastFileBytesNeeded is the amount of data actually read by the file + * system. + * + * IID_IHXFastFileStats: + * + * {0000021E-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXFastFileStats, 0x0000021E, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#define CLSID_IHXFastFileStats IID_IHXFastFileStats + +#undef INTERFACE +#define INTERFACE IHXFastFileStats + +DECLARE_INTERFACE_(IHXFastFileStats, IUnknown) +{ + /* + * IUnknown methods + */ + + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXFastFileStats methods + */ + + STDMETHOD(UpdateFileObjectStats) (THIS_ + UINT32 /*IN*/ ulFastFileBytesSaved, + UINT32 /*IN*/ ulFastFileBytesNeeded) PURE; +}; + +// $Private: +/**************************************************************************** + * + * Interface: + * + * IHXHTTPPostObject + * + * Purpose: + * + * Object that exports file control API + * + * IID_IHXHTTPPostObject: + * + * {00000112-0901-11d1-8B06-00A024406D59} + * + */ + +DEFINE_GUID(IID_IHXHTTPPostObject, 0x00000112, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#define CLSID_IHXHTTPPostObject IID_IHXHTTPPostObject + +#undef INTERFACE +#define INTERFACE IHXHTTPPostObject + +DECLARE_INTERFACE_(IHXHTTPPostObject, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXHTTPPostObject methods + */ + + /************************************************************************ + * Method: + * IHXHTTPPostObject::Init + * Purpose: + * Associates a file object with the file response object it should + * notify of operation completness. This method should also check + * for validity of the object (for example by opening it if it is + * a local file). + */ + STDMETHOD(Init) (THIS_ + ULONG32 /*IN*/ ulFlags, + IHXHTTPPostResponse* /*IN*/ pFileResponse) PURE; + + /************************************************************************ + * Method: + * IHXHTTPPostObject::Close + * Purpose: + * Closes the file resource and releases all resources associated + * with the object. + */ + STDMETHOD(Close) (THIS) PURE; + + /************************************************************************ + * Method: + * IHXHTTPPostObject::GetResponse + * Purpose: + * Tells the object to retrieve any response data from the POST. + * Calls IHXHTTPPostResponse with ResponseReady(IHXValues*). + */ + STDMETHOD(GetResponse) (THIS) PURE; + + + /************************************************************************ + * Method: + * IHXHTTPPostObject::Post + * Purpose: + * Writes a buffer of data to the HTTP URL and asynchronously notifies + * the caller via the IHXFileResponse interface passed in to Init, + * of the completeness of the operation. + */ + STDMETHOD(Post) (THIS_ + IHXBuffer* pBuffer) PURE; + + /************************************************************************ + * Method: + * IHXHTTPPostObject::SetSize + * Purpose: + * Set the total size of the Post(s) about to be made. + */ + STDMETHOD(SetSize) (THIS_ + ULONG32 ulLength) PURE; +}; + + +/**************************************************************************** + * + * Interface: + * + * IHXHTTPPostResponse + * + * Purpose: + * + * Object that exports file response API + * + * IID_IHXHTTPPostResponse: + * + * {00000113-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXHTTPPostResponse, 0x00000113, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXHTTPPostResponse + +DECLARE_INTERFACE_(IHXHTTPPostResponse, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXHTTPPostResponse methods + */ + + /************************************************************************ + * Method: + * IHXHTTPPostResponse::InitDone + * Purpose: + * Notification interface provided by users of the IHXHTTPPostObject + * interface. This method is called by the IHXHTTPPostObject when the + * initialization of the file is complete. If the file is not valid + * for the file system, the status HXR_FAILED should be + * returned. + */ + STDMETHOD(InitDone) (THIS_ + HX_RESULT status) PURE; + + /************************************************************************ + * Method: + * IHXHTTPPostResponse::CloseDone + * Purpose: + * Notification interface provided by users of the IHXHTTPPostObject + * interface. This method is called by the IHXHTTPPostObject when the + * close of the file is complete. + */ + STDMETHOD(CloseDone) (THIS_ + HX_RESULT status) PURE; + + /************************************************************************ + * Method: + * IHXHTTPPostResponse::ResponseReady + * Purpose: + * Notification interface provided by users of the IHXHTTPPostObject + * interface. This method is called by the IHXHTTPPostObject when the + * POST response data has been completely read. + */ + STDMETHOD(ResponseReady) (THIS_ + HX_RESULT status, + IHXBuffer* pContentBuffer) PURE; + + /************************************************************************ + * Method: + * IHXHTTPPostResponse::WriteDone + * Purpose: + * Notification interface provided by users of the IHXHTTPPostObject + * interface. This method is called by the IHXHTTPPostObject when the + * last write to the file is complete. + */ + STDMETHOD(PostDone) (THIS_ + HX_RESULT status) PURE; +}; +// $EndPrivate. + + +// $Private: +/**************************************************************************** + * + * Interface: + * + * IHXHTTPRedirect + * + * Purpose: + * + * Allows you to get redirect URL + * + * IID_IHXHTTPRedirect: + * + * {00002E00-0901-11d1-8B06-00A024406D59} + * + */ + +DEFINE_GUID(IID_IHXHTTPRedirect, 0x21eae0b9, 0x2e0c, 0x4003, 0xbb, 0x79, + 0xbc, 0x8c, 0xc5, 0x67, 0x2c, 0x2d); + +#undef INTERFACE +#define INTERFACE IHXHTTPRedirect + +DECLARE_INTERFACE_(IHXHTTPRedirect, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXHTTPRedirect methods + */ + + /************************************************************************ + * Method: + * IHXHTTPRedirect::Init + * Purpose: + * Initialize the response object + */ + STDMETHOD(Init) (THIS_ + IHXHTTPRedirectResponse* pRedirectResponse) PURE; + + /************************************************************************ + * Method: + * IHXHTTPRedirect::SetResponseObject + * Purpose: + * Initialize the response object w/o calling Init + */ + STDMETHOD(SetResponseObject) (THIS_ + IHXHTTPRedirectResponse* pRedirectResponse) PURE; + +}; + +/**************************************************************************** + * + * Interface: + * + * IHXHTTPRedirectResponse + * + * Purpose: + * + * Allows you to get redirect URL + * + * IID_IHXHTTPRedirectResponse: + * + * {00002E01-0901-11d1-8B06-00A024406D59} + * + */ + +DEFINE_GUID(IID_IHXHTTPRedirectResponse, 0x125a63b1, 0x669c, 0x42f9, 0xb1, + 0xf9, 0x1b, 0x53, 0xe9, 0x95, 0x82, 0x95); + +#undef INTERFACE +#define INTERFACE IHXHTTPRedirectResponse + +DECLARE_INTERFACE_(IHXHTTPRedirectResponse, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXHTTPRedirectResponse methods + */ + + /************************************************************************ + * Method: + * IHXHTTPRedirectResponse::RedirectDone + * Purpose: + * return the redirect URL + */ + STDMETHOD(RedirectDone) (THIS_ + IHXBuffer* pURL) PURE; + +}; + +/**************************************************************************** + * + * Interface: + * + * IHXRM2Converter2 + * + * Purpose: + * + * Interface to RM1->RM2 merge/converter module. This is a new improved + * interface which contains all of the functionality of the old one. + * However, this one is asynchronous where necessary and takes standard + * IHXFileObjects instead of pathnames for the required files. + * + * IHXRM2Converter + * + * {00002F00-0901-11D1-8B06-00A024406D59} + */ + +DEFINE_GUID(IID_IHXRM2Converter2, 0x00002F00, 0x901, 0x11d1, 0x8b, + 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#define CLSID_IRMARM2Converter2 IID_IHXRM2Converter2 + +#undef INTERFACE +#define INTERFACE IHXRM2Converter2 + +DECLARE_INTERFACE_(IHXRM2Converter2, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXRM2Converter2 methods + */ + STDMETHOD(Init) (THIS_ + IHXRM2Converter2Response* pResponse) PURE; + // + // AddStream takes a stream from an RM1 file and + // adds it to the new RM2 multirate section + // + STDMETHOD(AddStream) (THIS_ + const char* pFilename, + IHXFileObject* pFileObject, + UINT16 nStreamNumber, + HXBOOL bTagAsBackwardCompatible) PURE; + + // + // AddInterleavedStream takes a stream from an + // RM1 file and adds it to the initial interleaved + // backward compatibility section + // + STDMETHOD(AddInterleavedStream) (THIS_ + const char* pFilename, + IHXFileObject* pFileObject, + UINT16 nStreamNumber) PURE; + + // + // Add file slurps down all of the streams in + // an RM1 file and adds them to the new RM2 + // multirate section. + // + STDMETHOD(AddFile) (THIS_ + const char* pFilename, + IHXFileObject* pFileObject) PURE; + + // + // SetTitle,Author,Copyright, and Comment + // should all be pretty self explanatory... + // + STDMETHOD(SetTitle) (THIS_ + const char* pTitle) PURE; + STDMETHOD(SetAuthor) (THIS_ + const char* pAuthor) PURE; + STDMETHOD(SetCopyright) (THIS_ + const char* pCopyright) PURE; + STDMETHOD(SetComment) (THIS_ + const char* pComment) PURE; + + // + // PairStreams lets you pair two different + // streams together to play at a particular + // bandwidth (i.e. the sum of the stream + // bandwidths involved). + // + STDMETHOD(PairStreams) (THIS_ + const char* pFilename1, + UINT16 nStreamNumber1, + const char* pFilename2, + UINT16 nStreamNumber2) PURE; + + STDMETHOD(Merge) (THIS_ + const char* pFilename, + IHXFileObject* pOutputFile, + UINT32 ulBytesToWrite) PURE; + + STDMETHOD(Reset) (THIS) PURE; + STDMETHOD(Done) (THIS) PURE; +}; + + +/**************************************************************************** + * + * Interface: + * + * IHXRM2Converter2Response + * + * Purpose: + * + * Response Interface for IHXRM2Converter2. + * + * IHXRM2Converter + * + * {00002F01-0901-11D1-8B06-00A024406D59} + */ + +DEFINE_GUID(IID_IHXRM2Converter2Response, 0x00002F01, 0x901, 0x11d1, 0x8b, + 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59); + + +#undef INTERFACE +#define INTERFACE IHXRM2Converter2Response + +DECLARE_INTERFACE_(IHXRM2Converter2Response, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXRM2Converter2Response methods + */ + STDMETHOD(InitDone) (THIS_ + HX_RESULT status) PURE; + STDMETHOD(AddStreamDone) (THIS_ + HX_RESULT status) PURE; + STDMETHOD(AddInterleavedStreamDone) (THIS_ + HX_RESULT status) PURE; + STDMETHOD(AddFileDone) (THIS_ + HX_RESULT status) PURE; + STDMETHOD(MergeDone) (THIS_ + HX_RESULT status) PURE; +}; + + +/**************************************************************************** + * + * Interface: + * + * IHXPoolPathAdjustment + * + * Purpose: + * + * For file systems that use GetFileObjectFromPool to properly + * adjust absolute local URLs so they will be accessed relative + * to the pool filesystem mountpoint. + * + * IID_IHXPoolPathAdjustment: + * + * {00002F02-0901-11d1-8b06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXPoolPathAdjustment, 0x00002F02, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXPoolPathAdjustment + +DECLARE_INTERFACE_(IHXPoolPathAdjustment, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXPoolPathAdjustment Methods. + */ + STDMETHOD(AdjustAbsolutePath) (THIS_ + IHXBuffer* /*IN*/ pOldPath, + REF(IHXBuffer*) /*OUT*/ pNewPath) PURE; + +}; + +/* + * Interface: + * IHXPostDataHandler + * + * Purpose: + * + * Allows a file object to receive out of band POST data + * from an HTTP request. If this interface is not implemented + * data will be placed in request header as "PostData" + * + * When there is no more Data remaining, PostData will be + * called with a NULL argument. + * + * IID_IHXPostDataHandler: + * + * {0x0222a1b5-49fc-47e2-b69098befa0a588e} + * + */ +DEFINE_GUID(IID_IHXPostDataHandler, 0x0222a1b5, 0x49fc, 0x47e2, 0xb6, + 0x90, 0x98, 0xbe, 0xfa, 0x0a, 0x58, 0x8e); + +#undef INTERFACE +#define INTERFACE IHXPostDataHandler + +DECLARE_INTERFACE_(IHXPostDataHandler, IUnknown) +{ + /* + * IUnknownMethods + */ + + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + /* + * IHXPostDataHandler methods + */ + STDMETHOD(PostData) (THIS_ + IHXBuffer* pBuf) PURE; +}; + +// $EndPrivate. + +#endif /* _HXFILES_H_ */ diff --git a/amarok/src/engine/helix/helix-sp/helix-include/common/include/hxiids.h b/amarok/src/engine/helix/helix-sp/helix-include/common/include/hxiids.h new file mode 100644 index 00000000..ddcf9b33 --- /dev/null +++ b/amarok/src/engine/helix/helix-sp/helix-include/common/include/hxiids.h @@ -0,0 +1,1899 @@ + +/* + * + * This software is released under the provisions of the GPL version 2. + * see file "COPYING". If that file is not available, the full statement + * of the license can be found at + * + * http://www.fsf.org/licensing/licenses/gpl.txt + * + * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved. + * + */ + +/**************************************************************************** + * + * Exhaustive list of IID's used in IHX interfaces + * + * Note: These IIDs generally are duplicated in the headers that are specific + * to each interface, so if you change this file, change the other file(s) as + * well. Having all these IIDS in one files is convenient to some folks, but + * not everyone includes this file, hence the need to keep them in individual + * files as well. + */ + +#ifndef _HXIIDS_H_ +#define _HXIIDS_H_ + +/* + * File: + * hxcom.h + * Description: + * Interfaces defined by COM. + * Interfaces: + * IID_IUnknown: {00000000-0000-0000-C000000000000046} + * IID_IMalloc: {00000002-0000-0000-C000000000000046} + */ + +/* + * These GUIDs are defined in hxcom.h: + * + * DEFINE_GUID_ENUM(IID_IUnknown, 0x00000000, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46) + * DEFINE_GUID_ENUM(IID_IMalloc, 0x00000002, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46) + * + */ + +/* + * File: + * hxcomm.h + * Description: + * RealMedia Common Utility interfaces + * Interfaces: + * IID_IHXCommonClassFactory: {00000000-0901-11d1-8B06-00A024406D59} + * IID_IHXStatistics: {00000001-0901-11d1-8B06-00A024406D59} + * IID_IHXRegistryID: {00000002-0901-11d1-8B06-00A024406D59} + * IID_IHXServerFork: {00000003-0901-11d1-8B06-00A024406D59} + * IID_IHXServerControl: {00000004-0901-11d1-8B06-00A024406D59} + * IID_IHXServerControl2: {00000005-0901-11d1-8B06-00A024406D59} + * IID_IHXReconfigServerResponse: {00000006-0901-11d1-8B06-00A024406D59} +// $Private: + * IID_IHXFastAlloc: {0000000a-0901-11d1-8B06-00A024406D59} + * IID_IHXAccurateClock: {0000000b-0901-11d1-8B06-00A024406D59} +// $EndPrivate. + */ + +#ifndef _HXCCF_H_ +DEFINE_GUID_ENUM(IID_IHXCommonClassFactory, 0x00000000, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +#endif /* _HXCCF_H_ */ + +#ifndef _HXCOMM_H_ +DEFINE_GUID_ENUM(IID_IHXStatistics, 0x00000001, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXRegistryID, 0x00000002, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXServerFork, 0x00000003, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXServerControl, 0x00000004, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXServerControl2, 0x00000005, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXReconfigServerResponse, 0x00000006, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXServerReconfigNotification, 0x00000007, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXWantServerReconfigNotification, 0x00000008, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +// $Private: +DEFINE_GUID_ENUM(IID_IHXResolverExec, 0x00000009, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +// $Private: +DEFINE_GUID_ENUM(IID_IHXFastAlloc, 0x0000000a, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXAccurateClock, 0x0000000b, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +// $EndPrivate. +#endif + +/* + * File: + * hxengin.h + * Description: + * Interfaces related to callbacks, networking, and scheduling. + * Interfaces: + * IID_IHXCallback: {00000100-0901-11d1-8B06-00A024406D59} + * IID_IHXScheduler: {00000101-0901-11d1-8B06-00A024406D59} + * IID_IHXTCPResponse: {00000102-0901-11d1-8B06-00A024406D59} + * IID_IHXTCPSocket: {00000103-0901-11d1-8B06-00A024406D59} + * IID_IHXListenResponse: {00000104-0901-11d1-8B06-00A024406D59} + * IID_IHXListenSocket: {00000105-0901-11d1-8B06-00A024406D59} + * IID_IHXNetworkServices: {00000106-0901-11d1-8B06-00A024406D59} + * IID_IHXUDPResponse: {00000107-0901-11d1-8B06-00A024406D59} + * IID_IHXUDPSocket: {00000108-0901-11d1-8B06-00A024406D59} + * IID_IHXResolver: {00000109-0901-11d1-8B06-00A024406D59} + * IID_IHXResolverResponse: {0000010A-0901-11d1-8B06-00A024406D59} + * IID_IHXInterruptSafe: {0000010B-0901-11d1-8B06-00A024406D59} + * IID_IHXAsyncIOSelection: {0000010C-0901-11d1-8B06-00A024406D59} + * IID_IHXUDPMulticastInit: {0000010D-0901-11d1-8B06-00A024406D59} + * IID_IHXInterruptState: {0000010E-0901-11d1-8B06-00A024406D59} + * IID_IHXOptimizedScheduler: {0000010F-0901-11d1-8B06-00A024406D59} + * IID_IHXLoadBalancedListen: {00000110-0901-11d1-8B06-00A024406D59} + * IID_IHXOverrideDefaultServices: {00000111-0901-11d1-8B06-00A024406D59} + * IID_IHXHTTPPostObject: {00000112-0901-11d1-8B06-00A024406D59} + * IID_IHXHTTPPostResponse: {00000113-0901-11d1-8B06-00A024406D59} + * IID_IHXSetSocketOption: {00000114-0901-11d1-8B06-00A024406D59} + * IID_IHXThreadSafeMethods: {00000115-0901-11d1-8B06-00A024406D59} + * IID_IHXMutex: {00000116-0901-11d1-8B06-00A024406D59} + * IID_IHXNetworkInterfaceEnumerator{00000121-0901-11d1-8B06-00A024406D59} + * IID_IHXUDPConnectedSocket {0000012A-0901-11d1-8B06-00A024406D59} +// $Private: + * IID_IHXFastPathNetWrite: {00000117-0901-11d1-8B06-00A024406D59} + * IID_IHXWouldBlockResponse: {00000118-0901-11d1-8B06-00A024406D59} + * IID_IHXWouldBlock: {00000119-0901-11d1-8B06-00A024406D59} + * IID_IHXThreadSafeScheduler:{00000120-0901-11d1-8B06-00A024406D59} + * IID_IHXProcess: {00000122-0901-11d1-8B06-00A024406D59} + * IID_IHXProcessEntryPoint: {00000123-0901-11d1-8B06-00A024406D59} + * IID_IHXSharedUDPServices: {00000124-0901-11d1-8B06-00A024406D59} + * IID_IHXThreadLocal: {00000125-0901-11d1-8B06-00A024406D59} + * IID_IHXMemoryServices: {00000126-0901-11d1-8B06-00A024406D59} + * IID_IHXNetInterfaces: {00000128-0901-11d1-8B06-00A024406D59} + * IID_IHXNetInterfacesAdviseSink: {00000129-0901-11d1-8B06-00A024406D59} +// $EndPrivate. + * IID_IHXAutoBWDetection: {0000012b-0901-11d1-8B06-00A024406D59} + * IID_IHXAutoBWDetectionAdviseSink: {0000012c-0901-11d1-8B06-00A024406D59} + * + */ +#ifndef _HXENGIN_H_ +DEFINE_GUID_ENUM(IID_IHXCallback, 0x00000100, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXScheduler, 0x00000101, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXTCPResponse, 0x00000102, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXTCPSocket, 0x00000103, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXListenResponse, 0x00000104, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXListenSocket, 0x00000105, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXNetworkServices, 0x00000106, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXNetworkServices2, 0x17951551, 0x5683, 0x11d3, 0xb6, 0xba, 0x0, 0xc0, 0xf0, 0x31, 0xc2, 0x37) +DEFINE_GUID_ENUM(IID_IHXUDPResponse, 0x00000107, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXUDPSocket, 0x00000108, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXResolver, 0x00000109, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXResolverResponse, 0x0000010A, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXInterruptSafe, 0x0000010B, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXAsyncIOSelection, 0x0000010C, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXUDPMulticastInit, 0x0000010D, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXInterruptState, 0x0000010E, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXOptimizedScheduler, 0x0000010F, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXLoadBalancedListen, 0x00000110, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXOverrideDefaultServices, 0x00000111, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXSetSocketOption, 0x00000114, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXThreadSafeMethods, 0x00000115, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXMutex, 0x00000116, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXNetworkInterfaceEnumerator,0x00000121, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXUDPConnectedSocket, 0x0000012a, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +// $Private: +DEFINE_GUID_ENUM(IID_IHXFastPathNetWrite, 0x00000117, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXWouldBlockResponse, 0x00000118, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXWouldBlock, 0x00000119, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXThreadSafeScheduler,0x00000120, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXProcess, 0x00000122, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXProcessEntryPoint, 0x00000123, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXSharedUDPServices, 0x00000124, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXThreadLocal, 0x00000125, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXMemoryServices, 0x00000126, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXSetPrivateSocketOption,0x00000127, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXNetInterfaces, 0x00000128, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXNetInterfacesAdviseSink, 0x00000129, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXAutoBWDetection, 0x0000012b, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXAutoBWDetectionAdviseSink, 0x0000012c, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXAutoBWCalibration, 0x0000012d, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXAutoBWCalibrationAdviseSink, 0x0000012e, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXConnectionBWInfo, 0x9d1edfb0, 0x7a10, 0x43f1, 0xb0, 0x8, 0x8d, 0xe, 0x0, 0xca, 0x27, 0x9f) +DEFINE_GUID_ENUM(IID_IHXConnectionBWAdviseSink, 0x7568b47f, 0xc1a, 0x4099, 0xb8, 0x4b, 0xd4, 0x25, 0xc9, 0x74, 0x67, 0x37) +DEFINE_GUID_ENUM(IID_IHXSSL, 0x34e171d4, 0xa8f0, 0x4832, 0xbc, 0x7d, 0x06, 0xdf, 0xe3, 0xae, 0x58, 0xfd) +DEFINE_GUID_ENUM(IID_IHXTCPSecureSocket, 0x00000203, 0x911, 0x21d1, 0x8c, 0x4, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x54) +// $EndPrivate. +#endif + + +/* + * File: + * hxfiles.h + * Description: + * Interfaces related to file systems. + * Interfaces: + * IID_IHXFileObject: {00000200-0901-11d1-8B06-00A024406D59} + * IID_IHXFileObjectExt: {96dd5eb5-7efd-4084-95cd-4d192a9036af} + * IID_IHXFileResponse: {00000201-0901-11d1-8B06-00A024406D59} + * IID_IHXAdvise: {43C3A3B8-8F76-4394-A4F8-07AA9091A0CA} + * IID_IHXFileSystemObject: {00000202-0901-11d1-8B06-00A024406D59} + * IID_IHXDirObject: {00000203-0901-11d1-8B06-00A024406D59} + * IID_IHXDirResponse: {00000204-0901-11d1-8B06-00A024406D59} + * IID_IHXFileStat: {00000205-0901-11d1-8B06-00A024406D59} + * IID_IHXFileStatResponse: {00000206-0901-11d1-8B06-00A024406D59} + * IID_IHXFileSystemManager: {00000207-0901-11d1-8B06-00A024406D59} + * IID_IHXFileSystemManagerResponse: + * {00000208-0901-11d1-8B06-00A024406D59} + * IID_IHXFileExists: {00000209-0901-11d1-8B06-00A024406D59} + * IID_IHXFileExistsResponse: {0000020A-0901-11d1-8B06-00A024406D59} + * IID_IHXFileMimeMapper: {0000020B-0901-11d1-8B06-00A024406D59} + * IID_IHXFileMimeMapperResponse: {0000020C-0901-11d1-8B06-00A024406D59} + * IID_IHXBroadcastMapper: {0000020D-0901-11d1-8B06-00A024406D59} + * IID_BroadcastMimeMapperResponse:{0000020E-0901-11d1-8B06-00A024406D59} + * IID_IHXGetFileFromSamePool: {0000020F-0901-11d1-8B06-00A024406D59} + * IID_GetFileFromSamePoolResponse:{00000210-0901-11d1-8B06-00A024406D59} + * IID_IHXFileAuthenticator: {00000211-0901-11d1-8B06-00A024406D59} + * IID_IHXRequestHandler: {00000212-0901-11d1-8B06-00A024406D59} + * IID_IHXRequest: {00000213-0901-11d1-8B06-00A024406D59} + * IID_IHXFileRename: {00000214-0901-11d1-8B06-00A024406D59} + * IID_IHXFileMove: {23E72FB0-DE0E-11d5-AA9A-00010251B340} + * IID_IHXDirHandler: {00000215-0901-11d1-8B06-00A024406D59} + * IID_IHXDirHandlerResponse: {00000216-0901-11d1-8B06-00A024406D59} + * IID_IHXRequestContext {00000217-0901-11d1-8B06-00A024406D59} +// $Private: + * IID_IHXGetRecursionLevel {00000218-0901-11d1-8B06-00A024406D59} + * IID_IHXFileRestrictor {00000219-0901-11d1-8B06-00A024406D59} +// $EndPrivate. + * IID_IHXFileRemove: {0000021A-0901-11d1-8B06-00A024406D59} + * DEPRECATED DEPRECATED {0000021B-0901-11d1-8B06-00A024406D59} +// $Private: + * IID_IHXFastFileFactory {0000021C-0901-11d1-8B06-00A024406D59} + * IID_IHXFilePlacementRead {0000021D-0901-11d1-8B06-00A024406D59} + * IID_IHXFastFileStats {0000021E-0901-11d1-8B06-00A024406D59} + * IID_IHXFastFileFactory2 {0000021F-0901-11d1-8B06-00A024406D59} +// $EndPrivate. + * + */ +#ifndef _HXFILES_H_ +DEFINE_GUID_ENUM(IID_IHXHTTPPostObject, 0x00000112, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXHTTPPostResponse, 0x00000113, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXFileObject, 0x00000200, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXFileObjectExt, 0x96dd5eb5, 0x7efd, 0x4084, 0x95, 0xcd, 0x4d, 0x19, 0x2a, 0x90, 0x36, 0xaf) +DEFINE_GUID_ENUM(IID_IHXFileResponse, 0x00000201, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXAdvise, 0x43c3a3b8, 0x8f76, 0x4394, 0xa4, 0xf8, 0x7, 0xaa, 0x90, 0x91, 0xa0, 0xca) +DEFINE_GUID_ENUM(IID_IHXFileSystemObject, 0x00000202, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXDirObject, 0x00000203, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) //NOTE, use is deprecated +DEFINE_GUID_ENUM(IID_IHXDirResponse, 0x00000204, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) //NOTE, use is deprecated +DEFINE_GUID_ENUM(IID_IHXFileStat, 0x00000205, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXFileStatResponse, 0x00000206, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXFileSystemManager, 0x00000207, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXFileSystemManagerResponse, 0x00000208, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXFileExists, 0x00000209, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXFileExistsResponse, 0x0000020a, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXFileMimeMapper, 0x0000020b, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXFileMimeMapperResponse, 0x0000020c, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXBroadcastMapper, 0x0000020d, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXBroadcastMapperResponse, 0x0000020e, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXGetFileFromSamePool, 0x0000020f, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXGetFileFromSamePoolResponse,0x00000210, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXFileAuthenticator, 0x00000211, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXRequestHandler, 0x00000212, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXRequest, 0x00000213, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXFileRename, 0x00000214, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXFileMove, 0x23e72fb0, 0xde0e, 0x11d5, 0xaa, 0x9a, 0x0, 0x1, 0x2, 0x51, 0xb3, 0x40) +DEFINE_GUID_ENUM(IID_IHXDirHandler, 0x00000215, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXDirHandlerResponse, 0x00000216, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXRequestContext, 0x00000217, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +// $Private: +DEFINE_GUID_ENUM(IID_IHXGetRecursionLevel, 0x00000218, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXFileRestrictor, 0x00000219, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +// $EndPrivate. +DEFINE_GUID_ENUM(IID_IHXFileRemove, 0x0000021a, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +// $Private: +DEFINE_GUID_ENUM(IID_IHXFastFileFactory, 0x0000021c, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXFilePlacementRead, 0x0000021d, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXFastFileStats, 0x0000021e, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXFastFileFactory2, 0x0000021f, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +// $EndPrivate. +DEFINE_GUID_ENUM(IID_IHXFileRecognizer, 0x00000220, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXFileRecognizerResponse, 0x00000221, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +#endif +/* + * File: + * hxrendr.h + * Description: + * Interfaces related to renderers. + * Interfaces: + * IID_IHXRenderer: {00000300-0901-11d1-8B06-00A024406D59} + * IID_IHXPersistentRenderer: {00000301-0901-11d1-8B06-00A024406D59} + * IID_IHXUntimedRenderer: {00000303-0901-11d1-8B06-00A024406D59} + */ +DEFINE_GUID_ENUM(IID_IHXRenderer, 0x00000300, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +// $Private: +DEFINE_GUID_ENUM(IID_IHXPersistentRenderer, 0x00000301, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +// $EndPrivate. +DEFINE_GUID_ENUM(IID_IHXUntimedRenderer, 0x00000303, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) + + +/* + * File: + * hxcore.h + * Description: + * Interfaces related to the client core services. + * Interfaces: + * IID_IHXStream: {00000400-0901-11d1-8B06-00A024406D59} + * IID_IHXStreamSource {00000401-0901-11d1-8B06-00A024406D59} + * IID_IHXPlayer: {00000402-0901-11d1-8B06-00A024406D59} + * IID_IHXClientEngine: {00000403-0901-11d1-8B06-00A024406D59} + * IID_IHXClientEngineSelector{00000404-0901-11d1-8B06-00A024406D59} + * IID_IHXClientEngineSetup: {00000405-0901-11d1-8B06-00A024406D59} + * : {00000406-0901-11d1-8B06-00A024406D59} -- Deprecated + * IID_IHXInfoLogger: {00000409-0901-11d1-8B06-00A024406D59} +// $Private: + * IID_IHXPersistenceManager: {0000040B-0901-11d1-8B06-00A024406D59} + * IID_IHXDriverStreamManager:{0000040C-0901-11d1-8B06-00A024406D59} + * IID_IHXRendererAdviseSink: {0000040D-0901-11d1-8B06-00A024406D59} + * IID_IHXLayoutStream: {0000040E-0901-11d1-8B06-00A024406D59} + * IID_IHXValidator: {00000412-0901-11d1-8B06-00A024406D59} +// $EndPrivate. + * {0000040F-0901-11d1-8B06-00A024406D59} -- Deprecated + * IID_IHXPlayer2: {00000411-0901-11d1-8B06-00A024406D59} + * IID_IHXPlayerNavigator: {00000414-0901-11d1-8B06-00A024406D59} + * IID_IHXPersistentComponentManager: {00000415-0901-11d1-8B06-00A024406D59} + * IID_IHXPersistentComponent: {00000416-0901-11d1-8B06-00A024406D59} + */ +#ifndef _HXCORE_H_ +DEFINE_GUID_ENUM(IID_IHXStream, 0x00000400, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXStream2, 0x00000400, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x5a) +DEFINE_GUID_ENUM(IID_IHXStreamSource, 0x00000401, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXPlayer, 0x00000402, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXClientEngine, 0x00000403, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +#ifdef _UNIX +DEFINE_GUID_ENUM(IID_IHXClientEngineSelector, 0x00000404, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +#endif +DEFINE_GUID_ENUM(IID_IHXClientEngineSetup, 0x00000405, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXInfoLogger, 0x00000409, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +// $Private: +DEFINE_GUID_ENUM(IID_IHXClientEngineMapper, 0x0000040A, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXPersistenceManager, 0x0000040B, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXDriverStreamManager, 0x0000040C, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXPrivateStreamSource, 0x57dfd0e2, 0xc76e, 0x11d1, 0x8b, 0x5c, 0x0, 0x60, 0x8, 0x6, 0x55, 0x52) +DEFINE_GUID_ENUM(IID_IHXRendererAdviseSink, 0x0000040D, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXLayoutStream, 0x0000040E, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXRendererUpgrade, 0x00000410, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXValidator, 0x00000412, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +// $EndPrivate. +DEFINE_GUID_ENUM(IID_IHXPlayer2, 0x00000411, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXUpdateProperties, 0x00000413, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXUpdateProperties2, 0x00000413, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x5a) +DEFINE_GUID_ENUM(IID_IHXPlayerNavigator, 0x00000414, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXPersistentComponentManager, 0x00000415, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXPersistentComponent, 0x00000416, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +// $Private: +DEFINE_GUID_ENUM(IID_IHXSourceBufferingStats, 0x00000418, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXSourceBufferingStats2, 0x00000418, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x5a) +// $EndPrivate. +DEFINE_GUID_ENUM(IID_IHXSourceLatencyStats, 0x7A4D7872, 0xE5A9, 0x11D8, 0xAB, 0xE7, 0x00, 0x0A, 0x95, 0xBE, 0xFE, 0x6C) +#endif +/* + * File: + * hxprefs.h + * Description: + * Interfaces related to persistent preferences services. + * Interfaces: + * IID_IHXPreferences: {00000500-0901-11d1-8B06-00A024406D59} + */ +#ifndef _HXPREFS_H_ +DEFINE_GUID_ENUM(IID_IHXPreferences, 0x00000500, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXPreferences2, 0x00000503, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXPreferenceEnumerator, 0x00000504, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59) +#endif + +/* + * File: + * hxmon.h + * Description: + * Interfaces related to Monitor plugins. + * Interfaces: + * IID_IHXRegistry: {00000600-0901-11d1-8B06-00A024406D59} + * IID_IHXPropWatch: {00000601-0901-11d1-8B06-00A024406D59} + * IID_IHXPropWatchResponse: {00000602-0901-11d1-8B06-00A024406D59} + * IID_IHXActiveRegistry: {00000603-0901-11d1-8B06-00A024406D59} + * IID_IHXActivePropUser: {00000604-0901-11d1-8B06-00A024406D59} + * IID_IHXActivePropUserResponse: {00000605-0901-11d1-8B06-00A024406D59} + * IID_IHXCopyRegistry: {00000606-0901-11d1-8B06-00A024406D59} + * IID_IHXRegistryAltStringHandling: {00000607-0901-11d1-8B06-00A024406D59} + * IID_IHXRegistry2: {00000608-0901-11d1-8B06-00A024406D59} +// $Private: + * IID_IHXDeletePropResponse: {00000609-0901-11d1-8B06-00A024406D59} +// $EndPrivate. + */ +#ifndef _HXMON_H_ +DEFINE_GUID_ENUM(IID_IHXRegistry, 0x00000600, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXPropWatch, 0x00000601, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXPropWatchResponse, 0x00000602, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXActiveRegistry, 0x00000603, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXActivePropUser, 0x00000604, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXActivePropUserResponse, 0x00000605, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXCopyRegistry, 0x00000606, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXRegistryAltStringHandling,0x00000607, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXRegistry2, 0x00000608, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +// $Private: +DEFINE_GUID_ENUM(IID_IHXDeletedPropResponse, 0x00000609, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +// $EndPrivate. +#endif +/* + * File: + * hxausvc.h + * Description: + * Interfaces related to audio services. + * Interfaces: + * IID_IHXAudioPlayer: {00000700-0901-11d1-8B06-00A024406D59} + * IID_IHXAudioPlayerResponse: {00000701-0901-11d1-8B06-00A024406D59} + * IID_IHXAudioStream: {00000702-0901-11d1-8B06-00A024406D59} + * IID_IHXAudioDevice: {00000703-0901-11d1-8B06-00A024406D59} + * IID_IHXAudioDeviceResponse: {00000704-0901-11d1-8B06-00A024406D59} + * IID_IHXAudioHook: {00000705-0901-11d1-8B06-00A024406D59} + * IID_IHXAudioStreamInfoResponse: {00000706-0901-11d1-8B06-00A024406D59} + * IID_IHXVolume: {00000707-0901-11d1-8B06-00A024406D59} + * IID_IHXVolumeAdviseSink: {00000708-0901-11d1-8B06-00A024406D59} + * IID_IHXDryNotification: {00000709-0901-11d1-8B06-00A024406D59} + * IID_IHXAudioDeviceManager: {0000070A-0901-11d1-8B06-00A024406D59} + * IID_IHXAudioCrossFade: {0000070B-0901-11d1-8B06-00A024406D59} + * IID_IHXAudioStream2: {0000070C-0901-11d1-8B06-00A024406D59} + * IID_IHXAudioPushdown: {0000070D-0901-11d1-8B06-00A024406D59} + * IID_IHXAudioHookManager: {0000070E-0901-11d1-8B06-00A024406D59} + * IID_IHXAudioDeviceHookManager: {00000715-0901-11d1-8B06-00A024406D59} + * IID_IHXAudioLevelNormalization: {00000716-0901-11d1-8B06-00A024406D59} +// $Private: + * IID_IHXMultiPlayPauseSupport: {0000070F-0901-11d1-8B06-00A024406D59} + * IID_IHXAudioDeviceManager2: {00000710-0901-11d1-8B06-00A024406D59} + * IID_IHXAudioResampler: {00000711-0901-11d1-8b06-00A024406d59} + * IID_IHXAudioResamplerManager: {00000712-0901-11d1-8b06-00A024406d59} + * IID_IHXAudioPushdown2: {00000713-0901-11d1-8b06-00A024406d59} + * IID_IHXAudioMultiChannel: {00000714-0901-11d1-8b06-00A024406d59} +// $EndPrivate. + */ +DEFINE_GUID_ENUM(IID_IHXAudioPlayer, 0x00000700, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXAudioPlayerResponse, 0x00000701, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXAudioStream, 0x00000702, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXAudioDevice, 0x00000703, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXAudioDeviceResponse, 0x00000704, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXAudioHook, 0x00000705, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXAudioStreamInfoResponse, 0x00000706, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXVolume, 0x00000707, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXVolumeAdviseSink, 0x00000708, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXDryNotification, 0x00000709, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXAudioDeviceManager, 0x0000070A, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXAudioCrossFade, 0x0000070B, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXAudioStream2, 0x0000070C, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXAudioPushdown, 0x0000070D, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXAudioHookManager, 0x0000070E, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXAudioDeviceHookManager, 0x00000715, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXAudioLevelNormalization, 0x00000716, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) + +#if defined(HELIX_FEATURE_TIMELINE_WATCHER) +DEFINE_GUID_ENUM(IID_IHXTimelineWatcher, 0x211a3cae, 0xf1da, 0x4678, 0x84, 0xd5, 0xf, 0x12, 0xe7, 0xb1, 0xd8, 0xc6) +DEFINE_GUID_ENUM(IID_IHXTimelineManager, 0x9ed91bc3, 0x9e92, 0x46bb, 0xa0, 0x94, 0x6c, 0x8b, 0x94, 0x16, 0xcf, 0xb6) +#endif + +// $Private: +DEFINE_GUID_ENUM(IID_IHXMultiPlayPauseSupport, 0x0000070F, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXAudioDeviceManager2, 0x00000710, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXAudioResampler, 0x00000711, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXAudioResamplerManager, 0x00000712, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXAudioPushdown2, 0x00000713, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXAudioMultiChannel, 0x00000714, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +// $EndPrivate. + +/* + * File: + * hxerror.h + * Description: + * Interfaces related to error reporting and receiving notification of errors. + * Interfaces: + * IID_IHXErrorMessages: {00000800-0901-11d1-8B06-00A024406D59} + * IID_IHXErrorSink: {00000801-0901-11d1-8B06-00A024406D59} + * IID_IHXErrorSinkControl: {00000802-0901-11d1-8B06-00A024406D59} + */ +#ifndef _HXERROR_H_ +DEFINE_GUID_ENUM(IID_IHXErrorMessages, 0x00000800, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXErrorSink, 0x00000801, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXErrorSinkControl, 0x00000802, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +#endif +/* + * File: + * hxhyper.h + * Description: + * Simple Hyper Navigation Interfaces + * Interfaces: + * IID_IHXHyperNavigate: {00000900-0901-11d1-8B06-00A024406D59} + * IID_IHXHyperNavigate2: {00000901-0901-11d1-8B06-00A024406D59} + * IID_IHXHyperNavigateWithContext: {00000902-0901-11d1-8B06-00A024406D59} + */ +DEFINE_GUID_ENUM(IID_IHXHyperNavigate, 0x00000900, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +// $Private: +// will be made public post redstone beta 1 XXXRA +DEFINE_GUID_ENUM(IID_IHXHyperNavigate2, 0x00000901, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXHyperNavigateWithContext, 0x00000902, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXHyperNavigateHint, 0xd6507709, 0xf344, 0x4011, 0x94, 0xee, 0x57, 0x37, 0xd3, 0x78, 0xec, 0x4a) +// $EndPrivate. + +/* + * File: + * hxclsnk.h + * Description: + * Client Advise Sink Interfaces + * Interfaces: + * IID_IHXClientAdviseSink: {00000B00-0901-11d1-8B06-00A024406D59} +// $Private: + * IID_IHXClientRequestSink: {00000B01-0901-11d1-8B06-00A024406D59} +// $EndPrivate. + */ +DEFINE_GUID_ENUM(IID_IHXClientAdviseSink, 0x00000B00, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +// $Private: +DEFINE_GUID_ENUM(IID_IHXClientRequestSink, 0x00000B01, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +// $EndPrivate. + +/* + * File: + * hxplugn.h + * Description: + * Plugin inspector interface + * Interfaces: + * IID_IHXPlugin: {00000C00-0901-11d1-8B06-00A024406D59} + * IID_IHXPluginEnumerator {00000C01-0901-11d1-8B06-00A024406D59} + * IID_IHXPluginGroupEnumerator {00000C02-0901-11d1-8B06-00A024406D59} + * IID_IHXPluginReloader {00000C03-0901-11d1-8B06-00A024406D59} + * IID_IHXPluginFactory {00000C04-0901-11d1-8B06-00A024406D59} +// $Private: + * IID_IHXPluginChallenger {00000C05-0901-11d1-8B06-00A024406D59} + * IID_IHXPluginQuery {00000C06-0901-11d1-8B06-00A024406D59} + * : {00000C07-0901-11d1-8B06-00A024406D59} -- Deprecated + * : {00000C08-0901-11d1-8B06-00A024406D59} -- Deprecated + * IID_IHXGenericPlugin {00000C09-0901-11d1-8B06-00A024406D59} + * IID_IHXProxiedPlugin {00000C0A-0901-11d1-8B06-00A024406D59} +// $EndPrivate. + * IID_IHXPersistentComponent {00000C0B-0901-11d1-8B06-00A024406D59} + */ +#ifndef _HXPLUGN_H_ +DEFINE_GUID_ENUM(IID_IHXPlugin, 0x00000C00, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXPluginEnumerator, 0x00000C01, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXPluginGroupEnumerator, 0x00000C02, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXPluginSearchEnumerator, 0x3244b391, 0x42d4, 0x11d4, 0x95, 0x3, 0x0, 0x90, 0x27, 0x90, 0x29, 0x9c) +DEFINE_GUID_ENUM(IID_IHXPluginReloader, 0x00000C03, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXPluginFactory, 0x00000C04, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +// $Private: +DEFINE_GUID_ENUM(IID_IHXPluginChallenger, 0x00000C05, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXPluginQuery, 0x00000C06, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXGenericPlugin, 0x00000C09, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXProxiedPlugin, 0x00000C0A, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) + +DEFINE_GUID_ENUM(IID_IHXPluginHandler3, 0x32b19771, 0x2299, 0x11d4, 0x95, 0x3, 0x0, 0x90, 0x27, 0x90, 0x29, 0x9c) +DEFINE_GUID_ENUM(IID_IHXComponentPlugin, 0xf8a31571, 0x22ac, 0x11d4, 0x95, 0x3, 0x0, 0x90, 0x27, 0x90, 0x29, 0x9c) +DEFINE_GUID_ENUM(IID_IHXPluginNamespace, 0xf09e8891, 0x8e2d, 0x11d4, 0x82, 0xdb, 0x0, 0xd0, 0xb7, 0x4c, 0x2d, 0x25) +DEFINE_GUID_ENUM( IID_IHXPluginDatabase, 0xc2c65401, 0xa478, 0x11d4, 0x95, 0x18, 0x0, 0x90, 0x27, 0x90, 0x29, 0x9c) +// $EndPrivate. +#endif +/* + * File: + * hxwin.h + * Description: + * Site interfaces + * Interfaces: + * IID_IHXSiteWindowed: {00000D01-0901-11d1-8B06-00A024406D59} + * IID_IHXSiteWindowless: {00000D02-0901-11d1-8B-6-00A024406D59} + * IID_IHXSite: {00000D03-0901-11d1-8B-6-00A024406D59} + * IID_IHXSiteUser: {00000D04-0901-11d1-8B-6-00A024406D59} + * IID_IHXSiteWatcher: {00000D05-0901-11d1-8B-6-00A024406D59} + * IID_IHXSiteUserSupplier: {00000D06-0901-11d1-8B-6-00A024406D59} + * IID_IHXSiteSupplier: {00000D07-0901-11d1-8B-6-00A024406D59} + * IID_IHXSiteManager: {00000D08-0901-11d1-8B-6-00A024406D59} + * IID_IHXMultiInstanceSiteUserSupplier: {00000D09-0901-11d1-8B-6-00A024406D59} + * IID_IHXSite2: {00000D0A-0901-11d1-8B-6-00A024406D59} + * IID_IHXSiteTreeNavigation: {b52abc41-a919-11d8-b8a3-0003939ba95e} + * IID_IHXSiteFullScreen {00000D0B-0901-11d1-8B-6-00A024406D59} + // $Private: + * IID_IHXLayoutSiteGroupManager {00000D0C-0901-11d1-8B-6-00A024406D59} + // $EndPrivate. + * IID_IHXEventHookMgr {00000D0D-0901-11d1-8B-6-00A024406D59} + * IID_IHXEventHook {00000D0E-0901-11d1-8B-6-00A024406D59} + * IID_IHXPassiveSiteWatcher {00000D0F-0901-11d1-8B-6-00A024406D59} + * IID_IHXStatusMessage {00000D10-0901-11d1-8B-6-00A024406D59} + // $Private: + * IID_IHXGetImplementation {00000D11-0901-11d1-8B-6-00A024406D59} + * IID_IHXSiteEventHandler {00000D12-0901-11d1-8B-6-00A024406D59} + * IID_IHXSiteTransition {00000D13-0901-11d1-8B-6-00A024406D59} + * IID_IHXRegion {00000D14-0901-11d1-8B-6-00A024406D59} + * IID_IHXColorConverterManager {00000D15-0901-11d1-8B-6-00A024406D59} + * IID_IHXColorConverter {00000D16-0901-11d1-8B-6-00A024406D59} + * IID_IHXColorConverterHandler {00000D17-0901-11d1-8B-6-00A024406D59} + // $EndPrivate. + */ +#ifndef _HXWIN_H_ +DEFINE_GUID_ENUM(IID_IHXSiteWindowed, 0x00000D01, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXSiteWindowless, 0x00000D02, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXSite, 0x00000D03, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +// $Private: +DEFINE_GUID_ENUM(IID_IHXSiteComposition, 0x00000D19, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +// $EndPrivate. +DEFINE_GUID_ENUM(IID_IHXSiteUser, 0x00000D04, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXSiteWatcher, 0x00000D05, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXSiteUserSupplier, 0x00000D06, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXSiteSupplier, 0x00000D07, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXSiteManager, 0x00000D08, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXMultiInstanceSiteUserSupplier, 0x00000D09, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXSite2, 0x00000D0A, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXSiteTreeNavigation, 0xb52abc41, 0xa919, 0x11d8, 0xb8, 0xa3, 0x0, 0x03, 0x93, 0x9b, 0xa9, 0x5e) +// $Private: +DEFINE_GUID_ENUM(IID_IHXSiteControl, 0xdd25ca2e, 0x73a5, 0x4811, 0x99, 0x6f, 0x7e, 0x67, 0x26, 0xe7, 0x66, 0x8f) +// $EndPrivate. + +DEFINE_GUID_ENUM(IID_IHXSiteFullScreen, 0x00000D0B, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +// $Private: +DEFINE_GUID_ENUM(IID_IHXLayoutSiteGroupManager, 0x00000D0C, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +// $EndPrivate. +DEFINE_GUID_ENUM(IID_IHXEventHookMgr, 0x00000D0D, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXEventHook, 0x00000D0E, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXPassiveSiteWatcher, 0x00000D0F, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXStatusMessage, 0x00000D10, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +// $Private: +DEFINE_GUID_ENUM(IID_IHXGetImplementation, 0x00000D11, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXSiteEventHandler, 0x00000D12, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXSiteTransition, 0x00000D13, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXRegion, 0x00000D14, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXColorConverterManager, 0x00000D15, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXColorConverter, 0x00000D16, 0x903, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXColorConverterHandler, 0x00000D17, 0x903, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXColorConverterPlugin, 0x00000D18, 0x903, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXSiteManager2, 0x00000D20, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXSiteEnumerator, 0x67f8c5bd, 0x4b1d, 0x4c09, 0x8f, 0xb7, 0x8a, 0xc7, 0xc2, 0x0d, 0x29, 0xc7) +DEFINE_GUID_ENUM(IID_IHXOverlayResponse, 0x00000D22, 0x903, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXOverlayManager, 0x00000D21, 0x903, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +// $EndPrivate. +DEFINE_GUID_ENUM(IID_IHXKeyBoardFocus, 0x00000D23, 0x903, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXDrawFocus, 0x00000D24, 0x903, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXSubRectSite, 0x00000D25, 0x903, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +#endif +/* + * File: + * hxformt.h + * Description: + * Interfaces related to file and broadcast format plugins. + * Interfaces: + * + * IID_IHXFileFormatObject: {00000F00-0901-11d1-8B06-00A024406D59} + * IID_IHXBroadcastFormatObject: {00000F01-0901-11d1-8B06-00A024406D59} + * IID_IHXFormatResponse: {00000F02-0901-11d1-8B06-00A024406D59} + * IID_IHXFormatReuse: {e55077c4-a299-11d7-864c-0002b3658720} + * IID_IHXPacketFormat: {00000F03-0901-11d1-8B06-00A024406D59} + * IID_IHXPacketTimeOffsetHandler {00000F04-0901-11d1-8B06-00A024406D59} + * IID_IHXPacketTimeOffsetHandlerResponse {00000F05-0901-11d1-8B06-00A024406D59} + * IID_IHXLiveFileFormatInfo {00000F06-0901-11d1-8B06-00A024406D59} +// $Private: + * IID_IHXBroadcastLatency {00000F08-0901-11d1-8B06-00A024406D59} + * IID_IHXPayloadFormatObject {00000F07-0901-11d1-8B06-00A024406D59} + * IID_IHXBlockFormatObject {00000F09-0901-11d1-8B06-00A024406D59} + * IID_IHXFileFormatHeaderAdvise {00000F0A-0901-11d1-8B06-00A024406D59} + * IID_IHXFileFormatHeaderAdviseResponse {00000F0B-0901-11d1-8B06-00A024406D59} +// $EndPrivate. + */ +#ifndef _HXFORMT_H_ +DEFINE_GUID_ENUM(IID_IHXFileFormatObject, 0x00000F00, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXBroadcastFormatObject, 0x00000F01, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXFormatResponse, 0x00000F02, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXFormatReuse, 0xe55077c4, 0xa299, 0x11d7, 0x86, 0x4c, 0x0, 0x2, 0xb3, 0x65, 0x87, 0x20) +DEFINE_GUID_ENUM(IID_IHXPacketFormat, 0x00000F03, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXPacketTimeOffsetHandler, 0x00000F04, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXPacketTimeOffsetHandlerResponse, 0x00000F05, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXLiveFileFormatInfo, 0x00000F06, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +// $Private: +DEFINE_GUID_ENUM(IID_IHXPayloadFormatObject, 0x00000F07, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXBroadcastLatency, 0x00000F08, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXBlockFormatObject, 0x00000F09, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXFileFormatHeaderAdvise, 0x00000F0A, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXFileFormatHeaderAdviseResponse, 0x00000F0B, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +// $EndPrivate. + +DEFINE_GUID_ENUM(IID_IHXSyncFileFormatObject, 0x00000F0C, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) + +#endif + +#ifndef _HXFWRTR_H_ +// $Private: +DEFINE_GUID_ENUM(IID_IHXFileWriterMonitor, 0xb5615de1, 0x42a6, 0x11d5, 0xa9, 0xc, 0x0, 0x1, 0x2, 0x51, 0xb3, 0x40) +DEFINE_GUID_ENUM(IID_IHXPropertyAdviser, 0x264fd2f0, 0x432b, 0x11d5, 0xa9, 0xd, 0x0, 0x1, 0x2, 0x51, 0xb3, 0x40) +DEFINE_GUID_ENUM(IID_IHXFileWriter, 0xb5615de0, 0x42a6, 0x11d5, 0xa9, 0xc, 0x0, 0x1, 0x2, 0x51, 0xb3, 0x40) +// $EndPrivate. +#endif + +#ifndef _HXSRCIN_H_ +DEFINE_GUID_ENUM(IID_IHXSourceInput, 0xebf8d220, 0x40f7, 0x11d6, 0xab, 0x3f, 0x0, 0x1, 0x2, 0x51, 0xb3, 0x40) +DEFINE_GUID_ENUM(IID_IHXSourceHandler, 0xb2646da0, 0x410a, 0x11d6, 0xab, 0x3f, 0x0, 0x1, 0x2, 0x51, 0xb3, 0x40) +#endif + +/* + * File: + * hxpends.h + * Description: + * Interfaces related to get pending status from objects + * Interfaces: + * IHXPendingStatus: {00001100-0901-11d1-8B06-00A024406D59} + */ +#ifndef _HXPENDS_H_ +DEFINE_GUID_ENUM(IID_IHXPendingStatus, 0x00001100, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +#endif +/* + * File: + * ihxpckts.h + * Description: + * Interfaces related to buffers, packets, streams, etc. + * Interfaces: + * IID_IHXBuffer: {00001300-0901-11d1-8B06-00A024406D59} + * IID_IHXPacket: {00001301-0901-11d1-8B06-00A024406D59} + * IID_IHXRTPPacket {0169A731-1ED0-11d4-952B-00902742C923} + * IID_IHXRTPPacketInfo {0169A731-1ED0-11d4-952B-00902742C923} + * IID_IHXValues: {00001302-0901-11d1-8B06-00A024406D59} + * IID_IHXValuesRemove: {00001303-0901-11d1-8B06-00A024406D59} + * IID_IHXClientPacket: {00001304-0901-11d1-8B06-00A024406D59} + * IID_IHXBroadcastDistPktExt: {3B022922-94A1-4be5-BD25-216DA27BD8FC} + */ +#ifndef _IHXPCKTS_H_ +DEFINE_GUID_ENUM(IID_IHXBuffer, 0x00001300, 0x0901, 0x11d1, 0x8b, 0x06, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXPacket, 0x00001301, 0x0901, 0x11d1, 0x8b, 0x06, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXRTPPacket, 0x0169a731, 0x1ed0, 0x11d4, 0x95, 0x2b, 0x0, 0x90, 0x27, 0x42, 0xc9, 0x23) +DEFINE_GUID_ENUM(IID_IHXRTPPacketInfo, 0xec7d67bb, 0x2e79, 0x49c3, 0xb6, 0x67, 0xba, 0x8a, 0x93, 0x8d, 0xbc, 0xe0) +DEFINE_GUID_ENUM(IID_IHXValues, 0x00001302, 0x0901, 0x11d1, 0x8b, 0x06, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXValues2, +0x7ae64d81, 0xc5ab, 0x4b0a, 0x94, 0xf2, 0xb4, 0xd6, 0xdd, 0x2b, 0xda, 0x7a) +DEFINE_GUID_ENUM(IID_IHXValuesRemove, 0x00001303, 0x0901, 0x11d1, 0x8b, 0x06, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +// $Private: +DEFINE_GUID_ENUM(IID_IHXClientPacket, 0x00001304, 0x0901, 0x11d1, 0x8b, 0x06, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXBroadcastDistPktExt, 0x3b022922, 0x94a1, 0x4be5, 0xbd, 0x25, 0x21, 0x6d, 0xa2, 0x7b, 0xd8, 0xfc) +// $EndPrivate. +#endif +/* + * File: + * hxasm.h + * Description: + * Interfaces related to abm and back channel support. + * + * Interfaces: + * IID_IHXBackChannel: {00001500-0901-11d1-8B06-00A024406D59} + * IID_IHXASMSource: {00001501-0901-11d1-8B06-00A024406D59} + * IID_IHXASMStream: {00001502-0901-11d1-8B06-00A024406D59} + * IID_IHXASMStream2: {30d39e2c-1e56-11b2-8618-0002b39a94d0} + * IID_IHXASMStreamSink: {00001503-0901-11d1-8B06-00A024406D59} + */ +#ifndef _HXASM_H_ +DEFINE_GUID_ENUM(IID_IHXBackChannel, 0x00001500, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXASMSource, 0x00001501, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXASMStream, 0x00001502, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXASMStream2, 0x00001504, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXASMStreamSink, 0x00001503, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +#endif +/* + * File: + * hxencod.h + * Description: + * Interfaces related to superencoders. + * + * Interaces: + * IID_IHXEncoderResponse {00001600-0901-11d1-8B06-00A024406D59} + * IID_IHXEncoder {00001601-0901-11d1-8B06-00A024406D59} + * IID_IHXEncoderCompletion {00001602-0901-11d1-8B06-00A024406D59} + * IID_IHXConnectionlessControl + {00001603-0901-11d1-8B06-00A024406D59} + * IID_IHXEncoderResponseCompletion + {00001604-0901-11d1-8B06-00A024406D59} + * IID_IHXTransportControl {00001605-0901-11d1-8B06-00A024406D59} + */ +#ifndef _HXENCOD_H_ +DEFINE_GUID_ENUM(IID_IHXEncoderResponse, 0x00001600, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXEncoder, 0x00001601, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXEncoderCompletion, 0x00001602, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXConnectionlessControl, + 0x00001603, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXEncoderResponseCompletion, + 0x00001604, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXTransportControl, 0x00001605, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +#endif + +/* + * File: + * hxbrdcst.h + * Description: + * + * RealSystem iQ remote broadcast functionality + * + * Interfaces: + * + * IID_IHXRemoteBroadcastServices: {8F933081-27B6-11d5-9569-00902742E832} + * IID_IHXRemoteBroadcastConfiguration: {8F933083-27B6-11d5-9569-00902742E832} + * IID_IHXRemoteBroadcastConfigurationResponse: {67C1BA10-39BC-11d5-956A-00902742E832} + * IID_IHXRemoteBroadcastStatisticsReport: {CEE9CC1E-DCDF-4159-A315-8A3B86EE9A86} + */ + +#ifndef _HXBRDCST_H_ +DEFINE_GUID_ENUM(IID_IHXRemoteBroadcastServices, 0x8f933081, 0x27b6, 0x11d5, 0x95, 0x69, 0x0, 0x90, 0x27, 0x42, 0xe8, 0x32) +DEFINE_GUID_ENUM(IID_IHXRemoteBroadcastConfiguration, 0x8f933083, 0x27b6, 0x11d5, 0x95, 0x69, 0x0, 0x90, 0x27, 0x42, 0xe8, 0x32) +DEFINE_GUID_ENUM(IID_IHXRemoteBroadcastConfigurationResponse, 0x67c1ba10, 0x39bc, 0x11d5, 0x95, 0x6a, 0x0, 0x90, 0x27, 0x42, 0xe8, 0x32) +DEFINE_GUID_ENUM(IID_IHXRemoteBroadcastStatisticsReport, 0xcee9cc1e, 0xdcdf, 0x4159, 0xa3, 0x15, 0x8a, 0x3b, 0x86, 0xee, 0x9a, 0x86) +#endif + +/* + * File: + * hxauth.h + * Description: + * Password handling API + * Interfaces: + * IID_IHXPassword {00001700-0901-11d1-8B06-00A024406D59} + */ + +/* + * 000017**-0901-11d1-8B06-00A024406D59 is reserved for interfaces in hxauth.h (below) + */ + +/* + * File: + * hxauth.h + * Description: + * Authentication API + * Interfaces: + * IID_IHXAuthenticator {00001800-0901-11d1-8B06-00A024406D59} + * IID_IHXAuthenticatorResponse {00001801-0901-11d1-8B06-00A024406D59} + * IID_IHXAuthenticatorRequest {00001802-0901-11d1-8B06-00A024406D59} + * IID_IHXPassword {00001700-0901-11d1-8B06-00A024406D59} + * IID_IHXAuthenticationManager {00001A00-0901-11d1-8B06-00A024406D59} + * IID_IHXAuthenticationManager2 + * {34e171d2-a8f0-4832-bc7d-06dfe3ae58fd} + * IID_IHXAuthenticationManagerResponse + * {00001A01-0901-11d1-8B06-00A024406D59} + */ +#ifndef _HXAUTH_H_ +DEFINE_GUID_ENUM(IID_IHXAuthenticator, 0x00001800, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXAuthenticatorResponse, 0x00001801, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXAuthenticatorRequest, 0x00001802, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXPassword, 0x00001700, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXAuthenticationManager, 0x00001a00, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXAuthenticationManager2, 0x34e171d2, 0xa8f0, 0x4832, 0xbc, 0x7d, 0x06, 0xdf, 0xe3, 0xae, 0x58, 0xfd) +DEFINE_GUID_ENUM(IID_IHXAuthenticationManagerResponse, 0x00001a01, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +#endif +/* + * File: + * hxsdesc.h + * Description: + * Stream description API + * Interfaces: + * IID_IHXStreamDescription {00001900-0901-11d1-8B06-00A024406D59} + * IID_IHXRTPPayloadInfo {00001901-0901-11d1-8B06-00A024406D59} + * IID_IHXStreamDescriptionSettings {00001902-0901-11d1-8B06-00A024406D59} + */ +#ifndef _HXSDESC_ +DEFINE_GUID_ENUM(IID_IHXStreamDescription, 0x00001900, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXRTPPayloadInfo, 0x00001901, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXStreamDescriptionSettings, 0x00001902, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +#endif +/* + * 00001A**-0901-11d1-8B06-00A024406D59 is reserved for interfaces in hxauth.h (above) + */ + +/* + * File: + * hxlvtxt.h + * Description: + * Interfaces related to live text superencoder. + * + * Interaces: + * IID_IHXLiveText {00001b00-0901-11d1-8B06-00A024406D59} + * IID_IHXLiveText2 {00001b01-0901-11d1-8B06-00A024406D59} + */ +#ifndef _HXLVTXT_H_ +DEFINE_GUID_ENUM(IID_IHXLiveText, 0x00001b00, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXLiveText2, 0x00001b01, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +#endif +/* + * File: + * hxcfg.h + * Description: + * Interfaces used by server configuration tools. + * + * Interfaces: + * IID_IHXConfigFile {00001c00-0901-11d1-8B06-00A024406D59} + * IID_IHXRegConfig {00001c01-0901-11d1-8B06-00A024406D59} + * + */ +#ifndef _HXCFG_H_ +DEFINE_GUID_ENUM(IID_IHXConfigFile, 0x00001c00, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXRegConfig, 0x00001c01, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59) +#endif + +/* + * File: + * rmappv.h + * Description: + * Interfaces related to Pay Per View Database Plugins + * Interfaces: + * IID_IHXPPVDatabase {00001d00-0901-11d1-8B06-00A024406D59} + */ +DEFINE_GUID_ENUM(IID_IHXPPVDatabase, + 0x00001d00, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) + + +/* + * 00001e**-0901-11d1-8B06-00A024406D59 is reserved for an interface which + * has been deprecated. + */ + +/* + * File: + * rmacmenu.h + * Description: + * Interfaces used by renderers for context menus. + * + * Interfaces: + * IID_IHXContextMenu {00001f00-0901-11d1-8B06-00A024406D59} + * IID_IHXContextMenuResponse {00001f01-0901-11d1-8B06-00A024406D59} + */ +#ifndef _HXCMENU_H_ +DEFINE_GUID_ENUM(IID_IHXContextMenu, 0x00001f00, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXContextMenuResponse, 0x00001f01, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59) + +#endif +/* + * File: + * hxphook.h + * Description: + * Interfaces used by the top level client. client core and renderer to + * support Selective Record. + * + * Interfaces: + * IID_IHXPacketHook {00002000-0901-11d1-8B06-00A024406D59} + * IID_IHXPacketHookManager {00002001-0901-11d1-8B06-00A024406D59} + * IID_IHXPacketHookHelper {00002002-0901-11d1-8B06-00A024406D59} + * IID_IHXPacketHookHelperResponse {00002003-0901-11d1-8B06-00A024406D59} + */ + +#ifndef _HXPHOOK_H_ + +DEFINE_GUID_ENUM(IID_IHXPacketHook, 0x00002000, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXPacketHookManager, 0x00002001, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXPacketHookHelper, 0x00002002, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXPacketHookHelperResponse, 0x00002003, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) + +#endif + +/* + * File: + * hxpsink.h + * Description: + * Interfaces used by the top level client or renderers to determine + * that a player has been created or closed. + * + * Interfaces: + * IID_IHXPlayerCreationSink {00002100-0901-11d1-8B06-00A024406D59} + * IID_IHXPlayerSinkControl {00002101-0901-11d1-8B06-00A024406D59} + */ +DEFINE_GUID_ENUM(IID_IHXPlayerCreationSink, 0x00002100, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXPlayerSinkControl, 0x00002101, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) + +/* + * File: + * hxvsurf.h + * Description: + * Interface used by renderers to blt data to the screen (when in + * full screen mode). + * + * Interfaces: + * IID_IHXVideoSurface {00002200-0901-11d1-8B06-00A024406D59} + */ +DEFINE_GUID_ENUM(IID_IHXVideoSurface, 0x00002200, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXVideoHookSink, 0x00002201, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXVideoHook, 0x00002202, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXVideoSurface2, 0x00002203, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXRenderTimeLine, 0x00002204, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXSubRectVideoSurface, 0x00002205, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) + + +// $Private: +/* + * File: + * ihxfgbuf.h + * Description: + * Interfaces related to fragmented buffers + * Interfaces: + * IID_IHXFragmentedBuffer: {00002300-0901-11d1-8B06-00A024406D59} + * IID_IHXEnumFragmentedBuffer: {00002301-0901-11d1-8B06-00A024406D59} + */ +#ifndef _IHXFGBUF_H_ +DEFINE_GUID_ENUM(IID_IHXFragmentedBuffer, 0x00002300, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXEnumFragmentedBuffer, 0x00002301, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +#endif +// $EndPrivate. + +/* + * File: + * hxgroup.h + * Description: + * Client side Group related interfaces + * + * Interfaces: + * IID_IHXGroup {00002400-0901-11d1-8B06-00A024406D59} + * IID_IHXGroupManager {00002401-0901-11d1-8B06-00A024406D59} + * IID_IHXGroupSink {00002402-0901-11d1-8B06-00A024406D59} + * IID_IHXTrack {00002404-0901-11d1-8B06-00A024406D59} + */ +DEFINE_GUID_ENUM(IID_IHXGroup, 0x00002400, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXGroupManager, 0x00002401, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXGroupSink, 0x00002402, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +// $Private: +DEFINE_GUID_ENUM(IID_IHXGroup2, 0x00002403, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +// $EndPrivate. +DEFINE_GUID_ENUM(IID_IHXTrack, 0x00002404, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXTrackSink, 0x00002405, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXPrefetchSink, 0x00002406, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXGroupSink2, 0x00002407, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXPrefetch, 0x00002408, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) + +/* + * File: + * hxupgrd.h + * Description: + * Interfaces used by player for auto-upgrade. + * + * Interfaces: + * IID_IHXUpgradeCollection {00002500-0901-11d1-8B06-00A024406D59} + * IID_IHXUpgradeHandler {00002501-0901-11d1-8B06-00A024406D59} +// $Private: + * IID_IHXSystemRequired {00002502-0901-11d1-8B06-00A024406D59} +// $EndPrivate. + */ +DEFINE_GUID_ENUM(IID_IHXUpgradeCollection, + 0x00002500, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXUpgradeHandler, + 0x00002501, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +// $Private: +DEFINE_GUID_ENUM(IID_IHXSystemRequired, + 0x00002502, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +// $EndPrivate. + +DEFINE_GUID_ENUM(IID_IHXUpgradeCollection2, + 0x00002503, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) + +/* + * File: + * hxallow.h + * Description: + * Interfaces related to Allowance plugins + * Interfaces: + * IID_IHXPlayerConnectionAdviseSink {00002600-0901-11d1-8B06-00A024406D59} + * IID_IHXPlayerConnectionResponse {00002601-0901-11d1-8B06-00A024406D59} + * IID_IHXPlayerController {00002602-0901-11d1-8B06-00A024406D59} + * IID_IHXPlayerConnectionAdviseSinkManager + {00002603-0901-11d1-8B06-00A024406D59} + * IID_IHXPlayerConnectionAdviseEvents + {8fe78da6-a828-11d7-939c-00601df0ce4c} + * IID_IHXProxyConnectionAdviseSink {00002604-0901-11d1-8B06-00A024406D59} + * IID_IHXProxyConnectionResponse {00002605-0901-11d1-8B06-00A024406D59} + * IID_IHXProxyController {00002605-0901-11d1-8B06-00A024406D59} + * IID_IHXPlayerControllerProxyRedirect {00002607-0901-11d1-8B06-00A024406D59} + * IID_IHXMidBoxNotify {f8c5dcaf-9a5f-4d1b-a061-22fa0d038848} + */ +DEFINE_GUID_ENUM(IID_IHXPlayerConnectionAdviseSink, + 0x00002600, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXPlayerConnectionResponse, + 0x00002601, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXPlayerController, + 0x00002602, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXPlayerConnectionAdviseSinkManager, + 0x00002603, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXPlayerConnectionAdviseEvents, + 0x8fe78da6, 0xa828, 0x11d7, 0x93, 0x9c, 0x0, 0x60, 0x1d, 0xf0, 0xce, 0x4c) + +DEFINE_GUID_ENUM(IID_IHXProxyConnectionAdviseSink, + 0x00002604, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXProxyConnectionResponse, + 0x00002605, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXProxyController, + 0x00002606, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXPlayerControllerProxyRedirect, 0x00002607, 0x901, 0x11d1, + 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXMidBoxNotify, 0xf8c5dcaf, 0x9a5f, 0x4d1b, + 0xa0, 0x61, 0x22, 0xfa, 0x0d, 0x03, 0x88, 0x48) + +/* + * File: + * rmaaconf.h + * Description: + * Interfaces used by the top level client. client core to + * support Auto. Transport Configuration + * + * Interfaces: + * IID_IHXAutoConfig {00002700-0901-11d1-8B06-00A024406D59} + * IID_IHXAutoConfigResponse {00002701-0901-11d1-8B06-00A024406D59} + */ + +DEFINE_GUID_ENUM(IID_IHXAutoConfig, 0x00002700, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXAutoConfigResponse, 0x00002701, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) + +/* + * File: + * hxauthn.h + * Description: + * Interfaces used to validate a users access to content. + * + * Interfaces: + * IID_IHXCredRequestResponse, {00002800-0901-11d1-8B06-00A024406D59} + * IID_IHXCredRequest, {00002801-0901-11d1-8B06-00A024406D59} + * IID_IHXClientAuthResponse, {00002802-0901-11d1-8B06-00A024406D59} + * IID_IHXClientAuthConversation, {00002803-0901-11d1-8B06-00A024406D59} + * IID_IHXServerAuthResponse, {00002804-0901-11d1-8B06-00A024406D59} + * IID_IHXServerAuthConversation, {00002805-0901-11d1-8B06-00A024406D59} + * IID_IHXUserContext, {00002806-0901-11d1-8B06-00A024406D59} + * IID_IHXUserProperties, {00002807-0901-11d1-8B06-00A024406D59} + * IID_IHXUserImpersonation, {00002808-0901-11d1-8B06-00A024406D59} + * IID_IHXUserDB, {00002809-0901-11d1-8B06-00A024406D59} + * IID_IHXChallengeResponse, {0000280A-0901-11d1-8B06-00A024406D59} + * IID_IHXChallenge, {0000280B-0901-11d1-8B06-00A024406D59} + */ + +DEFINE_GUID_ENUM(IID_IHXCredRequestResponse, 0x00002800, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXCredRequest, 0x00002801, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXClientAuthResponse, 0x00002802, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXClientAuthConversation, 0x00002803, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXServerAuthResponse, 0x00002804, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXServerAuthConversation, 0x00002805, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXUserContext, 0x00002806, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXUserProperties, 0x00002807, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXUserImpersonation, 0x00002808, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXChallengeResponse, 0x00002809, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXChallenge, 0x0000280A, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) + + +/* + * File: + * hxplgns.h + * + * Description: + * Interfaces for Plugins: + * IHXObjectConfiguration - Consistant configuration. + * IHXPluginProperties - Consistant property retrival. + * + * Interfaces: + * IID_IHXObjectConfiguration: {00002900-0901-11d1-8B06-00A024406D59} + * IID_IHXPluginProperties: {00002901-0901-11d1-8B06-00A024406D59} + */ +DEFINE_GUID_ENUM(IID_IHXObjectConfiguration, 0x00002900, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXPluginProperties, 0x00002901, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) + +/* + * File: + * hxdb.h + * + * Description: + * Interfaces for Plugins: + * IHXDatabaseManager - Creates Configured Database Instances + * IHXAuthenticationDBManagerResponse - Provides Callbacks for IHXAuthenticationDBManager + * IHXAuthenticationDBManager - Functions to add and remove users from a database + * IHXAsyncEnumAuthenticationDBResponse - Provides Callbacks for IHXAsyncEnumAuthenticationDB + * IHXAsyncEnumAuthenticationDB - Functions to enumerate the list of users in a database + * IHXAuthenticationDBAccessResponse - Provides Callbacks for IHXAuthenticationDBAccess + * IHXAuthenticationDBAccess - Functions to access a users info in the database + * IHXGUIDDBManagerResponse - Provides Callbacks for IHXGUIDDBManager + * IHXGUIDDBManager - Functions to add and remove GUID's from a database + * IHXPPVDBManagerResponse - Provides Callbacks for IHXPPVDBManager + * IHXPPVDBManager - Functions to add, remove, and adjust a user's permissions from a database + * IHXRedirectDBManagerResponse - Provides Callbacks for IHXRedirectDBManager + * IHXRedirectDBManager - Functions to add and remove URL Redirects from a database + * IHXRegistrationLoggerResponse - Provides Callbacks for IHXRegistrationLogger + * IHXRegistrationLogger - Functions to Log registration Activity. + * + * Interfaces: + * IID_IHXDatabaseManager: {00002A00-0901-11d1-8B06-00A024406D59} + * IID_IHXAuthenticationDBManagerResponse: {00002A01-0901-11d1-8B06-00A024406D59} + * IID_IHXAuthenticationDBManager: {00002A02-0901-11d1-8B06-00A024406D59} + * IID_IHXAsyncEnumAuthenticationDBResponse:{00002A03-0901-11d1-8B06-00A024406D59} + * IID_IHXAsyncEnumAuthenticationDB: {00002A04-0901-11d1-8B06-00A024406D59} + * IID_IHXAuthenticationDBAccessResponse: {00002A05-0901-11d1-8B06-00A024406D59} + * IID_IHXAuthenticationDBAccess: {00002A06-0901-11d1-8B06-00A024406D59} + * IID_IHXGUIDDBManagerResponse: {00002A07-0901-11d1-8B06-00A024406D59} + * IID_IHXGUIDDBManager: {00002A08-0901-11d1-8B06-00A024406D59} + * IID_IHXPPVDBManagerResponse: {00002A09-0901-11d1-8B06-00A024406D59} + * IID_IHXPPVDBManager: {00002A0A-0901-11d1-8B06-00A024406D59} + * IID_IHXRedirectDBManagerResponse: {00002A0B-0901-11d1-8B06-00A024406D59} + * IID_IHXRedirectDBManager: {00002A0C-0901-11d1-8B06-00A024406D59} + * IID_IHXRegistrationLoggerResponse: {00002A0D-0901-11d1-8B06-00A024406D59} + * IID_IHXRegistrationLogger: {00002A0E-0901-11d1-8B06-00A024406D59} + */ +DEFINE_GUID_ENUM(IID_IHXDatabaseManager, 0x00002A00, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXAuthenticationDBManagerResponse, 0x00002A01, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXAuthenticationDBManager, 0x00002A02, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXAsyncEnumAuthenticationDBResponse, 0x00002A03, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXAsyncEnumAuthenticationDB, 0x00002A04, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXAuthenticationDBAccessResponse, 0x00002A05, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXAuthenticationDBAccess, 0x00002A06, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXGUIDDBManagerResponse, 0x00002A07, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXGUIDDBManager, 0x00002A08, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXPPVDBManagerResponse, 0x00002A09, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXPPVDBManager, 0x00002A0A, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXRedirectDBManagerResponse, 0x00002A0B, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXRedirectDBManager, 0x00002A0C, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXRegistrationLoggerResponse, 0x00002A0D, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXRegistrationLogger, 0x00002A0E, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) + +// $Private: +/* + * File: + * ihxostrm.h + * Description: + */ + +DEFINE_GUID_ENUM(IID_IHXStreamableObj, 0x47533471, 0xbece, 0x11d1, 0x8f, 0x9, 0x0, 0x60, 0x8, 0x3b, 0xe5, 0x61) +DEFINE_GUID_ENUM(IID_IHXObjOutStream, 0xb3a156d1, 0xbedd, 0x11d1, 0x8f, 0xa, 0x0, 0x60, 0x8, 0x3b, 0xe5, 0x61) +DEFINE_GUID_ENUM(IID_IHXObjInStream, 0xb3a156d2, 0xbedd, 0x11d1, 0x8f, 0xa, 0x0, 0x60, 0x8, 0x3b, 0xe5, 0x61) +// $EndPrivate. + +/* + * File: + * hxxmltg.h + * + * Description: + * Interfaces for Plugins: + * IHXXMLTagHandler: Interface for registering for a specific tag + * and providing an IHXXMLTagObject to tagfsys. + * (Works like IHXFileSystemObject) + * + * IHXXMLTagObject: Interface for receiving the contents of a tag + * for which the creating IHXXMLTagHandler has registerd. + * + * IHXXMLTagObjectResponse: Interface for IHXXMLTagObject to return + * the replacement for the tag. This is implemented by tagfsys. + * + * + * Interfaces: + * IID_IHXXMLTagObjectResponse: {00002C02-0901-11d1-8B06-00A024406D59} + * IID_IHXXMLTagHandler: {00002C03-0901-11d1-8B06-00A024406D59} + * IID_IHXXMLTagObject: {00002C04-0901-11d1-8B06-00A024406D59} + */ +#ifndef _HXXMLTG_H +DEFINE_GUID_ENUM(IID_IHXXMLTagObjectResponse, 0x00002C02, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXXMLTagHandler, 0x00002C03, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXXMLTagObject, 0x00002C04, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +#endif +// $Private: +/* + * File: + * archplin/flcreatr.h + */ +DEFINE_GUID_ENUM(IID_IHXFileCreatorResponse, 0x00002D00, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +// $EndPrivate. + +// $Private: +/* + * File: + * hxfiles.h + */ +#ifndef _HXFILES_H_ +DEFINE_GUID_ENUM(IID_IHXHTTPRedirect, 0x21eae0b9, 0x2e0c, 0x4003, 0xbb, 0x79, 0xbc, 0x8c, 0xc5, 0x67, 0x2c, 0x2d) +DEFINE_GUID_ENUM(IID_IHXHTTPRedirectResponse, 0x125a63b1, 0x669c, 0x42f9, 0xb1, 0xf9, 0x1b, 0x53, 0xe9, 0x95, 0x82, 0x95) +DEFINE_GUID_ENUM(IID_IHXRM2Converter2, 0x00002F00, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXRM2Converter2Response, 0x00002F01, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXPoolPathAdjustment, 0x00002F02, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +#endif +// $EndPrivate. + +/* + * File: + * hxcache.h + * + * Description: + * Interfaces for caching services: + * IHXCache - Creates IHXCacheFiles + * IHXCacheResponse - Response object for IHXCache + * IHXCacheFile - Persistant store object for caching + * IHXCacheFileResponse - Response object for IHXCacheFile + * +// $Private: +// cdist interfaces private for development, expect to make public when finalized. + * IHXContentDistribution - Content distribution services + * IHXContentDistributionResponse - Content distribution service replies + * IHXContentDistributionAdvise - advise sink for cdist + * IHXContentDistributionAdviseResponse - cdist advise sink responses + * IHXMIIPullEntire - MII will pull the entire file + * from the next read. + * IHXMIIPullEntireResponse + * IHXCacheFileSetVersion - Get specific version from cache. + * IHXMIIReadStatCollection - Collect read data for MII +// $EndPrivate. + * + * Interfaces: + * IID_IHXCache: {00002E00-0901-11d1-8B06-00A024406D59} + * IID_IHXCacheResponse: {00002E01-0901-11d1-8B06-00A024406D59} + * IID_IHXCacheFile: {00002E02-0901-11d1-8B06-00A024406D59} + * IID_IHXCacheFileResponse: {00002E03-0901-11d1-8B06-00A024406D59} + * IID_IHXMIIFetch: {00002E04-0901-11d1-8B06-00A024406D59} + * IID_IHXFIFOCache: {00002E05-0901-11d1-8B06-00A024406D59} +// $Private: + * IID_IHXContentDistribution {00002E06-0901-11d1-8B06-00A024406D59} + * IID_IHXContentDistributionResponse {00002E07-0901-11d1-8B06-00A024406D59} + * IID_IHXContentDistributionAdvise {00002E08-0901-11d1-8B06-00A024406D59} + * IID_IHXContentDistributionAdviseResponse {00002E09-0901-11d1-8B06-00A024406D59} + * IID_IHXMIIPullEntire {00002E0A-0901-11d1-8B06-00A024406D59} + * IID_IHXMIIPullEntireResponse {00002E0B-0901-11d1-8B06-00A024406D59} + * IID_IHXCacheFileSetVersion {00002E0C-0901-11d1-8B06-00A024406D59} + * IID_IHXMIIReadStatCollection {00002E0D-0901-11d1-8B06-00A024406D59} +// $EndPrivate. + */ + +DEFINE_GUID_ENUM(IID_IHXCache, 0x00002E00, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXCacheResponse, 0x00002E01, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXCacheFile, 0x00002E02, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXCacheFileResponse, 0x00002E03, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXMIIFetch, 0x00002E04, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXFIFOCache, 0x00002E05, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +// $Private: +DEFINE_GUID_ENUM(IID_IHXContentDistribution, 0x00002E06, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXContentDistributionResponse,0x00002E07, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXContentDistributionAdvise, 0x00002E08, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXContentDistributionAdviseResponse, + 0x00002E09, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXMIIPullEntire, 0x00002E0A, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXMIIPullEntireResponse, 0x00002E0B, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXCacheFileSetVersion, 0x00002E0C, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXMIIReadStatCollection, 0x00002E0D, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +// $EndPrivate. + +/* + * File: intrpm.h + * + * IID_IHXInterPluginMessenger: {00003000-0901-11d1-8B06-00A024406D59} + */ +DEFINE_GUID_ENUM(IID_IHXInterPluginMessenger, 0x00003000, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) + +/* + * File: intrpm.h + * + * IID_IHXInterPluginMessenger2: {00003001-0901-11d1-8B06-00A024406D59} + */ +DEFINE_GUID_ENUM(IID_IHXInterPluginMessenger2, 0x00003001, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) + +/* + * File: hxvalue.h + * + * DEPRECATED: {00003100-0901-11d1-8B06-00A024406D59} + * DEPRECATED: {00003101-0901-11d1-8B06-00A024406D59} + * DEPRECATED: {00003102-0901-11d1-8B06-00A024406D59} + * DEPRECATED: {00003103-0901-11d1-8B06-00A024406D59} + * DEPRECATED: {00003104-0901-11d1-8B06-00A024406D59} + * DEPRECATED: {00003105-0901-11d1-8B06-00A024406D59} + * DEPRECATED: {00003106-0901-11d1-8B06-00A024406D59} + * DEPRECATED: {00003107-0901-11d1-8B06-00A024406D59} + * + * IID_IHXKeyValueList: {00003108-0901-11d1-8B06-00A024406D59} + * IID_IHXKeyValueListIter: {00003109-0901-11d1-8B06-00A024406D59} + * IID_IHXKeyValueListIterOneKey: {00003110-0901-11d1-8B06-00A024406D59} + * IID_IHXOptions: {00003111-0901-11d1-8B06-00A024406D59} + */ +#ifndef _HXVALUE_H_ +/* DEPRECATED 3100 - 3107 */ +DEFINE_GUID_ENUM(IID_IHXKeyValueList, 0x00003108, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXKeyValueListIter, 0x00003109, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXKeyValueListIterOneKey, 0x00003110, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXOptions, 0x00003111, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +#endif + +/* + * File: + * ihxcookies.h + * + * Description: + * Interfaces for Plugins: + * IHXCookies - Cookie database management APIs + * IHXCookiesHelper - Cookie output helper APIs + * + * Interfaces: + * IID_IHXCookies: {00003200-0901-11d1-8B06-00A024406D59} + * IID_IHXCookiesHelper: {00003201-0901-11d1-8B06-00A024406D59} + */ +DEFINE_GUID_ENUM(IID_IHXCookies, 0x00003200, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXCookiesHelper, 0x00003201, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) + +/* + * File: addrpool.h + * + * IID_IHXMulticastAddressPool: {00003300-0901-11d1-8B06-00A024406D59} + */ +DEFINE_GUID_ENUM(IID_IHXMulticastAddressPool, 0x00003300, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) + +/* + * File: sapmgr.h + * + * IID_IHXSapManager: {00003400-0901-11d1-8B06-00A024406D59} + */ +DEFINE_GUID_ENUM(IID_IHXSapManager, 0x00003400, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) + +/* + * File: namedlock.h + * + * IID_IHXNamedLock: {A5814CDC-232A-448D-8204-98E4D66445B5} + */ +DEFINE_GUID_ENUM(IID_IHXNamedLock, 0xa5814cdc, 0x232a, 0x448d, 0x82, 0x4, + 0x98, 0xe4, 0xd6, 0x64, 0x45, 0xb5) + + +/* + * File: + * hxvsrc.h + * + * Description: + * Interfaces for Plugins: + * IHXFileViewSource - Interface so file formats can support view source. + * IHXFileViewSourceResponse - Response interface. + * + * Interfaces: + * IID_IHXFileViewSource: {00003500-0901-11d1-8B06-00A024406D59} + * IID_IHXFileViewSourceResponse: {00003501-0901-11d1-8B06-00A024406D59} +// $Private: + * IID_IHXClientViewSource: {00003502-0901-11d1-8B06-00A024406D59} + * IID_IHXClientViewSourceSink: {00003503-0901-11d1-8B06-00A024406D59} +// $EndPrivate. + * IID_IHXViewSourceCommand: {00003504-0901-11d1-8B06-00A024406D59} + * IID_IHXViewSourceURLResponse {00003505-0901-11d1-8B06-00A024406D59} +// $Private: + * IID_IHXClientViewRights: {00003506-0901-11d1-8B06-00A024406D59} +// $EndPrivate. + */ +#ifndef _HXVSRC_H_ +DEFINE_GUID_ENUM(IID_IHXFileViewSource, 0x00003500, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXFileViewSourceResponse, 0x00003501, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +// $Private: +DEFINE_GUID_ENUM(IID_IHXClientViewSource, 0x00003502, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXClientViewSourceSink, 0x00003503, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +// $EndPrivate: +DEFINE_GUID_ENUM(IID_IHXViewSourceCommand, 0x00003504, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXViewSourceURLResponse, 0x00003505, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) + +// $Private: +DEFINE_GUID_ENUM(IID_IHXClientViewRights, 0x00003506, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +// $EndPrivate: + +#endif + +/* + * File: + * hxdllaccess.h + * Description: + * Interfaces to Dll Access Service + * Interfaces: + * IID_IHXDllAccess: {D5A71AA1-A6ED-479f-9FC6-F06B99142691} + */ +#ifndef _HXDLLACCESS_H_ +DEFINE_GUID_ENUM(IID_IHXDllAccess, 0xd5a71aa1, 0xa6ed, 0x479f, 0x9f, 0xc6, 0xf0, 0x6b, 0x99, 0x14, 0x26, 0x91) +#endif // _HXDLLACCESS_H_ + +// $Private: +/* + * File: + * hxpxymgr.h + * + * Description: + * + * IHXProxyManager - proxy APIs + * + * Interfaces: + * IID_IHXProxyManager: {00003600-0901-11d1-8B06-00A024406D59} + */ +DEFINE_GUID_ENUM(IID_IHXProxyManager, 0x00003600, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +// $EndPrivate. + +/* File: + * hxdtcvt.h + * + * Description + * IHXDataConvertSystemObject - RMA Stream data conversion creator + * IHXDataConvert - RMA Stream data conversion + * IHXDataConvertResponse - response for above + * IHXDataRevert - RMA Stream data reversion + * IHXDataRevertResponse - response for above + */ +DEFINE_GUID_ENUM(IID_IHXDataConvertSystemObject, + 0x00003900, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXDataConvert, + 0x00003901, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXDataConvertResponse, + 0x00003902, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXDataRevert, + 0x00003903, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXDataRevertResponse, + 0x00003904, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) + +/* File: + * hxvport.h + * + * Description + * IHXViewPortManager - RMA ViewportManager + * IHXViewPort - RMA Viewport + * IHXViewPortSink - RMA Viewport sink + * IHXViewPortSupplier - RMA Viewport supplier + */ +DEFINE_GUID_ENUM(IID_IHXViewPortManager, + 0x00004000, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXViewPort, + 0x00004001, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXViewPortSink, + 0x00004002, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXViewPortSupplier, + 0x00004003, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) + +/* File: + * hxmmrkr.h + * + * Description + * IHXMediaMarkerManager - RMA Media Marker Manager + * IHXMediaMarkerSink - RMA Media Marker Sink + */ +DEFINE_GUID_ENUM(IID_IHXMediaMarkerManager, + 0x46679d62, 0xf7ac, 0x4b0e, 0x80, 0x0, 0xf4, 0xee, 0x90, 0xf7, 0x85, 0xb1) +DEFINE_GUID_ENUM(IID_IHXMediaMarkerSink, + 0xa4643c85, 0x5b52, 0x4b07, 0xa7, 0x61, 0x32, 0xcf, 0xb2, 0xf2, 0x84, 0xfe) + +/* + * File: + * hxinter.h + * + * Description + * IHXEventManager - RMA Inter-datatype event manager + * IHXEventSink - RMA Inter-datatype event sink + * + */ +DEFINE_GUID_ENUM(IID_IHXEventManager, + 0xf932b582, 0x517, 0x4ca4, 0x84, 0x4c, 0x26, 0xa4, 0xe8, 0xe9, 0x69, 0x83) +DEFINE_GUID_ENUM(IID_IHXEventSink, + 0x76cf54bc, 0x9fce, 0x45e7, 0x90, 0xd1, 0x3, 0x46, 0x5, 0xf8, 0xdd, 0x14) + +/* + * File: + * hxslta.h + * + * Description: + * + * IHXSLTA - RMA version of slta. Simulates a live stream from file format. + * + * IHXSltaEvent - Allows events to be sent in an SLTA stream + */ +DEFINE_GUID_ENUM(IID_IHXSLTA, + 0x00000D00, 0xb4c8, 0x11d0, 0x99, 0x95, 0x0, 0xa0, 0x24, 0x8d, 0xa5, 0xf0) + +DEFINE_GUID_ENUM(IID_IHXSltaEvent, + 0x00000D01, 0xb4c8, 0x11d0, 0x99, 0x95, 0x0, 0xa0, 0x24, 0x8d, 0xa5, 0xf0) + + + +/* + * File: + * ihxcookies2.h + * + * Description: + * + * IHXCookies2 - Lets you get expired cookies + */ +#ifndef _ihxcookies2_h_ +DEFINE_GUID_ENUM(IID_IHXCookies2, 0x5634537f, 0x162f, 0x41b0, 0x93, 0xfc, 0x6e, 0xf, 0x77, 0x10, 0x71, 0x81) +#endif + + +/* + * File: + * rmasm2sm.h + * + * Description: + * + * IHXSmilToSmilRendererCommunicator - Lets you communicate from SMIL 1.0 + * renderer to SMIL 2.0 renderer that SMIL 2 stream is being proxied. + */ +#ifndef _HXSM2SM_H_ +// $Private: +DEFINE_GUID_ENUM(IID_IHXSmilToSmilRendererCommunicator, + 0x00004300, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +// $EndPrivate. +#endif + +/* + * File: + * hxproductversion.h + * + * Description: + * + * Provide version information regarding the executable core. + */ +DEFINE_GUID_ENUM(IID_IHXProductVersion, 0xA0991C6E, 0xD51B, 0x4549, + 0xA0, 0xD7, 0xBE, 0xD5, 0xED, 0x18, 0xF1, 0xC7) + + +/* + * File: + * progsink.h + * + * Description: + * + * Enables subscription for notification on the progress of merging files. + */ +#ifndef _PROGSINK_H_ + +DEFINE_GUID_ENUM(IID_IHXProgressSink, + 0x6f8c5fb0, 0xc1d3, 0x11d2, 0x87, 0x1b, 0x0, 0xc0, 0xf0, 0x31, 0xc2, 0x66) + +DEFINE_GUID_ENUM(IID_IHXProgressSinkControl, + 0xdc464800, 0xc1d3, 0x11d2, 0x87, 0x1b, 0x0, 0xc0, 0xf0, 0x31, 0xc2, 0x66) + +#endif + +/* + * File: + * rmflsnk.h + * + * Description: + * + * Enables receipt of header and packets from the rmeditor, prior to writing to file. + */ + +#ifndef _RMFLSNK_H_ + +DEFINE_GUID_ENUM(IID_IHXRMFileSink, + 0x19137680, 0x377b, 0x11d2, 0xa1, 0xc4, 0x0, 0x60, 0x8, 0x3b, 0xe5, 0x63) + +DEFINE_GUID_ENUM(IID_IHXRMFileSinkControl, + 0xcb88b91, 0xa444, 0x11d2, 0x87, 0x92, 0x0, 0xc0, 0xf0, 0x31, 0x93, 0x8b) + +#endif + +/* + * File: + * hxqos.h + * + * Description: + * + * Server QoS subsystem profile selection, and QoS Signalling mechanism + */ +#ifndef _HX_QOS_H_ + +DEFINE_GUID_ENUM(IID_IHXQoSProfileSelector, + 0x75db043b, 0xc5a8, 0x49b2, 0x8d, 0x3f, 0x8c, 0xf9, 0x9f, 0x9e, 0x64, 0x44) + +DEFINE_GUID_ENUM(IID_IHXQoSSignalSourceResponse, + 0xb6154b09, 0xbbc3, 0x4239, 0xbe, 0x8b, 0x81, 0x60, 0x7c, 0xa0, 0xbe, 0x9) + +DEFINE_GUID_ENUM(IID_IHXQoSSignalSource, + 0x42aeddae, 0x3c4a, 0x498c, 0x86, 0x3e, 0xe6, 0xed, 0xee, 0xd4, 0x2, 0xa5) + +DEFINE_GUID_ENUM(IID_IHXQoSSignalSink, + 0x8b94c9cf, 0x48e2, 0x4384, 0xbc, 0x39, 0x70, 0x1b, 0x92, 0x4f, 0x55, 0x6f) + +DEFINE_GUID_ENUM(IID_IHXQoSSignalBus, + 0x8003507e, 0x453f, 0x4439, 0xbf, 0x8, 0x7f, 0x8a, 0xe, 0x8, 0x3d, 0x9e) + +DEFINE_GUID_ENUM(IID_IHXQoSSignal, + 0x32126bdc, 0x74, 0x4f43, 0x8d, 0x2c, 0x65, 0xd7, 0x6d, 0x60, 0xb5, 0xcb) + +DEFINE_GUID_ENUM(IID_IHXQoSClassFactory, + 0xc1316a78, 0x2960, 0x4f5c, 0xa2, 0xa9, 0x46, 0xd9, 0xc9, 0x25, 0xe8, 0x8f) + +DEFINE_GUID_ENUM(IID_IHXQoSCongestionControl, + 0x99c0a316, 0xfbbc, 0x41a9, 0x96, 0x28, 0xee, 0xcc, 0xcc, 0xf2, 0x20, 0x3f) + +DEFINE_GUID_ENUM(IID_IHXQoSCongestionEquation, + 0xce97019f, 0x6a3d, 0x4c26, 0x81, 0x55, 0xc8, 0xae, 0xba, 0xdc, 0x77, 0x62) + +DEFINE_GUID_ENUM(IID_IHXQoSDiffServConfigurator, + 0x5fb79e3a, 0x9bef, 0x4676, 0x89, 0x77, 0x8, 0xff, 0x6b, 0x2d, 0x72, 0x70) + +DEFINE_GUID_ENUM(IID_IHXQoSProfileConfigurator, + 0x75db043b, 0xc5a8, 0x49b2, 0x8d, 0x3f, 0x8c, 0xf9, 0x9f, 0x9e, 0x64, 0x48) + +DEFINE_GUID_ENUM(IID_IHXQoSRateShapeAggregator, + 0x18496bea, 0xf2a7, 0x45fc, 0xb3, 0x12, 0x88, 0x52, 0x14, 0x11, 0x9a, 0xb2) + +DEFINE_GUID_ENUM(IID_IHXQoSRateShaper, + 0x2a287694, 0xef37, 0x4266, 0x85, 0x8f, 0xe8, 0x4f, 0xe3, 0x6a, 0xbd, 0x90) + +#endif /* _HX_QOS_H_ */ + +#ifndef _HXQOSSESS_H + +DEFINE_GUID_ENUM(IID_IHXQoSClientBufferVerifier, + 0x51d547a0, 0xf019, 0x49e8, 0xb4, 0x98, 0x50, 0xdf, 0x22, 0xf7, 0xeb, 0x6b) + +DEFINE_GUID_ENUM(IID_IHXQoSRateManager, + 0x8fce6b44, 0x6067, 0x4060, 0x86, 0x75, 0xd8, 0xb5, 0x6d, 0x5, 0x95, 0x92) + +DEFINE_GUID_ENUM(IID_IHXClientBufferInfo, + 0xa7a1ab2, 0x6e3e, 0x4ed4, 0x80, 0xf, 0x97, 0xd5, 0x4e, 0x67, 0xee, 0x3c) +#endif + +#ifndef _HXQOSBUFFERUPDATE_H_ +DEFINE_GUID_ENUM(IID_IHXBufferDepthUpdate, + 0xbdeaf826, 0x1cd5, 0x4200, 0xb1, 0xb2, 0x48, 0x53, 0x9d, 0xdf, 0xd4, 0x58) +#endif + +#ifndef _HXPCKTFLWCTRL_H_ +DEFINE_GUID_ENUM(IID_IHXPacketFlowControl, + 0x00000130, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) + +#endif + +#ifndef _HX_STREAMADAPT_H +DEFINE_GUID_ENUM(IID_IHXStreamAdaptationSetup, + 0xad4ebf5, 0xf125, 0x42ca, 0xb4, 0x77, 0xb4, 0xae, 0x82, 0x8a, 0xc5, 0xf) + +DEFINE_GUID_ENUM(IID_IHXQoSLinkCharSetup, + 0x1a668eb, 0xdc9e, 0x414f, 0xa8, 0x68, 0xc9, 0x48, 0x9, 0xa1, 0x3a, 0x6b) + +DEFINE_GUID_ENUM(IID_IHXQoSRateMgrClassFactory, + 0xcc46e2f4, 0xf985, 0x49b7, 0x9b, 0x7f, 0x83, 0x44, 0x72, 0xba, 0x0, 0xe2) +#endif +/* + * Server-specific IID's required for capability exchange + */ + +#ifndef _HX_CLIENT_PROFILE_H_ +DEFINE_GUID_ENUM(IID_IHXClientProfileManager, + 0x00004400, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) + +DEFINE_GUID_ENUM(IID_IHXClientProfileManagerResponse, + 0x00004401, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) + +DEFINE_GUID_ENUM(IID_IHXClientProfileInfo, + 0x00004402, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) + +DEFINE_GUID_ENUM(IID_IHXClientProfile, + 0x00004403, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) + +DEFINE_GUID_ENUM(IID_IHXCPAttribute, + 0x00004404, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +#endif + +#ifndef _HX_PSS_PROFILE_H +DEFINE_GUID_ENUM(IID_IHXPSSProfileData, + 0x00004410, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) + +DEFINE_GUID_ENUM(IID_IHXPSSPTAgent, + 0x00004411, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) + +DEFINE_GUID_ENUM(IID_IHXPSSPTAgentResponse, + 0x00004412, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +#endif + +#ifndef _HXRDFPARSER_H_ +DEFINE_GUID_ENUM(IID_IHXRDFParser, + 0x00004420, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) + +DEFINE_GUID_ENUM(IID_IHXRDFGraph, + 0x00004421, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) + +DEFINE_GUID_ENUM(IID_IHXRDFGraphNode, + 0x00004422, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) + +DEFINE_GUID_ENUM(IID_IHXRDFGraphEdge, + 0x00004423, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +#endif + +#ifndef _HXPROFILECACHE_H_ +DEFINE_GUID_ENUM(IID_IHXProfileCache, + 0x00004430, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +#endif + +#ifndef _HXPRDNLD_H_ +DEFINE_GUID_ENUM(IID_IHXPDStatusMgr, + 0x00004500, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXPDStatusObserver, + 0x00004501, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXMediaBytesToMediaDur, + 0x00004510, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +#endif /* _HXPRDNLD_H_ */ + +#ifndef _ISIFS_H_ +/* file server/engine/inputsource/pub/isifs.h */ +DEFINE_GUID_ENUM(IID_IHXRateDescription, + 0x4b5b28f6, 0xf6ff, 0x4f60, 0xad, 0x66, 0xac, 0x6d, 0xe9, 0xe7, 0x5c, 0xdf) +DEFINE_GUID_ENUM(IID_IHXRateDescManager, + 0x8467996c, 0x9097, 0x4721, 0x9d, 0x4d, 0xd8, 0xa6, 0xd4, 0xbb, 0x03, 0x03) +DEFINE_GUID_ENUM(IID_IHXRateDescResponse, + 0x1e8e5cb1, 0x36ee, 0x4209, 0x9d, 0xcc, 0x4b, 0x66, 0x1f, 0x7b, 0xaa, 0x59) +DEFINE_GUID_ENUM(IID_IHXUberStreamManager, + 0xe7903a70, 0x43f5, 0x4aee, 0xa3, 0x6b, 0xeb, 0xfe, 0x3c, 0x9b, 0x49, 0xea) +DEFINE_GUID_ENUM(IID_IHXUberStreamManagerInit, + 0xb9db235b, 0xdef7, 0x4e22, 0xb9, 0x4e, 0x6e, 0xcd, 0xce, 0xb7, 0xb3, 0x1b) +DEFINE_GUID_ENUM(IID_IHXUberStreamManagerConfig, + 0x280d65ea, 0x7b81, 0x42b0, 0xaa, 0xc6, 0x15, 0x1a, 0x94, 0xe7, 0xbb, 0x2e) +DEFINE_GUID_ENUM(IID_IHXStreamSelector, + 0x246e42b4, 0xecad, 0x4064, 0xab, 0x3a, 0x6f, 0x0, 0x60, 0x94, 0x46, 0x8c) +DEFINE_GUID_ENUM(IID_IHXRateDescEnumerator, + 0x9a32b0bb, 0xea2c, 0x47d3, 0x8f, 0x44, 0xb1, 0x80, 0xca, 0x6b, 0xa2, 0xbb) +DEFINE_GUID_ENUM(IID_IHXStreamRateDescManager, + 0x4d56585b, 0xe47a, 0x46e0, 0xba, 0xf6, 0x14, 0x62, 0x13, 0xba, 0x94, 0xab) +DEFINE_GUID_ENUM(IID_IHXStreamRateDescResponse, + 0x544d3cd9, 0x8ed8, 0x4473, 0xbd, 0x83, 0xec, 0x52, 0x90, 0xec, 0x8, 0x39) +#endif + +#ifndef _ISPIFS_H_ +/* file server/engine/inputsource/pub/ispifs.h */ +DEFINE_GUID_ENUM(IID_IHXSyncHeaderSource, + 0x5e5ed607, 0x79f0, 0x4b8d, 0xa0, 0xf6, 0x70, 0xd6, 0x57, 0x8d, 0x63, 0x99) +DEFINE_GUID_ENUM(IID_IHXRateDescVerifier, + 0xec48325f, 0x4f89, 0x4542, 0x94, 0x51, 0xf8, 0x8f, 0xd3, 0x74, 0x03, 0xef) + +#endif + +#ifndef _HXNET_H +/* common/include/hxnet.h */ +DEFINE_GUID_ENUM(IID_IHXSockAddrNative, 0x9a419062, 0xdb35, 0x11d8, 0xb8, 0x60, 0x00, + 0x02, 0xb3, 0x65, 0x87, 0x20) +DEFINE_GUID_ENUM(IID_IHXSockAddr, 0xa954f190, 0x7c47, 0x11d8, 0x8b, 0xcb, 0x00, + 0x02, 0xb3, 0x65, 0x87, 0x20) +DEFINE_GUID_ENUM(IID_IHXSockAddrLocal, 0xa9552beb, 0x7c47, 0x11d8, 0x8b, 0xcb, 0x00, + 0x02, 0xb3, 0x65, 0x87, 0x20) +DEFINE_GUID_ENUM(IID_IHXSockAddrIN4, 0xa9552bec, 0x7c47, 0x11d8, 0x8b, 0xcb, 0x00, + 0x02, 0xb3, 0x65, 0x87, 0x20) +DEFINE_GUID_ENUM(IID_IHXSockAddrIN6, 0xa95566de, 0x7c47, 0x11d8, 0x8b, 0xcb, 0x00, + 0x02, 0xb3, 0x65, 0x87, 0x20) +DEFINE_GUID_ENUM(IID_IHXSocketAccessControl, 0x831bc2f2, 0xd236, 0x11d8, 0x97, 0x45, 0x00, + 0x02, 0xb3, 0x65, 0x87, 0x20) +DEFINE_GUID_ENUM(IID_IHXSocketResponse, 0xa955da88, 0x7c47, 0x11d8, 0x8b, 0xcb, 0x00, + 0x02, 0xb3, 0x65, 0x87, 0x20) +DEFINE_GUID_ENUM(IID_IHXSocket, 0xa955a090, 0x7c47, 0x11d8, 0x8b, 0xcb, 0x00, + 0x02, 0xb3, 0x65, 0x87, 0x20) +DEFINE_GUID_ENUM(IID_IHXMulticastSocket, 0xa9561368, 0x7c47, 0x11d8, 0x8b, 0xcb, 0x00, + 0x02, 0xb3, 0x65, 0x87, 0x20) +DEFINE_GUID_ENUM(IID_IHXListeningSocketResponse, 0xa95686b8, 0x7c47, 0x11d8, 0x8b, 0xcb, 0x00, + 0x02, 0xb3, 0x65, 0x87, 0x20) +DEFINE_GUID_ENUM(IID_IHXListeningSocket, 0xa9564d38, 0x7c47, 0x11d8, 0x8b, 0xcb, 0x00, + 0x02, 0xb3, 0x65, 0x87, 0x20) +DEFINE_GUID_ENUM(IID_IHXAddrInfo, 0xa956c0ba, 0x7c47, 0x11d8, 0x8b, 0xcb, 0x00, + 0x02, 0xb3, 0x65, 0x87, 0x20) +DEFINE_GUID_ENUM(IID_IHXResolve, 0xa956fa80, 0x7c47, 0x11d8, 0x8b, 0xcb, 0x00, + 0x02, 0xb3, 0x65, 0x87, 0x20) +DEFINE_GUID_ENUM(IID_IHXResolveResponse, 0xa9574904, 0x7c47, 0x11d8, 0x8b, 0xcb, 0x00, + 0x02, 0xb3, 0x65, 0x87, 0x20) +DEFINE_GUID_ENUM(IID_IHXNetServices, 0xa9578e5a, 0x7c47, 0x11d8, 0x8b, 0xcb, 0x00, + 0x02, 0xb3, 0x65, 0x87, 0x20) +#endif + +#ifndef _IHXTLOGCONTEXTOBSERVER_H +DEFINE_GUID_ENUM(IID_IHXTLogContextObserver, + 0xd7bf2f2c, 0x19a, 0x4642, 0x89, 0x3c, 0x9b, 0x8c, 0xb7, 0x54, 0x29, 0x45) +#endif /* #ifndef _IHXTLOGCONTEXTOBSERVER_H */ + +#ifndef IHXTLOGSYSTEM_H +DEFINE_GUID_ENUM(IID_IHXTLogObserver, + 0xea6abcf4, 0x66eb, 0x11d4, 0x93, 0x1a, 0x0, 0xd0, 0xb7, 0x49, 0xde, 0x42) +DEFINE_GUID_ENUM(IID_IHXTLogObserver2, + 0x68afe313, 0xbe30, 0x4b46, 0xbf, 0xad, 0x6f, 0x3, 0x5e, 0x62, 0x4c, 0x8a) +DEFINE_GUID_ENUM(IID_IHXTFuncAreaEnum, + 0x938f4a21, 0x1327, 0x11d5, 0x93, 0x49, 0x0, 0xd0, 0xb7, 0x49, 0xde, 0x42) +DEFINE_GUID_ENUM(IID_IHXTLogObserverManager, + 0xea6abcdc, 0x66eb, 0x11d4, 0x93, 0x1a, 0x0, 0xd0, 0xb7, 0x49, 0xde, 0x42) +DEFINE_GUID_ENUM(IID_IHXTLogObserverManager2, + 0x0e38953f, 0x25ad, 0x4efb, 0x9a, 0xd4, 0x2c, 0xbb, 0xc9, 0xd6, 0x2a, 0xb0) +DEFINE_GUID_ENUM(IID_IHXTLogWriter, + 0xea6abcd9, 0x66eb, 0x11d4, 0x93, 0x1a, 0x0, 0xd0, 0xb7, 0x49, 0xde, 0x42) +DEFINE_GUID_ENUM(IID_IHXTInternalLogWriter, + 0xe7adc1b7, 0x7b6e, 0x4e54, 0x98, 0x78, 0xaa, 0x81, 0xe, 0xcc, 0x6d, 0xe6) +DEFINE_GUID_ENUM(IID_IHXTLogSystem, + 0xe50f7e51, 0x4640, 0x11d5, 0x93, 0x5b, 0x0, 0xd0, 0xb7, 0x49, 0xde, 0x42) +#endif /* #ifndef IHXTLOGSYSTEM_H */ + +#ifndef _IHXTLOGSYSTEMCONTEXT_H +DEFINE_GUID_ENUM(IID_IHXTLogSystemContext, + 0x1afceebd, 0x17b1, 0x4698, 0xa4, 0xa4, 0x1c, 0x8d, 0x84, 0x56, 0x55, 0x12) +#endif /* #ifndef _IHXTLOGSYSTEMCONTEXT_H */ + +#ifndef _IHXTFILEOBSERVER_H + +DEFINE_GUID_ENUM(IID_IHXTFileObserver, +0x478d6411, 0xa712, 0x11d5, 0x93, 0x74, 0x0, 0xd0, 0xb7, 0x49, 0xde, 0x42) + +#endif //_IHXTFILEOBSERVER_H + +#ifndef _RTP_INFO_SYNC_H_ +/* file protocol/transport/rtp/include/rtpinfosync.h */ +DEFINE_GUID_ENUM(IID_IHXRTPInfoSynch, + 0x1d9df3ad, 0x7429, 0x4efc, 0xb5, 0x4d, 0xd6, 0x7e, 0x25, 0x63, 0x4, 0xc4) +#endif + +#ifndef HXPLAYVELOCITY_H +DEFINE_GUID_ENUM(IID_IHXPlaybackVelocityCaps, + 0xdadb9abf, 0x549e, 0x4a6a, 0xa3, 0x6a, 0x59, 0x7e, 0xe2, 0x70, 0xc6, 0xa0) +DEFINE_GUID_ENUM(IID_IHXPlaybackVelocityResponse, + 0xa4f87cbb, 0x8ec5, 0x4d87, 0x90, 0x8d, 0x1a, 0x98, 0xdb, 0xbd, 0xe4, 0xdf) +DEFINE_GUID_ENUM(IID_IHXPlaybackVelocity, + 0x42bd6e35, 0x20ac, 0x4f7e, 0x9d, 0x9b, 0xb0, 0xbb, 0x3e, 0xcb, 0x1b, 0xe9) +DEFINE_GUID_ENUM(IID_IHXPlaybackVelocityTimeRegulator, + 0xa0a5d241, 0xc387, 0x4f85, 0x9b, 0xe6, 0x9b, 0xa4, 0xa0, 0xf1, 0x8c, 0x94) +#endif /* #ifndef HXPLAYVELOCITY_H */ + +DEFINE_GUID_ENUM(IID_IHXPacketOrderer, + 0x132bceaf, 0xbe58, 0x4090, 0x92, 0x9c, 0x50, 0x73, 0x6d, 0xb3, 0xe, 0x68) + +#endif /* _HXIIDS_H_ */ + diff --git a/amarok/src/engine/helix/helix-sp/helix-include/common/include/hxmon.h b/amarok/src/engine/helix/helix-sp/helix-include/common/include/hxmon.h new file mode 100644 index 00000000..d2f9f8af --- /dev/null +++ b/amarok/src/engine/helix/helix-sp/helix-include/common/include/hxmon.h @@ -0,0 +1,2356 @@ + +/* + * + * This software is released under the provisions of the GPL version 2. + * see file "COPYING". If that file is not available, the full statement + * of the license can be found at + * + * http://www.fsf.org/licensing/licenses/gpl.txt + * + * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved. + * + */ + +#ifndef _HXMON_H_ +#define _HXMON_H_ + +#include "hlxclib/limits.h" + +typedef _INTERFACE IUnknown IUnknown; +typedef _INTERFACE IHXPlugin IHXPlugin; +typedef _INTERFACE IHXBuffer IHXBuffer; +typedef _INTERFACE IHXValues IHXValues; +typedef _INTERFACE IHXPropWatch IHXPropWatch; +typedef _INTERFACE IHXPropWatchResponse IHXPropWatchResponse; +typedef _INTERFACE IHXActiveRegistry IHXActiveRegistry; +typedef _INTERFACE IHXActivePropUser IHXActivePropUser; +typedef _INTERFACE IHXActivePropUserResponse IHXActivePropUserResponse; +typedef _INTERFACE IHXRegistryAltStringHandling IHXRegistryAltStringHandling; + +/* + * Types of the values stored in the registry. + */ +typedef enum _HXPropType +{ + PT_UNKNOWN, + PT_COMPOSITE, /* Contains other values (elements) */ + PT_INTEGER, /* 32-bit signed value */ + PT_INTREF, /* Integer reference object -- 32-bit signed integer */ + PT_STRING, /* Signed char* value */ + PT_BUFFER, /* IHXBuffer object */ + + /*IHXRegistry2: */ + PT_INTEGER64, /* 64-bit signed value */ + PT_INT64REF /* Integer reference object -- 64-bit signed integer */ + +} HXPropType; + + +/* + * + * Interface: + * + * IHXRegistry + * + * Purpose: + * + * This interface provides access to the "Registry" in the server and + * client. The "Registry" is a hierarchical structure of Name/Value + * pairs (properties) which is capable of storing many different types + * of data including strings, buffers, and integers. The registry + * provides various types of information including statistics, + * configuration information, and system status. + * + * Note: This registry is not related to the Windows system registry. + * + * IID_IHXRegistry: + * + * {00000600-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXRegistry, 0x00000600, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#define CLSID_IHXRegistry IID_IHXRegistry + +#undef INTERFACE +#define INTERFACE IHXRegistry + +DECLARE_INTERFACE_(IHXRegistry, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXRegistry methods + */ + + /************************************************************************ + * Method: + * IHXRegistry::CreatePropWatch + * Purpose: + * Create a new IHXPropWatch object which can then be queried for + * the right kind of IHXPropWatch object. + * + * pPropWatch - OUT - returns a new addref'ed IHXPropWatch object + */ + STDMETHOD(CreatePropWatch) (THIS_ + REF(IHXPropWatch*) pPropWatch) PURE; + + /************************************************************************ + * Method: + * IHXRegistry::AddComp + * Purpose: + * Add a COMPOSITE property to the registry and return its ID + * if successful. It returns ZERO (0) if an error occurred + * during the operation. + * + * pName - IN - name of the Property that is going to be added to + * the registry + */ + STDMETHOD_(UINT32, AddComp) (THIS_ + const char* pName) PURE; + + /************************************************************************ + * Method: + * IHXRegistry::AddInt + * Purpose: + * Add an INTEGER property with name in "pName" and value in + * "iValue" to the registry. The return value is the id to + * the newly added Property or ZERO if there was an error. + * + * pName - IN - name of the Property that is going to be added to + * the registry + * nValue - IN - integer value of the Property that is going to be + * added to the registry + */ + STDMETHOD_(UINT32, AddInt) (THIS_ + const char* pName, + const INT32 nValue) PURE; + + /************************************************************************ + * Method: + * IHXRegistry::GetIntByName + * Purpose: + * Retreive an INTEGER value from the registry given its Property + * name "pName". If the Property is found, it will return HXR_OK, + * otherwise it returns HXR_FAIL. + * + * pName - IN - name of the Property whose value is to be retrieved + * nValue - OUT - parameter into which the value of the Property is + * going to be returned + */ + STDMETHOD(GetIntByName) (THIS_ + const char* pName, + REF(INT32) nValue) const PURE; + + /************************************************************************ + * Method: + * IHXRegistry::GetIntById + * Purpose: + * Retreive an INTEGER value from the registry given its id "ulId". + * If the Property is found, it will return HXR_OK, otherwise it + * returns HXR_FAIL. + * + * ulId - IN - unique id of the Property whose value is to be retrieved + * nValue - OUT - parameter into which the value of the Property is + * going to be returned + */ + STDMETHOD(GetIntById) (THIS_ + const UINT32 ulId, + REF(INT32) nValue) const PURE; + + /************************************************************************ + * Method: + * IHXRegistry::SetIntByName + * Purpose: + * Modify a Property's INTEGER value in the registry given the + * Property's name "pName". If the value was set, it will return HXR_OK, + * otherwise it returns HXR_FAIL. + * + * pName - IN - name of the Property whose value is to be set + * nValue - IN - the new value of the Property which is going to be set + */ + STDMETHOD(SetIntByName) (THIS_ + const char* pName, + const INT32 nValue) PURE; + + /************************************************************************ + * Method: + * IHXRegistry::SetIntById + * Purpose: + * Modify a Property's INTEGER value in the registry given the + * its id "id". If the value was set, it will return HXR_OK, otherwise + * it returns HXR_FAIL. + * + * ulId - IN - unique id of the Property whose value is to be set + * nValue - IN - the new value of the Property which is going to be set + */ + STDMETHOD(SetIntById) (THIS_ + const UINT32 id, + const INT32 nValue) PURE; + + /************************************************************************ + * Method: + * IHXRegistry::AddStr + * Purpose: + * Add an STRING property with name in "pName" and value in + * "pValue" to the registry. + * + * pName - IN - name of the Property that is going to be added to + * the registry + * pValue - IN - buffer value of the Property that is going to be + * added to the registry + */ + STDMETHOD_(UINT32, AddStr) (THIS_ + const char* pName, + IHXBuffer* pValue) PURE; + + /************************************************************************ + * Method: + * IHXRegistry::GetStrByName + * Purpose: + * Retreive an STRING value from the registry given its Property + * name "pName". If the Property is found, it will return HXR_OK, + * otherwise it returns HXR_FAIL. + * + * pName - IN - name of the Property whose value is to be retrieved + * pValue - OUT - parameter into which the value of the Property is + * going to be returned + */ + STDMETHOD(GetStrByName) (THIS_ + const char* pName, + REF(IHXBuffer*) pValue) const PURE; + + /************************************************************************ + * Method: + * IHXRegistry::GetStrById + * Purpose: + * Retreive an STRING value from the registry given its id "ulId". + * If the Property is found, it will return HXR_OK, otherwise it + * returns HXR_FAIL. + * + * ulId - IN - unique id of the Property whose value is to be retrieved + * pValue - OUT - parameter into which the value of the Property is + * going to be returned + */ + STDMETHOD(GetStrById) (THIS_ + const UINT32 ulId, + REF(IHXBuffer*) pValue) const PURE; + + /************************************************************************ + * Method: + * IHXRegistry::SetStrByName + * Purpose: + * Modify a Property's STRING value in the registry given the + * Property's name "pName". If the value was set, it will return + * HXR_OK, otherwise it returns HXR_FAIL. + * + * pName - IN - name of the Property whose value is to be set + * pValue - IN - the new value of the Property which is going to be set + */ + STDMETHOD(SetStrByName) (THIS_ + const char* pName, + IHXBuffer* pValue) PURE; + + /************************************************************************ + * Method: + * IHXRegistry::SetStrById + * Purpose: + * Modify a Property's STRING value in the registry given the + * its id "ulId". If the value was set, it will return HXR_OK, + * otherwise it returns HXR_FAIL. + * + * ulId - IN - unique id of the Property whose value is to be set + * pValue - IN - the new value of the Property which is going to be set + */ + STDMETHOD(SetStrById) (THIS_ + const UINT32 ulId, + IHXBuffer* pValue) PURE; + + /************************************************************************ + * Method: + * IHXRegistry::AddBuf + * Purpose: + * Add an BUFFER property with name in "pName" and value in + * "pValue" to the registry. + * + * pName - IN - name of the Property that is going to be added to + * the registry + * pValue - IN - buffer value of the Property that is going to be + * added to the registry + */ + STDMETHOD_(UINT32, AddBuf) (THIS_ + const char* pName, + IHXBuffer* pValue) PURE; + + /************************************************************************ + * Method: + * IHXRegistry::GetBufByName + * Purpose: + * Retreive the BUFFER from the registry given its Property name + * "pName". If the Property is found, it will return HXR_OK, otherwise + * it returns HXR_FAIL. + * + * pName - IN - name of the Property whose value is to be retrieved + * pValue - OUT - parameter into which the value of the Property is + * going to be returned + */ + STDMETHOD(GetBufByName) (THIS_ + const char* pName, + REF(IHXBuffer*) pValue) const PURE; + + /************************************************************************ + * Method: + * IHXRegistry::GetBufById + * Purpose: + * Retreive the BUFFER from the registry given its id "ulId". If the + * Property is found, it will return HXR_OK, otherwise it returns + * HXR_FAIL. + * + * ulId - IN - unique id of the Property whose value is to be retrieved + * pValue - OUT - parameter into which the value of the Property is + * going to be returned + */ + STDMETHOD(GetBufById) (THIS_ + const UINT32 ulId, + REF(IHXBuffer*) pValue) const PURE; + + /************************************************************************ + * Method: + * IHXRegistry::SetBufByName + * Purpose: + * Modify a Property's BUFFER in the registry given the + * Property's name "pName". If the value was set, it will return + * HXR_OK, otherwise it returns HXR_FAIL. + * + * pName - IN - name of the Property whose value is to be set + * pValue - IN - the new value of the Property which is going to be set + */ + STDMETHOD(SetBufByName) (THIS_ + const char* pName, + IHXBuffer* pValue) PURE; + + /************************************************************************ + * Method: + * IHXRegistry::SetBufById + * Purpose: + * Modify a Property's BUFFER in the registry given its id "ulId". + * If the value was set, it will return HXR_OK, otherwise it returns + * HXR_FAIL. + * + * ulId - IN - unique id of the Property whose value is to be set + * pValue - IN - the new value of the Property which is going to be set + */ + STDMETHOD(SetBufById) (THIS_ + const UINT32 ulId, + IHXBuffer* pValue) PURE; + + /************************************************************************ + * Method: + * IHXRegistry::AddIntRef + * Purpose: + * Add an INTEGER REFERENCE property with name in "pName" and + * value in "iValue" to the registry. This property allows the user + * to modify its contents directly, without having to go through the + * registry. + * + * pName - IN - name of the Property that is going to be added to + * the registry + * pValue - IN - the pointer of the integer value is what gets stored + * in the registry as the Interger Reference Property's + * value + */ + STDMETHOD_(UINT32, AddIntRef) (THIS_ + const char* pName, + INT32* pValue) PURE; + + /************************************************************************ + * Method: + * IHXRegistry::DeleteByName + * Purpose: + * Delete a Property from the registry using its name "pName". + * + * pName - IN - name of the Property that is going to be deleted + */ + STDMETHOD_(UINT32, DeleteByName) (THIS_ + const char* pName) PURE; + + /************************************************************************ + * Method: + * IHXRegistry::DeleteById + * Purpose: + * Delete a Property from the registry using its id "ulId". + * + * ulId - IN - unique id of the Property that is going to be deleted + */ + STDMETHOD_(UINT32, DeleteById) (THIS_ + const UINT32 ulId) PURE; + + /************************************************************************ + * Method: + * IHXRegistry::GetTypeByName + * Purpose: + * Returns the datatype of the Property given its name "pName". + * + * pName - IN - name of the Property whose type is to be retrieved + */ + STDMETHOD_(HXPropType, GetTypeByName) (THIS_ + const char* pName) const PURE; + + /************************************************************************ + * Method: + * IHXRegistry::GetTypeById + * Purpose: + * Returns the datatype of the Property given its its id "ulId". + * + * ulId - IN - unique id of the Property whose type is to be retrieved + */ + STDMETHOD_(HXPropType, GetTypeById) (THIS_ + const UINT32 ulId) const PURE; + + /************************************************************************ + * Method: + * IHXRegistry::FindParentIdByName + * Purpose: + * Returns the id value of the parent node of the Property whose + * name "pName" has been passed in. If it fails, a ZERO value is + * returned. + * + * pName - IN - name of the Property whose parent's unique id is to be + * retrieved + */ + STDMETHOD_(UINT32, FindParentIdByName) (THIS_ + const char* pName) const PURE; + + /************************************************************************ + * Method: + * IHXRegistry::FindParentIdById + * Purpose: + * Returns the id value of the parent node of the Property whose + * id "ulId" has been passed in. If it fails, a ZERO value is returned. + * + * ulId - IN - unique id of the Property whose parent's id is to be + * retrieved + */ + STDMETHOD_(UINT32, FindParentIdById) (THIS_ + const UINT32 ulId) const PURE; + + /************************************************************************ + * Method: + * HXRegistry::GetPropName + * Purpose: + * Returns the Property name in the pName char buffer passed + * as a parameter, given the Property's id "ulId". + * + * ulId - IN - unique id of the Property whose name is to be retrieved + * pName - OUT - parameter into which the Property name is going to be + * returned + */ + STDMETHOD(GetPropName) (THIS_ + const UINT32 ulId, + REF(IHXBuffer*) pName) const PURE; + + /************************************************************************ + * Method: + * HXRegistry::GetId + * Purpose: + * Returns the Property's id given the Property name. + * + * pName - IN - name of the Property whose unique id is to be + * retrieved + */ + STDMETHOD_(UINT32, GetId) (THIS_ + const char* pName) const PURE; + + /************************************************************************ + * Method: + * IHXRegistry::GetPropListOfRoot + * Purpose: + * Returns an array of a Properties under the root level of the + * registry's hierarchy. + * + * pValues - OUT - list of property name and unique id at the + * highest level (root) in the registry + */ + STDMETHOD(GetPropListOfRoot) (THIS_ + REF(IHXValues*) pValues) const PURE; + + /************************************************************************ + * Method: + * IHXRegistry::GetPropListByName + * Purpose: + * Returns an array of Properties immediately under the one whose + * name is passed in "pName". + * + * pName - IN - name of the Property whose child property list is to be + * retrieved + * pValues - OUT - list of property name and unique id under the + * Property whose name is in "pName" + */ + STDMETHOD(GetPropListByName) (THIS_ + const char* pName, + REF(IHXValues*) pValues) const PURE; + + /************************************************************************ + * Method: + * IHXRegistry::GetPropListById + * Purpose: + * Returns an array of Properties immediately under the one whose + * id is passed in "ulId". + * + * ulId - IN - unique id of the Property whose child property list is + * to be retrieved + * pValues - OUT - list of property name and unique id under the + * Property whose is is in "ulId" + */ + STDMETHOD(GetPropListById) (THIS_ + const UINT32 ulId, + REF(IHXValues*) pValues) const PURE; + + /************************************************************************ + * Method: + * IHXRegistry::GetNumPropsAtRoot + * Purpose: + * Returns the number of Properties at the root of the registry. + */ + STDMETHOD_(INT32, GetNumPropsAtRoot) (THIS) const PURE; + + /************************************************************************ + * Method: + * IHXRegistry::GetNumPropsByName + * Purpose: + * Returns the count of the number of Properties under the one + * whose name is specified in "pName". + * + * pName - IN - name of the Property whose number of children is to be + * retrieved + */ + STDMETHOD_(INT32, GetNumPropsByName) (THIS_ + const char* pName) const PURE; + + /************************************************************************ + * Method: + * IHXRegistry::GetNumPropsById + * Purpose: + * Returns the count of the number of Properties under the one + * whose unique id is specified in "ulId". + * + * ulId - IN - unique id of the Property whose number of children is + * to be retrieved + */ + STDMETHOD_(INT32, GetNumPropsById) (THIS_ + const UINT32 ulId) const PURE; +}; + + +/* + * + * Interface: + * + * IHXPropWatch + * + * Purpose: + * + * This interface allows the user to watch properties so that when + * changes happen to the properties the plugins receive notification via + * the IHXPropWatchResponse API. + * + * IID_IHXPropWatch: + * + * {00000601-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXPropWatch, 0x00000601, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXPropWatch + +DECLARE_INTERFACE_(IHXPropWatch, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXPropWatch methods + */ + + /************************************************************************ + * Method: + * IHXPropWatch::Init + * Purpose: + * Initialize with the response object so that the Watch + * notifications can be sent back to the respective plugins. + * + * pResponse - IN - pointer to the response object which gets used to + * initialize the IHXPropWatch object. the response + * object gets AddRef'd in the Init method. + */ + STDMETHOD(Init) (THIS_ + IHXPropWatchResponse* pResponse) PURE; + + /************************************************************************ + * Method: + * IHXPropWatch::SetWatchOnRoot + * Purpose: + * The SetWatch method puts a watch at the highest level of + * the registry hierarchy. It notifies ONLY IF properties at THIS LEVEL + * get added/modified/deleted. + */ + STDMETHOD_(UINT32, SetWatchOnRoot) (THIS) PURE; + + /************************************************************************ + * Method: + * IHXPropWatch::SetWatchByName + * Purpose: + * Sets a watch-point on the Property whose name is passed in. + * In case the mentioned Property gets modified or deleted a + * notification of that will be sent to the object which set the + * watch-point. + * + * pName - IN - name of Property on which a watch point is to be added + */ + STDMETHOD_(UINT32, SetWatchByName) (THIS_ + const char* pName) PURE; + + /************************************************************************ + * Method: + * IHXPropWatch::SetWatchById + * Purpose: + * Sets a watch-point on the Property whose name is passed in. + * In case the mentioned Property gets modified or deleted a + * notification of that will be sent to the object which set the + * watch-point. + * + * ulId - IN - unique id of Property on which a watch point is to be + * added + */ + STDMETHOD_(UINT32, SetWatchById) (THIS_ + const UINT32 ulId) PURE; + + /************************************************************************ + * Method: + * IHXPropWatch::ClearWatchOnRoot + * Purpose: + * It clears the watch on the root of the registry. + */ + STDMETHOD(ClearWatchOnRoot) (THIS) PURE; + + /************************************************************************ + * Method: + * IHXPropWatch::ClearWatchByName + * Purpose: + * Clears a watch-point based on the Property's name. + * + * pName - IN - name of Property whose watch point is to be cleared + */ + STDMETHOD(ClearWatchByName) (THIS_ + const char* pName) PURE; + + /************************************************************************ + * Method: + * IHXPropWatch::ClearWatchById + * Purpose: + * Clears a watch-point based on the Property's id. + * + * ulId - IN - unique id of Property whose watch point is to be cleared + */ + STDMETHOD(ClearWatchById) (THIS_ + const UINT32 ulId) PURE; +}; + + +/* + * + * Interface: + * + * IHXPropWatchResponse + * + * Purpose: + * + * Interface for notification of additions/modifications/deletions of + * properties in the registry which are being watched. + * + * IID_IHXPropWatchResponse: + * + * {00000602-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXPropWatchResponse, 0x00000602, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXPropWatchResponse + +DECLARE_INTERFACE_(IHXPropWatchResponse, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXPropWatchResponse methods + */ + + /************************************************************************ + * Method: + * IHXPropWatchResponse::AddedProp + * Purpose: + * Gets called when a new Property gets added under the Property + * on which the Watch was set. It passes the id of the Property just + * added, its datatype and the id of its immediate parent COMPOSITE + * property. + */ + STDMETHOD(AddedProp) (THIS_ + const UINT32 ulId, + const HXPropType propType, + const UINT32 ulParentID) PURE; + + /************************************************************************ + * Method: + * IHXPropWatchResponse::ModifiedProp + * Purpose: + * Gets called when a watched Property gets modified. It passes + * the id of the Property just modified, its datatype and the + * id of its immediate parent COMPOSITE property. + */ + STDMETHOD(ModifiedProp) (THIS_ + const UINT32 ulId, + const HXPropType propType, + const UINT32 ulParentID) PURE; + + /************************************************************************ + * Method: + * IHXPropWatchResponse::DeletedProp + * Purpose: + * Gets called when a watched Property gets deleted. As can be + * seen, it returns the id of the Property just deleted and + * its immediate parent COMPOSITE property. + */ + STDMETHOD(DeletedProp) (THIS_ + const UINT32 ulId, + const UINT32 ulParentID) PURE; +}; + +/* + * + * Interface: + * + * IHXActiveRegistry + * + * Purpose: + * + * Interface to get IHXActiveUser responsible for a particular property + * from the registry. + * + * IID_IHXActiveRegistry: + * + * {00000603-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXActiveRegistry, 0x00000603, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXActiveRegistry + +DECLARE_INTERFACE_(IHXActiveRegistry, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /************************************************************************ + * IHXActiveRegistry::SetAsActive + * + * Method to set prop pName to active and register pUser as + * the active prop user. + */ + STDMETHOD(SetAsActive) (THIS_ + const char* pName, + IHXActivePropUser* pUser) PURE; + + /************************************************************************ + * IHXActiveRegistry::SetAsInactive + * + * Method to remove an IHXActiveUser from Prop activation. + */ + STDMETHOD(SetAsInactive) (THIS_ + const char* pName, + IHXActivePropUser* pUser) PURE; + + /************************************************************************ + * IHXActiveRegistry::IsActive + * + * Tells if prop pName has an active user that must be queried to + * change the value, or if it can just be set. + */ + STDMETHOD_(HXBOOL, IsActive) (THIS_ + const char* pName) PURE; + + /************************************************************************ + * IHXActiveRegistry::SetActiveInt + * + * Async request to set int pName to ul. + */ + STDMETHOD(SetActiveInt) (THIS_ + const char* pName, + UINT32 ul, + IHXActivePropUserResponse* pResponse) PURE; + + /************************************************************************ + * IHXActiveRegistry::SetActiveStr + * + * Async request to set string pName to string in pBuffer. + */ + STDMETHOD(SetActiveStr) (THIS_ + const char* pName, + IHXBuffer* pBuffer, + IHXActivePropUserResponse* pResponse) PURE; + + /************************************************************************ + * IHXActiveRegistry::SetActiveBuf + * + * Async request to set buffer pName to buffer in pBuffer. + */ + STDMETHOD(SetActiveBuf) (THIS_ + const char* pName, + IHXBuffer* pBuffer, + IHXActivePropUserResponse* pResponse) PURE; + + /************************************************************************ + * IHXActiveRegistry::DeleteActiveProp + * + * Async request to delete the active property. + */ + STDMETHOD(DeleteActiveProp) (THIS_ + const char* pName, + IHXActivePropUserResponse* pResponse) PURE; + + +}; + + +/* + * + * Interface: + * + * IHXActivePropUser + * + * Purpose: + * + * An IHXActivePropUser can be set as the active user of a property in + * an IHXActiveRegistry. This causes the IHXActivePropUser to be consulted + * every time someone wants to change a property. The difference between this + * and a prop watch is that this is async, and can call a done method with + * failure to cause the prop to not be set, and this get called instead of + * calling into the IHXReg. + * + * IID_IHXActivePropUser: + * + * {00000604-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXActivePropUser, 0x00000604, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXActivePropUser + +DECLARE_INTERFACE_(IHXActivePropUser, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /************************************************************************ + * IHXActivePropUser::SetActiveInt + * + * Async request to set int pName to ul. + */ + STDMETHOD(SetActiveInt) (THIS_ + const char* pName, + UINT32 ul, + IHXActivePropUserResponse* pResponse) PURE; + + /************************************************************************ + * IHXActivePropUser::SetActiveStr + * + * Async request to set string pName to string in pBuffer. + */ + STDMETHOD(SetActiveStr) (THIS_ + const char* pName, + IHXBuffer* pBuffer, + IHXActivePropUserResponse* pResponse) PURE; + + /************************************************************************ + * IHXActivePropUser::SetActiveBuf + * + * Async request to set buffer pName to buffer in pBuffer. + */ + STDMETHOD(SetActiveBuf) (THIS_ + const char* pName, + IHXBuffer* pBuffer, + IHXActivePropUserResponse* pResponse) PURE; + + /************************************************************************ + * IHXActivePropUser::DeleteActiveProp + * + * Async request to delete the active property. + */ + STDMETHOD(DeleteActiveProp) (THIS_ + const char* pName, + IHXActivePropUserResponse* pResponse) PURE; + +}; + +/* + * + * Interface: + * + * IHXActivePropUserResponse + * + * Purpose: + * + * Gets responses from IHXActivePropUser for queries to set properties + * in the IHXActiveRegistry. + * + * + * IID_IHXActivePropUserResponse: + * + * {00000605-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXActivePropUserResponse, 0x00000605, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXActivePropUserResponse + +DECLARE_INTERFACE_(IHXActivePropUserResponse, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /************************************************************************ + * Called with status result on completion of set request. + */ + STDMETHOD(SetActiveIntDone) (THIS_ + HX_RESULT res, + const char* pName, + UINT32 ul, + IHXBuffer* pInfo[], + UINT32 ulNumInfo) PURE; + + STDMETHOD(SetActiveStrDone) (THIS_ + HX_RESULT res, + const char* pName, + IHXBuffer* pBuffer, + IHXBuffer* pInfo[], + UINT32 ulNumInfo) PURE; + + STDMETHOD(SetActiveBufDone) (THIS_ + HX_RESULT res, + const char* pName, + IHXBuffer* pBuffer, + IHXBuffer* pInfo[], + UINT32 ulNumInfo) PURE; + + STDMETHOD(DeleteActivePropDone) (THIS_ + HX_RESULT res, + const char* pName, + IHXBuffer* pInfo[], + UINT32 ulNumInfo) PURE; + +}; + +/* + * + * Interface: + * + * IHXCopyRegistry + * + * Purpose: + * + * Allows copying from one registry key to another. + * + * + * IID_IHXCopyRegistry + * + * {00000606-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXCopyRegistry, 0x00000606, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXCopyRegistry + +DECLARE_INTERFACE_(IHXCopyRegistry, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /************************************************************************ + * IHXCopyRegistry::Copy + * + * Here it is! The "Copy" method! + */ + STDMETHOD (CopyByName) (THIS_ + const char* pFrom, + const char* pTo) PURE; +}; + + +/* + * + * Interface: + * + * IHXRegistryAltStringHandling + * + * Purpose: + * + * Tells the registry about alternate handling of PT_STRING types. + * + * + * IID_IHXRegistryAltStringHandling + * + * {00000607-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXRegistryAltStringHandling, 0x00000607, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXRegistryAltStringHandling + +DECLARE_INTERFACE_(IHXRegistryAltStringHandling, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /************************************************************************ + * IHXRegistryAltStringHandling::SetStringAccessAsBufferById + * + * For those times when you added a property as a buffer, but wish it + * were a string (and of course, people now rely on the fact that it's + * a buffer)... Create the property as a string and then pass this + * method it's ID. The property will now be accessible/setable as a, + * but it will still be a string! + */ + STDMETHOD (SetStringAccessAsBufferById) (THIS_ + UINT32 ulId) PURE; +}; + + + +// $Private: +/* + * + * Interface: + * + * IHXRegistry2 + * + * Purpose: + * + * 1) Provide atomic update methods + * 2) Provide INT64 support + * 3) Provide access to INTREF pointers + * + * All operations occur atomically, ensuring that multiple users of the + * registry do not interfere with each other, even on multi-CPU systems. + * Note, this is essentially a superset of IHXRegistry. + * + * IID_IHXRegistry2 + * + * {00000608-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXRegistry2, 0x00000608, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXRegistry2 + +DECLARE_INTERFACE_(IHXRegistry2, IUnknown) +{ + + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXRegistry2 methods + */ + + /************************************************************************ + * Method: + * IHXRegistry2::CreatePropWatch + * Purpose: + * Create a new IHXPropWatch object which can then be queried for + * the right kind of IHXPropWatch object. + * + * pPropWatch - OUT - returns a new addref'ed IHXPropWatch object + */ + STDMETHOD(CreatePropWatch) (THIS_ + REF(IHXPropWatch*) pPropWatch) PURE; + + /************************************************************************ + * Method: + * IHXRegistry2::AddComp + * Purpose: + * Add a COMPOSITE property to the registry and return its ID + * if successful. It returns ZERO (0) if an error occurred + * during the operation. + * + * pName - IN - name of the Property that is going to be added to + * the registry + */ + STDMETHOD_(UINT32, AddComp) (THIS_ + const char* pName) PURE; + + /************************************************************************ + * Method: + * IHXRegistry2::AddInt + * Purpose: + * Add an INTEGER property with name in "pName" and value in + * "iValue" to the registry. The return value is the id to + * the newly added Property or ZERO if there was an error. + * + * pName - IN - name of the Property that is going to be added to + * the registry + * nValue - IN - integer value of the Property that is going to be + * added to the registry + */ + STDMETHOD_(UINT32, AddInt) (THIS_ + const char* pName, + const INT32 nValue) PURE; + + /************************************************************************ + * Method: + * IHXRegistry2::GetIntByName + * Purpose: + * Retreive an INTEGER value from the registry given its Property + * name "pName". If the Property is found, it will return HXR_OK, + * otherwise it returns HXR_FAIL. + * + * pName - IN - name of the Property whose value is to be retrieved + * nValue - OUT - parameter into which the value of the Property is + * going to be returned + */ + STDMETHOD(GetIntByName) (THIS_ + const char* pName, + REF(INT32) nValue) const PURE; + + /************************************************************************ + * Method: + * IHXRegistry2::GetIntById + * Purpose: + * Retreive an INTEGER value from the registry given its id "ulId". + * If the Property is found, it will return HXR_OK, otherwise it + * returns HXR_FAIL. + * + * ulId - IN - unique id of the Property whose value is to be retrieved + * nValue - OUT - parameter into which the value of the Property is + * going to be returned + */ + STDMETHOD(GetIntById) (THIS_ + const UINT32 ulId, + REF(INT32) nValue) const PURE; + + /************************************************************************ + * Method: + * IHXRegistry2::SetIntByName + * Purpose: + * Modify a Property's INTEGER value in the registry given the + * Property's name "pName". If the value was set, it will return HXR_OK, + * otherwise it returns HXR_FAIL. + * + * pName - IN - name of the Property whose value is to be set + * nValue - IN - the new value of the Property which is going to be set + */ + STDMETHOD(SetIntByName) (THIS_ + const char* pName, + const INT32 nValue) PURE; + + /************************************************************************ + * Method: + * IHXRegistry2::SetIntById + * Purpose: + * Modify a Property's INTEGER value in the registry given the + * its id "id". If the value was set, it will return HXR_OK, otherwise + * it returns HXR_FAIL. + * + * ulId - IN - unique id of the Property whose value is to be set + * nValue - IN - the new value of the Property which is going to be set + */ + STDMETHOD(SetIntById) (THIS_ + const UINT32 id, + const INT32 nValue) PURE; + + /************************************************************************ + * Method: + * IHXRegistry2::AddStr + * Purpose: + * Add an STRING property with name in "pName" and value in + * "pValue" to the registry and return its ID if successful. + * It returns ZERO (0) if an error occurred during the operation. + * + * pName - IN - name of the Property that is going to be added to + * the registry + * pValue - IN - buffer value of the Property that is going to be + * added to the registry + */ + STDMETHOD_(UINT32, AddStr) (THIS_ + const char* pName, + IHXBuffer* pValue) PURE; + + /************************************************************************ + * Method: + * IHXRegistry2::GetStrByName + * Purpose: + * Retreive an STRING value from the registry given its Property + * name "pName". If the Property is found, it will return HXR_OK, + * otherwise it returns HXR_FAIL. + * + * pName - IN - name of the Property whose value is to be retrieved + * pValue - OUT - parameter into which the value of the Property is + * going to be returned + */ + STDMETHOD(GetStrByName) (THIS_ + const char* pName, + REF(IHXBuffer*) pValue) const PURE; + + /************************************************************************ + * Method: + * IHXRegistry2::GetStrById + * Purpose: + * Retreive an STRING value from the registry given its id "ulId". + * If the Property is found, it will return HXR_OK, otherwise it + * returns HXR_FAIL. + * + * ulId - IN - unique id of the Property whose value is to be retrieved + * pValue - OUT - parameter into which the value of the Property is + * going to be returned + */ + STDMETHOD(GetStrById) (THIS_ + const UINT32 ulId, + REF(IHXBuffer*) pValue) const PURE; + + /************************************************************************ + * Method: + * IHXRegistry2::SetStrByName + * Purpose: + * Modify a Property's STRING value in the registry given the + * Property's name "pName". If the value was set, it will return + * HXR_OK, otherwise it returns HXR_FAIL. + * + * pName - IN - name of the Property whose value is to be set + * pValue - IN - the new value of the Property which is going to be set + */ + STDMETHOD(SetStrByName) (THIS_ + const char* pName, + IHXBuffer* pValue) PURE; + + /************************************************************************ + * Method: + * IHXRegistry2::SetStrById + * Purpose: + * Modify a Property's STRING value in the registry given the + * its id "ulId". If the value was set, it will return HXR_OK, + * otherwise it returns HXR_FAIL. + * + * ulId - IN - unique id of the Property whose value is to be set + * pValue - IN - the new value of the Property which is going to be set + */ + STDMETHOD(SetStrById) (THIS_ + const UINT32 ulId, + IHXBuffer* pValue) PURE; + + /************************************************************************ + * Method: + * IHXRegistry2::AddBuf + * Purpose: + * Add an BUFFER property with name in "pName" and value in + * "pValue" to the registry and return its ID if successful. + * It returns ZERO (0) if an error occurred during the operation. + * + * pName - IN - name of the Property that is going to be added to + * the registry + * pValue - IN - buffer value of the Property that is going to be + * added to the registry + */ + STDMETHOD_(UINT32, AddBuf) (THIS_ + const char* pName, + IHXBuffer* pValue) PURE; + + /************************************************************************ + * Method: + * IHXRegistry2::GetBufByName + * Purpose: + * Retreive the BUFFER from the registry given its Property name + * "pName". If the Property is found, it will return HXR_OK, otherwise + * it returns HXR_FAIL. + * + * pName - IN - name of the Property whose value is to be retrieved + * pValue - OUT - parameter into which the value of the Property is + * going to be returned + */ + STDMETHOD(GetBufByName) (THIS_ + const char* pName, + REF(IHXBuffer*) pValue) const PURE; + + /************************************************************************ + * Method: + * IHXRegistry2::GetBufById + * Purpose: + * Retreive the BUFFER from the registry given its id "ulId". If the + * Property is found, it will return HXR_OK, otherwise it returns + * HXR_FAIL. + * + * ulId - IN - unique id of the Property whose value is to be retrieved + * pValue - OUT - parameter into which the value of the Property is + * going to be returned + */ + STDMETHOD(GetBufById) (THIS_ + const UINT32 ulId, + REF(IHXBuffer*) pValue) const PURE; + + /************************************************************************ + * Method: + * IHXRegistry2::SetBufByName + * Purpose: + * Modify a Property's BUFFER in the registry given the + * Property's name "pName". If the value was set, it will return + * HXR_OK, otherwise it returns HXR_FAIL. + * + * pName - IN - name of the Property whose value is to be set + * pValue - IN - the new value of the Property which is going to be set + */ + STDMETHOD(SetBufByName) (THIS_ + const char* pName, + IHXBuffer* pValue) PURE; + + /************************************************************************ + * Method: + * IHXRegistry2::SetBufById + * Purpose: + * Modify a Property's BUFFER in the registry given its id "ulId". + * If the value was set, it will return HXR_OK, otherwise it returns + * HXR_FAIL. + * + * ulId - IN - unique id of the Property whose value is to be set + * pValue - IN - the new value of the Property which is going to be set + */ + STDMETHOD(SetBufById) (THIS_ + const UINT32 ulId, + IHXBuffer* pValue) PURE; + + /************************************************************************ + * Method: + * IHXRegistry2::AddIntRef + * Purpose: + * Add an INTEGER REFERENCE property with name in "pName" and + * value in "iValue" to the registry. This property allows the user + * to modify its contents directly, without having to go through the + * registry. The Property's id is returned if successful. + * It returns ZERO (0) if an error occurred during the operation. + * + * pName - IN - name of the Property that is going to be added to + * the registry + * pValue - IN - the pointer of the integer value is what gets stored + * in the registry as the Interger Reference Property's + * value + */ + STDMETHOD_(UINT32, AddIntRef) (THIS_ + const char* pName, + INT32* pValue) PURE; + + /************************************************************************ + * Method: + * IHXRegistry2::DeleteByName + * Purpose: + * Delete a Property from the registry using its name "pName". + * + * pName - IN - name of the Property that is going to be deleted + */ + STDMETHOD_(UINT32, DeleteByName) (THIS_ + const char* pName) PURE; + + /************************************************************************ + * Method: + * IHXRegistry2::DeleteById + * Purpose: + * Delete a Property from the registry using its id "ulId". + * + * ulId - IN - unique id of the Property that is going to be deleted + */ + STDMETHOD_(UINT32, DeleteById) (THIS_ + const UINT32 ulId) PURE; + + /************************************************************************ + * Method: + * IHXRegistry2::GetTypeByName + * Purpose: + * Returns the datatype of the Property given its name "pName". + * + * pName - IN - name of the Property whose type is to be retrieved + */ + STDMETHOD_(HXPropType, GetTypeByName) (THIS_ + const char* pName) const PURE; + + /************************************************************************ + * Method: + * IHXRegistry2::GetTypeById + * Purpose: + * Returns the datatype of the Property given its its id "ulId". + * + * ulId - IN - unique id of the Property whose type is to be retrieved + */ + STDMETHOD_(HXPropType, GetTypeById) (THIS_ + const UINT32 ulId) const PURE; + + /************************************************************************ + * Method: + * IHXRegistry2::FindParentIdByName + * Purpose: + * Returns the id value of the parent node of the Property whose + * name "pName" has been passed in. If it fails, a ZERO value is + * returned. + * + * pName - IN - name of the Property whose parent's unique id is to be + * retrieved + */ + STDMETHOD_(UINT32, FindParentIdByName) (THIS_ + const char* pName) const PURE; + + /************************************************************************ + * Method: + * IHXRegistry2::FindParentIdById + * Purpose: + * Returns the id value of the parent node of the Property whose + * id "ulId" has been passed in. If it fails, a ZERO value is returned. + * + * ulId - IN - unique id of the Property whose parent's id is to be + * retrieved + */ + STDMETHOD_(UINT32, FindParentIdById) (THIS_ + const UINT32 ulId) const PURE; + + /************************************************************************ + * Method: + * IHXRegistry2::GetPropName + * Purpose: + * Returns the Property name in the pName char buffer passed + * as a parameter, given the Property's id "ulId". + * + * ulId - IN - unique id of the Property whose name is to be retrieved + * pName - OUT - parameter into which the Property name is going to be + * returned + */ + STDMETHOD(GetPropName) (THIS_ + const UINT32 ulId, + REF(IHXBuffer*) pName) const PURE; + + /************************************************************************ + * Method: + * IHXRegistry2::GetId + * Purpose: + * Returns the Property's id given the Property name. + * + * pName - IN - name of the Property whose unique id is to be + * retrieved + */ + STDMETHOD_(UINT32, GetId) (THIS_ + const char* pName) const PURE; + + /************************************************************************ + * Method: + * IHXRegistry2::GetPropListOfRoot + * Purpose: + * Returns an array of a Properties under the root level of the + * registry's hierarchy. + * + * pValues - OUT - list of property name and unique id at the + * highest level (root) in the registry + */ + STDMETHOD(GetPropListOfRoot) (THIS_ + REF(IHXValues*) pValues) const PURE; + + /************************************************************************ + * Method: + * IHXRegistry2::GetPropListByName + * Purpose: + * Returns an array of Properties immediately under the one whose + * name is passed in "pName". + * + * pName - IN - name of the Property whose child property list is to be + * retrieved + * pValues - OUT - list of property name and unique id under the + * Property whose name is in "pName" + */ + STDMETHOD(GetPropListByName) (THIS_ + const char* pName, + REF(IHXValues*) pValues) const PURE; + + /************************************************************************ + * Method: + * IHXRegistry2::GetPropListById + * Purpose: + * Returns an array of Properties immediately under the one whose + * id is passed in "ulId". + * + * ulId - IN - unique id of the Property whose child property list is + * to be retrieved + * pValues - OUT - list of property name and unique id under the + * Property whose is is in "ulId" + */ + STDMETHOD(GetPropListById) (THIS_ + const UINT32 ulId, + REF(IHXValues*) pValues) const PURE; + + /************************************************************************ + * Method: + * IHXRegistry2::GetNumPropsAtRoot + * Purpose: + * Returns the number of Properties at the root of the registry. + * + */ + STDMETHOD_(INT32, GetNumPropsAtRoot) (THIS) const PURE; + + /************************************************************************ + * Method: + * IHXRegistry2::GetNumPropsByName + * Purpose: + * Returns the count of the number of Properties under the one + * whose name is specified in "pName". + * + * pName - IN - name of the Property whose number of children is to be + * retrieved + */ + STDMETHOD_(INT32, GetNumPropsByName) (THIS_ + const char* pName) const PURE; + + /************************************************************************ + * Method: + * IHXRegistry2::GetNumPropsById + * Purpose: + * Returns the count of the number of Properties under the one + * whose unique id is specified in "ulId". + * + * ulId - IN - unique id of the Property whose number of children is + * to be retrieved + */ + STDMETHOD_(INT32, GetNumPropsById) (THIS_ + const UINT32 ulId) const PURE; + + + /************************************************************************ + * Method: + * IHXRegistry2::ModifyIntByName + * Purpose: + * Changes the INTEGER value in the registry given its Property + * name "pName" and an amount to change it by. Modifies the value + * of the integer in the registry by the amount specified by "nDelta", + * setting nValue equal to the value after modification. If the + * Property is found, it will return HXR_OK, otherwise it + * returns HXR_FAIL. + * + * pName - IN - name of the Property whose value is to be retrieved + * nDelta - IN - amount to modify the named property by + * nValue - OUT - parameter into which the value of the Property is + * going to be returned, after modification + */ + STDMETHOD(ModifyIntByName) (THIS_ + const char* pName, + INT32 nDelta, + REF(INT32) nValue) PURE; + + /************************************************************************ + * Method: + * IHXRegistry2::ModifyIntById + * Purpose: + * Changes the INTEGER value in the registry given its id "ulID" + * and an amount to change it by. Modifies the value of the + * integer in the registry by the amount specified by "nDelta", + * setting nValue equal to the value after modification. If the + * Property is found, it will return HXR_OK, otherwise it + * returns HXR_FAIL. + * + * ulId - IN - unique id of the Property whose value is to be modified + * nDelta - IN - amount to modify the specified property by + * nValue - OUT - parameter into which the value of the Property is + * going to be returned, after modification + */ + STDMETHOD(ModifyIntById) (THIS_ + const UINT32 id, + INT32 nDelta, + REF(INT32) nValue) PURE; + + /************************************************************************ + * Method: + * IHXRegistry2::BoundedModifyIntByName + * Purpose: + * Changes the INTEGER value in the registry given its Property name + * "pName" and an amount to change it by and keeps the modified value + * within the bounds of the nMin and nMax values. Modifies the value + * of the integer in the registry by the amount specified by "nDelta", + * setting nValue equal to the value after modification, if the modified + * value is >= nMin and <= nMax. If either of these limits are violated + * the the resulting value stored in the registry is the value of the + * limit just violated. If the Property is found, it will return HXR_OK, + * otherwise it returns HXR_FAIL. + * + * pName - IN - name of the Property whose value is to be retrieved + * nDelta - IN - amount to modify the named property by + * nMin - IN - min value that the modified registry prop can have + * if the modified registry value < nMin then + * registry value = nMin + * nMax - IN - min value that the modified registry prop can have + * if the modified registry value > nMax then + * registry value = nMax + * nValue - OUT - parameter into which the value of the Property is + * going to be returned, after modification + */ + STDMETHOD(BoundedModifyIntByName) (THIS_ + const char* pName, + INT32 nDelta, + REF(INT32) nValue, + INT32 nMin=INT_MIN, + INT32 nMax=INT_MAX) PURE; + + /************************************************************************ + * Method: + * IHXRegistry2::BoundedModifyIntById + * Purpose: + * Changes the INTEGER value in the registry given its id "ulID" + * and an amount to change it by and keeps the modified value within + * the bounds of the nMin and nMax values. Modifies the value of the + * integer in the registry by the amount specified by "nDelta", + * setting nValue equal to the value after modification, if the modified + * value is >= nMin and <= nMax. If either of these limits are violated + * the the resulting value stored in the registry is the value of the + * limit just violated. If the Property is found, it will return HXR_OK, + * otherwise it returns HXR_FAIL. + * + * ulId - IN - unique id of the Property whose value is to be modified + * nDelta - IN - amount to modify the specified property by + * nMin - IN - min value that the modified registry prop can have + * if the modified registry value < nMin then + * registry value = nMin + * nMax - IN - min value that the modified registry prop can have + * if the modified registry value > nMax then + * registry value = nMax + * nValue - OUT - parameter into which the value of the Property is + * going to be returned, after modification + */ + STDMETHOD(BoundedModifyIntById) (THIS_ + const UINT32 id, + INT32 nDelta, + REF(INT32) nValue, + INT32 nMin=INT_MIN, + INT32 nMax=INT_MAX) PURE; + + /************************************************************************ + * Method: + * IHXRegistry2::SetAndReturnIntByName + * Purpose: + * Modify a Property's INTEGER value in the registry given the + * Property's name "pName". If the Property is found, the previous + * value, prior to setting it, will be assigned to nOldValue. + * If the Property is found, it will return HXR_OK, otherwise it + * returns HXR_FAIL. + * + * pName - IN - name of the Property whose value is to be retrieved + * nValue - IN - the new value of the Property which is going to be set + * nOldValue - OUT - parameter into which the previous value of the + * Property is returned + */ + STDMETHOD(SetAndReturnIntByName) (THIS_ + const char* pName, + INT32 nValue, + REF(INT32) nOldValue) PURE; + + /************************************************************************ + * Method: + * IHXRegistry2::SetAndReturnIntById + * Purpose: + * Modify a Property's INTEGER value in the registry given the + * Property's id "ulId". If the id is found, the previous + * value, prior to setting it, will be assigned to nOldValue. + * If the Property is found, it will return HXR_OK, otherwise it + * returns HXR_FAIL. + * + * pName - IN - name of the Property whose value is to be retrieved + * nValue - IN - the new value of the Property which is going to be set + * nOldValue - OUT - parameter into which the previous value of the + * Property is returned + */ + STDMETHOD(SetAndReturnIntById) (THIS_ + const UINT32 id, + INT32 nValue, + REF(INT32) nOldValue) PURE; + + /************************************************************************ + * Method: + * IHXRegistry2::GetIntRefByName + * Purpose: + * Retrieve an INTEGER REFERENCE property from the registry given + * its Property name "pName". If the Property is found it will return + * HXR_OK and pValue will be assigned the address of the integer + * (not the value of the integer, which can be obtained even for + * INTREFs via the GetIntByxxx methods.) Otherwise, it returns HXR_FAIL. + * + * pName - IN - name of the Property whose value is to be retrieved + * pValue - OUT - the address of the integer value + */ + STDMETHOD(GetIntRefByName) (THIS_ + const char* pName, + REF(INT32*) pValue) const PURE; + + /************************************************************************ + * Method: + * IHXRegistry2::GetIntRefById + * Purpose: + * Retrieve an INTEGER REFERENCE property from the registry given + * its id "ulId". If the Property is found it will return + * HXR_OK and pValue will be assigned the address of the integer + * (not the value of the integer, which can be obtained even for + * INTREFs via the GetIntByxxx methods.) Otherwise, it returns HXR_FAIL. + * + * pName - IN - name of the Property whose value is to be retrieved + * pValue - OUT - the address of the integer value + */ + STDMETHOD(GetIntRefById) (THIS_ + const UINT32 id, + REF(INT32*) pValue) const PURE; + + + + /************************************************************************ + * Method: + * IHXRegistry2::AddInt64 + * Purpose: + * Add an INTEGER property with name in "pName" and value in + * "iValue" to the registry. The return value is the id to + * the newly added Property or ZERO if there was an error. + * + * pName - IN - name of the Property that is going to be added to + * the registry + * nValue - IN - integer value of the Property that is going to be + * added to the registry + */ + STDMETHOD_(UINT32, AddInt64) (THIS_ + const char* pName, + const INT64 nValue) PURE; + + /************************************************************************ + * Method: + * IHXRegistry2::GetInt64ByName + * Purpose: + * Retrieve a 64-bit INTEGER value from the registry given its + * Property name "pName". If the Property is found, it will return + * HXR_OK, otherwise it returns HXR_FAIL. + * + * pName - IN - name of the Property whose value is to be retrieved + * nValue - OUT - parameter into which the value of the Property is + * going to be returned + */ + STDMETHOD(GetInt64ByName) (THIS_ + const char* pName, + REF(INT64) nValue) const PURE; + + /************************************************************************ + * Method: + * IHXRegistry2::GetInt64ById + * Purpose: + * Retrieve a 64-bit INTEGER value from the registry given its id + * "ulId". If the Property is found, it will return HXR_OK, otherwise + * it returns HXR_FAIL. + * + * ulId - IN - unique id of the Property whose value is to be retrieved + * nValue - OUT - parameter into which the value of the Property is + * going to be returned + */ + STDMETHOD(GetInt64ById) (THIS_ + const UINT32 ulId, + REF(INT64) nValue) const PURE; + + /************************************************************************ + * Method: + * IHXRegistry2::SetInt64ByName + * Purpose: + * Modify a Property's INTEGER value in the registry given the + * Property's name "pName". If the value was set, it will return HXR_OK, + * otherwise it returns HXR_FAIL. + * + * pName - IN - name of the Property whose value is to be set + * nValue - IN - the new value of the Property which is going to be set + */ + STDMETHOD(SetInt64ByName) (THIS_ + const char* pName, + const INT64 nValue) PURE; + + /************************************************************************ + * Method: + * IHXRegistry2::SetInt64ById + * Purpose: + * Modify a Property's 64-bit INTEGER value in the registry given the + * its id "id". If the value was set, it will return HXR_OK, otherwise + * it returns HXR_FAIL. + * + * ulId - IN - unique id of the Property whose value is to be set + * nValue - IN - the new value of the Property which is going to be set + */ + STDMETHOD(SetInt64ById) (THIS_ + const UINT32 id, + const INT64 nValue) PURE; + + /************************************************************************ + * Method: + * IHXRegistry2::ModifyInt64ByName + * Purpose: + * Changes the 64-bit INTEGER value in the registry given its Property + * name "pName" and an amount to change it by. Modifies the value + * of the integer in the registry by the amount specified by "nDelta", + * setting nValue equal to the value after modification. If the + * Property is found, it will return HXR_OK, otherwise it + * returns HXR_FAIL. + * + * pName - IN - name of the Property whose value is to be retrieved + * nDelta - IN - amount to modify the named property by + * nValue - OUT - parameter into which the value of the Property is + * going to be returned, after modification + */ + STDMETHOD(ModifyInt64ByName) (THIS_ + const char* pName, + INT64 nDelta, + REF(INT64) nValue) PURE; + + /************************************************************************ + * Method: + * IHXRegistry2::ModifyInt64ById + * Purpose: + * Changes the 64-bit INTEGER value in the registry given its id + * "ulID and an amount to change it by. Modifies the value of the + * integer in the registry by the amount specified by "nDelta", + * setting nValue equal to the value after modification. If the + * Property is found, it will return HXR_OK, otherwise it + * returns HXR_FAIL. + * + * ulId - IN - unique id of the Property whose value is to be modified + * nDelta - IN - amount to modify the specified property by + * nValue - OUT - parameter into which the value of the Property is + * going to be returned, after modification + */ + STDMETHOD(ModifyInt64ById) (THIS_ + const UINT32 id, + INT64 nDelta, + REF(INT64) nValue) PURE; + + /************************************************************************ + * Method: + * IHXRegistry2::BoundedModifyInt64ByName + * Purpose: + * Changes the 64-bit INT val in the registry given its Property name + * "pName" and an amount to change it by and keeps the modified value + * within the bounds of the nMin and nMax values. Modifies the value + * of the integer in the registry by the amount specified by "nDelta", + * setting nValue equal to the value after modification, if the modified + * value is >= nMin and <= nMax. If either of these limits are violated + * the the resulting value stored in the registry is the value of the + * limit just violated. If the Property is found, it will return HXR_OK, + * otherwise it returns HXR_FAIL. + * + * pName - IN - name of the Property whose value is to be retrieved + * nDelta - IN - amount to modify the named property by + * nMin - IN - min value that the modified registry prop can have + * if the modified registry value < nMin then + * registry value = nMin + * nMax - IN - min value that the modified registry prop can have + * if the modified registry value > nMax then + * registry value = nMax + * nValue - OUT - parameter into which the value of the Property is + * going to be returned, after modification + * + * NOTE: + * the default values should b changed from INT_MIN/MAX to their + * appropriate 64-bit values + */ + STDMETHOD(BoundedModifyInt64ByName) (THIS_ + const char* pName, + INT64 nDelta, + REF(INT64) nValue, + INT64 nMin=INT64(INT_MIN), + INT64 nMax=INT64(INT_MAX)) PURE; + + /************************************************************************ + * Method: + * IHXRegistry2::BoundedModifyInt64ById + * Purpose: + * Changes the 64-bit INT val in the registry given its id "ulID" + * and an amount to change it by and keeps the modified value within + * the bounds of the nMin and nMax values. Modifies the value of the + * integer in the registry by the amount specified by "nDelta", + * setting nValue equal to the value after modification, if the modified + * value is >= nMin and <= nMax. If either of these limits are violated + * the the resulting value stored in the registry is the value of the + * limit just violated. If the Property is found, it will return HXR_OK, + * otherwise it returns HXR_FAIL. + * + * ulId - IN - unique id of the Property whose value is to be modified + * nDelta - IN - amount to modify the specified property by + * nMin - IN - min value that the modified registry prop can have + * if the modified registry value < nMin then + * registry value = nMin + * nMax - IN - min value that the modified registry prop can have + * if the modified registry value > nMax then + * registry value = nMax + * nValue - OUT - parameter into which the value of the Property is + * going to be returned, after modification + * + * NOTE: + * the default values should b changed from INT_MIN/MAX to their + * appropriate 64-bit values + */ + STDMETHOD(BoundedModifyInt64ById) (THIS_ + const UINT32 id, + INT64 nDelta, + REF(INT64) nValue, + INT64 nMin=INT64(INT_MIN), + INT64 nMax=INT64(INT_MAX)) PURE; + + /************************************************************************ + * Method: + * IHXRegistry2::SetAndReturnInt64ByName + * Purpose: + * Modify a Property's 64-bit INTEGER value in the registry given + * the Property's name "pName". If the Property is found, the previous + * value, prior to setting it, will be assigned to nOldValue. + * If the Property is found, it will return HXR_OK, otherwise it + * returns HXR_FAIL. + * + * pName - IN - name of the Property whose value is to be retrieved + * nValue - IN - the new value of the Property which is going to be set + * nOldValue - OUT - parameter into which the previous value of the + * Property is returned + */ + STDMETHOD(SetAndReturnInt64ByName) (THIS_ + const char* pName, + INT64 nValue, + REF(INT64) nOldValue) PURE; + + /************************************************************************ + * Method: + * IHXRegistry2::SetAndReturnInt64ById + * Purpose: + * Modify a Property's 64-bit INTEGER value in the registry given + * the Property's id "ulId". If the id is found, the previous + * value, prior to setting it, will be assigned to nOldValue. + * If the Property is found, it will return HXR_OK, otherwise it + * returns HXR_FAIL. + * + * pName - IN - name of the Property whose value is to be retrieved + * nValue - IN - the new value of the Property which is going to be set + * nOldValue - OUT - parameter into which the previous value of the + * Property is returned + */ + STDMETHOD(SetAndReturnInt64ById) (THIS_ + const UINT32 id, + INT64 nValue, + REF(INT64) nOldValue) PURE; + + + /************************************************************************ + * Method: + * IHXRegistry2::AddInt64Ref + * Purpose: + * Add a 64-bit INTEGER REFERENCE property with name in "pName" + * and value in "iValue" to the registry. This property allows the user + * to modify its contents directly, without having to go through the + * registry. + * + * pName - IN - name of the Property that is going to be added to + * the registry + * pValue - IN - the pointer of the integer value is what gets stored + * in the registry as the Integer Reference Property's + * value + */ + STDMETHOD_(UINT32, AddInt64Ref) (THIS_ + const char* pName, + INT64* pValue) PURE; + + /************************************************************************ + * Method: + * IHXRegistry2::GetInt64RefByName + * Purpose: + * Retrieve a 64-bit INTEGER REFERENCE property from the registry + * given its Property name "pName". If the Property is found it will + * return HXR_OK and pValue will be assigned the address of the integer + * (not the value of the integer, which can be obtained even for + * INTREFs via the GetIntByxxx methods.) Otherwise, it returns HXR_FAIL. + * + * pName - IN - name of the Property whose value is to be retrieved + * pValue - OUT - the address of the integer value + */ + STDMETHOD(GetInt64RefByName) (THIS_ + const char* pName, + REF(INT64*) pValue) const PURE; + + + /************************************************************************ + * Method: + * IHXRegistry2::GetInt64RefById + * Purpose: + * Retrieve a 64-bit INTEGER REFERENCE property from the registry + * given its id "ulId". If the Property is found it will return + * HXR_OK and pValue will be assigned the address of the integer + * (not the value of the integer, which can be obtained even for + * INTREFs via the GetIntByxxx methods.) Otherwise, it returns HXR_FAIL. + * + * pName - IN - name of the Property whose value is to be retrieved + * pValue - OUT - the address of the integer value + */ + STDMETHOD(GetInt64RefById) (THIS_ + const UINT32 id, + REF(INT64*) pValue) const PURE; + + + /************************************************************************ + * Method: + * IHXRegistry2::GetChildIdListByName + * Purpose: + * Get a array which enumerates all of the children under a + * property by id. + * + * pName - IN - name of the Property whose children are to be enumerated. + * pValues - OUT - array of unique Property id's. + * ulCount - OUT - size of the returned pValues array. + * + * Note: The array must be deleted by the user. + */ + STDMETHOD(GetChildIdListByName) (THIS_ + const char* pName, + REF(UINT32*) pValues, + REF(UINT32) ulCount) const PURE; + + + /************************************************************************ + * Method: + * IHXRegistry2::GetChildIdListById + * Purpose: + * Get a array which enumerates all of the children under a + * property by id. + * + * ulId - IN - unique id of the Property whose children are to be enumerated. + * pValues - OUT - array of unique Property id's. + * ulCount - OUT - size of the returned pValues array. + * + * Note: The pValues array must be deleted by the user. + */ + STDMETHOD(GetChildIdListById) (THIS_ + const UINT32 ulId, + REF(UINT32*) pValues, + REF(UINT32) ulCount) const PURE; + + + /************************************************************************ + * Method: + * IHXRegistry2::GetPropStatusById + * Purpose: + * Gets status of a property by id. + * + * ulId - IN - id of property to get child ids for. + * + * Returns: + * HXR_OK if property exists. + * HXR_PROP_DELETE_PENDING if property exists, but is delete-pending. + * HXR_FAIL if property does not exist. + */ + + STDMETHOD(GetPropStatusById) (THIS_ + const UINT32 ulId) const PURE; + + + /************************************************************************ + * Method: + * IHXRegistry2::GetPropStatusByName + * Purpose: + * Gets status of a property by name. + * + * szPropName - IN - name of property to get child ids for. + * + * Returns: + * HXR_OK if property exists. + * HXR_PROP_DELETE_PENDING if property exists, but is delete-pending. + * HXR_FAIL if property does not exist. + */ + + STDMETHOD(GetPropStatusByName) (THIS_ + const char* pName) const PURE; + +}; +// $EndPrivate. + + + +// $Private: +/* + * + * Interface: + * + * IHXDeletedPropResponse + * + * Purpose: + * + * Provides an alternative way to be notified about the deletion + * of objects from the registry. + * + * IID_IHXDeletePropResponse + * + * {00000609-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXDeletedPropResponse, 0x00000609, 0x901, 0x11d1, + 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXDeletedPropResponse + +DECLARE_INTERFACE_(IHXDeletedPropResponse, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXDeletedPropResponse methods + */ + + /************************************************************************ + * Method: + * IHXDeletedPropResponse::DeletedComposite + * Purpose: + * Provide notification that a COMPOSITE Property has been deleted + * from the registry. + * + * ulId - IN - unique id of the Property which has been deleted + * ulParentID - IN - unique id of the parent Property of the deleted item + * bIsParentNotify - IN - TRUE if this is a parent notification. + * pName - IN - name of the deleted Property (null-terminated) + */ + STDMETHOD(DeletedComposite)(THIS_ + const UINT32 ulId, + const UINT32 ulParentID, + const HXBOOL bIsParentNotify, + IHXBuffer* pName) PURE; + + /************************************************************************ + * Method: + * IHXDeletedPropResponse::DeletedInt + * Purpose: + * Provide notification that a INTEGER Property has been deleted. + * + * ulId - IN - unique id of the Property which has been deleted + * ulParentID - IN - unique id of the parent Property of the deleted item + * bIsParentNotify - IN - TRUE if this is a parent notification. + * pName - IN - name of the Property being deleted (null-terminated) + * nValue - IN - integer value of the Property which has been deleted + */ + STDMETHOD(DeletedInt) (THIS_ + const UINT32 ulId, + const UINT32 ulParentID, + const HXBOOL bIsParentNotify, + IHXBuffer* pName, + const INT32 nValue) PURE; + + /************************************************************************ + * Method: + * IHXDeletedPropResponse::DeletedIntRef + * Purpose: + * Provide notification that an INTEGER reference Property has + * been deleted from the registry. + * + * ulId - IN - unique id of the Property which has been deleted + * ulParentID - IN - unique id of the parent Property of the deleted item + * bIsParentNotify - IN - TRUE if this is a parent notification. + * pName - IN - name of the deleted Property (null-terminated) + * nValue - IN - integer value of the Property which has been deleted + * pValue - IN - the pointer of the integer reference Property + * which has been deleted + */ + STDMETHOD(DeletedIntRef) (THIS_ + const UINT32 ulId, + const UINT32 ulParentID, + const HXBOOL bIsParentNotify, + IHXBuffer* pName, + const INT32 nValue, + const INT32* pValue) PURE; + + /************************************************************************ + * Method: + * IHXDeletedPropResponse::DeletedString + * Purpose: + * Provide notification that a String Property has been deleted + * from the registry. + * + * ulId - IN - unique id of the deleted Property which has been deleted + * ulParentID - IN - unique id of the parent Property of the deleted item + * bIsParentNotify - IN - TRUE if this is a parent notification. + * pName - IN - name of the deleted Property (null-terminated) + * pValue - IN - value of the deleted Property + */ + STDMETHOD(DeletedString) (THIS_ + const UINT32 ulId, + const UINT32 ulParentID, + const HXBOOL bIsParentNotify, + IHXBuffer* pName, + IHXBuffer* pValue) PURE; + + /************************************************************************ + * Method: + * IHXDeletedPropResponse::DeletedBuffer + * Purpose: + * Provide notification that a Buffer Property has been deleted + * from the registry. + * + * ulId - IN - unique id of the Property which has been deleted + * ulParentID - IN - unique id of the parent Property of the deleted item + * bIsParentNotify - IN - TRUE if this is a parent notification. + * pName - IN - name of the deleted Property (null-terminated) + * pValue - IN - buffer value of the deleted Property + */ + STDMETHOD(DeletedBuffer) (THIS_ + const UINT32 ulId, + const UINT32 ulParentID, + const HXBOOL bIsParentNotify, + IHXBuffer* pName, + IHXBuffer* pValue) PURE; + + /************************************************************************ + * Method: + * IHXDeletedPropResponse::DeletedInt64 + * Purpose: + * Provide notification that a 64-bit integer Property has been + * deleted from the registry. + * + * Note: This is not used for IHXRegistry, but for IHXRegistry2. + * + * ulId - IN - unique id of the Property which has been deleted + * ulParentID - IN - unique id of the parent Property of the deleted item + * bIsParentNotify - IN - TRUE if this is a parent notification. + * pName - IN - name of the deleted Property (null-terminated) + * nValue - IN - 64-bit integer value of the deleted Property + */ + STDMETHOD(DeletedInt64) (THIS_ + const UINT32 ulId, + const UINT32 ulParentID, + const HXBOOL bIsParentNotify, + IHXBuffer* pName, + const INT64 nValue) PURE; + + /************************************************************************ + * Method: + * IHXDeletedPropResponse::DeletedInt64Ref + * Purpose: + * Provide notification that a 64-bit integer reference Property + * has been deleted from the registry. + * + * Note: This is not used for IHXRegistry, but for IHXRegistry2. + * + * ulId - IN - unique id of the Property which has been deleted + * ulParentID - IN - unique id of the parent Property of the deleted item + * bIsParentNotify - IN - TRUE if this is a parent notification. + * pName - IN - name of the deleted Property (null-terminated) + * nValue - IN - 64-bit integer value of the deleted Property + * pValue - IN - the pointer of the 64-bit integer reference Property + * which has been deleted + */ + STDMETHOD(DeletedInt64Ref) (THIS_ + const UINT32 ulId, + const UINT32 ulParentID, + const HXBOOL bIsParentNotify, + IHXBuffer* pName, + const INT64 nValue, + const INT64* pValue) PURE; + +}; +// $EndPrivate. + +#endif /* _HXMON_H_ */ diff --git a/amarok/src/engine/helix/helix-sp/helix-include/common/include/hxpiids.h b/amarok/src/engine/helix/helix-sp/helix-include/common/include/hxpiids.h new file mode 100644 index 00000000..b3921e04 --- /dev/null +++ b/amarok/src/engine/helix/helix-sp/helix-include/common/include/hxpiids.h @@ -0,0 +1,1055 @@ + +/* + * + * This software is released under the provisions of the GPL version 2. + * see file "COPYING". If that file is not available, the full statement + * of the license can be found at + * + * http://www.fsf.org/licensing/licenses/gpl.txt + * + * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved. + * + */ + +#ifndef _HXPRIVATEIIDS_H_ +#define _HXPRIVATEIIDS_H_ + +/* + * File: + * hxcorgui.h + * Description: + * Interfaces used by gui to get info from core + * Interfaces: + * IID_IHXCoreGuiHook: {00000000-b4c8-11d0-9995-00a0248da5f0} + + */ +DEFINE_GUID_ENUM(IID_IHXCoreGuiHook, 0x00000000, 0xb4c8, 0x11d0, 0x99, 0x95, +0x0, 0xa0, 0x24, 0x8d, 0xa5, 0xf0) +DEFINE_GUID_ENUM(IID_IHXInternalReset, 0x00000001, 0xb4c8, 0x11d0, 0x99, 0x95, +0x0, 0xa0, 0x24, 0x8d, 0xa5, 0xf0) + +/* + * File: + * hxbdwdth.h + * Description: + * Interface used by raffplin and rmffplin to support 3.0/4.0 style + * Bandwidth negotiation. + * Interfaces: + * IID_IHXBandwidthNegotiator: {00000100-b4c8-11d0-9995-00a0248da5f0} + */ + +DEFINE_GUID_ENUM(IID_IHXBandwidthNegotiator, 0x00000100, 0xb4c8, 0x11d0, 0x99, 0x95, 0x0, 0xa0, 0x24, 0x8d, 0xa5, 0xf0) +DEFINE_GUID_ENUM(IID_IHXBandwidthLister, 0x00000101, 0xb4c8, 0x11d0, 0x99, 0x95, 0x0, 0xa0, 0x24, 0x8d, 0xa5, 0xf0) + +/* + * File: + * hxphand.h + * + * Description: + * Interface for PluginHandler - non-IHX, just gimme the pointer + * Bandwidth negotiation. + * Interfaces: + * IID_IHXPluginHandler: {00000200-b4c8-11d0-9995-00a0248da5f0} + * IID_IHXPlugin2Handler:{00000201-b4c8-11d0-9995-00a0248da5f0} + */ + + +#ifndef _HXPLUGN_H_ +DEFINE_GUID_ENUM(IID_IHXPluginHandler, 0x00000200, 0xb4c8, 0x11d0, 0x99, 0x95, 0x0, 0xa0, 0x24, 0x8d, 0xa5, 0xf0) +DEFINE_GUID_ENUM(IID_IHXPlugin2Handler, 0x00000201, 0xb4c8, 0x11d0, 0x99, 0x95, 0x0, 0xa0, 0x24, 0x8d, 0xa5, 0xf0) +#endif +#ifndef _HXPHAND_H_ +DEFINE_GUID_ENUM(IID_IHXPlugin2HandlerEnumeratorInterface, 0x00000202, 0xb4c8, 0x11d0, 0x99, 0x95, 0x0, 0xa0, 0x24, 0x8d, 0xa5, 0xf0) +#endif + +/* + * File: + * hxrendr.h + * Description: + * Interfaces related to renderers. + * Interfaces: + * IID_IHXMediaPushdown {00000302-0901-11d1-8B06-00A024406D59} + */ + +DEFINE_GUID_ENUM(IID_IHXMediaPushdown, 0x00000302, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) + +/* + * File: + * rtspif.h + * Description: + * Interface for resend handling. + * Interfaces: + * IID_IHXPacketResend: {00000400-b4c8-11d0-9995-00a0248da5f0} + */ + +DEFINE_GUID_ENUM(IID_IHXPacketResend, 0x00000400, 0xb4c8, 0x11d0, 0x99, 0x95, 0x0, 0xa0, 0x24, 0x8d, 0xa5, 0xf0) + +/* + * File: + * rtspif.h + * Description: + * Interface for Context. + * Interfaces: + * IID_IHXRTSPContext: {00000401-b4c8-11d0-9995-00a0248da5f0} + */ +DEFINE_GUID_ENUM(IID_IHXRTSPContext, 0x00000401, 0xb4c8, 0x11d0, 0x99, 0x95, 0x0, 0xa0, 0x24, 0x8d, 0xa5, 0xf0) + +/* + * File: + * rtspif.h + * Description: + * Interface for communicating timestamp. + * Interfaces: + * IID_IHXTimeStampSync: {00000402-b4c8-11d0-9995-00a0248da5f0} + */ +DEFINE_GUID_ENUM(IID_IHXTimeStampSync, 0x00000402, 0xb4c8, 0x11d0, 0x99, 0x95, 0x0, 0xa0, 0x24, 0x8d, 0xa5, 0xf0) + +/* + * File: + * rtspif.h + * Description: + * Interface for servicing synchronization across transport streams + * Interfaces: + * IID_IHXTransportSyncServer: {16b420d0-f4d0-11d5-aac0-00102051b340} + */ +DEFINE_GUID_ENUM(IID_IHXTransportSyncServer, 0x16b420d0, 0xf4d0, 0x11d5, 0xaa, 0xc0, 0x0, 0x1, 0x2, 0x51, 0xb3, 0x40) + +/* + * File: altserv.h + * + * IID_IHXAlternateServerProxy: {00000403-b4c8-11d0-9995-00a0248da5f0} + */ +DEFINE_GUID_ENUM(IID_IHXAlternateServerProxy, 0x00000403, 0xb4c8, 0x11d0, 0x99, 0x95, 0x0, 0xa0, 0x24, 0x8d, 0xa5, 0xf0) + +/* + * File: altserv.h + * + * IID_IHXAlternateServerProxyResponse: {00000404-b4c8-11d0-9995-00a0248da5f0} + */ +DEFINE_GUID_ENUM(IID_IHXAlternateServerProxyResponse, 0x00000404, 0xb4c8, 0x11d0, 0x99, 0x95, 0x0, 0xa0, 0x24, 0x8d, 0xa5, 0xf0) + +/* + * File: + * pnupdate.h + * Description: + * Interface for getting file objects to the RealUpdate renderer + * Interfaces: + * IID_IHXUpdateRenderer: {00000500-b4c8-11d0-9995-00a0248da5f0} + */ +DEFINE_GUID_ENUM(IID_IHXUpdateRenderer, 0x00000500, 0xb4c8, 0x11d0, 0x99, 0x95, 0x0, 0xa0, 0x24, 0x8d, 0xa5, 0xf0) +/* From file pnrup/pub/pnrup.h, but related nonetheless. */ +DEFINE_GUID_ENUM(IID_IHXCHXRealUpdateResponse, 0x00000501, 0xb4c8, 0x11d0, 0x99, 0x95, 0x0, 0xa0, 0x24, 0x8d, 0xa5, 0xf0) +DEFINE_GUID_ENUM(IID_IHXUpdateRendererResponse, 0x00000502, 0xb4c8, 0x11d0, 0x99, 0x95, 0x0, 0xa0, 0x24, 0x8d, 0xa5, 0xf0) + +/* + * File: + * hxprefs.h + * Description: + * Interface for deleting prefernces + * Interfaces: + * IID_IHXPreferences3: {0x00000505-9011-11d1-8b60-a024406d59} + */ + +#ifndef _HXPREFS_H_ +DEFINE_GUID_ENUM(IID_IHXPreferences3, 0x00000505, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +#endif + +/* + * File: + * hxpnets.h + * Description: + * Cloaked HTTP Network Services. Creation of cloaked Client and + * Server sockets. + * Interfaces: + * IID_IHXCloakedNetworkServices {00000600-b4c8-11d0-9995-00a0248da5f0} + * IID_IHXHTTPProxy {00000601-b4c8-11d0-9995-00a0248da5f0} + * IID_IHXCloakedTCPSocket {00000602-b4c8-11d0-9995-00a0248da5f0} + */ +DEFINE_GUID_ENUM(IID_IHXCloakedNetworkServices, 0x00000600, 0xb4c8, 0x11d0, 0x99, 0x95, 0x0, 0xa0, 0x24, 0x8d, 0xa5, 0xf0) +DEFINE_GUID_ENUM(IID_IHXHTTPProxy, 0x00000601, 0xb4c8, 0x11d0, 0x99, 0x95, 0x0, 0xa0, 0x24, 0x8d, 0xa5, 0xf0) +DEFINE_GUID_ENUM(IID_IHXCloakedTCPSocket, 0x00000602, 0xb4c8, 0x11d0, 0x99, 0x95, 0x0, 0xa0, 0x24, 0x8d, 0xa5, 0xf0) + +/* + * File: + * hxmeta.h + * Description: + * Metafile creation & navigation Interfaces + * Interfaces: + * IID_IHXMetaTrack: {00000E01-0901-11d1-8B06-00A024406D59} + * IID_IHXMetaGroup: {00000E02-0901-11d1-8B06-00A024406D59} + * IID_IHXMetaLayout: {00000E03-0901-11d1-8B06-00A024406D59} + * IID_IHXMetaTuner: {00000E04-0901-11d1-8B06-00A024406D59} + * IID_IHXMetaFileFormatObject: {00000E05-0901-11d1-8B06-00A024406D59} + * IID_IHXMetaFileFormatResponse: {00000E06-0901-11d1-8B06-00A024406D59} + * IID_IHXSiteLayout: {00000E07-0901-11d1-8B06-00A024406D59} + */ +DEFINE_GUID_ENUM(IID_IHXMetaTrack, 0x00000E01, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXMetaGroup, 0x00000E02, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXMetaLayout, 0x00000E03, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXMetaTuner, 0x00000E04, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXMetaFileFormatObject, 0x00000E05, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXMetaFileFormatResponse, 0x00000E06, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXSiteLayout, 0x00000E07, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) + +/* + * File: + * hxsrc.h + * Description: + * Interfaces related to raw sources and sinks + * Interfaces: + * IID_IHXRawSourceObject: {00001000-0901-11d1-8B06-00A024406D59} + * IID_IHXRawSinkObject: {00001001-0901-11d1-8B06-00A024406D59} + * IID_IHXSourceFinderObject: {00001002-0901-11d1-8B06-00A024406D59} + * IID_IHXSourceFinderResponse: {00001003-0901-11d1-8B06-00A024406D59} + * IID_IHXSourceFinderFileResponse: {00001004-0901-11d1-8B06-00A024406D59} + */ +DEFINE_GUID_ENUM(IID_IHXRawSourceObject, 0x00001000, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXRawSinkObject, 0x00001001, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXSourceFinderObject, 0x00001002, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXSourceFinderResponse, 0x00001003, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXSourceFinderFileResponse, 0x00001004, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) + +/* + * File: + * hxtbuf.h + * Description: + * Interface related TimeStamped IHXBuffers + * Interfaces: + * IID_IHXTimeStampedBuffer: {00000700-b4c8-11d0-9995-00a0248da5f0} + */ +DEFINE_GUID_ENUM(IID_IHXTimeStampedBuffer, 0x00000700, 0xb4c8, 0x11d0, 0x99, 0x95, 0x0, 0xa0, 0x24, 0x8d, 0xa5, 0xf0) + +/* + * File: + * hxsmbw.h + * Description: + * Interface related the ASM Bandwidth Manager + * Interfaces: + * IID_IHXBandwidthManager: {00000800-b4c8-11d0-9995-00a0248da5f0} + * IID_IHXSourceBandwidthInfo: {00000801-b4c8-11d0-9995-00a0248da5f0} + * IID_IHXBandwidthManagerInput: {00000802-b4c8-11d0-9995-00a0248da5f0} + * IID_IHXStreamBandwidthNegotiator: {00000803-b4c8-11d0-9995-00a0248da5f0} + * IID_IHXStreamBandwidthBias: {00000804-b4c8-11d0-9995-00a0248da5f0} + * IID_IHXThinnableSource: {00000805-b4c8-11d0-9995-00a0248da5f0} + * IID_IHXBandwidthNudger: {00000806-b4c8-11d0-9995-00a0248da5f0} + * IID_IHXASMProps: {00000807-b4c8-11d0-9995-00a0248da5f0} + * IID_IHXAtomicRuleChange: {00000808-b4c8-11d0-9995-00a0248da5f0} + * IID_IHXAtomicRuleGather: {00000809-b4c8-11d0-9995-00a0248da5f0} + * IID_IHXPlayerState: {0000080A-b4c8-11d0-9995-00a0248da5f0} + */ +DEFINE_GUID_ENUM(IID_IHXBandwidthManager, 0x00000800, 0xb4c8, 0x11d0, 0x99, 0x95, 0x0, 0xa0, 0x24, 0x8d, 0xa5, 0xf0) +DEFINE_GUID_ENUM(IID_IHXSourceBandwidthInfo, 0x00000801, 0xb4c8, 0x11d0, 0x99, 0x95, 0x0, 0xa0, 0x24, 0x8d, 0xa5, 0xf0) +DEFINE_GUID_ENUM(IID_IHXBandwidthManagerInput, 0x00000802, 0xb4c8, 0x11d0, 0x99, 0x95, 0x0, 0xa0, 0x24, 0x8d, 0xa5, 0xf0) +DEFINE_GUID_ENUM(IID_IHXStreamBandwidthNegotiator, 0x00000803, 0xb4c8, 0x11d0, 0x99, 0x95, 0x0, 0xa0, 0x24, 0x8d, 0xa5, 0xf0) +DEFINE_GUID_ENUM(IID_IHXStreamBandwidthBias, 0x00000804, 0xb4c8, 0x11d0, 0x99, 0x95, 0x0, 0xa0, 0x24, 0x8d, 0xa5, 0xf0) +DEFINE_GUID_ENUM(IID_IHXThinnableSource, 0x00000805, 0xb4c8, 0x11d0, 0x99, 0x95, 0x0, 0xa0, 0x24, 0x8d, 0xa5, 0xf0) +DEFINE_GUID_ENUM(IID_IHXBandwidthNudger, 0x00000806, 0xb4c8, 0x11d0, 0x99, 0x95, 0x0, 0xa0, 0x24, 0x8d, 0xa5, 0xf0) +DEFINE_GUID_ENUM(IID_IHXASMProps, 0x00000807, 0xb4c8, 0x11d0, 0x99, 0x95, 0x0, 0xa0, 0x24, 0x8d, 0xa5, 0xf0) +DEFINE_GUID_ENUM(IID_IHXAtomicRuleChange, 0x00000808, 0xb4c8, 0x11d0, 0x99, 0x95, 0x0, 0xa0, 0x24, 0x8d, 0xa5, 0xf0) +DEFINE_GUID_ENUM(IID_IHXAtomicRuleGather, 0x00000809, 0xb4c8, 0x11d0, 0x99, 0x95, 0x0, 0xa0, 0x24, 0x8d, 0xa5, 0xf0) +DEFINE_GUID_ENUM(IID_IHXPlayerState, 0x0000080A, 0xb4c8, 0x11d0, 0x99, 0x95, 0x0, 0xa0, 0x24, 0x8d, 0xa5, 0xf0) + +/* + * File: + * rmavctrl.c + * Description: + * Video Control Interface + * Interface: + * IID_IHXVideoControl: {00000900-b4c8-11d0-9995-00a0248da5f0} + * + */ +DEFINE_GUID_ENUM(IID_IHXVideoControl, 0x00000900, 0xb4c8, 0x11d0, 0x99, 0x95, 0x0, 0xa0, 0x24, 0x8d, 0xa5, 0xf0) + +/* + * File: + * hxxres.h + * Description: + * Cross platform resource reading class. Reads resources directly + * from Win32 DLL's and EXEs on any platform. + * + * Interfaces: + * IID_IHXXResFile {00000A00-b4c8-11d0-9995-00a0248da5f0} + * IID_IHXXResource {00000A01-b4c8-11d0-9995-00a0248da5f0} + */ +DEFINE_GUID_ENUM(IID_IHXXResFile, 0x00000A00, 0xb4c8, 0x11d0, 0x99, 0x95, 0x0, 0xa0, 0x24, 0x8d, 0xa5, 0xf0) +DEFINE_GUID_ENUM(IID_IHXXResource, 0x00000A01, 0xb4c8, 0x11d0, 0x99, 0x95, 0x0, 0xa0, 0x24, 0x8d, 0xa5, 0xf0) + +/* + * File: + * hxrasyn.h + * Description: + * RealAudio Synchronization interface + * + * Interfaces: + * IID_IHXRealAudioSync {00000B00-b4c8-11d0-9995-00a0248da5f0} + */ +DEFINE_GUID_ENUM(IID_IHXRealAudioSync, 0x00000B00, 0xb4c8, 0x11d0, 0x99, 0x95, 0x0, 0xa0, 0x24, 0x8d, 0xa5, 0xf0) + +/* + * File: + * hxshtdn.h + * Description: + * Shut down all the plugins + * + * Interfaces: + * IID_IHXShutDownEverything {00000C00-b4c8-11d0-9995-00a0248da5f0} + */ +DEFINE_GUID_ENUM(IID_IHXShutDownEverything, 0x00000C00, 0xb4c8, 0x11d0, 0x99, + 0x95, 0x0, 0xa0, 0x24, 0x8d, 0xa5, 0xf0) + +/* + * File: + * hxgroup.h + * Description: + * Interface for precache manager + * Interfaces: + * IID_IHXPreCacheMgr: {00000E00-b4c8-11d0-9995-00a0248da5f0} + */ + +DEFINE_GUID_ENUM(IID_IHXPreCacheGroupMgr, 0x00000E00, 0xb4c8, 0x11d0, 0x99, 0x95, 0x0, 0xa0, 0x24, 0x8d, 0xa5, 0xf0) + +/* + * File: + * hxdataf.h + * Description: + * Interface for basic data file operations + * Interfaces: + * IID_IHXDataFileFactory: {00000F00-b4c8-11d0-9995-00a0248da5f0} + * IID_IHXDataFile: {00000F01-b4c8-11d0-9995-00a0248da5f0} + */ + +DEFINE_GUID_ENUM(IID_IHXDataFileFactory, 0x00000F00, 0xb4c8, 0x11d0, 0x99, 0x95, 0x0, 0xa0, 0x24, 0x8d, 0xa5, 0xf0) +DEFINE_GUID_ENUM(IID_IHXDataFile, 0x00000F01, 0xb4c8, 0x11d0, 0x99, 0x95, 0x0, 0xa0, 0x24, 0x8d, 0xa5, 0xf0) +DEFINE_GUID_ENUM(IID_IHXAsyncDataFile, 0x972bacc0, 0xaff, 0x11d7, 0xac, 0x45, 0x0, 0x1, 0x2, 0x51, 0xb3, 0x40) +#ifdef _SYMBIAN +DEFINE_GUID_ENUM(IID_IHXSymbFileSessionManager, 0x8a5c6080, 0xb16, 0x11d7, 0xac, 0x45, 0x0, 0x1, 0x2, 0x51, 0xb3, 0x40) +#endif // _SYMBIAN + +/* + * File: + * memfsys.h + * Description: + * Interface for basic data file operations + * Interfaces: + * IID_IHXMemoryFileContext: {00001000-b4c8-11d0-9995-00a0248da5f0} + * IID_IHXMemoryFileSystem: {00001001-b4c8-11d0-9995-00a0248da5f0} + */ + +DEFINE_GUID_ENUM(IID_IHXMemoryFileContext, 0x00001000, 0xb4c8, 0x11d0, 0x99, 0x95, 0x0, 0xa0, 0x24, 0x8d, 0xa5, 0xf0) +DEFINE_GUID_ENUM(IID_IHXMemoryFileSystem, 0x00001001, 0xb4c8, 0x11d0, 0x99, 0x95, 0x0, 0xa0, 0x24, 0x8d, 0xa5, 0xf0) +DEFINE_GUID_ENUM(IID_IHXMemoryFileSystem2, 0x00001002, 0xb4c8, 0x11d0, 0x99, 0x95, 0x0, 0xa0, 0x24, 0x8d, 0xa5, 0xf0) + +/* + * File: + * hxrsdbf.h + * Description: + * Interface for resend buffer management + * Interfaces: + * IID_IHXResendBufferControl: {00002B00-b4c8-11d0-9995-00a0248da5f0} + * IID_IHXResendBufferControl2: {D3103F1E-738F-4161-9B39-1DE7BC60E0E3} + */ + +DEFINE_GUID_ENUM(IID_IHXResendBufferControl, 0x00002B00, 0xb4c8, 0x11d0, 0x99, 0x95, 0x0, 0xa0, 0x24, 0x8d, 0xa5, 0xf0) +DEFINE_GUID_ENUM(IID_IHXResendBufferControl2, 0xd3103f1e, 0x738f, 0x4161, 0x9b, 0x39, 0x1d, 0xe7, 0xbc, 0x60, 0xe0, 0xe3) + +/* + * File: + * hxtset.h + * Description: + * Timeout settings interface + * Interfaces: + * IID_IHXTimeoutSettings: {950A6ED6-36D5-11d2-8F78-0060083BE561} + */ +#ifndef _HXTSET_H_ +DEFINE_GUID_ENUM(IID_IHXTimeoutSettings, 0x950a6ed6, 0x36d5, 0x11d2, 0x8f, 0x78, 0x0, 0x60, 0x8, 0x3b, 0xe5, 0x61) +#endif +/* + * File: + * hxspriv.h + * Description: + * Interface for descriptor registration + * Interfaces: + * IID_IHXDescriptorRegistration: {00001100-b4c8-11d0-9995-00a0248da5f0} + */ +DEFINE_GUID_ENUM(IID_IHXDescriptorRegistration, 0x00001100, 0xb4c8, 0x11d0, 0x99, 0x95, 0x0, 0xa0, 0x24, 0x8d, 0xa5, 0xf0) + +/* + * File: + * hxxrsmg.h + * Description: + * Interface for external resource manager + * Interfaces: + * IID_IHXExternalResourceManager: {00001200-b4c8-11d0-9995-00a0248da5f0} + * IID_IHXExternalResourceReader: {00001201-b4c8-11d0-9995-00a0248da5f0} + */ +DEFINE_GUID_ENUM(IID_IHXExternalResourceManager, 0x00001200, 0xb4c8, 0x11d0, 0x99, 0x95, 0x0, 0xa0, 0x24, 0x8d, 0xa5, 0xf0) +DEFINE_GUID_ENUM(IID_IHXExternalResourceReader, 0x00001201, 0xb4c8, 0x11d0, 0x99, 0x95, 0x0, 0xa0, 0x24, 0x8d, 0xa5, 0xf0) + +/* + * File: + * hxcredc.h + * Description: + * Interface for credential(username/password) cache + * + * Interfaces: + * IID_IHXCredentialsCache {00002B00-0901-11d1-8B06-00A024406D59} + */ + +DEFINE_GUID_ENUM(IID_IHXCredentialsCache, 0x00002B00, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) + + +/* + * File: + * hxxml.h + * Description: + * Interface for XML parser + * + * Interfaces: + * IID_IHXXMLParser {00002D00-0901-11d1-8B06-00A024406D59} + * IID_IHXXMLParserResponse {00002D01-0901-11d1-8B06-00A024406D59} + * IID_IHXXMLNamespaceParser {00002D02-0901-11d1-8B06-00A024406D59} + * IID_IHXXMLNamespaceResponse {00002D03-0901-11d1-8B06-00A024406D59} + */ +DEFINE_GUID_ENUM(IID_IHXXMLParser, 0x1a39e773, 0xfe28, 0x4ca9, 0x93, 0x18, 0x9d, 0x21, 0xee, 0x85, 0xe4, 0x7a) +DEFINE_GUID_ENUM(IID_IHXXMLParserResponse, 0x00002D01, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXXMLNamespaceParser, 0x00002D02, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXXMLNamespaceResponse,0x00002D03, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) + +/* + * {0000150*-***************************} DEPRECATED + * {0000150*-***************************} DEPRECATED + */ + + +/* + * File: + * clbwcont.h + * Description: + * Interface for controlling client bandwidth usage from the server + * + * Interfaces: + * IID_IHXClientBandwidthController: {00001600-b4c8-11d0-9995-00a0248da5f0 +} + */ + +DEFINE_GUID_ENUM(IID_IHXClientBandwidthController, 0x00001600, 0xb4c8, 0x11d0, 0x99 , 0x95, 0x0, 0xa0, 0x24, 0x8d, 0xa5, 0xf0) + + +/* File: + * embdsvcs.h + * + * Description: + * + * IRCAEmbeddedServices - RCA embedded player services + */ + +DEFINE_GUID_ENUM(IID_IRCAEmbeddedServices, + 0x00003807, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) + + +/* This IID is just reserved here, there is no interface for it */ +DEFINE_GUID_ENUM(IID_ServerPacket, 0x00001700, 0xb4c8, 0x11d0, 0x99, 0x95, 0x0, 0xa0, 0x24, 0x8d, 0xa5, 0xf0) + +#ifndef _HXPHOOK_H_ + +DEFINE_GUID_ENUM(IID_IHXPacketHookSink, 0x00002004, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXRecordTimeline, 0x00002005, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) + +#endif + +/* File: + * Used by licrequest.cpp + * + * Description: + * + * IHXLicenseRequestResponse - Response interface for async license + * request + */ +// {b8676e90-625c-11d4-968500c0f031f80f} +DEFINE_GUID_ENUM( IID_IHXLicenseRequestResponse, + 0xb8676e90, 0x625c, 0x11d4, 0x96, 0x85, 0x00, 0xc0, 0xf0, 0x31, 0xf8, 0x0f) + +/* File: + * Used by inetwork.cpp + * + * Description: + * + * IHXBufferedSocket - buffered writes for higher performing + * TCP + * + */ + +#ifndef _HXENGIN_H_ +DEFINE_GUID_ENUM(IID_IHXBufferedSocket, + 0x00001402, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +#endif /* _HXENGIN_H_ */ +/* File: + * Used by propreadonly.*(server) + * + * Description: + * + * IHXInternalSetPropReadOnly - Set/Unset registry properties to be read only + */ +// {b8676e90-625c-11d4-968500c0f0320910} +DEFINE_GUID_ENUM(IID_IHXInternalSetPropReadOnly, + 0xb8676e90, 0x625c, 0x11d4, 0x96, 0x85, 0x00, 0xc0, 0xf0, 0x32, 0x09, 0x10) + + + +/* File: + * hxcore.h + * + * Description: + * + * IHXClientStatisticsGranularity - Experimental interface to allow setting the rate at which statictics are generated + * IHXPlayerPresentation - Control over the player's current presentation + * IHXCoreMutex - Control the core mutex + * IHXMacBlitMutex - control Mac blitting to ensure no reentrancy + */ +// {b8676e90-625c-11d4-968500c0f0320910} +#ifndef _HXCORE_H_ +DEFINE_GUID_ENUM(IID_IHXClientStatisticsGranularity, 0x00000417, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXPlayerPresentation, 0x6de011a7, 0xef05, 0x417b, 0x93, 0x67, 0x6f, 0xe0, 0xe5, 0x43, 0x2, 0xd3) +DEFINE_GUID_ENUM(IID_IHXCoreMutex, 0x6de011a7, 0xef05, 0x417b, 0x93, 0x67, 0x6f, 0xe0, 0xe4, 0x44, 0x4, 0xd4) +DEFINE_GUID_ENUM(IID_IHXMacBlitMutex, 0x294e6de4, 0xfbc6, 0x4c06, 0xbb, 0x94, 0x95, 0xa9, 0x69, 0x37, 0x3b, 0x4d) +#endif /* _HXCORE_H_ */ + +/* + * File: + * hxpreftr.h + * + * Description: + * + * IHXPreferredTransportManager + * IHXPreferredTransport + * IHXPreferredTransportSink + * + * Interfaces: + * IID_IHXPreferredTransportManager: {00003700-0901-11d1-8B06-00A024406D59} + * IID_IHXPreferredTransport: {00003701-0901-11d1-8B06-00A024406D59} + * IID_IHXPreferredTransportSink: {00003702-0901-11d1-8B06-00A024406D59} + */ +DEFINE_GUID_ENUM(IID_IHXPreferredTransportManager, 0x00003700, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXPreferredTransport, 0x00003701, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXPreferredTransportSink, 0x00003702, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) + +/* + * File: + * used by inetwork.cpp, rtspserv.cpp + * + * Description: + An IID to distiguish LocalBoundSocket from IHXTCPSocket + */ +DEFINE_GUID_ENUM(IID_IHXPLocalBoundSocket, 0xc1f00ad1, 0x61e7, 0x11d5, 0xa5, 0x30, + 0x0, 0x2, 0xb3, 0x6, 0xdc, 0x93) + +/* + * File: + * http.cpp, adminfo.cpp + * + * Description: + * File object interface to support out of band http post data + */ +#ifndef _HXFILES_H_ +DEFINE_GUID_ENUM(IID_IHXPostDataHandler, 0x0222a1b5, 0x49fc, 0x47e2, 0xb6, + 0x90, 0x98, 0xbe, 0xfa, 0x0a, 0x58, 0x8e) +#endif + +/* + * File: + * ihxlist.h - COM list containers + * + * Description: + * + * Interfaces: + * IID_IHXList + * IID_IHXListIterator + */ +#ifndef _IHXLIST_H_ +DEFINE_GUID_ENUM(IID_IHXList, 0x1599cb17, 0x9db4, 0x4f8a, 0x86, 0x5a, 0x78, 0xa5, 0x4e, 0xff, 0xbc, 0x60) +DEFINE_GUID_ENUM(IID_IHXListIterator, 0xe7ad1443, 0xf6bf, 0x4b0e, 0xbc, 0x00, 0x8f, 0x03, 0xc0, 0xb1, 0x27, 0x24) +#endif /* _IHXLIST_H_ */ + + +/* + * File: + * hxrtsp2.h - New RTSP stuff (tommy's new parser, etc.) + * + * Description: + * The faster and more efficient (memcpy-wise) RTSP parser, MIME header + * handler and related clases. + * + * Interfaces: + * IID_IHXMIMEParameter + * IID_IHXMIMEField + * IID_IHXMIMEHeader + * IID_IHXRTSPMessage + * IID_IHXRTSPRequestMessage + * IID_IHXRTSPResponseMessage + * IID_IHXRTSPConsumer + * IID_IHXRTSPProtocolResponse + * IID_IHXRTSPProtocol + */ +#ifndef _HXRTSP2_H_ +DEFINE_GUID_ENUM(IID_IHXMIMEParameter, 0x8ae57afa, 0x902c, 0x4327, 0x8c, 0x00, 0x31, 0x57, 0x85, 0xcd, 0xc2, 0x43) +DEFINE_GUID_ENUM(IID_IHXMIMEField, 0x946eed6, 0x0501, 0x4fc3, 0x94, 0xbb, 0x30, 0x23, 0xa0, 0xe5, 0x23, 0xc7) +DEFINE_GUID_ENUM(IID_IHXMIMEHeader, 0x97e681a3, 0xbd71, 0x4b81, 0x8f, 0xa0, 0x81, 0x19, 0x9e, 0x79, 0x9a, 0xe7) +DEFINE_GUID_ENUM(IID_IHXRTSPMessage, 0x1bff98ab, 0xe5c9, 0x459d, 0x80, 0xee, 0xb8, 0x0d, 0x20, 0xe4, 0xf3, 0x0e) +DEFINE_GUID_ENUM(IID_IHXRTSPRequestMessage, 0xddb0e73f, 0x0d5a, 0x4fd1, 0xbd, 0xc8, 0x95, 0x7f, 0x0d, 0x87, 0x2a, 0x33) +DEFINE_GUID_ENUM(IID_IHXRTSPResponseMessage, 0x876baec2, 0xec9e, 0x41dc, 0x8c, 0xb6, 0xe8, 0x74, 0xb6, 0x0f, 0xba, 0xd6) +DEFINE_GUID_ENUM(IID_IHXRTSPInterleavedPacket, 0x4d737eff, 0x8218, 0x4762, 0xac, 0xe3, 0xfc, 0xf2, 0x7c, 0x08, 0xf9, 0x16) +DEFINE_GUID_ENUM(IID_IHXRTSPConsumer, 0xda62eb99, 0x2120, 0x410a, 0x98, 0x66, 0x90, 0xf7, 0xec, 0x9c, 0xc1, 0x5d) +DEFINE_GUID_ENUM(IID_IHXRTSPProtocol, 0x29d8eebf, 0x5597, 0x410b, 0xa2, 0x90, 0x81, 0x81, 0xbe, 0x1e, 0x24, 0x30) +DEFINE_GUID_ENUM(IID_IHXRTSPProtocolResponse, 0xbf646cd4, 0x922c, 0x4b9c, 0xac, 0x92, 0x96, 0xe7, 0x74, 0xde, 0x56, 0x39) +#endif + +/* + * File: + * hxsdp.h - New SDP stuff + * + * Description: + * The faster and more efficient (memcpy-wise) SDP parser + * handler and related clases. + * + * Interfaces: + * IID_IHXSDPAttrib + * IID_IHXSDPTimeDesc + * IID_IHXSDPMedia + * IID_IHXSDP + */ +#ifndef _HXSDP_H_ +DEFINE_GUID_ENUM(IID_IHXSDPAttrib, 0x538d82b4, 0xa284, 0x4527, 0xae, 0xf5, 0x47, 0xce, 0x87, 0xd8, 0x31, 0xcc) +DEFINE_GUID_ENUM(IID_IHXSDPTimeDesc, 0xffc46a97, 0x965d, 0x41cb, 0xa3, 0x50, 0xde, 0x2c, 0x48, 0x6e, 0x72, 0x90) +DEFINE_GUID_ENUM(IID_IHXSDPMedia, 0x30069090, 0xa6dc, 0x47c5, 0x89, 0x1f, 0x9e, 0xea, 0x9c, 0x86, 0x0a, 0x6b) +DEFINE_GUID_ENUM(IID_IHXSDP, 0x6fe2a32b, 0x36fa, 0x4fd4, 0x88, 0x09, 0x1c, 0x5a, 0xda, 0x9b, 0xea, 0x15) +#endif + +#ifndef _HXLISTP_H_ +DEFINE_GUID_ENUM(IID_IHXListIteratorPrivate, 0x2d3f1b24, 0x6e4e, 0x4fdc, 0x9e, 0x53, 0x4b, 0x9b, 0xd7, 0x3f, 0x3f, 0xed) +DEFINE_GUID_ENUM(IID_IHXVectorIteratorPrivate, 0x58b7d31f, 0x2261, 0x4608, 0xa6, 0x34, 0x4e, 0x98, 0xdc, 0x8f, 0xa8, 0x4f) +DEFINE_GUID_ENUM(IID_IHXRingBufferIteratorPrivate, 0x87d7ca44, 0x7a75, 0x11d7, 0x8b, 0x4a, 0x00, 0xd0, 0xb7, 0x10, 0x35, 0xfe) +#endif + +/* + * File: + * hxmms.h - MMS protocol + * + * Description: + * MMS (Windows Media) protocol. + * + * Interfaces: + * IID_ + */ +#ifndef _HXMMS2_H_ +DEFINE_GUID_ENUM(IID_IHXMMSMessage, 0x3cb3f6dc, 0xcb9a, 0x4a37, 0x99, 0x3a, 0xd1, 0x81, 0x7a, 0xdd, 0x91, 0x00) +DEFINE_GUID_ENUM(IID_IHXMMSConsumer, 0x3cb3f6dc, 0xcb9a, 0x4a37, 0x99, 0x3a, 0xd1, 0x81, 0x7a, 0xdd, 0x91, 0x01) +DEFINE_GUID_ENUM(IID_IHXMMSMessageClientHello, 0x3cb3f6dc, 0xcb9a, 0x4a37, 0x99, 0x3a, 0xd1, 0x81, 0x7a, 0xdd, 0x91, 0x02) +DEFINE_GUID_ENUM(IID_IHXMMSMessageServerHello, 0x3cb3f6dc, 0xcb9a, 0x4a37, 0x99, 0x3a, 0xd1, 0x81, 0x7a, 0xdd, 0x91, 0x03) +DEFINE_GUID_ENUM(IID_IHXMMSMessageSetup1, 0x3cb3f6dc, 0xcb9a, 0x4a37, 0x99, 0x3a, 0xd1, 0x81, 0x7a, 0xdd, 0x91, 0x04) +DEFINE_GUID_ENUM(IID_IHXMMSMessageSetup2, 0x3cb3f6dc, 0xcb9a, 0x4a37, 0x99, 0x3a, 0xd1, 0x81, 0x7a, 0xdd, 0x91, 0x05) +DEFINE_GUID_ENUM(IID_IHXMMSMessageSetup3, 0x3cb3f6dc, 0xcb9a, 0x4a37, 0x99, 0x3a, 0xd1, 0x81, 0x7a, 0xdd, 0x91, 0x06) +DEFINE_GUID_ENUM(IID_IHXMMSMessageSetup4, 0x3cb3f6dc, 0xcb9a, 0x4a37, 0x99, 0x3a, 0xd1, 0x81, 0x7a, 0xdd, 0x91, 0x07) +DEFINE_GUID_ENUM(IID_IHXMMSMessageSetup5, 0x3cb3f6dc, 0xcb9a, 0x4a37, 0x99, 0x3a, 0xd1, 0x81, 0x7a, 0xdd, 0x91, 0x08) +DEFINE_GUID_ENUM(IID_IHXMMSMessageSetup6, 0x3cb3f6dc, 0xcb9a, 0x4a37, 0x99, 0x3a, 0xd1, 0x81, 0x7a, 0xdd, 0x91, 0x09) +DEFINE_GUID_ENUM(IID_IHXMMSMessageSetup7, 0x3cb3f6dc, 0xcb9a, 0x4a37, 0x99, 0x3a, 0xd1, 0x81, 0x7a, 0xdd, 0x91, 0x0a) +DEFINE_GUID_ENUM(IID_IHXMMSMessageSetup8, 0x3cb3f6dc, 0xcb9a, 0x4a37, 0x99, 0x3a, 0xd1, 0x81, 0x7a, 0xdd, 0x91, 0x0b) +DEFINE_GUID_ENUM(IID_IHXMMSMessageSubscribe, 0x3cb3f6dc, 0xcb9a, 0x4a37, 0x99, 0x3a, 0xd1, 0x81, 0x7a, 0xdd, 0x91, 0x0c) +DEFINE_GUID_ENUM(IID_IHXMMSMessageSubscribeAck, 0x3cb3f6dc, 0xcb9a, 0x4a37, 0x99, 0x3a, 0xd1, 0x81, 0x7a, 0xdd, 0x91, 0x0d) +DEFINE_GUID_ENUM(IID_IHXMMSMessagePlay, 0x3cb3f6dc, 0xcb9a, 0x4a37, 0x99, 0x3a, 0xd1, 0x81, 0x7a, 0xdd, 0x91, 0x0e) +DEFINE_GUID_ENUM(IID_IHXMMSMessagePlayAck, 0x3cb3f6dc, 0xcb9a, 0x4a37, 0x99, 0x3a, 0xd1, 0x81, 0x7a, 0xdd, 0x91, 0x0f) +DEFINE_GUID_ENUM(IID_IHXMMSMessageStop, 0x3cb3f6dc, 0xcb9a, 0x4a37, 0x99, 0x3a, 0xd1, 0x81, 0x7a, 0xdd, 0x91, 0x10) +DEFINE_GUID_ENUM(IID_IHXMMSMessageEndData, 0x3cb3f6dc, 0xcb9a, 0x4a37, 0x99, 0x3a, 0xd1, 0x81, 0x7a, 0xdd, 0x91, 0x11) +DEFINE_GUID_ENUM(IID_IHXMMSMessageFastPlay, 0x3cb3f6dc, 0xcb9a, 0x4a37, 0x99, 0x3a, 0xd1, 0x81, 0x7a, 0xdd, 0x91, 0x12) +DEFINE_GUID_ENUM(IID_IHXMMSMessageFastPlayAck, 0x3cb3f6dc, 0xcb9a, 0x4a37, 0x99, 0x3a, 0xd1, 0x81, 0x7a, 0xdd, 0x91, 0x13) +DEFINE_GUID_ENUM(IID_IHXMMSMessageStats, 0x3cb3f6dc, 0xcb9a, 0x4a37, 0x99, 0x3a, 0xd1, 0x81, 0x7a, 0xdd, 0x91, 0x14) +DEFINE_GUID_ENUM(IID_IHXMMSMessageGoodbye, 0x3cb3f6dc, 0xcb9a, 0x4a37, 0x99, 0x3a, 0xd1, 0x81, 0x7a, 0xdd, 0x91, 0x15) +DEFINE_GUID_ENUM(IID_IHXMMSMessagePing, 0x3cb3f6dc, 0xcb9a, 0x4a37, 0x99, 0x3a, 0xd1, 0x81, 0x7a, 0xdd, 0x91, 0x16) +DEFINE_GUID_ENUM(IID_IHXMMSFileHeader, 0x3cb3f6dc, 0xcb9a, 0x4a37, 0x99, 0x3a, 0xd1, 0x81, 0x7a, 0xdd, 0x91, 0x17) +DEFINE_GUID_ENUM(IID_IHXMMSInterleavedPacket, 0x3cb3f6dc, 0xcb9a, 0x4a37, 0x99, 0x3a, 0xd1, 0x81, 0x7a, 0xdd, 0x91, 0x18) +DEFINE_GUID_ENUM(IID_IHXMMSProtocol, 0x3cb3f6dc, 0xcb9a, 0x4a37, 0x99, 0x3a, 0xd1, 0x81, 0x7a, 0xdd, 0x91, 0x19) +DEFINE_GUID_ENUM(IID_IHXMMSProtocolResponse, 0x3cb3f6dc, 0xcb9a, 0x4a37, 0x99, 0x3a, 0xd1, 0x81, 0x7a, 0xdd, 0x91, 0x1a) +DEFINE_GUID_ENUM(IID_IHXWM60PlayerLiveHack, 0x3cb3f6dc, 0xcb9a, 0x4a37, 0x99, 0x3a, 0xd1, 0x81, 0x7a, 0xdd, 0x91, 0x1b) +DEFINE_GUID_ENUM(IID_IHXMMSMessageLiveHeader, 0x3cb3f6dc, 0xcb9a, 0x4a37, 0x99, 0x3a, 0xd1, 0x81, 0x7a, 0xdd, 0x91, 0x1c) +#endif + +/* + * + * File: + * hxstktrc.h -- Stack Trace interface for debugging + * + * Description: + * This interface provides the user the ability to get the stack + * trace at any point in the program by a call to GetTrace(). + */ +DEFINE_GUID_ENUM(IID_IHXStackTrace, 0x00004702, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) + +/* + * File: + * hxformt.h + * Description: + * Interfaces related to file and broadcast format plugins. + * Interfaces: + * IID_IHXSetPlayParam {0x503c212c-413f-478b-9fc8daa7b145b8a9} + * IID_IHXSetPlayParamResponse {0x750008af-5588-4838-85faaa203a32c799} + * IID_IHXSeekByPacket {0x171c3c4e-c4ea-46fd-b47b-c3b82dbb9517} + * IID_IHXSeekByPacketResponse {0xe978476d-6c99-4dc6-9279-7525c693dc34} + */ +#ifndef _HXFORMT_H_ +DEFINE_GUID_ENUM(IID_IHXSetPlayParam, 0x503c212c, 0x413f, 0x478b, 0x9f, 0xc8, 0xda, 0xa7, 0xb1, 0x45, 0xb8, 0xa9) +DEFINE_GUID_ENUM(IID_IHXSetPlayParamResponse, 0x750008af, 0x5588, 0x4838, 0x85, 0xfa, 0xaa, 0x20, 0x3a, 0x32, 0xc7, 0x99) + +DEFINE_GUID_ENUM(IID_IHXSeekByPacket, 0x171c3c4e, 0xc4ea, 0x46fd, 0xb4, 0x7b, 0xc3, 0xb8, 0x2d, 0xbb, 0x95, 0x17) +DEFINE_GUID_ENUM(IID_IHXSeekByPacketResponse, 0xe978476d, 0x6c99, 0x4dc6, 0x92, 0x79, 0x75, 0x25, 0xc6, 0x93, 0xdc, 0x34) +#endif + +/* + * + * File: + * hxpac.h + * + */ +DEFINE_GUID_ENUM(IID_IHXProxyAutoConfig, 0x00004800, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXProxyAutoConfigCallback, 0x00004801, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXProxyAutoConfigAdviseSink, 0x00004802, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) + +/* + * + * File: + * hxfwmgr.h + * + */ +DEFINE_GUID_ENUM(IID_IHXFirewallControlManager, 0x00004900, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) + +/* + * + * File: + * mmsrsend.h + * + */ +#ifndef MMSRSEND_H +DEFINE_GUID_ENUM(IID_IHXMMSResendManager, 0x4676c245, 0xad4e, 0x45b9, 0xa8, 0xdd, 0x60, 0xa8, 0xe4, 0x7b, 0xf6, 0x05) +DEFINE_GUID_ENUM(IID_IHXMMSResendResponse, 0xa581f3a4, 0xc508, 0x4800, 0xba, 0xdc, 0xb4, 0xf1, 0xcd, 0x11, 0x07, 0xb6) +#endif + + +/* + * + * File: + * hxdist_lic_requester.h + * + */ +DEFINE_GUID_ENUM(IID_IHXDistributedLicenseRequester, 0x00005702, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXDistributedLicenseRequestStatus, 0x00005703, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) + +/* + * File: hxauto.h + */ +DEFINE_GUID_ENUM(IID_IHXHTTPAutoStream, + 0x60b68af1, 0x9f0, 0x11d3, 0x8b, 0x57, 0x0, 0x90, 0x27, 0x42, 0xc8, 0xa7) + +/* + * File: hxflche.h + */ +DEFINE_GUID_ENUM(IID_IHXFileSystemCache, + 0x18d8a780, 0xf90d, 0x11d2, 0xad, 0x55, 0x0, 0xc0, 0xf0, 0x31, 0xc2, 0x36) + +/* + * File: ihxident.h + */ +DEFINE_GUID_ENUM(IID_IHXProductIdentity, + 0xae7eb8a0, 0x32dc, 0x11d2, 0x8a, 0xc0, 0x0, 0xc0, 0x4f, 0xee, 0x3a, 0x97) +DEFINE_GUID_ENUM(IID_IHXProductIdentity2, + 0xae7eb8a0, 0x32dc, 0x11d2, 0x7a, 0xc0, 0x0, 0xc0, 0x4f, 0xee, 0x3a, 0x98) + +/* + * File: ihxperplex.h + */ +DEFINE_GUID_ENUM(IID_IHXPerplex, + 0xb0f17ee1, 0xdd86, 0x11d2, 0xb3, 0x39, 0x0, 0xc0, 0xf0, 0x31, 0x87, 0x98) + +/* + * File: ihxdefpackethookhlp.h + */ +DEFINE_GUID_ENUM(IID_IHXDefaultPacketHookHelper, + 0x50540475, 0x79e8, 0x471e, 0xb6, 0x89, 0x3, 0xc4, 0x20, 0xea, 0xc5, 0xe1) + +/* + * File: rvsink.h + */ +DEFINE_GUID_ENUM(IID_IRVSinkMgr, + 0xa57fd431, 0x4599, 0x11d4, 0xb4, 0x23, 0x0, 0x90, 0x27, 0x43, 0xf, 0x4c) + +/* + * File: hxpcmkr.h + */ +DEFINE_GUID_ENUM(IID_IHXPaceMaker, + 0x1223eff0, 0x4dad, 0x11d5, 0xa9, 0x38, 0x0, 0x1, 0x2, 0x51, 0xb3, 0x40) + +DEFINE_GUID_ENUM(IID_IHXPaceMakerResponse, + 0xb4d81b50, 0x4dac, 0x11d5, 0xa9, 0x38, 0x0, 0x1, 0x2, 0x51, 0xb3, 0x40) + + +/* + * File: hxfswch.h + */ +DEFINE_GUID_ENUM(IID_IHXFileSwitcher, + 0x55cc55b0, 0x1ba, 0x11d4, 0x95, 0x23, 0x0, 0x90, 0x27, 0x42, 0xc9, 0x23) + +/* + * File: hxatmzr.h + */ +DEFINE_GUID_ENUM(IID_IHXAtomizationCommander, + 0xafdcd230, 0x4b, 0x11d4, 0x95, 0x23, 0x0, 0x90, 0x27, 0x42, 0xc9, 0x23) + +DEFINE_GUID_ENUM(IID_IHXAtomizerResponse, + 0x72bc0330, 0x41, 0x11d4, 0x95, 0x23, 0x0, 0x90, 0x27, 0x42, 0xc9, 0x23) + +#if defined(_STATICALLY_LINKED) && defined(WIN32) && !defined(_NO_COM) +/* + * File: ddraw.h + */ +//DEFINE_GUID_ENUM(IID_IDirectDraw2, +// 0xB3A6F3E0, 0x2B43, 0x11CF, 0xA2, 0xDE, 0x00, 0xAA, 0x00, 0xB9, 0x33, 0x56) + +#endif + +/* + * File: hxacodec.h + */ +DEFINE_GUID_ENUM(IID_IHXAudioDecoder, + 0x26139eda, 0x98d8, 0x437b, 0x92, 0x29, 0x94, 0x9d, 0x3b, 0xef, 0x25, 0x51) + +DEFINE_GUID_ENUM(IID_IHXAudioEncoder, + 0x56ff66f4, 0xa6c5, 0x42aa, 0xb2, 0x67, 0xc2, 0x96, 0xdd, 0x7, 0x5d, 0xa3) + +DEFINE_GUID_ENUM(IID_IHXAudioEncoderConfigurator, + 0xb9919b52, 0x54ff, 0x4099, 0x98, 0x4a, 0x53, 0x3e, 0x75, 0xb5, 0x78, 0xb9) + +DEFINE_GUID_ENUM(IID_IHXCodecOldStyleAuthenticate, + 0xeae2fec7, 0xac4b, 0x4d15, 0xb1, 0xdc, 0xa, 0x94, 0x54, 0x9c, 0xf2, 0xc9) + +DEFINE_GUID_ENUM(IID_IQueryDecoderUnit, + 0x5720a4df, 0x7b79, 0x47ae, 0x84, 0x94, 0x6b, 0x46, 0x52, 0xf2, 0xd4, 0xad) + +/* + * File: ihxsimpleencryptionservice.h + */ +DEFINE_GUID_ENUM(IID_IHXSimpleEncryptionService, + 0x2a3e75dd, 0xe43e, 0x41f3, 0xad, 0xe3, 0xac, 0x9b, 0x95, 0xc2, 0x79, 0xd1) + +/* + * File: rmacrptg.h + */ +DEFINE_GUID_ENUM(IID_IHXCryptograph, + 0x78b12021, 0x9250, 0x11d4, 0x8f, 0x56, 0xe0, 0xde, 0x51, 0xc1, 0x0, 0x0) + + +#ifdef HELIX_FEATURE_HTTP_SERVICE +/* + * File: hxhttp.h + */ +DEFINE_GUID_ENUM(IID_IHXHttp, + 0xe7ddb0b0, 0x9846, 0x11d1, 0xa5, 0xfe, 0x0, 0x60, 0x97, 0xe5, 0x7c, 0x78); +DEFINE_GUID_ENUM(IID_IHXHttp2, + 0xe7ddb0b0, 0x9846, 0x11d1, 0xb5, 0xfe, 0x0, 0x60, 0x97, 0xe5, 0x7c, 0x87); +DEFINE_GUID_ENUM(IID_IHXHttpResponse, + 0xbdf0bb0, 0x9847, 0x11d1, 0xa5, 0xfe, 0x0, 0x60, 0x97, 0xe5, 0x7c, 0x78); +DEFINE_GUID_ENUM(IID_IHXHttpResponse2, + 0xe7ddb0b0, 0x9846, 0x11d1, 0xb5, 0xfe, 0x0, 0x60, 0x95, 0xe5, 0x7d, 0x87); +DEFINE_GUID_ENUM(IID_IHXHttpInitialize, + 0x083912d6, 0x6c54, 0x4e13, 0xa6, 0x99, 0xc5, 0xb3, 0x06, 0xd7, 0x61, 0xae); +#endif + + +/* + * File: ihxhurl.h + */ +DEFINE_GUID_ENUM(IID_IHXHurl, + 0xb16c0330, 0xb2ec, 0x11d1, 0x8e, 0xfd, 0x0, 0x60, 0x8, 0x3b, 0xe5, 0x61) + +/* + * File: icmdbcst.h + */ +DEFINE_GUID_ENUM(IID_IHXCmdBroadcaster, + 0x00000700, 0x6050, 0x1450, 0x7c, 0xea, 0x7, 0x0b, 0x18, 0xf8, 0x6a, 0x71) + +DEFINE_GUID_ENUM(IID_IHXCmdObserver, + 0x00000700, 0x6050, 0x1450, 0x7c, 0xea, 0x7, 0x0b, 0x18, 0xa8, 0x6a, 0x72) + +/* + * File: dunitprvt.h + */ +DEFINE_GUID_ENUM(IID_IPrepareDecoderUnit, + 0x655dd923, 0x3ab, 0x45b2, 0xb9, 0x1e, 0xf, 0x46, 0x57, 0xec, 0x9d, 0xc3) + +/* + * pxffmcod.h + */ +DEFINE_GUID_ENUM(IID_IHXRealPixFileFormatCodec, + 0x309f2d21, 0xcc0a, 0x11d2, 0x8a, 0x53, 0x10, 0xf, 0xf0, 0x0, 0x0, 0x0) + +/* + * pxrndcod.h + */ +DEFINE_GUID_ENUM(IID_IHXRealPixRendererCodec, + 0x10552e61, 0xc6f1, 0x11d2, 0x8a, 0x4f, 0x28, 0x90, 0x9a, 0x0, 0x0, 0x0) + +/* + * File: hxrtsp2.h + */ +DEFINE_GUID_ENUM(IID_IHXRTSPAggregateEventStats, 0x4ed8aabe, 0x5597, 0x410b, 0xa2, 0x90, 0x81, 0x81, 0xbe, 0x1e, 0x24, 0x01) +DEFINE_GUID_ENUM(IID_IHXRTSPEventsSink, 0x26a03092, 0x49d6, 0x483a, 0xa1, 0x2d, 0x4d, 0x69, 0xe2, 0x88, 0x56, 0xd0) +DEFINE_GUID_ENUM(IID_IHXRTSPEventsManager, 0x84988f28, 0x9264, 0x46ba, 0x8b, 0x5a, 0xb2, 0x6b, 0xd6, 0xf1, 0x63, 0x72) + +/* + * File: rtspif.h + */ +DEFINE_GUID_ENUM(IID_IHXRTSPServerPauseResponse, 0xbf646cd4, 0x922c, 0x4b9c, 0xac, 0x92, 0x96, 0xe7, 0x74, 0xde, 0x56, 0x02) + +/* + * File: hxsdp.h + */ +DEFINE_GUID_ENUM(IID_IHXSDPAggregateStats, 0x4ed8aabe, 0x5597, 0x410b, 0xa2, 0x90, 0x81, 0x81, 0xbe, 0x1e, 0x24, 0x03) + +/* + * File: hxrecord.h + */ +DEFINE_GUID_ENUM(IID_IHXRecordManager, 0x9b7854dd, 0x92c8, 0x42c6, 0x93, 0x6c, 0x56, 0x5e, 0xc3, 0x73, 0xe2, 0xad) +DEFINE_GUID_ENUM(IID_IHXRecordService, 0xf2f8c09a, 0xa607, 0x40c9, 0x9c, 0x26, 0x48, 0x3b, 0xbb, 0xc4, 0xa0, 0x86) +DEFINE_GUID_ENUM(IID_IHXRecordSource, 0xe007f531, 0x4ec9, 0x4555, 0x8e, 0xc5, 0x1d, 0x58, 0x49, 0x99, 0x4, 0xdf) + + +/* + * File: hxstats.h + */ + +DEFINE_GUID_ENUM(IID_IHXSessionStats, 0x3c02c47f, 0x6f44, 0x47fd, 0xb6, 0x25, 0xc8, 0x1a, 0x2b, 0xf0, 0x5d, 0x4f) +DEFINE_GUID_ENUM(IID_IHXClientStats, 0x83ce47e8, 0x3ebe, 0x450a, 0xbf, 0x49, 0x66, 0xd8, 0x80, 0x94, 0xe5, 0x16) +DEFINE_GUID_ENUM(IID_IHXClientStats2, 0x5dad23df, 0xa442, 0x4fe9, 0x89, 0xcd, 0x39, 0xb1, 0x3d, 0xa5, 0xca, 0x51) +DEFINE_GUID_ENUM(IID_IHXClientStatsSink, 0xfdc6d1aa, 0xd78e, 0x40b0, 0xa3, 0xe6, 0xb7, 0xd6, 0xdf, 0x30, 0x38, 0x3b) +DEFINE_GUID_ENUM(IID_IHXClientStatsManager, 0xe0ece2b8, 0xa94b, 0x4eb4, 0xaf, 0xd, 0x17, 0x7d, 0x5, 0x3b, 0x2e, 0xd6) + +/* + * File: server_stats.h + */ + +DEFINE_GUID_ENUM(IID_IHXClientStatsTimerControl, 0x793ab631, 0xb654, 0x4aae, 0x99, 0xa3, 0xd, 0xf8, 0x72, 0x58, 0xc3, 0x63) + +/* + * File: ihxplayerstateobserver.h + */ + +DEFINE_GUID_ENUM(IID_IHXPlayerStateObserver, 0x466c7e42, 0x81d1, 0x4746, 0xa6, 0xd4, 0x9e, 0x9f, 0x99, 0xba, 0x7b, 0xb5) + + +/* + * File: hxqosinfo.h + */ + +DEFINE_GUID_ENUM(IID_IHXQoSTransportAdaptationInfo, 0x213645c5, 0x3a56, 0x4945, 0xa8, 0xa9, 0x67, 0x2, 0xbb, 0x56, 0x4, 0xb6) +DEFINE_GUID_ENUM(IID_IHXQoSSessionAdaptationInfo, 0xaed09295, 0xa71, 0x4520, 0x9d, 0x7f, 0xbf, 0xa9, 0xb5, 0xa9, 0x72, 0x45) +DEFINE_GUID_ENUM(IID_IHXQoSApplicationAdaptationInfo, 0x207e23e5, 0xf71f, 0x4a18, 0xb7, 0xd0, 0xf4, 0xf8, 0x65, 0xa2, 0x5, 0x8b) + +/* + * File: hxcache2.h + */ + +DEFINE_GUID_ENUM(IID_IHXCacheObjectResponse, + 0x00002E11, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXCache2, + 0x00002E0E, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +DEFINE_GUID_ENUM(IID_IHXCacheObject, + 0x00002E10, 0x901, 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59) +/* + * File: hxrssmgr.h + */ + +DEFINE_GUID_ENUM(IID_IHXRSSReport, 0xbd2f1e35, 0x83e4, 0x4459, 0x94, 0x30, 0x2e, 0xb6, 0x37, 0xad, 0xbe, 0x17) +DEFINE_GUID_ENUM(IID_IHXRSSManager, 0x66fb8dc5, 0xd3d4, 0x4aaf, 0x8f, 0x88, 0x56, 0xb3, 0xc8, 0xa7, 0xd3, 0x1f) + +/* + * File: hxlogoutputs.h + */ +DEFINE_GUID_ENUM(IID_IHXLogOutput, 0x88ec448c, 0x136c, 0x4733, 0x94, 0x3, 0xdc, 0x43, 0x40, 0x9d, 0xca, 0x29) +DEFINE_GUID_ENUM(IID_IHXLogFileOutput, 0xc5c9c037, 0x53fa, 0x4478, 0x8b, 0xfd, 0xe4, 0x9e, 0x58, 0x8b, 0xf2, 0x1f) + +/* File: + * hxbufctl.h + * + * Description: + * Client buffer control interfaces + */ +DEFINE_GUID_ENUM(IID_IHXBufferControl, 0x68b2aef9, 0x1384, 0x46ec, 0xa4, 0xd0, 0x0, 0x68, 0xa, 0x7d, 0xbb, 0xae) +DEFINE_GUID_ENUM(IID_IHXWatermarkBufferControl, 0x68b2aef9, 0x1384, 0x46ec, 0xa4, 0xd0, 0x0, 0x68, 0xa, 0x7d, 0xbb, 0xaf) +DEFINE_GUID_ENUM(IID_IHXTransportBufferLimit, 0x68b2aef9, 0x1384, 0x46ec, 0xa4,0xd0, 0x0, 0x68, 0xa, 0x7d, 0xbb, 0xb0) + +/* File: + * ihxaccesspoint.h + * + * Description: + * Access Point interfaces + */ +DEFINE_GUID_ENUM(IID_IHXAccessPointConnectResponse, 0x9e9ca2d6, 0xcbfe, 0x40f8, 0x94, + 0xfd, 0x38, 0xf4, 0xeb, 0x5d, 0xf8, 0xf) +DEFINE_GUID_ENUM(IID_IHXAccessPointManager, 0x9e9ca2d6, 0xcbfe, 0x40f8, 0x94, + 0xfd, 0x38, 0xf4, 0xeb, 0x5d, 0xf8, 0x10) +DEFINE_GUID_ENUM(IID_IHXAccessPointSelectorResponse, + 0x9e9ca2d6, 0xcbfe, 0x40f8, 0x94, + 0xfd, 0x38, 0xf4, 0xeb, 0x5d, 0xf8, 0x11) +DEFINE_GUID_ENUM(IID_IHXAccessPointSelector, 0x9e9ca2d6, 0xcbfe, 0x40f8, 0x94, + 0xfd, 0x38, 0xf4, 0xeb, 0x5d, 0xf8, 0x12) + +/* File: + * ihxrateadaptctl.h + * + * Description: + * Rate adaptation control interfaces + */ +DEFINE_GUID_ENUM(IID_IHXClientRateAdaptControl, 0x44f5ac8c, 0x654c, 0x414e, 0x9d, + 0x18, 0xe7, 0xa4, 0x80, 0x90, 0x70, 0x9) + +/* File: + * ihxtranstime.h + * + * Description: + * Transport time interfaces + */ +DEFINE_GUID_ENUM(IID_IHXTransportTimeSink, +0xdb838ab3, 0x4637, 0x41e0, 0xbc, 0x3f, 0xed, 0x3, 0xb6, 0xa6, 0x84, 0xc1) + +DEFINE_GUID_ENUM(IID_IHXTransportTimeManager, +0xd0a5ba01, 0xedfb, 0x4741, 0xb7, 0x9, 0x88, 0x4e, 0x68, 0x5d, 0x2d, 0x8) + +/* File: + * ihx3gpp.h + * + * Description: + * Interfaces for 3GPP functionality + */ +DEFINE_GUID_ENUM(IID_IHX3gppNADU, +0x8069baaf, 0x777a, 0x4cf0, 0x90, 0x3, 0xd2, 0x6d, 0x68, 0xb8, 0xd7, 0x86) + +/* File: + * hxservnet.h + * + * Description: + * Interfaces for server-specific networking services. + */ + +DEFINE_GUID_ENUM(IID_IHXLocalBoundNetServices, + 0x669b1aaa, 0xdda3, 0x4892, 0x8d, 0x58, 0x6d, 0xc, 0xd5, 0xea, 0xcb, 0x19) + + +/* File: + * hxprivstats.h + * + * Description: + * Private client stats functionality interfaces. + */ +DEFINE_GUID_ENUM(IID_IHXPrivateClientStats, +0x2399cc75, 0x3c38, 0x4680, 0xac, 0xa5, 0xcd, 0x66, 0x3a, 0x64, 0x70, 0x1a) + +#endif /* _HXPRIVATEIIDS_H_ */ + + diff --git a/amarok/src/engine/helix/helix-sp/helix-include/common/include/hxplugn.h b/amarok/src/engine/helix/helix-sp/helix-include/common/include/hxplugn.h new file mode 100644 index 00000000..80461212 --- /dev/null +++ b/amarok/src/engine/helix/helix-sp/helix-include/common/include/hxplugn.h @@ -0,0 +1,1424 @@ + +/* + * + * This software is released under the provisions of the GPL version 2. + * see file "COPYING". If that file is not available, the full statement + * of the license can be found at + * + * http://www.fsf.org/licensing/licenses/gpl.txt + * + * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved. + * + */ + +#ifndef _HXPLUGN_H_ +#define _HXPLUGN_H_ + +#include "hxcom.h" +#include "hxplugncompat.h" + +/* + * Forward declarations of some interfaces defined or used here-in. + */ +typedef _INTERFACE IUnknown IUnknown; +typedef _INTERFACE IHXPlugin IHXPlugin; +typedef _INTERFACE IHXPluginEnumerator IHXPluginEnumerator; +// $Private: +typedef _INTERFACE IHXPluginSearchEnumerator IHXPluginSearchEnumerator; +typedef _INTERFACE IHXPluginChallenger IHXPluginChallenger; +// $EndPrivate. +typedef _INTERFACE IHXBuffer IHXBuffer; +typedef _INTERFACE IHXValues IHXValues; +typedef _INTERFACE IHXPreferences IHXPreferences; + +// Plugin Types. +#define PLUGIN_FILESYSTEM_TYPE "PLUGIN_FILE_SYSTEM" +#define PLUGIN_FILEFORMAT_TYPE "PLUGIN_FILE_FORMAT" +#define PLUGIN_FILEWRITER_TYPE "PLUGIN_FILE_WRITER" +#define PLUGIN_METAFILEFORMAT_TYPE "PLUGIN_METAFILE_FORMAT" +#define PLUGIN_RENDERER_TYPE "PLUGIN_RENDERER" +#define PLUGIN_DEPACKER_TYPE "PLUGIN_DEPACKER" +#define PLUGIN_REVERTER_TYPE "PLUGIN_REVERTER" +#define PLUGIN_BROADCAST_TYPE "PLUGIN_BROADCAST" +#define PLUGIN_STREAM_DESC_TYPE "PLUGIN_STREAM_DESC" +#define PLUGIN_ALLOWANCE_TYPE "PLUGIN_ALLOWANCE" +#define PLUGIN_PAC_TYPE "PLUGIN_PAC" +#define PLUGIN_CLASS_FACTORY_TYPE "PLUGIN_CLASS_FACT" + +#define PLUGIN_CLASS "PluginType" +#define PLUGIN_FILENAME "PluginFilename" +#define PLUGIN_REGKEY_ROOT "PluginHandlerData" +#define PLUGIN_PLUGININFO "PluginInfo" +#define PLUGIN_GUIDINFO "GUIDInfo" +#define PLUGIN_NONHXINFO "NonHXDLLs" +#define PLUGIN_IDENTIFIER "Plugin#" +// This may no longer be needed... +#define PLUGINDIRECTORYHASH "DirHash" +// XXXAH WHO is defining this ... I think I know.. +#define PLUGIN_DESCRIPTION2 "Description" +// XXXAH WHO is defining this ... I think I know.. +#define PLUGIN_FILE_HASH "FileHash" +#define PLUGIN_INDEX "IndexNumber" +#define PLUGIN_FILENAMES "FileInfo" +#define PLUGIN_COPYRIGHT2 "Copyright" +#define PLUGIN_LOADMULTIPLE "LoadMultiple" +#define PLUGIN_VERSION "Version" +#define PLUGIN_FILESYSTEMSHORT "FileShort" +#define PLUGIN_FILESYSTEMPROTOCOL "FileProtocol" +#define PLUGIN_FILEMIMETYPES "FileMime" +#define PLUGIN_FILEEXTENSIONS "FileExtensions" +#define PLUGIN_FILEOPENNAMES "FileOpenNames" +#define PLUGIN_RENDERER_MIME "RendererMime" +#define PLUGIN_RENDERER_GRANULARITY "Renderer_Granularity" +#define PLUGIN_DEPACKER_MIME "DepackerMime" +#define PLUGIN_REVERTER_MIME "ReverterMime" +#define PLUGIN_BROADCASTTYPE "BroadcastType" +#define PLUGIN_STREAMDESCRIPTION "StreamDescription" + +// +#define PLUGIN_GUID_RESPONSE "MainGuid" +#define PLUGIN_FACTORY_GUIDS "" // These are comma delimited. + +// + +#define PLUGIN_NUM_PLUGINS "NumPlugins" +#define PLUGIN_FILE_CHECKSUM "DLLCheckSum" +#define PLUGIN_DLL_SIZE "DLLSize" +#define PLUGIN_HAS_FACTORY "DLLHasFactory" + +/**************************************************************************** + * + * Function: + * + * HXCreateInstance() + * + * Purpose: + * + * Function implemented by all plugin DLL's to create an instance of + * any of the objects supported by the DLL. This method is similar to + * Window's CoCreateInstance() in its purpose, except that it only + * creates objects from this plugin DLL. + * + * NOTE: Aggregation is never used. Therefore an outer unknown is + * not passed to this function, and you do not need to code for this + * situation. + * + */ +#ifdef _MAC_CFM +#pragma export on +#endif + +STDAPI HXCreateInstance + ( + IUnknown** /*OUT*/ ppIUnknown + ); + +#ifdef _MAC_CFM +#pragma export off +#endif + + +/**************************************************************************** + * + * Function: + * + * HXShutdown() + * + * Purpose: + * + * Function implemented by all plugin DLL's to free any *global* + * resources. This method is called just before the DLL is unloaded. + * + */ +#ifdef _MAC_CFM +#pragma export on +#endif + +STDAPI HXShutdown(void); + +#ifdef _MAC_CFM +#pragma export off +#endif + + +/**************************************************************************** + * + * Interface: + * + * IHXPlugin + * + * Purpose: + * + * Interface exposed by a plugin DLL to allow inspection of objects + * supported by the plugin DLL. + * + * IID_IHXPlugin: + * + * {00000C00-0901-11d1-8B06-00A024406D59} + * + */ + +DEFINE_GUID(IID_IHXPlugin, 0x00000C00, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXPlugin + +DECLARE_INTERFACE_(IHXPlugin, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXPlugin methods + */ + + /************************************************************************ + * Method: + * IHXPlugin::GetPluginInfo + * Purpose: + * Returns the basic information about this plugin. Including: + * + * bMultipleLoad Whether or not this plugin can be instantiated + * multiple times. All File Formats must set + * this value to TRUE. The only other type of + * plugin that can specify bMultipleLoad=TRUE is + * a filesystem plugin. Any plugin that sets + * this flag to TRUE must not use global variables + * of any type. + * + * Setting this flag to TRUE implies that you + * accept that your plugin may be instantiated + * multiple times (possibly in different + * address spaces). Plugins are instantiated + * multiple times only in the server (for + * performance reasons). + * + * An example of a plugin, that must set this + * flag to FALSE is a filesystem plugin that + * uses a single TCP connection to communicate + * with a database. + * + * pDescription which is used in about UIs (can be NULL) + * pCopyright which is used in about UIs (can be NULL) + * pMoreInfoURL which is used in about UIs (can be NULL) + * ulVersionNumber The version of this plugin. + */ + STDMETHOD(GetPluginInfo) (THIS_ + REF(HXBOOL) /*OUT*/ bMultipleLoad, + REF(const char*) /*OUT*/ pDescription, + REF(const char*) /*OUT*/ pCopyright, + REF(const char*) /*OUT*/ pMoreInfoURL, + REF(ULONG32) /*OUT*/ ulVersionNumber) PURE; + + /************************************************************************ + * Method: + * IHXPlugin::InitPlugin + * Purpose: + * Initializes the plugin for use. This interface must always be + * called before any other method is called. This is primarily needed + * so that the plugin can have access to the context for creation of + * IHXBuffers and IMalloc. + */ + STDMETHOD(InitPlugin) (THIS_ + IUnknown* /*IN*/ pContext) PURE; + +}; + + +/**************************************************************************** + * + * Interface: + * + * IHXPluginEnumerator + * + * Purpose: + * + * provide methods to enumerate through all the plugins installed + * + * IID_IHXPluginEnumerator: + * + * {00000C01-0901-11d1-8B06-00A024406D59} + * + */ + +DEFINE_GUID(IID_IHXPluginEnumerator, 0x00000C01, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXPluginEnumerator + +DECLARE_INTERFACE_(IHXPluginEnumerator, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXPluginEnumerator methods + */ + + /************************************************************************ + * Method: + * IHXPluginEnumerator::GetNumOfPlugins + * + * Purpose: + * return the number of plugins available + * + */ + STDMETHOD_(ULONG32,GetNumOfPlugins) (THIS) PURE; + + /************************************************************************ + * Method: + * IHXPluginEnumerator::GetPlugin + * Purpose: + * Return an instance (IUnknown) of the plugin + * + */ + STDMETHOD(GetPlugin) (THIS_ + ULONG32 /*IN*/ ulIndex, + REF(IUnknown*) /*OUT*/ pPlugin) PURE; + +}; + +/**************************************************************************** + * + * Interface: + * + * IHXPluginGroupEnumerator + * + * Purpose: + * + * Provide a way to enumerate through all of the plugins which + * implement a specific interface. + * + * IID_IHXPluginGroupEnumerator: + * + * {00000C02-0901-11d1-8B06-00A024406D59} + * + */ + +DEFINE_GUID(IID_IHXPluginGroupEnumerator, 0x00000C02, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXPluginGroupEnumerator + +#define CLSID_IHXPluginGroupEnumerator IID_IHXPluginGroupEnumerator + +DECLARE_INTERFACE_(IHXPluginGroupEnumerator, IUnknown) +{ + /* + * IUnknown methods + */ + + /* + * IHXPluginGroupEnumerator methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /****************************************************************** + * Method: + * IHXPluginGroupEnumerator::Init + * + * Purpose: + * tell the group enumerator which interface to group the plugins + * into, this method must be called before the other methods can + * be called. + * + */ + STDMETHOD(Init) (THIS_ + REFIID iid) PURE; + + + /****************************************************************** + * Method: + * IHXPluginGroupEnumerator::GetNumOfPlugins + * + * Purpose: + * return the number of plugins available that support a + * particular interface. + * + */ + STDMETHOD_(ULONG32,GetNumOfPlugins) (THIS) PURE; + + + /****************************************************************** + * Method: + * IHXPluginGroupEnumerator::GetPlugin + * Purpose: + * Return an instance (IUnknown) of the plugin + * + */ + STDMETHOD(GetPlugin) (THIS_ + UINT32 /*IN*/ ulIndex, + REF(IUnknown*) /*OUT*/ pPlugin) PURE; + +}; + + + +// $Private: +/**************************************************************************** + * + * Interface: + * + * IHXPluginSearchEnumerator + * + * Purpose: + * Walk through the result set of a plugin search + * + * {3244B391-42D4-11d4-9503-00902790299C} + * + */ + +DEFINE_GUID( IID_IHXPluginSearchEnumerator, + 0x3244b391, 0x42d4, 0x11d4, 0x95, 0x3, 0x0, 0x90, 0x27, 0x90, 0x29, 0x9c); + +#undef INTERFACE +#define INTERFACE IHXPluginSearchEnumerator + +DECLARE_INTERFACE_( IHXPluginSearchEnumerator, IUnknown ) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface)(THIS_ REFIID riid, void** ppvObj) PURE; + STDMETHOD_(ULONG32,AddRef)(THIS) PURE; + STDMETHOD_(ULONG32,Release)(THIS) PURE; + + + /* + * IHXPluginSearchEnumerator methods + */ + + /************************************************************************ + * Method: + * IHXPluginSearchEnumerator::GetNumPlugins + * + * Purpose: + * Returns numbers of plugins found during search + * + */ + STDMETHOD_( UINT32, GetNumPlugins)(THIS) PURE; + + /************************************************************************ + * Method: + * IHXPluginSearchEnumerator::GoHead + * + * Purpose: + * Moves the iterator to the beginning of the collection + * + */ + STDMETHOD_(void, GoHead)(THIS) PURE; + + /************************************************************************ + * Method: + * IHXPluginSearchEnumerator::GetNextPlugin + * + * Purpose: + * Returns an instance of the next plugin in the collection + * + */ + STDMETHOD(GetNextPlugin)( THIS_ REF(IUnknown*) pIUnkResult, + IUnknown* pIUnkOuter ) PURE; + + /************************************************************************ + * Method: + * IHXPluginSearchEnumerator::GetNextPluginInfo + * + * Purpose: + * Gets information about the next plugin in the list + * + */ + STDMETHOD(GetNextPluginInfo)( THIS_ REF(IHXValues*) pRetValues ) PURE; + + + /************************************************************************ + * Method: + * IHXPluginSearchEnumerator::GetPluginAt + * + * Purpose: + * Returns an instance of a plugin at a specific index in the list + * + */ + STDMETHOD(GetPluginAt)( THIS_ UINT32 index, + REF(IUnknown*) pIUnkResult, + IUnknown* pIUnkOuter ) PURE; + + /************************************************************************ + * Method: + * IHXPluginSearchEnumerator:: + * + * Purpose: + * Returns information about a plugin at a specific index in the list + * + */ + STDMETHOD(GetPluginInfoAt)( THIS_ UINT32 index, + REF(IHXValues*) pRetValues ) PURE; + +}; +// $EndPrivate. + + +/**************************************************************************** + * + * Interface: + * + * IHXPluginReloader + * + * Purpose: + * + * Tells the client core to reload all plugins. + * + * IID_IHXPluginReloader: + * + * {00000C03-0901-11d1-8B06-00A024406D59} + * + */ + +DEFINE_GUID(IID_IHXPluginReloader, 0x00000C03, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXPluginReloader + +DECLARE_INTERFACE_(IHXPluginReloader, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXPluginReloader methods + */ + + /************************************************************************ + * Method: + * IHXPluginReloader::ReloadPlugins + * Purpose: + * Causes the client core to reload all plugins. + * + */ + STDMETHOD(ReloadPlugins) (THIS) PURE; +}; + + + +/**************************************************************************** + * + * Interface: + * + * IHXPluginFactory + * + * Purpose: + * + * This interface is implemented by a plugin in order to have more then + * one "RMA plugin" in a single DLL. I.e., a plugin author could + * use this interface to have 3 different file format plugins in + * a single DLL. + * + * IID_IHXPluginFactory: + * + * {00000C04-0901-11d1-8B06-00A024406D59} + * + */ + +DEFINE_GUID(IID_IHXPluginFactory, 0x00000C04, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXPluginFactory + +DECLARE_INTERFACE_(IHXPluginFactory, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXPluginFactory methods + */ + + /***************************************************************** + * Method: + * IHXPluginFactory::GetNumPlugins + * Purpose: + * Report the number of Plugins within the DLL. + * + * Parameters: + */ + STDMETHOD_(UINT16, GetNumPlugins) (THIS) PURE; + + /***************************************************************** + * Method: + * IHXPluginFactory::GetPlugin + * Purpose: + * Returns an IUnknown interface to the requested plugin. + * + * Parameters: + */ + + STDMETHOD(GetPlugin) (THIS_ + UINT16 uIndex, + IUnknown** pPlugin) PURE; +}; + +// $Private: +/**************************************************************************** + * + * Interface: + * + * IHXPluginChallenger + * + * Purpose: + * + * This interface is implemented by a plugin in order to allow + * verification of a plugin's authenticity, by issuing a challenge + * and receiving a challenge response. + * + * IID_IHXPluginChallenger: + * + * {00000C05-0901-11d1-8B06-00A024406D59} + * + */ + +DEFINE_GUID(IID_IHXPluginChallenger, 0x00000C05, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXPluginChallenger + +typedef struct _HXTimeval HXTimeval; + +DECLARE_INTERFACE_(IHXPluginChallenger, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXPluginChallenger methods + */ + + /***************************************************************** + * Method: + * IHXPluginChallenger::Challenge + * Purpose: + * Challenge the plugin's authenticity. Returns a challenge + * response which the caller can use to verify that the + * plugin is authentic. + * + * Parameters: + * tVal A time value which may be used to create the + * challenge response. + */ + STDMETHOD(Challenge) (THIS_ + HXTimeval /*IN*/ tVal, + REF(IHXBuffer*) /*OUT*/ pResponse) PURE; +}; + + +/**************************************************************************** + * + * Interface: + * + * IHXPluginQuery + * + * Purpose: + * + * Queries the plugin handler for information on plugins. + * + * IID_IHXPluginQuery: + * + * {00000C06-0901-11d1-8B06-00A024406D59} + * + */ + +#define PLUGIN_FILE_PATH "PluginFilePath" + +#define PLUGIN_PATH "PlgPath" +#define PLUGIN_DESCRIPTION "PlgDesc" +#define PLUGIN_COPYRIGHT "PlgCopy" +#define PLUGIN_MOREINFO "PlgMore" +#define PLUGIN_MIMETYPES "PlgMime" +#define PLUGIN_EXTENSIONS "PlgExt" +#define PLUGIN_OPENNAME "PlgOpen" +#define PLUGIN_MULTIPLE "PlgMult" + +DEFINE_GUID(IID_IHXPluginQuery, 0x00000C06, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXPluginQuery + +DECLARE_INTERFACE_(IHXPluginQuery, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXPluginQuery methods + */ + + /************************************************************************ + * Method: + * IHXPluginQuery::GetNumPluginsGivenGroup + * + * Purpose: + * Gets the number of plugins associated with a particular class id. + * + */ + STDMETHOD(GetNumPluginsGivenGroup) (THIS_ REFIID riid, + REF(UINT32) /*OUT*/ unNumPlugins) PURE; + + /************************************************************************ + * Method: + * IHXPluginQuery::GetPluginInfo + * + * Purpose: + * Gets the info of a particular plugin. + * + */ + STDMETHOD(GetPluginInfo) (THIS_ REFIID riid, + UINT32 unIndex, REF(IHXValues*) /*OUT*/ Values) PURE; +}; +// $EndPrivate. + + +/**************************************************************************** + * + * Interface: + * + * IHXGenericPlugin + * + * Purpose: + * + * Interface exposed by a plugin DLL to inform the client / server core + * that your plugin wishes to have InitPlugin called immediately. + * + * IID_IHXGenericPlugin: + * + * {00000C09-0901-11d1-8B06-00A024406D59} + * + */ + +DEFINE_GUID(IID_IHXGenericPlugin, 0x00000C09, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXGenericPlugin + +DECLARE_INTERFACE_(IHXGenericPlugin, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXGenericPlugin methods + */ + + STDMETHOD(IsGeneric) (THIS_ + REF(HXBOOL) /*OUT*/ bIsGeneric) PURE; +}; + + +DEFINE_GUID(IID_IHXPluginHandler, 0x00000200, 0xb4c8, 0x11d0, 0x99, 0x95, 0x0, 0xa0, 0x24, 0x8d, 0xa5, 0xf0); + +DEFINE_GUID(IID_IHXPlugin2Handler, 0x00000201, 0xb4c8, 0x11d0, 0x99, 0x95, 0x0, 0xa0, 0x24, 0x8d, 0xa5, 0xf0); + +#undef INTERFACE +#define INTERFACE IHXPlugin2Handler + +DECLARE_INTERFACE_(IHXPlugin2Handler, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXPlugin2Handler Methods + */ + + /************************************************************************ + * Method: + * IHXPlugin2Handler::Init + * + * Purpose: + * Specifies the context and sets the pluginhandler in motion. + * + */ + STDMETHOD(Init) (THIS_ IUnknown* pContext) PURE; + + /************************************************************************ + * Method: + * IHXPlugin2Handler::GetNumPlugins2 + * + * Purpose: + * Gets the info of a particular plugin. + * + */ + STDMETHOD_(ULONG32,GetNumOfPlugins2) (THIS) PURE; + + /************************************************************************ + * Method: + * IHXPlugin2Handler::GetPluginInfo + * + * Purpose: + * Gets the info of a particular plugin. + * + */ + STDMETHOD(GetPluginInfo) (THIS_ + UINT32 unIndex, + REF(IHXValues*) /*OUT*/ Values) PURE; + + /************************************************************************ + * Method: + * IHXPlugin2Handler::FlushCache() + * + * Purpose: + * Flushes the LRU cache -- Unloads all DLLs from memory + * which currenltly have a refcount of 0. + */ + + STDMETHOD(FlushCache) (THIS) PURE; + + /************************************************************************ + * Method: + * IHXPlugin2Handler::SetCacheSize + * + * Purpose: + * This function sets the size of the Cache. The cache is + * initally set to 1000KB. To disable the cache simply set + * the size to 0.If the cache is disabled a DLL will be + * unloaded whenever it's refcount becomes zero. Which MAY + * cause performance problems. + */ + + STDMETHOD(SetCacheSize) (THIS_ ULONG32 nSizeKB) PURE; + + /************************************************************************ + * Method: + * IHXPlugin2Handler::GetInstance + * + * Purpose: + * + * This function will return a plugin instance given a plugin index. + * + */ + + STDMETHOD(GetInstance) (THIS_ UINT32 index, REF(IUnknown*) pUnknown) PURE; + + /************************************************************************ + * Method: + * IHXPlugin2Handler::FindIndexUsingValues + * + * Purpose: + * Finds a plugin which matches the set of values given. An index + * is returned which can be used to either get the values (using + * GetPluginInfo) or an instance can be created using GetPluing(). + * + */ + + STDMETHOD(FindIndexUsingValues) (THIS_ IHXValues*, + REF(UINT32) unIndex) PURE; + + /************************************************************************ + * Method: + * IHXPlugin2Handler::FindPluginUsingValues + * + * Purpose: + * Finds a plugin which matches the set of values given. A Plugin + * instance is returned. + * + */ + + STDMETHOD(FindPluginUsingValues) (THIS_ IHXValues*, + REF(IUnknown*) pUnk) PURE; + + /************************************************************************ + * Method: + * IHXPlugin2Handler::FindIndexUsingStrings + * + * Purpose: + * Finds a plugin which matches the set of values given. An index + * is returned which can be used to either get the values (using + * GetPluginInfo) or an instance can be created using GetPluing(). + * NOTE: that a max of two values may be given. + */ + + STDMETHOD(FindIndexUsingStrings) (THIS_ char* PropName1, + char* PropVal1, + char* PropName2, + char* PropVal2, + char* PropName3, + char* PropVal3, + REF(UINT32) unIndex) PURE; + + /************************************************************************ + * Method: + * IHXPlugin2Handler::FindPluginUsingStrings + * + * Purpose: + * Finds a plugin which matches the set of values given. A Plugin + * instance is returned. + * NOTE: that a max of two values may be given. + */ + + STDMETHOD(FindPluginUsingStrings) (THIS_ char* PropName1, + char* PropVal1, + char* PropName2, + char* PropVal2, + char* PropName3, + char* PropVal3, + REF(IUnknown*) pUnk) PURE; + + /************************************************************************ + * Method: + * IHXPlugin2Handler::FindImplementationFromClassID + * + * Purpose: + * Finds a CommonClassFactory plugin which supports the + * ClassID given. An instance of the Class is returned. + */ + + STDMETHOD(FindImplementationFromClassID) + ( + THIS_ + REFGUID GUIDClassID, + REF(IUnknown*) pIUnknownInstance + ) PURE; + + /************************************************************************ + * Method: + * IHXPlugin2Handler::Close + * + * Purpose: + * A function which performs all of the functions of delete. + * + * + */ + + STDMETHOD(Close) (THIS) PURE; + + /************************************************************************ + * Method: + * IHXPlugin2Handler::SetRequiredPlugins + * + * Purpose: + * This function sets the required plugin list + * + * + */ + + STDMETHOD(SetRequiredPlugins) (THIS_ const char** ppszRequiredPlugins) PURE; + + +}; + + +// $Private: +/**************************************************************************** + * + * Interface: + * + * IHXPluginHandler3 + * + * Purpose: + * + * Extensions to the IHXPlugin2Handler so we can interact with the + * Gemini Object Broker + * + * IID_IHXPluginHandler3: + * + * {32B19771-2299-11d4-9503-00902790299C} + * + */ +DEFINE_GUID( IID_IHXPluginHandler3, 0x32b19771, 0x2299, 0x11d4, 0x95, 0x3, 0x0, 0x90, 0x27, 0x90, 0x29, 0x9c); + +#undef INTERFACE +#define INTERFACE IHXPluginHandler3 + +DECLARE_INTERFACE_(IHXPluginHandler3, IUnknown) +{ + /************************************************************************ + * Method: + * IHXPluginHandler3::RegisterContext + * + * Purpose: + * Sets up the context without loading any plugin info + * + */ + STDMETHOD( RegisterContext )( THIS_ IUnknown* pContext ) PURE; + + /************************************************************************ + * Method: + * IHXPluginHandler3::AddPluginMountPoint + * + * Purpose: + * Sets up the plugins stored in this preferences object + * + */ + STDMETHOD( AddPluginMountPoint )( THIS_ const char* pName, UINT32 majorVersion, + UINT32 minorVersion, IHXBuffer* pPath ) PURE; + + + /************************************************************************ + * Method: + * IHXPluginHandler3::RefreshPluginMountPoint + * + * Purpose: + * Refreshes plugin information associated with this + * preferences object + */ + STDMETHOD( RefreshPluginMountPoint )( THIS_ const char* pName ) PURE; + + + /************************************************************************ + * Method: + * IHXPluginHandler3::RemovePluginMountPoint + * + * Purpose: + * Removes plugins associated with this preferences object + */ + STDMETHOD( RemovePluginMountPoint )( THIS_ const char* pName ) PURE; + + + /************************************************************************ + * Method: + * IHXPluginHandler3::FindImplementationFromClassID + * + * Purpose: + * Finds a CommonClassFactory plugin which supports the + * ClassID given. An instance of the Class is returned. + * The plugin instance is initialized with the specified + * context + */ + + STDMETHOD( FindImplementationFromClassID )( THIS_ REFGUID GUIDClassID, + REF(IUnknown*) pIUnknownInstance, IUnknown* pIUnkOuter, IUnknown* pContext ) PURE; + + + /************************************************************************ + * Method: + * IHXPluginHandler3::FindCLSIDFromName + * + * Purpose: + * + * Maps a text name to a CLSID based on information from + * component plugins + */ + STDMETHOD( FindCLSIDFromName )( THIS_ const char* pName, REF(IHXBuffer*) pCLSID ) PURE; + + + /************************************************************************ + * Method: + * IHXPluginHandler3::FindGroupOfPluginsUsingValues + * + * Purpose: + * Builds a collection of plugins that match the criteria + * + */ + STDMETHOD(FindGroupOfPluginsUsingValues)(THIS_ IHXValues* pValues, + REF(IHXPluginSearchEnumerator*) pIEnumerator) PURE; + + /************************************************************************ + * Method: + * IHXPluginHandler3::FindGroupOfPluginsUsingStrings + * + * Purpose: + * Builds a collection of plugins that match the criteria + * + */ + STDMETHOD(FindGroupOfPluginsUsingStrings)(THIS_ char* PropName1, + char* PropVal1, + char* PropName2, + char* PropVal2, + char* PropName3, + char* PropVal3, + REF(IHXPluginSearchEnumerator*) pIEnumerator) PURE; + + + /************************************************************************ + * Method: + * IHXPluginHandler3::GetPlugin + * + * Purpose: + * Allocates a plugin based on index. Supports aggregation + * + */ + STDMETHOD(GetPlugin)(THIS_ ULONG32 ulIndex, + REF(IUnknown*) pIUnkResult, + IUnknown* pIUnkOuter ) PURE; + + /************************************************************************ + * Method: + * IHXPluginHandler3::FindPluginUsingValues + * + * Purpose: + * Allocates a plugin based on criteria. Supports aggregation + * + */ + STDMETHOD(FindPluginUsingValues)(THIS_ IHXValues*, + REF(IUnknown*) pIUnkResult, + IUnknown* pIUnkOuter ) PURE; + + + /************************************************************************ + * Method: + * IHXPluginHandler3::FindPluginUsingStrings + * + * Purpose: + * Allocates a plugin based on criteria. Supports aggregation + * + */ + STDMETHOD(FindPluginUsingStrings)(THIS_ char* PropName1, + char* PropVal1, + char* PropName2, + char* PropVal2, + char* PropName3, + char* PropVal3, + REF(IUnknown*) pIUnkResult, + IUnknown* pIUnkOuter ) PURE; + + /************************************************************************ + * Method: + * IHXPluginHandler3::UnloadPluginFromClassID + * + * Purpose: + * Finds a plugin from the classID and unloads it if it supports CanUnload2 + * and returns TRUE in response to query + */ + + STDMETHOD( UnloadPluginFromClassID )( THIS_ REFGUID GUIDClassID ) PURE; + + /************************************************************************ + * Method: + * IHXPluginHandler3::UnloadPackageByName + * + * Purpose: + * finds a package from the name passed in and attempts to unload it. + */ + STDMETHOD (UnloadPackageByName) (char const* pName) PURE; + +}; +// $EndPrivate. + +// $Private: +/**************************************************************************** + * + * Interface: + * + * IHXPluginDatabase + * + * Purpose: + * + * Extensions to the plugin handler for optimized searching + * on a single key + * + * IID_IHXPluginDatabase: + * + * {C2C65401-A478-11d4-9518-00902790299C} + * + */ + +enum EPluginIndexType +{ + kIndex_StringType, + kIndex_BufferType, + kIndex_GUIDType, + kIndex_MVStringType, + + kIndex_NumTypes +}; + +DEFINE_GUID( IID_IHXPluginDatabase, 0xc2c65401, 0xa478, 0x11d4, 0x95, 0x18, 0x0, 0x90, 0x27, 0x90, 0x29, 0x9c); + +#undef INTERFACE +#define INTERFACE IHXPluginDatabase + +DECLARE_INTERFACE_(IHXPluginDatabase, IUnknown) +{ + /************************************************************************ + * Method: + * IHXPluginDatabase::AddPluginIndex + * + * Purpose: + * Create a new index in the plugin-handler + * + */ + STDMETHOD( AddPluginIndex ) ( THIS_ const char* pKeyName, EPluginIndexType indexType, HXBOOL bScanExisting ) PURE; + + /************************************************************************ + * Method: + * IHXPluginDatabase::RemovePluginIndex + * + * Purpose: + * Remove an index from the plugin handler + * + */ + STDMETHOD( RemovePluginIndex )( THIS_ const char* pKeyName ) PURE; + + + /************************************************************************ + * Method: + * IHXPluginDatabase::FindPluginInfoViaIndex + * + * Purpose: + * Look up a plugin's info based on a single attribute. Use an index if + * possible, otherwise defer to a linear search + * + */ + STDMETHOD( FindPluginInfoViaIndex )( THIS_ const char* pKeyName, const void* pValue, IHXValues** ppIInfo ) PURE; + + /************************************************************************ + * Method: + * IHXPluginDatabase::FindPluginSetViaIndex + * + * Purpose: + * // XXXND Should this take pValue? Should it just return a list from an index? + * Find a set of plugins matching a single attribute + * + */ + STDMETHOD( FindPluginSetViaIndex )( THIS_ const char* pKeyName, const void* pValue, IHXPluginSearchEnumerator** ppIEnumerator ) PURE; + + /************************************************************************ + * Method: + * IHXPluginDatabase::FindPluginViaIndex + * + * Purpose: + * Create a plugin based on a simple attribute. + * + */ + STDMETHOD( CreatePluginViaIndex )( THIS_ const char* pKeyName, const void* pValue, IUnknown** ppIUnkPlugin, IUnknown* pIUnkOuter ) PURE; +}; + +// $EndPrivate. + + + +// $Private: +/**************************************************************************** + * + * Interface: + * + * IHXProxiedPugin + * + * Purpose: + * + * Provides The IHXPlugin actually being used. + * + * IID_IHXProxiedPlugin: + * + * {00000C10-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXProxiedPlugin, 0x00000C0A, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXProxiedPlugin + +DECLARE_INTERFACE_(IHXProxiedPlugin, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXProxiedPlugin methods + */ + + /************************************************************************ + * Method: + * IHXProxiedPlugin::GetProxiedPlugin + * Purpose: + * Gets the Actual Plugin being used... + */ + STDMETHOD(GetProxiedPlugin)(THIS_ + REF(IHXPlugin*) /*OUT*/ pPlugin) PURE; + +}; +// $EndPrivate. + + +// $Private: +/**************************************************************************** + * + * Component plugin property names + * + * These attribute names are standard. + * + * ComponentCLSID maps to an IHXBuffer value that contains the + * binary CLSID for the component + * + * ComponentName maps to a string value that contains the tag or + * actor name for the component + * + */ +#define PLUGIN_COMPONENT_CLSID "ComponentCLSID" +#define PLUGIN_COMPONENT_NAME "ComponentName" + + +/**************************************************************************** + * + * Interface: + * + * IHXComponentPlugin + * + * Purpose: + * + * Allows the plugin handler to iterator over multiple plugins in a DLL + * + * {F8A31571-22AC-11d4-9503-00902790299C} + * + */ +DEFINE_GUID( IID_IHXComponentPlugin, 0xf8a31571, 0x22ac, 0x11d4, 0x95, 0x3, 0x0, 0x90, 0x27, 0x90, 0x29, 0x9c); + +#undef INTERFACE +#define INTERFACE IHXComponentPlugin + +DECLARE_INTERFACE_(IHXComponentPlugin, IUnknown) +{ + /* + * IHXComponentPlugin methods + */ + + /************************************************************************ + * Method: + * IHXComponentPlugin::GetNumberComponents + * Purpose: + */ + STDMETHOD_(UINT32, GetNumComponents)(THIS) PURE; + + /************************************************************************ + * Method: + * IHXComponentPlugin::GetPackageName + * Purpose: + */ + STDMETHOD_(char const*, GetPackageName)(THIS) CONSTMETHOD PURE; + + /************************************************************************ + * Method: + * IHXComponentPlugin::GetComponentInfoAtIndex + * Purpose: + */ + STDMETHOD(GetComponentInfoAtIndex) (THIS_ + UINT32 /*IN*/ nIndex, + REF(IHXValues*) /*OUT*/ pInfo) PURE; + + /************************************************************************ + * Method: + * IHXComponentPlugin::CreateComponentInstance + * Purpose: + */ + STDMETHOD(CreateComponentInstance)(THIS_ + REFCLSID /*IN*/ rclsid, + REF(IUnknown*) /*OUT*/ ppUnknown, + IUnknown* /*IN*/ pUnkOuter) PURE; +}; + +/**************************************************************************** + * + * Interface: + * + * IHXPluginNamespace + * + * Purpose: + * + * Allows the plugin handler to retrieve a plugin's namespace + * + * {F09E8891-8E2D-11d4-82DB-00D0B74C2D25} + * + */ +DEFINE_GUID(IID_IHXPluginNamespace, 0xf09e8891, 0x8e2d, 0x11d4, 0x82, 0xdb, 0x0, 0xd0, 0xb7, 0x4c, 0x2d, 0x25); + +#undef INTERFACE +#define INTERFACE IHXPluginNamespace + +DECLARE_INTERFACE_(IHXPluginNamespace, IUnknown) +{ + /************************************************************************ + * Method: + * IHXPluginNamespace::GetPluginNamespace + * Purpose: + */ + STDMETHOD(GetPluginNamespace) (THIS_ + REF(IHXBuffer*) /*OUT*/ pBuffer) PURE; + +}; +// $EndPrivate. + +#endif /* _HXPLUGN_H_ */ diff --git a/amarok/src/engine/helix/helix-sp/helix-include/common/include/hxplugncompat.h b/amarok/src/engine/helix/helix-sp/helix-include/common/include/hxplugncompat.h new file mode 100644 index 00000000..a4d1244e --- /dev/null +++ b/amarok/src/engine/helix/helix-sp/helix-include/common/include/hxplugncompat.h @@ -0,0 +1,38 @@ + +/* + * + * This software is released under the provisions of the GPL version 2. + * see file "COPYING". If that file is not available, the full statement + * of the license can be found at + * + * http://www.fsf.org/licensing/licenses/gpl.txt + * + * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved. + * + */ + +#ifndef _HXPLUGNCOMPAT_H_ +#define _HXPLUGNCOMPAT_H_ + +// In order to maintain interoperability with existing software that uses +// plugins, redefine these symbols to their old names. + +#ifndef HXCreateInstance +#define HXCreateInstance RMACreateInstance +#endif /* HXCreateInstance */ + +#ifndef HXCREATEINSTANCE +#define HXCREATEINSTANCESTR "RMACreateInstance" +#define HXCREATEINSTANCE RMACreateInstance +#endif /* HXCREATEINSTANCE */ + +#ifndef HXShutdown +#define HXShutdown RMAShutdown +#endif /* HXShutdown */ + +#ifndef HXSHUTDOWN +#define HXSHUTDOWNSTR "RMAShutdown" +#define HXSHUTDOWN RMAShutdown +#endif /* HXSHUTDOWN */ + +#endif // _HXPLUGNCOMPAT_H_ diff --git a/amarok/src/engine/helix/helix-sp/helix-include/common/include/hxprefs.h b/amarok/src/engine/helix/helix-sp/helix-include/common/include/hxprefs.h new file mode 100644 index 00000000..4750c4c3 --- /dev/null +++ b/amarok/src/engine/helix/helix-sp/helix-include/common/include/hxprefs.h @@ -0,0 +1,348 @@ + +/* + * + * This software is released under the provisions of the GPL version 2. + * see file "COPYING". If that file is not available, the full statement + * of the license can be found at + * + * http://www.fsf.org/licensing/licenses/gpl.txt + * + * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved. + * + */ + +/**************************************************************************** + * + * + * Persistent Preferences Interfaces + * + * Here are the preference entries set by the client core and renderers: + * KEY DEFAULT VALUES + * ================= ==================== + * AttemptMulticast 1 + * AttemptTCP 1 + * AttemptUDP 1 + * AudioQuality 0 + * AutoTransport 1 + * Bandwidth 28800 + * BitsPerSample 16 + * BroadcastPluginInfo {dllpath;description;copyright;moreinfo;loadmultiple;type}{ ... } + * ClientLicenseKey 7FF7FF00 + * EndScan 10000 + * FactoryPluginInfo + * FileFormatPluginInfo {dllpath;description;copyright;moreinfo;loadmultiple;mimetype1|mimetype2;extension1|extension2}{ ... } + * FileSystemPluginInfo {dllpath;description;copyright;moreinfo;loadmultiple;protocol;shortname}{ ... } + * GeneralPluginInfo {dllpath;description;copyright;moreinfo;loadmultiple}{ ... } + * PNAProxyHost + * PNAProxyPort 1090 + * RTSPProxyHost + * RTSPProxyPort 554 + * HTTPProxyHost + * HTTPProxyPort 1092 + * HurledURL 0 + * InfoandVolume 1 + * LastURL + * MaxClipCount 4 + * MetaFormatPluginInfo {dllpath;description;copyright;moreinfo;loadmultiple;mimetype1|mimetype2;extension1|extension2}{ ... } + * MiscPluginInfo {dllpath;description;copyright;moreinfo;loadmultiple}{ ... } + * MulticastTimeout 2000 + * NotProxy + * OnTop 0 + * PerfectPlayMode 0 + * PerfectPlayTime 60 + * PerfPlayEntireClip 1 + * PluginDirectory + * Presets# + * ProxySupport 0 + * RendererPluginInfo {dllpath;description;copyright;moreinfo;loadmultiple;mimetype1|mimetype2}{ ... } + * SamplingRate 8000 + * SeekPage 40 + * SendStatistics 1 + * ServerTimeOut 90 + * ShowPresets 0 + * StatusBar 1 + * StreamDescriptionPluginInfo {dllpath;description;copyright;moreinfo;loadmultiple;mimetype}{ ... } + * SyncMultimedia 1 + * UDPPort 7070 + * UDPTimeout 10000 + * UpgradeAvailable 0 + * UseUDPPort 0 + * Volume 50 + * x:Pref_windowPositionX + * y:Pref_WindowPositionY + */ + +#ifndef _HXPREFS_H_ +#define _HXPREFS_H_ + +#define HXREGISTRY_PREFPROPNAME "ApplicationData" +/* + * Forward declarations of some interfaces defined or used here-in. + */ +typedef _INTERFACE IHXBuffer IHXBuffer; + + +// CLSID for creating a preferences objects via a CCF +// {EC5C2B01-D105-11d4-951F-00902790299C} +#define CLSID_HXPreferences IID_IHXPreferences + +/**************************************************************************** + * + * Interface: + * + * IHXPreferences + * + * Purpose: + * + * This interface allows you to store persistant preferences in the + * server or player's config / registry. + * + * IID_IHXPreferences: + * + * {00000500-0901-11d1-8B06-00A024406D59} + * + */ + +DEFINE_GUID(IID_IHXPreferences, 0x00000500, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXPreferences + +DECLARE_INTERFACE_(IHXPreferences, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXPreferences methods + */ + + /************************************************************************ + * Method: + * IHXPreferences::ReadPref + * Purpose: + * Read a preference from the registry or configuration. + */ + STDMETHOD(ReadPref) (THIS_ + const char* pPrekKey, REF(IHXBuffer*) pBuffer) PURE; + + /************************************************************************ + * Method: + * IHXPreferences::WritePref + * Purpose: + * TBD + */ + STDMETHOD(WritePref) (THIS_ + const char* pPrekKey, IHXBuffer* pBuffer) PURE; +}; + + +/**************************************************************************** + * + * Interface: + * + * IHXPreferenceEnumerator + * + * Purpose: + * + * Allows preference Enumeration + * + * + * IHXPreferenceEnumerator: + * + * {00000504-0901-11d1-8B06-00A024406D59} + * + */ + +DEFINE_GUID(IID_IHXPreferenceEnumerator, 0x00000504, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXPreferenceEnumerator + +DECLARE_INTERFACE_(IHXPreferenceEnumerator, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXPreferenceEnumerator methods + */ + + /************************************************************************ + * Method: + * IHXPreferenceEnumerator::EndSubPref + * Purpose: + * TBD + */ + + STDMETHOD(BeginSubPref) (THIS_ const char* szSubPref) PURE; + + + /************************************************************************ + * Method: + * IHXPreferenceEnumerator::EndSubPref + * Purpose: + * TBD + */ + + STDMETHOD(EndSubPref) (THIS) PURE; + + /************************************************************************ + * Method: + * IHXPreferenceEnumerator::GetPrefKey + * Purpose: + * TBD + */ + + STDMETHOD(GetPrefKey) (THIS_ UINT32 nIndex, REF(IHXBuffer*) pBuffer) PURE; + + /************************************************************************ + * Method: + * IHXPreferenceEnumerator::ReadPref + * Purpose: + * TBD + */ + STDMETHOD(ReadPref) (THIS_ + const char* pPrefKey, IHXBuffer*& pBuffer) PURE; + +}; + + + +/**************************************************************************** + * + * Interface: + * + * IHXPreferences2 + * + * Purpose: + * + * New interface which gives sub-preference options abilities. + * + * + * IID_IHXPreferences2: + * + * {00000503-0901-11d1-8B06-00A024406D59} + * + */ + +DEFINE_GUID(IID_IHXPreferences2, 0x00000503, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXPreferences2 + +DECLARE_INTERFACE_(IHXPreferences2, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXPreferences2 methods + */ + + /************************************************************************ + * Method: + * IHXPreferences2::GetPreferenceEnumerator + * Purpose: + * Read a preference from the registry or configuration. + */ + + STDMETHOD(GetPreferenceEnumerator)(THIS_ REF(IHXPreferenceEnumerator*) /*OUT*/ pEnum) PURE; + + /************************************************************************ + * Method: + * IHXPreferences2::ResetRoot + * Purpose: + * Reset the root of the preferences + */ + + STDMETHOD(ResetRoot)(THIS_ const char* pCompanyName, const char* pProductName, + int nProdMajorVer, int nProdMinorVer) PURE; +}; + + +// $Private: +/**************************************************************************** + * + * Interface: + * + * IHXPreferences3 + * + * Purpose: + * + * New interface for deleting preferences, and whatever we might think of next! + * + * + * IID_IHXPreferences3: + * + * {00000505-0901-11d1-8B06-00A024406D59} + * + */ + +DEFINE_GUID(IID_IHXPreferences3, 0x00000505, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXPreferences3 + +DECLARE_INTERFACE_(IHXPreferences3, IUnknown) +{ + /************************************************************************ + * Method: + * IHXPreferences3::Open + * Purpose: + * Open a specified collection of preferences + */ + + STDMETHOD( Open )(THIS_ const char* pCompanyName, const char* pProductName, + ULONG32 nProdMajorVer, ULONG32 nProdMinorVer) PURE; + + /************************************************************************ + * Method: + * IHXPreferences3::OpenShared + * Purpose: + * Have this preference object read/write from the company wide + * shared location for all products + */ + STDMETHOD( OpenShared )( THIS_ const char* pCompanyName) PURE; + + /************************************************************************ + * Method: + * IHXPreferences3::DeletePref + * Purpose: + * Delete a preference + */ + + STDMETHOD(DeletePref)( THIS_ const char* pPrekKey ) PURE; + +}; +// $EndPrivate. + +#endif /* _HXPREFS_H_ */ diff --git a/amarok/src/engine/helix/helix-sp/helix-include/common/include/hxresult.h b/amarok/src/engine/helix/helix-sp/helix-include/common/include/hxresult.h new file mode 100644 index 00000000..c2697da7 --- /dev/null +++ b/amarok/src/engine/helix/helix-sp/helix-include/common/include/hxresult.h @@ -0,0 +1,583 @@ + +/* + * + * This software is released under the provisions of the GPL version 2. + * see file "COPYING". If that file is not available, the full statement + * of the license can be found at + * + * http://www.fsf.org/licensing/licenses/gpl.txt + * + * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved. + * + */ + +#ifndef _HXRESULT_H_ +#define _HXRESULT_H_ + +/* Some files include this before pntypes.h. */ +#include "hxtypes.h" + +typedef LONG32 HX_RESULT; + +#ifndef _WIN32 + typedef HX_RESULT HRESULT; +# undef NOERROR +# define NOERROR 0 +# define FACILITY_ITF 4 +# define MAKE_HRESULT(sev,fac,code) \ + ((HRESULT) (((unsigned long)(sev)<<31) | ((unsigned long)(fac)<<16) | \ + ((unsigned long)(code))) ) +# define SUCCEEDED(Status) (((unsigned long)(Status)>>31) == 0) +# define FAILED(Status) (((unsigned long)(Status)>>31) != 0) +#else +# ifndef _HRESULT_DEFINED + typedef LONG32 HRESULT; +# endif /* _HRESULT_DEFINED */ +# include <winerror.h> +#endif /* _WIN32 */ + +#define MAKE_HX_RESULT(sev,fac,code) MAKE_HRESULT(sev, FACILITY_ITF, \ + ((fac << 6) | (code))) + +#define SS_GLO 0 /* General errors */ +#define SS_NET 1 /* Networking errors */ +#define SS_FIL 2 /* File errors */ +#define SS_PRT 3 /* Protocol Error */ +#define SS_AUD 4 /* Audio error */ +#define SS_INT 5 /* General internal errors */ +#define SS_USR 6 /* The user is broken. */ +#define SS_MSC 7 /* Miscellaneous */ +#define SS_DEC 8 /* Decoder errors */ +#define SS_ENC 9 /* Encoder errors */ +#define SS_REG 10 /* Registry (not Windows registry ;) errors */ +#define SS_PPV 11 /* Pay Per View errors */ +#define SS_RSC 12 /* Errors for HXXRES */ +#define SS_UPG 13 /* Auto-upgrade & Certificate Errors */ +#define SS_PLY 14 /* RealPlayer/Plus specific errors (USE ONLY IN /rpmisc/pub/rpresult.h) */ +#define SS_RMT 15 /* RMTools Errors */ +#define SS_CFG 16 /* AutoConfig Errors */ +#define SS_RPX 17 /* RealPix-related Errors */ +#define SS_XML 18 /* XML-related Errors */ +// $Private: +#define SS_TKO 19 /* Taiko specific errors (USE ONLY IN /taiko/tresult.h) */ +#define SS_SEC 20 /* Security (key handling, encrypt,decrypt, CSP,...) errors */ +// $EndPrivate. +#define SS_RCA 21 /* RCA errors */ +#define SS_ENC_AX 22 /* Encoder Active x error */ +#define SS_SOCK 24 /* Socket errors */ +#define SS_RSLV 25 /* Resolver errors */ + +#define SS_DPR 63 /* Deprecated errors */ +#define SS_SAM 100 /* ServerAlert errors */ + + +#define HXR_NOTIMPL MAKE_HRESULT(1,0,0x4001) // 80004001 +#define HXR_OUTOFMEMORY MAKE_HRESULT(1,7,0x000e) // 8007000e +#define HXR_INVALID_PARAMETER MAKE_HRESULT(1,7,0x0057) // 80070057 +#define HXR_NOINTERFACE MAKE_HRESULT(1,0,0x4002) // 80004002 +#define HXR_POINTER MAKE_HRESULT(1,0,0x4003) // 80004003 +#define HXR_HANDLE MAKE_HRESULT(1,7,0x0006) // 80070006 +#define HXR_ABORT MAKE_HRESULT(1,0,0x4004) // 80004004 +#define HXR_FAIL MAKE_HRESULT(1,0,0x4005) // 80004005 +#define HXR_ACCESSDENIED MAKE_HRESULT(1,7,0x0005) // 80070005 +#define HXR_IGNORE MAKE_HRESULT(1,0,0x0006) // 80000006 +#define HXR_OK MAKE_HRESULT(0,0,0) // 00000000 + + +#define HXR_INVALID_OPERATION MAKE_HX_RESULT(1,SS_GLO,4) // 80040004 +#define HXR_INVALID_VERSION MAKE_HX_RESULT(1,SS_GLO,5) // 80040005 +#define HXR_INVALID_REVISION MAKE_HX_RESULT(1,SS_GLO,6) // 80040006 +#define HXR_NOT_INITIALIZED MAKE_HX_RESULT(1,SS_GLO,7) // 80040007 +#define HXR_DOC_MISSING MAKE_HX_RESULT(1,SS_GLO,8) // 80040008 +#define HXR_UNEXPECTED MAKE_HX_RESULT(1,SS_GLO,9) // 80040009 +#define HXR_INCOMPLETE MAKE_HX_RESULT(1,SS_GLO,12) // 8004000c +#define HXR_BUFFERTOOSMALL MAKE_HX_RESULT(1,SS_GLO,13) // 8004000d +#define HXR_UNSUPPORTED_VIDEO MAKE_HX_RESULT(1,SS_GLO,14) // 8004000e +#define HXR_UNSUPPORTED_AUDIO MAKE_HX_RESULT(1,SS_GLO,15) // 8004000f +#define HXR_INVALID_BANDWIDTH MAKE_HX_RESULT(1,SS_GLO,16) // 80040010 +/* HXR_NO_RENDERER and HXR_NO_FILEFORMAT old value is being deprecated +#define HXR_NO_FILEFORMAT MAKE_HX_RESULT(1,SS_GLO,10) +#define HXR_NO_RENDERER MAKE_HX_RESULT(1,SS_GLO,11)*/ +#define HXR_NO_RENDERER MAKE_HX_RESULT(1,SS_GLO,17) // 80040011 +#define HXR_NO_FILEFORMAT MAKE_HX_RESULT(1,SS_GLO,17) // 80040011 +#define HXR_MISSING_COMPONENTS MAKE_HX_RESULT(1,SS_GLO,17) // 80040011 +#define HXR_ELEMENT_NOT_FOUND MAKE_HX_RESULT(0,SS_GLO,18) // 00040012 +#define HXR_NOCLASS MAKE_HX_RESULT(0,SS_GLO,19) // 00040013 +#define HXR_CLASS_NOAGGREGATION MAKE_HX_RESULT(0,SS_GLO,20) // 00040014 +#define HXR_NOT_LICENSED MAKE_HX_RESULT(1,SS_GLO,21) // 80040015 +#define HXR_NO_FILESYSTEM MAKE_HX_RESULT(1,SS_GLO,22) // 80040016 +#define HXR_REQUEST_UPGRADE MAKE_HX_RESULT(1,SS_GLO,23) // 80040017 + +#define HXR_CHECK_RIGHTS MAKE_HX_RESULT(1,SS_GLO,24) // 80040018 +#define HXR_RESTORE_SERVER_DENIED MAKE_HX_RESULT(1,SS_GLO,25) // 80040019 +#define HXR_DEBUGGER_DETECTED MAKE_HX_RESULT(1,SS_GLO,26) // 8004001a +#define HXR_RESTORE_SERVER_CONNECT MAKE_HX_RESULT(1,SS_NET,28) // 8004005c +#define HXR_RESTORE_SERVER_TIMEOUT MAKE_HX_RESULT(1,SS_NET,29) // 8004005d +#define HXR_REVOKE_SERVER_CONNECT MAKE_HX_RESULT(1,SS_NET,30) // 8004005e +#define HXR_REVOKE_SERVER_TIMEOUT MAKE_HX_RESULT(1,SS_NET,31) // 8004005f +#define HXR_VIEW_RIGHTS_NODRM MAKE_HX_RESULT(1,SS_MSC,13) // 800401cd +#define HXR_VSRC_NODRM MAKE_HX_RESULT(1,SS_MSC,19) // 800401d3 +#define HXR_WM_OPL_NOT_SUPPORTED MAKE_HX_RESULT(1,SS_GLO,36) // 80040024 + +// $Private: +/* Status Code for backup/restore*/ +#define HXR_RESTORATION_COMPLETE MAKE_HX_RESULT(1,SS_GLO,27) // 8004001b +#define HXR_BACKUP_COMPLETE MAKE_HX_RESULT(1,SS_GLO,28) // 8004001c +#define HXR_TLC_NOT_CERTIFIED MAKE_HX_RESULT(1,SS_GLO,29) // 8004001d +#define HXR_CORRUPTED_BACKUP_FILE MAKE_HX_RESULT(1,SS_GLO,30) // 8004001e +// $EndPrivate. +#define HXR_AWAITING_LICENSE MAKE_HX_RESULT(1,SS_GLO,31) // 8004001f +#define HXR_ALREADY_INITIALIZED MAKE_HX_RESULT(1,SS_GLO,32) // 80040020 +#define HXR_NOT_SUPPORTED MAKE_HX_RESULT(1,SS_GLO,33) // 80040021 +#define HXR_S_FALSE MAKE_HX_RESULT(0,SS_GLO,34) // 00040022 +#define HXR_WARNING MAKE_HX_RESULT(0,SS_GLO,35) // 00040023 + +#define HXR_BUFFERING MAKE_HX_RESULT(0,SS_NET,0) // 00040040 +#define HXR_PAUSED MAKE_HX_RESULT(0,SS_NET,1) // 00040041 +#define HXR_NO_DATA MAKE_HX_RESULT(0,SS_NET,2) // 00040042 +#define HXR_STREAM_DONE MAKE_HX_RESULT(0,SS_NET,3) // 00040043 +#define HXR_NET_SOCKET_INVALID MAKE_HX_RESULT(1,SS_NET,3) // 80040043 +#define HXR_NET_CONNECT MAKE_HX_RESULT(1,SS_NET,4) // 80040044 +#define HXR_BIND MAKE_HX_RESULT(1,SS_NET,5) // 80040045 +#define HXR_SOCKET_CREATE MAKE_HX_RESULT(1,SS_NET,6) // 80040046 +#define HXR_INVALID_HOST MAKE_HX_RESULT(1,SS_NET,7) // 80040047 +#define HXR_NET_READ MAKE_HX_RESULT(1,SS_NET,8) // 80040048 +#define HXR_NET_WRITE MAKE_HX_RESULT(1,SS_NET,9) // 80040049 +#define HXR_NET_UDP MAKE_HX_RESULT(1,SS_NET,10) // 8004004a +#define HXR_RETRY MAKE_HX_RESULT(1,SS_NET,11) /* XXX */ // 8004004b +#define HXR_SERVER_TIMEOUT MAKE_HX_RESULT(1,SS_NET,12) // 8004004c +#define HXR_SERVER_DISCONNECTED MAKE_HX_RESULT(1,SS_NET,13) // 8004004d +#define HXR_WOULD_BLOCK MAKE_HX_RESULT(1,SS_NET,14) // 8004004e +#define HXR_GENERAL_NONET MAKE_HX_RESULT(1,SS_NET,15) // 8004004f +#define HXR_BLOCK_CANCELED MAKE_HX_RESULT(1,SS_NET,16) /* XXX */ // 80040050 +#define HXR_MULTICAST_JOIN MAKE_HX_RESULT(1,SS_NET,17) // 80040051 +#define HXR_GENERAL_MULTICAST MAKE_HX_RESULT(1,SS_NET,18) // 80040052 +#define HXR_MULTICAST_UDP MAKE_HX_RESULT(1,SS_NET,19) // 80040053 +#define HXR_AT_INTERRUPT MAKE_HX_RESULT(1,SS_NET,20) // 80040054 +#define HXR_MSG_TOOLARGE MAKE_HX_RESULT(1,SS_NET,21) // 80040055 +#define HXR_NET_TCP MAKE_HX_RESULT(1,SS_NET,22) // 80040056 +#define HXR_TRY_AUTOCONFIG MAKE_HX_RESULT(1,SS_NET,23) // 80040057 +#define HXR_NOTENOUGH_BANDWIDTH MAKE_HX_RESULT(1,SS_NET,24) // 80040058 +#define HXR_HTTP_CONNECT MAKE_HX_RESULT(1,SS_NET,25) // 80040059 +#define HXR_PORT_IN_USE MAKE_HX_RESULT(1,SS_NET,26) // 8004005a +#define HXR_LOADTEST_NOT_SUPPORTED MAKE_HX_RESULT(1,SS_NET,27) // 8004005b +#define HXR_TCP_CONNECT MAKE_HX_RESULT(0,SS_NET,32) // 00040060 +#define HXR_TCP_RECONNECT MAKE_HX_RESULT(0,SS_NET,33) // 00040061 +#define HXR_TCP_FAILED MAKE_HX_RESULT(1,SS_NET,34) // 80040062 +#define HXR_AUTH_SOCKET_CREATE_FAILURE MAKE_HX_RESULT(1,SS_NET,35) // 80040063 +#define HXR_AUTH_TCP_CONNECT_FAILURE MAKE_HX_RESULT(1,SS_NET,36) // 80040064 +#define HXR_AUTH_TCP_CONNECT_TIMEOUT MAKE_HX_RESULT(1,SS_NET,37) // 80040065 +#define HXR_AUTH_FAILURE MAKE_HX_RESULT(1,SS_NET,38) // 80040066 +#define HXR_AUTH_REQ_PARAMETER_MISSING MAKE_HX_RESULT(1,SS_NET,39) // 80040067 +#define HXR_DNS_RESOLVE_FAILURE MAKE_HX_RESULT(1,SS_NET,40) // 80040068 +#define HXR_AUTH_SUCCEEDED MAKE_HX_RESULT(0,SS_NET,40) // 00040068 +#define HXR_PULL_AUTHENTICATION_FAILED MAKE_HX_RESULT(1,SS_NET,41) // 80040069 +#define HXR_BIND_ERROR MAKE_HX_RESULT(1,SS_NET,42) // 8004006a +#define HXR_PULL_PING_TIMEOUT MAKE_HX_RESULT(1,SS_NET,43) // 8004006b +#define HXR_AUTH_TCP_FAILED MAKE_HX_RESULT(1,SS_NET,44) // 8004006c +#define HXR_UNEXPECTED_STREAM_END MAKE_HX_RESULT(1,SS_NET,45) // 8004006d +#define HXR_AUTH_READ_TIMEOUT MAKE_HX_RESULT(1,SS_NET,46) // 8004006e +#define HXR_AUTH_CONNECTION_FAILURE MAKE_HX_RESULT(1,SS_NET,47) // 8004006f +#define HXR_BLOCKED MAKE_HX_RESULT(1,SS_NET,48) // 80040070 +#define HXR_NOTENOUGH_PREDECBUF MAKE_HX_RESULT(1,SS_NET,49) // 80040071 +#define HXR_END_WITH_REASON MAKE_HX_RESULT(1,SS_NET,50) // 80040072 +#define HXR_SOCKET_NOBUFS MAKE_HX_RESULT(1,SS_NET,51) // 80040073 + +#define HXR_AT_END MAKE_HX_RESULT(0,SS_FIL,0) // 00040080 +#define HXR_INVALID_FILE MAKE_HX_RESULT(1,SS_FIL,1) // 80040081 +#define HXR_INVALID_PATH MAKE_HX_RESULT(1,SS_FIL,2) // 80040082 +#define HXR_RECORD MAKE_HX_RESULT(1,SS_FIL,3) // 80040083 +#define HXR_RECORD_WRITE MAKE_HX_RESULT(1,SS_FIL,4) // 80040084 +#define HXR_TEMP_FILE MAKE_HX_RESULT(1,SS_FIL,5) // 80040085 +#define HXR_ALREADY_OPEN MAKE_HX_RESULT(1,SS_FIL,6) // 80040086 +#define HXR_SEEK_PENDING MAKE_HX_RESULT(1,SS_FIL,7) // 80040087 +#define HXR_CANCELLED MAKE_HX_RESULT(1,SS_FIL,8) // 80040088 +#define HXR_FILE_NOT_FOUND MAKE_HX_RESULT(1,SS_FIL,9) // 80040089 +#define HXR_WRITE_ERROR MAKE_HX_RESULT(1,SS_FIL,10) // 8004008a +#define HXR_FILE_EXISTS MAKE_HX_RESULT(1,SS_FIL,11) // 8004008b +#define HXR_FILE_NOT_OPEN MAKE_HX_RESULT(1,SS_FIL,12) // 8004008c +#define HXR_ADVISE_PREFER_LINEAR MAKE_HX_RESULT(0,SS_FIL,13) // 0004008d +#define HXR_PARSE_ERROR MAKE_HX_RESULT(1,SS_FIL,14) // 8004008e +#define HXR_ADVISE_NOASYNC_SEEK MAKE_HX_RESULT(0,SS_FIL,15) // 0004008f +#define HXR_HEADER_PARSE_ERROR MAKE_HX_RESULT(1,SS_FIL,16) // 80040090 +#define HXR_CORRUPT_FILE MAKE_HX_RESULT(1,SS_FIL,17) // 80040091 + +#define HXR_BAD_SERVER MAKE_HX_RESULT(1,SS_PRT,0) // 800400c0 +#define HXR_ADVANCED_SERVER MAKE_HX_RESULT(1,SS_PRT,1) // 800400c1 +#define HXR_OLD_SERVER MAKE_HX_RESULT(1,SS_PRT,2) // 800400c2 +#define HXR_REDIRECTION MAKE_HX_RESULT(0,SS_PRT,3) /* XXX */ // 000400c3 +#define HXR_SERVER_ALERT MAKE_HX_RESULT(1,SS_PRT,4) // 800400c4 +#define HXR_PROXY MAKE_HX_RESULT(1,SS_PRT,5) // 800400c5 +#define HXR_PROXY_RESPONSE MAKE_HX_RESULT(1,SS_PRT,6) // 800400c6 +#define HXR_ADVANCED_PROXY MAKE_HX_RESULT(1,SS_PRT,7) // 800400c7 +#define HXR_OLD_PROXY MAKE_HX_RESULT(1,SS_PRT,8) // 800400c8 +#define HXR_INVALID_PROTOCOL MAKE_HX_RESULT(1,SS_PRT,9) // 800400c9 +#define HXR_INVALID_URL_OPTION MAKE_HX_RESULT(1,SS_PRT,10) // 800400ca +#define HXR_INVALID_URL_HOST MAKE_HX_RESULT(1,SS_PRT,11) // 800400cb +#define HXR_INVALID_URL_PATH MAKE_HX_RESULT(1,SS_PRT,12) // 800400cc +#define HXR_HTTP_CONTENT_NOT_FOUND MAKE_HX_RESULT(1,SS_PRT,13) // 800400cd +#define HXR_NOT_AUTHORIZED MAKE_HX_RESULT(1,SS_PRT,14) // 800400ce +#define HXR_UNEXPECTED_MSG MAKE_HX_RESULT(1,SS_PRT,15) // 800400cf +#define HXR_BAD_TRANSPORT MAKE_HX_RESULT(1,SS_PRT,16) // 800400d0 +#define HXR_NO_SESSION_ID MAKE_HX_RESULT(1,SS_PRT,17) // 800400d1 +#define HXR_PROXY_DNR MAKE_HX_RESULT(1,SS_PRT,18) // 800400d2 +#define HXR_PROXY_NET_CONNECT MAKE_HX_RESULT(1,SS_PRT,19) // 800400d3 +#define HXR_AGGREGATE_OP_NOT_ALLOWED MAKE_HX_RESULT(1,SS_PRT,20) // 800400d4 +#define HXR_RIGHTS_EXPIRED MAKE_HX_RESULT(1,SS_PRT,21) // 800400d5 +#define HXR_NOT_MODIFIED MAKE_HX_RESULT(1,SS_PRT,22) // 800400d6 +#define HXR_FORBIDDEN MAKE_HX_RESULT(1,SS_PRT,23) // 800400d7 + +#define HXR_AUDIO_DRIVER MAKE_HX_RESULT(1,SS_AUD,0) // 80040100 +#define HXR_LATE_PACKET MAKE_HX_RESULT(1,SS_AUD,1) // 80040101 +#define HXR_OVERLAPPED_PACKET MAKE_HX_RESULT(1,SS_AUD,2) // 80040102 +#define HXR_OUTOFORDER_PACKET MAKE_HX_RESULT(1,SS_AUD,3) // 80040103 +#define HXR_NONCONTIGUOUS_PACKET MAKE_HX_RESULT(1,SS_AUD,4) // 80040104 + +#define HXR_OPEN_NOT_PROCESSED MAKE_HX_RESULT(1,SS_INT,0) // 80040140 +#define HXR_WINDRAW_EXCEPTION MAKE_HX_RESULT(1,SS_INT,1) // 80040141 + +#define HXR_EXPIRED MAKE_HX_RESULT(1,SS_USR,0) // 80040180 + +#define HXR_INVALID_INTERLEAVER MAKE_HX_RESULT(1,SS_DPR,0) // 80040fc0 +#define HXR_BAD_FORMAT MAKE_HX_RESULT(1,SS_DPR,1) // 80040fc1 +#define HXR_CHUNK_MISSING MAKE_HX_RESULT(1,SS_DPR,2) // 80040fc2 +#define HXR_INVALID_STREAM MAKE_HX_RESULT(1,SS_DPR,3) // 80040fc3 +#define HXR_DNR MAKE_HX_RESULT(1,SS_DPR,4) // 80040fc4 +#define HXR_OPEN_DRIVER MAKE_HX_RESULT(1,SS_DPR,5) // 80040fc5 +#define HXR_UPGRADE MAKE_HX_RESULT(1,SS_DPR,6) // 80040fc6 +#define HXR_NOTIFICATION MAKE_HX_RESULT(1,SS_DPR,7) // 80040fc7 +#define HXR_NOT_NOTIFIED MAKE_HX_RESULT(1,SS_DPR,8) // 80040fc8 +#define HXR_STOPPED MAKE_HX_RESULT(1,SS_DPR,9) // 80040fc9 +#define HXR_CLOSED MAKE_HX_RESULT(1,SS_DPR,10) // 80040fca +#define HXR_INVALID_WAV_FILE MAKE_HX_RESULT(1,SS_DPR,11) // 80040fcb +#define HXR_NO_SEEK MAKE_HX_RESULT(1,SS_DPR,12) // 80040fcc + +#define HXR_DEC_INITED MAKE_HX_RESULT(1,SS_DEC,0) // 80040200 +#define HXR_DEC_NOT_FOUND MAKE_HX_RESULT(1,SS_DEC,1) // 80040201 +#define HXR_DEC_INVALID MAKE_HX_RESULT(1,SS_DEC,2) // 80040202 +#define HXR_DEC_TYPE_MISMATCH MAKE_HX_RESULT(1,SS_DEC,3) // 80040203 +#define HXR_DEC_INIT_FAILED MAKE_HX_RESULT(1,SS_DEC,4) // 80040204 +#define HXR_DEC_NOT_INITED MAKE_HX_RESULT(1,SS_DEC,5) // 80040205 +#define HXR_DEC_DECOMPRESS MAKE_HX_RESULT(1,SS_DEC,6) // 80040206 +#define HXR_OBSOLETE_VERSION MAKE_HX_RESULT(1,SS_DEC,7) // 80040207 +#define HXR_DEC_AT_END MAKE_HX_RESULT(0,SS_DEC,8) // 00040208 + +#define HXR_ENC_FILE_TOO_SMALL MAKE_HX_RESULT(1,SS_ENC,0) // 80040240 +#define HXR_ENC_UNKNOWN_FILE MAKE_HX_RESULT(1,SS_ENC,1) // 80040241 +#define HXR_ENC_BAD_CHANNELS MAKE_HX_RESULT(1,SS_ENC,2) // 80040242 +#define HXR_ENC_BAD_SAMPSIZE MAKE_HX_RESULT(1,SS_ENC,3) // 80040243 +#define HXR_ENC_BAD_SAMPRATE MAKE_HX_RESULT(1,SS_ENC,4) // 80040244 +#define HXR_ENC_INVALID MAKE_HX_RESULT(1,SS_ENC,5) // 80040245 +#define HXR_ENC_NO_OUTPUT_FILE MAKE_HX_RESULT(1,SS_ENC,6) // 80040246 +#define HXR_ENC_NO_INPUT_FILE MAKE_HX_RESULT(1,SS_ENC,7) // 80040247 +#define HXR_ENC_NO_OUTPUT_PERMISSIONS MAKE_HX_RESULT(1,SS_ENC,8) // 80040248 +#define HXR_ENC_BAD_FILETYPE MAKE_HX_RESULT(1,SS_ENC,9) // 80040249 +#define HXR_ENC_INVALID_VIDEO MAKE_HX_RESULT(1,SS_ENC,10) // 8004024a +#define HXR_ENC_INVALID_AUDIO MAKE_HX_RESULT(1,SS_ENC,11) // 8004024b +#define HXR_ENC_NO_VIDEO_CAPTURE MAKE_HX_RESULT(1,SS_ENC,12) // 8004024c +#define HXR_ENC_INVALID_VIDEO_CAPTURE MAKE_HX_RESULT(1,SS_ENC,13) // 8004024d +#define HXR_ENC_NO_AUDIO_CAPTURE MAKE_HX_RESULT(1,SS_ENC,14) // 8004024e +#define HXR_ENC_INVALID_AUDIO_CAPTURE MAKE_HX_RESULT(1,SS_ENC,15) // 8004024f +#define HXR_ENC_TOO_SLOW_FOR_LIVE MAKE_HX_RESULT(1,SS_ENC,16) // 80040250 +#define HXR_ENC_ENGINE_NOT_INITIALIZED MAKE_HX_RESULT(1,SS_ENC,17) // 80040251 +#define HXR_ENC_CODEC_NOT_FOUND MAKE_HX_RESULT(1,SS_ENC,18) // 80040252 +#define HXR_ENC_CODEC_NOT_INITIALIZED MAKE_HX_RESULT(1,SS_ENC,19) // 80040253 +#define HXR_ENC_INVALID_INPUT_DIMENSIONS MAKE_HX_RESULT(1,SS_ENC,20) // 80040254 +#define HXR_ENC_MESSAGE_IGNORED MAKE_HX_RESULT(1,SS_ENC,21) // 80040255 +#define HXR_ENC_NO_SETTINGS MAKE_HX_RESULT(1,SS_ENC,22) // 80040256 +#define HXR_ENC_NO_OUTPUT_TYPES MAKE_HX_RESULT(1,SS_ENC,23) // 80040257 +#define HXR_ENC_IMPROPER_STATE MAKE_HX_RESULT(1,SS_ENC,24) // 80040258 +#define HXR_ENC_INVALID_SERVER MAKE_HX_RESULT(1,SS_ENC,25) // 80040259 +#define HXR_ENC_INVALID_TEMP_PATH MAKE_HX_RESULT(1,SS_ENC,26) // 8004025a +#define HXR_ENC_MERGE_FAIL MAKE_HX_RESULT(1,SS_ENC,27) // 8004025b +#define HXR_BIN_DATA_NOT_FOUND MAKE_HX_RESULT(0,SS_ENC,28) // 0004025c +#define HXR_BIN_END_OF_DATA MAKE_HX_RESULT(0,SS_ENC,29) // 0004025d +#define HXR_BIN_DATA_PURGED MAKE_HX_RESULT(1,SS_ENC,30) // 8004025e +#define HXR_BIN_FULL MAKE_HX_RESULT(1,SS_ENC,31) // 8004025f +#define HXR_BIN_OFFSET_PAST_END MAKE_HX_RESULT(1,SS_ENC,32) // 80040260 +#define HXR_ENC_NO_ENCODED_DATA MAKE_HX_RESULT(1,SS_ENC,33) // 80040261 +#define HXR_ENC_INVALID_DLL MAKE_HX_RESULT(1,SS_ENC,34) // 80040262 +#define HXR_NOT_INDEXABLE MAKE_HX_RESULT(1,SS_ENC,35) // 80040263 +#define HXR_ENC_NO_BROWSER MAKE_HX_RESULT(1,SS_ENC,36) // 80040264 +#define HXR_ENC_NO_FILE_TO_SERVER MAKE_HX_RESULT(1,SS_ENC,37) // 80040265 +#define HXR_ENC_INSUFFICIENT_DISK_SPACE MAKE_HX_RESULT(1,SS_ENC,38) // 80040266 +#define HXR_ENC_SAMPLE_DISCARDED MAKE_HX_RESULT(0,SS_ENC,39) // 00040267 +#define HXR_ENC_RV10_FRAME_TOO_LARGE MAKE_HX_RESULT(1,SS_ENC,40) // 80040268 +#define HXR_S_NOT_HANDLED MAKE_HX_RESULT(0,SS_ENC,41) // 00040269 +#define HXR_S_END_OF_STREAM MAKE_HX_RESULT(0,SS_ENC,42) // 0004026a +#define HXR_S_JOBFILE_INCOMPLETE MAKE_HX_RESULT(0, SS_ENC, 43 ) // 0004026b +#define HXR_S_NOTHING_TO_SERIALIZE MAKE_HX_RESULT(0, SS_ENC, 44 ) // 0004026c +#define HXR_SIZENOTSET MAKE_HX_RESULT(1,SS_ENC,45) // 8004026d +#define HXR_ALREADY_COMMITTED MAKE_HX_RESULT(1,SS_ENC,46) // 8004026e +#define HXR_BUFFERS_OUTSTANDING MAKE_HX_RESULT(1,SS_ENC,47) // 8004026f +#define HXR_NOT_COMMITTED MAKE_HX_RESULT(1,SS_ENC,48) // 80040270 +#define HXR_SAMPLE_TIME_NOT_SET MAKE_HX_RESULT(1,SS_ENC,49) // 80040271 +#define HXR_TIMEOUT MAKE_HX_RESULT(1,SS_ENC,50) // 80040272 +#define HXR_WRONGSTATE MAKE_HX_RESULT(1,SS_ENC,51) // 80040273 + +#define HXR_RMT_USAGE_ERROR MAKE_HX_RESULT(1,SS_RMT,1) // 800403c1 +#define HXR_RMT_INVALID_ENDTIME MAKE_HX_RESULT(1,SS_RMT,2) // 800403c2 +#define HXR_RMT_MISSING_INPUT_FILE MAKE_HX_RESULT(1,SS_RMT,3) // 800403c3 +#define HXR_RMT_MISSING_OUTPUT_FILE MAKE_HX_RESULT(1,SS_RMT,4) // 800403c4 +#define HXR_RMT_INPUT_EQUALS_OUTPUT_FILE MAKE_HX_RESULT(1,SS_RMT,5) // 800403c5 +#define HXR_RMT_UNSUPPORTED_AUDIO_VERSION MAKE_HX_RESULT(1,SS_RMT,6) // 800403c6 +#define HXR_RMT_DIFFERENT_AUDIO MAKE_HX_RESULT(1,SS_RMT,7) // 800403c7 +#define HXR_RMT_DIFFERENT_VIDEO MAKE_HX_RESULT(1,SS_RMT,8) // 800403c8 +#define HXR_RMT_PASTE_MISSING_STREAM MAKE_HX_RESULT(1,SS_RMT,9) // 800403c9 +#define HXR_RMT_END_OF_STREAM MAKE_HX_RESULT(1,SS_RMT,10) // 800403ca +#define HXR_RMT_IMAGE_MAP_PARSE_ERROR MAKE_HX_RESULT(1,SS_RMT,11) // 800403cb +#define HXR_RMT_INVALID_IMAGEMAP_FILE MAKE_HX_RESULT(1,SS_RMT,12) // 800403cc +#define HXR_RMT_EVENT_PARSE_ERROR MAKE_HX_RESULT(1,SS_RMT,13) // 800403cd +#define HXR_RMT_INVALID_EVENT_FILE MAKE_HX_RESULT(1,SS_RMT,14) // 800403ce +#define HXR_RMT_INVALID_OUTPUT_FILE MAKE_HX_RESULT(1,SS_RMT,15) // 800403cf +#define HXR_RMT_INVALID_DURATION MAKE_HX_RESULT(1,SS_RMT,16) // 800403d0 +#define HXR_RMT_NO_DUMP_FILES MAKE_HX_RESULT(1,SS_RMT,17) // 800403d1 +#define HXR_RMT_NO_EVENT_DUMP_FILE MAKE_HX_RESULT(1,SS_RMT,18) // 800403d2 +#define HXR_RMT_NO_IMAP_DUMP_FILE MAKE_HX_RESULT(1,SS_RMT,19) // 800403d3 +#define HXR_RMT_NO_DATA MAKE_HX_RESULT(1,SS_RMT,20) // 800403d4 +#define HXR_RMT_EMPTY_STREAM MAKE_HX_RESULT(1,SS_RMT,21) // 800403d5 +#define HXR_RMT_READ_ONLY_FILE MAKE_HX_RESULT(1,SS_RMT,22) // 800403d6 +#define HXR_RMT_PASTE_MISSING_AUDIO_STREAM MAKE_HX_RESULT(1,SS_RMT,23) // 800403d7 +#define HXR_RMT_PASTE_MISSING_VIDEO_STREAM MAKE_HX_RESULT(1,SS_RMT,24) // 800403d8 +#define HXR_RMT_ENCRYPTED_CONTENT MAKE_HX_RESULT(1,SS_RMT,25) // 800403d9 + +#define HXR_PROP_NOT_FOUND MAKE_HX_RESULT(1,SS_REG,1) // 80040281 +#define HXR_PROP_NOT_COMPOSITE MAKE_HX_RESULT(1,SS_REG,2) // 80040282 +#define HXR_PROP_DUPLICATE MAKE_HX_RESULT(1,SS_REG,3) // 80040283 +#define HXR_PROP_TYPE_MISMATCH MAKE_HX_RESULT(1,SS_REG,4) // 80040284 +#define HXR_PROP_ACTIVE MAKE_HX_RESULT(1,SS_REG,5) // 80040285 +#define HXR_PROP_INACTIVE MAKE_HX_RESULT(1,SS_REG,6) // 80040286 +#define HXR_PROP_VAL_UNDERFLOW MAKE_HX_RESULT(1,SS_REG,7) // 80040287 +#define HXR_PROP_VAL_OVERFLOW MAKE_HX_RESULT(1,SS_REG,8) // 80040288 +#define HXR_PROP_VAL_LT_LBOUND MAKE_HX_RESULT(1,SS_REG,9) // 80040289 +#define HXR_PROP_VAL_GT_UBOUND MAKE_HX_RESULT(1,SS_REG,10) // 8004028a +#define HXR_PROP_DELETE_PENDING MAKE_HX_RESULT(0,SS_REG,11) // 0004028b + +#define HXR_COULDNOTINITCORE MAKE_HX_RESULT(1,SS_MSC,1) // 800401c1 +#define HXR_PERFECTPLAY_NOT_SUPPORTED MAKE_HX_RESULT(1,SS_MSC,2) // 800401c2 +#define HXR_NO_LIVE_PERFECTPLAY MAKE_HX_RESULT(1,SS_MSC,3) // 800401c3 +#define HXR_PERFECTPLAY_NOT_ALLOWED MAKE_HX_RESULT(1,SS_MSC,4) // 800401c4 +#define HXR_NO_CODECS MAKE_HX_RESULT(1,SS_MSC,5) // 800401c5 +#define HXR_SLOW_MACHINE MAKE_HX_RESULT(1,SS_MSC,6) // 800401c6 +#define HXR_FORCE_PERFECTPLAY MAKE_HX_RESULT(1,SS_MSC,7) // 800401c7 +#define HXR_INVALID_HTTP_PROXY_HOST MAKE_HX_RESULT(1,SS_MSC,8) // 800401c8 +#define HXR_INVALID_METAFILE MAKE_HX_RESULT(1,SS_MSC,9) // 800401c9 +#define HXR_BROWSER_LAUNCH MAKE_HX_RESULT(1,SS_MSC,10) // 800401ca +#define HXR_VIEW_SOURCE_NOCLIP MAKE_HX_RESULT(1,SS_MSC,11) // 800401cb +#define HXR_VIEW_SOURCE_DISSABLED MAKE_HX_RESULT(1,SS_MSC,12) // 800401cc +#define HXR_TIMELINE_SUSPENDED MAKE_HX_RESULT(1,SS_MSC,14) // 800401ce +#define HXR_BUFFER_NOT_AVAILABLE MAKE_HX_RESULT(1,SS_MSC,15) // 800401cf +#define HXR_COULD_NOT_DISPLAY MAKE_HX_RESULT(1,SS_MSC,16) // 800401d0 +#define HXR_VSRC_DISABLED MAKE_HX_RESULT(1,SS_MSC,17) // 800401d1 +#define HXR_VSRC_NOCLIP MAKE_HX_RESULT(1,SS_MSC,18) // 800401d2 + +#define HXR_RESOURCE_NOT_CACHED MAKE_HX_RESULT(1,SS_RSC,1) // 80040301 +#define HXR_RESOURCE_NOT_FOUND MAKE_HX_RESULT(1,SS_RSC,2) // 80040302 +#define HXR_RESOURCE_CLOSE_FILE_FIRST MAKE_HX_RESULT(1,SS_RSC,3) // 80040303 +#define HXR_RESOURCE_NODATA MAKE_HX_RESULT(1,SS_RSC,4) // 80040304 +#define HXR_RESOURCE_BADFILE MAKE_HX_RESULT(1,SS_RSC,5) // 80040305 +#define HXR_RESOURCE_PARTIALCOPY MAKE_HX_RESULT(1,SS_RSC,6) // 80040306 + +#define HXR_PPV_NO_USER MAKE_HX_RESULT(1,SS_PPV,0) // 800402c0 +#define HXR_PPV_GUID_READ_ONLY MAKE_HX_RESULT(1,SS_PPV,1) // 800402c1 +#define HXR_PPV_GUID_COLLISION MAKE_HX_RESULT(1,SS_PPV,2) // 800402c2 +#define HXR_REGISTER_GUID_EXISTS MAKE_HX_RESULT(1,SS_PPV,3) // 800402c3 +#define HXR_PPV_AUTHORIZATION_FAILED MAKE_HX_RESULT(1,SS_PPV,4) // 800402c4 +#define HXR_PPV_OLD_PLAYER MAKE_HX_RESULT(1,SS_PPV,5) // 800402c5 +#define HXR_PPV_ACCOUNT_LOCKED MAKE_HX_RESULT(1,SS_PPV,6) // 800402c6 +// #define HXR_PPV_PROTOCOL_IGNORES MAKE_HX_RESULT(1,SS_PPV,7) +#define HXR_PPV_DBACCESS_ERROR MAKE_HX_RESULT(1,SS_PPV,8) // 800402c8 +#define HXR_PPV_USER_ALREADY_EXISTS MAKE_HX_RESULT(1,SS_PPV,9) // 800402c9 + +// auto-upgrade (RealUpdate) errors +#define HXR_UPG_AUTH_FAILED MAKE_HX_RESULT(1,SS_UPG,0) // 80040340 +#define HXR_UPG_CERT_AUTH_FAILED MAKE_HX_RESULT(1,SS_UPG,1) // 80040341 +#define HXR_UPG_CERT_EXPIRED MAKE_HX_RESULT(1,SS_UPG,2) // 80040342 +#define HXR_UPG_CERT_REVOKED MAKE_HX_RESULT(1,SS_UPG,3) // 80040343 +#define HXR_UPG_RUP_BAD MAKE_HX_RESULT(1,SS_UPG,4) // 80040344 +#define HXR_UPG_SYSTEM_BUSY MAKE_HX_RESULT(1,SS_UPG,5) // 80040345 + +// auto-config errors +#define HXR_AUTOCFG_SUCCESS MAKE_HX_RESULT(1,SS_CFG,0) +#define HXR_AUTOCFG_FAILED MAKE_HX_RESULT(1,SS_CFG,1) +#define HXR_AUTOCFG_ABORT MAKE_HX_RESULT(1,SS_CFG,2) + +//producer activex errors. +#define HXR_ENC_AX_INIT_FAILED MAKE_HX_RESULT(1,SS_ENC_AX,0) +#define HXR_ENC_AX_NOTVALID_WHILE_ENCODING MAKE_HX_RESULT(1,SS_ENC_AX,1) +#define HXR_ENC_AX_REALMEDIAEVENTS_DISABLED MAKE_HX_RESULT(1,SS_ENC_AX,2) +#define HXR_ENC_AX_EVENT_START_TIME_DECREASING MAKE_HX_RESULT(1,SS_ENC_AX,3) +#define HXR_ENC_AX_FAILED_MEDIASINK_INPUT MAKE_HX_RESULT(1,SS_ENC_AX,4) +#define HXR_ENC_AX_INVALID_EVENT_TYPE MAKE_HX_RESULT(1,SS_ENC_AX,5) +#define HXR_ENC_AX_JOB_NOT_SET MAKE_HX_RESULT(1,SS_ENC_AX,6) +#define HXR_ENC_AX_NOTVALID_WHILE_NOTENCODING MAKE_HX_RESULT(1,SS_ENC_AX,7) +#define HXR_ENC_AX_NO_AUDIO_GAIN_SET MAKE_HX_RESULT(1,SS_ENC_AX,8) +#define HXR_ENC_AX_UPDATE_CODECS_FAILED MAKE_HX_RESULT(1,SS_ENC_AX,9) +#define HXR_ENC_AX_EVENT_LATE MAKE_HX_RESULT(1,SS_ENC_AX,10) +#define HXR_ENC_AX_EVENT_INVALID_START_TIME MAKE_HX_RESULT(1,SS_ENC_AX,11) +#define HXR_ENC_AX_JOB_START_TIME_NOT_SET MAKE_HX_RESULT(1,SS_ENC_AX,12) +#define HXR_ENC_AX_INVALID_JOB_FILE MAKE_HX_RESULT(1,SS_ENC_AX,13) +#define HXR_ENC_AX_INVALID_JOB_XML MAKE_HX_RESULT(1,SS_ENC_AX,14) +#define HXR_ENC_AX_INVALID_ARGUMENTS MAKE_HX_RESULT(1,SS_ENC_AX,15) + +// RealPix errors +#define HXR_UNKNOWN_IMAGE MAKE_HX_RESULT(1,SS_RPX,0) +#define HXR_UNKNOWN_EFFECT MAKE_HX_RESULT(1,SS_RPX,1) +#define HXR_SENDIMAGE_ABORTED MAKE_HX_RESULT(0,SS_RPX,2) +#define HXR_SENDEFFECT_ABORTED MAKE_HX_RESULT(0,SS_RPX,3) + +// server alert errors +#define HXR_SE_MIN_VALUE MAKE_HX_RESULT(1, SS_SAM, 0) // 80041800 +#define HXR_SE_NO_ERROR MAKE_HX_RESULT(1, SS_SAM, 1) // 80041901 +#define HXR_SE_INVALID_VERSION MAKE_HX_RESULT(1, SS_SAM, 2) // 80041902 +#define HXR_SE_INVALID_FORMAT MAKE_HX_RESULT(1, SS_SAM, 3) // 80041903 +#define HXR_SE_INVALID_BANDWIDTH MAKE_HX_RESULT(1, SS_SAM, 4) // 80041904 +#define HXR_SE_INVALID_PATH MAKE_HX_RESULT(1, SS_SAM, 5) // 80041905 +#define HXR_SE_UNKNOWN_PATH MAKE_HX_RESULT(1, SS_SAM, 6) // 80041906 +#define HXR_SE_INVALID_PROTOCOL MAKE_HX_RESULT(1, SS_SAM, 7) // 80041907 +#define HXR_SE_INVALID_PLAYER_ADDR MAKE_HX_RESULT(1, SS_SAM, 8) // 80041908 +#define HXR_SE_LOCAL_STREAMS_PROHIBITED MAKE_HX_RESULT(1, SS_SAM, 9) // 80041909 +#define HXR_SE_SERVER_FULL MAKE_HX_RESULT(1, SS_SAM, 10) // 8004190a +#define HXR_SE_REMOTE_STREAMS_PROHIBITED MAKE_HX_RESULT(1, SS_SAM, 11) // 8004190b +#define HXR_SE_EVENT_STREAMS_PROHIBITED MAKE_HX_RESULT(1, SS_SAM, 12) // 8004190c +#define HXR_SE_INVALID_HOST MAKE_HX_RESULT(1, SS_SAM, 13) // 8004190d +#define HXR_SE_NO_CODEC MAKE_HX_RESULT(1, SS_SAM, 14) // 8004190e +#define HXR_SE_LIVEFILE_INVALID_BWN MAKE_HX_RESULT(1, SS_SAM, 15) // 8004190f +#define HXR_SE_UNABLE_TO_FULFILL MAKE_HX_RESULT(1, SS_SAM, 16) // 80041910 +#define HXR_SE_MULTICAST_DELIVERY_ONLY MAKE_HX_RESULT(1, SS_SAM, 17) // 80041911 +#define HXR_SE_LICENSE_EXCEEDED MAKE_HX_RESULT(1, SS_SAM, 18) // 80041912 +#define HXR_SE_LICENSE_UNAVAILABLE MAKE_HX_RESULT(1, SS_SAM, 19) // 80041913 +#define HXR_SE_INVALID_LOSS_CORRECTION MAKE_HX_RESULT(1, SS_SAM, 20) // 80041914 +#define HXR_SE_PROTOCOL_FAILURE MAKE_HX_RESULT(1, SS_SAM, 21) // 80041915 +#define HXR_SE_REALVIDEO_STREAMS_PROHIBITED MAKE_HX_RESULT(1, SS_SAM, 22) // 80041916 +#define HXR_SE_REALAUDIO_STREAMS_PROHIBITED MAKE_HX_RESULT(1, SS_SAM, 23) // 80041917 +#define HXR_SE_DATATYPE_UNSUPPORTED MAKE_HX_RESULT(1, SS_SAM, 24) // 80041918 +#define HXR_SE_DATATYPE_UNLICENSED MAKE_HX_RESULT(1, SS_SAM, 25) // 80041919 +#define HXR_SE_RESTRICTED_PLAYER MAKE_HX_RESULT(1, SS_SAM, 26) // 8004191a +#define HXR_SE_STREAM_INITIALIZING MAKE_HX_RESULT(1, SS_SAM, 27) // 8004191b +#define HXR_SE_INVALID_PLAYER MAKE_HX_RESULT(1, SS_SAM, 28) // 8004191c +#define HXR_SE_PLAYER_PLUS_ONLY MAKE_HX_RESULT(1, SS_SAM, 29) // 8004191d +#define HXR_SE_NO_EMBEDDED_PLAYERS MAKE_HX_RESULT(1, SS_SAM, 30) // 8004191e +#define HXR_SE_PNA_PROHIBITED MAKE_HX_RESULT(1, SS_SAM, 31) // 8004191f +#define HXR_SE_AUTHENTICATION_UNSUPPORTED MAKE_HX_RESULT(1, SS_SAM, 32) // 80041920 +#define HXR_SE_MAX_FAILED_AUTHENTICATIONS MAKE_HX_RESULT(1, SS_SAM, 33) // 80041921 +#define HXR_SE_AUTH_ACCESS_DENIED MAKE_HX_RESULT(1, SS_SAM, 34) // 80041922 +#define HXR_SE_AUTH_UUID_READ_ONLY MAKE_HX_RESULT(1, SS_SAM, 35) // 80041923 +#define HXR_SE_AUTH_UUID_NOT_UNIQUE MAKE_HX_RESULT(1, SS_SAM, 36) // 80041924 +#define HXR_SE_AUTH_NO_SUCH_USER MAKE_HX_RESULT(1, SS_SAM, 37) // 80041925 +#define HXR_SE_AUTH_REGISTRATION_SUCCEEDED MAKE_HX_RESULT(1, SS_SAM, 38) // 80041926 +#define HXR_SE_AUTH_REGISTRATION_FAILED MAKE_HX_RESULT(1, SS_SAM, 39) // 80041927 +#define HXR_SE_AUTH_REGISTRATION_GUID_REQUIRED MAKE_HX_RESULT(1, SS_SAM, 40) // 80041928 +#define HXR_SE_AUTH_UNREGISTERED_PLAYER MAKE_HX_RESULT(1, SS_SAM, 41) // 80041929 +#define HXR_SE_AUTH_TIME_EXPIRED MAKE_HX_RESULT(1, SS_SAM, 42) // 8004192a +#define HXR_SE_AUTH_NO_TIME_LEFT MAKE_HX_RESULT(1, SS_SAM, 43) // 8004192b +#define HXR_SE_AUTH_ACCOUNT_LOCKED MAKE_HX_RESULT(1, SS_SAM, 44) // 8004192c +#define HXR_SE_AUTH_INVALID_SERVER_CFG MAKE_HX_RESULT(1, SS_SAM, 45) // 8004192d +#define HXR_SE_NO_MOBILE_DOWNLOAD MAKE_HX_RESULT(1, SS_SAM, 46) // 8004192e +#define HXR_SE_NO_MORE_MULTI_ADDR MAKE_HX_RESULT(1, SS_SAM, 47) // 8004192f +#define HXR_PE_PROXY_MAX_CONNECTIONS MAKE_HX_RESULT(1, SS_SAM, 48) // 80041930 +#define HXR_PE_PROXY_MAX_GW_BANDWIDTH MAKE_HX_RESULT(1, SS_SAM, 49) // 80041931 +#define HXR_PE_PROXY_MAX_BANDWIDTH MAKE_HX_RESULT(1, SS_SAM, 50) // 80041932 +#define HXR_SE_BAD_LOADTEST_PASSWORD MAKE_HX_RESULT(1, SS_SAM, 51) // 80041933 +#define HXR_SE_PNA_NOT_SUPPORTED MAKE_HX_RESULT(1, SS_SAM, 52) // 80041934 +#define HXR_PE_PROXY_ORIGIN_DISCONNECTED MAKE_HX_RESULT(1, SS_SAM, 53) // 80041935 +#define HXR_SE_INTERNAL_ERROR MAKE_HX_RESULT(1, SS_SAM, 54) // 80041936 +#define HXR_SE_MAX_VALUE MAKE_HX_RESULT(1, SS_SAM, 55) // 80041937 + + +#define HXR_SOCK_INTR MAKE_HX_RESULT(1, SS_SOCK, 0) // 80040600 +#define HXR_SOCK_BADF MAKE_HX_RESULT(1, SS_SOCK, 1) // 80040601 +#define HXR_SOCK_ACCES MAKE_HX_RESULT(1, SS_SOCK, 2) // 80040602 +#define HXR_SOCK_FAULT MAKE_HX_RESULT(1, SS_SOCK, 3) // 80040603 +#define HXR_SOCK_INVAL MAKE_HX_RESULT(1, SS_SOCK, 4) // 80040604 +#define HXR_SOCK_MFILE MAKE_HX_RESULT(1, SS_SOCK, 5) // 80040605 +#define HXR_SOCK_WOULDBLOCK MAKE_HX_RESULT(1, SS_SOCK, 6) // 80040606 +#define HXR_SOCK_INPROGRESS MAKE_HX_RESULT(1, SS_SOCK, 7) // 80040607 +#define HXR_SOCK_ALREADY MAKE_HX_RESULT(1, SS_SOCK, 8) // 80040608 +#define HXR_SOCK_NOTSOCK MAKE_HX_RESULT(1, SS_SOCK, 9) // 80040609 +#define HXR_SOCK_DESTADDRREQ MAKE_HX_RESULT(1, SS_SOCK, 10) // 8004060a +#define HXR_SOCK_MSGSIZE MAKE_HX_RESULT(1, SS_SOCK, 11) // 8004060b +#define HXR_SOCK_PROTOTYPE MAKE_HX_RESULT(1, SS_SOCK, 12) // 8004060c +#define HXR_SOCK_NOPROTOOPT MAKE_HX_RESULT(1, SS_SOCK, 13) // 8004060d +#define HXR_SOCK_PROTONOSUPPORT MAKE_HX_RESULT(1, SS_SOCK, 14) // 8004060e +#define HXR_SOCK_SOCKTNOSUPPORT MAKE_HX_RESULT(1, SS_SOCK, 15) // 8004060f +#define HXR_SOCK_OPNOTSUPP MAKE_HX_RESULT(1, SS_SOCK, 16) // 80040610 +#define HXR_SOCK_PFNOSUPPORT MAKE_HX_RESULT(1, SS_SOCK, 17) // 80040611 +#define HXR_SOCK_AFNOSUPPORT MAKE_HX_RESULT(1, SS_SOCK, 18) // 80040612 +#define HXR_SOCK_ADDRINUSE MAKE_HX_RESULT(1, SS_SOCK, 19) // 80040613 +#define HXR_SOCK_ADDRNOTAVAIL MAKE_HX_RESULT(1, SS_SOCK, 20) // 80040614 +#define HXR_SOCK_NETDOWN MAKE_HX_RESULT(1, SS_SOCK, 21) // 80040615 +#define HXR_SOCK_NETUNREACH MAKE_HX_RESULT(1, SS_SOCK, 22) // 80040616 +#define HXR_SOCK_NETRESET MAKE_HX_RESULT(1, SS_SOCK, 23) // 80040617 +#define HXR_SOCK_CONNABORTED MAKE_HX_RESULT(1, SS_SOCK, 24) // 80040618 +#define HXR_SOCK_CONNRESET MAKE_HX_RESULT(1, SS_SOCK, 25) // 80040619 +#define HXR_SOCK_NOBUFS MAKE_HX_RESULT(1, SS_SOCK, 26) // 8004061a +#define HXR_SOCK_ISCONN MAKE_HX_RESULT(1, SS_SOCK, 27) // 8004061b +#define HXR_SOCK_NOTCONN MAKE_HX_RESULT(1, SS_SOCK, 28) // 8004061c +#define HXR_SOCK_SHUTDOWN MAKE_HX_RESULT(1, SS_SOCK, 29) // 8004061d +#define HXR_SOCK_TOOMANYREFS MAKE_HX_RESULT(1, SS_SOCK, 30) // 8004061e +#define HXR_SOCK_TIMEDOUT MAKE_HX_RESULT(1, SS_SOCK, 31) // 8004061f +#define HXR_SOCK_CONNREFUSED MAKE_HX_RESULT(1, SS_SOCK, 32) // 80040620 +#define HXR_SOCK_LOOP MAKE_HX_RESULT(1, SS_SOCK, 33) // 80040621 +#define HXR_SOCK_NAMETOOLONG MAKE_HX_RESULT(1, SS_SOCK, 34) // 80040622 +#define HXR_SOCK_HOSTDOWN MAKE_HX_RESULT(1, SS_SOCK, 35) // 80040623 +#define HXR_SOCK_HOSTUNREACH MAKE_HX_RESULT(1, SS_SOCK, 36) // 80040624 +#define HXR_SOCK_PIPE MAKE_HX_RESULT(1, SS_SOCK, 37) // 80040625 +#define HXR_SOCK_ENDSTREAM MAKE_HX_RESULT(1, SS_SOCK, 38) // 80040626 +#define HXR_SOCK_BUFFERED MAKE_HX_RESULT(0, SS_SOCK, 39) // 00040627 + + +#define HXR_RSLV_NONAME MAKE_HX_RESULT(1, SS_RSLV, 0) // 80040640 +#define HXR_RSLV_NODATA MAKE_HX_RESULT(1, SS_RSLV, 1) // 80040641 + +#define SA_OFFSET 2 +#define MAKE_SA(sa) HXR_SE_MIN_VALUE+sa+SA_OFFSET +#define IS_SERVER_ALERT(sa) ((HXR_SE_MIN_VALUE < sa && sa < HXR_SE_MAX_VALUE) || sa == HXR_SERVER_ALERT) + + +#define HXR_FAILED HXR_FAIL + +#ifdef _WIN16 +/*typedef UINT MMRESULT;*/ +#else +#ifdef _WIN32 + +#if defined(WIN32_PLATFORM_PSPC) +#undef _HRESULT_TYPEDEF_ +#undef E_NOTIMPL +#undef E_OUTOFMEMORY +#undef E_INVALIDARG +#undef E_NOINTERFACE +#undef E_POINTER +#undef E_HANDLE +#undef E_ABORT +#undef E_FAIL +#undef E_ACCESSDENIED +#endif /* defined(WIN32_PLATFORM_PSPC) */ +#define _HRESULT_TYPEDEF_(_sc) ((HRESULT)_sc) +#define E_NOTIMPL _HRESULT_TYPEDEF_(0x80004001L) +#define E_OUTOFMEMORY _HRESULT_TYPEDEF_(0x8007000EL) +#define E_INVALIDARG _HRESULT_TYPEDEF_(0x80070057L) +#define E_NOINTERFACE _HRESULT_TYPEDEF_(0x80004002L) +#define E_POINTER _HRESULT_TYPEDEF_(0x80004003L) +#define E_HANDLE _HRESULT_TYPEDEF_(0x80070006L) +#define E_ABORT _HRESULT_TYPEDEF_(0x80004004L) +#define E_FAIL _HRESULT_TYPEDEF_(0x80004005L) +#define E_ACCESSDENIED _HRESULT_TYPEDEF_(0x80070005L) +#else +#define S_OK HXR_OK +#define E_NOTIMPL HXR_NOTIMPL +#define E_INVALIDARG HXR_INVALID_PARAMETER +#define E_NOINTERFACE HXR_NOINTERFACE +#define E_POINTER HXR_POINTER +#define E_HANDLE HXR_HANDLE +#define E_ABORT HXR_ABORT +#define E_FAIL HXR_FAIL +#define E_ACCESSDENIES HXR_ACCESSDENIED +#endif /* _WIN32 */ +#endif /* _WIN16 */ + +#define HX_STATUS_OK HXR_OK +#define HX_STATUS_FAILED E_FAIL + +#endif /* _HXRESULT_H_ */ diff --git a/amarok/src/engine/helix/helix-sp/helix-include/common/include/hxtbuf.h b/amarok/src/engine/helix/helix-sp/helix-include/common/include/hxtbuf.h new file mode 100644 index 00000000..d7e1b92c --- /dev/null +++ b/amarok/src/engine/helix/helix-sp/helix-include/common/include/hxtbuf.h @@ -0,0 +1,64 @@ + +/* + * + * This software is released under the provisions of the GPL version 2. + * see file "COPYING". If that file is not available, the full statement + * of the license can be found at + * + * http://www.fsf.org/licensing/licenses/gpl.txt + * + * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved. + * + */ + +#ifndef _HXTBUF_H_ +#define _HXTBUF_H_ + +/**************************************************************************** + * + * Interface: + * + * IHXTimeStampedBuffer + * + * Purpose: + * + * Basic opaque data storage buffer. Used in interfaces where + * object ownership is best managed through COM style reference + * counting. + * + * IID_IHXTimeStampedBuffer: + * + * {00000700-b4c8-11d0-9995-00a0248da5f0} + * + */ +DEFINE_GUID(IID_IHXTimeStampedBuffer, 0x00000700, 0xb4c8, 0x11d0, + 0x99, 0x95, 0x0, 0xa0, 0x24, 0x8d, 0xa5, 0xf0); + +#define CLSID_IHXTimeStampedBuffer IID_IHXTimeStampedBuffer + +#undef INTERFACE +#define INTERFACE IHXTimeStampedBuffer + +DECLARE_INTERFACE_(IHXTimeStampedBuffer, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXTimeStampedBuffer methods + */ + STDMETHOD_(UINT32,GetTimeStamp)(THIS) PURE; + + STDMETHOD(SetTimeStamp)(THIS_ + UINT32 ulTimeStamp) PURE; +}; + +#endif /* _HXTBUF_H_ */ diff --git a/amarok/src/engine/helix/helix-sp/helix-include/common/include/hxtypes.h b/amarok/src/engine/helix/helix-sp/helix-include/common/include/hxtypes.h new file mode 100644 index 00000000..32c19701 --- /dev/null +++ b/amarok/src/engine/helix/helix-sp/helix-include/common/include/hxtypes.h @@ -0,0 +1,730 @@ + +/* + * + * This software is released under the provisions of the GPL version 2. + * see file "COPYING". If that file is not available, the full statement + * of the license can be found at + * + * http://www.fsf.org/licensing/licenses/gpl.txt + * + * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved. + * + */ + +#if defined(_SYMBIAN) +# include <e32def.h> +# include <e32std.h> +# include <platform/symbian/symbiantypes.h> /* For our TInt64 impl */ +#endif + +#ifdef _VXWORKS +#include "types/vxTypesOld.h" +#include "vxWorks.h" + /* md3 - added to override SENS macro. net/mbuf.h */ +# ifdef m_flags +# undef m_flags +# endif /* m_flags */ + /* md3 - added to override SENS macro, net/radix.h */ +# ifdef Free +# undef Free +# endif /* Free */ +#endif + +#ifdef _MACINTOSH +#pragma once +#endif + +#ifndef _HXTYPES_H_ +#define _HXTYPES_H_ + +// this is essential to make sure that new is not #define'd before the C++ version is included. +#if (defined(_MSC_VER) && defined(_DEBUG) && defined(__cplusplus) && !defined(WIN32_PLATFORM_PSPC) && !defined(_SYMBIAN) && !defined(_OPENWAVE)) +#include <memory> +#endif + +// disable the "debug info truncated at 255" warning. +#if defined _MSC_VER +#pragma warning (disable: 4786) +#endif + +#if (defined(_MSC_VER) && (_MSC_VER > 1100) && defined(_BASETSD_H_)) +#error For VC++ 6.0 or higher you must include hxtypes.h before other windows header files. +#endif + +#if defined(_SYMBIAN) +typedef TInt8 INT8; +typedef TUint8 UINT8; +typedef TInt16 INT16; +typedef TUint16 UINT16; +typedef TInt32 INT32; +typedef TUint32 UINT32; +typedef TUint32 UINT; /* Its unclear, but UINT is suppose to be 32 bits. */ +typedef TBool BOOL; +#else +# ifndef _VXWORKS + +# if defined(QWS) && !defined(QT_CLEAN_NAMESPACE) +#error "You need to define QT_CLEAN_NAMESPACE when using Qt with Helix. If you don't you'll have conflicts with the Helix INT32, UINT32, and UINT definitions" +# endif + + typedef char INT8; /* signed 8 bit value */ + typedef unsigned char UINT8; /* unsigned 8 bit value */ + typedef short int INT16; /* signed 16 bit value */ + typedef unsigned short int UINT16; /* unsigned 16 bit value */ +# if (defined _UNIX && defined _LONG_IS_64) + typedef int INT32; /* signed 32 bit value */ + typedef unsigned int UINT32; /* unsigned 32 bit value */ + typedef unsigned int UINT; +# elif defined _VXWORKS + typedef int INT32; /* signed 32 bit value */ + typedef unsigned int UINT32; /* unsigned 32 bit value */ + typedef unsigned int UINT; +# else + typedef long int INT32; /* signed 32 bit value */ + typedef unsigned long int UINT32; /* unsigned 32 bit value */ + typedef unsigned int UINT; +# endif /* (defined _UNIX && (defined _ALPHA || OSF1)) */ + +# if (defined _UNIX && defined _IRIX) +# ifdef __LONG_MAX__ +# undef __LONG_MAX__ +# endif +# define __LONG_MAX__ 2147483647 +# endif + + +#endif /* _VXWORKS */ + +#endif /* _SYMBIAN */ + + +#if defined(HELIX_CONFIG_AVOID_BOOL) + typedef int HXBOOL; +# if defined(BOOL) +# undef BOOL +# endif +#else +# if defined(BOOL) + typedef BOOL HXBOOL; +# else + typedef int HXBOOL; + typedef HXBOOL BOOL; +# endif +#endif + + +#define ARE_BOOLS_EQUAL(a,b) (((a) && (b)) || (!(a) && !(b))) + +#ifndef HX_BITFIELD +typedef unsigned char HX_BITFIELD; +#endif + +typedef INT32 LONG32; /* signed 32 bit value */ +typedef UINT32 ULONG32; /* unsigned 32 bit value */ + + +#ifdef _LONG_IS_64 +typedef long int INT64; +typedef unsigned long int UINT64; +#elif defined(_WINDOWS) || defined(_OPENWAVE_SIMULATOR) +typedef __int64 INT64; +typedef unsigned __int64 UINT64; +#elif defined(_SYMBIAN) && !defined (_SYMBIAN_81_) +typedef SymInt64 INT64; +typedef SymInt64 UINT64; +#else +typedef long long INT64; +typedef unsigned long long UINT64; +#endif /* _WINDOWS */ + + +/* define the float and double type for all platforms */ +#define HXFLOAT float +#define HXDOUBLE double + +typedef ULONG32 HX_MOFTAG; + + + +/* Some platforms have native 64 bit int types, others don't + * so, we provide these casting macros that have to be used + * to cast 64-bit ints to smaller datatypes + */ +#if defined(_SYMBIAN) && !defined (_SYMBIAN_81_) +#define INT64_TO_ULONG32(a) ((ULONG32)((a).Low())) +#define INT64_TO_UINT32(a) ((UINT32)((a).Low())) +#define INT64_TO_INT32(a) ((INT32)((a).Low())) +#define INT64_TO_DOUBLE(a) ((a).GetTReal()) +#define INT64_TO_FLOAT(a) ((a).GetTReal()) +#define UINT32_TO_DOUBLE(a) (SymbianUINT32toDouble(a)) +#else +#define INT64_TO_ULONG32(a) ((ULONG32)(a)) +#define INT64_TO_UINT32(a) ((UINT32) (a)) +#define INT64_TO_INT32(a) ((INT32) (a)) +#define INT64_TO_DOUBLE(a) ((double) (a)) +#define INT64_TO_FLOAT(a) ((float) (a)) +#define UINT32_TO_DOUBLE(a) ((double) (a)) +#endif + + +#ifdef _MACINTOSH + #ifdef powerc + #define _MACPPC + #else + #define _MAC68K + #endif +#endif + +#if defined(_SYMBIAN) +#define PATH_MAX KMaxPath +#endif + +#ifdef __cplusplus +extern "C" { /* Assume C declarations for C++ */ +#endif /* __cplusplus */ + +#define LANGUAGE_CODE "EN" + +#ifdef _WIN16 +#define MAX_PATH 260 +#define PRODUCT_ID "play16" +#define PLUS_PRODUCT_ID "plus16" +#else +#define PRODUCT_ID "play32" +#define PLUS_PRODUCT_ID "plus32" +#endif + +// $Private: +#define DEFAULT_CONN_TIMEOUT 20 // in seconds +#define MAX_TIMESTAMP_GAP 0x2fffffff +#if !defined(MAX_UINT32) +#define MAX_UINT32 0xffffffff +#endif /* MAX_UINT32 */ +#if defined(_MACINTOSH) +#define kLetInterruptsFinishBeforeQuittingGestalt 'RN$~' +#endif +// $EndPrivate. + +#define MAX_DISPLAY_NAME 256 +#define HX_INVALID_VALUE (ULONG32)0xffffffff + +#define HX_FREE(x) ((x) ? (free (x), (x) = 0) : 0) + +#if defined(HELIX_CONFIG_NULL_DELETE_UNSAFE) + +#define HX_DELETE(x) ((x) ? (delete (x), (x) = 0) : 0) +#define HX_VECTOR_DELETE(x) ((x) ? (delete [] (x), (x) = 0) : 0) + +#else // defined(HELIX_CONFIG_NULL_DELETE_UNSAFE) + +#define HX_DELETE(x) (delete (x), (x) = 0) +#define HX_VECTOR_DELETE(x) (delete [] (x), (x) = 0) + +#endif // defined(HELIX_CONFIG_NULL_DELETE_UNSAFE) + +#define RA_FILE_MAGIC_NUMBER 0x2E7261FDL /* RealAudio File Identifier */ +#define RM_FILE_MAGIC_NUMBER 0x2E524D46L /* RealMedia File Identifier */ +#define RIFF_FILE_MAGIC_NUMBER 0x52494646L /* RIFF (AVI etc.) File Identifier */ + +#ifndef _VXWORKS +typedef UINT8 UCHAR; /* unsigned 8 bit value */ +#endif +typedef INT8 CHAR; /* signed 8 bit value */ + +typedef UINT8 BYTE; + +typedef INT32 long32; +typedef UINT32 u_long32; + +#ifndef _MACINTOSH +typedef INT8 Int8; +#endif +typedef UINT8 u_Int8; +typedef INT16 Int16; +typedef UINT16 u_Int16; +typedef INT32 Int32; +typedef UINT32 u_Int32; + +/* + * XXXGo + * deprecated...now that we need UFIXED and FIXED, this name is confusing... + * use the ones below. + */ +typedef ULONG32 UFIXED32; /* FIXED point value */ +#define FLOAT_TO_FIXED(x) ((UFIXED32) ((x) * (1L << 16) + 0.5)) +#define FIXED_TO_FLOAT(x) ((float) ((((float)x)/ (float)(1L <<16)))) + +/* + * float and fixed point value conversion + */ +#define HX_FLOAT_TO_UFIXED(x) ((UFIXED32) ((x) * (1L << 16) + 0.5)) +#define HX_UFIXED_TO_FLOAT(x) ((float) ((((float)x)/ (float)(1L <<16)))) + +typedef LONG32 FIXED32; /* FIXED point value */ +#define HX_FLOAT_TO_FIXED(x) ((FIXED32) ((x) * (1L << 16) + 0.5)) +#define HX_FIXED_TO_FLOAT(x) ((float) ((((float)x)/ (float)(1L <<16)))) + + +/* + * UFIXED32 is a 32 value where the upper 16 bits are the unsigned integer + * portion of value, and the lower 16 bits are the fractional part of the + * value + */ + +typedef const char* PCSTR; + +/* + * FOURCC's are 32bit codes used in Tagged File formats like + * the RealMedia file format. + */ +#ifndef FOURCC +typedef UINT32 FOURCC; +#endif + +#ifndef HX_FOURCC +#define HX_FOURCC( ch0, ch1, ch2, ch3 ) \ + ( (UINT32)(UINT8)(ch0) | ( (UINT32)(UINT8)(ch1) << 8 ) | \ + ( (UINT32)(UINT8)(ch2) << 16 ) | ( (UINT32)(UINT8)(ch3) << 24 ) ) +#endif + +typedef UINT16 PrefKey; + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#ifdef TRUE +#undef TRUE +#endif + +#ifdef FALSE +#undef FALSE +#endif + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef NULL +#ifdef __cplusplus +#define NULL 0 +#else +#define NULL ((void *)0) +#endif +#endif + +#ifndef _WINDOWS /* defined in windef.h on Windows platform */ +#ifndef HIWORD +#define HIWORD(x) ((x) >> 16) +#endif +#ifndef LOWORD +#define LOWORD(x) ((x) & 0xffff) +#endif +#endif + +#ifndef NOMINMAX +/* Always use macro versions of these! */ +#ifndef max +#define max(a, b) (((a) > (b)) ? (a) : (b)) +#endif +#ifndef min +#define min(a, b) (((a) < (b)) ? (a) : (b)) +#endif +#endif + +/* Should use capitalized macro versions of these, as the lowercase + versions conflict with the STL spec */ +//get these from sys/param.h to avoid tons of warnings about redefining them: +#ifdef _UNIX +#include <sys/param.h> +#endif +#ifndef MAX +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) +#endif +#ifndef MIN +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#endif + +#define HX_MAX(a, b) (((a) > (b)) ? (a) : (b)) +#define HX_MIN(a, b) (((a) < (b)) ? (a) : (b)) + +/*-------------------------------------------------------------------------- +| ZeroInit - initializes a block of memory with zeros +--------------------------------------------------------------------------*/ +#define ZeroInit(pb) memset((void *)pb,0,sizeof(*(pb))) + +#ifndef __MACTYPES__ +typedef unsigned char Byte; +#endif + +/* +///////////////////////////////////////////////////////////////////////////// +// HXEXPORT needed for RA.H and RAGUI.H, should be able to be defined +// and used in cross platform code... +///////////////////////////////////////////////////////////////////////////// +*/ +#if defined(_WIN32) || defined(_WINDOWS) +#ifdef _WIN32 +#define HXEXPORT __declspec(dllexport) __stdcall +#define HXEXPORT_PTR __stdcall * +#else /* Windows, but not 32 bit... */ +#define HXEXPORT _pascal __export +#define HXEXPORT_PTR _pascal * +#define WAVE_FORMAT_PCM 1 +#define LPCTSTR LPCSTR +#endif +#else /* Not Windows... */ + + + + +#define HXEXPORT +#define HXEXPORT_PTR * +#endif + +#if defined(_WIN32) || defined(_WINDOWS) || defined (_MACINTOSH)|| defined (_UNIX) +typedef void (*RANOTIFYPROC)( void* ); +#endif + +#if defined(EXPORT_CLASSES) && defined(_WINDOWS) +#ifdef _WIN32 +#define HXEXPORT_CLASS __declspec(dllexport) +#else +#define HXEXPORT_CLASS __export +#endif // _WIN32 +#else +#define HXEXPORT_CLASS +#endif // EXPORT_CLASSES + + +/* + * STDMETHODCALLTYPE + */ +#ifndef STDMETHODCALLTYPE +#if defined(_WIN32) || defined(_MPPC_) +#ifdef _MPPC_ +#define STDMETHODCALLTYPE __cdecl +#else +#define STDMETHODCALLTYPE __stdcall +#endif +#elif defined(_WIN16) +// XXXTW I made the change below on 5/18/98. The __export was causing +// conflicts with duplicate CHXBuffer methods in being linked into +// rpupgrd and rpdestpn. Also, the warning was "export imported". +// This was fixed by removing the __export. The __export is also +// causing the same problem in pndebug methods. +//#define STDMETHODCALLTYPE __export far _cdecl +#define STDMETHODCALLTYPE far _cdecl +#else +#define STDMETHODCALLTYPE +#endif +#endif + +/* + * STDMETHODVCALLTYPE (V is for variable number of arguments) + */ +#ifndef STDMETHODVCALLTYPE +#if defined(_WINDOWS) || defined(_MPPC_) +#define STDMETHODVCALLTYPE __cdecl +#else +#define STDMETHODVCALLTYPE +#endif +#endif + +/* + * STDAPICALLTYPE + */ +#ifndef STDAPICALLTYPE +#if defined(_WIN32) || defined(_MPPC_) +#define STDAPICALLTYPE __stdcall +#elif defined(_WIN16) +#define STDAPICALLTYPE __export FAR PASCAL +#else +#define STDAPICALLTYPE +#endif +#endif + +/* + * STDAPIVCALLTYPE (V is for variable number of arguments) + */ +#ifndef STDAPIVCALLTYPE +#if defined(_WINDOWS) || defined(_MPPC_) +#define STDAPIVCALLTYPE __cdecl +#else +#define STDAPIVCALLTYPE +#endif +#endif + +/* +///////////////////////////////////////////////////////////////////////////// +// +// Macro: +// +// HX_GET_MAJOR_VERSION() +// +// Purpose: +// +// Returns the Major version portion of the encoded product version +// of the RealAudio application interface DLL previously returned from +// a call to RaGetProductVersion(). +// +// Parameters: +// +// prodVer +// The encoded product version of the RealAudio application interface +// DLL previously returned from a call to RaGetProductVersion(). +// +// Return: +// +// The major version number of the RealAudio application interface DLL +// +// +*/ +#define HX_GET_MAJOR_VERSION(prodVer) ((prodVer >> 28) & 0xF) + +/* +///////////////////////////////////////////////////////////////////////////// +// +// Macro: +// +// HX_GET_MINOR_VERSION() +// +// Purpose: +// +// Returns the minor version portion of the encoded product version +// of the RealAudio application interface DLL previously returned from +// a call to RaGetProductVersion(). +// +// Parameters: +// +// prodVer +// The encoded product version of the RealAudio application interface +// DLL previously returned from a call to RaGetProductVersion(). +// +// Return: +// +// The minor version number of the RealAudio application interface DLL +// +// +*/ +#define HX_GET_MINOR_VERSION(prodVer) ((prodVer >> 20) & 0xFF) + +/* +///////////////////////////////////////////////////////////////////////////// +// +// Macro: +// +// HX_GET_RELEASE_NUMBER() +// +// Purpose: +// +// Returns the release number portion of the encoded product version +// of the RealAudio application interface DLL previously returned from +// a call to RaGetProductVersion(). +// +// Parameters: +// +// prodVer +// The encoded product version of the RealAudio application interface +// DLL previously returned from a call to RaGetProductVersion(). +// +// Return: +// +// The release number of the RealAudio application interface DLL +// +// +*/ +#define HX_GET_RELEASE_NUMBER(prodVer) ((prodVer >> 12) & 0xFF) + +/* +///////////////////////////////////////////////////////////////////////////// +// +// Macro: +// +// HX_GET_BUILD_NUMBER() +// +// Purpose: +// +// Returns the build number portion of the encoded product version +// of the RealAudio application interface DLL previously returned from +// a call to RaGetProductVersion(). +// +// Parameters: +// +// prodVer +// The encoded product version of the RealAudio application interface +// DLL previously returned from a call to RaGetProductVersion(). +// +// Return: +// +// The build number of the RealAudio application interface DLL +// +// +*/ +#define HX_GET_BUILD_NUMBER(prodVer) (prodVer & 0xFFF) + +/* +///////////////////////////////////////////////////////////////////////////// +// +// Macro: +// +// HX_ENCODE_PROD_VERSION() +// +// Purpose: +// +// Encodes a major version, minor version, release number, and build +// number into a product version for testing against the product version +// of the RealAudio application interface DLL returned from a call to +// RaGetProductVersion(). +// +// Parameters: +// +// major +// The major version number to encode. +// +// mimor +// The minor version number to encode. +// +// release +// The release number to encode. +// +// build +// The build number to encode. +// +// Return: +// +// The encoded product version. +// +// NOTES: +// +// Macintosh DEVELOPERS especially, make sure when using the HX_ENCODE_PROD_VERSION +// that you are passing a ULONG32 or equivalent for each of the parameters. +// By default a number passed in as a constant is a short unless it requires more room, +// so designate the constant as a long by appending a L to the end of it. +// Example: +// WORKS: +// HX_ENCODE_VERSION(2L,1L,1L,0L); +// +// DOES NOT WORK: +// HX_ENCODE_VERSION(2,1,1,0); +// +*/ + +#define HX_ENCODE_PROD_VERSION(major,minor,release,build) \ + ((ULONG32)((ULONG32)major << 28) | ((ULONG32)minor << 20) | \ + ((ULONG32)release << 12) | (ULONG32)build) + +#define HX_ENCODE_ADD_PRIVATE_FIELD(ulversion,ulprivate) \ + ((ULONG32)((ULONG32)(ulversion) & (UINT32)0xFFFFFF00) | (ULONG32)(ulprivate) ) + +#define HX_EXTRACT_PRIVATE_FIELD(ulversion)(ulversion & (UINT32)0xFF) + +#define HX_EXTRACT_MAJOR_VERSION(ulversion) ((ulversion)>>28) +#define HX_EXTRACT_MINOR_VERSION(ulversion) (((ulversion)>>20) & (UINT32)0xFF) + +#ifdef _AIX + typedef int tv_sec_t; + typedef int tv_usec_t; +#elif (defined _HPUX) + typedef UINT32 tv_sec_t; + typedef INT32 tv_usec_t; +#else + typedef INT32 tv_sec_t; + typedef INT32 tv_usec_t; +#endif /* _AIX */ + +#ifndef VOLATILE +#define VOLATILE volatile +#endif + +typedef ULONG32 HXXRESOURCE; +typedef ULONG32 HXXHANDLE; +typedef ULONG32 HXXIMAGE; + +// Macro which indicates that a particular variable is unused. Use this to +// avoid compiler warnings. +#define UNUSED(x) + +/* + * For VC++ 6.0 and higher we need to include this substitute header file + * in place of the standard header file basetsd.h, since this standard + * header file conflicts with our definitions. + */ +#if defined(_MSC_VER) && (_MSC_VER > 1100) && !defined(_SYMBIAN) +#include "hxbastsd.h" +#ifdef WIN32_PLATFORM_PSPC +#define _TYPES_H_ +#endif +#endif + +#ifdef _VXWORKS +/* throw in some defines for VXWORKS */ +#define MAXPATHLEN 255 + + +#endif + +#ifdef _MACINTOSH +// xxxbobclark with CWPro 7, there is a code generation bug in the long long +// casting code. It can be avoided by first casting to an unsigned long long. +// This is supposedly fixed in the upcoming (as of this writing) CWPro 8, but +// for now we're setting up the casting using a macro to change it easily. +#define CAST_TO_INT64 (INT64)(UINT64) +#else +#define CAST_TO_INT64 (INT64) +#endif + + +#if defined _LONG_IS_64 && (defined _OSF1 || defined _SOLARIS || defined _HPUX) +typedef unsigned long PTR_INT; +#else +typedef unsigned int PTR_INT; +#endif + + +/* +///////////////////////////////////////////////////////////////////////////// +// +// Macro: +// +// DEFINE_CONSTANT_STRING() +// +// Purpose: +// +// declare a constant string as "const char *" or "extern const char *" depending on whether or +// not INITGUID is defined. This allows constant strings to be safely added +// to a header file without risk of multiply defined symbols if that header is included in a DLL +// and in a library that the DLL links to. While INITGUID doesn't have anything to do with constant +// strings, it is a great constant to switch off of because it is already used to denote when a GUID +// should be defined as external. +// +// Parameters: +// +// name +// The name of the variable to which the constant string will be assigned +// +// string +// the constant string (in quotes). +// +// +// +// NOTES: +// There is currently no way to use DEFINE_CONSTANT_STRING() to declare an "extern const char*" when +// INITGUID is defined. This functionality could be added, but until there is a use case, it would just +// make things more complicated. +// +*/ +#if defined (INITGUID) +#define DEFINE_CONSTANT_STRING(name, string) \ + const char *name = string; +#else +#define DEFINE_CONSTANT_STRING(name, string) \ + extern const char* name; +#endif + + +#endif /* _HXTYPES_H_ */ diff --git a/amarok/src/engine/helix/helix-sp/helix-include/common/include/hxvalue.h b/amarok/src/engine/helix/helix-sp/helix-include/common/include/hxvalue.h new file mode 100644 index 00000000..6d201641 --- /dev/null +++ b/amarok/src/engine/helix/helix-sp/helix-include/common/include/hxvalue.h @@ -0,0 +1,379 @@ + +/* + * + * This software is released under the provisions of the GPL version 2. + * see file "COPYING". If that file is not available, the full statement + * of the license can be found at + * + * http://www.fsf.org/licensing/licenses/gpl.txt + * + * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved. + * + */ + +#ifndef _HXVALUE_H_ +#define _HXVALUE_H_ + +#include "hxcom.h" + +/* + * Forward declarations of some interfaces defined or used here-in. + */ +typedef _INTERFACE IUnknown IUnknown; +typedef _INTERFACE IHXBuffer IHXBuffer; +typedef _INTERFACE IHXKeyValueList IHXKeyValueList; +typedef _INTERFACE IHXKeyValueListIter IHXKeyValueListIter; +typedef _INTERFACE IHXKeyValueListIterOneKey IHXKeyValueListIterOneKey; +typedef _INTERFACE IHXValues IHXValues; +typedef _INTERFACE IHXOptions IHXOptions; + +/* Note : GUIDS 3101 - 3107 are deprecated. */ + +/**************************************************************************** + * + * Interface: + * + * IHXKeyValueList + * + * Purpose: + * + * Stores a list of strings, where strings are keyed by not necessarily + * unique keys. + * + * + * IHXKeyValueList: + * + * {0x00003108-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXKeyValueList, 0x00003108, 0x901, 0x11d1, + 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59); +#define CLSID_IHXKeyValueList IID_IHXKeyValueList + +#undef INTERFACE +#define INTERFACE IHXKeyValueList + +DECLARE_INTERFACE_(IHXKeyValueList, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * Regular methods + */ + + /************************************************************************ + * Method: + * IHXKeyValueList::AddKeyValue + * Purpose: + * Add a new key/value tuple to our list of strings. You can have + * multiple strings for the same key. + */ + STDMETHOD(AddKeyValue) (THIS_ + const char* pKey, + IHXBuffer* pStr) PURE; + + /************************************************************************ + * Method: + * IHXKeyValueList::GetIter + * Purpose: + * Return an iterator that allows you to iterate through all the + * key/value tuples in our list of strings. + */ + STDMETHOD(GetIter) (THIS_ + REF(IHXKeyValueListIter*) pIter) PURE; + + + /************************************************************************ + * Method: + * IHXKeyValueList::GetIterOneKey + * Purpose: + * Return an iterator that allows you to iterate through all the + * strings for a particular key. + */ + STDMETHOD(GetIterOneKey) (THIS_ + const char* pKey, + REF(IHXKeyValueListIterOneKey*) pIter) PURE; + + /************************************************************************ + * Method: + * IHXKeyValueList::AppendAllListItems + * Purpose: + * Append all the key/string tuples from another list to this list. + * (You can have duplicate keys.) + */ + STDMETHOD(AppendAllListItems) (THIS_ + IHXKeyValueList* pList) PURE; + /************************************************************************ + * Method: + * IHXKeyValueList::KeyExists + * Purpose: + * See whether any strings exist for a particular key. + */ + STDMETHOD_(HXBOOL,KeyExists) (THIS_ + const char* pKey) PURE; + + /************************************************************************ + * Method: + * IHXKeyValueList::CreateObject + * Purpose: + * Create an empty object that is the same class as the current object. + */ + STDMETHOD(CreateObject) (THIS_ + REF(IHXKeyValueList*) pNewList) PURE; + + /************************************************************************ + * Method: + * IHXKeyValueList::ImportValues. + * Purpose: + * Import all the strings from an IHXValues object into this object. + * If this object also supports IHXValues, it should also import the + * ULONGs and Buffers. You can have duplicate keys, and old data is + * left untouched. + */ + STDMETHOD(ImportValues) (THIS_ + IHXValues* pValues) PURE; +}; + + +/**************************************************************************** + * + * Interface: + * + * IHXKeyValueListIter + * + * Purpose: + * + * Iterate over all the items in a CKeyValueList. + * Call IHXKeyValueList::GetIter to create an iterator. + * + * + * IHXKeyValueListIter: + * + * {0x00003109-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXKeyValueListIter, 0x00003109, 0x901, 0x11d1, + 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#define CLSID_IHXKeyValueListIter IID_IHXKeyValueListIter + +#undef INTERFACE +#define INTERFACE IHXKeyValueListIter + +DECLARE_INTERFACE_(IHXKeyValueListIter, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + + /* + * Regular methods + */ + + /************************************************************************ + * Method: + * IHXKeyValueListIter::GetNextPair + * Purpose: + * Each call to this method returns one key/value tuple from your + * list of strings. Strings are returned in same order that they + * were inserted. + */ + STDMETHOD(GetNextPair) (THIS_ + REF(const char*) pKey, + REF(IHXBuffer*) pStr) PURE; + + /************************************************************************ + * Method: + * IHXKeyValueListIter::ReplaceCurr + * Purpose: + * Replaces the value in the key/value tuple that was returned + * in the last call to GetNextPair with a new string. + */ + STDMETHOD(ReplaceCurr) (THIS_ + IHXBuffer* pStr) PURE; +}; + + +/**************************************************************************** + * + * Interface: + * + * IHXKeyValueListIterOneKey + * + * Purpose: + * + * Iterate over all the items in a CKeyValueList that match a particular key. + * Call IHXKeyValueList::GetIterOneKey to create an iterator. + * + * + * IHXKeyValueListIterOneKey: + * + * {0x00003110-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXKeyValueListIterOneKey, 0x00003110, 0x901, 0x11d1, + 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#define CLSID_IHXKeyValueListIterOneKey IID_IHXKeyValueListIterOneKey + +#undef INTERFACE +#define INTERFACE IHXKeyValueListIterOneKey + +DECLARE_INTERFACE_(IHXKeyValueListIterOneKey, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + + /* + * Regular methods + */ + + /************************************************************************ + * Method: + * IHXKeyValueListIterOneKey::GetNextString + * Purpose: + * Each call to this method returns one string that matches the + * key for this iterator. Strings are returned in same order that they + * were inserted. + * + */ + STDMETHOD(GetNextString) (THIS_ + REF(IHXBuffer*) pStr) PURE; + + /************************************************************************ + * Method: + * IHXKeyValueListIterOneKey::ReplaceCurr + * Purpose: + * Replaces the value in the key/value tuple that was referenced + * in the last call to GetNextString with a new string. + * + */ + STDMETHOD(ReplaceCurr) (THIS_ + IHXBuffer* pStr) PURE; +}; + + +/**************************************************************************** + * + * Interface: + * + * IHXOptions + * + * Purpose: + * + * This is a generic options interface, implemented by any object to + * allow its options to be read and set by another component of the + * system. + * + * + * IHXOptions: + * + * {0x00003111-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXOptions, 0x00003111, 0x901, 0x11d1, + 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#define CLSID_IHXOptions IID_IHXOptions + +#undef INTERFACE +#define INTERFACE IHXOptions + +DECLARE_INTERFACE_(IHXOptions, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + + /* + * Regular methods + */ + + /************************************************************************ + * Method: + * IHXOptions::GetOptions + * Purpose: + * This method returns a list of the options supported by this + * particular object, along with the value currently set for each + * option. Enumerate the members of the returned IHXValues object + * to discover what options a component supports and the type of + * each of those options. The value for each name-value pair is + * the current setting for that option. + * + */ + STDMETHOD(GetOptions) (THIS_ + REF(IHXValues*) pOptions) PURE; + + /************************************************************************ + * Method: + * IHXOptions::SetOptionULONG32 + * Purpose: + * Sets the value of a ULONG32 option. The return value indicates + * whether or not the SetOptionULONG32 call succeeded. + * + */ + STDMETHOD(SetOptionULONG32) (THIS_ + const char* pName, + ULONG32 ulValue) PURE; + + /************************************************************************ + * Method: + * IHXOptions::SetOptionCString + * Purpose: + * Sets the value of a CString option. The return value indicates + * whether or not the SetOptionCString call succeeded. + * + */ + STDMETHOD(SetOptionCString) (THIS_ + const char* pName, + IHXBuffer* pValue) PURE; + + /************************************************************************ + * Method: + * IHXOptions::SetOptionBuffer + * Purpose: + * Sets the value of a Buffer option. The return value indicates + * whether or not the SetOptionBuffer call succeeded. + * + */ + STDMETHOD(SetOptionBuffer) (THIS_ + const char* pName, + IHXBuffer* pValue) PURE; +}; + + +#endif /* !_HXVALUE_H_ */ diff --git a/amarok/src/engine/helix/helix-sp/helix-include/common/include/hxvsrc.h b/amarok/src/engine/helix/helix-sp/helix-include/common/include/hxvsrc.h new file mode 100644 index 00000000..747aa319 --- /dev/null +++ b/amarok/src/engine/helix/helix-sp/helix-include/common/include/hxvsrc.h @@ -0,0 +1,297 @@ + +/* + * + * This software is released under the provisions of the GPL version 2. + * see file "COPYING". If that file is not available, the full statement + * of the license can be found at + * + * http://www.fsf.org/licensing/licenses/gpl.txt + * + * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved. + * + */ + +#ifndef _HXVSRC_H +#define _HXVSRC_H + +typedef _INTERFACE IHXStreamSource IHXStreamSource; +typedef _INTERFACE IHXFileObject IHXFileObject; + +// Interfaces definded in this file +typedef _INTERFACE IHXFileViewSource IHXFileViewSource; +typedef _INTERFACE IHXFileViewSourceResponse IHXFileViewSourceResponse; +typedef _INTERFACE IHXViewSourceCommand IHXViewSourceCommand; +typedef _INTERFACE IHXViewSourceURLResponse IHXViewSourceURLResponse; + +// $Private: +typedef _INTERFACE IHXClientViewSource IHXClientViewSource; +typedef _INTERFACE IHXClientViewSourceSink IHXClientViewSourceSink; +// $EndPrivate. + + + +/**************************************************************************** + * + * Interface: + * + * IHXFileViewSource + * + * IID_IHXFileViewSource: + * + * {00003500-0901-11d1-8B06-00A024406D59} + * + */ + +enum SOURCE_TYPE +{ + RAW_SOURCE, + HTML_SOURCE +}; + +DEFINE_GUID(IID_IHXFileViewSource, 0x00003500, 0x901, 0x11d1, 0x8b, 0x6, + 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXFileViewSource + +DECLARE_INTERFACE_(IHXFileViewSource, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ REFIID riid, void** ppvObj) PURE; + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /************************************************************************ + * IHXFileViewSource + */ + STDMETHOD(InitViewSource) (THIS_ + IHXFileObject* /*IN*/ pFileObject, + IHXFileViewSourceResponse* /*IN*/ pResp, + SOURCE_TYPE /*IN*/ sourceType, + IHXValues* /*IN*/ pOptions) PURE; + STDMETHOD(GetSource) (THIS) PURE; + STDMETHOD(Close) (THIS) PURE; +}; + +/**************************************************************************** + * + * Interface: + * + * IHXFileViewSourceResponse + * + * IID_IHXFileViewSourceResponse: + * + * {00003501-0901-11d1-8B06-00A024406D59} + * + */ + +DEFINE_GUID(IID_IHXFileViewSourceResponse, 0x00003501, 0x901, 0x11d1, 0x8b, + 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXFileViewSourceResponse + +DECLARE_INTERFACE_(IHXFileViewSourceResponse, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ REFIID riid, void** ppvObj) PURE; + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /************************************************************************ + * IHXFileViewSourceResoponse + */ + STDMETHOD(InitDone) (THIS_ HX_RESULT status ) PURE; + STDMETHOD(SourceReady) (THIS_ HX_RESULT status, + IHXBuffer* pSource ) PURE; + STDMETHOD(CloseDone) (THIS_ HX_RESULT) PURE; +}; + +/**************************************************************************** + * + * Interface: + * + * IHXViewSourceCommand + * + * IID_IHXViewSourceCommand: + * + * {00003504-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXViewSourceCommand, 0x00003504, 0x901, 0x11d1, 0x8b, 0x6, + 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXViewSourceCommand + +DECLARE_INTERFACE_(IHXViewSourceCommand, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ REFIID riid, void** ppvObj) PURE; + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /************************************************************************ + * IHXViewSourceCommand + */ + STDMETHOD_(HXBOOL, CanViewSource) (THIS_ + IHXStreamSource* pStream) PURE; + STDMETHOD(DoViewSource) (THIS_ + IHXStreamSource* pStream) PURE; + STDMETHOD(GetViewSourceURL) (THIS_ + IHXStreamSource* pSource, + IHXViewSourceURLResponse* pResp) PURE; +}; + +/**************************************************************************** + * + * Interface: + * + * IHXViewSourceURLResponse + * + * IID_IHXViewSourceURLResponse: + * + * {00003505-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXViewSourceURLResponse, 0x00003505, 0x901, 0x11d1, 0x8b, 0x6, + 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXViewSourceURLResponse + +DECLARE_INTERFACE_(IHXViewSourceURLResponse, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ REFIID riid, void** ppvObj) PURE; + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /************************************************************************ + * IHXViewSourceURLResponse + */ + STDMETHOD(ViewSourceURLReady) (THIS_ + const char* /*out*/ pUrl) PURE; +}; + +// $Private: +/**************************************************************************** + * + * Interface: + * + * IHXClientViewSource + * + * IID_IHXClientViewSource: + * + * {00003502-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXClientViewSource, 0x00003502, 0x901, 0x11d1, 0x8b, 0x6, + 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXClientViewSource + +DECLARE_INTERFACE_(IHXClientViewSource, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ REFIID riid, void** ppvObj) PURE; + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /************************************************************************ + * IHXClientViewSource + */ + STDMETHOD(DoViewSource) (THIS_ + IUnknown* /*IN*/ pPlayerContext, + IHXStreamSource* /*IN*/ pSource) PURE; + STDMETHOD_(HXBOOL, CanViewSource) (THIS_ + IHXStreamSource* /*IN*/ pSource) PURE; + + STDMETHOD(GetViewSourceURL) (THIS_ + IUnknown* pPlayerContext, + IHXStreamSource* pSource, + IHXViewSourceURLResponse* pResp) PURE; + +}; + +/**************************************************************************** + * + * Interface: + * + * IHXClientViewSourceSink + * + * IID_IHXClientViewSourceSink: + * + * {00003503-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXClientViewSourceSink, 0x00003503, 0x901, 0x11d1, 0x8b, 0x6, + 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXClientViewSourceSink + +DECLARE_INTERFACE_(IHXClientViewSourceSink, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ REFIID riid, void** ppvObj) PURE; + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /************************************************************************ + * IHXClientViewSourceSink + */ + STDMETHOD(RegisterViewSourceHdlr) (THIS_ + IHXClientViewSource* /*in*/ pViewSourceHdlr) PURE; +}; + +/**************************************************************************** + * + * Interface: + * + * IHXClientViewRights + * + * IID_IHXClientViewRights: + * + * {00003506-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXClientViewRights, 0x00003506, 0x901, 0x11d1, 0x8b, 0x6, + 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXClientViewRights + +DECLARE_INTERFACE_(IHXClientViewRights, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ REFIID riid, void** ppvObj) PURE; + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /************************************************************************ + * IHXClientViewRights + */ + STDMETHOD(ViewRights) (THIS_ + IUnknown* /*IN*/ pPlayerContext) PURE; + STDMETHOD_(HXBOOL, CanViewRights) (THIS) PURE; + +}; +// $EndPrivate. + +#endif diff --git a/amarok/src/engine/helix/helix-sp/helix-include/common/include/hxwin.h b/amarok/src/engine/helix/helix-sp/helix-include/common/include/hxwin.h new file mode 100644 index 00000000..d7cd38fd --- /dev/null +++ b/amarok/src/engine/helix/helix-sp/helix-include/common/include/hxwin.h @@ -0,0 +1,1703 @@ + +/* + * + * This software is released under the provisions of the GPL version 2. + * see file "COPYING". If that file is not available, the full statement + * of the license can be found at + * + * http://www.fsf.org/licensing/licenses/gpl.txt + * + * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved. + * + */ + +#ifndef _HXWIN_H_ +#define _HXWIN_H_ + +/* + * Forward declarations of some interfaces defined or used here-in. + */ +typedef _INTERFACE IHXSite IHXSite; +typedef _INTERFACE IHXSiteUser IHXSiteUser; +typedef _INTERFACE IHXSiteWindowed IHXSiteWindowed; +typedef _INTERFACE IHXSiteEventHandler IHXSiteEventHandler; +typedef _INTERFACE IHXSiteWindowless IHXSiteWindowless; +typedef _INTERFACE IHXSiteWatcher IHXSiteWatcher; +typedef _INTERFACE IHXValues IHXValues; +typedef _INTERFACE IHXSiteFullScreen IHXSiteFullScreen; +typedef _INTERFACE IHXLayoutSiteGroupManager IHXLayoutSiteGroupManager; +typedef _INTERFACE IHXEventHook IHXEventHook; +typedef _INTERFACE IHXColorConverter IHXColorConverter; +typedef _INTERFACE IHXSubRectVideoSurface IHXSubRectVideoSurface; + +typedef struct _HXBitmapInfoHeader HXBitmapInfoHeader; +typedef struct _HXxWindow HXxWindow; +typedef struct _HXxRegion HXxBoxRegion; +typedef struct _HXxSize HXxSize; +typedef struct _HXxPoint HXxPoint; +typedef struct _HXxRect HXxRect; +typedef void* HXxRegion; + + +/* + * Styles for IHXDrawFocus + */ +#define HX_SOLID_LINE 1 +#define HX_DASHED_LINE HX_SOLID_LINE<<1 +#define HX_DOTTED_LINE HX_SOLID_LINE<<2 +#define HX_CUSTOM_LINE HX_SOLID_LINE<<3 + +/* + * Focus Navigation + */ +typedef enum _HXFocusContext +{ + HXFirstFocus, + HXUpFocus, + HXDownFocus, + HXLeftFocus, + HXRightFocus, + HXNextFocus, + HXPrevFocus, + HXLastFocus +} HXFocusContext; + +typedef enum _HXFocusState +{ + HXNoFocus, + HXFocused +} HXFocusState; + +/**************************************************************************** + * + * Interface: + * + * IHXSiteWindowed + * + * Purpose: + * + * Interface for IHXSite objects which are associated with platform + * specific window objects on Microsoft Windows and X-Windows. + * + * IID_IHXSiteWindowed: + * + * {00000D01-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXSiteWindowed, 0x00000D01, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#define CLSID_IHXSiteWindowed IID_IHXSiteWindowed + +// $Private: +DEFINE_GUID(IID_IHXGetImplementation, 0x00000D11, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); +// $EndPrivate. + +#undef INTERFACE +#define INTERFACE IHXSiteWindowed + +DECLARE_INTERFACE_(IHXSiteWindowed, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXSiteWindowed methods called by site suppliers + * when they want the site associated with a + * previously created (and externally managed) window. + * This method will "sub-class" that window (Win32). + * On Unix, the site supplier must pass events from + * the externally managed window to the core via + * IHXClientEngine::EventOccurred(). Please note that + * The HXxWindow ptr must remain in scope for the life + * of Site. + * + */ + STDMETHOD(AttachWindow) (THIS_ + HXxWindow* /*IN*/ pWindow) PURE; + + STDMETHOD(DetachWindow) (THIS) PURE; + + /* + * IHXSiteWindowed methods called by Owners of the site + * in the event that want a default top level window created + * for the site. + */ + STDMETHOD(Create) (THIS_ + void* ParentWindow, + UINT32 style) PURE; + + STDMETHOD(Destroy) (THIS) PURE; + + /* + * IHXSiteWindowed method. Returns actual window of the site. + */ + STDMETHOD_(HXxWindow*,GetWindow)(THIS) PURE; +}; + +// $Private: +/**************************************************************************** + * + * Interface: + * + * IHXSiteEventHandler + * + * Purpose: + * + * Interface for allowing client core engine to pass events to a site imeplementor + * which it implemented as a factory plugin. + * + * IID_IHXSiteEventHandler + * + * {00000D12-0901-11d1-8B-6-00A024406D59} + * + */ + +DEFINE_GUID(IID_IHXSiteEventHandler, 0x00000D12, 0x901, 0x11d1, 0x8b, 0x6, + 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#define CLSID_IHXSiteEventHandler IID_IHXSiteEventHandler + +#undef INTERFACE +#define INTERFACE IHXSiteEventHandler + +DECLARE_INTERFACE_(IHXSiteEventHandler, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * This method is called from Site Manager EventOccured(). + * The imeplementation of this interface must pass the events + * on to the individual CHXSiteWindowed sites. + */ + STDMETHOD(EventOccurred) (THIS_ HXxEvent* pEvent) PURE; +}; +// $EndPrivate. + + +/**************************************************************************** + * + * Interface: + * + * IHXSiteWindowless + * + * Purpose: + * + * Interface for IHXSite objects which are "windowless" or not + * associated with platform specific window objects. + * + * IID_IHXSiteWindowless: + * + * {00000D02-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXSiteWindowless, 0x00000D02, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXSiteWindowless + +#define CLSID_IHXSiteWindowless IID_IHXSiteWindowless + +DECLARE_INTERFACE_(IHXSiteWindowless, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXSiteWindowless methods called by owners of the site. + */ + STDMETHOD(EventOccurred) (THIS_ + HXxEvent* /*IN*/ pEvent) PURE; + + /* + * IHXSiteWindowless method. Returns some parent window that + * owns the windowless site. Useful for right-click menus and + * dialog box calls. + */ + STDMETHOD_(HXxWindow*,GetParentWindow)(THIS) PURE; +}; + + +/**************************************************************************** + * + * Interface: + * + * IHXSite + * + * Purpose: + * + * Interface for IHXSite objects. + * + * IID_IHXSite: + * + * {00000D03-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXSite, 0x00000D03, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXSite + +DECLARE_INTERFACE_(IHXSite, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXSite methods usually called by the "context" to + * associate users with the site, and to create child sites + * as appropriate. + */ + STDMETHOD(AttachUser) (THIS_ + IHXSiteUser* /*IN*/ pUser) PURE; + + STDMETHOD(DetachUser) (THIS) PURE; + + + STDMETHOD(GetUser) (THIS_ + REF(IHXSiteUser*) /*OUT*/ pUser) PURE; + + STDMETHOD(CreateChild) (THIS_ + REF(IHXSite*) /*OUT*/ pChildSite) PURE; + + STDMETHOD(DestroyChild) (THIS_ + IHXSite* /*IN*/ pChildSite) PURE; + + /* + * IHXSite methods called by the the "context" in which the site + * is displayed in order to manage its position. Site users should + * not generally call these methods. + */ + STDMETHOD(AttachWatcher) (THIS_ + IHXSiteWatcher* /*IN*/ pWatcher) PURE; + + STDMETHOD(DetachWatcher) (THIS) PURE; + + STDMETHOD(SetPosition) (THIS_ + HXxPoint position) PURE; + + STDMETHOD(GetPosition) (THIS_ + REF(HXxPoint) position) PURE; + + /* + * IHXSite methods called by the user of the site to get + * information about the site, and to manipulate the site. + */ + STDMETHOD(SetSize) (THIS_ + HXxSize size) PURE; + + STDMETHOD(GetSize) (THIS_ + REF(HXxSize) size) PURE; + + STDMETHOD(DamageRect) (THIS_ + HXxRect rect) PURE; + + STDMETHOD(DamageRegion) (THIS_ + HXxRegion region) PURE; + + STDMETHOD(ForceRedraw) (THIS) PURE; +}; + + +// $Private +/**************************************************************************** + * + * Interface: + * + * IHXSiteComposition + * + * Purpose: + * + * Interface for IHXSite objects to let them compose composition + * frames and display them on a regular basis rather than many + * discrete blts. + * + * IID_IHXSiteComposition: + * + * {00000D03-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXSiteComposition, 0x00000D19, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXSiteComposition + +DECLARE_INTERFACE_(IHXSiteComposition, IUnknown) +{ + /* IUnknown methods */ + STDMETHOD(QueryInterface) (THIS_ REFIID riid, void** ppvObj) PURE; + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* IHXSiteComposition methods. */ + STDMETHOD(LockComposition) (THIS) PURE; + STDMETHOD(UnlockComposition) (THIS) PURE; + STDMETHOD(BltComposition) (THIS) PURE; + STDMETHOD(SetCompositionMode) (THIS_ HXBOOL OnOrOff) PURE; + STDMETHOD_(HXBOOL, IsCompositionLocked) (THIS) PURE; + STDMETHOD_(HXBOOL, IsCompositionMode) (THIS) PURE; +}; +// $EndPrivate. + + +/**************************************************************************** + * + * Interface: + * + * IHXSiteUser + * + * Purpose: + * + * Interface for the user of the IHXSite objects. + * + * IID_IHXSiteUser: + * + * {00000D04-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXSiteUser, 0x00000D04, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXSiteUser + +DECLARE_INTERFACE_(IHXSiteUser, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXSiteUser methods usually called by the "context" to + * associate users with the site. + */ + STDMETHOD(AttachSite) (THIS_ + IHXSite* /*IN*/ pSite) PURE; + + STDMETHOD(DetachSite) (THIS) PURE; + + /* + * IHXSiteUser methods called to inform user of an event. + */ + STDMETHOD(HandleEvent) (THIS_ + HXxEvent* /*IN*/ pEvent) PURE; + + STDMETHOD_(HXBOOL,NeedsWindowedSites) (THIS) PURE; +}; + +/**************************************************************************** + * + * Interface: + * + * IHXSiteWatcher + * + * Purpose: + * + * Interface for IHXSiteWatcher objects. + * + * IID_IHXSite: + * + * {00000D05-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXSiteWatcher, 0x00000D05, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXSiteWatcher + +DECLARE_INTERFACE_(IHXSiteWatcher, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXSiteWatcher methods called by the site when a watcher + * is attached to or detached from it. + */ + STDMETHOD(AttachSite) (THIS_ + IHXSite* /*IN*/ pSite) PURE; + + STDMETHOD(DetachSite) (THIS) PURE; + + /* + * IHXSiteWatcher methods called by the site an attempt is + * made to change it's position or size. The site watcher must + * return HXR_OK for the change to occur. If the site watcher + * returns any value other than HXR_OK then the size or position + * will not change. The site watcher can also modify the new + * size of position. + */ + STDMETHOD(ChangingPosition) (THIS_ + HXxPoint posOld, + REF(HXxPoint)/*IN-OUT*/ posNew) PURE; + + STDMETHOD(ChangingSize) (THIS_ + HXxSize sizeOld, + REF(HXxSize) /*IN-OUT*/ sizeNew) PURE; +}; + +/**************************************************************************** + * + * Interface: + * + * IHXSiteUserSupplier + * + * Purpose: + * + * Interface implemented by renderers and objects with provide layouts to + * the client core. This interface is called by the core when it needs a + * new IHXSiteUser, or when it is done using an IHXSiteUser. + * + * IID_IHXSiteUserSupplier: + * + * {00000D06-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXSiteUserSupplier, 0x00000D06, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXSiteUserSupplier + +DECLARE_INTERFACE_(IHXSiteUserSupplier, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXSiteUserSupplier methods usually called by the + * "context" to ask for additional or to release previously + * created site users. + */ + STDMETHOD(CreateSiteUser) (THIS_ + REF(IHXSiteUser*)/*OUT*/ pSiteUser) PURE; + + STDMETHOD(DestroySiteUser) (THIS_ + IHXSiteUser* /*IN*/ pSiteUser) PURE; + + STDMETHOD_(HXBOOL,NeedsWindowedSites) (THIS) PURE; +}; + + +/**************************************************************************** + * + * Interface: + * + * IHXSiteSupplier + * + * Purpose: + * + * Interface implemented by users of the client core. This interface is + * called by the core when it needs a new IHXSite, or when it is done + * using an IHXSite. + * + * IID_IHXSiteSupplier: + * + * {00000D07-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXSiteSupplier, 0x00000D07, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXSiteSupplier + +DECLARE_INTERFACE_(IHXSiteSupplier, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXSiteSupplier methods + */ + + /************************************************************************ + * Method: + * IHXSiteSupplier::SitesNeeded + * Purpose: + * Called to inform the site supplier that a site with a particular + * set of characteristics is needed. If the site supplier can + * fulfill the request it should call the site manager and add one + * or more new sites. + * Note that the request for sites is associated with a Request ID + * the client core will inform the site supplier when this requested + * site is no longer needed. + */ + STDMETHOD(SitesNeeded) (THIS_ + UINT32 uReqestID, + IHXValues* pSiteProps) PURE; + + /************************************************************************ + * Method: + * IHXSiteSupplier::SitesNotNeeded + * Purpose: + * Called to inform the site supplier that all sites from a previos + * site request are no longer needed. If the site supplier had + * previously created non-persistant sites (like popup windows) + * to fulfill a request for sites it should call the site manager + * and remove those sites. + */ + STDMETHOD(SitesNotNeeded) (THIS_ + UINT32 uReqestID) PURE; + + + /************************************************************************ + * Method: + * IHXSiteSupplier::BeginChangeLayout + * Purpose: + * Called to inform the site supplier a layout change has beginning + * it can expect to receive SitesNeeded() and SitesNotNeeded() calls + * while a layout change is in progress, + */ + STDMETHOD(BeginChangeLayout) (THIS) PURE; + + /************************************************************************ + * Method: + * IHXSiteSupplier::DoneChangeLayout + * Purpose: + * Called to inform the site supplier the layout change has been + * completed. + */ + STDMETHOD(DoneChangeLayout) (THIS) PURE; + +}; + + +/**************************************************************************** + * + * Interface: + * + * IHXSiteManager + * + * Purpose: + * + * Interface implemented by the client core. This interface is called + * by users of the client core to inform it of IHXSite's which are + * available for layout of renderers + * + * IID_IHXSiteManager: + * + * {00000D08-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXSiteManager, 0x00000D08, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXSiteManager + +DECLARE_INTERFACE_(IHXSiteManager, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXSiteManager methods + */ + + /************************************************************************ + * Method: + * IHXSiteManager::AddSite + * Purpose: + * Called to inform the site manager of the existence of a site. + */ + STDMETHOD(AddSite) (THIS_ + IHXSite* pSite) PURE; + + /************************************************************************ + * Method: + * IHXSiteManager::RemoveSite + * Purpose: + * Called to inform the site manager that a site is no longer + * available. + */ + STDMETHOD(RemoveSite) (THIS_ + IHXSite* pSite) PURE; +}; + + +// $Private: +/**************************************************************************** + * + * Interface: + * + * IHXSiteManager2 + * + * Purpose: + * + * Interface implemented by the client core. This interface is called + * by users of the client core to iterate over the sites known by this + * site manager. + * + * IID_IHXSiteManager: + * + * {00000D20-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXSiteManager2, 0x00000D20, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXSiteManager2 + +DECLARE_INTERFACE_(IHXSiteManager2, IUnknown) +{ + /* + * IHXSiteManager2 methods + */ + + /************************************************************************ + * Method: + * IHXSiteManager2::GetNumberOfSites + * Purpose: + * Called to get the number of sites that the site mananger currently + * knows about. + */ + STDMETHOD(GetNumberOfSites) (THIS_ REF(UINT32) nNumSites ) PURE; + + /************************************************************************ + * Method: + * IHXSiteManager2::GetSiteAt + * Purpose: + * Used to iterate over the sites. + * + */ + STDMETHOD(GetSiteAt) (THIS_ UINT32 nIndex, REF(IHXSite*) pSite) PURE; +}; +// $EndPrivate. + + +/**************************************************************************** + * + * Interface: + * + * IHXMultiInstanceSiteUserSupplier + * + * Purpose: + * + * This is the interface for a special default object which is available + * from the common class factory. This object will act as a site user + * supplier for any renderer (or other site user object) that wants + * default support for multiple instances. The site user must work as + * a windowless site for this default implementation to work. The + * default object also implements the IHXSite interfave to allow + * the site user object to control all the sites through a single + * interface instance. + * + * IID_IHXMultiInstanceSiteUserSupplier: + * + * {00000D09-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXMultiInstanceSiteUserSupplier, 0x00000D09, 0x901, + 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#define CLSID_IHXMultiInstanceSiteUserSupplier \ + IID_IHXMultiInstanceSiteUserSupplier + +#undef INTERFACE +#define INTERFACE IHXMultiInstanceSiteUserSupplier + +DECLARE_INTERFACE_(IHXMultiInstanceSiteUserSupplier, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXMultiInstanceSiteUserSupplier methods called by site users. + */ + STDMETHOD(SetSingleSiteUser) (THIS_ + IUnknown* pUnknown) PURE; + + STDMETHOD(ReleaseSingleSiteUser) (THIS) PURE; + +}; + + +/**************************************************************************** + * + * Interface: + * + * IHXSiteEnumerator + * + * Purpose: + * + * Provides an interface to enumerate sites. Currently implemented + * in the IHXMultiInstanceSiteUserSupplier supplied by the core player, + * it can be used to render to MISUS sites outside of an HX_SURFACE_UPDATE; + * this is especially useful when using IHXVideoSurface2. + * + * IID_IHXSiteEnumerator: + * + * {67f8c5bd-4b1d-4c09-8fb7-8ac7c20d29c7} + * + */ +DEFINE_GUID(IID_IHXSiteEnumerator, 0x67f8c5bd, 0x4b1d, + 0x4c09, 0x8f, 0xb7, 0x8a, 0xc7, 0xc2, 0x0d, 0x29, 0xc7); + +#undef INTERFACE +#define INTERFACE IHXSiteEnumerator + +DECLARE_INTERFACE_(IHXSiteEnumerator, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + typedef void* SitePosition; + + /* + * HXSiteEnumerator methods + */ + + + /************************************************************************ + * Method: + * HXSiteEnumerator::GetFirstSite + * Purpose: + * Retrieves both the first site in the enumeration and initializes + * sitePosition with the position of the next site (if any). + * Returns HXR_OK if the first site is available, HXR_FAIL if not. + */ + STDMETHOD(GetFirstSite) (THIS_ + REF(IHXSite*) /* OUT */ pFirstSite, + REF(SitePosition) /* OUT */ nextPosition) PURE; + + /************************************************************************ + * Method: + * HXSiteEnumerator::GetNextSite + * Purpose: + * Retrieves both the next site in the enumeration (as specified by + * nextSite) and initializes sitePosition with the position of the + * following site (if any). + * Returns HXR_OK if the first site is available, HXR_FAIL if not. + */ + STDMETHOD(GetNextSite) (THIS_ + REF(IHXSite*) pNextSite, + REF(SitePosition) /* IN/OUT */ nextPosition) PURE; +}; + + +/**************************************************************************** + * + * Interface: + * IHXSiteFullScreen + * + * Purpose: + * + * This is the interface for turning on/off the full screen mode + * + * IID_IHXSiteFullScreen: + * + * {00000D0B-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXSiteFullScreen, 0x00000D0B, 0x901, + 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXSiteFullScreen + +DECLARE_INTERFACE_(IHXSiteFullScreen, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXSiteFullScreen methods + */ + STDMETHOD(EnterFullScreen) (THIS) PURE; + + STDMETHOD(ExitFullScreen) (THIS) PURE; + + STDMETHOD(TestFullScreen) (THIS_ + void* hTestBitmap,const char* pszStatusText) PURE; + + STDMETHOD_(HXBOOL, IsFullScreen) (THIS) PURE; +}; + + +// $Private: +/**************************************************************************** + * + * Interface: + * IHXLayoutSiteGroupManager + * + * Purpose: + * + * Allow layout site groups to be added and removed + * + * IID_IHXLayoutSiteGroupManager: + * + * {00000D0C-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXLayoutSiteGroupManager, 0x00000D0C, 0x901, + 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXLayoutSiteGroupManager + +DECLARE_INTERFACE_(IHXLayoutSiteGroupManager, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXLayoutSiteGroupManager methods + */ + STDMETHOD(AddLayoutSiteGroup) (THIS_ + IUnknown* pLSG) PURE; + + STDMETHOD(RemoveLayoutSiteGroup) (THIS_ + IUnknown* pLSG) PURE; +}; +// $EndPrivate. + + +/**************************************************************************** + * + * Interface: + * IHXEventHookMgr + * + * Purpose: + * + * Add ability to hook events from a named region + * + * IID_IHXEventHookMgr: + * + * {00000D0D-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXEventHookMgr, 0x00000D0D, 0x901, + 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXEventHookMgr + +DECLARE_INTERFACE_(IHXEventHookMgr, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXEventHookMgr methods + */ + STDMETHOD(AddHook) (THIS_ + IHXEventHook* pHook, + const char* pRegionName, + UINT16 uLayer) PURE; + + STDMETHOD(RemoveHook) (THIS_ + IHXEventHook* pHook, + const char* pRegionName, + UINT16 uLayer) PURE; +}; + +/**************************************************************************** + * + * Interface: + * IHXEventHook + * + * Purpose: + * + * Object that gets hooked events sent by IHXEventHookMgr + * + * IID_IHXEventHookMgr: + * + * {00000D0E-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXEventHook, 0x00000D0E, 0x901, + 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXEventHook + +DECLARE_INTERFACE_(IHXEventHook, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXEventHook methods + */ + STDMETHOD(SiteAdded) (THIS_ + IHXSite* pSite) PURE; + STDMETHOD(HandleEvent) (THIS_ + IHXSite* pSite, + HXxEvent* pEvent) PURE; + STDMETHOD(SiteRemoved) (THIS_ + IHXSite* pSite) PURE; +}; + + +/**************************************************************************** + * + * Interface: + * IHXStatusMessage + * + * Purpose: + * + * This is the interface for setting the status text. + * + * IID_IHXStatusMessage: + * + * {00000D10-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXStatusMessage, 0x00000D10, 0x901, + 0x11d1, 0x8b, 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXStatusMessage + +DECLARE_INTERFACE_(IHXStatusMessage, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXStatusMessage methods + */ + + STDMETHOD(SetStatus) (THIS_ const char* pText) PURE; +}; + +// $Private: +/**************************************************************************** + * + * Interface: + * + * IHXSiteTransition + * + * Purpose: + * + * Interface for transitioning between IHXSites. + * + * IID_IHXSiteTransition: + * + * {00000D01-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXSiteTransition, 0x00000D13, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#define CLSID_IHXSiteTransition IID_IHXSiteTransition + +#undef INTERFACE +#define INTERFACE IHXSiteTransition + +DECLARE_INTERFACE_(IHXSiteTransition, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + STDMETHOD(Initialize) (THIS_ + IHXValues* pParams) PURE; + + STDMETHOD(SetPercentage) (THIS_ + UINT32 nThousandnthsComplete) PURE; + +}; + + +/**************************************************************************** + * + * Interface: + * + * IHXRegion + * + * Purpose: + * + * Interface for managing HXRegions. + * + * IHXRegion: + * + * {00002200-0903-11d1-8B06-00A024406D59} + * + */ + + +DEFINE_GUID(IID_IHXRegion, 0x00000D14, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#define CLSID_IHXRegion IID_IHXRegion + +#undef INTERFACE +#define INTERFACE IHXRegion + +DECLARE_INTERFACE_(IHXRegion, IUnknown) +{ + /************************************************************************ + * Method: + * IHXRegion::SetRect + * Purpose: + * This function creates a rectangular region. + * + */ + STDMETHOD(SetRect) (THIS_ HXxRect* pRect) PURE; + + /************************************************************************ + * Method: + * IHXRegion::SetRect + * Purpose: + * This function creates a rectangular region. + * + */ + STDMETHOD(SetRect) (THIS_ int x, int y, int x1, int y1) PURE; + + /************************************************************************ + * Method: + * IHXRegion::SetPoly + * Purpose: + * This function creates a region defined by an arbitrary polygon. + * + */ + + STDMETHOD(SetPoly) (THIS_ HXxPoint** pRect, HXBOOL bUseWinding) PURE; + + /************************************************************************ + * Method: + * IHXRegion::IsEqual + * Purpose: + * This function determines if two regions are equal. + * + */ + + STDMETHOD_(HXBOOL,IsEqual) (THIS_ IHXRegion* pRegion) PURE; + + /************************************************************************ + * Method: + * IHXRegion::GetExtents + * Purpose: + * This function allows the user to determine the extents of the region + * + */ + + STDMETHOD(GetExtents) (THIS_ REF(HXxRect) rExtents) PURE; + + /************************************************************************ + * Method: + * IHXRegion::Offset + * Purpose: + * This function offsets the region by the spectified origin + * + */ + + STDMETHOD(Offset) (THIS_ HXxPoint* pOrigin) PURE; + + /************************************************************************ + * Method: + * IHXRegion::PointHitTest + * Purpose: + * This function returns if TRUE if the specified point is in the region + * + */ + + STDMETHOD_(HXBOOL,PointHitTest) (THIS_ HXxPoint* pPoint) PURE; + + /************************************************************************ + * Method: + * IHXRegion::RectHitTest + * Purpose: + * This function returns + * HX_CONTAINED if the rect is fully contained within the region + * HX_PART if part of the rect is within the region + * HX_OUT if no part of the rect is within the region + * + */ + + STDMETHOD_(INT32,RectHitTest) (THIS_ HXxRect* pRect) PURE; + + /************************************************************************ + * Method: + * IHXRegion::GetNumRects + * Purpose: + * This function gets the number of rects which describe the + * region + * + */ + + STDMETHOD_(UINT32, GetNumRects) (THIS) PURE; + + /************************************************************************ + * Method: + * IHXRegion::GetRectAtIndex + * Purpose: + * This function gets the RECT at index nRectIndex + * + */ + + STDMETHOD(GetRectAtIndex) (THIS_ UINT32 nRectIndex, REF(HXxRect) rRect) PURE; + + /************************************************************************ + * Method: + * IHXRegion::UnionRegion + * Purpose: + * Union Region -- this operator like all of the operators will create an IHXRegion + * if pDest is NULL. + * + */ + + STDMETHOD(UnionRegion) (THIS_ REF(IHXRegion*) pDest, IHXRegion* pSrc1) PURE; + + /************************************************************************ + * Method: + * IHXRegion::GetRectAtIndex + * Purpose: + * Copy Region + * + */ + + STDMETHOD(CopyRegion) (THIS_ REF(IHXRegion*) pDest) PURE; + + /************************************************************************ + * Method: + * IHXRegion::GetRectAtIndex + * Purpose: + * Diff Region + * + */ + + STDMETHOD(DiffRegion) (THIS_ REF(IHXRegion*) pDest, IHXRegion* pSrc1) PURE; + + /************************************************************************ + * Method: + * IHXRegion::GetRectAtIndex + * Purpose: + * And Region + * + */ + + STDMETHOD(AndRegion) (THIS_ REF(IHXRegion*) pDest, IHXRegion* pSrc1) PURE; + + /************************************************************************ + * Method: + * IHXRegion::GetRectAtIndex + * Purpose: + * XOR Region + * + */ + + STDMETHOD(XORRegion) (THIS_ REF(IHXRegion*) pDest, IHXRegion* pSrc1) PURE; + + /************************************************************************ + * Method: + * IHXRegion::GetRegion + * Purpose: + * Bad hack for the moment to obtain the REGION pointer of an HXRegion. + * Will have to remove this later. + * + */ + + STDMETHOD_(void*, GetRegion) (THIS) PURE; + +}; + +/**************************************************************************** + * + * Interface: + * + * IHXColorConverterManager + * + * Purpose: + * + * Interface for obtaining IHXColorConverters + * + * IHXColorConverterManager: + * + * {00000D15-0902-11d1-8B06-00A024406D59} + * + */ + +DEFINE_GUID(IID_IHXColorConverterManager, 0x00000D15, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXColorConverterManager + +DECLARE_INTERFACE_(IHXColorConverterManager, IUnknown) +{ + /* + * Get ColorConverter is called to obtain a color converter to convert + * from a particular bitmap to another bitmap. + */ + STDMETHOD(GetColorConverter) (THIS_ + HXBitmapInfoHeader* /*IN*/ pBitmapInfoIn, + HXBitmapInfoHeader* /*IN*/ pBitmapInfoOut, + REF(IHXColorConverter*) /*OUT*/ pConverter) PURE; + +}; + +/**************************************************************************** + * + * Interface: + * + * IHXColorConverter + * + * Purpose: + * + * Interface for converting between two bitmaps of different color formats. + * + * IHXColorConverterManager: + * + * {00000D16-0902-11d1-8B06-00A024406D59} + * + */ + +DEFINE_GUID(IID_IHXColorConverter, 0x00000D16, 0x903, 0x11d1, 0x8b, + 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXColorConverter + +DECLARE_INTERFACE_(IHXColorConverter, IUnknown) +{ + /* + * ColorConvert converts the pBitsIn from one color format to + * the format of pBitsOut + */ + STDMETHOD(ColorConvert) (THIS_ + UCHAR* pBitsIn, + UCHAR* pBitsOut, + HXxRect* pRectIn, + HXxRect* pRectOut + ) PURE; + +}; + +/**************************************************************************** + * + * Interface: + * + * IHXOverlayResponse + * + * Purpose: + * + * Interface for reporting/computing the current statistics relevant to + * Video Presentations. + * + * IHXOverlayResponse: + * + * {00000D22-0902-11d1-8B06-00A024406D59} + * + */ + +DEFINE_GUID(IID_IHXOverlayResponse, 0x00000D22, 0x903, 0x11d1, 0x8b, + 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXOverlayResponse + +DECLARE_INTERFACE_(IHXOverlayResponse, IUnknown) +{ + STDMETHOD(OverlayGranted) (THIS ) PURE; + STDMETHOD(OverlayRevoked) (THIS ) PURE; +}; + + +/**************************************************************************** + * + * Interface: + * + * IHXOverlayManager + * + * Purpose: + * + * Interface for reporting/computing the current statistics relevant to + * Video Presentations. + * + * IHXOverlayManager: + * + * {00000D21-0902-11d1-8B06-00A024406D59} + * + */ + +DEFINE_GUID(IID_IHXOverlayManager, 0x00000D21, 0x903, 0x11d1, 0x8b, + 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXOverlayManager + +DECLARE_INTERFACE_(IHXOverlayManager, IUnknown) +{ + STDMETHOD(HasOverlay) (THIS_ + IHXOverlayResponse* pResp + ) PURE; + + STDMETHOD(AddStats) (THIS_ + IHXOverlayResponse* pResp, + UINT32 ulNumPixels + ) PURE; + + + STDMETHOD(RemoveOverlayRequest)(THIS_ IHXOverlayResponse* pResp ) PURE; +}; + + +// $EndPrivate. + +/**************************************************************************** + * + * Interface: + * + * IHXKeyBoardFocus + * + * Purpose: + * + * Interface for setting/getting the keyboard focus for a particular siteuser + * + * IHXKeyBoardFocus: + * + * {00000D23-0902-11d1-8B06-00A024406D59} + * + */ + +DEFINE_GUID(IID_IHXKeyBoardFocus, 0x00000D23, 0x903, 0x11d1, 0x8b, + 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXKeyBoardFocus + +DECLARE_INTERFACE_(IHXKeyBoardFocus, IUnknown) +{ + STDMETHOD(SetKeyboardFocus)(THIS_ IHXSiteUser* pSiteUser ) PURE; + STDMETHOD(GetKeyboardFocus)(THIS_ IHXSiteUser* &pSiteUser ) PURE; + +}; + +/**************************************************************************** + * + * Interface: + * + * IHXDrawFocus + * + * Purpose: + * + * Interface for displaying the site that has the keyboard focus + * + * IHXDrawFocus: + * + * {00000D24-0902-11d1-8B06-00A024406D59} + * + */ + +DEFINE_GUID(IID_IHXDrawFocus, 0x00000D24, 0x903, 0x11d1, 0x8b, + 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXDrawFocus + +DECLARE_INTERFACE_(IHXDrawFocus, IUnknown) +{ + /************************************************************************ + * Method: + * IHXDrawFocus::SetStyle + * Purpose: + * Ask the site to set the focus style. + * + * Syles: + * ULONG32 Properties: + * + * LINE_STYLE = HX_SOLID_LINE, HX_DASHED_LINE, HX_DOTTED_LINE, + * HX_CUSTOM_LINE + * LINE_WIDTH = Width of the line in pixels + * RED = 0-255 color of the primary pixel + * GREEN = 0-255 color of the primary pixel + * BLUE = 0-255 color of the primary pixel + * RED_OFF = 0-255 color of the secondary pixel + * GREEN_OFF = 0-255 color of the secondary pixel + * BLUE_OFF = 0-255 color of the secondary pixel + * CUSTOM_LINE_ENTRIES number of ULONG32s in CUSTOM_LINE_PATTERN + * + * IHXBuffer Properties: + * + * CUSTOM_LINE_PATTERN list of ULONG32s describing the number + * of primary and secondary pixels (eq 4241 = "----..----." where + * - is a primary pixel and . is a secondary pixel) and + * CUSTOM_LINE_ENTRIES equals 4. Secondary pixels are not + * drawn if RED_OFF, GREEN_OFF, and BLUE_OFF are not set. + */ + STDMETHOD(SetStyle) (THIS_ IHXValues* pProperties) PURE; + + /************************************************************************ + * Method: + * IHXDrawFocus::ClearFocus + * Purpose: + * Ask the site to clear the current focus drawing. + */ + STDMETHOD(ClearFocus)(THIS) PURE; + + /************************************************************************ + * Method: + * IHXDrawFocus::SetFocusPolygon + * Purpose: + * Ask the site to draw polygon around focus + */ + STDMETHOD(SetFocusPolygon)(THIS_ HXxPoint* pPoints, ULONG32 numPoints) PURE; + + /************************************************************************ + * Method: + * IHXDrawFocus::SetFocusRect + * Purpose: + * Ask the site to draw rectangle around focus + */ + STDMETHOD(SetFocusRect) (THIS_ HXxRect* pRect) PURE; + + /************************************************************************ + * Method: + * IHXDrawFocus::SetFocusEllipse + * Purpose: + * Ask the site to draw ellipse around focus + */ + STDMETHOD(SetFocusEllipse) (THIS_ HXxRect* pRect) PURE; +}; + +// $Private: + +/**************************************************************************** + * + * Interface: + * + * IHXSubRectSite + * + * Purpose: + * + * Interface to determine if a site support sub rect BLT'ing via + * the HX_SURFACE_UPDATE2 message. If the site does support sub + * rect BLT'ing you can tell the site to send you the + * HX_SURFACE_UPDATE2 messages via this interface. + * + * IHXSubRectSite: + * + * {00000D25-0902-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXSubRectSite, 0x00000D25, 0x903, 0x11d1, 0x8b, + 0x6, 0x0, 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXSubRectSite + +DECLARE_INTERFACE_(IHXSubRectSite, IHXSite) +{ + /* + * Tells the site to send/not-send HX_SURFACE_UPDATE2 messages. + * These messages contain actuall dirty rects so that the renderer + * does not need to BLT the entire frame. + */ + STDMETHOD(SendSubRectMessages) (THIS_ HXBOOL bRet ) PURE; + /* + * New damage region call that takes the cross platfrom region + * defined in hxwintyp.h and invalidates the rects in it + */ + STDMETHOD(SubRectDamageRegion) (THIS_ HXxBoxRegion* pRegion ) PURE; + /* + * Method to get the new video surface that comes with the sub + * rect BLT'ing support. + */ + STDMETHOD(GetSubRectVideoSurface) (THIS_ + REF(IHXSubRectVideoSurface*) pSurface + ) PURE; +}; + +// $EndPrivate. + +/**************************************************************************** + * + * Interface: + * + * IHXFocusNavigation + * + * Purpose: + * + * Interface for navigating between and within keyboard focus sites + * + * IHXFocusNavigation: + * + * {B42B7677-F605-438e-9002-E2AAB7784B43} + * + */ + +DEFINE_GUID(IID_IHXFocusNavigation, 0xb42b7677, 0xf605, 0x438e, 0x90, + 0x2, 0xe2, 0xaa, 0xb7, 0x78, 0x4b, 0x43); + +#undef INTERFACE +#define INTERFACE IHXFocusNavigation + +DECLARE_INTERFACE_(IHXFocusNavigation, IUnknown) +{ + /************************************************************************ + * Method: + * IHXFocusNavigation::SetFocus + * Purpose: + * Ask the renderer to set the focus to the given item. + */ + STDMETHOD(SetFocus) (THIS_ HXFocusContext eFocus) PURE; + + /************************************************************************ + * Method: + * IHXFocusNavigation::ClearFocus + * Purpose: + * Ask the renderer to clear the current focus. + */ + STDMETHOD(ClearFocus) (THIS) PURE; + + /************************************************************************ + * Method: + * IHXFocusNavigation::ActivateFocus + * Purpose: + * Ask the renderer to activate the focused link. Do nothing if + * there is no focus point. + */ + STDMETHOD(ActivateFocus) (THIS) PURE; + + /************************************************************************ + * Method: + * IHXFocusNavigation::GetFocusState + * Purpose: + * Obtain the current focus state + */ + STDMETHOD_(HXFocusState,GetFocusState) (THIS) PURE; +}; + +#endif /* _HXWIN_H_ */ diff --git a/amarok/src/engine/helix/helix-sp/helix-include/common/include/hxwintyp.h b/amarok/src/engine/helix/helix-sp/helix-include/common/include/hxwintyp.h new file mode 100644 index 00000000..4316cc2f --- /dev/null +++ b/amarok/src/engine/helix/helix-sp/helix-include/common/include/hxwintyp.h @@ -0,0 +1,374 @@ + +/* + * + * This software is released under the provisions of the GPL version 2. + * see file "COPYING". If that file is not available, the full statement + * of the license can be found at + * + * http://www.fsf.org/licensing/licenses/gpl.txt + * + * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved. + * + */ + +#ifndef _HXWINTYP_H_ +#define _HXWINTYP_H_ + +#include "hxtypes.h" /* Needed at least for various defines and types. */ + +#ifdef _WIN16 +#define BI_BITFIELDS 3L +#endif + +#ifdef _SYMBIAN +#include <coemain.h> +#include <w32std.h> +#endif + +/**************************************************************************** + * + * Structure: + * + * HXxSize + * + * Purpose: + * + * Cross Platform definition of a size. + * + */ +typedef struct HXEXPORT_CLASS _HXxSize +{ + INT32 cx; + INT32 cy; +} HXxSize; + +#ifdef __cplusplus + +inline HXBOOL operator ==( const HXxSize& a, const HXxSize& b ) +{ + return ( a.cx == b.cx ) && ( a.cy == b.cy ); +} + +inline HXBOOL operator !=( const HXxSize& a, const HXxSize& b ) +{ + return !( a == b ); +} + +#endif // __cplusplus + +/**************************************************************************** + * + * Structure: + * + * HXxPoint + * + * Purpose: + * + * Cross Platform definition of a point. + * + */ +typedef struct HXEXPORT_CLASS _HXxPoint +{ + INT32 x; + INT32 y; +} HXxPoint; + +#ifdef __cplusplus + +inline HXBOOL operator ==( const HXxPoint& a, const HXxPoint& b ) +{ + return ( a.x == b.x ) && ( a.y == b.y ); +} + +inline HXBOOL operator !=( const HXxPoint& a, const HXxPoint& b ) +{ + return !( a == b ); +} + +#endif // __cplusplus + + + +/**************************************************************************** + * + * Structure: + * + * HXxRect + * + * Purpose: + * + * Cross Platform definition of a rectangle. + * + */ +typedef struct HXEXPORT_CLASS _HXxRect +{ + INT32 left; + INT32 top; + INT32 right; + INT32 bottom; +} HXxRect; + +#define HXxRECT_WIDTH(r) ((r).right - (r).left) +#define HXxRECT_HEIGHT(r) ((r).bottom - (r).top) + +#ifdef __cplusplus + +inline HXBOOL operator ==( const HXxRect& a, const HXxRect& b ) +{ + return ( a.left == b.left ) && + ( a.top == b.top ) && + ( a.right == b.right ) && + ( a.bottom == b.bottom ); +} + +inline HXBOOL operator !=( const HXxRect& a, const HXxRect& b ) +{ + return !( a == b ); +} + +inline HXBOOL HXxRect_IsEmpty( const HXxRect& rect ) +{ + return ( rect.left >= rect.right ) || + ( rect.top >= rect.bottom ); +} + +inline void HXxRect_Intersection( const HXxRect& r1, const HXxRect& r2, HXxRect* result ) +{ + result->left = ( r1.left > r2.left ) ? r1.left : r2.left; + result->top = ( r1.top > r2.top ) ? r1.top : r2.top; + result->right = ( r1.right < r2.right ) ? r1.right : r2.right; + result->bottom = ( r1.bottom < r2.bottom ) ? r1.bottom : r2.bottom; +} + +#endif // __cplusplus + +/**************************************************************************** + * + * Structure: + * + * HXxWindow + * + * Purpose: + * + * Cross Platform definition of a window. This struct is sufficiently + * wide to describe parent or child windows in Windows, MacOS, and + * various flavors of X-Windows. + * + * Data Members: + * + * void* window + * platform specific window handle + * + * ULONG32 x, y + * position of top left corner relative to a client page + * + * ULONG32 width, height + * maximum window size + * + * HXxRect clipRect; + * clipping rectangle in port coordinates + * + */ +typedef struct HXEXPORT_CLASS _HXxWindow +{ + /* NOTE: The window parameter is NOT guaranteed to be unique for every + corresponding CHXWindow. Use HXxWindowID if this is desired. */ + void* window; + ULONG32 x; + ULONG32 y; + ULONG32 width; + ULONG32 height; + HXxRect clipRect; +#ifdef _UNIX + void * display; +#endif +#ifdef _SYMBIAN + CDirectScreenAccess* iDSA; +#endif +} HXxWindow; + +typedef void* HXxWindowID; + +/**************************************************************************** + * + * Structure: + * + * HXxEvent + * + * Purpose: + * + * Cross Platform definition of a event. This struct is sufficiently + * wide to describe an event in Windows, MacOS, and various flavors of + * X-Windows. + * + * Data Members: + * + * void* event + * platform specific event ID, can also be one of the several HXxMSG_* + * event IDs which map onto existing platform specific event IDs + * UNIX: X Event Type + * + * void* window + * platform specific window handle + * UNIX: X Window ID + * + * void* param1 + * message specific parameter + * UNIX: Display* + * + * void* param2 + * Mac: for UpdateEvt, either NULL or RgnHandle to be filled with updated area + * UNIX: Native XEvent* + * HX_SURFACE_UPDATE HXxWindow* + * + */ +typedef struct HXEXPORT_CLASS _HXxEvent +{ + ULONG32 event; /* IN */ + void* window; /* IN */ + void* param1; /* IN */ + void* param2; /* IN */ + + UINT32 result; /* OUT */ + HXBOOL handled; /* OUT */ +} HXxEvent; + + +/**************************************************************************** + * + * typedef: + * + * HXxRegion + * + * Purpose: + * + * Cross Platform definition of a region. This typedef is redefined as + * appropriate to describe a region in Windows, MacOS, and various + * flavors of X-Windows. + * + */ +typedef void* HXxRegion; + +/**************************************************************************** + * + * typedef: + * + * HXxDC + * + * Purpose: + * + * Cross Platform definition of a device context. This typedef is redefined as + * appropriate to describe a device context in Windows, MacOS, and various + * flavors of X-Windows. + * + */ +typedef void* HXxDC; + +/**************************************************************************** + * + * typedef: + * + * HXxFont + * + * Purpose: + * + * Cross Platform definition of a font. This typedef is redefined as + * appropriate to describe a font in Windows, MacOS, and various + * flavors of X-Windows. + * + */ +typedef void* HXxFont; + +/**************************************************************************** + * + * typedef: + * + * HXxColor + * + * Purpose: + * + * Cross Platform definition of a color. This typedef is redefined as + * appropriate to describe a font in Windows, MacOS, and various + * flavors of X-Windows. + * + */ +typedef ULONG32 HXxColor; + +/**************************************************************************** + * + * typedef: + * + * HXxIcon + * + * Purpose: + * + * Cross Platform definition of a icon. This typedef is redefined as + * appropriate to describe a font in Windows, MacOS, and various + * flavors of X-Windows. + * + */ +typedef void* HXxIcon; + +/**************************************************************************** + * + * typedef: + * + * HXxMenu + * + * Purpose: + * + * Cross Platform definition of a menu. This typedef is redefined as + * appropriate to describe a font in Windows, MacOS, and various + * flavors of X-Windows. + * + */ +typedef void* HXxMenu; + +/**************************************************************************** + * + * typedef: + * + * HXxCursor + * + * Purpose: + * + * Cross Platform definition of a cursor. This typedef is redefined as + * appropriate to describe a cursor in Windows, MacOS, and various + * flavors of X-Windows. + * + */ +typedef void* HXxCursor; + + +/**************************************************************************** + * + * Structure: + * + * HXREGION + * + * Purpose: + * + * Cross Platform Region definition. + */ +typedef struct HXEXPORT_CLASS _HXBox +{ + short x1, x2, y1, y2; +} HXBOX, *HXBoxPtr; + +typedef struct HXEXPORT_CLASS _HXxRegion +{ + HXBOX* rects; + long numRects; +} HXxBoxRegion, *HXxRegionPtr; + +//Definition of the ExposeInfo structure pass with HX_SURFACE_UPDATE2. +typedef struct HXEXPORT_CLASS _HXxExposeInfo +{ + HXxRect extents; //The bounding rect of all dirty rects. + HXxBoxRegion* pRegion; //Pointer to dirty region. DO NOT MODIFY. + HXxWindow* pWindow; //Pointer to the HXxWindow for this site. + void* pParam1; //Reserved + void* pParam2; //Reserved +} HXxExposeInfo; + +#endif /* _HXWINTYP_H_ */ diff --git a/amarok/src/engine/helix/helix-sp/helix-include/common/include/ihxpckts.h b/amarok/src/engine/helix/helix-sp/helix-include/common/include/ihxpckts.h new file mode 100644 index 00000000..c4a927a8 --- /dev/null +++ b/amarok/src/engine/helix/helix-sp/helix-include/common/include/ihxpckts.h @@ -0,0 +1,660 @@ + +/* + * + * This software is released under the provisions of the GPL version 2. + * see file "COPYING". If that file is not available, the full statement + * of the license can be found at + * + * http://www.fsf.org/licensing/licenses/gpl.txt + * + * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved. + * + */ + +#ifndef _IHXPCKTS_H_ +#define _IHXPCKTS_H_ + +// Define IHXUtilities +// $Private +#include "hxvalue.h" +// $EndPrivate + +/* ASMFlags in IHXPacket */ +#define HX_ASM_SWITCH_ON 0x01 +#define HX_ASM_SWITCH_OFF 0x02 +#define HX_ASM_DROPPED_PKT 0x04 + + +/**************************************************************************** + * + * Interface: + * + * IHXBuffer + * + * Purpose: + * + * Basic opaque data storage buffer. Used in interfaces where + * object ownership is best managed through COM style reference + * counting. + * + * IID_IHXBuffer: + * + * {00001300-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXBuffer, 0x00001300, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +/* + * The IHXCommonClassFactory supports creating an instance + * of this object. + */ +#define CLSID_IHXBuffer IID_IHXBuffer + +#undef INTERFACE +#define INTERFACE IHXBuffer + +DECLARE_INTERFACE_(IHXBuffer, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXBuffer methods + */ + STDMETHOD(Get) (THIS_ + REF(UCHAR*) pData, + REF(ULONG32) ulLength) PURE; + + STDMETHOD(Set) (THIS_ + const UCHAR* pData, + ULONG32 ulLength) PURE; + + STDMETHOD(SetSize) (THIS_ + ULONG32 ulLength) PURE; + + STDMETHOD_(ULONG32,GetSize) (THIS) PURE; + + STDMETHOD_(UCHAR*,GetBuffer)(THIS) PURE; +}; + + +/**************************************************************************** + * + * Interface: + * + * IHXPacket + * + * Purpose: + * + * Basic data packet in the RealMedia system. + * + * IID_IHXPacket: + * + * {00001301-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXPacket, 0x00001301, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +/* + * The IHXCommonClassFactory supports creating an instance + * of this object. + */ +#define CLSID_IHXPacket IID_IHXPacket + +#undef INTERFACE +#define INTERFACE IHXPacket + +DECLARE_INTERFACE_(IHXPacket, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXPacket methods + */ + STDMETHOD(Get) (THIS_ + REF(IHXBuffer*) pBuffer, + REF(UINT32) ulTime, + REF(UINT16) unStreamNumber, + REF(UINT8) unASMFlags, + REF(UINT16) unASMRuleNumber + ) PURE; + + STDMETHOD_(IHXBuffer*,GetBuffer) (THIS) PURE; + + STDMETHOD_(ULONG32,GetTime) (THIS) PURE; + + STDMETHOD_(UINT16,GetStreamNumber) (THIS) PURE; + + STDMETHOD_(UINT8,GetASMFlags) (THIS) PURE; + + STDMETHOD_(UINT16,GetASMRuleNumber) (THIS) PURE; + + STDMETHOD_(HXBOOL,IsLost) (THIS) PURE; + + STDMETHOD(SetAsLost) (THIS) PURE; + + STDMETHOD(Set) (THIS_ + IHXBuffer* pBuffer, + UINT32 ulTime, + UINT16 uStreamNumber, + UINT8 unASMFlags, + UINT16 unASMRuleNumber + ) PURE; +}; + + +/**************************************************************************** + * + * Interface: + * + * IHXPacket + * + * Purpose: + * + * RTP data packet in the RealMedia system. + * + * IID_IHXRTPPacket: + * + * {0169A731-1ED0-11d4-952B-00902742C923} + * + */ +DEFINE_GUID(IID_IHXRTPPacket, 0x169a731, 0x1ed0, 0x11d4, 0x95, 0x2b, 0x0, + 0x90, 0x27, 0x42, 0xc9, 0x23); + +/* + * The IHXCommonClassFactory supports creating an instance + * of this object. + */ +#define CLSID_IHXRTPPacket IID_IHXRTPPacket + +#undef INTERFACE +#define INTERFACE IHXRTPPacket + +DECLARE_INTERFACE_(IHXRTPPacket, IHXPacket) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXPacket methods + */ + STDMETHOD(Get) (THIS_ + REF(IHXBuffer*) pBuffer, + REF(UINT32) ulTime, + REF(UINT16) unStreamNumber, + REF(UINT8) unASMFlags, + REF(UINT16) unASMRuleNumber + ) PURE; + + STDMETHOD_(IHXBuffer*,GetBuffer) (THIS) PURE; + + STDMETHOD_(ULONG32,GetTime) (THIS) PURE; + + STDMETHOD_(UINT16,GetStreamNumber) (THIS) PURE; + + STDMETHOD_(UINT8,GetASMFlags) (THIS) PURE; + + STDMETHOD_(UINT16,GetASMRuleNumber) (THIS) PURE; + + STDMETHOD_(HXBOOL,IsLost) (THIS) PURE; + + STDMETHOD(SetAsLost) (THIS) PURE; + + STDMETHOD(Set) (THIS_ + IHXBuffer* pBuffer, + UINT32 ulTime, + UINT16 uStreamNumber, + UINT8 unASMFlags, + UINT16 unASMRuleNumber + ) PURE; + + /* + * IHXRTPPacket methods + */ + STDMETHOD_(ULONG32,GetRTPTime) (THIS) PURE; + + STDMETHOD(GetRTP) (THIS_ + REF(IHXBuffer*) pBuffer, + REF(UINT32) ulTime, + REF(UINT32) ulRTPTime, + REF(UINT16) unStreamNumber, + REF(UINT8) unASMFlags, + REF(UINT16) unASMRuleNumber + ) PURE; + + STDMETHOD(SetRTP) (THIS_ + IHXBuffer* pBuffer, + UINT32 ulTime, + UINT32 ulRTPTime, + UINT16 uStreamNumber, + UINT8 unASMFlags, + UINT16 unASMRuleNumber + ) PURE; +}; + +/**************************************************************************** + * + * Interface: + * + * IHXRTPPacketInfo + * + * Purpose: + * + * Provides complete RTP packet header info (RFC 1889) + * + * IID_IHXPacket: + * + * {EC7D67BB-2E79-49c3-B667-BA8A938DBCE0} + * + */ +DEFINE_GUID(IID_IHXRTPPacketInfo, + 0xec7d67bb, 0x2e79, 0x49c3, 0xb6, 0x67, 0xba, 0x8a, 0x93, 0x8d, 0xbc, 0xe0); + +#undef INTERFACE +#define INTERFACE IHXRTPPacketInfo + +DECLARE_INTERFACE_(IHXRTPPacketInfo, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ REFIID riid, void** ppvObj) PURE; + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXRTPPacketInfo methods + */ + STDMETHOD_(UINT8, GetVersion) (THIS) PURE; + + STDMETHOD(GetPaddingBit) (THIS_ REF(HXBOOL)bPadding) PURE; + STDMETHOD(SetPaddingBit) (THIS_ HXBOOL bPadding) PURE; + + STDMETHOD(GetExtensionBit) (THIS_ REF(HXBOOL)bExtension) PURE; + STDMETHOD(SetExtensionBit) (THIS_ HXBOOL bExtension) PURE; + + STDMETHOD(GetCSRCCount) (THIS_ REF(UINT8)unCSRCCount) PURE; + STDMETHOD(SetCSRCCount) (THIS_ UINT8 unCSRCCount) PURE; + + STDMETHOD(GetMarkerBit) (THIS_ REF(HXBOOL)bMarker) PURE; + STDMETHOD(SetMarkerBit) (THIS_ HXBOOL bMarker) PURE; + + STDMETHOD(GetPayloadType) (THIS_ REF(UINT8)unPayloadType) PURE; + STDMETHOD(SetPayloadType) (THIS_ UINT8 unPayloadType) PURE; + + STDMETHOD(GetSequenceNumber) (THIS_ REF(UINT16)unSeqNo) PURE; + STDMETHOD(SetSequenceNumber) (THIS_ UINT16 unSeqNo) PURE; + + STDMETHOD(GetTimeStamp) (THIS_ REF(UINT32)ulTS) PURE; + STDMETHOD(SetTimeStamp) (THIS_ UINT32 ulTS) PURE; + + STDMETHOD(GetSSRC) (THIS_ REF(UINT32)ulSSRC) PURE; + STDMETHOD(SetSSRC) (THIS_ UINT32 ulSSRC) PURE; + + + STDMETHOD(GetCSRCList) (THIS_ REF(const char*) pulCSRC) PURE; + STDMETHOD(SetCSRCList) (THIS_ const char* pCSRCList, UINT32 ulSize) PURE; + STDMETHOD(GetPadding) (THIS_ REF(const char*) pPadding) PURE; + STDMETHOD(SetPadding) (THIS_ const char* pPadding, UINT32 ulSize) PURE; + STDMETHOD(GetExtension) (THIS_ REF(const char*) pExtension) PURE; + STDMETHOD(SetExtension) (THIS_ const char* pExtension, UINT32 ulSize) PURE; +}; + + +/**************************************************************************** + * + * Interface: + * + * IHXValues + * + * Purpose: + * + * This is an interface to a generic name-value pair facility. This + * is used in various places (such as stream headers). + * + * IID_IHXValues: + * + * {00001302-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXValues, 0x00001302, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +/* + * The IHXCommonClassFactory supports creating an instance + * of this object. + */ +#define CLSID_IHXValues IID_IHXValues + +#undef INTERFACE +#define INTERFACE IHXValues + +DECLARE_INTERFACE_(IHXValues, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXValues methods + */ + + /* + * Note: That strings returned as references should be copied or + * used immediately because their lifetime is only as long as the + * IHXValues's objects lifetime. + * + * Note: Your iterator will be reset once you give up control to the + * RMA core (i.e. you exit whatever function gave you a time slice). + */ + + STDMETHOD(SetPropertyULONG32) (THIS_ + const char* pPropertyName, + ULONG32 uPropertyValue) PURE; + + STDMETHOD(GetPropertyULONG32) (THIS_ + const char* pPropertyName, + REF(ULONG32) uPropertyName) PURE; + + STDMETHOD(GetFirstPropertyULONG32) (THIS_ + REF(const char*) pPropertyName, + REF(ULONG32) uPropertyValue) PURE; + + STDMETHOD(GetNextPropertyULONG32) (THIS_ + REF(const char*) pPropertyName, + REF(ULONG32) uPropertyValue) PURE; + + STDMETHOD(SetPropertyBuffer) (THIS_ + const char* pPropertyName, + IHXBuffer* pPropertyValue) PURE; + + STDMETHOD(GetPropertyBuffer) (THIS_ + const char* pPropertyName, + REF(IHXBuffer*) pPropertyValue) PURE; + + STDMETHOD(GetFirstPropertyBuffer) (THIS_ + REF(const char*) pPropertyName, + REF(IHXBuffer*) pPropertyValue) PURE; + + STDMETHOD(GetNextPropertyBuffer) (THIS_ + REF(const char*) pPropertyName, + REF(IHXBuffer*) pPropertyValue) PURE; + + STDMETHOD(SetPropertyCString) (THIS_ + const char* pPropertyName, + IHXBuffer* pPropertyValue) PURE; + + STDMETHOD(GetPropertyCString) (THIS_ + const char* pPropertyName, + REF(IHXBuffer*) pPropertyValue) PURE; + + STDMETHOD(GetFirstPropertyCString) (THIS_ + REF(const char*) pPropertyName, + REF(IHXBuffer*) pPropertyValue) PURE; + + STDMETHOD(GetNextPropertyCString) (THIS_ + REF(const char*) pPropertyName, + REF(IHXBuffer*) pPropertyValue) PURE; +}; + +/**************************************************************************** + * + * Interface: + * + * IHXValues2 + * + * Purpose: + * This is an extension to the IHXValues interface. This extension + * let's store IUnknown properties and remove values + * + * IID_IHXValues2: + * + * {7AE64D81-C5AB-4b0a-94F2-B4D6DD2BDA7A} + * + */ +DEFINE_GUID(IID_IHXValues2, +0x7ae64d81, 0xc5ab, 0x4b0a, 0x94, 0xf2, 0xb4, 0xd6, 0xdd, 0x2b, 0xda, 0x7a); + +#define CLSID_IHXValues2 IID_IHXValues2 + +#undef INTERFACE +#define INTERFACE IHXValues2 + +DECLARE_INTERFACE_(IHXValues2, IHXValues) +{ + /* + * IHXValues2 methods + */ + + STDMETHOD(SetPropertyObject) (THIS_ + const char* pPropertyName, + IUnknown* pPropertyValue) PURE; + + STDMETHOD(GetPropertyObject) (THIS_ + const char* pPropertyName, + REF(IUnknown*) pPropertyValue) PURE; + + STDMETHOD(GetFirstPropertyObject) (THIS_ + REF(const char*) pPropertyName, + REF(IUnknown*) pPropertyValue) PURE; + + STDMETHOD(GetNextPropertyObject) (THIS_ + REF(const char*) pPropertyName, + REF(IUnknown*) pPropertyValue) PURE; + + /************************************************************************ + * Method: + * IHXValues2::Remove + * Purpose: + * Remove all items matching pKey. (If you know what datatype you saved + * the key as, use the specific method.) + */ + STDMETHOD(Remove) (const char* pKey) PURE; + + /************************************************************************ + * Method: + * IHXValues2::RemoveULONG32 + * Purpose: + * Remove all ULONG32 items matching pKey. + */ + STDMETHOD(RemoveULONG32) (const char* pKey) PURE; + + /************************************************************************ + * Method: + * IHXValues2::RemoveBuffer + * Purpose: + * Remove all Buffer items matching pKey. + */ + STDMETHOD(RemoveBuffer) (const char* pKey) PURE; + + /************************************************************************ + * Method: + * IHXValues2::RemoveCString + * Purpose: + * Remove all CString items matching pKey. + */ + STDMETHOD(RemoveCString) (const char* pKey) PURE; + + /************************************************************************ + * Method: + * IHXValues2::RemoveObject + * Purpose: + * Remove all IUnknown items matching pKey. + */ + STDMETHOD(RemoveObject) (const char* pKey) PURE; +}; + +/**************************************************************************** + * + * Interface: + * + * IHXValuesRemove + * + * Purpose: + * + * This interface is to add Remove methods to a class that supports + * IHXValues. All classes that support this interface will also + * support IHXValues. + * + * + * + * IID_IHXValuesRemove: + * + * {00001303-0901-11d1-8B06-00A024406D59} + * + */ +DEFINE_GUID(IID_IHXValuesRemove, 0x00001303, 0x901, 0x11d1, 0x8b, 0x6, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +/* + * The IHXCommonClassFactory does not support creating an instance + * of this object. + */ + +#undef INTERFACE +#define INTERFACE IHXValuesRemove + +DECLARE_INTERFACE_(IHXValuesRemove, IUnknown) +{ + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + /* + * IHXValuesRemove methods + */ + + /************************************************************************ + * Method: + * IHXKeyValuesRemove::Remove + * Purpose: + * Remove all items matching pKey. (If you know what datatype you saved + * the key as, use the specific method.) + */ + STDMETHOD(Remove) (const char* pKey) PURE; + + /************************************************************************ + * Method: + * IHXKeyValuesRemove::RemoveULONG32 + * Purpose: + * Remove all ULONG32 items matching pKey. + */ + STDMETHOD(RemoveULONG32) (const char* pKey) PURE; + + /************************************************************************ + * Method: + * IHXKeyValuesRemove::RemoveBuffer + * Purpose: + * Remove all Buffer items matching pKey. + */ + STDMETHOD(RemoveBuffer) (const char* pKey) PURE; + + /************************************************************************ + * Method: + * IHXKeyValuesRemove::RemoveCString + * Purpose: + * Remove all CString items matching pKey. + */ + STDMETHOD(RemoveCString) (const char* pKey) PURE; +}; + +// $Private: +DEFINE_GUID(IID_IHXClientPacket, 0x00001304, 0x0901, 0x11d1, 0x8b, 0x06, 0x0, + 0xa0, 0x24, 0x40, 0x6d, 0x59); + +#undef INTERFACE +#define INTERFACE IHXClientPacket + +DECLARE_INTERFACE_(IHXClientPacket, IUnknown) +{ + /* + * IUnknown methods + */ + + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; +}; + +DEFINE_GUID(IID_IHXBroadcastDistPktExt, 0x3b022922, 0x94a1, 0x4be5, 0xbd, 0x25, 0x21, + 0x6d, 0xa2, 0x7b, 0xd8, 0xfc); + +#undef INTERFACE +#define INTERFACE IHXBroadcastDistPktExt + +DECLARE_INTERFACE_(IHXBroadcastDistPktExt, IUnknown) +{ + /* + * IUnknown methods + */ + + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj) PURE; + + STDMETHOD_(ULONG32,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG32,Release) (THIS) PURE; + + STDMETHOD_(UINT32,GetSeqNo) (THIS) PURE; + STDMETHOD_(UINT32,GetStreamSeqNo) (THIS) PURE; + STDMETHOD_(HXBOOL,GetIsLostRelaying) (THIS) PURE; + STDMETHOD_(HXBOOL,SupportsLowLatency) (THIS) PURE; + STDMETHOD_(UINT16,GetRuleSeqNoArraySize) (THIS) PURE; + STDMETHOD_(UINT16*,GetRuleSeqNoArray) (THIS) PURE; + + STDMETHOD(SetSeqNo) (THIS_ UINT32 ulSeqNo) PURE; + STDMETHOD(SetStreamSeqNo) (THIS_ UINT32 ulStreamSeqNo) PURE; + STDMETHOD(SetIsLostRelaying) (THIS_ HXBOOL bLostRelay) PURE; + STDMETHOD(SetRuleSeqNoArray) (THIS_ UINT16* pRuleSeqNoArray, UINT16 uSize) PURE; +}; + +// $EndPrivate. + +#endif /* _IHXPCKTS_H_ */ + diff --git a/amarok/src/engine/helix/helix-sp/helix-include/common/system/dllpath.h b/amarok/src/engine/helix/helix-sp/helix-include/common/system/dllpath.h new file mode 100644 index 00000000..604c5448 --- /dev/null +++ b/amarok/src/engine/helix/helix-sp/helix-include/common/system/dllpath.h @@ -0,0 +1,238 @@ + +/* + * + * This software is released under the provisions of the GPL version 2. + * see file "COPYING". If that file is not available, the full statement + * of the license can be found at + * + * http://www.fsf.org/licensing/licenses/gpl.txt + * + * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved. + * + */ + +#ifndef _DLL_PATH +#define _DLL_PATH + +#include "hxcom.h" +#include "hxmap.h" +#include "hxstring.h" + +#ifdef _MACINTOSH +#pragma export on +STDAPI SetDLLAccessPath(const char* pPathDescriptor); +#pragma export off +#endif + +#if defined(HELIX_CONFIG_NOSTATICS) +#include "globals/hxglobals.h" +#endif + +/* + * Used to identify dll types. + */ +typedef enum dll_types +{ + DLLTYPE_NOT_DEFINED = 0, // Arbitrary DLLs (no predefined path used) + DLLTYPE_PLUGIN, // Plug-ins + DLLTYPE_CODEC, // Codecs + DLLTYPE_ENCSDK, // Encoder SDK DLLs + DLLTYPE_COMMON, // Common libraries + DLLTYPE_UPDATE, // Setup/Upgrade libraries + DLLTYPE_OBJBROKR, // Special entry for the object broker + DLLTYPE_RCAPLUGIN, // Gemini plugins + DLLTYPE_NUMBER // Not a type, used as number of predefined types. +} DLLTYPES; + +typedef HX_RESULT (HXEXPORT_PTR FPSETDLLACCESSPATH) (const char*); + + +class DLLAccessPath +{ +public: + DLLAccessPath(); + virtual ~DLLAccessPath(); + + // This class is only ref-counted if it is used as such. + // Most of the system uses this as a non-refcounted class. + STDMETHOD_(ULONG32,AddRef) (THIS); + STDMETHOD_(ULONG32,Release) (THIS); + + HX_RESULT SetAccessPaths(const char* pPathDescriptor); + HX_RESULT SetPath(UINT16 nLibType, const char* szPath); + HX_RESULT SetPath(const char* szLibType, const char* szPath); + + const char* GetPath(UINT16 nLibType); + const char* GetPath(const char* szLibType); + const char* GetLibTypeName(UINT16 nLibType); + + HX_RESULT PassDLLAccessPath(FPSETDLLACCESSPATH pSetDLLAccessPath); + + HX_RESULT AddPathToEnvironment(const char* szPath); + HX_RESULT RestoreEnvironment(); + UINT32 GetNumPaths() {return m_mapPathes.GetCount();} + +protected: + + static const char* const zm_pszDllTypeNames[DLLTYPE_NUMBER]; + +private: + + LONG32 m_lRefCount; + + CHXMapStringToString m_mapPathes; + CHXString m_strPathEnvVar; +}; + +extern DLLAccessPath* GetDLLAccessPath(); + +class DLLAccessDestructor +{ +public: + DLLAccessDestructor() {}; + ~DLLAccessDestructor() + { +#ifndef _VXWORKS + if (GetDLLAccessPath()) + { + GetDLLAccessPath()->Release(); + } +#endif + } +}; + + +// +// Macros for setting DLL loading paths +// +#ifndef _VXWORKS + +#if defined(_STATICALLY_LINKED) && !defined(HELIX_FEATURE_SERVER) + +// We need this since many DLLs have this listed +// as an export, so we have to have it defined +#define ENABLE_DLLACCESS_PATHS(GLOBAL) \ +STDAPI ENTRYPOINT(SetDLLAccessPath)(const char* pPathDescriptor) \ +{ \ + return HXR_OK; \ +} + +#define ENABLE_MULTILOAD_DLLACCESS_PATHS(GLOBAL) \ +STDAPI ENTRYPOINT(SetDLLAccessPath)(const char* pPathDescriptor) \ +{ \ + return HXR_OK; \ +} + +#elif defined(HELIX_CONFIG_NOSTATICS) + +#define ENABLE_DLLACCESS_PATHS(GLOBAL) \ + static const DLLAccessPath* const _g_##GLOBAL = NULL; \ + \ + DLLAccessPath* ENTRYPOINT(GetDLLAccessPath)() \ + { \ + return &HXGlobalDLLAccessPath::Get(&_g_##GLOBAL); \ + } \ + \ + STDAPI ENTRYPOINT(SetDLLAccessPath)(const char* pPathDescriptor) \ + { \ + return (GetDLLAccessPath()->SetAccessPaths(pPathDescriptor)); \ + } + +#else /* #if defined(_STATICALLY_LINKED) && !defined(HELIX_FEATURE_SERVER) */ + +#define ENABLE_DLLACCESS_PATHS(GLOBAL) \ + DLLAccessPath GLOBAL; \ + \ + DLLAccessPath* ENTRYPOINT(GetDLLAccessPath)() \ + { \ + return &GLOBAL; \ + } \ + \ + STDAPI ENTRYPOINT(SetDLLAccessPath)(const char* pPathDescriptor) \ + { \ + return (GetDLLAccessPath()->SetAccessPaths(pPathDescriptor)); \ + } + +#ifdef _UNIX + +#define ENABLE_MULTILOAD_DLLACCESS_PATHS(GLOBAL) \ + DLLAccessPath* GLOBAL = NULL; \ + \ + DLLAccessPath* GetDLLAccessPath() \ + { \ + if (!GLOBAL) \ + { \ + GLOBAL = new DLLAccessPath(); \ + GLOBAL->AddRef(); \ + } \ + return GLOBAL; \ + } \ + \ + STDAPI ENTRYPOINT(SetDLLAccessPath)(const char* pPathDescriptor) \ + { \ + if (!GLOBAL) \ + { \ + GLOBAL = new DLLAccessPath(); \ + GLOBAL->AddRef(); \ + } \ + \ + return (GLOBAL->SetAccessPaths(pPathDescriptor)); \ + } + +#else /* #ifdef _UNIX */ + +#define ENABLE_MULTILOAD_DLLACCESS_PATHS(GLOBAL) \ + DLLAccessPath* GLOBAL = NULL; \ + DLLAccessDestructor GLOBALDestructor; \ + \ + DLLAccessPath* GetDLLAccessPath() \ + { \ + if (!GLOBAL) \ + { \ + GLOBAL = new DLLAccessPath(); \ + GLOBAL->AddRef(); \ + } \ + return GLOBAL; \ + } \ + \ + STDAPI ENTRYPOINT(SetDLLAccessPath)(const char* pPathDescriptor) \ + { \ + if (!GLOBAL) \ + { \ + GLOBAL = new DLLAccessPath(); \ + GLOBAL->AddRef(); \ + } \ + \ + return (GLOBAL->SetAccessPaths(pPathDescriptor)); \ + } + +#endif /* #ifdef _UNIX #else */ + +#endif /* #if defined(_STATICALLY_LINKED) #else */ + +#else /* #ifndef _VXWORKS */ + +#define ENABLE_DLLACCESS_PATHS(GLOBAL) \ + STDAPI ENTRYPOINT(SetDLLAccessPath)(const char* pPathDescriptor) \ + { \ + return 0; \ + } + +#ifdef _SERVER +#define ENABLE_MULTILOAD_DLLACCESS_PATHS(GLOBAL) \ + STDAPI ENTRYPOINT(SetDLLAccessPath)(const char* pPathDescriptor) \ + { \ + return 0; \ + } +#else +#define ENABLE_MULTILOAD_DLLACCESS_PATHS(GLOBAL) \ + STDAPI ENTRYPOINT(SetDLLAccessPath)(const char* pPathDescriptor) \ + { \ + return 0; \ + } +#endif + +#endif /* #ifndef _VXWORKS #else */ + +#endif /* #ifndef _DLL_PATH */ + diff --git a/amarok/src/engine/helix/helix-sp/helix-include/common/util/hxmangle.h b/amarok/src/engine/helix/helix-sp/helix-include/common/util/hxmangle.h new file mode 100644 index 00000000..dd7b8845 --- /dev/null +++ b/amarok/src/engine/helix/helix-sp/helix-include/common/util/hxmangle.h @@ -0,0 +1,30 @@ + +/* + * + * This software is released under the provisions of the GPL version 2. + * see file "COPYING". If that file is not available, the full statement + * of the license can be found at + * + * http://www.fsf.org/licensing/licenses/gpl.txt + * + * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved. + * + */ + +#ifndef _HXMANGLE_H_ +#define _HXMANGLE_H_ + +#define CLIENT_GUID_REGNAME "Rotuma" +#define CLIENT_ID_REGNAME "Futuna" + +static const char CLIENT_ZERO_GUID[] = "00000000-0000-0000-0000-000000000000"; + +// given an input buffer, mangle it and return it back +// The caller has to call delete[] on the returned pointer +char* Cipher(const char* pszBuffer); + +// given an input buffer, de-mangle it and return it back +// The caller has to call delete[] on the returned pointer +char* DeCipher(const char* pszBuffer); + +#endif // _HXMANGLE_H_ diff --git a/amarok/src/engine/helix/helix-sp/helix-include/common/util/hxstrutl.h b/amarok/src/engine/helix/helix-sp/helix-include/common/util/hxstrutl.h new file mode 100644 index 00000000..d9df49cc --- /dev/null +++ b/amarok/src/engine/helix/helix-sp/helix-include/common/util/hxstrutl.h @@ -0,0 +1,143 @@ + +/* + * + * This software is released under the provisions of the GPL version 2. + * see file "COPYING". If that file is not available, the full statement + * of the license can be found at + * + * http://www.fsf.org/licensing/licenses/gpl.txt + * + * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved. + * + */ + +#ifndef _HXSTRUTL_H_ +#define _HXSTRUTL_H_ + +#include "hlxclib/string.h" /* for strxxx functions */ +#include "hlxclib/stdlib.h" /* for atoi64() and itoa() functionallity */ + +#include "safestring.h" + +#if !defined(_VXWORKS) +#ifdef _UNIX +#include <strings.h> +#include <ctype.h> +#endif +#endif +#ifdef _MACINTOSH +#include <ctype.h> +#endif + +#include "hxresult.h" + +#if defined (_MACINTOSH) + +#define isascii isprint + +inline const char *AnsiNext(const char* pcPtr) { return( pcPtr + 1 ); } +inline const char *AnsiPrev(const char * /* pcStart */, const char* pcPtr) { return (pcPtr - 1 ); } + +int CopyP2CString(ConstStr255Param inSource, char* outDest, int inDestLength); +void CopyC2PString(const char* inSource, Str255 outDest); +char WINToMacCharacter( char inWINChar ); +// these functions are used to convert Windows extended chars (used in non-English Roman languages) +// to Mac extended chars & vice-versa +void StripWinChars( char* pChars); +void StripMacChars( char* pChars); + +inline void pstrcpy(Str255 dst, ConstStr255Param src) { BlockMoveData(src, dst, 1+src[0]); } + +#ifndef _CARBON +inline void PStrCopy(StringPtr dest, ConstStr255Param src) { BlockMoveData(src, dest, 1+src[0]); } +inline void p2cstrcpy(char *dst, ConstStr255Param src) { CopyP2CString(src, dst, 255); } +inline void c2pstrcpy(Str255 dst, const char * src) { CopyC2PString(src, dst); } +#endif + +#endif /* _MACINTOSH */ + +#define CR (CHAR) '\r' +#define LF (CHAR) '\n' +#define CRLF "\r\n" + +#ifdef _WIN32 + #define LINEBREAK "\015\012" + #define LINEBREAK_LEN 2 +#else + #define LINEBREAK "\012" + #define LINEBREAK_LEN 1 +#endif /* _WIN32 */ + +#define LINE_BUFFER_SIZE 4096 +#define MAX_BYTES_PER_COOKIE 4096 +#define MAX_NUMBER_OF_COOKIES 300 +#define MAX_COOKIES_PER_SERVER 20 + +/* +According to C99 7.4/1: +--------------- +The header <ctype.h> declares several functions useful for +classifying and mapping characters. In all cases the argument is an +int, the value of which shall be representable as an unsigned char or +shall equal the value of the macro EOF. If the argument has any other +value, the behavior is undefined. +--------------- +Typecast the value to an (unsigned char) before passing it to isspace() to ensure that +if the value is a signed char it doesn't get bit extended on certain (VC) compilers. +*/ +#define IS_SPACE(x) (isspace((unsigned char) x)) + +#ifdef __cplusplus +void StrAllocCopy(char*& pDest, const char* pSrc); +#else +void StrAllocCopy(char** pDest, const char* pSrc); +#endif +char* StripLine(char* pLine); + +#include "hxtypes.h" +#include "hxcom.h" +typedef _INTERFACE IHXValues IHXValues; +HX_RESULT SaveStringToHeader(IHXValues* /* IN OUT */ pHeader, + const char* /* IN */ pszKey, + const char* /* IN */ pszValue); + +char* StrStrCaseInsensitive(const char* str1, const char* str2); +char* StrNStr(const char* str1, const char* str2, size_t depth1, size_t depth2); +char *StrNChr(const char *str, int c, size_t depth); +char *StrNRChr(const char *str, int c, size_t depth); +size_t StrNSpn(const char *str1, const char *str2, size_t depth1, size_t depth2); +size_t StrNCSpn(const char *str1, const char *str2, size_t depth1, size_t depth2); + +char* StrToUpper(char *pString); + +#if defined( _SYMBIAN) +#define NEW_FAST_TEMP_STR(NAME, EstimatedBiggestSize, LenNeeded) \ + char* NAME = new char[(LenNeeded)]; + +#define DELETE_FAST_TEMP_STR(NAME) \ + delete[] NAME; + +#else +/* XXXSMP We can use alloca() on platforms that support it for more speed! */ +#define NEW_FAST_TEMP_STR(NAME, EstimatedBiggestSize, LenNeeded) \ + char __##NAME##__StaticVersion[EstimatedBiggestSize]; \ + char* NAME; \ + UINT32 ulNeeded##NAME##Len = (LenNeeded); \ + \ + if (ulNeeded##NAME##Len <= EstimatedBiggestSize) \ + { \ + NAME = __##NAME##__StaticVersion; \ + } \ + else \ + { \ + NAME = new char[ulNeeded##NAME##Len]; \ + } + +#define DELETE_FAST_TEMP_STR(NAME) \ + if (NAME != __##NAME##__StaticVersion) \ + { \ + delete[] NAME; \ + } +#endif /* defined(_SYMBIAN) */ + +#endif /* _HXSTRUTL_H_ */ diff --git a/amarok/src/engine/helix/helix-sp/helix-include/runtime/hlxclib/assert.h b/amarok/src/engine/helix/helix-sp/helix-include/runtime/hlxclib/assert.h new file mode 100644 index 00000000..f1982a22 --- /dev/null +++ b/amarok/src/engine/helix/helix-sp/helix-include/runtime/hlxclib/assert.h @@ -0,0 +1,51 @@ + +/* + * + * This software is released under the provisions of the GPL version 2. + * see file "COPYING". If that file is not available, the full statement + * of the license can be found at + * + * http://www.fsf.org/licensing/licenses/gpl.txt + * + * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved. + * + */ + +#ifndef HLXSYS_ASSERT_H +#define HLXSYS_ASSERT_H + +#if defined(_OPENWAVE) +#include "platform/openwave/hx_op_debug.h" +#define assert(x) OpASSERT(x) +#elif !defined(WIN32_PLATFORM_PSPC) +#include <assert.h> +#endif /* !defined(WIN32_PLATFORM_PSPC) */ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +void __helix_assert(const char* pExpression, + const char* pFilename, int lineNum); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#if defined(WIN32_PLATFORM_PSPC) + +#include "hxtypes.h" +#include <winbase.h> +#include <dbgapi.h> + +#ifdef _DEBUG +#define assert(x) if(!(x)) __helix_assert(#x, __FILE__, __LINE__); +#else /* _DEBUG */ +#define assert(x) +#endif /* _DEBUG */ + +#endif /* defined(WIN32_PLATFORM_PSPC) */ + +#endif /* HLXSYS_ASSERT_H */ diff --git a/amarok/src/engine/helix/helix-sp/helix-include/runtime/hlxclib/limits.h b/amarok/src/engine/helix/helix-sp/helix-include/runtime/hlxclib/limits.h new file mode 100644 index 00000000..fbd2dc06 --- /dev/null +++ b/amarok/src/engine/helix/helix-sp/helix-include/runtime/hlxclib/limits.h @@ -0,0 +1,31 @@ + +/* + * + * This software is released under the provisions of the GPL version 2. + * see file "COPYING". If that file is not available, the full statement + * of the license can be found at + * + * http://www.fsf.org/licensing/licenses/gpl.txt + * + * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved. + * + */ + +#ifndef HLXSYS_LIMITS_H +#define HLXSYS_LIMITS_H + +#ifdef _OPENWAVE_SIMULATOR +#ifndef _WIN32 +#define _WIN32 +#define LIMITS_UNDEF_WIN32 +#endif /* _WIN32 */ +#endif /* _OPENWAVE_SIMULATOR */ + +#include <limits.h> + +#ifdef LIMITS_UNDEF_WIN32 +#undef _WIN32 +#undef LIMITS_UNDEF_WIN32 +#endif /* LIMITS_UNDEF_WIN32 */ + +#endif /* HLXSYS_LIMITS_H */ diff --git a/amarok/src/engine/helix/helix-sp/helix-include/runtime/hlxclib/memory.h b/amarok/src/engine/helix/helix-sp/helix-include/runtime/hlxclib/memory.h new file mode 100644 index 00000000..2cc9d506 --- /dev/null +++ b/amarok/src/engine/helix/helix-sp/helix-include/runtime/hlxclib/memory.h @@ -0,0 +1,29 @@ + +/* + * + * This software is released under the provisions of the GPL version 2. + * see file "COPYING". If that file is not available, the full statement + * of the license can be found at + * + * http://www.fsf.org/licensing/licenses/gpl.txt + * + * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved. + * + */ + +#ifndef HLXSYS_MEMORY_H +#define HLXSYS_MEMORY_H + +#if defined(_SYMBIAN) +#include <string.h> +#elif defined(_OPENWAVE) +#include "platform/openwave/hx_op_stdc.h" +#elif !defined(__TCS__) && !defined(_VXWORKS) +#include <memory.h> +#endif + +#if defined(_SOLARIS) || defined(_MAC_CFM) +#include <string.h> +#endif + +#endif /* HLXSYS_MEMORY_H */ diff --git a/amarok/src/engine/helix/helix-sp/helix-include/runtime/hlxclib/stdio.h b/amarok/src/engine/helix/helix-sp/helix-include/runtime/hlxclib/stdio.h new file mode 100644 index 00000000..117f9ec5 --- /dev/null +++ b/amarok/src/engine/helix/helix-sp/helix-include/runtime/hlxclib/stdio.h @@ -0,0 +1,117 @@ + +/* + * + * This software is released under the provisions of the GPL version 2. + * see file "COPYING". If that file is not available, the full statement + * of the license can be found at + * + * http://www.fsf.org/licensing/licenses/gpl.txt + * + * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved. + * + */ + +#ifndef HLXSYS_STDIO_H +#define HLXSYS_STDIO_H + +#if defined(_OPENWAVE) +#include "platform/openwave/hx_op_debug.h" +#include "platform/openwave/hx_op_stdc.h" +#include "platform/openwave/hx_op_fs.h" +#include "hlxclib/sys/types.h" +#else +#include <stdio.h> +#include <stdarg.h> +#endif + +#if __cplusplus +extern "C" { +#endif +/* Make sure vsnprintf is defined for all platforms */ + +int __helix_snprintf(char *str, size_t size, const char *format, ...); +int __helix_vsnprintf(char *str, size_t size, const char *format, va_list ap); + + +#if defined(_OPENWAVE) +int __helix_printf(const char* format, ...); +int __helix_vprintf(const char *format, va_list ap); + +int __helix_sscanf(const char *buffer, const char *format, ...); + +#define printf __helix_printf +#define vprintf __helix_vprintf +#define snprintf op_snprintf +#define vsnprintf __helix_vsnprintf +#define _vsnprintf __helix_vsnprintf +#define sscanf __helix_sscanf +#define unlink OpFsRemove + +typedef void* FILE; +#define stdin (FILE*)0 +#define stdout (FILE*)1 +#define stderr (FILE*)2 + +#ifndef EOF +#define EOF ((size_t)-1) + +FILE* __helix_fopen(const char *, const char *); +size_t __helix_fread(void *, size_t, size_t, FILE *); +size_t __helix_fwrite(const void *, size_t, size_t, FILE *); +int __helix_fseek(FILE *, long, int); +int __helix_fclose(FILE *); +int __helix_feof(FILE *); +long __helix_ftell(FILE *); +char* __helix_fgets(char*, int, FILE *); +int __helix_fputc(int, FILE *); +int __helix_ferror(FILE *); + +int __helix_fflush(FILE *); +int __helix_rename(const char *oldname, const char *newname); + +FILE* __helix_fdopen(int, const char *); +int __helix_fileno(FILE* ); + +int __helix_fprintf(FILE* f, const char *format, ...); +int __helix_vfprintf(FILE* f, const char *format, va_list ap); +#define puts(x) printf("%s\n", (x)) + +#define fopen __helix_fopen +#define fread __helix_fread +#define fseek __helix_fseek +#define fwrite __helix_fwrite +#define fclose __helix_fclose +#define feof __helix_feof +#define ftell __helix_ftell +#define fgets __helix_fgets +#define fputc __helix_fputc +#define putc __helix_fputc +#define ferror __helix_ferror +#define rewind(fp) __helix_fseek(fp, 0, SEEK_SET) + +#define fprintf __helix_fprintf +#define vfprintf __helix_fprintf + +#define fflush __helix_fflush +#define rename __helix_rename + +#define _fdopen __helix_fdopen + +#define fileno __helix_fileno + +#endif // end of _OPENWAVE + +#elif defined(_WINDOWS) +#define snprintf _snprintf +#define vsnprintf _vsnprintf + +#elif defined(_SYMBIAN) || defined(_WINCE) || defined(_IRIX) +#define snprintf __helix_snprintf +#define vsnprintf __helix_vsnprintf +#endif + +#if __cplusplus +} +#endif + +#endif /* HLXSYS_STDIO_H */ diff --git a/amarok/src/engine/helix/helix-sp/helix-include/runtime/hlxclib/stdlib.h b/amarok/src/engine/helix/helix-sp/helix-include/runtime/hlxclib/stdlib.h new file mode 100644 index 00000000..c9c427a2 --- /dev/null +++ b/amarok/src/engine/helix/helix-sp/helix-include/runtime/hlxclib/stdlib.h @@ -0,0 +1,136 @@ + +/* + * + * This software is released under the provisions of the GPL version 2. + * see file "COPYING". If that file is not available, the full statement + * of the license can be found at + * + * http://www.fsf.org/licensing/licenses/gpl.txt + * + * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved. + * + */ + +#ifndef HLXSYS_STDLIB_H +#define HLXSYS_STDLIB_H + +#include "hxtypes.h" + +#if defined(_OPENWAVE) +// XXXSAB Include compiler <stdlib.h> so we can modify it??? +#ifdef _OPENWAVE_SIMULATOR +#ifndef _WIN32 +#define STDLIB_UNDEF_WIN32 +#define _WIN32 +#endif /* _WIN32 */ +#endif /* _OPENWAVE_SIMULATOR */ + +#include <stdlib.h> +#undef itoa // just in case + +#ifdef STDLIB_UNDEF_WIN32 +#undef _WIN32 +#undef STDLIB_UNDEF_WIN32 +#endif /* STDLIB_UNDEF_WIN32 */ + +// XXXSAB Define malloc()/free() wrappers for Openwave in here??? +#else +#include <stdlib.h> +#endif + +char* __helix_itoa(int val, char *str, int radix); +char* __helix_i64toa(INT64 val, char *str, int radix); +INT64 __helix_atoi64(char* str); +void* __helix_bsearch( const void *key, const void *base, size_t num, + size_t width, + int ( *compare ) ( const void *elem1, + const void *elem2 ) ); +int __helix_remove(const char* pPath); +int __helix_putenv(const char* pStr); +char* __helix_getenv(const char* pName); + +#if defined(_WINDOWS) && !defined(_OPENWAVE) + +#if !defined(WIN32_PLATFORM_PSPC) +_inline char* +i64toa(INT64 val, char* str, int radix) +{ + return _i64toa(val, str, radix); +} + +#else /* !defined(WIN32_PLATFORM_PSPC) */ + +_inline +int remove(const char* pPath) +{ + return __helix_remove(pPath); +} + +_inline +char* getenv(const char* pName) +{ + return __helix_getenv(pName); +} + +#define i64toa __helix_i64toa +#define itoa __helix_itoa + +_inline +void* bsearch( const void *key, const void *base, size_t num, + size_t width, + int ( *compare ) ( const void *elem1, + const void *elem2 ) ) +{ + return __helix_bsearch(key, base, num, width, compare); +} +#endif /* !defined(WIN32_PLATFORM_PSPC) */ + +_inline INT64 +atoi64(const char* str) +{ + return _atoi64(str); +} +#endif /* _WINDOWS */ + +#if defined (_MACINTOSH) + +#define itoa __helix_itoa +#define i64toa __helix_i64toa +#define atoi64 __helix_atoi64 + +#endif /* _MACINTOSH */ + +#if defined (_UNIX) && !defined (__QNXNTO__) + +// Convert integer to string + +#define itoa __helix_itoa +#define i64toa __helix_i64toa +#define atoi64 __helix_atoi64 + +#endif /* _UNIX */ + +#if defined(_SYMBIAN) + +#define itoa __helix_itoa +#define i64toa __helix_i64toa +#define atoi64 __helix_atoi64 +#define putenv __helix_putenv + +#endif + +#if defined(_OPENWAVE) + +#define itoa(v,s,r) __helix_itoa((v),(s),(r)) +#define i64toa(v,s,r) __helix_i64toa((v),(s),(r)) +#define atoi64(s) __helix_atoi64((s)) +#define putenv __helix_putenv + +__inline int remove(const char* pPath) +{ + return __helix_remove(pPath); +} + +#endif // _OPENWAVE + +#endif /* HLXSYS_STDLIB_H */ diff --git a/amarok/src/engine/helix/helix-sp/helix-include/runtime/hlxclib/string.h b/amarok/src/engine/helix/helix-sp/helix-include/runtime/hlxclib/string.h new file mode 100644 index 00000000..5da00a5a --- /dev/null +++ b/amarok/src/engine/helix/helix-sp/helix-include/runtime/hlxclib/string.h @@ -0,0 +1,254 @@ + +/* + * + * This software is released under the provisions of the GPL version 2. + * see file "COPYING". If that file is not available, the full statement + * of the license can be found at + * + * http://www.fsf.org/licensing/licenses/gpl.txt + * + * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved. + * + */ + +#ifndef HLXSYS_STRING_H +#define HLXSYS_STRING_H + +#if defined(_OPENWAVE) +#include "platform/openwave/hx_op_stdc.h" +#else +#include <string.h> +#endif /* !_OPENWAVE */ + +#ifdef _SYMBIAN +//on symbian we have stuff scattered all about. +# include <stdlib.h> +# include <ctype.h> +#endif +#if !defined(_VXWORKS) +#ifdef _UNIX +#include <strings.h> +#endif /* _UNIX */ +#endif /* !defined(_VXWORKS) */ + +/* If we are on Windows and are compiling + * a .c file and are using Visual C++, + * then use __inline instead of inline. + */ +#if (defined(_WINDOWS) || defined(_OPENWAVE)) && \ + !defined(__cplusplus) && defined(_MSC_VER) +#define HLX_INLINE __inline +#else +#define HLX_INLINE inline +#endif + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +char * __helix_strrev(char * str); +void __helix_strlwr(char *s); +void __helix_strupr(char *s); + +const char* __helix_strnchr(const char* sc, const char c, size_t n); +const char* __helix_strnstr(const char* sc, const char* str, size_t n); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#define strnchr __helix_strnchr +#define strnstr __helix_strnstr + +#ifdef _WINDOWS +#ifdef _WINCE + int strcasecmp(const char* str1, const char* str2); + #else +HLX_INLINE int +strcasecmp(const char* str1, const char* str2) +{ + return _stricmp(str1, str2); +} +#endif //_WINCE +HLX_INLINE int +strncasecmp(const char* str1, const char* str2, int len) +{ + return _strnicmp(str1, str2, (size_t) len); +} + +#if defined(WIN32_PLATFORM_PSPC) +#define strrev __helix_strrev +#define stricmp strcasecmp +#define strcmpi strcasecmp +#define strnicmp strncasecmp +#define strlwr __helix_strlwr +#define strupr __helix_strupr +#endif /* defined(WIN32_PLATFORM_PSPC) */ + +#endif /* _WINDOWS */ + + +#if defined(_SYMBIAN) +unsigned long __helix_strtoul(const char*s, char**end, int base); +#define strtoul __helix_strtoul +#define strrev __helix_strrev +#define stricmp strcasecmp +#define strnicmp strncasecmp +#define strlwr __helix_strlwr +#define strupr __helix_strupr + +#endif /* _SYMBIAN */ + +#if defined(_OPENWAVE) +#define strcmpi stricmp +#define strrev __helix_strrev +#define strlwr __helix_strlwr +#undef stricmp +#undef strnicmp +#define stricmp strcasecmp +#define strnicmp strncasecmp +#endif /* _OPENWAVE_ARMULATOR */ + +#if defined (_MACINTOSH) + +#ifdef _MAC_MACHO + +#define strlwr __helix_strlwr +#define stricmp strcasecmp +#define strnicmp strncasecmp +#define strrev __helix_strrev + +#else + +int strnicmp(const char *first, const char *last, size_t count); +int stricmp(const char *first, const char *last); +char * strrev(char * str); + +#endif + +#define strcmpi stricmp + +#ifndef _MAC_MACHO + +HLX_INLINE int +strcasecmp(const char* str1, const char* str2) +{ + return stricmp(str1, str2); +} + +#endif + + +#endif /* _MACINTOSH */ + +#if defined (_UNIX) && !defined (__QNXNTO__) + +/* strcasecmp, strncasecmp are defined in strings.h */ +#define stricmp strcasecmp +#define strcmpi strcasecmp +#define strnicmp strncasecmp + +// Convert integer to string + +// reverse a string in place + +#define strrev __helix_strrev +#define strlwr __helix_strlwr +#define strupr __helix_strupr + +#endif /* _UNIX */ + + +#if defined (_MACINTOSH) || defined (_UNIX) +#define _tcsspn strspn +#define _tcscspn strcspn +#define _tcsrchr strchr +#define _tcsstr strstr +#endif + +#ifdef _VXWORKS +extern "C" { +int strncasecmp(const char *first, const char *last, size_t count); +int strcasecmp(const char *first, const char *last); +} +#endif /* _VXWORKS */ + + +#if defined(_WINDOWS) && !defined(__cplusplus) && defined(_MSC_VER) +#include "hlxclib/stdlib.h" // malloc() + +#define NEW_STRING_BUFFER new_string_buffer +#define HLX_ALLOC(x) ((char*) malloc((x))) +#define HLX_DEALLOC(x) free((x)) + +#elif defined(__cplusplus) +#define NEW_STRING_BUFFER new_string +#define HLX_ALLOC(x) (new char[(x)]) +#define HLX_DEALLOC(x) delete [] (x) +#endif /* #elif defined(__cplusplus) */ + +#ifdef HLX_ALLOC +HLX_INLINE char* +NEW_STRING_BUFFER(const void* mem, int len) +{ + char* str = HLX_ALLOC(len+1); + if (str) + { + memcpy((void*)str, mem, len); /* Flawfinder: ignore */ + str[len] = '\0'; + } + return str; +} + +HLX_INLINE char* +new_string(const char* str) +{ + char* pTmp = HLX_ALLOC(strlen(str)+1); + + return pTmp ? strcpy(pTmp, str) : NULL; /* Flawfinder: ignore */ +} + +HLX_INLINE char* +new_path_string(const char* str) +{ + char* pnew = HLX_ALLOC(strlen(str) + 1); + const char* psrc = str; + char* pdst = pnew; + if (!pnew) return NULL; + + while (*psrc) + { + if (*psrc == '/' || *psrc == '\\') + { +#if defined _WIN32 || defined(_SYMBIAN) + *pdst = '\\'; +#elif defined _UNIX || defined _OPENWAVE + *pdst = '/'; +#elif defined __MWERKS__ + *pdst = ':'; +#else + *pdst = *psrc; +#endif + } + else + { + *pdst = *psrc; + } + psrc++; + pdst++; + } + *pdst = '\0'; + return pnew; +} + +HLX_INLINE void +delete_string(char* str) +{ + if (str) + { + HLX_DEALLOC(str); + } +} +#endif /* HLX_ALLOC */ + +#endif /* HLXSYS_STRING_H */ diff --git a/amarok/src/engine/helix/helix-sp/helix-include/runtime/hlxclib/sys/stat.h b/amarok/src/engine/helix/helix-sp/helix-include/runtime/hlxclib/sys/stat.h new file mode 100644 index 00000000..3c2cc1f2 --- /dev/null +++ b/amarok/src/engine/helix/helix-sp/helix-include/runtime/hlxclib/sys/stat.h @@ -0,0 +1,67 @@ + +/* + * + * This software is released under the provisions of the GPL version 2. + * see file "COPYING". If that file is not available, the full statement + * of the license can be found at + * + * http://www.fsf.org/licensing/licenses/gpl.txt + * + * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved. + * + */ + +#ifndef HLXSYS_SYS_STAT_H +#define HLXSYS_SYS_STAT_H + +#if !defined(WIN32_PLATFORM_PSPC) && !defined(_OPENWAVE) && !defined(_WINCE) +#if defined(_MACINTOSH) && !defined(_MAC_MACHO) +#include <stat.h> +#else +#include <sys/stat.h> +#endif +#endif /* !defined(WIN32_PLATFORM_PSPC) */ + +#include "hlxclib/sys/types.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* Helix implementations */ +int __helix_fstat(int filedes, struct stat *buf); +int __helix_stat(const char* pFilename, struct stat *buf); + +#if defined(WIN32_PLATFORM_PSPC) || defined(_OPENWAVE) + +#ifdef _OPENWAVE +#define S_IFDIR 0x1000 /* specify a directory*/ +#endif + +inline +int fstat(int filedes, struct stat *buf) +{ + return __helix_fstat(filedes, buf); +} + +inline +int _stat(const char* pFilename, struct _stat *buf) +{ + return __helix_stat(pFilename, (struct stat *)buf); +} + +inline +int stat(const char* pFilename, struct stat *buf) +{ + return __helix_stat(pFilename, buf); +} + +#endif /* defined(WIN32_PLATFORM_PSPC) */ + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* HLXSYS_SYS_STAT_H */ diff --git a/amarok/src/engine/helix/helix-sp/helix-include/runtime/hlxclib/sys/types.h b/amarok/src/engine/helix/helix-sp/helix-include/runtime/hlxclib/sys/types.h new file mode 100644 index 00000000..4fbb554e --- /dev/null +++ b/amarok/src/engine/helix/helix-sp/helix-include/runtime/hlxclib/sys/types.h @@ -0,0 +1,66 @@ + +/* + * + * This software is released under the provisions of the GPL version 2. + * see file "COPYING". If that file is not available, the full statement + * of the license can be found at + * + * http://www.fsf.org/licensing/licenses/gpl.txt + * + * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved. + * + */ + +#ifndef HLXSYS_SYS_TYPES_H +#define HLXSYS_SYS_TYPES_H + +#if !defined(WIN32_PLATFORM_PSPC) && !defined(_MACINTOSH) && !defined(_OPENWAVE) +#include <sys/types.h> +#endif /* !defined(WIN32_PLATFORM_PSPC) */ + +#if defined (_WINDOWS) || (defined (_MACINTOSH) && !defined(_MAC_MACHO)) +typedef long off_t; +#endif /* defined (_WINDOWS) || defined (_MACINTOSH) */ + +#if defined(_OPENWAVE) +#include "hlxclib/time.h" +#include "platform/openwave/hx_op_fs.h" +typedef unsigned short mode_t; +typedef OpFsSize off_t; // XXXSAB??? + +#ifndef SEEK_SET +#define SEEK_SET kOpFsSeekSet +#define SEEK_CUR kOpFsSeekCur +#define SEEK_END kOpFsSeekEnd +#endif + +struct stat +{ + // XXXSAB fill this in... + mode_t st_mode; + off_t st_size; + time_t st_atime; + time_t st_ctime; + time_t st_mtime; + short st_nlink; +}; + +#elif defined(WIN32_PLATFORM_PSPC) +#include "hlxclib/windows.h" + +typedef unsigned short mode_t; + +#define S_IFDIR 0040000 + +struct stat { + mode_t st_mode; + off_t st_size; + time_t st_atime; + time_t st_ctime; + time_t st_mtime; + short st_nlink; +}; + +#endif /* defined(WIN32_PLATFORM_PSPC) */ + +#endif /* HLXSYS_SYS_TYPES_H */ diff --git a/amarok/src/engine/helix/helix-sp/helix-include/runtime/hlxclib/time.h b/amarok/src/engine/helix/helix-sp/helix-include/runtime/hlxclib/time.h new file mode 100644 index 00000000..f0009d0a --- /dev/null +++ b/amarok/src/engine/helix/helix-sp/helix-include/runtime/hlxclib/time.h @@ -0,0 +1,148 @@ + +/* + * + * This software is released under the provisions of the GPL version 2. + * see file "COPYING". If that file is not available, the full statement + * of the license can be found at + * + * http://www.fsf.org/licensing/licenses/gpl.txt + * + * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved. + * + */ + +#ifndef HLXSYS_TIME_H +#define HLXSYS_TIME_H + +#if defined(_SYMBIAN) +# include <sys/time.h> +#endif + +#if defined(WIN32_PLATFORM_PSPC) +# include "hxtypes.h" +# include "hlxclib/windows.h" +#elif !defined(WIN32_PLATFORM_PSPC) && !defined(_OPENWAVE) +# include <time.h> +#endif /* !defined(WIN32_PLATFORM_PSPC) && !defined(_OPENWAVE) */ + +#if defined(_OPENWAVE) +# include "platform/openwave/hx_op_timeutil.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +/******************************* + * Types + */ + +#if defined(_OPENWAVE) + +#define NO_TM_ISDST +typedef U32 time_t; +#define tm op_tm // XXXSAB any other way for 'struct tm' to + // work in a C-includeable file? +struct timeval { + time_t tv_sec; + time_t tv_usec; +}; + + +#elif defined(WIN32_PLATFORM_PSPC) + +struct tm { + int tm_sec; + int tm_min; + int tm_hour; + int tm_mday; + int tm_mon; + int tm_year; + int tm_wday; + int tm_yday; + int tm_isdst; +}; + +#define timezone _timezone +extern long _timezone; + +#endif /* defined(WIN32_PLATFORM_PSPC) */ + + +/******************************* + * Helix declarations + */ +long __helix_time(long *t); +struct tm* __helix_localtime(long* timep); +void __helix_tzset(); +long __helix_mktime(struct tm* tm); +struct tm *__helix_gmtime(long *timep); +int __helix_gettimeofday(struct timeval *tv, void *tz); +char * __helix_ctime(long *timer); + +#if defined(_WINCE) +char * __helix_asctime (struct tm *tm); + +/******************************* + * platform specifics declarations + */ + +_inline char * ctime(time_t *timp) +{ + return __helix_ctime((long*)timp); +} + +_inline char * asctime (struct tm *tm) +{ + return __helix_asctime(tm); +} + +_inline +void _tzset() +{ + __helix_tzset(); +} + +_inline +struct tm* localtime(time_t* timep) +{ + return __helix_localtime((long *)timep); +} + +_inline +long time(time_t *t) +{ + return __helix_time((long *)t); +} + + +_inline +long mktime(struct tm* tm) +{ + return __helix_mktime(tm); +} + +_inline +struct tm* gmtime(time_t *timep) +{ + return __helix_gmtime((long*)timep); +} + +#elif defined(_OPENWAVE) +#define time(t) __helix_time(t) +#define ctime(t) __helix_ctime(t) +#define gmtime(t) __helix_gmtime(t) +#define localtime(t) __helix_gmtime(t) // XXXSAB is there a _local_ time call? +#define mktime(tm) __helix_mktime(tm) +#define gettimeofday __helix_gettimeofday + +#define strftime op_strftime + +#endif /* defined(WIN32_PLATFORM_PSPC) */ + +#ifdef __cplusplus +}; +#endif /* __cplusplus */ + +#endif /* HLXSYS_TIME_H */ diff --git a/amarok/src/engine/helix/helix-sp/helix-include/runtime/safestring.h b/amarok/src/engine/helix/helix-sp/helix-include/runtime/safestring.h new file mode 100644 index 00000000..55df7220 --- /dev/null +++ b/amarok/src/engine/helix/helix-sp/helix-include/runtime/safestring.h @@ -0,0 +1,37 @@ + +/* + * + * This software is released under the provisions of the GPL version 2. + * see file "COPYING". If that file is not available, the full statement + * of the license can be found at + * + * http://www.fsf.org/licensing/licenses/gpl.txt + * + * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved. + * + */ + +#ifndef _SAFESTRING_H_ +#define _SAFESTRING_H_ + +#include "hxtypes.h" /* UINT32 */ +#include "hlxclib/string.h" /* for strxxx functions */ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +char* SafeStrCpy(char* pDestStr, const char* pSourceStr, UINT32 ulBufferSize); +char* SafeStrCat(char* pDestStr, const char* pSourceStr, UINT32 ulBufferSize); + +#ifdef _OPENWAVE_ARMULATOR +#define SafeSprintf op_snprintf +#else +int SafeSprintf(char* pBuffer, UINT32 ulBufferSize, const char* pFormatStr, ...); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* _SAFESTRING_H_ */ diff --git a/amarok/src/engine/helix/helix-sp/helix-sp.cpp b/amarok/src/engine/helix/helix-sp/helix-sp.cpp new file mode 100644 index 00000000..c7ac5943 --- /dev/null +++ b/amarok/src/engine/helix/helix-sp/helix-sp.cpp @@ -0,0 +1,2108 @@ +/* ********** + * + * This software is released under the provisions of the GPL version 2. + * see file "COPYING". If that file is not available, the full statement + * of the license can be found at + * + * http://www.fsf.org/licensing/licenses/gpl.txt + * + * Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved. + * Portions Copyright (c) Paul Cifarelli 2005 + * + * ********** */ +#include <stdlib.h> +#include <stdarg.h> + +#include "hxcomm.h" +#include "hxcore.h" +#include "hxclsnk.h" +#include "hxerror.h" +#include "hxauth.h" +#include "hxprefs.h" +#include "hxstrutl.h" +#include "hxvsrc.h" +#include "hxresult.h" +#include "hxplugn.h" + +#include "hspadvisesink.h" +#include "hsperror.h" +#include "hspauthmgr.h" +#include "hspcontext.h" +#include <X11/Xlib.h> +#include <dlfcn.h> +#include <errno.h> +#include <fcntl.h> +#include <sys/ioctl.h> + +#include "hxausvc.h" + +#include "dllpath.h" + +#include <config.h> + +#include "helix-sp.h" +#include "hspvoladvise.h" +#include "utils.h" +#include "hsphook.h" +#include "hxfiles.h" + +#ifdef USE_HELIX_ALSA +#include <alsa/asoundlib.h> +#include "hspalsadevice.h" +#endif + +#ifdef HX_LOG_SUBSYSTEM +#include "hxtlogutil.h" +#endif + +#ifdef __FreeBSD__ +#define PTHREAD_MUTEX_FAST_NP PTHREAD_MUTEX_NORMAL +#endif + +#if !defined(__NetBSD__) && !defined(__OpenBSD__) + #include <sys/soundcard.h> +#else + #include <soundcard.h> +#endif + +typedef HX_RESULT (HXEXPORT_PTR FPRMSETDLLACCESSPATH) (const char*); + +class HelixSimplePlayerAudioStreamInfoResponse : public IHXAudioStreamInfoResponse +{ +public: + HelixSimplePlayerAudioStreamInfoResponse(HelixSimplePlayer *player, int playerIndex) : + m_Player(player), m_index(playerIndex), m_lRefCount(0) {} + virtual ~HelixSimplePlayerAudioStreamInfoResponse() {} + + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj); + + STDMETHOD_(ULONG32,AddRef) (THIS); + + STDMETHOD_(ULONG32,Release) (THIS); + + /* + * IHXAudioStreamInfoResponse methods + */ + STDMETHOD(OnStream) (THIS_ + IHXAudioStream *pAudioStream + ); +private: + HelixSimplePlayer *m_Player; + IHXAudioStream *m_Stream; + int m_index; + LONG32 m_lRefCount; + HXAudioFormat m_audiofmt; +}; + +STDMETHODIMP +HelixSimplePlayerAudioStreamInfoResponse::QueryInterface(REFIID riid, void**ppvObj) +{ + if(IsEqualIID(riid, IID_IUnknown)) + { + AddRef(); + *ppvObj = (IUnknown*)(IHXAudioStreamInfoResponse *)this; + return HXR_OK; + } + else if(IsEqualIID(riid, IID_IHXAudioStreamInfoResponse)) + { + AddRef(); + *ppvObj = (IHXAudioStreamInfoResponse*)this; + return HXR_OK; + } + *ppvObj = NULL; + return HXR_NOINTERFACE; +} + +STDMETHODIMP_(UINT32) +HelixSimplePlayerAudioStreamInfoResponse::AddRef() +{ + return InterlockedIncrement(&m_lRefCount); +} + +STDMETHODIMP_(UINT32) +HelixSimplePlayerAudioStreamInfoResponse::Release() +{ + if (InterlockedDecrement(&m_lRefCount) > 0) + { + return m_lRefCount; + } + + delete this; + return 0; +} + +STDMETHODIMP HelixSimplePlayerAudioStreamInfoResponse::OnStream(IHXAudioStream *pAudioStream) +{ + m_Player->print2stderr("Stream Added on player %d, stream duration %ld, sources %d\n", m_index, + m_Player->duration(m_index), m_Player->ppctrl[m_index]->pPlayer->GetSourceCount()); + + m_Player->ppctrl[m_index]->pStream = pAudioStream; + m_Player->ppctrl[m_index]->pPreMixHook = new HSPPreMixAudioHook(m_Player, m_index, pAudioStream, + m_Player->ppctrl[m_index]->bFadeIn, + m_Player->ppctrl[m_index]->ulFadeLength); + + // addpremixhook adds another ref + pAudioStream->AddPreMixHook(m_Player->ppctrl[m_index]->pPreMixHook, false); + m_Player->ppctrl[m_index]->pPreMixHook->Release(); // release the ref added in the premixhook constructor + + m_Player->ppctrl[m_index]->bStarting = false; + + return HXR_OK; +} + +// Constants +const int DEFAULT_TIME_DELTA = 2000; +const int DEFAULT_STOP_TIME = -1; +const int SLEEP_TIME = 10; +const int GUID_LEN = 64; + +// *** IUnknown methods *** + +///////////////////////////////////////////////////////////////////////// +// Method: +// IUnknown::QueryInterface +// Purpose: +// Implement this to export the interfaces supported by your +// object. +// +STDMETHODIMP HelixSimplePlayerVolumeAdvice::QueryInterface(REFIID riid, void** ppvObj) +{ + if (IsEqualIID(riid, IID_IUnknown)) + { + AddRef(); + *ppvObj = (IUnknown*)(IHXClientAdviseSink*)this; + return HXR_OK; + } + else if (IsEqualIID(riid, IID_IHXVolumeAdviseSink)) + { + AddRef(); + *ppvObj = (IHXVolumeAdviseSink*)this; + return HXR_OK; + } + + *ppvObj = NULL; + return HXR_NOINTERFACE; +} + +///////////////////////////////////////////////////////////////////////// +// Method: +// IUnknown::AddRef +// Purpose: +// Everyone usually implements this the same... feel free to use +// this implementation. +// +STDMETHODIMP_(ULONG32) HelixSimplePlayerVolumeAdvice::AddRef() +{ + return InterlockedIncrement(&m_lRefCount); +} + +///////////////////////////////////////////////////////////////////////// +// Method: +// IUnknown::Release +// Purpose: +// Everyone usually implements this the same... feel free to use +// this implementation. +// +STDMETHODIMP_(ULONG32) HelixSimplePlayerVolumeAdvice::Release() +{ + if (InterlockedDecrement(&m_lRefCount) > 0) + { + return m_lRefCount; + } + + delete this; + return 0; +} + +STDMETHODIMP HelixSimplePlayerVolumeAdvice::OnVolumeChange(const UINT16 /*uVolume*/) +{ + m_Player->onVolumeChange(m_index); +#ifdef HELIX_SW_VOLUME_INTERFACE + m_Player->ppctrl[m_index]->volume = uVolume; +#endif + return HXR_OK; +} + +STDMETHODIMP HelixSimplePlayerVolumeAdvice::OnMuteChange(const BOOL bMute) +{ + m_Player->onMuteChange(m_index); + m_Player->ppctrl[m_index]->ismute = bMute; + return HXR_OK; +} + + +int HelixSimplePlayer::print2stdout(const char *fmt, ...) +{ + va_list args; + char buf[1024]; + + va_start(args, fmt); + + int ret = vsprintf(buf, fmt, args); + std::cout << buf; + + va_end(args); + + return ret; +} + +int HelixSimplePlayer::print2stderr(const char *fmt, ...) +{ + va_list args; + char buf[1024]; + + va_start(args, fmt); + + int ret = vsprintf(buf, fmt, args); + std::cerr << buf; + + va_end(args); + + return ret; +} + + +void HelixSimplePlayer::setFadeout(bool fadeout, unsigned long fadelength, int playerIndex ) +{ + if (playerIndex == ALL_PLAYERS) + { + for (int i = 0; i<nNumPlayers; i++) + setFadeout(fadeout, fadelength, i); + } + else + { + if (playerIndex >=0 && playerIndex < nNumPlayers && ppctrl[playerIndex]->pPreMixHook) + { + ppctrl[playerIndex]->ulFadeLength = fadelength; + ((HSPPreMixAudioHook *)ppctrl[playerIndex]->pPreMixHook)->setFadelength(ppctrl[playerIndex]->ulFadeLength); + ((HSPPreMixAudioHook *)ppctrl[playerIndex]->pPreMixHook)->setFadeout(fadeout); + } + } +} + + +void HelixSimplePlayer::cleanUpStream(int playerIndex) +{ + //print2stderr("CLEANUPSTREAM\n"); + stop(playerIndex); +} + + +void HelixSimplePlayer::updateEQgains() +{ + for (int i = 0; i<nNumPlayers; i++) + if (pFinalAudioHook && isEQenabled()) + ((HSPFinalAudioHook *)pFinalAudioHook)->updateEQgains(m_preamp, m_equalizerGains); +} + +/* + * handle one event + */ +void HelixSimplePlayer::DoEvent() +{ + struct _HXxEvent *pNothing = 0x0; + struct timeval mtime; + + mtime.tv_sec = 0; + mtime.tv_usec = SLEEP_TIME * 1000; + usleep(SLEEP_TIME*1000); + pEngine->EventOccurred(pNothing); +} + +/* + * handle events for at most nTimeDelta milliseconds + */ +void HelixSimplePlayer::DoEvents(int) +{ + DoEvent(); +} + +/* + * return the number of milliseconds since the epoch + */ +UINT32 HelixSimplePlayer::GetTime() +{ + timeval t; + gettimeofday(&t, NULL); + + // FIXME: + // the fact that the result is bigger than a UINT32 is really irrelevant; + // we can still play a stream for many many years... + return (UINT32)((t.tv_sec * 1000) + (t.tv_usec / 1000)); +} + +char* HelixSimplePlayer::RemoveWrappingQuotes(char* str) +{ + int len = strlen(str); + if (len > 0) + { + if (str[len-1] == '"') str[--len] = 0; + if (str[0] == '"') {int i = 0; do { str[i] = str[i+1]; ++i; } while(--len); } + } + return str; +} + + +HelixSimplePlayer::HelixSimplePlayer() : + theErr(HXR_FAILED), + pErrorSink(NULL), + pErrorSinkControl(NULL), + pPluginE(0), + pPlugin2Handler(0), + ppctrl(NULL), + bURLFound(false), + nNumPlayers(0), + nNumPlayRepeats(1), + nTimeDelta(DEFAULT_TIME_DELTA), + nStopTime(DEFAULT_STOP_TIME), + bStopTime(true), + bStopping(false), + nPlay(0), + bEnableAdviceSink(false), + bEnableVerboseMode(false), + pEngine(NULL), + pEngineContext(NULL), + m_pszUsername(NULL), + m_pszPassword(NULL), + m_pszGUIDFile(NULL), + m_pszGUIDList(NULL), + m_Error(0), + m_ulNumSecondsPlayed(0), + mimehead(0), + mimelistlen(0), + m_preamp(0), + m_outputsink(OSS), + m_device(0), +#ifdef USE_HELIX_ALSA + m_direct(ALSA), // TODO: out why my alsa direct HW reader doesn't pickup changes in kmix (the whole purpose of this!) +#else + m_direct(OSS), +#endif + m_AlsaCapableCore(false), + m_nDevID(-1), + m_pAlsaMixerHandle(NULL), + m_pAlsaMasterMixerElem(NULL), + m_pAlsaPCMMixerElem(NULL), + m_alsaDevice("default"), + m_urlchanged(0), + m_volBefore(-1), + m_volAtStart(-1), + m_MvolBefore(-1), + m_MvolAtStart(-1) +{ + + pthread_mutexattr_t ma; + + pthread_mutexattr_init(&ma); + pthread_mutexattr_settype(&ma, PTHREAD_MUTEX_FAST_NP); // note this is not portable outside linux and a few others + pthread_mutex_init(&m_engine_m, &ma); +} + +void HelixSimplePlayer::init(const char *corelibhome, const char *pluginslibhome, const char *codecshome, int numPlayers) +{ + int i; + + theErr = HXR_OK; + + FPRMCREATEENGINE fpCreateEngine; + FPRMSETDLLACCESSPATH fpSetDLLAccessPath; + + SafeSprintf(mCoreLibPath, MAX_PATH, "%s/%s", corelibhome, "clntcore.so"); + + // Allocate arrays to keep track of players and client + // context pointers + ppctrl = new struct playerCtrl *[MAX_PLAYERS]; + memset(ppctrl, 0, sizeof(struct playerCtrl *) * MAX_PLAYERS); + + if (!ppctrl) + { + print2stderr("Error: Out of Memory.\n"); + theErr = HXR_UNEXPECTED; + return; + } + + fpCreateEngine = NULL; + + // prepare/load the HXCore module + //print2stdout("Simpleplayer is looking for the client core at %s\n", mCoreLibPath ); + + core_handle = dlopen(mCoreLibPath, RTLD_LAZY | RTLD_GLOBAL); + if (!core_handle) + { + print2stderr("splayer: failed to open corelib, errno %d\n", errno); + theErr = HXR_FAILED; + return; + } + fpCreateEngine = (FPRMCREATEENGINE) dlsym(core_handle, "CreateEngine"); + fpSetDLLAccessPath = (FPRMSETDLLACCESSPATH) dlsym(core_handle, "SetDLLAccessPath"); + + if (fpCreateEngine == NULL || + fpSetDLLAccessPath == NULL ) + { + theErr = HXR_FAILED; + return; + } + + //Now tell the client core where to find the plugins and codecs it + //will be searching for. + if (NULL != fpSetDLLAccessPath) + { + //Create a null delimited, double-null terminated string + //containing the paths to the encnet library (DT_Common) and + //the sdpplin library (DT_Plugins)... + char pPaths[256]; /* Flawfinder: ignore */ + char* pPathNextPosition = pPaths; + memset(pPaths, 0, 256); + UINT32 ulBytesLeft = 256; + + char* pNextPath = new char[256]; + memset(pNextPath, 0, 256); + + SafeSprintf(pNextPath, 256, "DT_Common=%s", corelibhome); + //print2stderr("Common DLL path %s\n", pNextPath ); + UINT32 ulBytesToCopy = strlen(pNextPath) + 1; + if (ulBytesToCopy <= ulBytesLeft) + { + memcpy(pPathNextPosition, pNextPath, ulBytesToCopy); + pPathNextPosition += ulBytesToCopy; + ulBytesLeft -= ulBytesToCopy; + } + + SafeSprintf(pNextPath, 256, "DT_Plugins=%s", pluginslibhome); + //print2stderr("Plugin path %s\n", pNextPath ); + ulBytesToCopy = strlen(pNextPath) + 1; + if (ulBytesToCopy <= ulBytesLeft) + { + memcpy(pPathNextPosition, pNextPath, ulBytesToCopy); + pPathNextPosition += ulBytesToCopy; + ulBytesLeft -= ulBytesToCopy; + } + + SafeSprintf(pNextPath, 256, "DT_Codecs=%s", codecshome); + //print2stderr("Codec path %s\n", pNextPath ); + ulBytesToCopy = strlen(pNextPath) + 1; + if (ulBytesToCopy <= ulBytesLeft) + { + memcpy(pPathNextPosition, pNextPath, ulBytesToCopy); + pPathNextPosition += ulBytesToCopy; + ulBytesLeft -= ulBytesToCopy; + *pPathNextPosition='\0'; + } + + fpSetDLLAccessPath((char*)pPaths); + + HX_VECTOR_DELETE(pNextPath); + } + + // create client engine + if (HXR_OK != fpCreateEngine((IHXClientEngine**)&pEngine)) + { + theErr = HXR_FAILED; + return; + } + + pCommonClassFactory = 0; + // get the common class factory + pEngine->QueryInterface(IID_IHXCommonClassFactory, (void **) &pCommonClassFactory); + if (!pCommonClassFactory) + print2stderr("no CommonClassFactory\n"); + + // get the engine setup interface + IHXClientEngineSetup *pEngineSetup = 0; + pEngine->QueryInterface(IID_IHXClientEngineSetup, (void **) &pEngineSetup); + if (!pEngineSetup) + print2stderr("no engine setup interface\n"); + else + { + pEngineContext = new HSPEngineContext(this, pCommonClassFactory); + pEngineContext->AddRef(); +#ifdef HX_LOG_SUBSYSTEM + HX_ENABLE_LOGGING(pEngineContext); +#endif + pEngineSetup->Setup(pEngineContext); + pEngineSetup->Release(); + } + + // get the client engine selector + pCEselect = 0; + pEngine->QueryInterface(IID_IHXClientEngineSelector, (void **) &pCEselect); + if (!pCEselect) + print2stderr("no CE selector\n"); + + pPluginE = 0; + // get the plugin enumerator + pEngine->QueryInterface(IID_IHXPluginEnumerator, (void **) &pPluginE); + if (!pPluginE) + print2stderr("no plugin enumerator\n"); + + pPlugin2Handler = 0; + // get the plugin2handler + pEngine->QueryInterface(IID_IHXPlugin2Handler, (void **) &pPlugin2Handler); + if (!pPlugin2Handler) + print2stderr("no plugin enumerator\n"); + + pAudioDeviceManager = 0; + // get the audio device mananger + pEngine->QueryInterface(IID_IHXAudioDeviceManager, (void **) &pAudioDeviceManager); + if (!pAudioDeviceManager) + print2stderr("no audio device manager\n"); + + // create players + for (i = 0; i < numPlayers; i++) + { + addPlayer(); + } + +#ifdef USE_HELIX_ALSA + pAudioDevice = 0; + if ( m_outputsink == ALSA && pAudioDeviceManager && !m_AlsaCapableCore) + { + pAudioDevice = new HSPAudioDevice(this, m_device); + + // change the AudioDevice if we are using alsa (player 0's audion device is special...) + pAudioDeviceManager->Replace(pAudioDevice); + } +#endif + + pAudioDeviceResponse = 0; + pEngine->QueryInterface(IID_IHXAudioDeviceResponse, (void**) &pAudioDeviceResponse); + + pAudioHookManager = 0; + pFinalAudioHook = 0; + pEngine->QueryInterface(IID_IHXAudioHookManager, (void **) &pAudioHookManager); + if (!pAudioHookManager) + print2stderr("no audio device hook manager\n"); + + + // install hook for visualizations, equalizer, and volume control - for use with streams + // the time in the packets is the presentation time - which maps better to streams + HSPFinalAudioHook *pPMAH = new HSPFinalAudioHook(this); + pAudioHookManager->AddHook(pPMAH); + pFinalAudioHook = pPMAH; + + if (pPlugin2Handler) + { + IHXValues* pPluginProps; + const char* szPropName; + IHXBuffer* pPropValue; + char *value; + HX_RESULT ret; + MimeList *ml; + mimehead = 0; + char mime[1024]; + char ext[1024]; + bool hasmime, hasexts; + + pPluginProps = 0; + int n = pPlugin2Handler->GetNumOfPlugins2(); + m_numPlugins = n; + print2stderr("Got the plugin2 handler: numplugins = %d\n", n); + m_pluginInfo = new pluginInfo* [n]; + for (i=0; i<n; i++) + { + m_pluginInfo[i] = new pluginInfo; + m_pluginInfo[i]->description = ""; + m_pluginInfo[i]->copyright = ""; + m_pluginInfo[i]->moreinfourl = ""; + hasmime = hasexts = false; + pPlugin2Handler->GetPluginInfo(i, pPluginProps); + if (pPluginProps) + { + ret = pPluginProps->GetFirstPropertyCString(szPropName, pPropValue); + while(SUCCEEDED(ret)) + { + value = (char*)pPropValue->GetBuffer(); + if (!strcmp(szPropName, "FileMime")) + { + strcpy(mime, value); + hasmime = true; + } + + if (!strcmp(szPropName, "FileExtensions")) + { + strcpy(ext, value); + hasexts = true; + } + + if (!strcmp(szPropName, "Description")) + { + m_pluginInfo[i]->description = new char[ strlen(value) + 1 ]; + strcpy(m_pluginInfo[i]->description, value); + } + + if (!strcmp(szPropName, "Copyright")) + { + m_pluginInfo[i]->copyright = new char[ strlen(value) + 1 ]; + strcpy(m_pluginInfo[i]->copyright, value); + } + + + if (!strcmp(szPropName, "PlgCopy")) + { + m_pluginInfo[i]->moreinfourl = new char[ strlen(value) + 1 ]; + strcpy(m_pluginInfo[i]->moreinfourl, value); + } + + ret = pPluginProps->GetNextPropertyCString(szPropName, pPropValue); + } + HX_RELEASE(pPluginProps); + if (hasmime && hasexts) + { + mimelistlen++; + ml = new MimeList(mime, ext); + ml->fwd = mimehead; + mimehead = ml; + } + } + } + } +} + +int HelixSimplePlayer::initDirectSS() +{ + if (m_outputsink == ALSA) + { + closeAudioDevice(); + m_direct = ALSA; + openAudioDevice(); + } + else + { + closeAudioDevice(); + m_direct = OSS; + openAudioDevice(); + } + +#ifdef USE_HELIX_ALSA + m_MvolBefore = m_MvolAtStart = getDirectMasterVolume(); + print2stderr("***Master VolAtStart is %d\n", m_MvolAtStart); + setDirectMasterVolume(m_MvolAtStart); +#endif + + m_volBefore = m_volAtStart = getDirectPCMVolume(); + print2stderr("***VolAtStart is %d\n", m_volAtStart); + setDirectPCMVolume(m_volAtStart); + return 0; +} + +int HelixSimplePlayer::addPlayer() +{ + if ((nNumPlayers+1) == MAX_PLAYERS) + { + print2stderr("MAX_PLAYERS: %d nNumPlayers: %d\n", MAX_PLAYERS, nNumPlayers); + return -1; + } + + ppctrl[nNumPlayers] = new struct playerCtrl; + memset(ppctrl[nNumPlayers], 0, sizeof(struct playerCtrl)); + + pthread_mutexattr_t ma; + + pthread_mutexattr_init(&ma); + pthread_mutexattr_settype(&ma, PTHREAD_MUTEX_FAST_NP); // note this is not portable outside linux and a few others + pthread_mutex_init(&ppctrl[nNumPlayers]->m_scope_m, &ma); + + ppctrl[nNumPlayers]->bPlaying = false; + ppctrl[nNumPlayers]->bStarting = false; + ppctrl[nNumPlayers]->bFadeIn = false; + ppctrl[nNumPlayers]->bFadeOut = false; + ppctrl[nNumPlayers]->ulFadeLength = 0; + ppctrl[nNumPlayers]->pStream = 0; + ppctrl[nNumPlayers]->pszURL = 0; + + memset(&ppctrl[nNumPlayers]->md, 0, sizeof(ppctrl[nNumPlayers]->md)); + + ppctrl[nNumPlayers]->pHSPContext = new HSPClientContext(nNumPlayers, this); + if (!ppctrl[nNumPlayers]->pHSPContext) + { + print2stdout("Error: Out of Memory. num players is %d\n", nNumPlayers); + theErr = HXR_UNEXPECTED; + return -1; + } + ppctrl[nNumPlayers]->pHSPContext->AddRef(); + + //initialize the example context + + char pszGUID[GUID_LEN + 1]; /* Flawfinder: ignore */ // add 1 for terminator + //char* token = NULL; + IHXPreferences* pPreferences = NULL; + + if (HXR_OK != pEngine->CreatePlayer(ppctrl[nNumPlayers]->pPlayer)) + { + theErr = HXR_FAILED; + return -1; + } + + pszGUID[0] = '\0'; + +// disable for now - I don't know what I was thinking +#ifdef _DISABLE_CUZ_I_MUST_HAVE_BEEN_NUTS_ + if (m_pszGUIDList) + { + // Get next GUID from the GUID list + if (nNumPlayers == 0) + { + token = strtok(m_pszGUIDList, "\n\0"); + } + else + { + token = strtok(NULL, "\n\0"); + } + if (token) + { + strncpy(pszGUID, token, GUID_LEN); /* Flawfinder: ignore */ + pszGUID[GUID_LEN] = '\0'; + } + } +#endif + + ppctrl[nNumPlayers]->pPlayer->QueryInterface(IID_IHXPreferences, (void**) &pPreferences); + ppctrl[nNumPlayers]->pHSPContext->Init(ppctrl[nNumPlayers]->pPlayer, pPreferences, pszGUID); + ppctrl[nNumPlayers]->pPlayer->SetClientContext(ppctrl[nNumPlayers]->pHSPContext); + HX_RELEASE(pPreferences); + + ppctrl[nNumPlayers]->pPlayer->QueryInterface(IID_IHXErrorSinkControl, (void**) &pErrorSinkControl); + if (pErrorSinkControl) + { + ppctrl[nNumPlayers]->pHSPContext->QueryInterface(IID_IHXErrorSink, (void**) &pErrorSink); + if (pErrorSink) + pErrorSinkControl->AddErrorSink(pErrorSink, HXLOG_EMERG, HXLOG_INFO); + HX_RELEASE(pErrorSink); + } + + HX_RELEASE(pErrorSinkControl); + + // Get the Player2 interface + ppctrl[nNumPlayers]->pPlayer->QueryInterface(IID_IHXPlayer2, (void**) &ppctrl[nNumPlayers]->pPlayer2); + if (!ppctrl[nNumPlayers]->pPlayer2) + print2stderr("no player2 device\n"); + + // Get the Audio Player + ppctrl[nNumPlayers]->pPlayer->QueryInterface(IID_IHXAudioPlayer, (void**) &ppctrl[nNumPlayers]->pAudioPlayer); + if (ppctrl[nNumPlayers]->pAudioPlayer) + { + // ...and now the volume interface + //ppctrl[nNumPlayers]->pVolume = ppctrl[nNumPlayers]->pAudioPlayer->GetAudioVolume(); + //ppctrl[nNumPlayers]->pVolume = ppctrl[nNumPlayers]->pAudioPlayer->GetDeviceVolume(); + if (ppctrl[nNumPlayers]->pVolume) + { +#ifndef HELIX_SW_VOLUME_INTERFACE + HelixSimplePlayerVolumeAdvice *pVA = new HelixSimplePlayerVolumeAdvice(this, nNumPlayers); + pVA->AddRef(); + ppctrl[nNumPlayers]->pVolume->AddAdviseSink((IHXVolumeAdviseSink *)pVA); + ppctrl[nNumPlayers]->pVolumeAdvise = pVA; + ppctrl[nNumPlayers]->volume = 50; // should get volume advise, which will set this properly +#else + // set the helix sw interface volume to 100, we'll control the volume either ourselves post equalization or by + // amaorok using direct hardware volume + //ppctrl[nNumPlayers]->pVolume->SetVolume(100); + ppctrl[nNumPlayers]->pVolume->SetVolume(100); + ppctrl[nNumPlayers]->pVolumeAdvise = 0; +#endif + } + + // add the IHXAudioStreamInfoResponse it the AudioPlayer + HelixSimplePlayerAudioStreamInfoResponse *pASIR = new HelixSimplePlayerAudioStreamInfoResponse(this, nNumPlayers); + ppctrl[nNumPlayers]->pAudioPlayer->SetStreamInfoResponse(pASIR); + ppctrl[nNumPlayers]->pStreamInfoResponse = pASIR; + + // ...and get the CrossFader + ppctrl[nNumPlayers]->pAudioPlayer->QueryInterface(IID_IHXAudioCrossFade, (void **) &(ppctrl[nNumPlayers]->pCrossFader)); + if (!ppctrl[nNumPlayers]->pCrossFader) + print2stderr("CrossFader not available\n"); + + // install hook for visualizations, equalizer, and volume control - for use with local files + // a FinalAudioHook is used for streams + HSPPostMixAudioHook *pPMAH = new HSPPostMixAudioHook(this, nNumPlayers); + ppctrl[nNumPlayers]->pAudioPlayer->AddPostMixHook(pPMAH, false, true); + ppctrl[nNumPlayers]->pPostMixHook = pPMAH; + } + else + print2stderr("No AudioPlayer Found - how can we play music!!\n"); + + ++nNumPlayers; + + //print2stderr("Added player, total is %d\n",nNumPlayers); + return 0; +} + +HelixSimplePlayer::~HelixSimplePlayer() +{ + tearDown(); + + // only now invalidate the device, not whenever we teardown + delete [] m_device; +} + +void HelixSimplePlayer::tearDown() +{ + int i; + FPRMCLOSEENGINE fpCloseEngine; + + if (theErr != HXR_OK) // failed to initialize properly + return; + + // make sure all players are stopped, + stop(); + + print2stderr("TEARDOWN\n"); + + for (i=nNumPlayers-1; i>=0; i--) + { + if (ppctrl[i]->pCrossFader) + ppctrl[i]->pCrossFader->Release(); + + if (ppctrl[i]->pAudioPlayer) + { + ppctrl[i]->pAudioPlayer->RemovePostMixHook( ppctrl[i]->pPostMixHook ); + ppctrl[i]->pPostMixHook->Release(); + + ppctrl[i]->pAudioPlayer->RemoveStreamInfoResponse((IHXAudioStreamInfoResponse *) ppctrl[i]->pStreamInfoResponse); + + if (ppctrl[i]->pVolume) + { + if (ppctrl[i]->pVolumeAdvise) + { + ppctrl[i]->pVolume->RemoveAdviseSink(ppctrl[i]->pVolumeAdvise); + ppctrl[i]->pVolumeAdvise->Release(); + } + ppctrl[i]->pVolume->Release(); + } + + ppctrl[i]->pAudioPlayer->Release(); + } + + if ( ppctrl[i]->pszURL ) + delete [] ppctrl[i]->pszURL; + + if (ppctrl[i]->pHSPContext) + ppctrl[i]->pHSPContext->Release(); + + if (ppctrl[i]->pPlayer2) + ppctrl[i]->pPlayer2->Release(); + + if (ppctrl[i]->pPlayer && pEngine) + { + pEngine->ClosePlayer(ppctrl[i]->pPlayer); + ppctrl[i]->pPlayer->Release(); + } + + delete ppctrl[i]; + } + + if (pAudioDevice) + pAudioDevice->Release(); + + if (pAudioDeviceResponse) + pAudioDeviceResponse->Release(); + + delete [] ppctrl; + + if (pCommonClassFactory) + pCommonClassFactory->Release(); + if (pCEselect) + pCEselect->Release(); + if (pPluginE) + pPluginE->Release(); + if (pPlugin2Handler) + pPlugin2Handler->Release(); + if (pAudioDeviceManager) + pAudioDeviceManager->Release(); + if (pAudioHookManager) + { + pAudioHookManager->RemoveHook(pFinalAudioHook); + pFinalAudioHook->Release(); + pAudioHookManager->Release(); + } + if (pEngineContext) + pEngineContext->Release(); + + fpCloseEngine = (FPRMCLOSEENGINE) dlsym(core_handle, "CloseEngine"); + if (fpCloseEngine && pEngine) + { + fpCloseEngine(pEngine); + pEngine = NULL; + } + + dlclose(core_handle); + + if (m_pszUsername) + { + delete [] m_pszUsername; + } + if (m_pszPassword) + { + delete [] m_pszPassword; + } + if (m_pszGUIDFile) + { + delete [] m_pszGUIDFile; + } + if (m_pszGUIDList) + { + delete [] m_pszGUIDList; + } + + for (i=0; i<m_numPlugins; i++) + delete m_pluginInfo[i]; + delete [] m_pluginInfo; + + if (bEnableVerboseMode) + { + print2stdout("\nDone.\n"); + } + + MimeList *ml = mimehead, *mh; + while (ml) + { + mh = ml->fwd; + delete ml; + ml = mh; + } + + closeAudioDevice(); + + theErr = HXR_FAILED; + pErrorSink = NULL; + pErrorSinkControl = NULL; + pPluginE = 0; + pPlugin2Handler = 0; + ppctrl = NULL; + bURLFound = false; + nNumPlayers = 0; + nNumPlayRepeats = 1; + nTimeDelta = DEFAULT_TIME_DELTA; + nStopTime = DEFAULT_STOP_TIME; + bStopTime = true; + bStopping = false; + nPlay = 0; + bEnableAdviceSink = false; + bEnableVerboseMode = false; + pEngine = NULL; + m_pszUsername = NULL; + m_pszPassword = NULL; + m_pszGUIDFile = NULL; + m_pszGUIDList = NULL; + m_Error = 0; + m_ulNumSecondsPlayed = 0; + mimehead = 0; + m_preamp = 0; +} + + +void HelixSimplePlayer::setOutputSink( HelixSimplePlayer::AUDIOAPI out ) +{ +#ifdef USE_HELIX_ALSA + m_outputsink = out; +#else + m_outputsink = OSS; +#endif +} + +HelixSimplePlayer::AUDIOAPI HelixSimplePlayer::getOutputSink() +{ + return m_outputsink; +} + +void HelixSimplePlayer::setDevice( const char *dev ) +{ + delete [] m_device; + + int len = strlen(dev); + m_device = new char [len + 1]; + strcpy(m_device, dev); +} + +const char *HelixSimplePlayer::getDevice() +{ + return m_device; +} + + +#define MAX_DEV_NAME 255 +#define HX_VOLUME SOUND_MIXER_PCM +void HelixSimplePlayer::openAudioDevice() +{ + switch (m_direct) + { + case OSS: + { + //Check the environmental variable to let user overide default device. + char *pszOverrideName = getenv( "AUDIO" ); /* Flawfinder: ignore */ + char szDevName[MAX_DEV_NAME]; /* Flawfinder: ignore */ + + // Use defaults if no environment variable is set. + if ( pszOverrideName && strlen(pszOverrideName)>0 ) + { + SafeStrCpy( szDevName, pszOverrideName, MAX_DEV_NAME ); + } + else + { + SafeStrCpy( szDevName, "/dev/mixer", MAX_DEV_NAME ); + } + + // Open the audio device if it isn't already open + if ( m_nDevID < 0 ) + { + m_nDevID = ::open( szDevName, O_WRONLY ); + } + + if ( m_nDevID < 0 ) + { + print2stderr("Failed to open audio(%s)!!!!!!! Code is: %d errno: %d\n", + szDevName, m_nDevID, errno ); + + //Error opening device. + } + } + break; + + case ALSA: + { +#ifdef USE_HELIX_ALSA + int err; + + print2stderr("Opening ALSA mixer device PCM\n"); + + err = snd_mixer_open(&m_pAlsaMixerHandle, 0); + if (err < 0) + print2stderr("snd_mixer_open: %s\n", snd_strerror (err)); + + if (err == 0) + { + err = snd_mixer_attach(m_pAlsaMixerHandle, m_alsaDevice); + if (err < 0) + print2stderr("snd_mixer_attach: %s\n", snd_strerror (err)); + } + + if (err == 0) + { + err = snd_mixer_selem_register(m_pAlsaMixerHandle, NULL, NULL); + if (err < 0) + print2stderr("snd_mixer_selem_register: %s\n", snd_strerror (err)); + } + + if (err == 0) + { + err = snd_mixer_load(m_pAlsaMixerHandle); + if(err < 0 ) + print2stderr("snd_mixer_load: %s\n", snd_strerror (err)); + } + + if (err == 0) + { + /* Find the mixer element */ + snd_mixer_elem_t* elem = snd_mixer_first_elem(m_pAlsaMixerHandle); + snd_mixer_elem_type_t type; + const char* elem_name = NULL; + snd_mixer_selem_id_t *sid = NULL; + + snd_mixer_selem_id_alloca(&sid); + + while (elem) + { + type = snd_mixer_elem_get_type(elem); + if (type == SND_MIXER_ELEM_SIMPLE) + { + snd_mixer_selem_get_id(elem, sid); + + /* We're only interested in playback volume controls */ + if(snd_mixer_selem_has_playback_volume(elem) && !snd_mixer_selem_has_common_volume(elem) ) + { + elem_name = snd_mixer_selem_id_get_name(sid); + if (!m_pAlsaPCMMixerElem && strcmp(elem_name, "Master") == 0) + m_pAlsaMasterMixerElem = elem; + + if (!m_pAlsaPCMMixerElem && strcmp(elem_name, "PCM") == 0) + m_pAlsaPCMMixerElem = elem; + + if (m_pAlsaMasterMixerElem && m_pAlsaPCMMixerElem) + break; + } + } + elem = snd_mixer_elem_next(elem); + } + + if (!elem) + { + print2stderr("Could not find a usable mixer element\n"); + err = -1; + } + } + + + if (err != 0) + { + if(m_pAlsaMixerHandle) + { + snd_mixer_close(m_pAlsaMixerHandle); + m_pAlsaMixerHandle = NULL; + } + } +#endif + } + break; + + default: + print2stderr("Unknown audio interface in openAudioDevice()\n"); + } +} + +void HelixSimplePlayer::closeAudioDevice() +{ + switch (m_direct) + { + case OSS: + { + if( m_nDevID >= 0 ) + { + ::close( m_nDevID ); + m_nDevID = -1; + } + } + break; + + case ALSA: + { +#ifdef USE_HELIX_ALSA + int err = 0; + + if (m_pAlsaMixerHandle && m_pAlsaMasterMixerElem) + { + err = snd_mixer_detach(m_pAlsaMixerHandle, "Master"); + if (err < 0) + print2stderr("snd_mixer_detach: %s\n", snd_strerror(err)); + } + + if (m_pAlsaMixerHandle && m_pAlsaPCMMixerElem) + { + err = snd_mixer_detach(m_pAlsaMixerHandle, "PCM"); + if (err < 0) + print2stderr("snd_mixer_detach: %s\n", snd_strerror(err)); + } + + + if (m_pAlsaMixerHandle) + { + if(err == 0) + { + err = snd_mixer_close(m_pAlsaMixerHandle); + if(err < 0) + print2stderr("snd_mixer_close: %s\n", snd_strerror (err)); + } + + if(err == 0) + { + m_pAlsaMixerHandle = NULL; + m_pAlsaPCMMixerElem = NULL; + } + } +#endif + } + break; + + default: + print2stderr("Unknown audio interface in closeAudioDevice()\n"); + } +} + +// it seems the master volume only gets reset on track change when using ALSA +// sheez, I thought Amarok wasnt supposed to be a mixer?? +// all this code is so that you can actually *use* a mixer and have it work +// the way you expect... +#ifdef USE_HELIX_ALSA +int HelixSimplePlayer::getDirectMasterVolume() +{ + int nRetVolume = 0; + + switch (m_direct) + { + case ALSA: + { + if (!m_pAlsaMasterMixerElem) + return nRetVolume; + + snd_mixer_elem_type_t type; + int err = 0; + type = snd_mixer_elem_get_type(m_pAlsaMasterMixerElem); + + if (type == SND_MIXER_ELEM_SIMPLE) + { + long volumeL, volumeR, min_volume, max_volume; + + if(snd_mixer_selem_has_playback_volume(m_pAlsaMasterMixerElem) || + snd_mixer_selem_has_playback_volume_joined(m_pAlsaMasterMixerElem)) + { + err = snd_mixer_selem_get_playback_volume(m_pAlsaMasterMixerElem, + SND_MIXER_SCHN_FRONT_LEFT, + &volumeL); + if (err < 0) + print2stderr("snd_mixer_selem_get_playback_volume (L): %s\n", snd_strerror (err)); + else + { + if ( snd_mixer_selem_is_playback_mono ( m_pAlsaMasterMixerElem )) + volumeR = volumeL; + else + { + err = snd_mixer_selem_get_playback_volume(m_pAlsaMasterMixerElem, + SND_MIXER_SCHN_FRONT_RIGHT, + &volumeR); + if (err < 0) + print2stderr("snd_mixer_selem_get_playback_volume (R): %s\n", snd_strerror (err)); + } + } + + if (err == 0) + { + snd_mixer_selem_get_playback_volume_range(m_pAlsaMasterMixerElem, + &min_volume, + &max_volume); + + if(max_volume > min_volume) + nRetVolume = (UINT16) (0.5 + (100.0 * (double)(volumeL + volumeR) / (2.0 * (max_volume - min_volume)))); + } + } + } + } + break; + + default: + print2stderr("Unknown audio interface in getDirectMasterVolume()\n"); + } + + return nRetVolume; +} + +void HelixSimplePlayer::setDirectMasterVolume(int vol) +{ + switch (m_direct) + { + case ALSA: + { + if (!m_pAlsaMasterMixerElem) + return; + + snd_mixer_elem_type_t type; + int err = 0; + type = snd_mixer_elem_get_type(m_pAlsaMasterMixerElem); + + + if (type == SND_MIXER_ELEM_SIMPLE) + { + long volume, min_volume, max_volume, range; + + if(snd_mixer_selem_has_playback_volume(m_pAlsaMasterMixerElem) || + snd_mixer_selem_has_playback_volume_joined(m_pAlsaMasterMixerElem)) + { + snd_mixer_selem_get_playback_volume_range(m_pAlsaMasterMixerElem, + &min_volume, + &max_volume); + + range = max_volume - min_volume; + volume = (long) (((double)vol / 100) * range + min_volume); + + + err = snd_mixer_selem_set_playback_volume( m_pAlsaMasterMixerElem, + SND_MIXER_SCHN_FRONT_LEFT, + volume); + if (err < 0) + print2stderr("snd_mixer_selem_set_playback_volume: %s\n", snd_strerror (err)); + + if (!snd_mixer_selem_is_playback_mono (m_pAlsaMasterMixerElem)) + { + /* Set the right channel too */ + err = snd_mixer_selem_set_playback_volume( m_pAlsaMasterMixerElem, + SND_MIXER_SCHN_FRONT_RIGHT, + volume); + if (err < 0) + print2stderr("snd_mixer_selem_set_playback_volume: %s\n", snd_strerror (err)); + } + } + } + } + break; + + default: + print2stderr("Unknown audio interface in setDirectMasterVolume()\n"); + } +} +#endif + +int HelixSimplePlayer::getDirectPCMVolume() +{ + int nRetVolume = 0; + + switch (m_direct) + { + case OSS: + { + int nVolume = 0; + int nLeftVolume = 0; + int nRightVolume = 0; + + if (m_nDevID < 0 || (::ioctl( m_nDevID, MIXER_READ(HX_VOLUME), &nVolume) < 0)) + { + print2stderr("ioctl fails when reading HW volume: mnDevID=%d, errno=%d\n", m_nDevID, errno); + nRetVolume = 50; // sensible default + } + else + { + nLeftVolume = (nVolume & 0x000000ff); + nRightVolume = (nVolume & 0x0000ff00) >> 8; + + //Which one to use? Average them? + nRetVolume = nLeftVolume ; + } + } + break; + + case ALSA: + { +#ifdef USE_HELIX_ALSA + if (!m_pAlsaPCMMixerElem) + return nRetVolume; + + snd_mixer_elem_type_t type; + int err = 0; + type = snd_mixer_elem_get_type(m_pAlsaPCMMixerElem); + + if (type == SND_MIXER_ELEM_SIMPLE) + { + long volumeL, volumeR, min_volume, max_volume; + + if(snd_mixer_selem_has_playback_volume(m_pAlsaPCMMixerElem) || + snd_mixer_selem_has_playback_volume_joined(m_pAlsaPCMMixerElem)) + { + err = snd_mixer_selem_get_playback_volume(m_pAlsaPCMMixerElem, + SND_MIXER_SCHN_FRONT_LEFT, + &volumeL); + if (err < 0) + print2stderr("snd_mixer_selem_get_playback_volume (L): %s\n", snd_strerror (err)); + else + { + if ( snd_mixer_selem_is_playback_mono ( m_pAlsaPCMMixerElem )) + volumeR = volumeL; + else + { + err = snd_mixer_selem_get_playback_volume(m_pAlsaPCMMixerElem, + SND_MIXER_SCHN_FRONT_RIGHT, + &volumeR); + if (err < 0) + print2stderr("snd_mixer_selem_get_playback_volume (R): %s\n", snd_strerror (err)); + } + } + + if (err == 0) + { + snd_mixer_selem_get_playback_volume_range(m_pAlsaPCMMixerElem, + &min_volume, + &max_volume); + + if(max_volume > min_volume) + nRetVolume = (UINT16) (0.5 + (100.0 * (double)(volumeL + volumeR) / (2.0 * (max_volume - min_volume)))); + } + } + } +#endif + } + break; + + default: + print2stderr("Unknown audio interface in getDirectPCMVolume()\n"); + } + + return nRetVolume; +} + +void HelixSimplePlayer::setDirectPCMVolume(int vol) +{ + switch (m_direct) + { + case OSS: + { + int nNewVolume=0; + + //Set both left and right volumes. + nNewVolume = (vol & 0xff) | ((vol & 0xff) << 8); + + if (::ioctl( m_nDevID, MIXER_WRITE(HX_VOLUME), &nNewVolume) < 0) + print2stderr("Unable to set direct HW volume\n"); + } + break; + + case ALSA: + { +#ifdef USE_HELIX_ALSA + if (!m_pAlsaPCMMixerElem) + return; + + snd_mixer_elem_type_t type; + int err = 0; + type = snd_mixer_elem_get_type(m_pAlsaPCMMixerElem); + + + if (type == SND_MIXER_ELEM_SIMPLE) + { + long volume, min_volume, max_volume, range; + + if(snd_mixer_selem_has_playback_volume(m_pAlsaPCMMixerElem) || + snd_mixer_selem_has_playback_volume_joined(m_pAlsaPCMMixerElem)) + { + snd_mixer_selem_get_playback_volume_range(m_pAlsaPCMMixerElem, + &min_volume, + &max_volume); + + range = max_volume - min_volume; + volume = (long) (((double)vol / 100) * range + min_volume); + + + err = snd_mixer_selem_set_playback_volume( m_pAlsaPCMMixerElem, + SND_MIXER_SCHN_FRONT_LEFT, + volume); + if (err < 0) + print2stderr("snd_mixer_selem_set_playback_volume: %s\n", snd_strerror (err)); + + if (!snd_mixer_selem_is_playback_mono (m_pAlsaPCMMixerElem)) + { + /* Set the right channel too */ + err = snd_mixer_selem_set_playback_volume( m_pAlsaPCMMixerElem, + SND_MIXER_SCHN_FRONT_RIGHT, + volume); + if (err < 0) + print2stderr("snd_mixer_selem_set_playback_volume: %s\n", snd_strerror (err)); + } + } + } +#endif + } + break; + + default: + print2stderr("Unknown audio interface in setDirectPCMVolume()\n"); + } +} + + +int HelixSimplePlayer::setURL(const char *file, int playerIndex, bool islocal) +{ + if (playerIndex == ALL_PLAYERS) + { + int i; + + for (i=0; i<nNumPlayers; i++) + setURL(file, i); + } + else + { + int len = strlen(file); + if (len >= MAXPATHLEN) + return -1;; + + print2stderr("SETURL MASTER VOL: %d\n",getDirectMasterVolume()); + + if (ppctrl[playerIndex]->pszURL) + delete [] ppctrl[playerIndex]->pszURL; + + // see if the file is already in the form of a url + char *tmp = strstr(file, "://"); + if (!tmp) + { + char pszURLOrig[MAXPATHLEN]; + const char* pszAddOn; + + strcpy(pszURLOrig, file); + RemoveWrappingQuotes(pszURLOrig); + pszAddOn = "file://"; + + ppctrl[playerIndex]->pszURL = new char[strlen(pszURLOrig)+strlen(pszAddOn)+1]; + if ( (len + strlen(pszAddOn)) < MAXPATHLEN ) + { + sprintf( ppctrl[playerIndex]->pszURL, "%s%s", pszAddOn, pszURLOrig ); + islocal = true; // diesnt matter what we were told... + } + else + return -1; + } + else + { + ppctrl[playerIndex]->pszURL = new char[len + 1]; + if (ppctrl[playerIndex]->pszURL) + strcpy(ppctrl[playerIndex]->pszURL, file); + else + return -1; + } + + ppctrl[playerIndex]->isLocal = islocal; + + print2stderr("opening %s on player %d, src cnt %d\n", + ppctrl[playerIndex]->pszURL, playerIndex, ppctrl[playerIndex]->pPlayer->GetSourceCount()); + +#ifdef __NOCROSSFADER__ + + if (HXR_OK == ppctrl[playerIndex]->pPlayer->OpenURL(ppctrl[playerIndex]->pszURL)) + { + print2stderr("opened player on %d src cnt %d\n", playerIndex, ppctrl[playerIndex]->pPlayer->GetSourceCount()); + m_urlchanged = true; + } +#else + /* try OpenRequest instead... */ + IHXRequest *ireq = 0; + pthread_mutex_lock(&m_engine_m); + pCommonClassFactory->CreateInstance(CLSID_IHXRequest, (void **)&ireq); + if (ireq) + { + //print2stderr("GOT THE IHXRequest Interface!!\n"); + ireq->SetURL(ppctrl[playerIndex]->pszURL); + ppctrl[playerIndex]->pPlayer2->OpenRequest(ireq); + m_urlchanged = true; + ireq->Release(); + } + pthread_mutex_unlock(&m_engine_m); + +#endif + + } + + return 0; +} + +int HelixSimplePlayer::numPlugins() const +{ + if (pPluginE) + { + return ((int)pPluginE->GetNumOfPlugins()); + } + + return 0; +} + +int HelixSimplePlayer::getPluginInfo(int index, + const char *&description, + const char *©right, + const char *&moreinfourl) const +{ + if (index < m_numPlugins) + { + description = m_pluginInfo[index]->description; + copyright = m_pluginInfo[index]->copyright; + moreinfourl = m_pluginInfo[index]->moreinfourl; + + return 0; + } + return -1; +} + + +void HelixSimplePlayer::play(const char *file, int playerIndex, bool fadein, bool fadeout, unsigned long fadetime) +{ + if (!setURL(file, playerIndex)) + play(playerIndex, fadein, fadeout, fadetime); +} + +void HelixSimplePlayer::play(int playerIndex, bool fadein, bool fadeout, unsigned long fadetime) +{ + int i; + int firstPlayer = playerIndex == ALL_PLAYERS ? 0 : playerIndex; + int lastPlayer = playerIndex == ALL_PLAYERS ? nNumPlayers : playerIndex + 1; + + nPlay = 0; + nNumPlayRepeats=1; + while(nPlay < nNumPlayRepeats) + { + nPlay++; + if (bEnableVerboseMode) + { + print2stdout("Starting play #%d...\n", nPlay); + } + //print2stderr("firstplayer = %d lastplayer=%d\n",firstPlayer,lastPlayer); + + UINT32 starttime=0, endtime=0, now=0; + for (i = firstPlayer; i < lastPlayer; i++) + { + // start is already protected... + start(i, fadein, fadetime); + + starttime = GetTime(); + endtime = starttime + nTimeDelta; + while (1) + { + pthread_mutex_lock(&m_engine_m); + DoEvents(nTimeDelta); + pthread_mutex_unlock(&m_engine_m); + now = GetTime(); + if (now >= endtime) + break; + if (fadeout && !ppctrl[i]->bFadeOut && now > endtime - fadetime) + { + ppctrl[i]->bFadeOut = true; + ((HSPPreMixAudioHook *)ppctrl[i]->pPreMixHook)->setFadelength(fadetime); + ((HSPPreMixAudioHook *)ppctrl[i]->pPreMixHook)->setFadeout(true); + } + } + } + + starttime = GetTime(); + if (nStopTime == -1) + { + bStopTime = false; + } + else + { + endtime = starttime + nStopTime; + } + bStopping = false; + // Handle events coming from all of the players + while (!done(playerIndex)) + { + now = GetTime(); + if (!bStopping && bStopTime && now >= endtime) + { + // Stop all of the players, as they should all be done now + if (bEnableVerboseMode) + { + print2stdout("\nEnd (Stop) time reached. Stopping...\n"); + } + stop(playerIndex); + bStopping = true; + } + pthread_mutex_lock(&m_engine_m); + DoEvent(); + pthread_mutex_unlock(&m_engine_m); + } + + // Stop all of the players, as they should all be done now + if (bEnableVerboseMode) + { + print2stdout("\nPlayback complete. Stopping all players...\n"); + } + stop(playerIndex); + + // repeat until nNumRepeats + } +} + +void HelixSimplePlayer::start(int playerIndex, bool fadein, unsigned long fadetime) +{ + if (playerIndex == ALL_PLAYERS) + { + int i; + for (i=0; i<nNumPlayers; i++) + start(i, fadein, fadetime); + } + else + { + if (!ppctrl[playerIndex]->pszURL) + return; + + print2stderr("START MASTER VOL: %d\n",getDirectMasterVolume()); + + if (bEnableVerboseMode) + { + print2stdout("Starting player %d...\n", playerIndex); + } + + ppctrl[playerIndex]->bFadeIn = fadein; + ppctrl[playerIndex]->bFadeOut = false; // assume we'll only fade out if we have another track + ppctrl[playerIndex]->ulFadeLength = fadetime; + if (!ppctrl[playerIndex]->bPlaying) + { + pthread_mutex_lock(&m_engine_m); + ppctrl[playerIndex]->pPlayer->Begin(); + pthread_mutex_unlock(&m_engine_m); + + ppctrl[playerIndex]->bPlaying = true; + ppctrl[playerIndex]->bStarting = true; + //print2stderr("Begin player %d\n", playerIndex); + } + } +} + + +void HelixSimplePlayer::start(const char *file, int playerIndex, bool fadein, unsigned long fadetime) +{ + setURL(file, playerIndex); + start(playerIndex, fadein, fadetime); +} + + + +bool HelixSimplePlayer::done(int playerIndex) +{ + BOOL bAllDone = true; + + if (playerIndex == ALL_PLAYERS) + // Start checking at the end of the array since those players + // were started last and are therefore more likely to not be + // finished yet. + for (int i = nNumPlayers - 1; i >= 0 && bAllDone; i--) + { + pthread_mutex_lock(&m_engine_m); + if (ppctrl[i]->bStarting || !ppctrl[i]->pPlayer->IsDone()) + ppctrl[i]->bPlaying = (bAllDone = false); + pthread_mutex_unlock(&m_engine_m); + } + else + { + if (playerIndex < nNumPlayers) + { + pthread_mutex_lock(&m_engine_m); + if ((bAllDone = (!ppctrl[playerIndex]->bStarting && ppctrl[playerIndex]->pPlayer->IsDone()))) + ppctrl[playerIndex]->bPlaying = false; + pthread_mutex_unlock(&m_engine_m); + } + } + + return bAllDone; +} + +void HelixSimplePlayer::stop(int playerIndex) +{ + if (playerIndex == ALL_PLAYERS) + for (int i = 0; i < nNumPlayers; i++) + { + pthread_mutex_lock(&m_engine_m); + ppctrl[i]->pPlayer->Stop(); + pthread_mutex_unlock(&m_engine_m); + + ppctrl[i]->bPlaying = false; + ppctrl[i]->bStarting = false; + ppctrl[i]->isLocal = false; + } + else + { + if (playerIndex < nNumPlayers) + { + pthread_mutex_lock(&m_engine_m); + ppctrl[playerIndex]->pPlayer->Stop(); + pthread_mutex_unlock(&m_engine_m); + ppctrl[playerIndex]->bPlaying = false; + ppctrl[playerIndex]->bStarting = false; + ppctrl[playerIndex]->isLocal = false; + memset(&ppctrl[playerIndex]->md, 0, sizeof(ppctrl[playerIndex]->md)); + } + } +} + +HelixSimplePlayer::metaData *HelixSimplePlayer::getMetaData(int playerIndex) +{ + return &ppctrl[playerIndex]->md; +} + + +void HelixSimplePlayer::dispatch() +{ + struct _HXxEvent *pNothing = 0x0; + struct timeval tv; + int volAfter = 0; + + tv.tv_sec = 0; + tv.tv_usec = SLEEP_TIME*1000; + + if (m_urlchanged) + { +#ifdef USE_HELIX_ALSA + m_MvolBefore = getDirectMasterVolume(); + print2stderr("Master Volume is: %d\n", m_MvolBefore); +#endif + m_volBefore = getDirectPCMVolume(); + m_urlchanged = false; + print2stderr("Volume is: %d\n", m_volBefore); + } + pEngine->EventOccurred(pNothing); +#ifdef USE_HELIX_ALSA + if (m_MvolBefore > 0 && m_MvolAtStart != m_MvolBefore && (volAfter = getDirectMasterVolume()) != m_MvolBefore) + { + print2stderr("RESETTING MASTER VOLUME TO: %d\n", m_MvolBefore); + setDirectMasterVolume(m_volBefore); + print2stderr("Now Master Volume is %d\n", getDirectMasterVolume()); + m_MvolBefore = -1; + } +#endif + if (m_volBefore > 0 && m_volAtStart != m_volBefore && (volAfter = getDirectPCMVolume()) != m_volBefore) + { + print2stderr("RESETTING VOLUME TO: %d\n", m_volBefore); + setDirectPCMVolume(m_volBefore); + print2stderr("Now Volume is %d\n", getDirectPCMVolume()); + m_volBefore = -1; + } +} + + +bool HelixSimplePlayer::isPlaying(int playerIndex) const +{ + if (playerIndex < nNumPlayers) + return ppctrl[playerIndex]->bPlaying; + else + return false; +} + +bool HelixSimplePlayer::isLocal(int playerIndex) const +{ + if (playerIndex < nNumPlayers) + return (ppctrl[playerIndex]->isLocal && duration(playerIndex)); + else + return false; +} + +void HelixSimplePlayer::pause(int playerIndex) +{ + int i; + + if (playerIndex == ALL_PLAYERS) + for (i=0; i<nNumPlayers; i++) + pause(i); + else + if (playerIndex < nNumPlayers) + { + pthread_mutex_lock(&m_engine_m); + ppctrl[playerIndex]->pPlayer->Pause(); + pthread_mutex_unlock(&m_engine_m); + ppctrl[playerIndex]->bPlaying = false; + } +} + +void HelixSimplePlayer::resume(int playerIndex) +{ + int i; + + if (playerIndex == ALL_PLAYERS) + for (i=0; i<nNumPlayers; i++) + resume(i); + else + if (playerIndex < nNumPlayers) + { + pthread_mutex_lock(&m_engine_m); + ppctrl[playerIndex]->pPlayer->Begin(); + pthread_mutex_unlock(&m_engine_m); + ppctrl[playerIndex]->bPlaying = true; + } +} + + +void HelixSimplePlayer::seek(unsigned long pos, int playerIndex) +{ + int i; + + if (playerIndex == ALL_PLAYERS) + for (i=0; i<nNumPlayers; i++) + seek(pos, i); + else + if (playerIndex < nNumPlayers) + { + pthread_mutex_lock(&m_engine_m); + ppctrl[playerIndex]->pPlayer->Seek(pos); + pthread_mutex_unlock(&m_engine_m); + } +} + +unsigned long HelixSimplePlayer::where(int playerIndex) const +{ + if (playerIndex < nNumPlayers && ppctrl[playerIndex]->pHSPContext) + //return ppctrl[playerIndex]->pHSPContext->position(); + return ppctrl[playerIndex]->pPlayer->GetCurrentPlayTime(); + else + return 0; +} + +unsigned long HelixSimplePlayer::duration(int playerIndex) const +{ + if (playerIndex < nNumPlayers && ppctrl[playerIndex]->pHSPContext) + return ppctrl[playerIndex]->pHSPContext->duration(); + else + return 0; +} + +unsigned long HelixSimplePlayer::getVolume(int playerIndex) +{ + unsigned long vol; + + if (playerIndex < nNumPlayers) + { + pthread_mutex_lock(&m_engine_m); + vol = ppctrl[playerIndex]->volume; + //if (ppctrl[playerIndex]->pVolume) + //pVolume->GetVolume(); + pthread_mutex_unlock(&m_engine_m); + + return (vol); + } + else + return 0; +} + +void HelixSimplePlayer::setVolume(unsigned long vol, int playerIndex) +{ + int i; + + if (playerIndex == ALL_PLAYERS) + { + for (i=0; i<nNumPlayers; i++) + setVolume(vol, i); + } + else + if (playerIndex < nNumPlayers) + { + pthread_mutex_lock(&m_engine_m); +#ifndef HELIX_SW_VOLUME_INTERFACE + ppctrl[playerIndex]->volume = vol; + ((HSPFinalAudioHook *)pFinalAudioHook)->setGain(vol); +#else + ppctrl[playerIndex]->pVolume->SetVolume(vol); +#endif + pthread_mutex_unlock(&m_engine_m); + } +} + +void HelixSimplePlayer::setMute(bool mute, int playerIndex) +{ + int i; + + if (playerIndex == ALL_PLAYERS) + { + for (i=0; i<nNumPlayers; i++) + setMute(mute, i); + } + else + if (playerIndex < nNumPlayers) + { + pthread_mutex_lock(&m_engine_m); + ppctrl[playerIndex]->pVolume->SetMute(mute); + pthread_mutex_unlock(&m_engine_m); + } +} + + +bool HelixSimplePlayer::getMute(int playerIndex) +{ + bool ismute; + + if (playerIndex < nNumPlayers) + { + pthread_mutex_lock(&m_engine_m); + ismute = ppctrl[playerIndex]->ismute; +//pVolume->GetMute(); + pthread_mutex_unlock(&m_engine_m); + + return ismute; + } + else + return false; +} + +bool HelixSimplePlayer::ReadGUIDFile() +{ + BOOL bSuccess = false; + FILE* pFile = NULL; + int nNumRead = 0; + int readSize = 10000; + char* pszBuffer = new char[readSize]; + + if (m_pszGUIDFile) + { + if((pFile = fopen(m_pszGUIDFile, "r")) != NULL) + { + // Read in the entire file + nNumRead = fread(pszBuffer, sizeof(char), readSize, pFile); + pszBuffer[nNumRead] = '\0'; + + // Store it for later parsing + m_pszGUIDList = new char[nNumRead + 1]; + strcpy(m_pszGUIDList, pszBuffer); /* Flawfinder: ignore */ + + fclose(pFile); + pFile = NULL; + + if (nNumRead > 0) + { + bSuccess = true; + } + } + } + + delete [] pszBuffer; + + return bSuccess; +} + + +void HelixSimplePlayer::addScopeBuf(struct DelayQueue *item, int playerIndex) +{ + if (playerIndex >=0 && playerIndex < nNumPlayers) + { + pthread_mutex_lock(&ppctrl[playerIndex]->m_scope_m); + + if (ppctrl[playerIndex]->scopebuftail) + { + item->fwd = 0; + ppctrl[playerIndex]->scopebuftail->fwd = item; + ppctrl[playerIndex]->scopebuftail = item; + ppctrl[playerIndex]->scopecount++; + } + else + { + item->fwd = 0; + ppctrl[playerIndex]->scopebufhead = item; + ppctrl[playerIndex]->scopebuftail = item; + ppctrl[playerIndex]->scopecount = 1; + } + pthread_mutex_unlock(&ppctrl[playerIndex]->m_scope_m); + } +} + +struct DelayQueue *HelixSimplePlayer::getScopeBuf(int playerIndex) +{ + if (playerIndex >=0 && playerIndex < nNumPlayers) + { + pthread_mutex_lock(&ppctrl[playerIndex]->m_scope_m); + + struct DelayQueue *item = ppctrl[playerIndex]->scopebufhead; + + if (item) + { + ppctrl[playerIndex]->scopebufhead = item->fwd; + ppctrl[playerIndex]->scopecount--; + if (!ppctrl[playerIndex]->scopebufhead) + ppctrl[playerIndex]->scopebuftail = 0; + } + + pthread_mutex_unlock(&ppctrl[playerIndex]->m_scope_m); + + return item; + } + else + return 0; +} + +int HelixSimplePlayer::peekScopeTime(unsigned long &t, int playerIndex) +{ + if (playerIndex >=0 && playerIndex < nNumPlayers) + { + if (ppctrl[playerIndex]->scopebufhead) + t = ppctrl[playerIndex]->scopebufhead->time; + else + return -1; + return 0; + } + return -1; +} + +void HelixSimplePlayer::clearScopeQ(int playerIndex) +{ + if (playerIndex < 0) + { + for (int i=0; i<nNumPlayers; i++) + clearScopeQ(i); + } + else + { + struct DelayQueue *item; + while ((item = getScopeBuf(playerIndex))) + delete item; + } +} + diff --git a/amarok/src/engine/helix/helix-sp/helix-sp.h b/amarok/src/engine/helix/helix-sp/helix-sp.h new file mode 100644 index 00000000..1b758b5f --- /dev/null +++ b/amarok/src/engine/helix/helix-sp/helix-sp.h @@ -0,0 +1,373 @@ +/* + * + * This software is released under the provisions of the GPL version 2. + * see file "COPYING". If that file is not available, the full statement + * of the license can be found at + * + * http://www.fsf.org/licensing/licenses/gpl.txt + * + * Copyright (c) Paul Cifarelli 2005 + * Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved. + * + * at this point, this class should be HelixNotSoSimplePlayer... + */ + +#ifndef _HELIX_SIMPLEPLAYER_LIB_H_INCLUDED_ +#define _HELIX_SIMPLEPLAYER_LIB_H_INCLUDED_ + +class HSPClientAdviceSink; +class HSPClientContext; +class IHXErrorSinkControl; +class HelixSimplePlayerAudioStreamInfoResponse; + +#include <limits.h> +#include <sys/param.h> +#include <pthread.h> +#include <vector> +#include <config.h> +#include <iostream> +using std::vector; + +#define MAX_PATH PATH_MAX + +#define MAX_PLAYERS 100 // that should do it... +#define MAX_SCOPE_SAMPLES 5120 + +class HelixSimplePlayer; +class CHXURL; +class IHXVolumeAdviseSink; +class IHXAudioPlayer; +class IHXAudioStream; +class IHXAudioCrossFade; +class IHXPlayer; +class IHXPlayer2; +class IHXErrorSink; +class IHXErrorSinkControl; +class IHXAudioPlayer; +class IHXVolume; +class IHXPlayerNavigator; +class IHXClientEngineSelector; +class IHXClientEngine; +class IHXAudioHook; +class IHXAudioStreamInfoResponse; +class IHXCommonClassFactory; +class IHXPluginEnumerator; +class IHXPlugin2Handler; +class IHXAudioDeviceManager; +class IHXAudioHookManager; +class IHXPreferences; +class HSPAudioDevice; +class IHXAudioDeviceResponse; +#ifdef USE_HELIX_ALSA +struct _snd_mixer; +struct _snd_mixer_elem; +#endif + +// scope delay queue +class DelayQueue +{ +public: + DelayQueue() : fwd(0), len(0), time(0), etime(0), nchan(0), bps(0), allocd(false), buf(0) {} + DelayQueue(int bufsize) : fwd(0), len(bufsize), time(0), etime(0), nchan(0), bps(0), allocd(true), buf(0) + { buf = new unsigned char [ bufsize ]; } + DelayQueue(DelayQueue &src) : fwd(0), len(src.len), time(src.time), etime(src.etime), nchan(src.nchan), bps(src.bps), allocd(true), buf(0) + { buf = new unsigned char [ len ]; memcpy( (void *) buf, (void *) src.buf, src.len ); } + ~DelayQueue() { if (allocd) delete [] buf; } + struct DelayQueue *fwd; + int len; // len of the buffer + unsigned long time; // start time of the buffer + unsigned long etime; // end time of the buffer + int nchan; // number of channels + int bps; // bytes per sample + double tps; // time per sample + int spb; // samples per buffer + bool allocd; // did we allocate the memory? + unsigned char *buf; +}; + + +// simple list of supported mime type +struct MimeList +{ + MimeList(char *mimestr, char *ext) : mimetypes(0), mimeexts(0) + { mimetypes = new char[strlen(mimestr)+1]; strcpy(mimetypes,mimestr); mimeexts = new char[strlen(ext)+1]; strcpy(mimeexts,ext); } + ~MimeList() { delete [] mimetypes; delete [] mimeexts; } + struct MimeList *fwd; + char *mimetypes; + char *mimeexts; +}; + +class HelixSimplePlayer +{ +public: + enum { ALL_PLAYERS = -1 }; + + HelixSimplePlayer(); + virtual ~HelixSimplePlayer(); + + void init(const char *corelibpath, const char *pluginslibpath, const char *codecspath, int numPlayers = 1); + void tearDown(); + int initDirectSS(); + int addPlayer(); // add another player + void play(int playerIndex = ALL_PLAYERS, + bool fadein = false, bool fadout = false, + unsigned long fadetime = 0); // play the current url, waiting for it to finish + void play(const char *url, int playerIndex = ALL_PLAYERS, + bool fadein = false, bool fadeout = false, + unsigned long fadetime = 0); // play the file, setting it as the current url; wait for it to finish + int setURL(const char *url, + int playerIndex = ALL_PLAYERS, + bool islocal = true); // set the current url + bool done(int playerIndex = ALL_PLAYERS); // test to see if the player(s) is(are) done + void start(int playerIndex = ALL_PLAYERS, + bool fadein = false, + unsigned long fadetime = 0); // start the player + void start(const char *file, int playerIndex = ALL_PLAYERS, + bool fadein = false, + unsigned long fadetime = 0); // start the player, setting the current url first + void stop(int playerIndex = ALL_PLAYERS); // stop the player(s) + void pause(int playerIndex = ALL_PLAYERS); // pause the player(s) + void resume(int playerIndex = ALL_PLAYERS); // pause the player(s) + void seek(unsigned long pos, int playerIndex = ALL_PLAYERS); // seek to the pos + unsigned long where(int playerIndex) const; // where is the player in the playback + unsigned long duration(int playerIndex) const; // how long (ms) is this clip? + unsigned long getVolume(int playerIndex); // get the current volume + void setVolume(unsigned long vol, int playerIndex = ALL_PLAYERS); // set the volume + void setMute(bool mute, int playerIndex = ALL_PLAYERS); // set mute: mute = true to mute the volume, false to unmute + bool getMute(int playerIndex); // get the mute state of the player + void dispatch(); // dispatch the player(s) + + void cleanUpStream(int playerIndex); // cleanup after stream complete + bool isPlaying(int playerIndex) const; // is the player currently playing? + bool isLocal(int playerIndex) const; // playing a local file + int numPlayers() const { return nNumPlayers; } // return the number of players + + + virtual void onVolumeChange(int) {} // called when the volume is changed + virtual void onMuteChange(int) {} // called when mute is changed + virtual void onContacting(const char */*host*/) {} // called when contacting a host + virtual void onBuffering(int /*percentage*/) {} // called when buffering + + int getError() const { return theErr; } + + static char* RemoveWrappingQuotes(char* str); + //void setUsername(const char *username) { m_pszUsername = username; } + //void setPassword(const char *password) { m_pszPassword = password; } + //void setGUIDFile(const char *file) { m_pszGUIDFile = file; } + bool ReadGUIDFile(); + + typedef struct + { + char title[512]; + char artist[512]; + unsigned long bitrate; + } metaData; + +private: + void DoEvent(); + void DoEvents(int nTimeDelta); + unsigned long GetTime(); + + char mCoreLibPath[MAXPATHLEN]; + char mPluginLibPath[MAXPATHLEN]; + int theErr; + IHXErrorSink* pErrorSink; + IHXErrorSinkControl* pErrorSinkControl; + IHXClientEngineSelector* pCEselect; + IHXCommonClassFactory* pCommonClassFactory; + IHXPluginEnumerator* pPluginE; + IHXPlugin2Handler* pPlugin2Handler; + IHXAudioDeviceManager* pAudioDeviceManager; + IHXAudioHookManager* pAudioHookManager; + IHXAudioHook* pFinalAudioHook; + HSPAudioDevice* pAudioDevice; + + struct playerCtrl + { + bool bPlaying; + bool bStarting; + bool bFadeIn; + bool bFadeOut; + unsigned long ulFadeLength; + IHXAudioStream* pStream; + HSPClientContext* pHSPContext; + IHXPlayer* pPlayer; + IHXPlayer2* pPlayer2; + IHXAudioPlayer* pAudioPlayer; + IHXAudioCrossFade* pCrossFader; + IHXVolume* pVolume; + IHXVolumeAdviseSink* pVolumeAdvise; + IHXAudioStreamInfoResponse* pStreamInfoResponse; + IHXAudioHook* pPreMixHook; + IHXAudioHook* pPostMixHook; + metaData md; + char* pszURL; + bool isLocal; + unsigned short volume; + bool ismute; + // scope + int scopecount; + struct DelayQueue *scopebufhead; + struct DelayQueue *scopebuftail; + pthread_mutex_t m_scope_m; + } **ppctrl; + + IHXAudioDeviceResponse *pAudioDeviceResponse; + bool bURLFound; + int nNumPlayers; + int nNumPlayRepeats; + int nTimeDelta; + int nStopTime; + bool bStopTime; + void* core_handle; + bool bStopping; + int nPlay; + +public: + const IHXAudioPlayer *getAudioPlayer(int playerIndex) const { return ppctrl[playerIndex]->pAudioPlayer; } + const IHXAudioCrossFade *getCrossFader(int playerIndex) const { return ppctrl[playerIndex]->pCrossFader; } + + // crossfade + void setFadeout(bool fadeout, unsigned long fadelength, int playerIndex = ALL_PLAYERS); + + // scope + void addScopeBuf(struct DelayQueue *item, int playerIndex); + DelayQueue *getScopeBuf(int playerIndex); + int getScopeCount(int playerIndex) { return playerIndex >= 0 && playerIndex < nNumPlayers ? ppctrl[playerIndex]->scopecount : 0; } + int peekScopeTime(unsigned long &t, int playerIndex); + void clearScopeQ(int playerIndex = ALL_PLAYERS); + + // equalizer + void enableEQ(bool enabled) { m_eq_enabled = enabled; } + bool isEQenabled() { return m_eq_enabled; } + void updateEQgains(); + + int numPlugins() const; + int getPluginInfo(int index, + const char *&description, + const char *©right, + const char *&moreinfourl) const; + + + metaData *getMetaData(int playerIndex); + + const MimeList *getMimeList() const { return mimehead; } + int getMimeListLen() const { return mimelistlen; } + +private: + + bool bEnableAdviceSink; + bool bEnableVerboseMode; + IHXClientEngine* pEngine; + IHXPreferences* pEngineContext; + char* m_pszUsername; + char* m_pszPassword; + char* m_pszGUIDFile; + char* m_pszGUIDList; + int m_Error; + unsigned long m_ulNumSecondsPlayed; + + pthread_mutex_t m_engine_m; + + // supported mime type list + MimeList *mimehead; + int mimelistlen; + + // equalizer + bool m_eq_enabled; + +public: + // 0 = OSS + // 1 = OldOSSsupport + // 2 = ESound + // 3 = Alsa + // 4 = USound + enum AUDIOAPI + { + OSS, + OLDOSS, + ESOUND, + ALSA, + USOUND + }; + + void setOutputSink( AUDIOAPI out ); + AUDIOAPI getOutputSink(); + void setDevice( const char *dev ); + const char *getDevice(); + void setAlsaCapableCore() { m_AlsaCapableCore = true; } + virtual int fallbackToOSS() { return 0; } + + int m_preamp; + vector<int> m_equalizerGains; + +private: + AUDIOAPI m_outputsink; + char *m_device; + + // work around the annoying problem of the core reseting the PCM volume on every url change +protected: + void openAudioDevice(); + void closeAudioDevice(); +#ifdef USE_HELIX_ALSA + int getDirectMasterVolume(); + void setDirectMasterVolume(int vol); +#endif + int getDirectPCMVolume(); + void setDirectPCMVolume(int vol); + +private: + AUDIOAPI m_direct; + bool m_AlsaCapableCore; + int m_nDevID; +#ifdef USE_HELIX_ALSA + struct _snd_mixer* m_pAlsaMixerHandle; + struct _snd_mixer_elem* m_pAlsaMasterMixerElem; + struct _snd_mixer_elem* m_pAlsaPCMMixerElem; +#endif + char *m_alsaDevice; + bool m_urlchanged; + int m_volBefore; + int m_volAtStart; +#ifdef USE_HELIX_ALSA + int m_MvolBefore; + int m_MvolAtStart; +#endif + + int m_numPlugins; + struct pluginInfo + { + char *description; + char *copyright; + char *moreinfourl; + } **m_pluginInfo; + + +protected: + virtual int print2stdout(const char *fmt, ...) +#ifdef __GNUC__ + __attribute__ ((format (printf, 2, 3))) +#endif + ; + virtual int print2stderr(const char *fmt, ...) +#ifdef __GNUC__ + __attribute__ ((format (printf, 2, 3))) +#endif + ; + virtual void notifyUser(unsigned long/*code*/, const char */*moreinfo*/, const char */*moreinfourl*/) {} + virtual void interruptUser(unsigned long/*code*/, const char */*moreinfo*/, const char */*moreinfourl*/) {} + + friend class HSPClientAdviceSink; + friend class HSPErrorSink; + friend class HSPAuthenticationManager; + friend class HelixSimplePlayerAudioStreamInfoResponse; + friend class HSPPreMixAudioHook; + friend class HSPPostProcessor; + friend class HSPPostMixAudioHook; + friend class HSPFinalAudioHook; + friend class HelixSimplePlayerVolumeAdvice; + friend class HSPEngineContext; + friend class HSPAudioDevice; +}; + +#endif diff --git a/amarok/src/engine/helix/helix-sp/helixdefines.h b/amarok/src/engine/helix/helix-sp/helixdefines.h new file mode 100644 index 00000000..243d682d --- /dev/null +++ b/amarok/src/engine/helix/helix-sp/helixdefines.h @@ -0,0 +1,87 @@ +#ifndef HELIX_CONFIG_XVIDEO +#define HELIX_CONFIG_XVIDEO 1 +#endif +#ifndef HELIX_FEATURE_ADVANCEDGROUPMGR +#define HELIX_FEATURE_ADVANCEDGROUPMGR 1 +#endif +#ifndef HELIX_FEATURE_AUDIO +#define HELIX_FEATURE_AUDIO 1 +#endif +#ifndef HELIX_FEATURE_AUDIO_CODEC_14_4 +#define HELIX_FEATURE_AUDIO_CODEC_14_4 1 +#endif +#ifndef HELIX_FEATURE_AUDIO_CODEC_28_8 +#define HELIX_FEATURE_AUDIO_CODEC_28_8 1 +#endif +#ifndef HELIX_FEATURE_AUDIO_CODEC_GECKO +#define HELIX_FEATURE_AUDIO_CODEC_GECKO 1 +#endif +#ifndef HELIX_FEATURE_AUDIO_CODEC_INTERLEAVE_ALL +#define HELIX_FEATURE_AUDIO_CODEC_INTERLEAVE_ALL 1 +#endif +#ifndef HELIX_FEATURE_AUDIO_CODEC_O5_6 +#define HELIX_FEATURE_AUDIO_CODEC_O5_6 1 +#endif +#ifndef HELIX_FEATURE_AUDIO_CODEC_SIPRO +#define HELIX_FEATURE_AUDIO_CODEC_SIPRO 1 +#endif +#ifndef HELIX_FEATURE_AUDIO_CODEC_TOKYO +#define HELIX_FEATURE_AUDIO_CODEC_TOKYO 1 +#endif +#ifndef HELIX_FEATURE_AUDIO_MPA_LAYER1 +#define HELIX_FEATURE_AUDIO_MPA_LAYER1 1 +#endif +#ifndef HELIX_FEATURE_AUDIO_MPA_LAYER2 +#define HELIX_FEATURE_AUDIO_MPA_LAYER2 1 +#endif +#ifndef HELIX_FEATURE_AUDIO_MPA_LAYER3 +#define HELIX_FEATURE_AUDIO_MPA_LAYER3 1 +#endif +#ifndef HELIX_FEATURE_AUDIO_REAL +#define HELIX_FEATURE_AUDIO_REAL 1 +#endif +#ifndef HELIX_FEATURE_BASICGROUPMGR +#define HELIX_FEATURE_BASICGROUPMGR 1 +#endif +#ifndef HELIX_FEATURE_FULLGUID +#define HELIX_FEATURE_FULLGUID 1 +#endif +#ifndef HELIX_FEATURE_PLAYBACK_LOCAL +#define HELIX_FEATURE_PLAYBACK_LOCAL 1 +#endif +#ifndef HELIX_FEATURE_PLUGINHANDLER2 +#define HELIX_FEATURE_PLUGINHANDLER2 1 +#endif +#ifndef RIBOSOME_TEST_BRANCH +#define RIBOSOME_TEST_BRANCH 1 +#endif +#ifndef THREADS_SUPPORTED +#define THREADS_SUPPORTED 1 +#endif +#ifndef USE_XWINDOWS +#define USE_XWINDOWS 1 +#endif +#ifndef _GNU_SOURCE +#define _GNU_SOURCE 1 +#endif +#ifndef _LINUX +#define _LINUX 1 +#endif +#ifndef _RED_HAT_5_X_ +#define _RED_HAT_5_X_ 1 +#endif +#ifndef _REENTRANT +#define _REENTRANT 1 +#endif +#ifndef _TIMEDWAITS_RECURSIVE_MUTEXES +#define _TIMEDWAITS_RECURSIVE_MUTEXES 1 +#endif +#ifndef _UNIX +#define _UNIX 1 +#endif +#ifndef _UNIX_THREADED_NETWORK_IO +#define _UNIX_THREADED_NETWORK_IO 1 +#endif +#ifndef _UNIX_THREADS_SUPPORTED +#define _UNIX_THREADS_SUPPORTED 1 +#endif diff --git a/amarok/src/engine/helix/helix-sp/hspadvisesink.cpp b/amarok/src/engine/helix/helix-sp/hspadvisesink.cpp new file mode 100644 index 00000000..f81e9c01 --- /dev/null +++ b/amarok/src/engine/helix/helix-sp/hspadvisesink.cpp @@ -0,0 +1,668 @@ + +/* + * + * This software is released under the provisions of the GPL version 2. + * see file "COPYING". If that file is not available, the full statement + * of the license can be found at + * + * http://www.fsf.org/licensing/licenses/gpl.txt + * + * Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved. + * Portions (c) Paul Cifarelli 2005 + */ + +#include <stdio.h> + +#include "hxcomm.h" +#include "hxmon.h" +#include "hxcore.h" +#include "hxengin.h" +#include "hxclsnk.h" +#include "ihxpckts.h" + +#include "hxausvc.h" +#include "helix-sp.h" +#include "hspadvisesink.h" +#include "utils.h" + +#if defined(__cplusplus) +extern "C" { +#endif /* defined(__cplusplus) */ + +#if defined(__cplusplus) +} +#endif /* defined(__cplusplus) */ + +HSPClientAdviceSink::HSPClientAdviceSink(IUnknown* pUnknown, LONG32 lClientIndex, HelixSimplePlayer *pSplay) + : m_splayer(pSplay) + , m_lRefCount (0) + , m_lClientIndex (lClientIndex) + , m_pUnknown (NULL) + , m_pRegistry (NULL) + , m_pScheduler (NULL) + , m_position(0) + , m_duration(0) + , m_lCurrentBandwidth(0) + , m_lAverageBandwidth(0) + , m_bOnStop(0) +{ + //m_splayer->bEnableAdviceSink = true; + + if (pUnknown) + { + m_pUnknown = pUnknown; + m_pUnknown->AddRef(); + + if (HXR_OK != m_pUnknown->QueryInterface(IID_IHXRegistry, (void**)&m_pRegistry)) + { + m_pRegistry = NULL; + } + + if (HXR_OK != m_pUnknown->QueryInterface(IID_IHXScheduler, (void**)&m_pScheduler)) + { + m_pScheduler = NULL; + } + + IHXPlayer* pPlayer; + if(HXR_OK == m_pUnknown->QueryInterface(IID_IHXPlayer, + (void**)&pPlayer)) + { + pPlayer->AddAdviseSink(this); + pPlayer->Release(); + } + } +} + +HSPClientAdviceSink::~HSPClientAdviceSink(void) +{ + if (m_pScheduler) + { + m_pScheduler->Release(); + m_pScheduler = NULL; + } + + if (m_pRegistry) + { + m_pRegistry->Release(); + m_pRegistry = NULL; + } + + if (m_pUnknown) + { + m_pUnknown->Release(); + m_pUnknown = NULL; + } +} + + +// *** IUnknown methods *** + +///////////////////////////////////////////////////////////////////////// +// Method: +// IUnknown::QueryInterface +// Purpose: +// Implement this to export the interfaces supported by your +// object. +// +STDMETHODIMP HSPClientAdviceSink::QueryInterface(REFIID riid, void** ppvObj) +{ + if (IsEqualIID(riid, IID_IUnknown)) + { + AddRef(); + *ppvObj = (IUnknown*)(IHXClientAdviseSink*)this; + return HXR_OK; + } + else if (IsEqualIID(riid, IID_IHXClientAdviseSink)) + { + AddRef(); + *ppvObj = (IHXClientAdviseSink*)this; + return HXR_OK; + } + + *ppvObj = NULL; + return HXR_NOINTERFACE; +} + +///////////////////////////////////////////////////////////////////////// +// Method: +// IUnknown::AddRef +// Purpose: +// Everyone usually implements this the same... feel free to use +// this implementation. +// +STDMETHODIMP_(ULONG32) HSPClientAdviceSink::AddRef() +{ + return InterlockedIncrement(&m_lRefCount); +} + +///////////////////////////////////////////////////////////////////////// +// Method: +// IUnknown::Release +// Purpose: +// Everyone usually implements this the same... feel free to use +// this implementation. +// +STDMETHODIMP_(ULONG32) HSPClientAdviceSink::Release() +{ + if (InterlockedDecrement(&m_lRefCount) > 0) + { + return m_lRefCount; + } + + delete this; + return 0; +} + +/* + * IHXClientAdviseSink methods + */ + +/************************************************************************ + * Method: + * IHXClientAdviseSink::OnPosLength + * Purpose: + * Called to advise the client that the position or length of the + * current playback context has changed. + */ +STDMETHODIMP +HSPClientAdviceSink::OnPosLength(UINT32 ulPosition, + UINT32 ulLength) +{ + if (m_splayer->bEnableAdviceSink) + { + m_splayer->print2stdout("OnPosLength(%ld, %ld)\n", ulPosition, ulLength); + } + m_position = ulPosition; + m_duration = ulLength; + + return HXR_OK; +} + +/************************************************************************ + * Method: + * IHXClientAdviseSink::OnPresentationOpened + * Purpose: + * Called to advise the client a presentation has been opened. + */ +STDMETHODIMP HSPClientAdviceSink::OnPresentationOpened() +{ +/* + if (m_splayer && m_splayer->xf().crossfading && m_lClientIndex == m_splayer->xf().toIndex) + { + m_splayer->print2stderr("Crossfading...\n"); + m_splayer->xf().toStream = 0; + m_splayer->xf().toStream = m_splayer->getAudioPlayer(m_lClientIndex)->GetAudioStream(0); + if (m_splayer->xf().toStream) + { + m_splayer->print2stderr("Got Stream 2\n"); + m_splayer->startCrossFade(); + } + else + m_splayer->stop(m_lClientIndex); + } +*/ + //m_splayer->bEnableAdviceSink = true; + + if (m_splayer->bEnableAdviceSink) + { + m_splayer->print2stdout("OnPresentationOpened()\n"); + } + + return HXR_OK; +} + + +/************************************************************************ + * Method: + * IHXClientAdviseSink::OnPresentationClosed + * Purpose: + * Called to advise the client a presentation has been closed. + */ +STDMETHODIMP HSPClientAdviceSink::OnPresentationClosed() +{ + if (m_splayer->bEnableAdviceSink) + { + m_splayer->print2stdout("OnPresentationClosed()\n"); + } + + return HXR_OK; +} + +void HSPClientAdviceSink::GetStatistics (char* pszRegistryKey) +{ + char szRegistryValue[MAX_DISPLAY_NAME] = {0}; /* Flawfinder: ignore */ + INT32 lValue = 0; + INT32 i = 0; + INT32 lStatistics = 8; + UINT32 *plValue; + + // collect statistic + for (i = 0; i < lStatistics; i++) + { + plValue = NULL; + switch (i) + { + case 0: + SafeSprintf(szRegistryValue, MAX_DISPLAY_NAME, "%s.Normal", pszRegistryKey); + break; + case 1: + SafeSprintf(szRegistryValue, MAX_DISPLAY_NAME, "%s.Recovered", pszRegistryKey); + break; + case 2: + SafeSprintf(szRegistryValue, MAX_DISPLAY_NAME, "%s.Received", pszRegistryKey); + break; + case 3: + SafeSprintf(szRegistryValue, MAX_DISPLAY_NAME, "%s.Lost", pszRegistryKey); + break; + case 4: + SafeSprintf(szRegistryValue, MAX_DISPLAY_NAME, "%s.Late", pszRegistryKey); + break; + case 5: + SafeSprintf(szRegistryValue, MAX_DISPLAY_NAME, "%s.ClipBandwidth", pszRegistryKey); + break; + case 6: + SafeSprintf(szRegistryValue, MAX_DISPLAY_NAME, "%s.AverageBandwidth", pszRegistryKey); + plValue = &m_lAverageBandwidth; + break; + case 7: + SafeSprintf(szRegistryValue, MAX_DISPLAY_NAME, "%s.CurrentBandwidth", pszRegistryKey); + plValue = &m_lCurrentBandwidth; + break; + default: + break; + } + + m_pRegistry->GetIntByName(szRegistryValue, lValue); + if (plValue) + { + if (m_bOnStop || lValue == 0) + { + lValue = *plValue; + } + else + { + *plValue = lValue; + } + } + if (m_splayer->bEnableAdviceSink || (m_splayer->bEnableVerboseMode && m_bOnStop)) + { + m_splayer->print2stdout("%s = %ld\n", szRegistryValue, lValue); + } + } +} + +void HSPClientAdviceSink::GetAllStatistics(void) +{ + UINT32 unPlayerIndex = 0; + UINT32 unSourceIndex = 0; + UINT32 unStreamIndex = 0; + + const char* pszRegistryPrefix = "Statistics"; + char szRegistryName[MAX_DISPLAY_NAME] = {0}; /* Flawfinder: ignore */ + + // display the content of whole statistic registry + if (m_pRegistry) + { + // ok, let's start from the top (player) + SafeSprintf(szRegistryName, MAX_DISPLAY_NAME, "%s.Player%ld", pszRegistryPrefix, m_lClientIndex); + if (PT_COMPOSITE == m_pRegistry->GetTypeByName(szRegistryName)) + { + // display player statistic + GetStatistics(szRegistryName); + + SafeSprintf(szRegistryName, MAX_DISPLAY_NAME, "%s.Source%ld", szRegistryName, unSourceIndex); + while (PT_COMPOSITE == m_pRegistry->GetTypeByName(szRegistryName)) + { + // display source statistic + GetStatistics(szRegistryName); + + SafeSprintf(szRegistryName, MAX_DISPLAY_NAME, "%s.Stream%ld", szRegistryName, unStreamIndex); + while (PT_COMPOSITE == m_pRegistry->GetTypeByName(szRegistryName)) + { + // display stream statistic + GetStatistics(szRegistryName); + + unStreamIndex++; + + SafeSprintf(szRegistryName, MAX_DISPLAY_NAME, "%s.Player%ld.Source%ld.Stream%ld", + pszRegistryPrefix, unPlayerIndex, unSourceIndex, unStreamIndex); + } + + unSourceIndex++; + + SafeSprintf(szRegistryName, MAX_DISPLAY_NAME, "%s.Player%ld.Source%ld", + pszRegistryPrefix, unPlayerIndex, unSourceIndex); + } + + unPlayerIndex++; + + SafeSprintf(szRegistryName, MAX_DISPLAY_NAME, "%s.Player%ld", pszRegistryPrefix, unPlayerIndex); + } + } +} + +/************************************************************************ + * Method: + * IHXClientAdviseSink::OnStatisticsChanged + * Purpose: + * Called to advise the client that the presentation statistics + * have changed. + */ +STDMETHODIMP HSPClientAdviceSink::OnStatisticsChanged(void) +{ + char szBuff[1024]; /* Flawfinder: ignore */ + HX_RESULT res = HXR_OK; + UINT16 uPlayer = 0; + + //if(m_splayer->bEnableAdviceSink) + { + if(m_splayer->bEnableAdviceSink) + m_splayer->print2stdout("OnStatisticsChanged():\n"); + + SafeSprintf(szBuff, 1024, "Statistics.Player%u", uPlayer ); + while( HXR_OK == res ) + { + res = DumpRegTree( szBuff, uPlayer ); + if ( HXR_OK == res ) + { + //m_splayer->print2stderr("%d Title: %s\n", uPlayer, m_splayer->ppctrl[uPlayer]->md.title); + //m_splayer->print2stderr("%d Author: %s\n", uPlayer, m_splayer->ppctrl[uPlayer]->md.artist); + //m_splayer->print2stderr("%d Bitrate: %ld\n", uPlayer, m_splayer->ppctrl[uPlayer]->md.bitrate); + } + uPlayer++; + SafeSprintf(szBuff, 1024, "Statistics.Player%u", uPlayer ); + } + } + + return HXR_OK; +} + +HX_RESULT HSPClientAdviceSink::DumpRegTree(const char* pszTreeName, UINT16 index ) +{ + const char* pszName = NULL; + ULONG32 ulRegID = 0; + HX_RESULT res = HXR_OK; + INT32 nVal = 0; + IHXBuffer* pBuff = NULL; + IHXValues* pValues = NULL; + + int len; + bool title = false; + bool bw = false; + bool author = false; + + //See if the name exists in the reg tree. + res = m_pRegistry->GetPropListByName( pszTreeName, pValues); + if( HXR_OK!=res || !pValues ) + return HXR_FAIL; + + //make sure this is a PT_COMPOSITE type reg entry. + if( PT_COMPOSITE != m_pRegistry->GetTypeByName(pszTreeName)) + return HXR_FAIL; + + //Print out the value of each member of this tree. + res = pValues->GetFirstPropertyULONG32( pszName, ulRegID ); + while( HXR_OK == res ) + { + title = false; + bw = false; + author = false; + + len = strlen(pszName); + len -= strlen("Title"); + if (len > 0 && !strcmp(&pszName[len], "Title")) + title = true; + len += strlen("Title"); + len -= strlen("Author"); + if (len > 0 && !strcmp(&pszName[len], "Author")) + author = true; + len += strlen("Author"); + len -= strlen("AverageBandwidth"); + if (len > 0 && !strcmp(&pszName[len], "AverageBandwidth")) + bw = true; + + //We have at least one entry. See what type it is. + HXPropType pt = m_pRegistry->GetTypeById(ulRegID); + switch(pt) + { + case PT_COMPOSITE: + DumpRegTree(pszName, index); + break; + case PT_INTEGER : + nVal = 0; + m_pRegistry->GetIntById( ulRegID, nVal ); + if(m_splayer->bEnableAdviceSink) + m_splayer->print2stdout("%s : %ld\n", pszName, nVal ); + if (bw) + m_splayer->ppctrl[index]->md.bitrate = nVal; + break; + case PT_INTREF : + nVal = 0; + m_pRegistry->GetIntById( ulRegID, nVal ); + if(m_splayer->bEnableAdviceSink) + m_splayer->print2stdout("%s : %ld\n", pszName, nVal ); + if (bw) + m_splayer->ppctrl[index]->md.bitrate = nVal; + break; + case PT_STRING : + pBuff = NULL; + m_pRegistry->GetStrById( ulRegID, pBuff ); + if(m_splayer->bEnableAdviceSink) + m_splayer->print2stdout("%s : \"", pszName ); + if( pBuff ) + if(m_splayer->bEnableAdviceSink) + m_splayer->print2stdout("%s", (const char *)(pBuff->GetBuffer()) ); + if(m_splayer->bEnableAdviceSink) + m_splayer->print2stdout("\"\n" ); + + if (title && pBuff) + { + strncpy(m_splayer->ppctrl[index]->md.title, (const char *) (pBuff->GetBuffer()), 512); + m_splayer->ppctrl[index]->md.title[511] = '\0'; + } + if (author && pBuff) + { + strncpy(m_splayer->ppctrl[index]->md.artist, (const char *) (pBuff->GetBuffer()), 512); + m_splayer->ppctrl[index]->md.artist[511] = '\0'; + } + HX_RELEASE(pBuff); + break; + case PT_BUFFER : + if(m_splayer->bEnableAdviceSink) + m_splayer->print2stdout("%s %ld : BUFFER TYPE NOT SHOWN\n", + pszName, nVal ); + break; + case PT_UNKNOWN: + if(m_splayer->bEnableAdviceSink) + m_splayer->print2stdout("%s Unkown registry type entry\n", pszName ); + break; + default: + if(m_splayer->bEnableAdviceSink) + m_splayer->print2stdout("%s Unkown registry type entry\n", pszName ); + break; + } + res = pValues->GetNextPropertyULONG32( pszName, ulRegID); + } + + HX_RELEASE( pValues ); + + return HXR_OK; +} + + +/************************************************************************ + * Method: + * IHXClientAdviseSink::OnPreSeek + * Purpose: + * Called by client engine to inform the client that a seek is + * about to occur. The render is informed the last time for the + * stream's time line before the seek, as well as the first new + * time for the stream's time line after the seek will be completed. + * + */ +STDMETHODIMP HSPClientAdviceSink::OnPreSeek( ULONG32 ulOldTime, + ULONG32 ulNewTime) +{ +#if !defined(__TCS__) + if (m_splayer->bEnableAdviceSink) + { + m_splayer->print2stdout("OnPreSeek(%ld, %ld)\n", ulOldTime, ulNewTime); + } +#endif + + return HXR_OK; +} + + +/************************************************************************ + * Method: + * IHXClientAdviseSink::OnPostSeek + * Purpose: + * Called by client engine to inform the client that a seek has + * just occurred. The render is informed the last time for the + * stream's time line before the seek, as well as the first new + * time for the stream's time line after the seek. + * + */ +STDMETHODIMP HSPClientAdviceSink::OnPostSeek( ULONG32 ulOldTime, + ULONG32 ulNewTime) +{ + if (m_splayer->bEnableAdviceSink) + { + m_splayer->print2stdout("OnPostSeek(%ld, %ld)\n", ulOldTime, ulNewTime); + } + + return HXR_OK; +} + + +/************************************************************************ + * Method: + * IHXClientAdviseSink::OnStop + * Purpose: + * Called by client engine to inform the client that a stop has + * just occurred. + * + */ +STDMETHODIMP HSPClientAdviceSink::OnStop(void) +{ + HXTimeval now; + + if (m_splayer->bEnableAdviceSink) + { + m_splayer->print2stdout("OnStop()\n"); + } + + if (m_splayer->bEnableVerboseMode) + { + m_splayer->print2stdout("Player %ld stopped.\n", m_lClientIndex); + m_bOnStop = true; + GetAllStatistics(); + } + + // Find out the current time and subtract the beginning time to + // figure out how many seconds we played + now = m_pScheduler->GetCurrentSchedulerTime(); + m_ulStopTime = now.tv_sec; + + m_splayer->m_ulNumSecondsPlayed = m_ulStopTime - m_ulStartTime; + + m_position = m_duration = 0; + return HXR_OK; +} + +/************************************************************************ + * Method: + * IHXClientAdviseSink::OnPause + * Purpose: + * Called by client engine to inform the client that a pause has + * just occurred. The render is informed the last time for the + * stream's time line before the pause. + * + */ +STDMETHODIMP HSPClientAdviceSink::OnPause(ULONG32 ulTime) +{ + if (m_splayer->bEnableAdviceSink) + { + m_splayer->print2stdout("OnPause(%ld)\n", ulTime); + } + + return HXR_OK; +} + + +/************************************************************************ + * Method: + * IHXClientAdviseSink::OnBegin + * Purpose: + * Called by client engine to inform the client that a begin or + * resume has just occurred. The render is informed the first time + * for the stream's time line after the resume. + * + */ +STDMETHODIMP HSPClientAdviceSink::OnBegin(ULONG32 ulTime) +{ + HXTimeval now; + +#if !defined(__TCS__) + if (m_splayer->bEnableAdviceSink) + { + m_splayer->print2stdout("OnBegin(%ld)\n", ulTime); + } + + if (m_splayer->bEnableVerboseMode) + { + m_splayer->print2stdout("Player %ld beginning playback...\n", m_lClientIndex); + } +#endif + + // Record the current time, so we can figure out many seconds we played + now = m_pScheduler->GetCurrentSchedulerTime(); + m_ulStartTime = now.tv_sec; + + return HXR_OK; +} + + +/************************************************************************ + * Method: + * IHXClientAdviseSink::OnBuffering + * Purpose: + * Called by client engine to inform the client that buffering + * of data is occurring. The render is informed of the reason for + * the buffering (start-up of stream, seek has occurred, network + * congestion, etc.), as well as percentage complete of the + * buffering process. + * + */ +STDMETHODIMP HSPClientAdviceSink::OnBuffering(ULONG32 ulFlags, + UINT16 unPercentComplete) +{ + if (m_splayer->bEnableAdviceSink) + { + m_splayer->print2stdout("OnBuffering(%ld, %d)\n", ulFlags, unPercentComplete); + } + m_splayer->onBuffering(unPercentComplete); + + return HXR_OK; +} + + +/************************************************************************ + * Method: + * IHXClientAdviseSink::OnContacting + * Purpose: + * Called by client engine to inform the client is contacting + * hosts(s). + * + */ +STDMETHODIMP HSPClientAdviceSink::OnContacting(const char* pHostName) +{ + if (m_splayer->bEnableAdviceSink) + { + m_splayer->print2stdout("OnContacting(\"%s\")\n", pHostName); + } + m_splayer->onContacting(pHostName); + return HXR_OK; +} + diff --git a/amarok/src/engine/helix/helix-sp/hspadvisesink.h b/amarok/src/engine/helix/helix-sp/hspadvisesink.h new file mode 100644 index 00000000..6816e75a --- /dev/null +++ b/amarok/src/engine/helix/helix-sp/hspadvisesink.h @@ -0,0 +1,203 @@ +/* + * + * This software is released under the provisions of the GPL version 2. + * see file "COPYING". If that file is not available, the full statement + * of the license can be found at + * + * http://www.fsf.org/licensing/licenses/gpl.txt + * + * Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved. + * Portions (c) Paul Cifarelli 2005 + * + */ + +#ifndef _HSPADVISESINK_ +#define _HSPADVISESINK_ + +struct IHXClientAdviseSink; +struct IUnknown; +struct IHXRegistry; +struct IHXScheduler; +class HelixSimplePlayer; + +class HSPClientAdviceSink : public IHXClientAdviseSink +{ + private: + HelixSimplePlayer *m_splayer; + LONG32 m_lRefCount; + LONG32 m_lClientIndex; + + IUnknown* m_pUnknown; + IHXRegistry* m_pRegistry; + IHXScheduler* m_pScheduler; + + UINT32 m_ulStartTime; + UINT32 m_ulStopTime; + + UINT32 m_position; + UINT32 m_duration; + + UINT32 m_lCurrentBandwidth; + UINT32 m_lAverageBandwidth; + BOOL m_bOnStop; + + HX_RESULT DumpRegTree(const char* pszTreeName, UINT16 index ); + + //PRIVATE_DESTRUCTORS_ARE_NOT_A_CRIME + + void GetStatistics (char* pszRegistryKey); + void GetAllStatistics (void); + + public: + + HSPClientAdviceSink(IUnknown* pUnknown, LONG32 lClientIndex, HelixSimplePlayer *pSplay); + virtual ~HSPClientAdviceSink(); + + UINT32 position() { return m_position; } + UINT32 duration() { return m_duration; } + UINT32 currentBW() { return m_lCurrentBandwidth; } + UINT32 averageBW() { return m_lAverageBandwidth; } + + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj); + + STDMETHOD_(ULONG32,AddRef) (THIS); + + STDMETHOD_(ULONG32,Release) (THIS); + + /* + * IHXClientAdviseSink methods + */ + + /************************************************************************ + * Method: + * IHXClientAdviseSink::OnPosLength + * Purpose: + * Called to advise the client that the position or length of the + * current playback context has changed. + */ + STDMETHOD(OnPosLength) (THIS_ + UINT32 ulPosition, + UINT32 ulLength); + + /************************************************************************ + * Method: + * IHXClientAdviseSink::OnPresentationOpened + * Purpose: + * Called to advise the client a presentation has been opened. + */ + STDMETHOD(OnPresentationOpened) (THIS); + + /************************************************************************ + * Method: + * IHXClientAdviseSink::OnPresentationClosed + * Purpose: + * Called to advise the client a presentation has been closed. + */ + STDMETHOD(OnPresentationClosed) (THIS); + + /************************************************************************ + * Method: + * IHXClientAdviseSink::OnStatisticsChanged + * Purpose: + * Called to advise the client that the presentation statistics + * have changed. + */ + STDMETHOD(OnStatisticsChanged) (THIS); + + /************************************************************************ + * Method: + * IHXClientAdviseSink::OnPreSeek + * Purpose: + * Called by client engine to inform the client that a seek is + * about to occur. The render is informed the last time for the + * stream's time line before the seek, as well as the first new + * time for the stream's time line after the seek will be completed. + * + */ + STDMETHOD (OnPreSeek) (THIS_ + ULONG32 ulOldTime, + ULONG32 ulNewTime); + + /************************************************************************ + * Method: + * IHXClientAdviseSink::OnPostSeek + * Purpose: + * Called by client engine to inform the client that a seek has + * just occurred. The render is informed the last time for the + * stream's time line before the seek, as well as the first new + * time for the stream's time line after the seek. + * + */ + STDMETHOD (OnPostSeek) (THIS_ + ULONG32 ulOldTime, + ULONG32 ulNewTime); + + /************************************************************************ + * Method: + * IHXClientAdviseSink::OnStop + * Purpose: + * Called by client engine to inform the client that a stop has + * just occurred. + * + */ + STDMETHOD (OnStop) (THIS); + + /************************************************************************ + * Method: + * IHXClientAdviseSink::OnPause + * Purpose: + * Called by client engine to inform the client that a pause has + * just occurred. The render is informed the last time for the + * stream's time line before the pause. + * + */ + STDMETHOD (OnPause) (THIS_ + ULONG32 ulTime); + + /************************************************************************ + * Method: + * IHXClientAdviseSink::OnBegin + * Purpose: + * Called by client engine to inform the client that a begin or + * resume has just occurred. The render is informed the first time + * for the stream's time line after the resume. + * + */ + STDMETHOD (OnBegin) (THIS_ + ULONG32 ulTime); + + /************************************************************************ + * Method: + * IHXClientAdviseSink::OnBuffering + * Purpose: + * Called by client engine to inform the client that buffering + * of data is occurring. The render is informed of the reason for + * the buffering (start-up of stream, seek has occurred, network + * congestion, etc.), as well as percentage complete of the + * buffering process. + * + */ + STDMETHOD (OnBuffering) (THIS_ + ULONG32 ulFlags, + UINT16 unPercentComplete); + + + /************************************************************************ + * Method: + * IHXClientAdviseSink::OnContacting + * Purpose: + * Called by client engine to inform the client is contacting + * hosts(s). + * + */ + STDMETHOD (OnContacting) (THIS_ + const char* pHostName); + +}; + +#endif /* _EXAMPLECLSNK_ */ diff --git a/amarok/src/engine/helix/helix-sp/hspalsadevice.cpp b/amarok/src/engine/helix/helix-sp/hspalsadevice.cpp new file mode 100644 index 00000000..38eadd79 --- /dev/null +++ b/amarok/src/engine/helix/helix-sp/hspalsadevice.cpp @@ -0,0 +1,2204 @@ +/****************************************************************************** + * * + * 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 library is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin St, 5th fl, Boston, MA 02110-1301, * + * USA, or check http://www.fsf.org/about/contact.html * + * * + * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved. * + * Portions Copyright (c) 2005 Paul Cifarelli * + * * + ******************************************************************************/ + +#include <unistd.h> +#include <fcntl.h> +#include <stdlib.h> +#include <errno.h> +#include <sys/ioctl.h> + +#include <stdio.h> +#include <math.h> +#include <time.h> +#include <sys/time.h> +#include <unistd.h> + +#include <config.h> + +#include "hxcomm.h" +#include "hxcore.h" +#include "hxprefs.h" +#include "hxstrutl.h" +#include "hxvsrc.h" +#include "hxresult.h" +#include "hxausvc.h" +#include "helix-sp.h" + +#include "ihxpckts.h" +#include "hxprefs.h" +#include "hspalsadevice.h" + +#ifdef HX_LOG_SUBSYSTEM +#include "hxtlogutil.h" +#include "ihxtlogsystem.h" +#endif + +#include "dllpath.h" + +#include "hxbuffer.h" + +#ifdef USE_HELIX_ALSA + +IHXPreferences* z_pIHXPrefs = 0; +#define RA_AOE_NOERR 0 +#define RA_AOE_GENERAL -1 +#define RA_AOE_DEVNOTOPEN -2 +#define RA_AOE_NOTENABLED -3 +#define RA_AOE_BADFORMAT -4 +#define RA_AOE_NOTSUPPORTED -5 +#define RA_AOE_DEVBUSY -6 +#define RA_AOE_BADOPEN -7 + +#ifdef __FreeBSD__ +#define PTHREAD_MUTEX_FAST_NP PTHREAD_MUTEX_NORMAL +#endif + +#if !defined(__NetBSD__) && !defined(__OpenBSD__) + #include <sys/soundcard.h> +#else + #include <soundcard.h> +#endif + +typedef HX_RESULT (HXEXPORT_PTR FPRMSETDLLACCESSPATH) (const char*); + + +AudioQueue::AudioQueue( const HXAudioData *buf) : fwd(0) +{ + ad = *buf; + ad.pData->AddRef(); +} + +AudioQueue::~AudioQueue() +{ + ad.pData->Release(); +} + + +STDMETHODIMP +HSPAudioDevice::QueryInterface(REFIID riid, void**ppvObj) +{ + if(IsEqualIID(riid, IID_IUnknown)) + { + AddRef(); + *ppvObj = (IUnknown*)(IHXAudioDevice *)this; + return HXR_OK; + } + else if(IsEqualIID(riid, IID_IHXAudioDevice)) + { + AddRef(); + *ppvObj = (IHXAudioDevice *)this; + return HXR_OK; + } + *ppvObj = NULL; + return HXR_NOINTERFACE; +} + +STDMETHODIMP_(UINT32) +HSPAudioDevice::AddRef() +{ + return InterlockedIncrement(&m_lRefCount); +} + +STDMETHODIMP_(UINT32) +HSPAudioDevice::Release() +{ + if (InterlockedDecrement(&m_lRefCount) > 0) + { + return m_lRefCount; + } + + delete this; + return 0; +} + +STDMETHODIMP +HSPAudioDevice::CheckFormat( const HXAudioFormat* pAudioFormat ) +{ + m_Player->print2stderr("########## Got to HSPAudioDevice::CheckFormat\n"); + + return (_CheckFormat(pAudioFormat)); +} + +STDMETHODIMP +HSPAudioDevice::Close( const BOOL bFlush ) +{ + m_Player->print2stderr("########## Got to HSPAudioDevice::Close flush %d\n", bFlush); + + pthread_mutex_lock(&m_m); + + if (bFlush) + { + clearQueue(); + _Drain(); + } + + _Reset(); + _CloseAudio(); + _CloseMixer(); + + m_closed = true; + + m_ulCurrentTime = m_ulQTime = 0; + + if (m_pStreamResponse) + m_pStreamResponse->Release(); + + + pthread_mutex_unlock(&m_m); + + return 0; +} + +STDMETHODIMP +HSPAudioDevice::Drain() +{ + m_Player->print2stderr("########## Got to HSPAudioDevice::Drain\n"); + pthread_mutex_lock(&m_m); + + LONG32 err = _Drain(); + clearQueue(); + pthread_mutex_unlock(&m_m); + return err; +} + +STDMETHODIMP +HSPAudioDevice::GetCurrentAudioTime( REF(ULONG32) ulCurrentTime ) +{ + //m_Player->print2stderr("########## Got to HSPAudioDevice::GetCurrentTime = %d\n", m_ulCurrentTime); + + int err = 0; + snd_pcm_sframes_t frame_delay = 0; + + pthread_mutex_lock(&m_m); + if (!m_closed) + { + err = snd_pcm_delay (m_pAlsaPCMHandle, &frame_delay); + if (err < 0) + { +#ifdef HX_LOG_SUBSYSTEM + HXLOGL1 ( HXLOG_ADEV, "snd_pcm_status: %s", snd_strerror(err)); +#endif + m_Player->print2stderr("########## HSPAudioDevice::GetCurrentAudioTime error getting frame_delay: %s\n", snd_strerror(err)); + pthread_mutex_unlock(&m_m); + return -1; + } + + ulCurrentTime = m_ulCurrentTime - (ULONG32)(((double)frame_delay * 1000.0) / (double)m_unSampleRate); + + //m_Player->print2stderr("########## HSPAudioDevice::GetCurrentAudioTime %d %d\n", ulCurrentTime, m_ulCurrentTime); + } + pthread_mutex_unlock(&m_m); + + return 0; +} + +STDMETHODIMP_(UINT16) +HSPAudioDevice::GetVolume() +{ + m_Player->print2stderr("########## Got to HSPAudioDevice::GetVolume\n"); + return 0; +} + +STDMETHODIMP_(BOOL) + HSPAudioDevice::InitVolume(const UINT16 /*uMinVolume*/, const UINT16 /*uMaxVolume*/) +{ + m_Player->print2stderr("########## Got to HSPAudioDevice::InitVolume\n"); + return true; +} + +STDMETHODIMP +HSPAudioDevice::Open(const HXAudioFormat* pAudioFormat, IHXAudioDeviceResponse* pStreamResponse) +{ + int err; + + m_Player->print2stderr("########## Got to HSPAudioDevice::Open\n"); + if (pStreamResponse) + pStreamResponse->AddRef(); + + pthread_mutex_lock(&m_m); + + m_drain = false; + m_closed = false; + m_ulTotalWritten = 0; + m_ulCurrentTime = 0; + m_SWPause = false; + m_pStreamResponse = pStreamResponse; + if (!m_pAlsaPCMHandle) + { + err = _OpenAudio(); + if (err) m_Player->print2stderr("########## HSPAudioDevice::Open error (device) %d\n", err); + err = SetDeviceConfig(pAudioFormat); + if (err) m_Player->print2stderr("########## HSPAudioDevice::Open error (config) %d\n", err); + m_ulCurrentTime = m_ulLastTime = m_ulQTime = 0; + } + + if (m_pAlsaMixerHandle != NULL) + { + err = _OpenMixer(); + if (err) m_Player->print2stderr("########## HSPAudioDevice::Open error (mixer) %d\n", err); + } + + pthread_mutex_unlock(&m_m); + + return 0; +} + +STDMETHODIMP +HSPAudioDevice::Pause() +{ + m_Player->print2stderr("########## Got to HSPAudioDevice::Pause %d\n", m_bHasHardwarePauseAndResume); + _Pause(); + return 0; +} + +STDMETHODIMP +HSPAudioDevice::Reset() +{ + m_Player->print2stderr("########## Got to HSPAudioDevice::Reset\n"); + return (_Reset()); +} + +STDMETHODIMP +HSPAudioDevice::Resume() +{ + m_Player->print2stderr("########## Got to HSPAudioDevice::Resume\n"); + _Resume(); + + return 0; +} + +STDMETHODIMP +HSPAudioDevice::SetVolume( const UINT16 /*uVolume*/ ) +{ + m_Player->print2stderr("########## Got to HSPAudioDevice::SetVolume\n"); + return 0; +} + +STDMETHODIMP +HSPAudioDevice::Write( const HXAudioData* pAudioData ) +{ + addBuf( new AudioQueue( pAudioData ) ); + return 0; +} + +int HSPAudioDevice::sync() +{ + if (m_pStreamResponse) + { + ULONG32 curtime; + if (!GetCurrentAudioTime(curtime) && curtime) + return m_pStreamResponse->OnTimeSync(curtime); + else + { + // probably a seek occurred + //clearQueue(); + _Reset(); + } + } + return -1; +} + + +HX_RESULT HSPAudioDevice::OnTimeSync() +{ + HX_RESULT err; + + if (!(err = sync())) + return HXR_OK; + + return err; +} + +int +HSPAudioDevice::_Write( const HXAudioData* pAudioData ) +{ + unsigned long len; + long bytes; + unsigned char *data; + int err = 0; + + pAudioData->pData->Get(data, len); + + // if the time of this buf is earlier than the last, or the time between this buf and the last is > 1 buffer's worth, this was a seek + if ( pAudioData->ulAudioTime < m_ulCurrentTime || + pAudioData->ulAudioTime - m_ulCurrentTime > (1000 * len) / (m_unNumChannels * m_unSampleRate) + 1 ) + { + m_Player->print2stderr("########## seek detected %ld %ld, len = %ld %d\n", m_ulCurrentTime, pAudioData->ulAudioTime, len, + abs(pAudioData->ulAudioTime - (m_ulCurrentTime + (1000 * len) / (m_unNumChannels * m_unSampleRate)))); + //_Reset(); + //clearQueue(); + } + + if (!err) + { + err = WriteBytes(data, len, bytes); + m_ulCurrentTime = pAudioData->ulAudioTime; + } + err = sync(); + + //m_Player->print2stderr("########## %d %d\n", m_ulCurrentTime,pAudioData->ulAudioTime); + + //m_Player->print2stderr("########## Got to HSPAudioDevice::Write len=%d byteswriten=%d err=%d time=%d\n", + // len,bytes,err,m_ulCurrentTime); + + return err; +} + + +//------------------------------------------ +// Ctors and Dtors. +//------------------------------------------ +HSPAudioDevice::HSPAudioDevice(HelixSimplePlayer *player, const char *device) : + m_pAlsaPCMHandle (NULL), + m_pAlsaMixerHandle (NULL), + m_pAlsaMixerElem (NULL), + + m_pPCMDeviceName (NULL), + m_pMixerDeviceName (NULL), + m_pMixerElementName (NULL), + + m_bHasHardwarePauseAndResume (FALSE), + m_nBytesPlayedBeforeLastTrigger(0), + + m_nLastBytesPlayed(0), + + m_bGotInitialTrigger(FALSE), + m_bUseMMAPTStamps(TRUE), + m_lRefCount(0), + m_wLastError(0), + m_SWPause(false), + m_Player(player), + m_done(false), + m_drain(false), + m_closed(true), + m_head(0), + m_tail(0) +{ + pthread_mutexattr_t ma; + + pthread_mutexattr_init(&ma); + pthread_mutexattr_settype(&ma, PTHREAD_MUTEX_FAST_NP); // note this is not portable outside linux and a few others + pthread_mutex_init(&m_m, &ma); + + pthread_cond_init(&m_cv, NULL); + + // create thread that will wait for buffers to appear to send to the device + pthread_create(&m_thrid, 0, writerThread, this); + + if (device) + { + int len = strlen( device ); + m_Player->pCommonClassFactory->CreateInstance(CLSID_IHXBuffer, (void **) &m_pPCMDeviceName); + if (m_pPCMDeviceName) + m_pPCMDeviceName->Set( (const unsigned char*) device, len + 1 ); + } +} + +HSPAudioDevice::~HSPAudioDevice() +{ + pthread_mutex_lock(&m_m); + m_done = true; + pthread_mutex_unlock(&m_m); + pthread_cond_signal(&m_cv); + void *tmp; + pthread_join(m_thrid, &tmp); + + + if(m_pPCMDeviceName) + { + HX_RELEASE(m_pPCMDeviceName); + } + + if(m_pMixerDeviceName) + { + HX_RELEASE(m_pMixerDeviceName); + } + + if(m_pMixerElementName) + { + HX_RELEASE(m_pMixerElementName); + } + + pthread_cond_destroy(&m_cv); + pthread_mutex_destroy(&m_m); +} + +void HSPAudioDevice::addBuf(struct AudioQueue *item) +{ + pthread_mutex_lock(&m_m); + + m_ulQTime = item->ad.ulAudioTime; + if (m_tail) + { + item->fwd = 0; + m_tail->fwd = item; + m_tail = item; + } + else + { + item->fwd = 0; + m_head = item; + m_tail = item; + } + + pthread_mutex_unlock(&m_m); + pthread_cond_signal(&m_cv); +} + +AudioQueue *HSPAudioDevice::getBuf() +{ + pthread_mutex_lock(&m_m); + + AudioQueue *item = m_head; + + if (item) + { + m_head = item->fwd; + if (!m_head) + m_tail = 0; + } + + pthread_mutex_unlock(&m_m); + + return item; +} + +// NOTE THAT THIS IS NOT UNDER LOCK, AND SHOULD ONLY BE CALLED WITH THE MUTEX LOCKED +void HSPAudioDevice::clearQueue() +{ + AudioQueue *item; + + if (!m_tail) + return; + + while (m_tail) + { + item = m_head; + m_head = item->fwd; + if (!m_head) + m_tail = 0; + delete item; + } +} + + +void *HSPAudioDevice::writerThread( void *arg ) +{ + HSPAudioDevice *thisObj = (HSPAudioDevice *) arg; + AudioQueue *item; + + pthread_mutex_lock(&thisObj->m_m); + while (!thisObj->m_done) + { + pthread_mutex_unlock(&thisObj->m_m); + item = thisObj->getBuf(); + + if (item) + thisObj->_Write(&item->ad); + + delete item; + + pthread_mutex_lock(&thisObj->m_m); + if (!thisObj->m_tail) + pthread_cond_wait(&thisObj->m_cv, &thisObj->m_m); + } + pthread_mutex_unlock(&thisObj->m_m); + + thisObj->m_Player->print2stderr("############ writerThread exit\n"); + return 0; +} + + +// These Device Specific methods must be implemented +// by the platform specific sub-classes. +INT16 HSPAudioDevice::GetAudioFd(void) +{ + //Not implemented. + return -1; +} + + +//Device specific methods to open/close the mixer and audio devices. +HX_RESULT HSPAudioDevice::_OpenAudio() +{ + int err = 0; + const char* szDevice; + + HX_ASSERT (m_pAlsaPCMHandle == NULL); + if (m_pAlsaPCMHandle) + { + m_wLastError = RA_AOE_BADOPEN; + return m_wLastError; + } + + if(z_pIHXPrefs) + { + HX_RELEASE(m_pPCMDeviceName); + z_pIHXPrefs->ReadPref("AlsaPCMDeviceName", m_pPCMDeviceName); + } + + if(!m_pPCMDeviceName) + { + const char szDefaultDevice[] = "default"; + + m_Player->pCommonClassFactory->CreateInstance(CLSID_IHXBuffer, (void **) &m_pPCMDeviceName); + if (m_pPCMDeviceName) + m_pPCMDeviceName->Set( (const unsigned char*) szDefaultDevice, sizeof(szDefaultDevice) ); + } + + szDevice = (const char*) m_pPCMDeviceName->GetBuffer(); + m_Player->print2stderr("########### Opening ALSA PCM device %s\n", szDevice); + +#ifdef HX_LOG_SUBSYSTEM + HXLOGL2 (HXLOG_ADEV, "Opening ALSA PCM device %s", + szDevice); +#endif + + err = snd_pcm_open( &m_pAlsaPCMHandle, + szDevice, + SND_PCM_STREAM_PLAYBACK, + 0); + if(err < 0) + { + m_Player->print2stderr("########### snd_pcm_open: %s %s\n", szDevice, snd_strerror (err)); +#ifdef HX_LOG_SUBSYSTEM + HXLOGL1 ( HXLOG_ADEV, "snd_pcm_open: %s", + szDevice, snd_strerror (err)); +#endif + + m_wLastError = RA_AOE_BADOPEN; + } + + if(err == 0) + { + err = snd_pcm_nonblock(m_pAlsaPCMHandle, TRUE); + if(err < 0) + { + m_Player->print2stderr("########## snd_pcm_nonblock: %s\n", snd_strerror (err)); +#ifdef HX_LOG_SUBSYSTEM + HXLOGL1 ( HXLOG_ADEV, "snd_pcm_nonblock: %s", + snd_strerror (err)); +#endif + m_wLastError = RA_AOE_BADOPEN; + } + } + + if(err == 0) + { + m_Player->print2stderr("########## return from OpenAudio\n"); + m_wLastError = RA_AOE_NOERR; + } + else + { + if(m_pAlsaPCMHandle) + { + snd_pcm_close(m_pAlsaPCMHandle); + m_pAlsaPCMHandle = NULL; + } + } + + return m_wLastError; +} + + +HX_RESULT HSPAudioDevice::_CloseAudio() +{ + if (!m_pAlsaPCMHandle) + { + m_wLastError = RA_AOE_DEVNOTOPEN; + return m_wLastError; + } + +#ifdef HX_LOG_SUBSYSTEM + HXLOGL2 (HXLOG_ADEV, "Closing ALSA PCM device"); +#endif + + snd_pcm_close(m_pAlsaPCMHandle); + m_pAlsaPCMHandle = NULL; + m_wLastError = RA_AOE_NOERR; + + return m_wLastError; +} + + +HX_RESULT HSPAudioDevice::_OpenMixer() +{ + int err; + const char* szDeviceName = NULL; + const char* szElementName = NULL; + int nElementIndex = 0; + + HX_ASSERT (m_pAlsaMixerHandle == NULL); + if (m_pAlsaMixerHandle != NULL) + { + m_wLastError = RA_AOE_BADOPEN; + return m_wLastError; + } + + HX_ASSERT(m_pAlsaMixerElem == NULL); + if (m_pAlsaMixerElem != NULL) + { + m_wLastError = RA_AOE_BADOPEN; + return m_wLastError; + } + + if(z_pIHXPrefs) + { + HX_RELEASE(m_pMixerDeviceName); + z_pIHXPrefs->ReadPref("AlsaMixerDeviceName", m_pMixerDeviceName); + } + + if(!m_pMixerDeviceName) + { + const char szDefaultDevice[] = "default"; + + m_Player->pCommonClassFactory->CreateInstance(CLSID_IHXBuffer, (void **) &m_pMixerDeviceName); + if (m_pMixerDeviceName) + m_pMixerDeviceName->Set( (const unsigned char*) szDefaultDevice, sizeof(szDefaultDevice) ); + } + + if(z_pIHXPrefs) + { + HX_RELEASE(m_pMixerElementName); + z_pIHXPrefs->ReadPref("AlsaMixerElementName", m_pMixerElementName); + } + + if(!m_pMixerElementName) + { + const char szDefaultElement[] = "PCM"; + + m_Player->pCommonClassFactory->CreateInstance(CLSID_IHXBuffer, (void **) &m_pMixerElementName); + if (m_pMixerElementName) + m_pMixerElementName->Set( (const unsigned char*) szDefaultElement, sizeof(szDefaultElement) ); + } + + if(z_pIHXPrefs) + { + IHXBuffer* pElementIndex = NULL; + z_pIHXPrefs->ReadPref("AlsaMixerElementIndex", pElementIndex); + if(pElementIndex) + { + const char* szElementIndex = (const char*) pElementIndex->GetBuffer(); + nElementIndex = atoi(szElementIndex); + + HX_RELEASE(pElementIndex); + } + } + + szDeviceName = (const char*) m_pMixerDeviceName->GetBuffer();; + szElementName = (const char*) m_pMixerElementName->GetBuffer(); + +#ifdef HX_LOG_SUBSYSTEM + HXLOGL2 (HXLOG_ADEV, "Opening ALSA mixer device %s", + szDeviceName); +#endif + + err = snd_mixer_open(&m_pAlsaMixerHandle, 0); + if (err < 0) + { +#ifdef HX_LOG_SUBSYSTEM + HXLOGL1 ( HXLOG_ADEV, "snd_mixer_open: %s", + snd_strerror (err)); +#endif + + m_wLastError = RA_AOE_BADOPEN; + } + + if (err == 0) + { + err = snd_mixer_attach(m_pAlsaMixerHandle, szDeviceName); + if (err < 0) + { +#ifdef HX_LOG_SUBSYSTEM + HXLOGL1 ( HXLOG_ADEV, "snd_mixer_attach: %s", + snd_strerror (err)); +#endif + + m_wLastError = RA_AOE_BADOPEN; + } + } + + if (err == 0) + { + err = snd_mixer_selem_register(m_pAlsaMixerHandle, NULL, NULL); + if (err < 0) + { +#ifdef HX_LOG_SUBSYSTEM + HXLOGL1 ( HXLOG_ADEV, "snd_mixer_selem_register: %s", + snd_strerror (err)); +#endif + m_wLastError = RA_AOE_BADOPEN; + } + } + + if (err == 0) + { + err = snd_mixer_load(m_pAlsaMixerHandle); + if(err < 0 ) + { +#ifdef HX_LOG_SUBSYSTEM + HXLOGL1 ( HXLOG_ADEV, "snd_mixer_load: %s", + snd_strerror (err)); +#endif + + m_wLastError = RA_AOE_NOTENABLED; + } + } + + if (err == 0) + { + /* Find the mixer element */ + snd_mixer_elem_t* fallback_elem = NULL; + snd_mixer_elem_t* elem = snd_mixer_first_elem (m_pAlsaMixerHandle); + snd_mixer_elem_type_t type; + const char* elem_name = NULL; + snd_mixer_selem_id_t *sid = NULL; + int index; + + snd_mixer_selem_id_alloca(&sid); + + while (elem) + { + type = snd_mixer_elem_get_type(elem); + if (type == SND_MIXER_ELEM_SIMPLE) + { + snd_mixer_selem_get_id(elem, sid); + + /* We're only interested in playback volume controls */ + if(snd_mixer_selem_has_playback_volume(elem) && + !snd_mixer_selem_has_common_volume(elem)) + { + if (!fallback_elem) + { + fallback_elem = elem; + } + + elem_name = snd_mixer_selem_id_get_name (sid); + index = snd_mixer_selem_id_get_index(sid); + if (strcmp(elem_name, szElementName) == 0 && + index == nElementIndex) + { + break; + } + } + } + + elem = snd_mixer_elem_next(elem); + } + + if (!elem && fallback_elem) + { + elem = fallback_elem; + elem_name = NULL; + type = snd_mixer_elem_get_type(elem); + + if (type == SND_MIXER_ELEM_SIMPLE) + { + snd_mixer_selem_get_id(elem, sid); + elem_name = snd_mixer_selem_id_get_name (sid); + } + +#ifdef HX_LOG_SUBSYSTEM + HXLOGL1 ( HXLOG_ADEV, "Could not find element %s, using element %s instead", + m_pMixerElementName, elem_name? elem_name: "unknown"); +#endif + } + else if (!elem) + { +#ifdef HX_LOG_SUBSYSTEM + HXLOGL1 ( HXLOG_ADEV, "Could not find a usable mixer element", + snd_strerror (err)); +#endif + m_wLastError = RA_AOE_BADOPEN; + err = -1; + } + + m_pAlsaMixerElem = elem; + } + + if(err == 0) + { + if (m_pAlsaMixerHandle) + { + m_bMixerPresent = 1; + _GetVolume(); + } + else + { + m_bMixerPresent = 0; + } + + m_wLastError = RA_AOE_NOERR; + } + else + { + if(m_pAlsaMixerHandle) + { + snd_mixer_close(m_pAlsaMixerHandle); + m_pAlsaMixerHandle = NULL; + } + } + + return m_wLastError; +} + +HX_RESULT HSPAudioDevice::_CloseMixer() +{ + int err; + const char* szMixerDeviceName = NULL; + + if (!m_pAlsaMixerHandle) + { + m_wLastError = RA_AOE_DEVNOTOPEN; + return m_wLastError; + } + + if (!m_pMixerDeviceName) + { + m_wLastError = RA_AOE_DEVNOTOPEN; + return m_wLastError; + } + + szMixerDeviceName = (const char*) m_pMixerDeviceName->GetBuffer(); + err = snd_mixer_detach(m_pAlsaMixerHandle, szMixerDeviceName); + if (err < 0) + { +#ifdef HX_LOG_SUBSYSTEM + HXLOGL1 ( HXLOG_ADEV, "snd_mixer_detach: %s", + snd_strerror (err)); +#endif + m_wLastError = RA_AOE_GENERAL; + } + + if(err == 0) + { + err = snd_mixer_close(m_pAlsaMixerHandle); + if(err < 0) + { +#ifdef HX_LOG_SUBSYSTEM + HXLOGL1 ( HXLOG_ADEV, "snd_mixer_close: %s", + snd_strerror (err)); +#endif + m_wLastError = RA_AOE_GENERAL; + } + } + + if(err == 0) + { + m_pAlsaMixerHandle = NULL; + m_pAlsaMixerElem = NULL; + m_wLastError = RA_AOE_NOERR; + } + + return m_wLastError; +} + + +//Device specific method to set the audio device characteristics. Sample rate, +//bits-per-sample, etc. +//Method *must* set member vars. m_unSampleRate and m_unNumChannels. +HX_RESULT HSPAudioDevice::SetDeviceConfig( const HXAudioFormat* pFormat ) +{ + snd_pcm_state_t state; + + HX_ASSERT(m_pAlsaPCMHandle != NULL); + if (!m_pAlsaPCMHandle) + { + m_wLastError = RA_AOE_DEVNOTOPEN; + return m_wLastError; + } + + state = snd_pcm_state(m_pAlsaPCMHandle); + if (state != SND_PCM_STATE_OPEN) + { +#ifdef HX_LOG_SUBSYSTEM + HXLOGL1 ( HXLOG_ADEV, "Device is not in open state in HSPAudioDevice::SetDeviceConfig (%d)", (int) state); +#endif + m_wLastError = RA_AOE_DEVNOTOPEN; + return m_wLastError; + } + + /* Translate from HXAudioFormat to ALSA-friendly values */ + snd_pcm_format_t fmt; + unsigned int sample_rate = 0; + unsigned int channels = 0; + unsigned int buffer_time = 500000; /* 0.5 seconds */ + unsigned int period_time = buffer_time / 4; /* 4 interrupts per buffer */ + + switch (pFormat->uBitsPerSample) + { + case 8: + fmt = SND_PCM_FORMAT_S8; + break; + + case 16: + fmt = SND_PCM_FORMAT_S16_LE; + break; + + case 24: + fmt = SND_PCM_FORMAT_S24_LE; + break; + + case 32: + fmt = SND_PCM_FORMAT_S32_LE; + break; + + default: + fmt = SND_PCM_FORMAT_UNKNOWN; + break; + } + + if (fmt == SND_PCM_FORMAT_UNKNOWN) + { +#ifdef HX_LOG_SUBSYSTEM + HXLOGL1 ( HXLOG_ADEV, "Unknown bits per sample: %d", pFormat->uBitsPerSample); +#endif + m_wLastError = RA_AOE_NOTENABLED; + return m_wLastError; + } + sample_rate = pFormat->ulSamplesPerSec; + channels = pFormat->uChannels; + + /* Apply to ALSA */ + int err = 0; + snd_pcm_hw_params_t *hwparams; + snd_pcm_sw_params_t *swparams; + + snd_pcm_hw_params_alloca(&hwparams); + snd_pcm_sw_params_alloca(&swparams); + + /* Hardware parameters */ + err = snd_pcm_hw_params_any(m_pAlsaPCMHandle, hwparams); + if (err < 0) + { +#ifdef HX_LOG_SUBSYSTEM + HXLOGL1 ( HXLOG_ADEV, "snd_pcm_hw_params_any: %s", snd_strerror(err)); +#endif + m_wLastError = RA_AOE_NOTENABLED; + } + + if (err == 0) + { + err = snd_pcm_hw_params_set_access(m_pAlsaPCMHandle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED); + if (err < 0) + { +#ifdef HX_LOG_SUBSYSTEM + HXLOGL1 ( HXLOG_ADEV, "snd_pcm_hw_params_set_access: %s", snd_strerror(err)); +#endif + m_wLastError = RA_AOE_NOTENABLED; + } + } + + if (err == 0) + { + err = snd_pcm_hw_params_set_format(m_pAlsaPCMHandle, hwparams, fmt); + if (err < 0) + { +#ifdef HX_LOG_SUBSYSTEM + HXLOGL1 ( HXLOG_ADEV, "snd_pcm_hw_params_set_format: %s", snd_strerror(err)); +#endif + m_wLastError = RA_AOE_NOTENABLED; + } + } + + if (err == 0) + { + err = snd_pcm_hw_params_set_channels(m_pAlsaPCMHandle, hwparams, channels); + if (err < 0) + { +#ifdef HX_LOG_SUBSYSTEM + HXLOGL1 ( HXLOG_ADEV, "snd_pcm_hw_params_set_channels: %s", snd_strerror(err)); +#endif + m_wLastError = RA_AOE_NOTENABLED; + } + } + + if (err == 0) + { + unsigned int sample_rate_out; + sample_rate_out = sample_rate; + + err = snd_pcm_hw_params_set_rate_near(m_pAlsaPCMHandle, hwparams, &sample_rate_out, 0); + if (err < 0) + { +#ifdef HX_LOG_SUBSYSTEM + HXLOGL1 ( HXLOG_ADEV, "snd_pcm_hw_params_set_channels: %s", snd_strerror(err)); +#endif + m_wLastError = RA_AOE_NOTENABLED; + } + + if (sample_rate_out != sample_rate) + { +#ifdef HX_LOG_SUBSYSTEM + HXLOGL2 ( HXLOG_ADEV, "Requested a sample rate of %d, got a rate of %d", + sample_rate, sample_rate_out); +#endif + + sample_rate = sample_rate_out; + } + } + + if (err == 0) + { + unsigned int buffer_time_out; + buffer_time_out = buffer_time; + + err = snd_pcm_hw_params_set_buffer_time_near(m_pAlsaPCMHandle, hwparams, &buffer_time_out, 0); + if (err < 0) + { +#ifdef HX_LOG_SUBSYSTEM + HXLOGL1 ( HXLOG_ADEV, "snd_pcm_hw_params_set_buffer_time_near: %s", + snd_strerror(err)); +#endif + m_wLastError = RA_AOE_NOTENABLED; + } + + if (buffer_time_out != buffer_time) + { +#ifdef HX_LOG_SUBSYSTEM + HXLOGL2 ( HXLOG_ADEV, "Requested a buffering time of %d, got a time of %d", + buffer_time, buffer_time_out); +#endif + + buffer_time = buffer_time_out; + } + } + + if (err == 0) + { + unsigned int period_time_out; + period_time_out = period_time; + + err = snd_pcm_hw_params_set_period_time_near(m_pAlsaPCMHandle, hwparams, &period_time_out, 0); + if (err < 0) + { +#ifdef HX_LOG_SUBSYSTEM + HXLOGL1 ( HXLOG_ADEV, "snd_pcm_hw_params_set_period_time_near: %s", + snd_strerror(err)); +#endif + m_wLastError = RA_AOE_NOTENABLED; + } + + if (period_time_out != period_time) + { +#ifdef HX_LOG_SUBSYSTEM + HXLOGL2 ( HXLOG_ADEV, "Requested a period time of %d, got a period of %d", + period_time, period_time_out); +#endif + period_time = period_time_out; + } + } + + /* Apply parameters */ + err = snd_pcm_hw_params(m_pAlsaPCMHandle, hwparams); + if (err < 0) + { +#ifdef HX_LOG_SUBSYSTEM + HXLOGL1 ( HXLOG_ADEV, "snd_pcm_hw_params: %s", + snd_strerror(err)); +#endif + m_wLastError = RA_AOE_NOTENABLED; + } + + /* read buffer & period sizes */ + snd_pcm_uframes_t buffer_size = 0; + snd_pcm_uframes_t period_size = 0; + + if (err == 0) + { + err = snd_pcm_hw_params_get_buffer_size(hwparams, &buffer_size); + if (err < 0) + { +#ifdef HX_LOG_SUBSYSTEM + HXLOGL1 ( HXLOG_ADEV, "snd_pcm_hw_params_get_buffer_size: %s", + snd_strerror(err)); +#endif + m_wLastError = RA_AOE_NOTENABLED; + } + else + { + HX_ASSERT (buffer_size > 0); + } + } + + if (err == 0) + { + err = snd_pcm_hw_params_get_period_size(hwparams, &period_size, 0); + if (err < 0) + { +#ifdef HX_LOG_SUBSYSTEM + HXLOGL1 ( HXLOG_ADEV, "snd_pcm_hw_params_get_period_size: %s", + snd_strerror(err)); +#endif + m_wLastError = RA_AOE_NOTENABLED; + } + } + + /* Get hardware pause */ + if (err == 0) + { + int can_pause = 0; + int can_resume = 0; + + can_pause = snd_pcm_hw_params_can_pause(hwparams); + can_resume = snd_pcm_hw_params_can_resume(hwparams); + + // could we really have one without the other? + m_bHasHardwarePauseAndResume = (can_pause && can_resume); + m_Player->print2stderr("########## can_pause %d can_resume %d\n", can_pause, can_resume); + } + + /* Software parameters */ + if (err == 0) + { + err = snd_pcm_sw_params_current(m_pAlsaPCMHandle, swparams); + if (err < 0) + { +#ifdef HX_LOG_SUBSYSTEM + HXLOGL1 ( HXLOG_ADEV, "snd_pcm_sw_params_current: %s", + snd_strerror(err)); +#endif + m_wLastError = RA_AOE_NOTENABLED; + } + } + + snd_pcm_uframes_t start_threshold = ((buffer_size - 1) / period_size) * period_size; + + if (err == 0) + { + err = snd_pcm_sw_params_set_start_threshold(m_pAlsaPCMHandle, swparams, start_threshold); + if (err < 0) + { +#ifdef HX_LOG_SUBSYSTEM + HXLOGL1 ( HXLOG_ADEV, "snd_pcm_sw_params_set_start_threshold: %s", + snd_strerror(err)); +#endif + m_wLastError = RA_AOE_NOTENABLED; + } + } + + if (err == 0) + { + err = snd_pcm_sw_params_set_avail_min(m_pAlsaPCMHandle, swparams, period_size); + if (err < 0) + { +#ifdef HX_LOG_SUBSYSTEM + HXLOGL1 ( HXLOG_ADEV, "snd_pcm_sw_params_set_avail_min: %s", + snd_strerror(err)); +#endif + m_wLastError = RA_AOE_NOTENABLED; + } + } + + if (err == 0) + { + err = snd_pcm_sw_params_set_xfer_align(m_pAlsaPCMHandle, swparams, 1); + if (err < 0) + { +#ifdef HX_LOG_SUBSYSTEM + HXLOGL1 ( HXLOG_ADEV, "snd_pcm_sw_params_set_xfer_align: %s", + snd_strerror(err)); +#endif + m_wLastError = RA_AOE_NOTENABLED; + } + } + + if (err == 0) + { + err = snd_pcm_sw_params_set_tstamp_mode(m_pAlsaPCMHandle, swparams, SND_PCM_TSTAMP_MMAP); + if (err < 0) + { +#ifdef HX_LOG_SUBSYSTEM + HXLOGL1 ( HXLOG_ADEV, "snd_pcm_sw_params_set_xfer_align: %s", + snd_strerror(err)); +#endif + m_wLastError = RA_AOE_NOTENABLED; + } + } + + if (err == 0) + { + err = snd_pcm_sw_params_set_stop_threshold(m_pAlsaPCMHandle, swparams, ~0U); + if (err < 0) + { +#ifdef HX_LOG_SUBSYSTEM + HXLOGL1 ( HXLOG_ADEV, "snd_pcm_sw_params_set_stop_threshold: %s", + snd_strerror(err)); +#endif + m_wLastError = RA_AOE_NOTENABLED; + } + } + + if (err == 0) + { + err = snd_pcm_sw_params(m_pAlsaPCMHandle, swparams); + if (err < 0) + { +#ifdef HX_LOG_SUBSYSTEM + HXLOGL1 ( HXLOG_ADEV, "snd_pcm_sw_params: %s", + snd_strerror(err)); +#endif + m_wLastError = RA_AOE_NOTENABLED; + } + } + + /* If all the calls to this point have succeeded, move to the PREPARE state. + We will enter the RUNNING state when we've buffered enough for our start theshold. */ + if (err == 0) + { + err = snd_pcm_prepare (m_pAlsaPCMHandle); + if (err < 0) + { +#ifdef HX_LOG_SUBSYSTEM + HXLOGL1 ( HXLOG_ADEV, "snd_pcm_prepare: %s", + snd_strerror(err)); +#endif + m_wLastError = RA_AOE_NOTENABLED; + } + } + + /* Sanity check: See if we're now in the PREPARE state */ + if (err == 0) + { + snd_pcm_state_t state; + state = snd_pcm_state (m_pAlsaPCMHandle); + if (state != SND_PCM_STATE_PREPARED) + { +#ifdef HX_LOG_SUBSYSTEM + HXLOGL1 ( HXLOG_ADEV, "Expected to be in PREPARE state, actually in state %d", + (int) state); +#endif + m_wLastError = RA_AOE_NOTENABLED; + } + } + + /* Use avail to get the alsa buffer size, which is distinct from the hardware buffer + size. This will match what GetRoomOnDevice uses. */ + int alsa_buffer_size = 0; + err = snd_pcm_avail_update(m_pAlsaPCMHandle); + if(err < 0) + { +#ifdef HX_LOG_SUBSYSTEM + HXLOGL1 ( HXLOG_ADEV, "snd_pcm_avail_update: %s", snd_strerror(err)); +#endif + } + else + { + alsa_buffer_size = snd_pcm_frames_to_bytes(m_pAlsaPCMHandle, err); + err = 0; + } + + if (err == 0) + { + m_wLastError = RA_AOE_NOERR; + + m_unSampleRate = sample_rate; + m_unNumChannels = channels; + m_wBlockSize = m_ulBytesPerGran; + m_ulDeviceBufferSize = alsa_buffer_size; + m_uSampFrameSize = snd_pcm_frames_to_bytes(m_pAlsaPCMHandle, 1) / channels; + +#ifdef HX_LOG_SUBSYSTEM + HXLOGL2 ( HXLOG_ADEV, "Device Configured:\n"); + HXLOGL2 ( HXLOG_ADEV, " Sample Rate: %d", m_unSampleRate); + HXLOGL2 ( HXLOG_ADEV, " Sample Width: %d", m_uSampFrameSize); + HXLOGL2 ( HXLOG_ADEV, " Num channels: %d", m_unNumChannels); + HXLOGL2 ( HXLOG_ADEV, " Block size: %d", m_wBlockSize); + HXLOGL2 ( HXLOG_ADEV, " Device buffer size: %lu", m_ulDeviceBufferSize); + HXLOGL2 ( HXLOG_ADEV, " Supports HW Pause: %d", m_bHasHardwarePauseAndResume); + HXLOGL2 ( HXLOG_ADEV, " Start threshold: %d", start_threshold); +#endif + + } + else + { + m_unSampleRate = 0; + m_unNumChannels = 0; + + if (m_pAlsaPCMHandle) + { + _CloseAudio(); + } + } + + return m_wLastError; +} + +//Device specific method to write bytes out to the audiodevice and return a +//count of bytes written. +HX_RESULT HSPAudioDevice::WriteBytes( UCHAR* buffer, ULONG32 ulBuffLength, LONG32& lCount ) +{ + int err = 0, count = 0; + unsigned int frames_written = 0; + snd_pcm_sframes_t num_frames = 0; + ULONG32 ulBytesToWrite = ulBuffLength; + ULONG32 ulBytesWrote = 0; + + lCount = 0; + + HX_ASSERT(m_pAlsaPCMHandle); + if (!m_pAlsaPCMHandle) + { + m_wLastError = RA_AOE_DEVNOTOPEN; + return m_wLastError; + } + + m_wLastError = RA_AOE_NOERR; + + if (ulBuffLength == 0) + { + lCount = ulBuffLength; + return m_wLastError; + } + + + do + { + pthread_mutex_lock(&m_m); + if (!m_closed) + { + if (!m_SWPause) + { + num_frames = snd_pcm_bytes_to_frames(m_pAlsaPCMHandle, ulBytesToWrite); + err = snd_pcm_writei( m_pAlsaPCMHandle, buffer, num_frames ); + } + else + err = -EAGAIN; + } + else + { + pthread_mutex_unlock(&m_m); + return 0; + } + pthread_mutex_unlock(&m_m); + count++; + if (err >= 0) + { + frames_written = err; + + pthread_mutex_lock(&m_m); + if (!m_closed) + ulBytesWrote = snd_pcm_frames_to_bytes (m_pAlsaPCMHandle, frames_written); + pthread_mutex_unlock(&m_m); + buffer += ulBytesWrote; + ulBytesToWrite -= ulBytesWrote; + lCount += ulBytesWrote; + + m_ulTotalWritten += ulBytesWrote; + + } + else + { + switch (err) + { + case -EAGAIN: + usleep(10000); + break; + + case -EPIPE: + HandleXRun(); + lCount = (LONG32) ulBuffLength; + break; + + case -ESTRPIPE: + HandleSuspend(); + lCount = (LONG32) ulBuffLength; + break; + + default: + m_Player->print2stderr("########### snd_pcm_writei: %s num_frames=%ld\n", snd_strerror(err), num_frames); +#ifdef HX_LOG_SUBSYSTEM + HXLOGL1 ( HXLOG_ADEV, "snd_pcm_writei: %s", snd_strerror(err)); +#endif + m_wLastError = RA_AOE_DEVBUSY; + } + } + } while (err == -EAGAIN || (err>0 && ulBytesToWrite>0)); + + //m_Player->print2stderr("############## count = %d\n", count); + + return m_wLastError; +} + +/* Subtract the `struct timeval' values X and Y, + storing the result in RESULT. + Return 1 if the difference is negative, otherwise 0. */ + +int +timeval_subtract (struct timeval *result, + const struct timeval *x, + const struct timeval *y_orig) +{ + struct timeval y = *y_orig; + + /* Perform the carry for the later subtraction by updating Y. */ + if (x->tv_usec < y.tv_usec) + { + int nsec = (y.tv_usec - x->tv_usec) / 1000000 + 1; + y.tv_usec -= 1000000 * nsec; + y.tv_sec += nsec; + } + if ((x->tv_usec - y.tv_usec) > 1000000) + { + int nsec = (x->tv_usec - y.tv_usec) / 1000000; + y.tv_usec += 1000000 * nsec; + y.tv_sec -= nsec; + } + + /* Compute the time remaining to wait. + `tv_usec' is certainly positive. */ + result->tv_sec = x->tv_sec - y.tv_sec; + result->tv_usec = x->tv_usec - y.tv_usec; + + /* Return 1 if result is negative. */ + return x->tv_sec < y.tv_sec; +} + +HX_RESULT HSPAudioDevice::GetBytesActuallyPlayedUsingTStamps(UINT64 &nBytesPlayed) const +{ + HX_RESULT retVal = HXR_FAIL; + + int err = 0; + + snd_timestamp_t trigger_tstamp, now_tstamp, diff_tstamp; + snd_pcm_status_t* status; + + snd_pcm_status_alloca(&status); + + err = snd_pcm_status(m_pAlsaPCMHandle, status); + if (err < 0) + { +#ifdef HX_LOG_SUBSYSTEM + HXLOGL1 ( HXLOG_ADEV, "snd_pcm_status: %s", snd_strerror(err)); +#endif + } + + if (err == 0) + { + snd_pcm_status_get_tstamp(status, &now_tstamp); + snd_pcm_status_get_trigger_tstamp(status, &trigger_tstamp); + + if(!m_bGotInitialTrigger && now_tstamp.tv_sec == 0 && now_tstamp.tv_usec == 0) + { + /* Our first "now" timestamp appears to be invalid (or the user is very unlucky, and + happened to start playback as the timestamp rolls over). Fall back to using + snd_pcm_delay. + + XXXRGG: Is there a better way to figure out if the driver supports mmap'd + timestamps? */ + + m_bUseMMAPTStamps = FALSE; + } + else + { + /* Timestamp seems to be valid */ + if(!m_bGotInitialTrigger) + { + m_bGotInitialTrigger = TRUE; + memcpy(&m_tstampLastTrigger, &trigger_tstamp, sizeof(m_tstampLastTrigger)); + } + else + { + if(memcmp(&m_tstampLastTrigger, &trigger_tstamp, sizeof(m_tstampLastTrigger)) != 0) + { + /* There's been a trigger since last time -- restart the timestamp counter + XXXRGG: What if there's been multiple triggers? */ + m_nBytesPlayedBeforeLastTrigger = m_nLastBytesPlayed; + memcpy(&m_tstampLastTrigger, &trigger_tstamp, sizeof(m_tstampLastTrigger)); + +#ifdef HX_LOG_SUBSYSTEM + HXLOGL1 ( HXLOG_ADEV, "Retriggered..."); +#endif + } + } + + timeval_subtract (&diff_tstamp, &now_tstamp, &m_tstampLastTrigger); + + double fTimePlayed = (double) diff_tstamp.tv_sec + + ((double) diff_tstamp.tv_usec / 1e6); + + nBytesPlayed = (UINT64) ((fTimePlayed * (double) m_unSampleRate * m_uSampFrameSize * m_unNumChannels) + m_nBytesPlayedBeforeLastTrigger); + retVal = HXR_OK; + } + } + + return retVal; +} + +HX_RESULT HSPAudioDevice::GetBytesActuallyPlayedUsingDelay (UINT64 &nBytesPlayed) const +{ + HX_RESULT retVal = HXR_FAIL; + int err = 0; + snd_pcm_sframes_t frame_delay = 0; + + err = snd_pcm_delay (m_pAlsaPCMHandle, &frame_delay); + if (err < 0) + { +#ifdef HX_LOG_SUBSYSTEM + HXLOGL1 ( HXLOG_ADEV, "snd_pcm_status: %s", snd_strerror(err)); +#endif + } + else + { + int bytes_delay; + bytes_delay = snd_pcm_frames_to_bytes (m_pAlsaPCMHandle, frame_delay); + + nBytesPlayed = m_ulTotalWritten - bytes_delay; + retVal = HXR_OK; + } + +#ifdef HX_LOG_SUBSYSTEM +// HXLOGL4 ( HXLOG_ADEV, "nBytesPlayed: %llu, m_ulTotalWritten: %llu\n", nBytesPlayed, m_ulTotalWritten); +#endif + + return retVal; +} + +HX_RESULT HSPAudioDevice::GetBytesActuallyPlayedUsingAvail(UINT64 &nBytesPlayed) const +{ + /* Try this the hwsync way. This method seems to crash & burn with dmix, + as avail seems to come from the device, and varies depending on what other + dmix clients are writing to the slave device. Currently not used for that reason. */ + + HX_RESULT retVal = HXR_FAIL; + int err = 0; + + err = snd_pcm_hwsync(m_pAlsaPCMHandle); + if(err < 0) + { +#ifdef HX_LOG_SUBSYSTEM + HXLOGL1 ( HXLOG_ADEV, "snd_pcm_hwsync: %s", snd_strerror(err)); +#endif + } + + err = snd_pcm_avail_update(m_pAlsaPCMHandle); + if(err < 0) + { +#ifdef HX_LOG_SUBSYSTEM + HXLOGL1 ( HXLOG_ADEV, "snd_pcm_avail_update: %s", snd_strerror(err)); +#endif + } + else + { + snd_pcm_sframes_t avail = err; + int bytes_avail; + bytes_avail = snd_pcm_frames_to_bytes (m_pAlsaPCMHandle, avail); + + nBytesPlayed = m_ulTotalWritten - (m_ulDeviceBufferSize - bytes_avail); + retVal = HXR_OK; + } + + return retVal; +} + +HX_RESULT HSPAudioDevice::GetBytesActuallyPlayedUsingTimer(UINT64 &/*nBytesPlayed*/) const +{ + /* Look at the alsa timer api, and how we can lock onto it as a timer source. */ + + return HXR_FAIL; +} + +UINT64 HSPAudioDevice::GetBytesActualyPlayed(void) const +{ + HX_ASSERT(m_pAlsaPCMHandle); + if (!m_pAlsaPCMHandle) + { + return 0; + } + + HX_RESULT retVal = HXR_OK; + UINT64 nBytesPlayed = 0; + snd_pcm_state_t state; + + for(;;) + { + state = snd_pcm_state(m_pAlsaPCMHandle); + switch(state) + { + case SND_PCM_STATE_OPEN: + case SND_PCM_STATE_SETUP: + case SND_PCM_STATE_PREPARED: + /* If we're in one of these states, written and played should match. */ + m_nLastBytesPlayed = m_ulTotalWritten; + return m_nLastBytesPlayed; + + case SND_PCM_STATE_XRUN: + HandleXRun(); + continue; + + case SND_PCM_STATE_RUNNING: + break; + + case SND_PCM_STATE_PAUSED: + // return m_nLastBytesPlayed; + break; + + case SND_PCM_STATE_DRAINING: + case SND_PCM_STATE_SUSPENDED: + case SND_PCM_STATE_DISCONNECTED: + HX_ASSERT(!"Not reached"); + break; + } + + break; + } + + // XXXRGG: Always use the delay method for now. + m_bUseMMAPTStamps = FALSE; + + if (m_bUseMMAPTStamps) + { + retVal = GetBytesActuallyPlayedUsingTStamps(nBytesPlayed); + } + + if (!m_bUseMMAPTStamps || FAILED(retVal)) + { + /* MMAP'd timestamps are fishy. Try using snd_pcm_delay. */ + retVal = GetBytesActuallyPlayedUsingDelay(nBytesPlayed); + } + + m_nLastBytesPlayed = nBytesPlayed; + return nBytesPlayed; +} + + +//this must return the number of bytes that can be written without blocking. +HX_RESULT HSPAudioDevice::GetRoomOnDevice(ULONG32& ulBytes) const +{ + ulBytes = 0; + + HX_ASSERT(m_pAlsaPCMHandle); + if (!m_pAlsaPCMHandle) + { + m_wLastError = RA_AOE_DEVNOTOPEN; + return m_wLastError; + } + + int err = 0; + err = snd_pcm_avail_update(m_pAlsaPCMHandle); + if(err > 0) + { + ulBytes = snd_pcm_frames_to_bytes(m_pAlsaPCMHandle, err); + } + else + { + switch (err) + { + case -EAGAIN: + break; + + case -EPIPE: + HandleXRun(); + break; + + case -ESTRPIPE: + HandleSuspend(); + break; + + default: +#ifdef HX_LOG_SUBSYSTEM + HXLOGL1 ( HXLOG_ADEV, "snd_pcm_avail_update: %s", snd_strerror(err)); +#endif + m_wLastError = RA_AOE_DEVBUSY; + } + } + +#ifdef HX_LOG_SUBSYSTEM +// HXLOGL4 ( HXLOG_ADEV, "RoomOnDevice: %d", ulBytes); +#endif + + return m_wLastError; +} + + +//Device specific method to get/set the devices current volume. +UINT16 HSPAudioDevice::_GetVolume() const +{ + HX_ASSERT(m_pAlsaMixerElem); + if (!m_pAlsaMixerElem) + { + return 0; + } + + UINT16 nRetVolume = 0; + + snd_mixer_elem_type_t type; + int err = 0; + type = snd_mixer_elem_get_type(m_pAlsaMixerElem); + + if (type == SND_MIXER_ELEM_SIMPLE) + { + long volume, min_volume, max_volume; + + if(snd_mixer_selem_has_playback_volume(m_pAlsaMixerElem) || + snd_mixer_selem_has_playback_volume_joined(m_pAlsaMixerElem)) + { + err = snd_mixer_selem_get_playback_volume(m_pAlsaMixerElem, + SND_MIXER_SCHN_MONO, + &volume); + if (err < 0) + { +#ifdef HX_LOG_SUBSYSTEM + HXLOGL1 ( HXLOG_ADEV, "snd_mixer_selem_get_playback_volume: %s", + snd_strerror (err)); +#endif + } + + if (err == 0) + { + snd_mixer_selem_get_playback_volume_range(m_pAlsaMixerElem, + &min_volume, + &max_volume); + + if(max_volume > min_volume) + { + nRetVolume = (UINT16) (100 * volume / (max_volume - min_volume)); + } + } + } + } + + return nRetVolume; +} + + +HX_RESULT HSPAudioDevice::_SetVolume(UINT16 unVolume) +{ + m_wLastError = RA_AOE_NOERR; + + HX_ASSERT(m_pAlsaMixerElem); + if (!m_pAlsaMixerElem) + { + m_wLastError = RA_AOE_DEVNOTOPEN; + return m_wLastError; + } + + snd_mixer_elem_type_t type; + int err = 0; + type = snd_mixer_elem_get_type(m_pAlsaMixerElem); + + if (type == SND_MIXER_ELEM_SIMPLE) + { + long volume, min_volume, max_volume, range; + + if(snd_mixer_selem_has_playback_volume(m_pAlsaMixerElem) || + snd_mixer_selem_has_playback_volume_joined(m_pAlsaMixerElem)) + { + snd_mixer_selem_get_playback_volume_range(m_pAlsaMixerElem, + &min_volume, + &max_volume); + + range = max_volume - min_volume; + volume = (long) ((unVolume / 100) * range + min_volume); + + err = snd_mixer_selem_set_playback_volume( m_pAlsaMixerElem, + SND_MIXER_SCHN_FRONT_LEFT, + volume); + if (err < 0) + { +#ifdef HX_LOG_SUBSYSTEM + HXLOGL1 ( HXLOG_ADEV, "snd_mixer_selem_set_playback_volume: %s", + snd_strerror (err)); +#endif + m_wLastError = RA_AOE_GENERAL; + } + + if (!snd_mixer_selem_is_playback_mono (m_pAlsaMixerElem)) + { + /* Set the right channel too */ + err = snd_mixer_selem_set_playback_volume( m_pAlsaMixerElem, + SND_MIXER_SCHN_FRONT_RIGHT, + volume); + if (err < 0) + { +#ifdef HX_LOG_SUBSYSTEM + HXLOGL1 ( HXLOG_ADEV, "snd_mixer_selem_set_playback_volume: %s", + snd_strerror (err)); +#endif + m_wLastError = RA_AOE_GENERAL; + } + } + } + } + + return m_wLastError; +} + +//Device specific method to drain a device. This should play the remaining +//bytes in the devices buffer and then return. +HX_RESULT HSPAudioDevice::_Drain() +{ + m_wLastError = RA_AOE_NOERR; + + HX_ASSERT(m_pAlsaPCMHandle); + if (!m_pAlsaPCMHandle) + { + m_wLastError = RA_AOE_DEVNOTOPEN; + return m_wLastError; + } + + int err = 0; + + err = snd_pcm_drain(m_pAlsaPCMHandle); + if (err < 0) + { +#ifdef HX_LOG_SUBSYSTEM + HXLOGL1 ( HXLOG_ADEV, "snd_pcm_drain: %s", + snd_strerror (err)); +#endif + m_wLastError = RA_AOE_GENERAL; + } + + err = snd_pcm_prepare(m_pAlsaPCMHandle); + if (err < 0) + { +#ifdef HX_LOG_SUBSYSTEM + HXLOGL1 ( HXLOG_ADEV, "snd_pcm_prepare: %s", + snd_strerror (err)); +#endif + m_wLastError = RA_AOE_GENERAL; + } + + return m_wLastError; +} + + +//Device specific method to reset device and return it to a state that it +//can accept new sample rates, num channels, etc. +HX_RESULT HSPAudioDevice::_Reset() +{ + if (!m_pAlsaPCMHandle) + { + m_wLastError = RA_AOE_DEVNOTOPEN; + return m_wLastError; + } + + m_wLastError = RA_AOE_NOERR; + + m_nLastBytesPlayed = 0; + + int err = 0; + + err = snd_pcm_drop(m_pAlsaPCMHandle); + if (err < 0) + { +#ifdef HX_LOG_SUBSYSTEM + HXLOGL1 ( HXLOG_ADEV, "snd_pcm_drop: %s", + snd_strerror (err)); +#endif + m_wLastError = RA_AOE_GENERAL; + } + + err = snd_pcm_prepare(m_pAlsaPCMHandle); + if (err < 0) + { +#ifdef HX_LOG_SUBSYSTEM + HXLOGL1 ( HXLOG_ADEV, "snd_pcm_prepare: %s", + snd_strerror (err)); +#endif + m_wLastError = RA_AOE_GENERAL; + } + + return m_wLastError; +} + +HX_RESULT HSPAudioDevice::_CheckFormat( const HXAudioFormat* pFormat ) +{ + HX_ASSERT(m_pAlsaPCMHandle == NULL); + + m_wLastError = _OpenAudio(); + if(m_wLastError != RA_AOE_NOERR) + { + return m_wLastError; + } + + m_wLastError = RA_AOE_NOERR; + + snd_pcm_format_t fmt; + unsigned int sample_rate = 0; + unsigned int channels = 0; + + switch (pFormat->uBitsPerSample) + { + case 8: + fmt = SND_PCM_FORMAT_S8; + break; + + case 16: + fmt = SND_PCM_FORMAT_S16_LE; + break; + + case 24: + fmt = SND_PCM_FORMAT_S24_LE; + break; + + case 32: + fmt = SND_PCM_FORMAT_S32_LE; + break; + + default: + fmt = SND_PCM_FORMAT_UNKNOWN; + break; + } + + if (fmt == SND_PCM_FORMAT_UNKNOWN) + { +#ifdef HX_LOG_SUBSYSTEM + HXLOGL1 ( HXLOG_ADEV, "Unknown bits per sample: %d", pFormat->uBitsPerSample); +#endif + m_wLastError = RA_AOE_NOTENABLED; + return m_wLastError; + } + sample_rate = pFormat->ulSamplesPerSec; + channels = pFormat->uChannels; + + /* Apply to ALSA */ + int err = 0; + snd_pcm_hw_params_t *hwparams; + + snd_pcm_hw_params_alloca(&hwparams); + + err = snd_pcm_hw_params_any(m_pAlsaPCMHandle, hwparams); + if (err < 0) + { +#ifdef HX_LOG_SUBSYSTEM + HXLOGL1 ( HXLOG_ADEV, "snd_pcm_hw_params_any: %s", snd_strerror(err)); +#endif + m_wLastError = RA_AOE_NOTENABLED; + } + + if (err == 0) + { + err = snd_pcm_hw_params_test_rate (m_pAlsaPCMHandle, hwparams, sample_rate, 0); + if (err < 0) + { + m_wLastError = RA_AOE_BADFORMAT; + } + } + + if (err == 0) + { + err = snd_pcm_hw_params_test_channels (m_pAlsaPCMHandle, hwparams, channels); + if (err < 0) + { + m_wLastError = RA_AOE_BADFORMAT; + } + } + + if (err == 0) + { + err = snd_pcm_hw_params_test_format (m_pAlsaPCMHandle, hwparams, fmt); + if (err < 0) + { + m_wLastError = RA_AOE_BADFORMAT; + } + } + + _CloseAudio(); + + return m_wLastError; +} + + +HX_RESULT HSPAudioDevice::CheckSampleRate( ULONG32 ulSampleRate ) +{ + HX_ASSERT(m_pAlsaPCMHandle == NULL); + bool shouldclose = false; + + if (!m_pAlsaPCMHandle) + { + m_wLastError = _OpenAudio(); + if(m_wLastError != RA_AOE_NOERR) + { + return m_wLastError; + } + shouldclose = true; + } + + int err = 0; + snd_pcm_hw_params_t *hwparams; + + snd_pcm_hw_params_alloca(&hwparams); + + m_wLastError = RA_AOE_NOERR; + + err = snd_pcm_hw_params_any(m_pAlsaPCMHandle, hwparams); + if (err < 0) + { +#ifdef HX_LOG_SUBSYSTEM + HXLOGL1 ( HXLOG_ADEV, "snd_pcm_hw_params_any: %s", snd_strerror(err)); +#endif + m_wLastError = RA_AOE_NOTENABLED; + } + + if (err == 0) + { + err = snd_pcm_hw_params_test_rate (m_pAlsaPCMHandle, hwparams, ulSampleRate, 0); + if (err < 0) + { + m_wLastError = RA_AOE_BADFORMAT; + } + } + + if (shouldclose) + _CloseAudio(); + + return m_wLastError; +} + + +HX_RESULT HSPAudioDevice::_Pause() +{ + HX_ASSERT(m_pAlsaPCMHandle); + if (!m_pAlsaPCMHandle) + { + m_wLastError = RA_AOE_DEVNOTOPEN; + return m_wLastError; + } + + if (m_bHasHardwarePauseAndResume) + { + snd_pcm_state_t state; + + state = snd_pcm_state(m_pAlsaPCMHandle); + if (state == SND_PCM_STATE_RUNNING) + { + int err = 0; + err = snd_pcm_pause(m_pAlsaPCMHandle, 1); + if (err < 0) + { +#ifdef HX_LOG_SUBSYSTEM + HXLOGL1 ( HXLOG_ADEV, "snd_pcm_pause: %s", + snd_strerror (err)); +#endif + + m_wLastError = RA_AOE_NOTSUPPORTED; + } + } + } + else + { + pthread_mutex_lock(&m_m); + m_SWPause = true; + _Drain(); + _Reset(); + pthread_mutex_unlock(&m_m); + } + + return m_wLastError; +} + +HX_RESULT HSPAudioDevice::_Resume() +{ + HX_ASSERT(m_pAlsaPCMHandle); + if (!m_pAlsaPCMHandle) + { + m_wLastError = RA_AOE_DEVNOTOPEN; + return m_wLastError; + } + + if (m_bHasHardwarePauseAndResume) + { + snd_pcm_state_t state; + + state = snd_pcm_state(m_pAlsaPCMHandle); + if (state == SND_PCM_STATE_PAUSED) + { + int err = 0; + err = snd_pcm_pause(m_pAlsaPCMHandle, 0); + + if (err < 0) + { +#ifdef HX_LOG_SUBSYSTEM + HXLOGL1 ( HXLOG_ADEV, "snd_pcm_pause: %s", + snd_strerror (err)); +#endif + + m_wLastError = RA_AOE_NOTSUPPORTED; + } + } + } + else + { + pthread_mutex_lock(&m_m); + m_SWPause = false; + _Reset(); + pthread_mutex_unlock(&m_m); + } + + return m_wLastError; +} + +BOOL HSPAudioDevice::HardwarePauseSupported() const +{ + HX_ASSERT(m_pAlsaPCMHandle != NULL); + + return m_bHasHardwarePauseAndResume; +} + + +void HSPAudioDevice::HandleXRun(void) const +{ + int err = 0; + +#ifdef HX_LOG_SUBSYSTEM + HXLOGL2 ( HXLOG_ADEV, "Handling XRun"); +#endif + + err = snd_pcm_prepare(m_pAlsaPCMHandle); + if (err < 0) + { +#ifdef HX_LOG_SUBSYSTEM + HXLOGL1 ( HXLOG_ADEV, "snd_pcm_resume: %s (xrun)", + snd_strerror (err)); +#endif + } + + /* Catch up to the write position of the audio device so we get new data. + XXXRGG: Is there some way we, the device, can force a rewind? */ + m_nLastBytesPlayed = m_ulTotalWritten; +} + +void HSPAudioDevice::HandleSuspend(void) const +{ + int err = 0; + + do + { + err = snd_pcm_resume(m_pAlsaPCMHandle); + if (err == 0) + { + break; + } + else if (err == -EAGAIN) + { + usleep(1000); + } + } while (err == -EAGAIN); + + if (err < 0) + { + HandleXRun(); + } +} + +#endif // HELIX_USE_ALSA diff --git a/amarok/src/engine/helix/helix-sp/hspalsadevice.h b/amarok/src/engine/helix/helix-sp/hspalsadevice.h new file mode 100644 index 00000000..b237e322 --- /dev/null +++ b/amarok/src/engine/helix/helix-sp/hspalsadevice.h @@ -0,0 +1,248 @@ +/****************************************************************************** + * * + * 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 library is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin St, 5th fl, Boston, MA 02110-1301, * + * USA, or check http://www.fsf.org/about/contact.html * + * * + * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved. * + * Portions Copyright (c) 2005 Paul Cifarelli * + * * + ******************************************************************************/ + +#ifndef _AUDLINUXALSA +#define _AUDLINUXALSA + +#ifdef USE_HELIX_ALSA + +#define ALSA_PCM_NEW_HW_PARAMS_API +#define ALSA_PCM_NEW_SW_PARAMS_API + +#include <alsa/asoundlib.h> + +class HelixSimplePlayer; +class AudioQueue +{ +public: + AudioQueue( const HXAudioData *buf ); + ~AudioQueue(); + + AudioQueue *fwd; + HXAudioData ad; + LONG32 bytes; +}; + +class HSPAudioDevice : public IHXAudioDevice +{ +public: + HSPAudioDevice(HelixSimplePlayer *player, const char *device); + virtual ~HSPAudioDevice(); + + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj); + STDMETHOD_(ULONG32,AddRef) (THIS); + STDMETHOD_(ULONG32,Release) (THIS); + /* + * IHXAudioDevice methods + */ + STDMETHOD(CheckFormat) ( + THIS_ + const HXAudioFormat* pAudioFormat + ); + + STDMETHOD(Close) ( + THIS_ + const BOOL bFlush + ); + + STDMETHOD(Drain) ( + THIS + ); + + STDMETHOD(GetCurrentAudioTime) ( + THIS_ + REF(ULONG32) ulCurrentTime + ); + + STDMETHOD_(UINT16,GetVolume) ( + THIS + ); + + STDMETHOD_(BOOL,InitVolume) ( + THIS_ + const UINT16 uMinVolume, + const UINT16 uMaxVolume + ); + + STDMETHOD(Open) ( + THIS_ + const HXAudioFormat* pAudioFormat, + IHXAudioDeviceResponse* pStreamResponse + ); + + STDMETHOD(Pause) ( + THIS + ); + + STDMETHOD(Reset) ( + THIS + ); + + STDMETHOD(Resume) ( + THIS + ); + + STDMETHOD(SetVolume) ( + THIS_ + const UINT16 uVolume + ); + + STDMETHOD(Write) ( + THIS_ + const HXAudioData* pAudioData + ); + + HX_RESULT OnTimeSync(); + + void setDevice( const char *device ); + +protected: + virtual INT16 GetAudioFd(void); + + //This ones important. + virtual UINT64 GetBytesActualyPlayed(void) const; + + //Device specific method to set the audio device characteristics. Sample rate, + //bits-per-sample, etc. + //Method *must* set member vars. m_unSampleRate and m_unNumChannels. + virtual HX_RESULT SetDeviceConfig( const HXAudioFormat* pFormat ); + + //Device specific method to test wether or not the device supports the + //give sample rate. If the device can not be opened, or otherwise tested, + //it should return RA_AOE_DEVBUSY. + virtual HX_RESULT CheckSampleRate( ULONG32 ulSampleRate ); + virtual HX_RESULT _CheckFormat( const HXAudioFormat* pFormat ); + + //Device specific method to write bytes out to the audiodevice and return a + //count of bytes written. + virtual HX_RESULT WriteBytes( UCHAR* buffer, ULONG32 ulBuffLength, LONG32& lCount ); + + //Device specific methods to open/close the mixer and audio devices. + virtual HX_RESULT _OpenAudio(); + virtual HX_RESULT _CloseAudio(); + virtual HX_RESULT _OpenMixer(); + virtual HX_RESULT _CloseMixer(); + + //Device specific method to reset device and return it to a state that it + //can accept new sample rates, num channels, etc. + virtual HX_RESULT _Reset(); + virtual HX_RESULT _Pause(); + virtual HX_RESULT _Resume(); + + //Device specific method to get/set the devices current volume. + virtual UINT16 _GetVolume() const; + virtual HX_RESULT _SetVolume(UINT16 volume); + + //Device specific method to drain a device. This should play the remaining + //bytes in the devices buffer and then return. + virtual HX_RESULT _Drain(); + + //Device specific method to return the amount of room available on the + //audio device that can be written without blocking. + virtual HX_RESULT GetRoomOnDevice( ULONG32& ulBytes) const; + + //A method to let us know if the hardware supports puase/resume. + //We can use this to remove unneeded memcpys and other expensive + //operations. The default implementation is 'No, not supported'. + virtual BOOL HardwarePauseSupported() const; + + int _Write( const HXAudioData *pAudioData ); + + int sync(); + +private: + HSPAudioDevice(); + //protect the unintentional copy ctor. + HSPAudioDevice( const HSPAudioDevice & ); //Not implemented. + + /* The constness imposed by the base class is a lost cause here -- + make all functions const, all members mutable. */ + void HandleXRun(void) const; + void HandleSuspend(void) const; + + HX_RESULT GetBytesActuallyPlayedUsingTStamps (UINT64 &nBytesPlayed) const; + HX_RESULT GetBytesActuallyPlayedUsingDelay (UINT64 &nBytesPlayed) const; + HX_RESULT GetBytesActuallyPlayedUsingAvail (UINT64 &nBytesPlayed) const; + HX_RESULT GetBytesActuallyPlayedUsingTimer (UINT64 &nBytesPlayed) const; + + mutable snd_pcm_t* m_pAlsaPCMHandle; + mutable snd_mixer_t* m_pAlsaMixerHandle; + mutable snd_mixer_elem_t* m_pAlsaMixerElem; + + mutable IHXBuffer* m_pPCMDeviceName; + mutable IHXBuffer* m_pMixerDeviceName; + mutable IHXBuffer* m_pMixerElementName; + + mutable BOOL m_bHasHardwarePauseAndResume; + + mutable UINT64 m_nBytesPlayedBeforeLastTrigger; + + mutable UINT64 m_nLastBytesPlayed; + + mutable snd_timestamp_t m_tstampLastTrigger; + mutable BOOL m_bGotInitialTrigger; + + mutable BOOL m_bUseMMAPTStamps; + + LONG32 m_lRefCount; + mutable LONG32 m_wLastError; + BOOL m_bMixerPresent; + UINT32 m_unSampleRate; + UINT16 m_unNumChannels; + UINT32 m_wBlockSize; + UINT32 m_ulBytesPerGran; + UINT32 m_ulDeviceBufferSize; + UINT16 m_uSampFrameSize; + UINT32 m_ulTotalWritten; + UINT32 m_ulCurrentTime; + UINT32 m_ulQTime; + UINT32 m_ulLastTime; + BOOL m_SWPause; + + HelixSimplePlayer *m_Player; + IHXAudioDeviceResponse *m_pStreamResponse; + + bool m_done; + bool m_drain; + bool m_closed; + AudioQueue *m_head; + AudioQueue *m_tail; + pthread_t m_thrid; + pthread_mutex_t m_m; + pthread_cond_t m_cv; + + void addBuf(struct AudioQueue *item); + void pushBuf(struct AudioQueue *item); + AudioQueue *getBuf(); + void clearQueue(); + + static void *writerThread( void *arg ); +}; + +#endif // USE_HELIX_ALSA + +#endif //_AUDIOOUTLINUXALSA diff --git a/amarok/src/engine/helix/helix-sp/hspauthmgr.cpp b/amarok/src/engine/helix/helix-sp/hspauthmgr.cpp new file mode 100644 index 00000000..db2b1f81 --- /dev/null +++ b/amarok/src/engine/helix/helix-sp/hspauthmgr.cpp @@ -0,0 +1,112 @@ + +/* + * + * This software is released under the provisions of the GPL version 2. + * see file "COPYING". If that file is not available, the full statement + * of the license can be found at + * + * http://www.fsf.org/licensing/licenses/gpl.txt + * + * Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved. + * Portions (c) Paul Cifarelli 2005 + */ + +#include <stdio.h> +#include "hxcom.h" +#include "hxauth.h" +#include "hspauthmgr.h" +#include <ctype.h> + +#include "hxausvc.h" +#include "helix-sp.h" +#include "utils.h" + + +HSPAuthenticationManager::HSPAuthenticationManager(HelixSimplePlayer *pSplay) : + m_lRefCount(0), + m_bSentPassword(false), + m_splayer(pSplay) +{ +} + +HSPAuthenticationManager::~HSPAuthenticationManager() +{ +} + +STDMETHODIMP +HSPAuthenticationManager::QueryInterface(REFIID riid, void**ppvObj) +{ + if(IsEqualIID(riid, IID_IUnknown)) + { + AddRef(); + *ppvObj = (IUnknown*)(IHXAuthenticationManager*)this; + return HXR_OK; + } + else if(IsEqualIID(riid, IID_IHXAuthenticationManager)) + { + AddRef(); + *ppvObj = (IHXAuthenticationManager*)this; + return HXR_OK; + } + *ppvObj = NULL; + return HXR_NOINTERFACE; +} + +STDMETHODIMP_(UINT32) +HSPAuthenticationManager::AddRef() +{ + return InterlockedIncrement(&m_lRefCount); +} + +STDMETHODIMP_(UINT32) +HSPAuthenticationManager::Release() +{ + if (InterlockedDecrement(&m_lRefCount) > 0) + { + return m_lRefCount; + } + + delete this; + return 0; +} + +STDMETHODIMP +HSPAuthenticationManager::HandleAuthenticationRequest(IHXAuthenticationManagerResponse* pResponse) +{ + char username[1024] = ""; /* Flawfinder: ignore */ + char password[1024] = ""; /* Flawfinder: ignore */ + HX_RESULT res = HXR_FAIL; + + if( !m_bSentPassword ) + { + res = HXR_OK; + if (m_splayer->bEnableVerboseMode) + m_splayer->print2stdout("\nSending Username and Password...\n"); + + SafeStrCpy(username, m_splayer->m_pszUsername, 1024); + SafeStrCpy(password, m_splayer->m_pszPassword, 1024); + + //strip trailing whitespace + char* c; + for(c = username + strlen(username) - 1; + c > username && isspace(*c); + c--) + ; + *(c+1) = 0; + + for(c = password + strlen(password) - 1; + c > password && isspace(*c); + c--) + ; + *(c+1) = 0; + + m_bSentPassword = true; + } + + if (m_splayer->bEnableVerboseMode && FAILED(res) ) + m_splayer->print2stdout("\nInvalid Username and/or Password.\n"); + + pResponse->AuthenticationRequestDone(res, username, password); + return res; +} + diff --git a/amarok/src/engine/helix/helix-sp/hspauthmgr.h b/amarok/src/engine/helix/helix-sp/hspauthmgr.h new file mode 100644 index 00000000..55d95cb7 --- /dev/null +++ b/amarok/src/engine/helix/helix-sp/hspauthmgr.h @@ -0,0 +1,37 @@ +/* + * + * This software is released under the provisions of the GPL version 2. + * see file "COPYING". If that file is not available, the full statement + * of the license can be found at + * + * http://www.fsf.org/licensing/licenses/gpl.txt + * + * Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved. + * Portions (c) Paul Cifarelli 2005 + * + */ + +#ifndef _HSPAUTHMGR_H_ +#define _HSPAUTHMGR_H_ + +#include "hxauth.h" + +class HelixSimplePlayer; + +class HSPAuthenticationManager : public IHXAuthenticationManager +{ +private: + INT32 m_lRefCount; + BOOL m_bSentPassword; + HelixSimplePlayer *m_splayer; + +public: + HSPAuthenticationManager(HelixSimplePlayer *pSplay); + virtual ~HSPAuthenticationManager(); + STDMETHOD(QueryInterface) (THIS_ REFIID riid, void** ppvObj); + STDMETHOD_(UINT32,AddRef) (THIS); + STDMETHOD_(UINT32,Release) (THIS); + + STDMETHOD(HandleAuthenticationRequest) (IHXAuthenticationManagerResponse* pResponse); +}; +#endif diff --git a/amarok/src/engine/helix/helix-sp/hspcontext.cpp b/amarok/src/engine/helix/helix-sp/hspcontext.cpp new file mode 100644 index 00000000..f87a9141 --- /dev/null +++ b/amarok/src/engine/helix/helix-sp/hspcontext.cpp @@ -0,0 +1,472 @@ + +/* + * + * This software is released under the provisions of the GPL version 2. + * see file "COPYING". If that file is not available, the full statement + * of the license can be found at + * + * http://www.fsf.org/licensing/licenses/gpl.txt + * + * Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved. + * Portions (c) Paul Cifarelli 2005 + * + * + */ +#include "hxcomm.h" +#include "hxcore.h" +#include "hxbuffer.h" +#include "hxmangle.h" + +#include "hxclsnk.h" +#include "hxerror.h" +#include "hxprefs.h" + +#include "hspadvisesink.h" +#include "hsperror.h" +#include "hspauthmgr.h" +#include "hspcontext.h" + +#include "hxausvc.h" +#include "helix-sp.h" +#include "utils.h" + +extern BOOL bEnableAdviceSink; + +HSPEngineContext::HSPEngineContext(HelixSimplePlayer *splayer, IHXCommonClassFactory *pCommonClassFactory) : m_lRefCount(0), m_CommonClassFactory(pCommonClassFactory), m_splayer(splayer) +{ +} + +HSPEngineContext::~HSPEngineContext() +{ + Close(); +} + +void HSPEngineContext::Close() +{ + // you don't own the common class factory, so don't even think about it... +} + +// *** IUnknown methods *** + +///////////////////////////////////////////////////////////////////////// +// Method: +// IUnknown::QueryInterface +// Purpose: +// Implement this to export the interfaces supported by your +// object. +// +STDMETHODIMP HSPEngineContext::QueryInterface(REFIID riid, void** ppvObj) +{ + if (IsEqualIID(riid, IID_IUnknown)) + { + AddRef(); + *ppvObj = this; + return HXR_OK; + } + else if (IsEqualIID(riid, IID_IHXPreferences)) + { + AddRef(); + *ppvObj = (IHXPreferences*)this; + return HXR_OK; + } + *ppvObj = NULL; + return HXR_NOINTERFACE; +} + +///////////////////////////////////////////////////////////////////////// +// Method: +// IUnknown::AddRef +// Purpose: +// Everyone usually implements this the same... feel free to use +// this implementation. +// +STDMETHODIMP_(ULONG32) HSPEngineContext::AddRef() +{ + return InterlockedIncrement(&m_lRefCount); +} + +///////////////////////////////////////////////////////////////////////// +// Method: +// IUnknown::Release +// Purpose: +// Everyone usually implements this the same... feel free to use +// this implementation. +// +STDMETHODIMP_(ULONG32) HSPEngineContext::Release() +{ + if (InterlockedDecrement(&m_lRefCount) > 0) + { + return m_lRefCount; + } + + delete this; + return 0; +} + + +// *** IHXPreference methods *** +void HSPEngineContext::Init(IUnknown* /*pUnknown*/) +{ + // nothing to do yet... +} + +///////////////////////////////////////////////////////////////////////// +// Method: +// IHXPreferences::ReadPref +// Purpose: +// Read a Preference from the registry. +// +STDMETHODIMP +HSPEngineContext::ReadPref(const char* pref_key, IHXBuffer*& buffer) +{ + HX_RESULT hResult = HXR_OK; + unsigned char *outbuf; + IHXBuffer *ibuf; + + + m_splayer->print2stderr("in engine context, key is <%s>\n", pref_key); + if (0 == (stricmp(pref_key, "OpenAudioDeviceOnPlayback"))) + { + m_CommonClassFactory->CreateInstance(CLSID_IHXBuffer, (void **) &ibuf); + if (ibuf) + { + ibuf->SetSize(2); + outbuf = ibuf->GetBuffer(); + strcpy((char *)outbuf, "0"); + buffer = ibuf; + //m_splayer->print2stderr("value = %d\n",atol((const char*) buffer->GetBuffer())); + } + } + else if (0 == (stricmp(pref_key, "SoundDriver"))) + { + m_CommonClassFactory->CreateInstance(CLSID_IHXBuffer, (void **) &ibuf); + if (ibuf) + { + ibuf->SetSize(2); + outbuf = ibuf->GetBuffer(); + + // 0 = OSS + // 1 = OldOSSsupport + // 2 = ESound + // 3 = Alsa + // 4 = USound + + if (m_splayer->getOutputSink() == HelixSimplePlayer::ALSA) + strcpy((char *)outbuf, "3"); // set SoundDriver = kALSA (ie 3) for Alsa native support + else if (m_splayer->getOutputSink() == HelixSimplePlayer::OSS) + strcpy((char *)outbuf, "0"); // set SoundDriver = kOSS (ie 0) for OSS + buffer = ibuf; + + if (m_splayer->getOutputSink() == HelixSimplePlayer::ALSA || m_splayer->getOutputSink() == HelixSimplePlayer::OSS) + m_splayer->print2stderr("Setting Sound System to %s\n", m_splayer->getOutputSink() == HelixSimplePlayer::ALSA ? "ALSA" : "OSS"); + else + m_splayer->print2stderr("Setting Sound System to UNKNOWN: %d\n", m_splayer->getOutputSink()); + } + } + // maybe also need to allow setting of "AlsaMixerDeviceName"? + else if (0 == (stricmp(pref_key, "AlsaMixerElementName"))) + { + m_splayer->setAlsaCapableCore(); // this just lets everyone know that this helix core is Alsa-capable + m_CommonClassFactory->CreateInstance(CLSID_IHXBuffer, (void **) &ibuf); + if (ibuf) + { + ibuf->SetSize(11); + outbuf = ibuf->GetBuffer(); + strcpy((char *)outbuf, "PC Speaker"); + buffer = ibuf; + m_splayer->print2stderr("Setting Mixer Element to use default mixer\n"); + } + } + else if (0 == (stricmp(pref_key, "AlsaMixerDeviceName"))) + { + m_splayer->setAlsaCapableCore(); // this just lets everyone know that this helix core is Alsa-capable + m_CommonClassFactory->CreateInstance(CLSID_IHXBuffer, (void **) &ibuf); + if (ibuf) + { + ibuf->SetSize(8); + outbuf = ibuf->GetBuffer(); + strcpy((char *)outbuf, "default"); + buffer = ibuf; + m_splayer->print2stderr("Setting Mixer Device to use the \"default\" mixer\n"); + } + } + else if (0 == (stricmp(pref_key, "AlsaPCMDeviceName"))) + { + m_splayer->setAlsaCapableCore(); // this just lets everyone know that this helix core is Alsa-capable + m_CommonClassFactory->CreateInstance(CLSID_IHXBuffer, (void **) &ibuf); + if (ibuf) + { + int len = strlen(m_splayer->getDevice()); + m_splayer->print2stderr("Setting Sound Device to \"%s\", %d\n", m_splayer->getDevice(), len); + ibuf->SetSize(len + 1); + outbuf = ibuf->GetBuffer(); + strcpy((char *)outbuf, m_splayer->getDevice()); + buffer = ibuf; + m_splayer->print2stderr("Setting Sound Device to \"%s\"\n", m_splayer->getDevice()); + } + } + else if (0 == (stricmp(pref_key, "ThreadedAudio"))) + { + m_CommonClassFactory->CreateInstance(CLSID_IHXBuffer, (void **) &ibuf); + if (ibuf) + { + ibuf->SetSize(2); + outbuf = ibuf->GetBuffer(); + strcpy((char *)outbuf, "1"); + buffer = ibuf; + + m_splayer->print2stderr("setting ThreadedAudio to value = %ld\n",atol((const char*) buffer->GetBuffer())); + } + } + else if (0 == (stricmp(pref_key, "UseCoreThread"))) + { + m_CommonClassFactory->CreateInstance(CLSID_IHXBuffer, (void **) &ibuf); + if (ibuf) + { + ibuf->SetSize(2); + outbuf = ibuf->GetBuffer(); + strcpy((char *)outbuf, "1"); + buffer = ibuf; + + m_splayer->print2stderr("setting initial UseCoreThread to value = %ld\n",atol((const char*) buffer->GetBuffer())); + } + } + else if (0 == (stricmp(pref_key, "NetworkThreading"))) + { + m_CommonClassFactory->CreateInstance(CLSID_IHXBuffer, (void **) &ibuf); + if (ibuf) + { + ibuf->SetSize(2); + outbuf = ibuf->GetBuffer(); + strcpy((char *)outbuf, "1"); + buffer = ibuf; + + m_splayer->print2stderr("setting initial NetworkTheading to value = %ld\n",atol((const char*) buffer->GetBuffer())); + } + } + else + { + hResult = HXR_NOTIMPL; + } + + return hResult; +} + +///////////////////////////////////////////////////////////////////////// +// Method: +// IHXPreferences::WritePref +// Purpose: +// Write a Preference to the registry. +// +STDMETHODIMP +HSPEngineContext::WritePref(const char* /*pref_key*/, IHXBuffer* /*buffer*/) +{ + //m_splayer->print2stderr("In EngineContext, WritePref, key %s\n", pref_key); + return HXR_OK; // for now, no one allowed to change it +} + + + +HSPClientContext::HSPClientContext(LONG32 lClientIndex, HelixSimplePlayer *pSplay) + : m_lRefCount(0) + , m_lClientIndex(lClientIndex) + , m_pClientSink(NULL) + , m_pErrorSink(NULL) + , m_pAuthMgr(NULL) + , m_pDefaultPrefs(NULL) + , m_splayer(pSplay) +{ +} + + +HSPClientContext::~HSPClientContext() +{ + Close(); +} + +void HSPClientContext::Init(IUnknown* pUnknown, + IHXPreferences* pPreferences, + char* pszGUID) +{ + //char* pszCipher = NULL; + + + m_pClientSink = new HSPClientAdviceSink(pUnknown, m_lClientIndex, m_splayer); + m_pErrorSink = new HSPErrorSink(pUnknown, m_splayer); + m_pAuthMgr = new HSPAuthenticationManager(m_splayer); + + if (m_pClientSink) + { + m_pClientSink->AddRef(); + } + + if (m_pErrorSink) + { + m_pErrorSink->AddRef(); + } + + if(m_pAuthMgr) + { + m_pAuthMgr->AddRef(); + } + + if (pPreferences) + { + m_pDefaultPrefs = pPreferences; + m_pDefaultPrefs->AddRef(); + } + + if (pszGUID && *pszGUID) + { + // Encode GUID + // TODO: find/implement Cipher + //pszCipher = Cipher(pszGUID); + //SafeStrCpy(m_pszGUID, pszCipher, 256); + } + else + { + m_pszGUID[0] = '\0'; + } +} + +void HSPClientContext::Close() +{ + HX_RELEASE(m_pClientSink); + HX_RELEASE(m_pErrorSink); + HX_RELEASE(m_pAuthMgr); + HX_RELEASE(m_pDefaultPrefs); +} + + + +// *** IUnknown methods *** + +///////////////////////////////////////////////////////////////////////// +// Method: +// IUnknown::QueryInterface +// Purpose: +// Implement this to export the interfaces supported by your +// object. +// +STDMETHODIMP HSPClientContext::QueryInterface(REFIID riid, void** ppvObj) +{ + if (IsEqualIID(riid, IID_IUnknown)) + { + AddRef(); + *ppvObj = this; + return HXR_OK; + } + else if (IsEqualIID(riid, IID_IHXPreferences)) + { + AddRef(); + *ppvObj = (IHXPreferences*)this; + return HXR_OK; + } + else if (m_pClientSink && + m_pClientSink->QueryInterface(riid, ppvObj) == HXR_OK) + { + return HXR_OK; + } + else if (m_pErrorSink && + m_pErrorSink->QueryInterface(riid, ppvObj) == HXR_OK) + { + return HXR_OK; + } + else if(m_pAuthMgr && + m_pAuthMgr->QueryInterface(riid, ppvObj) == HXR_OK) + { + return HXR_OK; + } + *ppvObj = NULL; + return HXR_NOINTERFACE; +} + +///////////////////////////////////////////////////////////////////////// +// Method: +// IUnknown::AddRef +// Purpose: +// Everyone usually implements this the same... feel free to use +// this implementation. +// +STDMETHODIMP_(ULONG32) HSPClientContext::AddRef() +{ + return InterlockedIncrement(&m_lRefCount); +} + +///////////////////////////////////////////////////////////////////////// +// Method: +// IUnknown::Release +// Purpose: +// Everyone usually implements this the same... feel free to use +// this implementation. +// +STDMETHODIMP_(ULONG32) HSPClientContext::Release() +{ + if (InterlockedDecrement(&m_lRefCount) > 0) + { + return m_lRefCount; + } + + delete this; + return 0; +} + + +// *** IHXPreference methods *** + +///////////////////////////////////////////////////////////////////////// +// Method: +// IHXPreferences::ReadPref +// Purpose: +// Read a Preference from the registry. +// +STDMETHODIMP +HSPClientContext::ReadPref(const char* pref_key, IHXBuffer*& buffer) +{ + HX_RESULT hResult = HXR_OK; + //char* pszCipher = NULL; + + if ((stricmp(pref_key, CLIENT_GUID_REGNAME) == 0) && + (*m_pszGUID)) + { + // Create a Buffer + //TODO: Implement an IHXBuffer + +// buffer = new CHXBuffer(); +// buffer->AddRef(); + + // Copy the encoded GUID into the buffer +// buffer->Set((UCHAR*)m_pszGUID, strlen(m_pszGUID) + 1); + } + else if (m_pDefaultPrefs) + { + hResult = m_pDefaultPrefs->ReadPref(pref_key, buffer); + } + else + { + hResult = HXR_NOTIMPL; + } + + return hResult; +} + +///////////////////////////////////////////////////////////////////////// +// Method: +// IHXPreferences::WritePref +// Purpose: +// Write a Preference to the registry. +// +STDMETHODIMP +HSPClientContext::WritePref(const char* pref_key, IHXBuffer* buffer) +{ + if (m_pDefaultPrefs) + { + return m_pDefaultPrefs->WritePref(pref_key, buffer); + } + else + { + return HXR_OK; + } +} + + diff --git a/amarok/src/engine/helix/helix-sp/hspcontext.h b/amarok/src/engine/helix/helix-sp/hspcontext.h new file mode 100644 index 00000000..2d4f5000 --- /dev/null +++ b/amarok/src/engine/helix/helix-sp/hspcontext.h @@ -0,0 +1,101 @@ +/* + * + * This software is released under the provisions of the GPL version 2. + * see file "COPYING". If that file is not available, the full statement + * of the license can be found at + * + * http://www.fsf.org/licensing/licenses/gpl.txt + * + * Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved. + * Portions (c) Paul Cifarelli 2005 + * + */ +#ifndef _HSPCONTEXT_ +#define _HSPCONTEXT_ + +struct IUnknown; +struct IHXPreferences; +struct IHXVolume; +class IHXCommonClassFactory; +class HSPClientAdviceSink; +class HSPErrorMessages; +class HSPAuthenticationManager; +class HelixSimplePlayer; + +class HSPEngineContext : public IHXPreferences +{ +public: + HSPEngineContext(HelixSimplePlayer *splayer, IHXCommonClassFactory *pCommonClassFactory); + virtual ~HSPEngineContext(); + void Init(IUnknown* /*IN*/ pUnknown); + void Close(); + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj); + + STDMETHOD_(ULONG32,AddRef) (THIS); + STDMETHOD_(ULONG32,Release) (THIS); + + /* + * IHXPreferences methods + */ + STDMETHOD(ReadPref) (THIS_ const char* pref_key, + IHXBuffer*& buffer); + STDMETHOD(WritePref) (THIS_ const char* pref_key, + IHXBuffer* buffer); + +private: + LONG32 m_lRefCount; + IHXCommonClassFactory *m_CommonClassFactory; + HelixSimplePlayer *m_splayer; +}; + +class HSPClientContext : public IHXPreferences +{ +private: + LONG32 m_lRefCount; + LONG32 m_lClientIndex; + + HSPClientAdviceSink* m_pClientSink; + HSPErrorSink* m_pErrorSink; + HSPAuthenticationManager* m_pAuthMgr; + IHXPreferences* m_pDefaultPrefs; + char m_pszGUID[256]; + HelixSimplePlayer *m_splayer; + +public: + + HSPClientContext(LONG32 lClientIndex, HelixSimplePlayer *pSplay); + virtual ~HSPClientContext(); + + unsigned long position() { return m_pClientSink ? m_pClientSink->position() : 0; } + unsigned long duration() { return m_pClientSink ? m_pClientSink->duration() : 0; } + + void Init(IUnknown* /*IN*/ pUnknown, + IHXPreferences* /*IN*/ pPreferences, + char* /*IN*/ pszGUID); + void Close(); + + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj); + + STDMETHOD_(ULONG32,AddRef) (THIS); + STDMETHOD_(ULONG32,Release) (THIS); + + /* + * IHXPreferences methods + */ + STDMETHOD(ReadPref) (THIS_ const char* pref_key, + IHXBuffer*& buffer); + STDMETHOD(WritePref) (THIS_ const char* pref_key, + IHXBuffer* buffer); +}; + +#endif diff --git a/amarok/src/engine/helix/helix-sp/hsperror.cpp b/amarok/src/engine/helix/helix-sp/hsperror.cpp new file mode 100644 index 00000000..3042ebf8 --- /dev/null +++ b/amarok/src/engine/helix/helix-sp/hsperror.cpp @@ -0,0 +1,194 @@ +/* + * + * This software is released under the provisions of the GPL version 2. + * see file "COPYING". If that file is not available, the full statement + * of the license can be found at + * + * http://www.fsf.org/licensing/licenses/gpl.txt + * + * Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved. + * Portions (c) Paul Cifarelli 2005 + * + */ +#include "hxcomm.h" +#include "hxerror.h" +#include "hxcore.h" +#include "hxbuffer.h" + +#include "hsperror.h" + +#include <stdio.h> + +#include "hxausvc.h" +#include "helix-sp.h" +#include "utils.h" + +HSPErrorSink::HSPErrorSink(IUnknown* pUnknown, HelixSimplePlayer *pSplay) + : m_lRefCount(0), + m_pPlayer(NULL), + m_splayer(pSplay) +{ + IHXClientEngine* pEngine = NULL; + pUnknown->QueryInterface(IID_IHXClientEngine, (void**)&pEngine ); + if( pEngine ) + { + IUnknown* pTmp = NULL; + pEngine->GetPlayer(0, pTmp); + m_pPlayer = (IHXPlayer*)pTmp; + } + + HX_RELEASE( pEngine ); + HX_ASSERT(m_pPlayer); +} + +HSPErrorSink::~HSPErrorSink() +{ + HX_RELEASE(m_pPlayer); +} + +// *** IUnknown methods *** + +///////////////////////////////////////////////////////////////////////// +// Method: +// IUnknown::QueryInterface +// Purpose: +// Implement this to export the interfaces supported by your +// object. +// +STDMETHODIMP HSPErrorSink::QueryInterface(REFIID riid, void** ppvObj) +{ + if (IsEqualIID(riid, IID_IUnknown)) + { + AddRef(); + *ppvObj = (IUnknown*)(IHXErrorSink*)this; + return HXR_OK; + } + else if (IsEqualIID(riid, IID_IHXErrorSink)) + { + AddRef(); + *ppvObj = (IHXErrorSink*) this; + return HXR_OK; + } + + *ppvObj = NULL; + return HXR_NOINTERFACE; +} + +///////////////////////////////////////////////////////////////////////// +// Method: +// IUnknown::AddRef +// Purpose: +// Everyone usually implements this the same... feel free to use +// this implementation. +// +STDMETHODIMP_(ULONG32) HSPErrorSink::AddRef() +{ + return InterlockedIncrement(&m_lRefCount); +} + +///////////////////////////////////////////////////////////////////////// +// Method: +// IUnknown::Release +// Purpose: +// Everyone usually implements this the same... feel free to use +// this implementation. +// +STDMETHODIMP_(ULONG32) HSPErrorSink::Release() +{ + if (InterlockedDecrement(&m_lRefCount) > 0) + { + return m_lRefCount; + } + + delete this; + return 0; +} + +/* + * IHXErrorSink methods + */ + +STDMETHODIMP +HSPErrorSink::ErrorOccurred(const UINT8 unSeverity, + const ULONG32 ulHXCode, + const ULONG32 ulUserCode, + const char* pUserString, + const char* pMoreInfoURL + ) +{ + char HXDefine[256]; /* Flawfinder: ignore */ + + // Store the code, so we can return it from main() + m_splayer->m_Error = ulHXCode; + + switch (unSeverity) + { + case HXLOG_NOTICE: + case HXLOG_INFO: + m_splayer->notifyUser(ulHXCode, + (pUserString && *pUserString) ? pUserString : "", + (pMoreInfoURL && *pMoreInfoURL) ? pMoreInfoURL : "" ); + break; + case HXLOG_WARNING: + case HXLOG_ERR: + case HXLOG_CRIT: + case HXLOG_ALERT: + case HXLOG_EMERG: + m_splayer->interruptUser(ulHXCode, + (pUserString && *pUserString) ? pUserString : "", + (pMoreInfoURL && *pMoreInfoURL) ? pMoreInfoURL : "" ); + break; + } + ConvertErrorToString(ulHXCode, HXDefine, 256); + + m_splayer->print2stdout("Report(%d, %ld, \"%s\", %ld, \"%s\", \"%s\")\n", + unSeverity, + ulHXCode, + (pUserString && *pUserString) ? pUserString : "(NULL)", + ulUserCode, + (pMoreInfoURL && *pMoreInfoURL) ? pMoreInfoURL : "(NULL)", + HXDefine); + + return HXR_OK; +} + +void +HSPErrorSink::ConvertErrorToString(const ULONG32 ulHXCode, char* pszBuffer, UINT32 ulBufLen) +{ + IHXErrorMessages* pErrMsg = NULL; + + if( !pszBuffer) + return; + + pszBuffer[0]='\0'; + + + HX_ASSERT(m_pPlayer); + if( m_pPlayer) + { + m_pPlayer->QueryInterface(IID_IHXErrorMessages, (void**)&pErrMsg); + if( pErrMsg ) + { + IHXBuffer* pMessage = pErrMsg->GetErrorText(ulHXCode); + if( pMessage ) + { + SafeStrCpy( pszBuffer, (const char*)pMessage->GetBuffer(), (int)ulBufLen); + pMessage->Release(); + } + else + m_splayer->print2stderr("NO expansion of error message available\n"); + + } + else + m_splayer->print2stderr("Unable to get Error Messages\n"); + } + + HX_RELEASE(pErrMsg); + + if( strlen(pszBuffer)==0 ) + { + SafeSprintf( pszBuffer, (int) ulBufLen, "Can't convert error code %lu - please find corresponding HXR code in common/include/hxresult.h", (unsigned long)ulHXCode ); + } + +} + diff --git a/amarok/src/engine/helix/helix-sp/hsperror.h b/amarok/src/engine/helix/helix-sp/hsperror.h new file mode 100644 index 00000000..f50e494d --- /dev/null +++ b/amarok/src/engine/helix/helix-sp/hsperror.h @@ -0,0 +1,70 @@ +/* + * + * This software is released under the provisions of the GPL version 2. + * see file "COPYING". If that file is not available, the full statement + * of the license can be found at + * + * http://www.fsf.org/licensing/licenses/gpl.txt + * + * Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved. + * Portions (c) Paul Cifarelli 2005 + * + */ + +#ifndef _HSPERROR_ +#define _HSPERROR_ + +struct IUnknown; +struct IHXErrorMessages; +struct IHXPlayer; +class HelixSimplePlayer; + +class HSPErrorSink : public IHXErrorSink +{ +public: + + HSPErrorSink(IUnknown* pUnknown, HelixSimplePlayer *pSplay); + virtual ~HSPErrorSink(); + + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj); + + STDMETHOD_(ULONG32,AddRef) (THIS); + + STDMETHOD_(ULONG32,Release) (THIS); + + /* + * IHXErrorSink methods + */ + + /************************************************************************ + * Method: + * IHXErrorSink::ErrorOccurred + * Purpose: + * After you have registered your error sink with an IHXErrorSinkControl + * (either in the server or player core) this method will be called to + * report an error, event, or status message. + * + * The meaning of the arguments is exactly as described in + * hxerror.h + */ + STDMETHOD(ErrorOccurred) (THIS_ + const UINT8 unSeverity, + const ULONG32 ulHXCode, + const ULONG32 ulUserCode, + const char* pUserString, + const char* pMoreInfoURL + ); + +protected: + LONG32 m_lRefCount; + IHXPlayer* m_pPlayer; + HelixSimplePlayer *m_splayer; + + void ConvertErrorToString (const ULONG32 ulHXCode, char* pszBuffer, UINT32 ulBufLen); +}; +#endif diff --git a/amarok/src/engine/helix/helix-sp/hsphook.cpp b/amarok/src/engine/helix/helix-sp/hsphook.cpp new file mode 100644 index 00000000..3434a77c --- /dev/null +++ b/amarok/src/engine/helix/helix-sp/hsphook.cpp @@ -0,0 +1,758 @@ +/* ********** + * + * This software is released under the provisions of the GPL version 2. + * see file "COPYING". If that file is not available, the full statement + * of the license can be found at + * + * http://www.fsf.org/licensing/licenses/gpl.txt + * + * Copyright (c) Paul Cifarelli 2005 + * Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved. + * PCM time-domain equalizer: + * (c) 2002 Felipe Rivera <liebremx at users sourceforge net> + * (c) 2004 Mark Kretschmann <markey@web.de> + * + * ********** */ +#include <math.h> +#include <stdlib.h> + +#include "hxcomm.h" +#include "hxcore.h" +#include "hxprefs.h" +#include "hxstrutl.h" +#include "hxvsrc.h" +#include "hxresult.h" +#include "hxausvc.h" +#include "helix-sp.h" +#ifndef HELIX_SW_VOLUME_INTERFACE +#include "gain.h" +#endif +#include "hsphook.h" +#include "iir_cf.h" // IIR filter coefficients +#include "hspalsadevice.h" + +#define SCOPESIZE 512 + +HSPPreMixAudioHook::HSPPreMixAudioHook(HelixSimplePlayer *player, int playerIndex, IHXAudioStream *pAudioStream, + bool fadein, unsigned long fadelength) : + m_Player(player), m_lRefCount(0), m_index(playerIndex), m_stream(pAudioStream), m_count(0), + m_gaintool(0), m_gaindb(0), m_fadein(fadein), m_fadeout(false), m_fadelength(fadelength) +{ + AddRef(); +} + +HSPPreMixAudioHook::~HSPPreMixAudioHook() +{ + if (m_gaintool) + gainFree(m_gaintool); +} + +STDMETHODIMP +HSPPreMixAudioHook::QueryInterface(REFIID riid, void**ppvObj) +{ + if(IsEqualIID(riid, IID_IUnknown)) + { + AddRef(); + *ppvObj = (IUnknown*)(IHXAudioHook *)this; + return HXR_OK; + } + else if(IsEqualIID(riid, IID_IHXAudioHook)) + { + AddRef(); + *ppvObj = (IHXAudioHook *)this; + return HXR_OK; + } + *ppvObj = NULL; + return HXR_NOINTERFACE; +} + +STDMETHODIMP_(UINT32) +HSPPreMixAudioHook::AddRef() +{ + return InterlockedIncrement(&m_lRefCount); +} + +STDMETHODIMP_(UINT32) +HSPPreMixAudioHook::Release() +{ + if (InterlockedDecrement(&m_lRefCount) > 0) + { + return m_lRefCount; + } + + delete this; + return 0; +} + +int HSPPreMixAudioHook::volumeize(unsigned char *data, unsigned char *outbuf, size_t len) +{ + gainFeed(data, outbuf, len, m_gaintool); + + return len; +} + +void HSPPreMixAudioHook::setFadeout(bool fadeout) +{ + m_fadeout = fadeout; + if (m_fadeout) + { + // the "time constant" (ms) is the time it takes to reach +/- 6db of the original + gainSetTimeConstant((float) m_fadelength / 8.0, m_gaintool); + gainSetSmoothdB(FADE_MIN_dB, m_gaintool); + } +} + +STDMETHODIMP HSPPreMixAudioHook::OnBuffer(HXAudioData *pAudioInData, HXAudioData *pAudioOutData) +{ + m_count++; + +#ifdef DEBUG_PURPOSES_ONLY + if (!(m_count % 100)) + { + m_Player->print2stderr("PRE: time: %d ", pAudioInData->ulAudioTime); + switch (pAudioInData->uAudioStreamType) + { + case INSTANTANEOUS_AUDIO: + m_Player->print2stderr(" INSTANTANEOUS_AUDIO "); + break; + case STREAMING_AUDIO: + m_Player->print2stderr(" STREAMING_AUDIO "); + break; + case TIMED_AUDIO: + m_Player->print2stderr(" TIMED_AUDIO "); + break; + case STREAMING_INSTANTANEOUS_AUDIO: + m_Player->print2stderr(" STREAMING_INSTANTANEOUS_AUDIO "); + break; + } + m_Player->print2stderr("pAudioOutData %lx, data %lx\n", pAudioOutData, pAudioOutData->pData); + } +#endif + + unsigned char *outbuf; + IHXBuffer *ibuf; + unsigned long len; + unsigned char *data; + + pAudioInData->pData->Get(data, len); + + // provide a little margin to prevent a slight but noticeable jump in vol when the fadein ends + if ((m_fadein && pAudioInData->ulAudioTime < 2*m_fadelength) || m_fadeout) + { + m_Player->pCommonClassFactory->CreateInstance(CLSID_IHXBuffer, (void **) &ibuf); + if (ibuf) + { + ibuf->SetSize(len); + outbuf = ibuf->GetBuffer(); + + len = volumeize(data, outbuf, len); + + pAudioOutData->pData = ibuf; + pAudioOutData->ulAudioTime = pAudioInData->ulAudioTime; + pAudioOutData->uAudioStreamType = pAudioInData->uAudioStreamType; + } + } + + return 0; +} + +STDMETHODIMP HSPPreMixAudioHook::OnInit(HXAudioFormat *pFormat) +{ + m_Player->print2stderr("PRE MIX HOOK OnInit AudioFormat: ch %d, bps %d, sps %ld, mbs %d\n", pFormat->uChannels, + pFormat->uBitsPerSample, + pFormat->ulSamplesPerSec, + pFormat->uMaxBlockSize); + + m_format = *pFormat; + + int bps = pFormat->uBitsPerSample / 8; + m_gaintool = gainInit(pFormat->ulSamplesPerSec, pFormat->uChannels, bps); + gainSetImmediatedB(0, m_gaintool); + + if (m_fadein) + { + gainSetImmediatedB(FADE_MIN_dB, m_gaintool); + // the "time constant" (ms) is the time it takes to reach -6db of the target + gainSetTimeConstant((float) m_fadelength / 2.0, m_gaintool); + gainSetSmoothdB(0, m_gaintool); + } + + return 0; +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +HSPPostProcessor::HSPPostProcessor(HelixSimplePlayer *player, int playerIndex) : + m_Player(player), m_lRefCount(0), m_index(playerIndex), m_count(0), m_item(0), + m_current(0), m_prevtime(0), m_i(0), m_j(2), m_k(1) +#ifndef HELIX_SW_VOLUME_INTERFACE + , m_gaintool(0), m_gaindB(0.0) +#endif +{ + AddRef(); + memset(&m_format, 0, sizeof(m_format)); + + // zero the data_history, to eliminate the buzz on playing the first track after enabling the equalizer + memset(&data_history, 0, sizeof(data_history)); +} + +HSPPostProcessor::~HSPPostProcessor() +{ +#ifndef HELIX_SW_VOLUME_INTERFACE + if (m_gaintool) + gainFree(m_gaintool); +#endif +} + +STDMETHODIMP +HSPPostProcessor::QueryInterface(REFIID riid, void**ppvObj) +{ + if(IsEqualIID(riid, IID_IUnknown)) + { + AddRef(); + *ppvObj = (IUnknown*)(IHXAudioHook *)this; + return HXR_OK; + } + else if(IsEqualIID(riid, IID_IHXAudioHook)) + { + AddRef(); + *ppvObj = (IHXAudioHook *)this; + return HXR_OK; + } + *ppvObj = NULL; + return HXR_NOINTERFACE; +} + +STDMETHODIMP_(UINT32) +HSPPostProcessor::AddRef() +{ + return InterlockedIncrement(&m_lRefCount); +} + +STDMETHODIMP_(UINT32) +HSPPostProcessor::Release() +{ + if (InterlockedDecrement(&m_lRefCount) > 0) + { + return m_lRefCount; + } + + delete this; + return 0; +} + +STDMETHODIMP HSPPostProcessor::OnBuffer(HXAudioData *pAudioInData, HXAudioData *pAudioOutData) +{ + unsigned long len; + unsigned char *data; + + pAudioInData->pData->Get(data, len); + + m_count++; + +#ifdef DEBUG_PURPOSES_ONLY + if (!(m_count % 100)) + { + m_Player->print2stderr("POST: time: %d ", pAudioInData->ulAudioTime); + switch (pAudioInData->uAudioStreamType) + { + case INSTANTANEOUS_AUDIO: + m_Player->print2stderr(" INSTANTANEOUS_AUDIO "); + break; + case STREAMING_AUDIO: + m_Player->print2stderr(" STREAMING_AUDIO "); + break; + case TIMED_AUDIO: + m_Player->print2stderr(" TIMED_AUDIO "); + break; + case STREAMING_INSTANTANEOUS_AUDIO: + m_Player->print2stderr(" STREAMING_INSTANTANEOUS_AUDIO "); + break; + } + m_Player->print2stderr("len %d\n", len); + m_Player->print2stderr("pAudioOutData %lx, data %lx\n", pAudioOutData, pAudioOutData->pData); + + m_Player->print2stderr("Volume is %d\n",m_Player->getDirectHWVolume()); + } + +#endif + + +#ifndef HELIX_SW_VOLUME_INTERFACE + unsigned char *outbuf; + IHXBuffer *ibuf; + + m_Player->pCommonClassFactory->CreateInstance(CLSID_IHXBuffer, (void **) &ibuf); + if (ibuf) + { + ibuf->SetSize(len); + outbuf = ibuf->GetBuffer(); + + // equalize + if (m_Player->ppctrl[m_index]->volume && m_Player->isEQenabled() && m_format.uBitsPerSample == 16) + { + equalize(data, outbuf, len); + + // finally adjust the volume + len = volumeize(outbuf, len); + } + else + // finally adjust the volume + len = volumeize(data, outbuf, len); + + pAudioOutData->pData = ibuf; + pAudioOutData->ulAudioTime = pAudioInData->ulAudioTime; + pAudioOutData->uAudioStreamType = pAudioInData->uAudioStreamType; + } +#else + // equalize + if (m_Player->ppctrl[m_index]->volume && m_Player->isEQenabled() && m_format.uBitsPerSample == 16) + { + unsigned char *outbuf; + IHXBuffer *ibuf; + + m_Player->pCommonClassFactory->CreateInstance(CLSID_IHXBuffer, (void **) &ibuf); + if (ibuf) + { + ibuf->SetSize(len); + outbuf = ibuf->GetBuffer(); + equalize(data, outbuf, len); + pAudioOutData->pData = ibuf; + pAudioOutData->ulAudioTime = pAudioInData->ulAudioTime; + pAudioOutData->uAudioStreamType = pAudioInData->uAudioStreamType; + pAudioInData->pData->Release(); + } + } +#endif + + return 0; +} + +STDMETHODIMP HSPPostProcessor::OnInit(HXAudioFormat *pFormat) +{ + m_format = *pFormat; + m_count = 0; + m_prevtime = 0; + + // set the filter coefficients, in case we need to use the equalizer + switch(pFormat->ulSamplesPerSec) + { + case 8000: + //iir_cf = iir_cf10_8000; <-- doesn't work + iir_cf = iir_cf10_11k_11025; // works + break; + + case 11025: + //iir_cf = iir_cf10_11025; <-- not tested (cant get an encoder to give me this sfreq) + iir_cf = iir_cf10_11k_11025; // not tested, but works for 8k + break; + + case 16000: + //iir_cf = iir_cf10_16000; <-- doesn't work + iir_cf = iir_cf10_22k_22050; // works + break; + + case 22050: + //iir_cf = iir_cf10_22050; + iir_cf = iir_cf10_22k_22050; // this set actually works... + break; + + case 32000: + iir_cf = iir_cf10_32000; // works + break; + + case 48000: + iir_cf = iir_cf10_48000; // not tested + break; + + case 44100: + default: + iir_cf = iir_cf10_44100; // works + break; + } + + m_i = 0; + m_j = 2; + m_k = 1; + + memset(&data_history, 0, sizeof(data_history)); + +#ifndef HELIX_SW_VOLUME_INTERFACE + // setup the gain tool for volume + if (m_gaintool) + gainFree(m_gaintool); + + int bps = pFormat->uBitsPerSample / 8; + m_gaintool = gainInit(pFormat->ulSamplesPerSec, pFormat->uChannels, bps); + setGain(m_Player->ppctrl[m_index]->volume); +#endif + + return 0; +} + + +#ifndef HELIX_SW_VOLUME_INTERFACE +void HSPPostProcessor::setGain(int volume) +{ + if (m_gaintool) + { + if (volume == 0) + gainSetMute(m_gaintool); + else + { + //m_gaindB = GAIN_MIN_dB + (GAIN_MAX_dB - GAIN_MIN_dB) * (float) volume / 100.0; + //m_Player->print2stderr("GAIN set to %f\n", m_gaindB); + //gainSetImmediatedB(m_gaindB, m_gaintool); + + gainSetImmediate( (float) volume / 100.0, m_gaintool ); + } + } +} +#endif + + +void HSPPostProcessor::scopeify(unsigned long time, unsigned char *data, size_t len) +{ + int bytes_per_sample = m_format.uBitsPerSample / 8; + + // TODO: 32 bit samples + if ( (bytes_per_sample != 1 && bytes_per_sample != 2) ) + return; // no scope + + unsigned long scopebuf_timeinc = (unsigned long)(1000.0 * (double)len / ((double)m_format.ulSamplesPerSec * (double)bytes_per_sample)); + DelayQueue *item = new DelayQueue(len); + memcpy(item->buf, data, len); + item->len = len; + item->time = time; + item->etime = time + scopebuf_timeinc; + + m_prevtime = item->etime; + + item->nchan = m_format.uChannels; + item->bps = bytes_per_sample; + item->spb = len / item->nchan; + item->spb /= bytes_per_sample; + item->tps = (double) scopebuf_timeinc / (double) item->spb; + m_Player->addScopeBuf(item, m_index); +} + + +void HSPPostProcessor::updateEQgains(int pamp, vector<int> &equalizerGains) +{ + for (int i=0; i<EQ_CHANNELS; i++) + { + preamp[i] = (float) pamp * 0.01; + + + for (int j=0; j<EQ_MAX_BANDS; j++) + gain[j][i] = (float)(equalizerGains[j]) * 0.012 - 0.2; + } +} + + +void HSPPostProcessor::equalize(unsigned char *inbuf, unsigned char *outbuf, size_t length) +{ + int index, band, channel; + int tempint, halflength; + float out[EQ_CHANNELS], pcm[EQ_CHANNELS]; + short int *data = (short int *) inbuf; + short int *dataout = (short int *) outbuf; + + /** + * IIR filter equation is + * y[n] = 2 * (alpha*(x[n]-x[n-2]) + gamma*y[n-1] - beta*y[n-2]) + * + * NOTE: The 2 factor was introduced in the coefficients to save + * a multiplication + * + * This algorithm cascades two filters to get nice filtering + * at the expense of extra CPU cycles + */ + /* 16bit, 2 bytes per sample, so divide by two the length of + * the buffer (length is in bytes) + */ + halflength = (length >> 1); + for (index = 0; index < halflength; index+=m_format.uChannels) + { + /* For each channel */ + for (channel = 0; channel < m_format.uChannels; channel++) + { + pcm[channel] = (float) data[index+channel]; + + /* Preamp gain */ + pcm[channel] *= preamp[channel]; + + out[channel] = 0.; + /* For each band */ + for (band = 0; band < BAND_NUM; band++) + { + /* Store Xi(n) */ + data_history[band][channel].x[m_i] = pcm[channel]; + /* Calculate and store Yi(n) */ + data_history[band][channel].y[m_i] = + ( + /* = alpha * [x(n)-x(n-2)] */ + iir_cf[band].alpha * ( data_history[band][channel].x[m_i] + - data_history[band][channel].x[m_k]) + /* + gamma * y(n-1) */ + + iir_cf[band].gamma * data_history[band][channel].y[m_j] + /* - beta * y(n-2) */ + - iir_cf[band].beta * data_history[band][channel].y[m_k] + ); + /* + * The multiplication by 2.0 was 'moved' into the coefficients to save + * CPU cycles here */ + /* Apply the gain */ + out[channel] += data_history[band][channel].y[m_i]*gain[band][channel]; // * 2.0; + } /* For each band */ + + /* Volume stuff + Scale down original PCM sample and add it to the filters + output. This substitutes the multiplication by 0.25 + Go back to use the floating point multiplication before the + conversion to give more dynamic range + */ + out[channel] += pcm[channel]*0.25; + + /* Round and convert to integer */ + tempint = lrintf(out[channel]); + + /* Limit the output */ + if (tempint < -32768) + dataout[index+channel] = -32768; + else if (tempint > 32767) + dataout[index+channel] = 32767; + else + dataout[index+channel] = tempint; + } /* For each channel */ + + m_i++; m_j++; m_k++; + + /* Wrap around the indexes */ + if (m_i == 3) m_i = 0; + else if (m_j == 3) m_j = 0; + else m_k = 0; + }/* For each pair of samples */ +} + + +#ifndef HELIX_SW_VOLUME_INTERFACE +int HSPPostProcessor::volumeize(unsigned char *data, size_t len) +{ + gainFeed(data, data, len, m_gaintool); + + return len; +} + + +int HSPPostProcessor::volumeize(unsigned char *data, unsigned char *outbuf, size_t len) +{ + gainFeed(data, outbuf, len, m_gaintool); + + return len; +} +#endif + +HSPPostMixAudioHook::HSPPostMixAudioHook(HelixSimplePlayer *player, int playerIndex) : + m_Player(player), m_index(playerIndex), m_lRefCount(0), m_processor(0) +{ + AddRef(); + + m_Player->print2stderr("POST MIX HOOK CTOR\n"); + + m_processor = new HSPPostProcessor(player, playerIndex); +} + + +HSPPostMixAudioHook::~HSPPostMixAudioHook() +{ + m_processor->Release(); +} + +/* + * IUnknown methods + */ +STDMETHODIMP +HSPPostMixAudioHook::QueryInterface(REFIID riid, void** ppvObj) +{ + if(IsEqualIID(riid, IID_IUnknown)) + { + AddRef(); + *ppvObj = (IUnknown*)(IHXAudioHook *)this; + return HXR_OK; + } + else if(IsEqualIID(riid, IID_IHXAudioHook)) + { + AddRef(); + *ppvObj = (IHXAudioHook *)this; + return HXR_OK; + } + *ppvObj = NULL; + return HXR_NOINTERFACE; +} + + +STDMETHODIMP_(UINT32) +HSPPostMixAudioHook::AddRef() +{ + return InterlockedIncrement(&m_lRefCount); +} + +STDMETHODIMP_(UINT32) +HSPPostMixAudioHook::Release() +{ + if (InterlockedDecrement(&m_lRefCount) > 0) + { + return m_lRefCount; + } + + m_Player->print2stderr("DELETING POST MIX HOOK index %d\n", m_index); + delete this; + return 0; +} + +STDMETHODIMP HSPPostMixAudioHook::OnBuffer(HXAudioData *pAudioInData, HXAudioData */*pAudioOutData*/) +{ + unsigned long len; + unsigned char *data; + + pAudioInData->pData->Get(data, len); + + // feed the visualizations, if this is a local file (otherwise do it in the FinalHook) + if (m_Player->isLocal(m_index)) + m_processor->scopeify(pAudioInData->ulAudioTime, data, len); + + return 0; +} + +STDMETHODIMP HSPPostMixAudioHook::OnInit(HXAudioFormat *pFormat) +{ + m_Player->print2stderr("POST MIX HOOK OnInit AudioFormat: idx %d ch %d, bps %d, sps %ld, mbs %d\n", m_index, pFormat->uChannels, + pFormat->uBitsPerSample, + pFormat->ulSamplesPerSec, + pFormat->uMaxBlockSize); + + + return (m_processor->OnInit(pFormat)); +} + +void HSPPostMixAudioHook::updateEQgains(int preamp, vector<int> &equalizerGains) +{ + m_processor->updateEQgains(preamp, equalizerGains); +} + + +#ifndef HELIX_SW_VOLUME_INTERFACE + +void HSPPostMixAudioHook::setGain(int volume) +{ + m_processor->setGain(volume); +} + +#endif + + +HSPFinalAudioHook::HSPFinalAudioHook(HelixSimplePlayer *player) : m_Player(player), m_lRefCount(0), m_processor(0) +{ + AddRef(); + m_processor = new HSPPostProcessor(player, 0); +} + + +HSPFinalAudioHook::~HSPFinalAudioHook() +{ + m_processor->Release(); +} + +/* + * IUnknown methods + */ +STDMETHODIMP +HSPFinalAudioHook::QueryInterface(REFIID riid, void** ppvObj) +{ + if(IsEqualIID(riid, IID_IUnknown)) + { + AddRef(); + *ppvObj = (IUnknown*)(IHXAudioHook *)this; + return HXR_OK; + } + else if(IsEqualIID(riid, IID_IHXAudioHook)) + { + AddRef(); + *ppvObj = (IHXAudioHook *)this; + return HXR_OK; + } + *ppvObj = NULL; + return HXR_NOINTERFACE; +} + + +STDMETHODIMP_(UINT32) +HSPFinalAudioHook::AddRef() +{ + return InterlockedIncrement(&m_lRefCount); +} + +STDMETHODIMP_(UINT32) +HSPFinalAudioHook::Release() +{ + if (InterlockedDecrement(&m_lRefCount) > 0) + { + return m_lRefCount; + } + + delete this; + return 0; +} + +STDMETHODIMP HSPFinalAudioHook::OnBuffer(HXAudioData *pAudioInData, HXAudioData *pAudioOutData) +{ + unsigned long len; + unsigned char *data; + + pAudioInData->pData->Get(data, len); + + // feed the visualizations, if this is a live stream + bool anyLocal = false; + int i = 0; + while (i< m_Player->numPlayers()) + { + if (m_Player->isPlaying(i)) + m_processor->setIndex(i); // put the buffers on the queue of the player that's playing + if (anyLocal = m_Player->isLocal(i)) + break; + i++; + } + if (!anyLocal) + m_processor->scopeify(pAudioInData->ulAudioTime, data, len); + + return (m_processor->OnBuffer(pAudioInData, pAudioOutData)); +} + +STDMETHODIMP HSPFinalAudioHook::OnInit(HXAudioFormat *pFormat) +{ + m_Player->print2stderr("FINAL HOOK OnInit AudioFormat: ch %d, bps %d, sps %ld, mbs %d\n", pFormat->uChannels, + pFormat->uBitsPerSample, + pFormat->ulSamplesPerSec, + pFormat->uMaxBlockSize); + + return (m_processor->OnInit(pFormat)); +} + +void HSPFinalAudioHook::updateEQgains(int preamp, vector<int> &equalizerGains) +{ + m_processor->updateEQgains(preamp, equalizerGains); +} + + +#ifndef HELIX_SW_VOLUME_INTERFACE + +void HSPFinalAudioHook::setGain(int volume) +{ + m_processor->setGain(volume); +} + +#endif + + diff --git a/amarok/src/engine/helix/helix-sp/hsphook.h b/amarok/src/engine/helix/helix-sp/hsphook.h new file mode 100644 index 00000000..6983124f --- /dev/null +++ b/amarok/src/engine/helix/helix-sp/hsphook.h @@ -0,0 +1,250 @@ +/* ********** + * + * This software is released under the provisions of the GPL version 2. + * see file "COPYING". If that file is not available, the full statement + * of the license can be found at + * + * http://www.fsf.org/licensing/licenses/gpl.txt + * + * Copyright (c) Paul Cifarelli 2005 + * Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved. + * PCM time-domain equalizer: + * (c) 2002 Felipe Rivera <liebremx at users sourceforge net> + * (c) 2004 Mark Kretschmann <markey@web.de> + * + * ********** */ +#ifndef _HSPHOOK_H_INCLUDED_ +#define _HSPHOOK_H_INCLUDED_ + +struct GAIN_STATE; + +#define FADE_MIN_dB -120 + +class HSPPreMixAudioHook : public IHXAudioHook +{ +public: + HSPPreMixAudioHook(HelixSimplePlayer *player, int playerIndex, IHXAudioStream *pAudioStream, + bool fadein = false, unsigned long fadelength = 0); + virtual ~HSPPreMixAudioHook(); + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj); + STDMETHOD_(ULONG32,AddRef) (THIS); + STDMETHOD_(ULONG32,Release) (THIS); + /* + * IHXAudioHook methods + */ + STDMETHOD(OnBuffer) (THIS_ + HXAudioData *pAudioInData, + HXAudioData *pAudioOutData); + STDMETHOD(OnInit) (THIS_ + HXAudioFormat *pFormat); + + void setFadeout(bool fadeout); + void setFadelength(unsigned long fadelength) { m_fadelength = fadelength; } + +private: + HSPPreMixAudioHook(); + + HelixSimplePlayer *m_Player; + LONG32 m_lRefCount; + int m_index; + IHXAudioStream *m_stream; + HXAudioFormat m_format; + int m_count; + + GAIN_STATE *m_gaintool; + float m_gaindb; + bool m_fadein; + bool m_fadeout; + unsigned long m_fadelength; + + int volumeize(unsigned char *data, unsigned char *outbuf, size_t len); +}; + + + +#define BAND_NUM 10 +#define EQ_MAX_BANDS 10 +#define EQ_CHANNELS 2 // Helix DNA currently only supports stereo + +// Floating point +typedef struct +{ + float beta; + float alpha; + float gamma; +} sIIRCoefficients; + +/* Coefficient history for the IIR filter */ +typedef struct +{ + float x[3]; /* x[n], x[n-1], x[n-2] */ + float y[3]; /* y[n], y[n-1], y[n-2] */ +} sXYData; + + +struct DelayQueue; + +class HSPPostProcessor : public IHXAudioHook +{ +public: + HSPPostProcessor(HelixSimplePlayer *player, int playerIndex); + virtual ~HSPPostProcessor(); + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj); + STDMETHOD_(ULONG32,AddRef) (THIS); + STDMETHOD_(ULONG32,Release) (THIS); + /* + * IHXAudioHook methods + */ + STDMETHOD(OnBuffer) (THIS_ + HXAudioData *pAudioInData, + HXAudioData *pAudioOutData); + STDMETHOD(OnInit) (THIS_ + HXAudioFormat *pFormat); + + void updateEQgains(int preamp, vector<int> &equalizerGains); + + void scopeify(unsigned long time, unsigned char *data, size_t len); + +#ifndef HELIX_SW_VOLUME_INTERFACE + void setGain(int volume); +#endif + + void setIndex(int playerIndex) { m_index = playerIndex; } + +private: + HSPPostProcessor(); + + void equalize(unsigned char *datain, unsigned char *dataout, size_t len); +#ifndef HELIX_SW_VOLUME_INTERFACE + int volumeize(unsigned char *data, size_t len); + int volumeize(unsigned char *data, unsigned char *outbuf, size_t len); + // returns samples (not bytes) + int unpack(unsigned char *data, size_t len, /*out*/ INT32 *signal); + // returns bytes (siglen is in samples) + int pack(INT32 *signal, size_t siglen, /*out*/ unsigned char *data); +#endif + + HelixSimplePlayer *m_Player; + LONG32 m_lRefCount; + int m_index; + HXAudioFormat m_format; + int m_count; + + // scope + struct DelayQueue *m_item; + int m_current; + unsigned long m_prevtime; + + // equalizer + + // Gain for each band + // values should be between -0.2 and 1.0 + float gain[EQ_MAX_BANDS][EQ_CHANNELS] __attribute__((aligned)); + // Volume gain + // values should be between 0.0 and 1.0 + float preamp[EQ_CHANNELS] __attribute__((aligned)); + // Coefficients + sIIRCoefficients* iir_cf; + sXYData data_history[EQ_MAX_BANDS][EQ_CHANNELS] __attribute__((aligned)); + // history indices + int m_i; + int m_j; + int m_k; + +#ifndef HELIX_SW_VOLUME_INTERFACE + // volume stuff + GAIN_STATE *m_gaintool; + float m_gaindB; +#endif +}; + + +class HSPPostMixAudioHook : public IHXAudioHook +{ +public: + HSPPostMixAudioHook(HelixSimplePlayer *player, int playerIndex); + virtual ~HSPPostMixAudioHook(); + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj); + STDMETHOD_(ULONG32,AddRef) (THIS); + STDMETHOD_(ULONG32,Release) (THIS); + /* + * IHXAudioHook methods + */ + STDMETHOD(OnBuffer) (THIS_ + HXAudioData *pAudioInData, + HXAudioData *pAudioOutData); + STDMETHOD(OnInit) (THIS_ + HXAudioFormat *pFormat); + + void updateEQgains(int preamp, vector<int> &equalizerGains); + +#ifndef HELIX_SW_VOLUME_INTERFACE + void setGain(int volume); +#endif + +private: + HSPPostMixAudioHook(); + + HelixSimplePlayer *m_Player; + int m_index; + LONG32 m_lRefCount; + + HSPPostProcessor *m_processor; +}; + + +class HSPFinalAudioHook : public IHXAudioHook +{ +public: + HSPFinalAudioHook(HelixSimplePlayer *player); + virtual ~HSPFinalAudioHook(); + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj); + STDMETHOD_(ULONG32,AddRef) (THIS); + STDMETHOD_(ULONG32,Release) (THIS); + /* + * IHXAudioHook methods + */ + STDMETHOD(OnBuffer) (THIS_ + HXAudioData *pAudioInData, + HXAudioData *pAudioOutData); + STDMETHOD(OnInit) (THIS_ + HXAudioFormat *pFormat); + + + void updateEQgains(int preamp, vector<int> &equalizerGains); + +#ifndef HELIX_SW_VOLUME_INTERFACE + void setGain(int volume); +#endif + +private: + HSPFinalAudioHook(); + + HelixSimplePlayer *m_Player; + LONG32 m_lRefCount; + + HSPPostProcessor *m_processor; +}; + + +#endif diff --git a/amarok/src/engine/helix/helix-sp/hspvoladvise.h b/amarok/src/engine/helix/helix-sp/hspvoladvise.h new file mode 100644 index 00000000..20b0572b --- /dev/null +++ b/amarok/src/engine/helix/helix-sp/hspvoladvise.h @@ -0,0 +1,52 @@ +/* + * + * This software is released under the provisions of the GPL version 2. + * see file "COPYING". If that file is not available, the full statement + * of the license can be found at + * + * http://www.fsf.org/licensing/licenses/gpl.txt + * + * Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved. + * Portions (c) Paul Cifarelli 2005 + * + */ + +#ifndef _HSPVOLADVISE_INCLUDED_ +#define _HSPVOLADVISE_INCLUDED_ + +class HelixSimplePlayerVolumeAdvice : public IHXVolumeAdviseSink +{ +public: + HelixSimplePlayerVolumeAdvice(HelixSimplePlayer *player, int playerIndex) : m_Player(player),m_index(playerIndex),m_lRefCount(0) {} + virtual ~HelixSimplePlayerVolumeAdvice() {} + + /* + * IUnknown methods + */ + STDMETHOD(QueryInterface) (THIS_ + REFIID riid, + void** ppvObj); + + STDMETHOD_(ULONG32,AddRef) (THIS); + + STDMETHOD_(ULONG32,Release) (THIS); + + /* + * IHXVolumeAdviceSink methods + */ + STDMETHOD(OnVolumeChange) (THIS_ + const UINT16 uVolume + ); + STDMETHOD(OnMuteChange) (THIS_ + const BOOL bMute + ); + +private: + HelixSimplePlayerVolumeAdvice(); + HelixSimplePlayer *m_Player; + int m_index; + LONG32 m_lRefCount; + LONG32 m_lClientIndex; +}; + +#endif diff --git a/amarok/src/engine/helix/helix-sp/iids.cpp b/amarok/src/engine/helix/helix-sp/iids.cpp new file mode 100644 index 00000000..c199cc2f --- /dev/null +++ b/amarok/src/engine/helix/helix-sp/iids.cpp @@ -0,0 +1,21 @@ + +/* + * + * This software is released under the provisions of the GPL version 2. + * see file "COPYING". If that file is not available, the full statement + * of the license can be found at + * + * http://www.fsf.org/licensing/licenses/gpl.txt + * + * Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved. + * Portions (c) Paul Cifarelli 2005 + */ + +// define all guids here once... +#define INITGUID +#define NCIHACK +#include "hxtypes.h" +#include "hxcom.h" +#include "hxiids.h" +#include "hxpiids.h" + diff --git a/amarok/src/engine/helix/helix-sp/iir_cf.h b/amarok/src/engine/helix/helix-sp/iir_cf.h new file mode 100644 index 00000000..a4958922 --- /dev/null +++ b/amarok/src/engine/helix/helix-sp/iir_cf.h @@ -0,0 +1,546 @@ +// IIR filter coefficient tables + +/* BETA, ALPHA, GAMMA */ +static sIIRCoefficients iir_cf10_11k_11025[] __attribute__((aligned)) = { + /* 31 Hz*/ +{ 9.8758524689e-01, 6.2073765555e-03, 1.9872750693e+00 }, + /* 62 Hz*/ +{ 9.7532461998e-01, 1.2337690008e-02, 1.9740916593e+00 }, + /* 125 Hz*/ +{ 9.5087485437e-01, 2.4562572817e-02, 1.9459267562e+00 }, + /* 250 Hz*/ +{ 9.0416308662e-01, 4.7918456688e-02, 1.8848691023e+00 }, + /* 500 Hz*/ +{ 8.1751373987e-01, 9.1243130064e-02, 1.7442229115e+00 }, + /* 1k Hz*/ +{ 6.6840529852e-01, 1.6579735074e-01, 1.4047189863e+00 }, + /* 2k Hz*/ +{ 4.4858358977e-01, 2.7570820511e-01, 6.0517475334e-01 }, + /* 3k Hz*/ +{ 3.1012671838e-01, 3.4493664081e-01, -1.8141012760e-01 }, + /* 4k Hz*/ +{ 2.4198119087e-01, 3.7900940457e-01, -8.0845085113e-01 }, + /* 5.5k Hz*/ +{ 3.3453245058e-01, 3.3273377471e-01, -1.3344985880e+00 }, +}; +static sIIRCoefficients iir_cf10_22k_22050[] __attribute__((aligned)) = { + /* 31 Hz*/ +{ 9.9377323686e-01, 3.1133815717e-03, 1.9936954495e+00 }, + /* 62 Hz*/ +{ 9.8758524689e-01, 6.2073765555e-03, 1.9872750693e+00 }, + /* 125 Hz*/ +{ 9.7512812040e-01, 1.2435939802e-02, 1.9738753198e+00 }, + /* 250 Hz*/ +{ 9.5087485437e-01, 2.4562572817e-02, 1.9459267562e+00 }, + /* 500 Hz*/ +{ 9.0416308662e-01, 4.7918456688e-02, 1.8848691023e+00 }, + /* 1k Hz*/ +{ 8.1751373987e-01, 9.1243130064e-02, 1.7442229115e+00 }, + /* 2k Hz*/ +{ 6.6840529852e-01, 1.6579735074e-01, 1.4047189863e+00 }, + /* 4k Hz*/ +{ 4.4858358977e-01, 2.7570820511e-01, 6.0517475334e-01 }, + /* 8k Hz*/ +{ 2.4198119087e-01, 3.7900940457e-01, -8.0845085113e-01 }, + /* 11k Hz*/ +{ 3.3453245058e-01, 3.3273377471e-01, -1.3344985880e+00 }, +}; +#ifdef _UNUSED_ +static sIIRCoefficients iir_cforiginal10_44100[] __attribute__((aligned)) = { + /* 60 Hz*/ +{ 9.9397349481e-01, 3.0132525945e-03, 1.9939006377e+00 }, + /* 170 Hz*/ +{ 9.8301906957e-01, 8.4904652142e-03, 1.9824374272e+00 }, + /* 310 Hz*/ +{ 9.6925150511e-01, 1.5374247445e-02, 1.9673310395e+00 }, + /* 600 Hz*/ +{ 9.4134330314e-01, 2.9328348430e-02, 1.9342541736e+00 }, + /* 1k Hz*/ +{ 9.0416308662e-01, 4.7918456688e-02, 1.8848691023e+00 }, + /* 3k Hz*/ +{ 7.3918401009e-01, 1.3040799496e-01, 1.5827185140e+00 }, + /* 6k Hz*/ +{ 5.4688945509e-01, 2.2655527245e-01, 1.0152665639e+00 }, + /* 12k Hz*/ +{ 3.1012671838e-01, 3.4493664081e-01, -1.8141012760e-01 }, + /* 14k Hz*/ +{ 2.6712322292e-01, 3.6643838854e-01, -5.2115143966e-01 }, + /* 16k Hz*/ +{ 2.4198119087e-01, 3.7900940457e-01, -8.0845085113e-01 }, +}; +static sIIRCoefficients iir_cforiginal10_48000[] __attribute__((aligned)) = { + /* 60 Hz*/ +{ 9.9446178985e-01, 2.7691050731e-03, 1.9944002760e+00 }, + /* 170 Hz*/ +{ 9.8438794122e-01, 7.8060293913e-03, 1.9838966333e+00 }, + /* 310 Hz*/ +{ 9.7171413384e-01, 1.4142933081e-02, 1.9700909975e+00 }, + /* 600 Hz*/ +{ 9.4597793866e-01, 2.7011030668e-02, 1.9399791381e+00 }, + /* 1k Hz*/ +{ 9.1159452679e-01, 4.4202736607e-02, 1.8952405706e+00 }, + /* 3k Hz*/ +{ 7.5755317065e-01, 1.2122341468e-01, 1.6237674017e+00 }, + /* 6k Hz*/ +{ 5.7422402554e-01, 2.1288798723e-01, 1.1131444836e+00 }, + /* 12k Hz*/ +{ 3.3730698905e-01, 3.3134650547e-01, 8.1883731790e-17 }, + /* 14k Hz*/ +{ 2.8947322018e-01, 3.5526338991e-01, -3.3374022753e-01 }, + /* 16k Hz*/ +{ 2.5620076154e-01, 3.7189961923e-01, -6.2810038077e-01 }, +}; +static sIIRCoefficients iir_cf10_8000[] __attribute__((aligned)) = { + /* 31 Hz*/ +{ 9.8293118010e-01, 8.5344099512e-03, 1.9823434752e+00 }, + /* 62 Hz*/ +{ 9.6615370528e-01, 1.6923147362e-02, 1.9638231211e+00 }, + /* 125 Hz*/ +{ 9.3293473908e-01, 3.3532630460e-02, 1.9236271300e+00 }, + /* 250 Hz*/ +{ 8.7036769704e-01, 6.4816151479e-02, 1.8344291062e+00 }, + /* 500 Hz*/ +{ 7.5755317065e-01, 1.2122341468e-01, 1.6237674017e+00 }, + /* 1k Hz*/ +{ 5.7422402554e-01, 2.1288798723e-01, 1.1131444836e+00 }, + /* 2k Hz*/ +{ 3.3730698905e-01, 3.3134650547e-01, 8.1883731790e-17 }, + /* 4k Hz*/ +{ 3.3730698905e-01, 3.3134650547e-01, -1.3373069891e+00 }, + /* 8k Hz*/ +{ -7.3760117555e+00, 4.1880058777e+00, -6.3760117555e+00 }, + /* 16k Hz*/ +{ -1.7632951026e+00, 1.3816475513e+00, -7.6329510259e-01 }, +}; +static sIIRCoefficients iir_cf10_11025[] __attribute__((aligned)) = { + /* 31 Hz*/ +{ 9.8758524689e-01, 6.2073765555e-03, 1.9872750693e+00 }, + /* 62 Hz*/ +{ 9.7532461998e-01, 1.2337690008e-02, 1.9740916593e+00 }, + /* 125 Hz*/ +{ 9.5087485437e-01, 2.4562572817e-02, 1.9459267562e+00 }, + /* 250 Hz*/ +{ 9.0416308662e-01, 4.7918456688e-02, 1.8848691023e+00 }, + /* 500 Hz*/ +{ 8.1751373987e-01, 9.1243130064e-02, 1.7442229115e+00 }, + /* 1k Hz*/ +{ 6.6840529852e-01, 1.6579735074e-01, 1.4047189863e+00 }, + /* 2k Hz*/ +{ 4.4858358977e-01, 2.7570820511e-01, 6.0517475334e-01 }, + /* 4k Hz*/ +{ 2.4198119087e-01, 3.7900940457e-01, -8.0845085113e-01 }, + /* 8k Hz*/ +{ -1.2157171596e+00, 1.1078585798e+00, 3.2910548979e-02 }, + /* 16k Hz*/ +{ -1.1844379135e+00, 1.0922189568e+00, 1.7585210768e-01 }, +}; +static sIIRCoefficients iir_cf10_16000[] __attribute__((aligned)) = { + /* 31 Hz*/ +{ 9.9142885790e-01, 4.2855710504e-03, 1.9912812966e+00 }, + /* 62 Hz*/ +{ 9.8293118010e-01, 8.5344099512e-03, 1.9823434752e+00 }, + /* 125 Hz*/ +{ 9.6588546081e-01, 1.7057269597e-02, 1.9635174657e+00 }, + /* 250 Hz*/ +{ 9.3293473908e-01, 3.3532630460e-02, 1.9236271300e+00 }, + /* 500 Hz*/ +{ 8.7036769704e-01, 6.4816151479e-02, 1.8344291062e+00 }, + /* 1k Hz*/ +{ 7.5755317065e-01, 1.2122341468e-01, 1.6237674017e+00 }, + /* 2k Hz*/ +{ 5.7422402554e-01, 2.1288798723e-01, 1.1131444836e+00 }, + /* 4k Hz*/ +{ 3.3730698905e-01, 3.3134650547e-01, 8.1883731790e-17 }, + /* 8k Hz*/ +{ 3.3730698905e-01, 3.3134650547e-01, -1.3373069891e+00 }, + /* 16k Hz*/ +{ -7.3760117555e+00, 4.1880058777e+00, -6.3760117555e+00 }, +}; +static sIIRCoefficients iir_cf10_22050[] __attribute__((aligned)) = { + /* 31 Hz*/ +{ 9.9377323686e-01, 3.1133815717e-03, 1.9936954495e+00 }, + /* 62 Hz*/ +{ 9.8758524689e-01, 6.2073765555e-03, 1.9872750693e+00 }, + /* 125 Hz*/ +{ 9.7512812040e-01, 1.2435939802e-02, 1.9738753198e+00 }, + /* 250 Hz*/ +{ 9.5087485437e-01, 2.4562572817e-02, 1.9459267562e+00 }, + /* 500 Hz*/ +{ 9.0416308662e-01, 4.7918456688e-02, 1.8848691023e+00 }, + /* 1k Hz*/ +{ 8.1751373987e-01, 9.1243130064e-02, 1.7442229115e+00 }, + /* 2k Hz*/ +{ 6.6840529852e-01, 1.6579735074e-01, 1.4047189863e+00 }, + /* 4k Hz*/ +{ 4.4858358977e-01, 2.7570820511e-01, 6.0517475334e-01 }, + /* 8k Hz*/ +{ 2.4198119087e-01, 3.7900940457e-01, -8.0845085113e-01 }, + /* 16k Hz*/ +{ -1.2157171596e+00, 1.1078585798e+00, 3.2910548979e-02 }, +}; +#endif +static sIIRCoefficients iir_cf10_32000[] __attribute__((aligned)) = { + /* 31 Hz*/ +{ 9.9570520792e-01, 2.1473960411e-03, 1.9956682380e+00 }, + /* 62 Hz*/ +{ 9.9142885790e-01, 4.2855710504e-03, 1.9912812966e+00 }, + /* 125 Hz*/ +{ 9.8279471928e-01, 8.6026403580e-03, 1.9821975386e+00 }, + /* 250 Hz*/ +{ 9.6588546081e-01, 1.7057269597e-02, 1.9635174657e+00 }, + /* 500 Hz*/ +{ 9.3293473908e-01, 3.3532630460e-02, 1.9236271300e+00 }, + /* 1k Hz*/ +{ 8.7036769704e-01, 6.4816151479e-02, 1.8344291062e+00 }, + /* 2k Hz*/ +{ 7.5755317065e-01, 1.2122341468e-01, 1.6237674017e+00 }, + /* 4k Hz*/ +{ 5.7422402554e-01, 2.1288798723e-01, 1.1131444836e+00 }, + /* 8k Hz*/ +{ 3.3730698905e-01, 3.3134650547e-01, 8.1883731790e-17 }, + /* 16k Hz*/ +{ 3.3730698905e-01, 3.3134650547e-01, -1.3373069891e+00 }, +}; +static sIIRCoefficients iir_cf10_44100[] __attribute__((aligned)) = { + /* 31 Hz*/ +{ 9.9688176273e-01, 1.5591186337e-03, 1.9968622855e+00 }, + /* 62 Hz*/ +{ 9.9377323686e-01, 3.1133815717e-03, 1.9936954495e+00 }, + /* 125 Hz*/ +{ 9.8748575691e-01, 6.2571215431e-03, 1.9871705722e+00 }, + /* 250 Hz*/ +{ 9.7512812040e-01, 1.2435939802e-02, 1.9738753198e+00 }, + /* 500 Hz*/ +{ 9.5087485437e-01, 2.4562572817e-02, 1.9459267562e+00 }, + /* 1k Hz*/ +{ 9.0416308662e-01, 4.7918456688e-02, 1.8848691023e+00 }, + /* 2k Hz*/ +{ 8.1751373987e-01, 9.1243130064e-02, 1.7442229115e+00 }, + /* 4k Hz*/ +{ 6.6840529852e-01, 1.6579735074e-01, 1.4047189863e+00 }, + /* 8k Hz*/ +{ 4.4858358977e-01, 2.7570820511e-01, 6.0517475334e-01 }, + /* 16k Hz*/ +{ 2.4198119087e-01, 3.7900940457e-01, -8.0845085113e-01 }, +}; +static sIIRCoefficients iir_cf10_48000[] __attribute__((aligned)) = { + /* 31 Hz*/ +{ 9.9713475915e-01, 1.4326204244e-03, 1.9971183163e+00 }, + /* 62 Hz*/ +{ 9.9427771143e-01, 2.8611442874e-03, 1.9942120343e+00 }, + /* 125 Hz*/ +{ 9.8849666727e-01, 5.7516663664e-03, 1.9882304829e+00 }, + /* 250 Hz*/ +{ 9.7712566171e-01, 1.1437169144e-02, 1.9760670839e+00 }, + /* 500 Hz*/ +{ 9.5477456091e-01, 2.2612719547e-02, 1.9505892385e+00 }, + /* 1k Hz*/ +{ 9.1159452679e-01, 4.4202736607e-02, 1.8952405706e+00 }, + /* 2k Hz*/ +{ 8.3100647694e-01, 8.4496761532e-02, 1.7686164442e+00 }, + /* 4k Hz*/ +{ 6.9062328809e-01, 1.5468835596e-01, 1.4641227157e+00 }, + /* 8k Hz*/ +{ 4.7820368352e-01, 2.6089815824e-01, 7.3910184176e-01 }, + /* 16k Hz*/ +{ 2.5620076154e-01, 3.7189961923e-01, -6.2810038077e-01 }, +}; +#ifdef _UNUSED_ +static sIIRCoefficients iir_cf15_44100[] __attribute__((aligned)) = { + /* 25 Hz*/ +{ 9.9834072702e-01, 8.2963648917e-04, 1.9983280505e+00 }, + /* 40 Hz*/ +{ 9.9734652663e-01, 1.3267366865e-03, 1.9973140908e+00 }, + /* 63 Hz*/ +{ 9.9582396353e-01, 2.0880182333e-03, 1.9957435641e+00 }, + /* 100 Hz*/ +{ 9.9337951306e-01, 3.3102434709e-03, 1.9931771947e+00 }, + /* 160 Hz*/ +{ 9.8942832039e-01, 5.2858398053e-03, 1.9889114258e+00 }, + /* 250 Hz*/ +{ 9.8353109588e-01, 8.2344520610e-03, 1.9822729654e+00 }, + /* 400 Hz*/ +{ 9.7378088082e-01, 1.3109559588e-02, 1.9705764276e+00 }, + /* 630 Hz*/ +{ 9.5901979676e-01, 2.0490101620e-02, 1.9511333590e+00 }, + /* 1k Hz*/ +{ 9.3574903986e-01, 3.2125480071e-02, 1.9161350100e+00 }, + /* 1.6k Hz*/ +{ 8.9923630641e-01, 5.0381846793e-02, 1.8501014162e+00 }, + /* 2.5k Hz*/ +{ 8.4722457681e-01, 7.6387711593e-02, 1.7312785699e+00 }, + /* 4k Hz*/ +{ 7.6755471307e-01, 1.1622264346e-01, 1.4881981417e+00 }, + /* 6.3k Hz*/ +{ 6.6125377473e-01, 1.6937311263e-01, 1.0357747868e+00 }, + /* 10k Hz*/ +{ 5.2683267950e-01, 2.3658366025e-01, 2.2218349322e-01 }, + /* 16k Hz*/ +{ 4.0179628792e-01, 2.9910185604e-01, -9.1248032613e-01 }, +}; +static sIIRCoefficients iir_cf15_48000[] __attribute__((aligned)) = { + /* 25 Hz*/ +{ 9.9847546664e-01, 7.6226668143e-04, 1.9984647656e+00 }, + /* 40 Hz*/ +{ 9.9756184654e-01, 1.2190767289e-03, 1.9975344645e+00 }, + /* 63 Hz*/ +{ 9.9616261379e-01, 1.9186931041e-03, 1.9960947369e+00 }, + /* 100 Hz*/ +{ 9.9391578543e-01, 3.0421072865e-03, 1.9937449618e+00 }, + /* 160 Hz*/ +{ 9.9028307215e-01, 4.8584639242e-03, 1.9898465702e+00 }, + /* 250 Hz*/ +{ 9.8485897264e-01, 7.5705136795e-03, 1.9837962543e+00 }, + /* 400 Hz*/ +{ 9.7588512657e-01, 1.2057436715e-02, 1.9731772447e+00 }, + /* 630 Hz*/ +{ 9.6228521814e-01, 1.8857390928e-02, 1.9556164694e+00 }, + /* 1k Hz*/ +{ 9.4080933132e-01, 2.9595334338e-02, 1.9242054384e+00 }, + /* 1.6k Hz*/ +{ 9.0702059196e-01, 4.6489704022e-02, 1.8653476166e+00 }, + /* 2.5k Hz*/ +{ 8.5868004289e-01, 7.0659978553e-02, 1.7600401337e+00 }, + /* 4k Hz*/ +{ 7.8409610788e-01, 1.0795194606e-01, 1.5450725522e+00 }, + /* 6.3k Hz*/ +{ 6.8332861002e-01, 1.5833569499e-01, 1.1426447155e+00 }, + /* 10k Hz*/ +{ 5.5267518228e-01, 2.2366240886e-01, 4.0186190803e-01 }, + /* 16k Hz*/ +{ 4.1811888447e-01, 2.9094055777e-01, -7.0905944223e-01 }, +}; +static sIIRCoefficients iir_cf25_44100[] __attribute__((aligned)) = { + /* 20 Hz*/ +{ 9.9934037157e-01, 3.2981421662e-04, 1.9993322545e+00 }, + /* 31.5 Hz*/ +{ 9.9896129025e-01, 5.1935487310e-04, 1.9989411587e+00 }, + /* 40 Hz*/ +{ 9.9868118265e-01, 6.5940867495e-04, 1.9986487252e+00 }, + /* 50 Hz*/ +{ 9.9835175161e-01, 8.2412419683e-04, 1.9983010452e+00 }, + /* 80 Hz*/ +{ 9.9736411067e-01, 1.3179446674e-03, 1.9972343673e+00 }, + /* 100 Hz*/ +{ 9.9670622662e-01, 1.6468866919e-03, 1.9965035707e+00 }, + /* 125 Hz*/ +{ 9.9588448566e-01, 2.0577571681e-03, 1.9955679690e+00 }, + /* 160 Hz*/ +{ 9.9473519326e-01, 2.6324033689e-03, 1.9942169198e+00 }, + /* 250 Hz*/ +{ 9.9178600786e-01, 4.1069960678e-03, 1.9905226414e+00 }, + /* 315 Hz*/ +{ 9.8966154150e-01, 5.1692292513e-03, 1.9876580847e+00 }, + /* 400 Hz*/ +{ 9.8689036168e-01, 6.5548191616e-03, 1.9836646251e+00 }, + /* 500 Hz*/ +{ 9.8364027156e-01, 8.1798642207e-03, 1.9786090689e+00 }, + /* 800 Hz*/ +{ 9.7395577681e-01, 1.3022111597e-02, 1.9611472340e+00 }, + /* 1k Hz*/ +{ 9.6755437936e-01, 1.6222810321e-02, 1.9476180811e+00 }, + /* 1.25k Hz*/ +{ 9.5961458750e-01, 2.0192706249e-02, 1.9286193446e+00 }, + /* 1.6k Hz*/ +{ 9.4861481164e-01, 2.5692594182e-02, 1.8982024567e+00 }, + /* 2.5k Hz*/ +{ 9.2095325455e-01, 3.9523372724e-02, 1.8003794694e+00 }, + /* 3.15k Hz*/ +{ 9.0153642498e-01, 4.9231787512e-02, 1.7132251201e+00 }, + /* 4k Hz*/ +{ 8.7685876255e-01, 6.1570618727e-02, 1.5802270232e+00 }, + /* 5k Hz*/ +{ 8.4886734822e-01, 7.5566325889e-02, 1.3992391376e+00 }, + /* 8k Hz*/ +{ 7.7175298860e-01, 1.1412350570e-01, 7.4018523020e-01 }, + /* 10k Hz*/ +{ 7.2627049462e-01, 1.3686475269e-01, 2.5120552756e-01 }, + /* 12.5k Hz*/ +{ 6.7674787974e-01, 1.6162606013e-01, -3.4978377639e-01 }, + /* 16k Hz*/ +{ 6.2482197550e-01, 1.8758901225e-01, -1.0576558797e+00 }, + /* 20k Hz*/ +{ 6.1776148240e-01, 1.9111925880e-01, -1.5492465594e+00 }, +}; +static sIIRCoefficients iir_cf25_48000[] __attribute__((aligned)) = { + /* 20 Hz*/ +{ 9.9939388451e-01, 3.0305774630e-04, 1.9993870327e+00 }, + /* 31.5 Hz*/ +{ 9.9904564663e-01, 4.7717668529e-04, 1.9990286528e+00 }, + /* 40 Hz*/ +{ 9.9878827195e-01, 6.0586402557e-04, 1.9987608731e+00 }, + /* 50 Hz*/ +{ 9.9848556942e-01, 7.5721528829e-04, 1.9984427652e+00 }, + /* 80 Hz*/ +{ 9.9757801538e-01, 1.2109923088e-03, 1.9974684869e+00 }, + /* 100 Hz*/ +{ 9.9697343933e-01, 1.5132803374e-03, 1.9968023538e+00 }, + /* 125 Hz*/ +{ 9.9621823598e-01, 1.8908820086e-03, 1.9959510180e+00 }, + /* 160 Hz*/ +{ 9.9516191728e-01, 2.4190413595e-03, 1.9947243453e+00 }, + /* 250 Hz*/ +{ 9.9245085008e-01, 3.7745749576e-03, 1.9913840669e+00 }, + /* 315 Hz*/ +{ 9.9049749914e-01, 4.7512504310e-03, 1.9888056233e+00 }, + /* 400 Hz*/ +{ 9.8794899744e-01, 6.0255012789e-03, 1.9852245824e+00 }, + /* 500 Hz*/ +{ 9.8495930023e-01, 7.5203498850e-03, 1.9807093500e+00 }, + /* 800 Hz*/ +{ 9.7604570090e-01, 1.1977149551e-02, 1.9652207158e+00 }, + /* 1k Hz*/ +{ 9.7014963927e-01, 1.4925180364e-02, 1.9532947360e+00 }, + /* 1.25k Hz*/ +{ 9.6283181641e-01, 1.8584091793e-02, 1.9366149237e+00 }, + /* 1.6k Hz*/ +{ 9.5268463224e-01, 2.3657683878e-02, 1.9100137880e+00 }, + /* 2.5k Hz*/ +{ 9.2711765003e-01, 3.6441174983e-02, 1.8248457659e+00 }, + /* 3.15k Hz*/ +{ 9.0912548757e-01, 4.5437256213e-02, 1.7491177803e+00 }, + /* 4k Hz*/ +{ 8.8619860800e-01, 5.6900696000e-02, 1.6334959111e+00 }, + /* 5k Hz*/ +{ 8.6010264114e-01, 6.9948679430e-02, 1.4757186436e+00 }, + /* 8k Hz*/ +{ 7.8757448309e-01, 1.0621275845e-01, 8.9378724155e-01 }, + /* 10k Hz*/ +{ 7.4415362476e-01, 1.2792318762e-01, 4.5142017567e-01 }, + /* 12.5k Hz*/ +{ 6.9581428034e-01, 1.5209285983e-01, -1.1091156053e-01 }, + /* 16k Hz*/ +{ 6.4120506488e-01, 1.7939746756e-01, -8.2060253244e-01 }, + /* 20k Hz*/ +{ 6.0884213704e-01, 1.9557893148e-01, -1.3932981614e+00 }, +}; +static sIIRCoefficients iir_cf31_44100[] __attribute__((aligned)) = { + /* 20 Hz*/ +{ 9.9934037157e-01, 3.2981421662e-04, 1.9993322545e+00 }, + /* 25 Hz*/ +{ 9.9917555233e-01, 4.1222383516e-04, 1.9991628705e+00 }, + /* 31.5 Hz*/ +{ 9.9896129025e-01, 5.1935487310e-04, 1.9989411587e+00 }, + /* 40 Hz*/ +{ 9.9868118265e-01, 6.5940867495e-04, 1.9986487252e+00 }, + /* 50 Hz*/ +{ 9.9835175161e-01, 8.2412419683e-04, 1.9983010452e+00 }, + /* 63 Hz*/ +{ 9.9792365217e-01, 1.0381739160e-03, 1.9978431682e+00 }, + /* 80 Hz*/ +{ 9.9736411067e-01, 1.3179446674e-03, 1.9972343673e+00 }, + /* 100 Hz*/ +{ 9.9670622662e-01, 1.6468866919e-03, 1.9965035707e+00 }, + /* 125 Hz*/ +{ 9.9588448566e-01, 2.0577571681e-03, 1.9955679690e+00 }, + /* 160 Hz*/ +{ 9.9473519326e-01, 2.6324033689e-03, 1.9942169198e+00 }, + /* 200 Hz*/ +{ 9.9342335280e-01, 3.2883236020e-03, 1.9926141028e+00 }, + /* 250 Hz*/ +{ 9.9178600786e-01, 4.1069960678e-03, 1.9905226414e+00 }, + /* 315 Hz*/ +{ 9.8966154150e-01, 5.1692292513e-03, 1.9876580847e+00 }, + /* 400 Hz*/ +{ 9.8689036168e-01, 6.5548191616e-03, 1.9836646251e+00 }, + /* 500 Hz*/ +{ 9.8364027156e-01, 8.1798642207e-03, 1.9786090689e+00 }, + /* 630 Hz*/ +{ 9.7943153305e-01, 1.0284233476e-02, 1.9714629236e+00 }, + /* 800 Hz*/ +{ 9.7395577681e-01, 1.3022111597e-02, 1.9611472340e+00 }, + /* 1k Hz*/ +{ 9.6755437936e-01, 1.6222810321e-02, 1.9476180811e+00 }, + /* 1.25k Hz*/ +{ 9.5961458750e-01, 2.0192706249e-02, 1.9286193446e+00 }, + /* 1.6k Hz*/ +{ 9.4861481164e-01, 2.5692594182e-02, 1.8982024567e+00 }, + /* 2k Hz*/ +{ 9.3620971896e-01, 3.1895140519e-02, 1.8581325022e+00 }, + /* 2.5k Hz*/ +{ 9.2095325455e-01, 3.9523372724e-02, 1.8003794694e+00 }, + /* 3.15k Hz*/ +{ 9.0153642498e-01, 4.9231787512e-02, 1.7132251201e+00 }, + /* 4k Hz*/ +{ 8.7685876255e-01, 6.1570618727e-02, 1.5802270232e+00 }, + /* 5k Hz*/ +{ 8.4886734822e-01, 7.5566325889e-02, 1.3992391376e+00 }, + /* 6.3k Hz*/ +{ 8.1417575446e-01, 9.2912122771e-02, 1.1311200817e+00 }, + /* 8k Hz*/ +{ 7.7175298860e-01, 1.1412350570e-01, 7.4018523020e-01 }, + /* 10k Hz*/ +{ 7.2627049462e-01, 1.3686475269e-01, 2.5120552756e-01 }, + /* 12.5k Hz*/ +{ 6.7674787974e-01, 1.6162606013e-01, -3.4978377639e-01 }, + /* 16k Hz*/ +{ 6.2482197550e-01, 1.8758901225e-01, -1.0576558797e+00 }, + /* 20k Hz*/ +{ 6.1776148240e-01, 1.9111925880e-01, -1.5492465594e+00 }, +}; +static sIIRCoefficients iir_cf31_48000[] __attribute__((aligned)) = { + /* 20 Hz*/ +{ 9.9939388451e-01, 3.0305774630e-04, 1.9993870327e+00 }, + /* 25 Hz*/ +{ 9.9924247917e-01, 3.7876041632e-04, 1.9992317740e+00 }, + /* 31.5 Hz*/ +{ 9.9904564663e-01, 4.7717668529e-04, 1.9990286528e+00 }, + /* 40 Hz*/ +{ 9.9878827195e-01, 6.0586402557e-04, 1.9987608731e+00 }, + /* 50 Hz*/ +{ 9.9848556942e-01, 7.5721528829e-04, 1.9984427652e+00 }, + /* 63 Hz*/ +{ 9.9809219264e-01, 9.5390367779e-04, 1.9980242502e+00 }, + /* 80 Hz*/ +{ 9.9757801538e-01, 1.2109923088e-03, 1.9974684869e+00 }, + /* 100 Hz*/ +{ 9.9697343933e-01, 1.5132803374e-03, 1.9968023538e+00 }, + /* 125 Hz*/ +{ 9.9621823598e-01, 1.8908820086e-03, 1.9959510180e+00 }, + /* 160 Hz*/ +{ 9.9516191728e-01, 2.4190413595e-03, 1.9947243453e+00 }, + /* 200 Hz*/ +{ 9.9395607757e-01, 3.0219612131e-03, 1.9932727986e+00 }, + /* 250 Hz*/ +{ 9.9245085008e-01, 3.7745749576e-03, 1.9913840669e+00 }, + /* 315 Hz*/ +{ 9.9049749914e-01, 4.7512504310e-03, 1.9888056233e+00 }, + /* 400 Hz*/ +{ 9.8794899744e-01, 6.0255012789e-03, 1.9852245824e+00 }, + /* 500 Hz*/ +{ 9.8495930023e-01, 7.5203498850e-03, 1.9807093500e+00 }, + /* 630 Hz*/ +{ 9.8108651246e-01, 9.4567437704e-03, 1.9743538683e+00 }, + /* 800 Hz*/ +{ 9.7604570090e-01, 1.1977149551e-02, 1.9652207158e+00 }, + /* 1k Hz*/ +{ 9.7014963927e-01, 1.4925180364e-02, 1.9532947360e+00 }, + /* 1.25k Hz*/ +{ 9.6283181641e-01, 1.8584091793e-02, 1.9366149237e+00 }, + /* 1.6k Hz*/ +{ 9.5268463224e-01, 2.3657683878e-02, 1.9100137880e+00 }, + /* 2k Hz*/ +{ 9.4122788957e-01, 2.9386055213e-02, 1.8750821533e+00 }, + /* 2.5k Hz*/ +{ 9.2711765003e-01, 3.6441174983e-02, 1.8248457659e+00 }, + /* 3.15k Hz*/ +{ 9.0912548757e-01, 4.5437256213e-02, 1.7491177803e+00 }, + /* 4k Hz*/ +{ 8.8619860800e-01, 5.6900696000e-02, 1.6334959111e+00 }, + /* 5k Hz*/ +{ 8.6010264114e-01, 6.9948679430e-02, 1.4757186436e+00 }, + /* 6.3k Hz*/ +{ 8.2760520925e-01, 8.6197395374e-02, 1.2405797786e+00 }, + /* 8k Hz*/ +{ 7.8757448309e-01, 1.0621275845e-01, 8.9378724155e-01 }, + /* 10k Hz*/ +{ 7.4415362476e-01, 1.2792318762e-01, 4.5142017567e-01 }, + /* 12.5k Hz*/ +{ 6.9581428034e-01, 1.5209285983e-01, -1.1091156053e-01 }, + /* 16k Hz*/ +{ 6.4120506488e-01, 1.7939746756e-01, -8.2060253244e-01 }, + /* 20k Hz*/ +{ 6.0884213704e-01, 1.9557893148e-01, -1.3932981614e+00 }, +}; + +#endif diff --git a/amarok/src/engine/helix/helix-sp/utils.cpp b/amarok/src/engine/helix/helix-sp/utils.cpp new file mode 100644 index 00000000..6605ad04 --- /dev/null +++ b/amarok/src/engine/helix/helix-sp/utils.cpp @@ -0,0 +1,36 @@ +/* + * + * This software is released under the provisions of the GPL version 2. + * see file "COPYING". If that file is not available, the full statement + * of the license can be found at + * + * http://www.fsf.org/licensing/licenses/gpl.txt + * + * Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved. + * Portions (c) Paul Cifarelli 2005 + */ + +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include "utils.h" + + +int SafeSprintf(char *str, int max, const char *fmt, ...) +{ + int ret; + va_list ap; + va_start(ap, fmt); + ret = vsnprintf(str,max,fmt,ap); + va_end(ap); + return ret; +} + +char *SafeStrCpy(char *str1, const char *str2, int sz) +{ + int len = strlen(str2); + if (len > sz) + return 0; + + return (strcpy(str1,str2)); +} diff --git a/amarok/src/engine/helix/helix-sp/utils.h b/amarok/src/engine/helix/helix-sp/utils.h new file mode 100644 index 00000000..e636d442 --- /dev/null +++ b/amarok/src/engine/helix/helix-sp/utils.h @@ -0,0 +1,22 @@ +/* + * + * This software is released under the provisions of the GPL version 2. + * see file "COPYING". If that file is not available, the full statement + * of the license can be found at + * + * http://www.fsf.org/licensing/licenses/gpl.txt + * + * Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved. + * Portions (c) Paul Cifarelli 2005 + */ +#ifndef _HELIXUTILS_ +#define _HELIXUTILS_ + +int SafeSprintf(char *str, int max, const char *fmt, ...) +#ifdef __GNUC__ +__attribute__ ((format (printf, 3, 4))) +#endif +; +char *SafeStrCpy(char *str1, const char *str2, int sz); + +#endif diff --git a/amarok/src/engine/helix/hxplayercontrol.cpp b/amarok/src/engine/helix/hxplayercontrol.cpp new file mode 100644 index 00000000..6c76eac2 --- /dev/null +++ b/amarok/src/engine/helix/hxplayercontrol.cpp @@ -0,0 +1,1296 @@ +/*************************************************************************** + * Copyright (C) 2006 Paul Cifarelli <paul@cifarelli.net> * + * * + * 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. * + * * + ***************************************************************************/ + +/*************************************************************************** + basically this implements a rudamentary rpc mechanism so the we can have + each player in a separate process. this is solely done to implement a + reliable crossfade (or more precicely, put the crossfade in the hands of + the alsa guys + ***************************************************************************/ + +#include <sys/time.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> +#include <iostream> +#include <cstring> +#include <sys/mman.h> + +using namespace std; + +#include "hxplayercontrol.h" + +class HSPPlayerControlled : public HelixSimplePlayer +{ +public: + HSPPlayerControlled(PlayerControl *pcntl, int index) : m_pcntl(pcntl), m_index(index) {} + virtual ~HSPPlayerControlled() {} + + virtual void notifyUser(unsigned long code, const char *moreinfo, const char *moreinfourl); + virtual void interruptUser(unsigned long code, const char *moreinfo, const char *moreinfourl); + virtual void onContacting(const char *host); + virtual void onBuffering(int percentage); + +private: + PlayerControl *m_pcntl; + int m_index; +}; + + +void HSPPlayerControlled::notifyUser(unsigned long code, const char *moreinfo, const char *moreinfourl) +{ + m_pcntl->sendnotifyuser(code, moreinfo, moreinfourl); +} + +void HSPPlayerControlled::interruptUser(unsigned long code, const char *moreinfo, const char *moreinfourl) +{ + m_pcntl->sendinterruptuser(code, moreinfo, moreinfourl); +} + +void HSPPlayerControlled::onContacting(const char *host) +{ + m_pcntl->sendcontacting(host); +} + +void HSPPlayerControlled::onBuffering(int percentage) +{ + m_pcntl->sendbuffering(percentage); +} + + +PlayerControl::PlayerControl() : m_eq_enabled(false), m_preamp(0), m_err(0), iamparent(0), m_index(0), nNumPlayers(0), + m_inited(false), m_api( HelixSimplePlayer::OSS ), m_device(0), mimehead(0), mimelistlen(0), + m_numPlugins(0), m_pluginInfo(0) +{ + memset(m_children, 0, sizeof(m_children)); +} + +PlayerControl::~PlayerControl() +{ + tearDown(); + + print2stderr("In PlayerControl::~PlayerControl(), m_index=%d\n", m_index); + + delete m_device; + + if (pmapped) + munmap(pmapped, sizeof(stateStuff) * 2); +} + +// init functions +void PlayerControl::init(const char *corelibpath, const char *pluginslibpath, const char *codecspath, int numPlayers) +{ + int err; + iamparent = 0; + nNumPlayers = numPlayers; + m_err = 0; + + print2stderr("In PlayerControl::init(), m_api=%d, m_device=%s\n", m_api, m_device ? m_device : "DEVICE NOT SET"); + + if (numPlayers > 2) // it's impossible + { + m_err = -1; + return; + } + + memset(&m_children, 0, numPlayers * sizeof(struct playerChildren)); + + m_inited = false; + + // create a shared memory region for state like stuff + if ( MAP_FAILED == (pmapped = (stateStuff *) mmap( (void *) statestuff, + sizeof(stateStuff) * 2, + PROT_READ | PROT_WRITE, +#ifdef __linux__ + MAP_SHARED | MAP_ANONYMOUS, +#else + MAP_SHARED | MAP_ANON, +#endif + -1, 0)) ) + pmapped = 0; + + // we do this the old fashioned way, so that we don't have to include an executable with our plugin... + for (int i = 0; i < numPlayers; i++) + { + if (pmapped) + { + m_children[i].current_time = &(pmapped[i].current_time); + m_children[i].duration = &(pmapped[i].duration); + m_children[i].md = &(pmapped[i].md); + + m_children[i].m_current = &(pmapped[i].m_current); + m_children[i].m_consumed = &(pmapped[i].m_consumed); + //m_children[i].q = pmapped[i].q; + //for (int j=0; j<NUM_SCOPEBUFS; j++) + //{ + // m_children[i].q[j].allocd = false; + // m_children[i].q[j].buf = pmapped[i].b[j]; + //} + } + err = pipe(m_children[i].m_pipeA); + err |= pipe(m_children[i].m_pipeB); + if ( !err && (iamparent = fork()) ) + { + // parent + print2stderr("%%%%%% parent initializes player %d\n", i); + + // parent's m_pid remains 0 + m_children[i].m_pid = iamparent; + close(m_children[i].m_pipeA[1]); // parent uses A for reading + close(m_children[i].m_pipeB[0]); // and B for writing + } + else if (!err) + { + // child + + cerr << "%%%%%% child initializes as player " << i << endl;; + + m_index = i; // child's index is saved + close(m_children[i].m_pipeA[0]); // child uses A for writing + close(m_children[i].m_pipeB[1]); // and B for reading + break; + } + } + + + if (!iamparent) // children stay here, parents return + { + int rfd = m_children[m_index].m_pipeB[0]; + int wfd = m_children[m_index].m_pipeA[1]; + int n; + struct timeval timeout; + HSPPlayerControlled *player = 0; + bool playing = false; + + player = new HSPPlayerControlled(this, m_index); + + timeout.tv_sec = 0; + timeout.tv_usec = 10000; + fd_set rdset, wrset; + FD_ZERO(&rdset); + FD_ZERO(&wrset); + FD_SET(rfd, &rdset); + FD_SET(wfd, &wrset); // really should check to see if we can write, but not gonna + + while ( 1 ) + { + FD_SET(rfd, &rdset); + + n = select(rfd + 1, &rdset, 0, 0, &timeout); + if ( FD_ISSET(rfd, &rdset) ) + { + msgid m; + unsigned char buf[65536]; + int sz = 0; + + if (getmessage(rfd, m, buf, sz) && player) + { + switch (m) + { + case INIT: + cerr << "INIT\n"; + if (!sz) + { + player->setOutputSink(m_api); + if (m_device) + player->setDevice(m_device); + player->init(corelibpath, pluginslibpath, codecspath, 1); + m_inited = true; + if (!m_index) // only player 0 need send the mime and plugin stuff + { + sendplugins(wfd, player); + sendmimetypes(wfd, player); + } + cerr << "%%%%%% player " << m_index << " child sends ready\n"; + sendready(wfd); + } + else + cerr << "CHILD " << m_index << " sz not right in INIT, sz=" << sz << endl; + break; + case SETURL: + { + bool islocal = (bool) buf[0]; + int len = strlen((const char *)&buf[1]); // not that this would prevent a crash... + if (m_inited) + { + cerr << "CHILD " << m_index << " setURL for " << (const char *)&buf[1] << + ",islocal=" << islocal << ",len=" << len << endl; + if (sz == len + 2) + player->setURL((const char *)&buf[1], 0, islocal); // remember, we sent the null... + else + cerr << "CHILD " << m_index << " sz not right in SETURL, sz=" << sz << endl; + } + } + break; + case START: + { + bool fadein = (bool) buf[0]; + unsigned long fadetime; + + if (m_inited) + { + if (pmapped) + *m_children[m_index].current_time = 0; + + if (sz == sizeof(unsigned long) + 1) + { + cerr << "CHILD " << m_index << " gets START\n"; + memcpy((void *)&fadetime, (void *)&buf[1], sizeof(unsigned long)); + playing = true; + player->start(0, fadein, fadetime); + } + else + cerr << "CHILD " << m_index << " sz not right in START, sz=" << sz << endl; + } + } + break; + case STOP: + if (m_inited) + if (!sz) + { + player->stop(); + playing = false; + cerr << "CHILD " << m_index << " gets STOP\n"; + } + else + cerr << "CHILD " << m_index << " sz not right in STOP, sz=" << sz << endl; + break; + case PAUSE: + if (m_inited) + if (!sz) + player->pause(); + else + cerr << "CHILD " << m_index << " sz not right in PAUSE, sz=" << sz << endl; + break; + case RESUME: + if (m_inited) + if (!sz) + player->resume(); + else + cerr << "CHILD " << m_index << " sz not right in RESUME, sz=" << sz << endl; + break; + case SEEK: + if (m_inited) + if (sz == sizeof(unsigned long)) + { + unsigned long pos; + memcpy( (void *) &pos, (void *) buf, sizeof(unsigned long) ); + if (pos < player->duration(0)) + player->seek(pos, 0); + } + else + cerr << "CHILD " << m_index << " sz not right in SEEK, sz=" << sz << endl; + break; + case SETVOLUME: + { + if (m_inited) + if (sz == sizeof(unsigned long)) + { + memcpy( (void *) &m_volume, (void *) buf, sizeof(unsigned long)); + cerr << "CHILD: received setvolume request " << m_volume <<endl;; + player->setVolume(m_volume); + } + else + cerr << "CHILD " << m_index << " sz not right in SETVOLUME, sz=" << sz << endl; + } + break; + case OUTPUTSINK: + if (m_inited) + if (sz == 1) + { + m_api = (HelixSimplePlayer::AUDIOAPI) buf[0]; + cerr << "CHILD: received OUTPUTSINK: " << m_api <<endl;; + } + else + cerr << "CHILD " << m_index << " sz not right in OUTPUTSINK, sz=" << sz << endl; + break; + case DEVICE: + { + char* dev = strdup((const char*) buf); + if (m_inited) + if ((unsigned)sz == strlen(dev) + 1) + { + cerr << "CHILD " << m_index << " gets device " << dev << endl; + setDevice( dev ); + } + else + cerr << "CHILD " << m_index << " sz not right in DEVICE, sz=" << sz << endl; + free(dev); + } + break; + case SETFADE: + { + if (m_inited) + if (sz == sizeof(unsigned long) + 1) + { + bool fadeout; + unsigned long fadelength; + fadeout = (bool) buf[0]; + memcpy((void *) &fadelength, (void *) &buf[1], sizeof(unsigned long)); + player->setFadeout(fadeout, fadelength, 0); + } + else + cerr << "CHILD " << m_index << " sz not right in SETFADE, sz=" << sz << endl; + } + break; + case ENABLEEQ: + { + if (m_inited) + if (sz == 1) + { + m_eq_enabled = (bool) buf[0]; + player->enableEQ(m_eq_enabled); + cerr << "CHILD " << m_index << " enables EQ\n"; + } + else + cerr << "CHILD " << m_index << " sz not right in ENABLEEQ, sz=" << sz << endl; + } + break; + case UPDATEEQGAINS: + { + int i, n, k; + + cerr << "UPDATEGAINS\n"; + if (m_inited) + { + if (sz >= 2) + { + memcpy( (void *) &m_preamp, (void *) buf, sizeof(m_preamp) ); + memcpy( (void *) &n, (void *) &buf[ sizeof(m_preamp) ], sizeof(int) ); + player->m_preamp = m_preamp; + } + else + cerr << "CHILD " << m_index << " sz not right in UPDATEEQGAINS, sz=" << sz << endl; + + if ((unsigned)sz == sizeof(m_preamp) + sizeof(int) + n * sizeof(int)) + { + if (n > 0) + { + m_equalizerGains.resize(n); + cerr << "CHILD " << m_index << " receives " << n << " equalizer gains\n"; + for (i=0; i<n; i++) + { + memcpy( (void *) &k, (void *) &buf[ sizeof(m_preamp) + (i+1)*sizeof(int) ], sizeof(int) ); + m_equalizerGains[i] = k; + } + player->m_equalizerGains = m_equalizerGains; + if (!m_eq_enabled) + player->enableEQ(true); + player->updateEQgains(); + player->enableEQ(m_eq_enabled); + } + } + else + cerr << "CHILD " << m_index << " sz not right in UPDATEEQGAINS, sz=" << sz << endl; + } + } + break; + case SCOPECLEAR: + if (m_inited) + { + player->clearScopeQ(0); + if (pmapped) + *m_children[m_index].m_consumed = *m_children[m_index].m_current = 0; + } + break; + case TEARDOWN: + if (!sz) + { + cerr << "CHILD: " << m_index << " received shutdown request\n"; + player->stop(0); + delete player; + raise(15); + exit(0); + } + else + cerr << "CHILD " << m_index << " sz not right in TEARDOWN, sz=" << sz << endl; + break; + + default: // send an error to the parent + cerr << "CHILD " << m_index << " received unhandled message, sz=" << sz << endl; + break; + } + } + else + { + cerr << "CHILD " << m_index << " gets EOD\n"; + break; + } + } + + if (m_inited) + { + player->dispatch(); + + if (playing && player->done(0)) + { + player->stop(0); + player->clearScopeQ(0); + senddone(wfd); + playing = false; + if (pmapped) + { + *m_children[m_index].current_time = 0; + *m_children[m_index].duration = 0; + } + } + + if (pmapped) + { + *m_children[m_index].current_time = player->where(0); + *m_children[m_index].duration = player->duration(0); + + HelixSimplePlayer::metaData *md = player->getMetaData(0); + if (md) + memcpy((void *) m_children[m_index].md, (void *) md, sizeof(HelixSimplePlayer::metaData)); + + struct DelayQueue *item; + //int j; + while ((item = player->getScopeBuf(0))) + { + //j = (*m_children[m_index].m_current + 1) % NUM_SCOPEBUFS; + + //cerr << "player:" << m_index << " j=" << j << " time=" << item->time << " etime=" << item->etime << " len=" << item->len << endl; + //m_children[m_index].q[j].len = item->len; + //m_children[m_index].q[j].time = item->time; + //m_children[m_index].q[j].etime = item->etime; + //m_children[m_index].q[j].nchan = item->nchan; + //m_children[m_index].q[j].bps = item->bps; + //m_children[m_index].q[j].tps = item->tps; + //m_children[m_index].q[j].spb = item->spb; + //memcpy((void *)m_children[m_index].q[j].buf, (void *) item->buf, item->len ); + //*m_children[m_index].m_current = j; + //cerr << "player:" << m_index << " time=" << item->time << " etime=" << item->etime << endl; + sendscopebuf(wfd, item); + delete item; + } + } + } + + timeout.tv_sec = 0; + timeout.tv_usec = 10000; + } + cerr << "CHILD " << m_index << " will exit!\n"; + } + else + { + int i; + bool done = false, dead = false; + + sendsetoutputsink(); + sendsetdevice(); + sendinit(); + + // wait for ready from children + while (!done && !dead) + { + dispatch(); + done = true; + for (i=0; i<numPlayers; i++) + { + done &= m_children[i].isready; + dead |= m_children[i].isdead; + } + } + + if (dead) + { + m_err = -1; + return; + } + } + m_inited = true; +} + + +void PlayerControl::setOutputSink( HelixSimplePlayer::AUDIOAPI out ) +{ + print2stderr("%%%% In PlayerControl::setOutputSink:%d\n", out); + m_api = out; +} + +void PlayerControl::setDevice( const char *dev ) +{ + delete [] m_device; + + int len = strlen(dev); + m_device = new char [len + 1]; + strcpy(m_device, dev); + print2stderr("%%%% In PlayerControl::setDevice:%s\n", dev); +} + +int PlayerControl::initDirectSS() +{ + return 0; +} + +void PlayerControl::tearDown() +{ + int tmp; + if (iamparent) + { + for (int i = 0; i < nNumPlayers; i++) + { + if (m_inited) + { + sendteardown(m_children[i].m_pipeB[1]); + close(m_children[i].m_pipeB[1]); + close(m_children[i].m_pipeA[0]); + cerr << "About to waitpid for pid " << m_children[i].m_pid << endl; + kill(m_children[i].m_pid, SIGTERM); + waitpid(m_children[i].m_pid, &tmp, 0); + } + } + } +} + +void PlayerControl::start(int playerIndex, bool fadein, unsigned long fadetime) +{ + m_children[playerIndex].isplaying = true; + if (pmapped) + *m_children[playerIndex].m_consumed = *m_children[playerIndex].m_current = 0; + sendstart(m_children[playerIndex].m_pipeB[1], fadein, fadetime); +} + +int PlayerControl::setURL(const char *url, int playerIndex, bool islocal) +{ + m_children[playerIndex].islocal = islocal; + if (sendsetURL(m_children[playerIndex].m_pipeB[1], url, islocal)) + return 0; + + return -1; +} + +bool PlayerControl::done(int playerIndex) +{ + return (!m_children[playerIndex].isplaying); +} + +void PlayerControl::stop(int playerIndex) +{ + if (playerIndex == HelixSimplePlayer::ALL_PLAYERS) + { + for (int i=0; i<nNumPlayers; i++) + stop(i); + } + else + { + m_children[playerIndex].isplaying = false; + sendstop(m_children[playerIndex].m_pipeB[1]); + } +} + +void PlayerControl::pause(int playerIndex) +{ + sendpause(m_children[playerIndex].m_pipeB[1]); +} + +void PlayerControl::resume(int playerIndex) +{ + sendresume(m_children[playerIndex].m_pipeB[1]); +} + +void PlayerControl::seek(unsigned long pos, int playerIndex) +{ + sendmessage(m_children[playerIndex].m_pipeB[1], SEEK, (unsigned char *) &pos, sizeof(unsigned long)); +} + +unsigned long PlayerControl::where(int playerIndex) const +{ + if (pmapped) + return *m_children[playerIndex].current_time; + else + return 0; +} + +unsigned long PlayerControl::duration(int playerIndex) const +{ + if (pmapped) + return *m_children[playerIndex].duration; + else + return 0; +} + +unsigned long PlayerControl::getVolume() +{ + return m_volume; +} + +void PlayerControl::setVolume(unsigned long vol) +{ + m_volume = vol; + for (int i = 0; i < nNumPlayers; i++) + sendsetvolume(m_children[i].m_pipeB[1], vol); +} + +void PlayerControl::dispatch() +{ + int i; + struct timeval timeout; + int n = -1, ntot; + int rfd; + int wfd; + + timeout.tv_sec = 0; + timeout.tv_usec = 0; + fd_set rdset, wrset; + + FD_ZERO(&rdset); + FD_ZERO(&wrset); + + for (i=0; i<nNumPlayers; i++) + { + rfd = m_children[i].m_pipeA[0]; + wfd = m_children[i].m_pipeB[1]; + FD_SET(rfd, &rdset); + FD_SET(wfd, &wrset); // really should check to see if we can write, but not gonna + if (rfd > n) + n = rfd; + } + + if (n < 0) + return; + + ntot = select(n + 1, &rdset, 0, 0, &timeout); + for (i=0; ntot && i < nNumPlayers; i++) + { + rfd = m_children[i].m_pipeA[0]; + wfd = m_children[i].m_pipeB[1]; + if ( FD_ISSET(rfd, &rdset) ) + { + msgid m; + unsigned char buf[65536]; + int sz = 0; + + if (getmessage(rfd, m, buf, sz)) + { + switch (m) + { + case READY: + print2stderr("CHILD %d is READY\n", i); + m_children[i].isready = true;; + break; + + case DONE: + print2stderr("CHILD %d is DONE\n", i); + if (!sz) + { + m_children[i].isplaying = false; + clearScopeQ(i); + play_finished(i); + } + else + print2stderr("PARENT: sz does not agree in DONE\n"); + break; + + case MIMETYPES: + { + int len, slen; + MimeList *entry; + char *tmp; + char tmpbuf[65536]; + + mimehead = 0; + memcpy( (void *) &mimelistlen, (void *) buf, sizeof(mimelistlen) ); + len = sizeof(mimelistlen); + + print2stderr("%%%%%%% Received %d mimetypes\n", mimelistlen); + for (int j = 0; j < mimelistlen; j++) + { + tmp = (char *) &buf[len]; + slen = strlen(tmp); + strcpy(tmpbuf, tmp); + tmp += slen + 1; + len += slen + 1; + entry = new MimeList(tmpbuf, tmp); + slen = strlen(tmp); + len += slen + 1; + + entry->fwd = mimehead; + mimehead = entry; + } + + if (sz != len) // sanity check + cerr << "PARENT: sz not = len in MIMETYPES " << sz << " " << len << endl; + } + break; + + case PLUGINS: + { + int len, slen; + int nplugins; + char *tmp; + + memcpy( (void *) &nplugins, (void *) buf, sizeof(nplugins) ); + len = sizeof(nplugins); + m_pluginInfo = new pluginInfo* [nplugins]; + m_numPlugins = nplugins; + + print2stderr("%%%%%%% Received %d plugins\n", nplugins); + for (int j = 0; j < nplugins; j++) + { + m_pluginInfo[j] = new pluginInfo; + + tmp = (char *) &buf[len]; + slen = strlen(tmp); + m_pluginInfo[j]->description = new char[ slen + 1 ]; + strcpy(m_pluginInfo[j]->description, tmp); + len += slen + 1; + + tmp = (char *) &buf[len]; + slen = strlen(tmp); + m_pluginInfo[j]->copyright = new char[ slen + 1 ]; + strcpy(m_pluginInfo[j]->copyright, tmp); + len += slen + 1; + + tmp = (char *) &buf[len]; + slen = strlen(tmp); + m_pluginInfo[j]->moreinfourl = new char[ slen + 1 ]; + strcpy(m_pluginInfo[j]->moreinfourl, tmp); + len += slen + 1; + } + + if (sz != len) // sanity check + cerr << "PARENT: sz not = len in PLUGINS " << sz << " " << len << endl; + } + break; + + case CONTACTING: + { + int len = strlen((const char *)buf); + if (sz == len + 1) + onContacting((const char *)buf); + else + cerr << "PARENT: sz not right in CONTACTING sz=" << sz << endl; + } + break; + + case BUFFERING: + { + unsigned long percent; + if (sz == sizeof(unsigned long)) + { + memcpy((void *)&percent, (void *) buf, sizeof(unsigned long)); + onBuffering(percent); + } + else + cerr << "PARENT: sz not right in BUFFERING sz=" << sz << endl; + } + break; + + case NOTIFYUSER: + { + int len1, len2; + unsigned long code; + const char *moreinfo, *moreinfourl; + memcpy((void *) &code, (void *) buf, sizeof(unsigned long)); + moreinfo = (const char *) &buf[sizeof(unsigned long)]; + len1 = strlen(moreinfo); + moreinfourl = (const char *) &moreinfo[ len1 + 1]; + len2 = strlen(moreinfourl); + // sanity check + if ((unsigned) sz != sizeof(unsigned long) + len1 + len2 + 2) + cerr << "PARENT: sz not right in NOTIFYUSER sz=" << sz << endl; + + notifyUser(code, moreinfo, moreinfourl); + } + break; + + case INTERRUPTUSER: + { + int len1, len2; + unsigned long code; + const char *moreinfo, *moreinfourl; + memcpy((void *) &code, (void *) buf, sizeof(unsigned long)); + moreinfo = (const char *) &buf[sizeof(unsigned long)]; + len1 = strlen(moreinfo); + moreinfourl = (const char *) &moreinfo[ len1 + 1]; + len2 = strlen(moreinfourl); + // sanity check + if ((unsigned) sz != sizeof(unsigned long) + len1 + len2 + 2) + cerr << "PARENT: sz not right in INTERRUPTUSER sz=" << sz << endl; + + interruptUser(code, moreinfo, moreinfourl); + } + break; + + case SCOPEBUF: + { + DelayQueue *item; + int bufsz, len = 0; + memcpy( (void *) &bufsz, (void *) buf, sizeof(int) ); len += sizeof(int); + if ((int) (bufsz + 2 * sizeof(unsigned long) + 4 * sizeof(int) + sizeof(double)) == sz) + { + item = new DelayQueue(bufsz); + memcpy( (void *) &item->time, (void *) &buf[len], sizeof(unsigned long) ); + len += sizeof(unsigned long); + memcpy( (void *) &item->etime, (void *) &buf[len], sizeof(unsigned long) ); + len += sizeof(unsigned long); + memcpy( (void *) &item->nchan, (void *) &buf[len], sizeof(int) ); len += sizeof(int); + memcpy( (void *) &item->bps, (void *) &buf[len], sizeof(int) ); len += sizeof(int); + memcpy( (void *) &item->tps, (void *) &buf[len], sizeof(double) ); len += sizeof(double); + memcpy( (void *) &item->spb, (void *) &buf[len], sizeof(int) ); len += sizeof(int); + memcpy( (void *) item->buf, (void *) &buf[len], item->len ); len += item->len; + + addScopeBuf(item, i); + } + else + cerr << "PARENT: sz not right in SCOPEBUF sz=" << sz << endl; + } + break; + default: + print2stderr("PARENT recvd unhandled message %d\n", m); + break; + } + } + else + { + m_children[i].isdead = true; + return; // should never happen + } + } + } + + for (i=0; pmapped && i<nNumPlayers; i++) + { + while (*m_children[i].m_consumed != *m_children[i].m_current) + { + addScopeBuf(&m_children[i].q[*m_children[i].m_consumed], i); + + //cerr << "j=" << *m_children[i].m_consumed << + // " time=" << m_children[i].q[*m_children[i].m_consumed].time << + // " etime=" << m_children[i].q[*m_children[i].m_consumed].etime << + // " len=" << m_children[i].q[*m_children[i].m_consumed].len << endl; + + *m_children[i].m_consumed = (*m_children[i].m_consumed + 1) % NUM_SCOPEBUFS; + } + } +} + +void PlayerControl::cleanUpStream(int playerIndex) +{ + stop(playerIndex); +} + +bool PlayerControl::isPlaying(int playerIndex) const +{ + return m_children[playerIndex].isplaying; +} + +bool PlayerControl::isLocal(int playerIndex) const +{ + return m_children[playerIndex].islocal; +} + + +void PlayerControl::setFadeout(bool fadeout, unsigned long fadelength, int playerIndex) +{ + sendsetfade(m_children[playerIndex].m_pipeB[1], fadeout, fadelength); +} + +void PlayerControl::addScopeBuf(struct DelayQueue *item, int playerIndex) +{ + if (playerIndex >=0 && playerIndex < nNumPlayers) + { + if (m_children[playerIndex].scopebuftail) + { + item->fwd = 0; + m_children[playerIndex].scopebuftail->fwd = item; + m_children[playerIndex].scopebuftail = item; + m_children[playerIndex].scopecount++; + } + else + { + item->fwd = 0; + m_children[playerIndex].scopebufhead = item; + m_children[playerIndex].scopebuftail = item; + m_children[playerIndex].scopecount = 1; + } + } +} + +DelayQueue *PlayerControl::getScopeBuf(int playerIndex) +{ + if (playerIndex >=0 && playerIndex < nNumPlayers) + { + struct DelayQueue *item = m_children[playerIndex].scopebufhead; + + if (item) + { + m_children[playerIndex].scopebufhead = item->fwd; + m_children[playerIndex].scopecount--; + if (!m_children[playerIndex].scopebufhead) + m_children[playerIndex].scopebuftail = 0; + } + return item; + } + else + return 0; +} + +int PlayerControl::getScopeCount(int playerIndex) +{ + return (playerIndex >= 0 && playerIndex < nNumPlayers ? m_children[playerIndex].scopecount : 0); +} + +int PlayerControl::peekScopeTime(unsigned long &t, int playerIndex) +{ + if (playerIndex >=0 && playerIndex < nNumPlayers) + { + if (m_children[playerIndex].scopebufhead) + t = m_children[playerIndex].scopebufhead->time; + else + return -1; + return 0; + } + return -1; +} + +void PlayerControl::clearScopeQ(int playerIndex) +{ + if (playerIndex < 0) + { + for (int i=0; i<nNumPlayers; i++) + clearScopeQ(i); + } + else + { + sendscopeclear(m_children[playerIndex].m_pipeB[1]); + struct DelayQueue *item; + while ((item = getScopeBuf(playerIndex))) + if (item->allocd) + delete item; + } +} + +void PlayerControl::enableEQ(bool enabled) +{ + int i; + unsigned char c = (char) enabled; + + for (i=0; i<nNumPlayers; i++) + sendmessage(m_children[i].m_pipeB[1], ENABLEEQ, (unsigned char *) &c, 1); + + m_eq_enabled = enabled; +} + +bool PlayerControl::isEQenabled() +{ + return m_eq_enabled; +} + +void PlayerControl::updateEQgains() +{ + sendupdateeqgains(); +} + +int PlayerControl::numPlugins() const +{ + return m_numPlugins; +} + +int PlayerControl::getPluginInfo(int index, const char *&description, const char *©right, const char *&moreinfourl) const +{ + if (m_pluginInfo && index < m_numPlugins) + { + description = m_pluginInfo[index]->description; + copyright = m_pluginInfo[index]->copyright; + moreinfourl = m_pluginInfo[index]->moreinfourl; + + return 0; + } + return -1; +} + +const MimeList *PlayerControl::getMimeList() const +{ + return mimehead; +} + +int PlayerControl::getMimeListLen() const +{ + return mimelistlen; +} + +HelixSimplePlayer::metaData *PlayerControl::getMetaData(int playerIndex ) +{ + return m_children[playerIndex].md; +} + +bool PlayerControl::sendsetoutputsink() +{ + int i; + char c = (char) m_api; + bool ok = false; + + for (i=0; i<nNumPlayers; i++) + ok |= sendmessage(m_children[i].m_pipeB[1], OUTPUTSINK, (unsigned char *) &c, 1); + + return ok; +} + +bool PlayerControl::sendsetdevice() +{ + if (!m_device) + return false; + + int i, len = strlen( m_device ); + bool ok = false; + + for (i=0; i<nNumPlayers; i++) + ok |= sendmessage(m_children[i].m_pipeB[1], DEVICE, (unsigned char *) m_device, len + 1); + + return ok; +} + +bool PlayerControl::sendinit() +{ + int i; + bool ok = false; + + for (i=0; i<nNumPlayers; i++) + ok |= sendrequest(m_children[i].m_pipeB[1], INIT); + + return ok; +} + +bool PlayerControl::sendupdateeqgains() +{ + unsigned char buf[ 65535 ]; + int bandGain; + uint i; + bool ok = false; + + memcpy((void *) buf, (void *) &m_preamp, sizeof(m_preamp)); + i = m_equalizerGains.size(); + memcpy( (void *) &buf[ sizeof(m_preamp) ], (void *) &i, sizeof(int) ); + for ( i = 0; i < m_equalizerGains.size(); i++ ) + { + bandGain = m_equalizerGains[i]; + memcpy((void *)&buf[ sizeof(m_preamp) + (i+1) * sizeof(int) ], (void *) &bandGain, sizeof(int)); + } + for ( i = 0; i < (uint) nNumPlayers; i++ ) + ok |= sendmessage(m_children[i].m_pipeB[1], UPDATEEQGAINS, buf, sizeof(m_preamp) + (m_equalizerGains.size()+1) * sizeof(int)); + + return ok; +} + +// children send this! +bool PlayerControl::sendnotifyuser(unsigned long code, const char *moreinfo, const char *moreinfourl) +{ + int len1 = strlen(moreinfo), len2 = strlen(moreinfourl), len; + unsigned char buf[65536]; + + memcpy( (void *) buf, (void *) &code, sizeof(unsigned long) ); + len = sizeof(unsigned long); + memcpy( (void *) &buf[ len ], (void *) moreinfo, len1 + 1); + len += len1 + 1; + memcpy( (void *) &buf[ len ], (void *) moreinfourl, len2 + 1); + len += len2 + 1; + + return (sendmessage(m_children[m_index].m_pipeA[1], NOTIFYUSER, buf, len)); +} + +// children send this! +bool PlayerControl::sendinterruptuser(unsigned long code, const char *moreinfo, const char *moreinfourl) +{ + int len1 = strlen(moreinfo), len2 = strlen(moreinfourl), len; + unsigned char buf[65536]; + + memcpy( (void *) buf, (void *) &code, sizeof(unsigned long) ); + len = sizeof(unsigned long); + memcpy( (void *) &buf[ len ], (void *) moreinfo, len1 + 1); + len += len1 + 1; + memcpy( (void *) &buf[ len ], (void *) moreinfourl, len2 + 1); + len += len2 + 1; + + return (sendmessage(m_children[m_index].m_pipeA[1], INTERRUPTUSER, buf, len)); +} + +// children send this! +bool PlayerControl::sendcontacting(const char *host) +{ + int len = strlen(host); + + return (sendmessage(m_children[m_index].m_pipeA[1], CONTACTING, (unsigned char *) host, len + 1)); +} + +// children send this! +bool PlayerControl::sendbuffering(int percentage) +{ + return (sendmessage(m_children[m_index].m_pipeA[1], BUFFERING, (unsigned char *) &percentage, sizeof(unsigned long))); +} + + +///////////// statics ////////////// + +bool PlayerControl::getmessage(int fd, msgid &m, unsigned char *buf, int &sz) +{ + int nbytes = 0, bytes = 0; + unsigned char mm; + + bytes = read(fd, (void *) &mm, 1); + if (bytes <= 0) + return false; + + m = (msgid) mm; + + nbytes = bytes; + + nbytes = 0; + char *tmp = (char *) &sz; + while ( bytes > 0 && 4 != nbytes ) + { + bytes = read(fd, (void *) &tmp[ nbytes ], 4 - nbytes); + nbytes += bytes; + } + + if (sz) + { + nbytes = 0; + while ( bytes > 0 && sz != nbytes ) + { + bytes = read(fd, (void *) &buf[ nbytes ], sz - nbytes); + nbytes += bytes; + } + } + + return (nbytes > 0); +} + +bool PlayerControl::sendmessage(int fd, msgid m, unsigned char *buf, int sz) +{ + unsigned char hdr[5]; + hdr[0] = (unsigned char) m; + int ret = 0; + + memcpy(&hdr[1], (void *) &sz, 4); + ret = write(fd, (void *) hdr, 5); + if (sz) + ret += write(fd, (void *) buf, sz); + + return (ret == (sz + 5)); +} + +bool PlayerControl::sendsetURL(int fd, const char *url, bool islocal) +{ + int len = strlen(url); + unsigned char* buf = new unsigned char[ len + 2 ]; + + buf[0] = (unsigned char) islocal; + memcpy((void *) &buf[1], (void *) url, len + 1); // go ahead and send the null, what the hell + bool r = sendmessage(fd, SETURL, (unsigned char *)buf, len + 2); + delete [] buf; + return r; +} + +bool PlayerControl::sendstart(int fd, bool fadin, unsigned long fadetime) +{ + unsigned char buf[32]; + + buf[0] = (unsigned char) fadin; + memcpy( (void *) &buf[1], (void *) &fadetime, sizeof(unsigned long) ); + + return sendmessage(fd, START, buf, sizeof(unsigned long) + 1); +} + +bool PlayerControl::sendsetvolume(int fd, unsigned long volume) +{ + return sendmessage(fd, SETVOLUME, (unsigned char *) &volume, sizeof(unsigned long)); +} + +bool PlayerControl::sendvolume(int fd, unsigned long volume) +{ + return sendmessage(fd, VOLUME, (unsigned char *) &volume, sizeof(unsigned long)); +} + +bool PlayerControl::sendsetfade(int fd, bool fadeout, unsigned long fadelength) +{ + unsigned char buf[ sizeof(bool) + sizeof(unsigned long) ]; + + buf[0] = (char )fadeout; + memcpy((void *) &buf[1], (void *) &fadelength, sizeof(unsigned long)); + return sendmessage(fd, SETFADE, buf, 1 + sizeof(unsigned long)); +} + +bool PlayerControl::sendplugins(int fd, HelixSimplePlayer *player) +{ + unsigned char buf[65536]; + int sz, slen; + int nplugins = player->numPlugins(); + const char *description, *copyright, *moreinfourl; + + memcpy( (void *) buf, (void *) &nplugins, sizeof(nplugins) ); + sz = sizeof(nplugins); + + for (int i = 0; i < nplugins; i++) + { + player->getPluginInfo(i, description, copyright, moreinfourl); + + slen = strlen(description); + memcpy( (void *) &buf[sz], (void *) description, slen + 1 ); // expecting the null + sz += slen + 1; + slen = strlen(copyright); + memcpy( (void *) &buf[sz], (void *) copyright, slen + 1 ); // expecting the null + sz += slen + 1; + slen = strlen(moreinfourl); + memcpy( (void *) &buf[sz], (void *) moreinfourl, slen + 1 ); // expecting the null + sz += slen + 1; + } + + cerr << "CHILD: nplugins " << nplugins << " sz " << sz << endl; + return sendmessage(fd, PLUGINS, buf, sz); +} + +bool PlayerControl::sendmimetypes(int fd, HelixSimplePlayer *player) +{ + unsigned char buf[65536]; + int sz, slen; + int mimelistlen = player->getMimeListLen(); + const MimeList *mimelisthead = player->getMimeList(); + + memcpy( (void *) buf, (void *) &mimelistlen, sizeof(mimelistlen) ); + sz = sizeof(mimelistlen); + + while (mimelisthead) + { + slen = strlen(mimelisthead->mimetypes); + memcpy( (void *) &buf[sz], (void *) mimelisthead->mimetypes, slen + 1 ); // expecting the null + sz += slen + 1; + slen = strlen(mimelisthead->mimeexts); + memcpy( (void *) &buf[sz], (void *) mimelisthead->mimeexts, slen + 1 ); // expecting the null + sz += slen + 1; + + mimelisthead = mimelisthead->fwd; + } + + return sendmessage(fd, MIMETYPES, buf, sz); +} + +bool PlayerControl::sendscopebuf(int fd, DelayQueue *item) +{ + unsigned char buf[65536]; + int len = 0; + + if (len + 2 * sizeof(unsigned long) + 4 * sizeof(int) + sizeof(double) < 65536) + { + memcpy( (void *) buf, (void *) &item->len, sizeof(int) ); len += sizeof(int); + memcpy( (void *) &buf[len], (void *) &item->time, sizeof(unsigned long) ); len += sizeof(unsigned long); + memcpy( (void *) &buf[len], (void *) &item->etime, sizeof(unsigned long) ); len += sizeof(unsigned long); + memcpy( (void *) &buf[len], (void *) &item->nchan, sizeof(int) ); len += sizeof(int); + memcpy( (void *) &buf[len], (void *) &item->bps, sizeof(int) ); len += sizeof(int); + memcpy( (void *) &buf[len], (void *) &item->tps, sizeof(double) ); len += sizeof(double); + memcpy( (void *) &buf[len], (void *) &item->spb, sizeof(int) ); len += sizeof(int); + memcpy( (void *) &buf[len], (void *) item->buf, item->len ); len += item->len; + + return sendmessage(fd, SCOPEBUF, buf, len); + } + return false; +} diff --git a/amarok/src/engine/helix/hxplayercontrol.h b/amarok/src/engine/helix/hxplayercontrol.h new file mode 100644 index 00000000..760284de --- /dev/null +++ b/amarok/src/engine/helix/hxplayercontrol.h @@ -0,0 +1,221 @@ +/*************************************************************************** + * Copyright (C) 2006 Paul Cifarelli <paul@cifarelli.net> * + * * + * 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. * + * * + ***************************************************************************/ + +#ifndef _HXSPLAY_INCLUDED_ +#define _HXSPLAY_INCLUDED_ + +#include "helix-sp.h" + +class PlayerControl +{ +public: + PlayerControl(); + virtual ~PlayerControl(); + + int getError() const { return m_err; } + + // init functions + void init(const char *corelibpath, const char *pluginslibpath, const char *codecspath, int numPlayers = 1); + void setOutputSink( HelixSimplePlayer::AUDIOAPI out ); + void setDevice( const char *dev ); + int initDirectSS(); + void tearDown(); + + // player functions + int setURL(const char *url, + int playerIndex, + bool islocal = true); + bool done(int playerIndex); + void start(int playerIndex, + bool fadein = false, + unsigned long fadetime = 0); + void stop(int playerIndex = HelixSimplePlayer::ALL_PLAYERS); + void pause(int playerIndex); + void resume(int playerIndex); + void seek(unsigned long pos, int playerIndex); + unsigned long where(int playerIndex) const; + unsigned long duration(int playerIndex) const; + unsigned long getVolume(); + void setVolume(unsigned long vol); + void dispatch(); + void cleanUpStream(int playerIndex); + bool isPlaying(int playerIndex) const; + bool isLocal(int playerIndex) const; + int numPlayers() const { return nNumPlayers; } + + // crossfade + void setFadeout(bool fadeout, unsigned long fadelength, int playerIndex); + + // scope + void addScopeBuf(struct DelayQueue *item, int playerIndex); + DelayQueue *getScopeBuf(int playerIndex); + int getScopeCount(int playerIndex); + int peekScopeTime(unsigned long &t, int playerIndex); + void clearScopeQ(int playerIndex); + + // equalizer + void enableEQ(bool enabled); + bool isEQenabled(); + void updateEQgains(); + + // config stuff + int numPlugins() const; + int getPluginInfo(int index, + const char *&description, + const char *©right, + const char *&moreinfourl) const; + const MimeList *getMimeList() const; + int getMimeListLen() const; + + virtual void play_finished(int /*playerIndex*/) {} + + // stream meta data + HelixSimplePlayer::metaData *getMetaData(int playerIndex); + + // need to simulate these + virtual void notifyUser(unsigned long/*code*/, const char */*moreinfo*/, const char */*moreinfourl*/) {} + virtual void interruptUser(unsigned long/*code*/, const char */*moreinfo*/, const char */*moreinfourl*/) {} + virtual int print2stdout(const char */*fmt*/, ...) { return 0; } + virtual int print2stderr(const char */*fmt*/, ...) { return 0; } + virtual void onContacting(const char */*host*/) {} + virtual void onBuffering(int /*percentage*/) {} + + // children send functions + bool sendnotifyuser(unsigned long code, const char *moreinfo, const char *moreinfourl); + bool sendinterruptuser(unsigned long code, const char *moreinfo, const char *moreinfourl); + bool sendcontacting(const char *host); + bool sendbuffering(int percentage); + +protected: + bool m_eq_enabled; + int m_preamp; + vector<int> m_equalizerGains; + +private: + int m_err; + pid_t iamparent; + int m_index; + int nNumPlayers; + bool m_inited; + + // not yet initd when these are set + HelixSimplePlayer::AUDIOAPI m_api; + char *m_device; + + static const int NUM_SCOPEBUFS = 50; + + struct playerChildren + { + int m_pipeA[2]; + int m_pipeB[2]; + pid_t m_pid; + bool isplaying; + bool islocal; + bool isready; + bool isdead; + HelixSimplePlayer::metaData *md; + int scopecount; + struct DelayQueue *scopebufhead; + struct DelayQueue *scopebuftail; + unsigned long *current_time; + unsigned long *duration; + DelayQueue *q; + int *m_current; + int *m_consumed; + } m_children[2]; + + unsigned long m_volume; + + // supported mime type list + MimeList *mimehead; + int mimelistlen; + + int m_numPlugins; + struct pluginInfo + { + char *description; + char *copyright; + char *moreinfourl; + } **m_pluginInfo; + + // for sharing + struct stateStuff + { + unsigned long current_time; + unsigned long duration; + HelixSimplePlayer::metaData md; + //DelayQueue q[NUM_SCOPEBUFS]; + //unsigned char b[NUM_SCOPEBUFS][65535]; + int m_current; + int m_consumed; + } statestuff[2], *pmapped; + + // msgs are a 1 byte code follows by a 4 byte sz. sz does NOT include the 5 bytes of this hdr + enum msgid + { + READY, + INIT, + SETURL, + START, + STOP, + PAUSE, + RESUME, + SEEK, + DONE, + SETVOLUME, + VOLUME, + OUTPUTSINK, + DEVICE, + SETFADE, + ENABLEEQ, + UPDATEEQGAINS, + SCOPEBUF, + SCOPECLEAR, + METADATAREQ, + METADATA, + PLUGINS, + MIMETYPES, + CONTACTING, + BUFFERING, + NOTIFYUSER, + INTERRUPTUSER, + TEARDOWN, + ERRORCODE + }; + + // utility functions + bool sendsetoutputsink(); + bool sendsetdevice(); + bool sendinit(); + bool sendupdateeqgains(); + + static bool getmessage(int fd, msgid &m, unsigned char *buf, int &sz); + static bool sendmessage(int fd, msgid m, unsigned char *buf, int sz); + static bool sendrequest(int fd, msgid m) { return (sendmessage(fd, m, 0, 0)); } + static bool sendready(int fd) { return (sendrequest(fd, READY)); } + static bool sendsetURL(int fd, const char *url, bool islocal); + static bool sendstart(int fd, bool fadin, unsigned long fadetime); + static bool sendstop(int fd) { return (sendrequest(fd, STOP)); } + static bool sendpause(int fd) { return (sendrequest(fd, PAUSE)); } + static bool sendresume(int fd) { return (sendrequest(fd, RESUME)); } + static bool senddone(int fd) { return (sendrequest(fd, DONE)); } + static bool sendsetvolume(int fd, unsigned long volume); + static bool sendvolume(int fd, unsigned long volume); + static bool sendsetfade(int fd, bool fadeout, unsigned long fadelength); + static bool sendteardown(int fd) { return (sendrequest(fd, TEARDOWN)); } + static bool sendscopeclear(int fd) { return (sendrequest(fd, SCOPECLEAR)); } + + static bool sendplugins(int fd, HelixSimplePlayer *player); + static bool sendmimetypes(int fd, HelixSimplePlayer *player); + static bool sendscopebuf(int fd, DelayQueue *item); +}; + + +#endif diff --git a/amarok/src/engine/kdemm/Makefile.am b/amarok/src/engine/kdemm/Makefile.am new file mode 100644 index 00000000..b040116e --- /dev/null +++ b/amarok/src/engine/kdemm/Makefile.am @@ -0,0 +1,28 @@ +kde_module_LTLIBRARIES = libamarok_kdemmengine_plugin.la +kde_services_DATA = amarok_kdemmengine_plugin.desktop + +INCLUDES = \ + -I$(top_srcdir)/amarok/src/engine \ + -I$(top_srcdir)/amarok/src/plugin \ + -I$(top_srcdir)/amarok/src \ + $(CFLAGS_MAS) $(all_includes) + +libamarok_kdemmengine_plugin_la_LIBADD = \ + $(top_builddir)/amarok/src/libamarok.la \ + $(top_builddir)/amarok/src/plugin/libplugin.la \ + $(LIB_KFILE) $(LIB_KDEUI) $(LIB_KDECORE) \ + $(LIBS_KDEMM) -lkdemm + +libamarok_kdemmengine_plugin_la_SOURCES = \ + kdemmengine.cpp + +libamarok_kdemmengine_plugin_la_LDFLAGS = \ + -module \ + -no-undefined \ + $(KDE_PLUGIN) \ + $(all_libraries) + +METASOURCES = AUTO + + + diff --git a/amarok/src/engine/kdemm/amarok_kdemmengine_plugin.desktop b/amarok/src/engine/kdemm/amarok_kdemmengine_plugin.desktop new file mode 100644 index 00000000..c28b5b85 --- /dev/null +++ b/amarok/src/engine/kdemm/amarok_kdemmengine_plugin.desktop @@ -0,0 +1,119 @@ +[Desktop Entry] +Encoding=UTF-8 +Type=Service +Name=KDEMM Engine +Name[af]=KDEMM Enjin +Name[ar]=محرك KDEMM +Name[bg]=KDEMM +Name[bn]=কেডিই-এমএম ইঞ্জিন +Name[br]=Keflusker KDEMM +Name[ca]=Motor KDEMM +Name[cs]=KDEMM systém +Name[da]=KDEMM-motor +Name[de]=KDEMM +Name[el]=Μηχανή KDEMM +Name[eo]=KDEMM Ilo +Name[es]=Motor KDEMM +Name[et]=KDEMM mootor +Name[eu]=KDEMM motorea +Name[fa]=موتور KDEMM +Name[fi]=KDEMM +Name[fr]=Moteur KDEMM +Name[ga]=Inneall KDEMM +Name[gl]=Motor KDEMM +Name[he]=מנוע שמע KDEMM +Name[hi]=केडीईएमएम इंजिन +Name[hu]=KDEMM alrendszer +Name[is]=KDEMM vél +Name[it]=Motore KDEMM +Name[ja]=KDEMM エンジン +Name[ka]=KDEMM dრავა +Name[km]=ម៉ាស៊ីន KDEMM +Name[lt]=KDEMM variklis +Name[mk]=KDEMM-машина +Name[ms]=Enjin KDEMM +Name[nb]=KDEMM-motor +Name[nds]=KDEMM +Name[ne]=KDEMM इन्जिन +Name[nn]=KDEMM-motor +Name[pa]=KDEMM ਇੰਜਣ +Name[pl]=Wyjście KDEMM +Name[pt]=Motor KDEMM +Name[pt_BR]=Mecanismo KDEMM +Name[ru]=KDEMM +Name[se]=KDEMM-mohtor +Name[sl]=Pogon KDEMM +Name[sq]=Motor KDEMM +Name[sr]=Мотор KDEMM +Name[sr@Latn]=Motor KDEMM +Name[ss]=Motor KDEMM +Name[sv]=KDEMM-gränssnitt +Name[ta]=KDEMM என்ஜின் +Name[tg]=Муҳаррики KDEMM +Name[tr]=KDEMM Motoru +Name[uk]=Рушій KDEMM +Name[uz]=KDEMM tizimi +Name[uz@cyrillic]=KDEMM тизими +Name[wa]=Éndjin KDEMM +Name[zh_CN]=KDEMM 引擎 +Name[zh_TW]=KDEMM 引擎 +Comment=Plugin for Amarok +Comment[af]=Inprop module vir Amarok +Comment[ar]= قابس ( برنامج مضاف الى) AmaroK +Comment[bg]=Приставка за Amarok +Comment[bn]=আমারক-এর জন্য প্লাগিন +Comment[br]=Lugent evit Amarok +Comment[ca]=Connector per l'Amarok +Comment[cs]=Modul pro AmaroK +Comment[de]=Modul für Amarok +Comment[el]=Πρόσθετο για το AmaroK +Comment[eo]=Kromaĵo por Amarok +Comment[es]=Extensión para Amarok +Comment[et]=Amaroki plugin +Comment[fa]=وصله برای amaroK +Comment[fi]=Amarok-liitännäinen +Comment[fr]=Module pour Amarok +Comment[ga]=Breiseán AmaroK +Comment[gl]=Extensión para Amarok +Comment[hu]=Bővítőmodul az Amarokhoz +Comment[is]=Íforrit fyrir Amarok +Comment[it]=Plugin per Amarok +Comment[ja]=Amarok のためのプラグイン +Comment[ka]=მოდული Amarok-ისთვის +Comment[km]=កម្មវិធី​ជំនួយ​សម្រាប់ Amarok +Comment[lt]=Amarok įskiepis +Comment[mk]=Приклучок за Амарок +Comment[nb]=Programtillegg for Amarok +Comment[nds]=Moduul för Amarok +Comment[ne]=अमारोकका लागि प्लगइन +Comment[nl]=Plugin voor Amarok +Comment[nn]=Programtillegg for Amarok +Comment[pa]=ਅਮਰੋਕ ਲਈ ਪਲੱਗਇਨ +Comment[pl]=Wtyczka Amaroka +Comment[pt]='Plugin' para o Amarok +Comment[pt_BR]=Plugin para o Amarok +Comment[ru]=Модуль amaroK +Comment[se]=Lassemoduvla Amarok:ii +Comment[sk]=Amarok modul +Comment[sr]=Прикључак за Amarok +Comment[sr@Latn]=Priključak za Amarok +Comment[sv]=Insticksprogram för Amarok +Comment[th]=โปรแกรมเสริมสำหรับ Amarok +Comment[tr]=Amarok için Eklenti +Comment[uk]=Втулок для Amarok +Comment[uz]=Amarok uchun plagin +Comment[uz@cyrillic]=Amarok учун плагин +Comment[wa]=Tchôke-divins po Amarok +Comment[zh_CN]=Amarok 插件 +Comment[zh_TW]=amaroK 插件 + +ServiceTypes=Amarok/Plugin +X-KDE-Library=libamarok_kdemmengine_plugin +X-KDE-Amarok-name=KDEMM Engine +X-KDE-Amarok-plugintype=engine +X-KDE-Amarok-authors=Roland Gigler +X-KDE-Amarok-email=rolandg@web.de +X-KDE-Amarok-rank=0 +X-KDE-Amarok-version=1 +X-KDE-Amarok-framework-version=32 + diff --git a/amarok/src/engine/kdemm/kdemmengine.cpp b/amarok/src/engine/kdemm/kdemmengine.cpp new file mode 100644 index 00000000..6c4174de --- /dev/null +++ b/amarok/src/engine/kdemm/kdemmengine.cpp @@ -0,0 +1,261 @@ +/*************************************************************************** + kdemmengine.cpp - KDE Multimedia (KDEMM) interface + ------------------- +begin : 2004-10-01 +copyright : (C) 2004 by Roland Gigler +email : rolandg@web.de +what : interface to the KDE Multimedia interface (KDEMM) +***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#include "enginebase.h" +#include "engineobserver.h" + +#include <assert.h> +#include <math.h> //setVolume(), timerEvent() +#include <string> +#include <vector> + +#include <qdir.h> +#include <qdom.h> +#include <qfile.h> +#include <qlayout.h> +#include <qstring.h> +#include <qtextstream.h> +#include <qtimer.h> + +#include <kapplication.h> +#include <kconfig.h> +#include <kdebug.h> +#include <kfileitem.h> +#include <kgenericfactory.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <kmimetype.h> +#include <kstandarddirs.h> +#include <kurl.h> + +#include <kdemm/simpleplayer.h> + +#include "kdemmengine.h" + +#define KDEMM_TIMER 250 + +AMAROK_EXPORT_PLUGIN( KDEMMEngine ) + +using namespace KDE::Multimedia; + +KDEMMEngine::KDEMMEngine( ) + : Engine::Base() +// , m_scopeId( 0 ) +// , m_xfadeFadeout( false ) +// , m_xfadeValue( 0.0 ) +// , m_xfadeCurrent( "invalue2" ) + , m_state( Engine::Empty ) + , m_pPlayingTimer( new QTimer( this ) ) +{ + kdDebug() << k_funcinfo << endl; + m_player = new SimplePlayer(); +} + + +KDEMMEngine::~KDEMMEngine() +{ + kdDebug() << "BEGIN " << k_funcinfo << endl; + m_pPlayingTimer->stop(); + delete m_player; + kdDebug() << "END " << k_funcinfo << endl; +} + + +bool KDEMMEngine::init() +{ + kdDebug() << "BEGIN " << k_funcinfo << endl; + /* + m_scopeSize = 1 << scopeSize; + m_restoreEffects = restoreEffects; + m_mixerHW = -1; //initialize + */ + + connect ( m_pPlayingTimer, SIGNAL( timeout() ), this, SLOT( playingTimeout() ) ); + + kdDebug() << "END " << k_funcinfo << endl; + return true; +} + + +//////////////////////////////////////////////////////////////////////////////// +// PUBLIC METHODS +//////////////////////////////////////////////////////////////////////////////// + +bool KDEMMEngine::canDecode( const KURL &url ) const +{ + static QStringList list; + + kdDebug() << "BEGIN " << k_funcinfo << endl; + kdDebug() << " Param: url: " << url << endl; + //kdDebug() << " url.protocol() >" << url.protocol() <<"<"<< endl; + + if (url.protocol() == "http" ) return false; + + // TODO determine list of supported MimeTypes/Extensions from KDEMM + list += QString("audio/x-mp3"); + + KFileItem fileItem( KFileItem::Unknown, KFileItem::Unknown, url, false ); //false = determineMimeType straight away + KMimeType::Ptr mimetype = fileItem.determineMimeType(); + kdDebug() << "mimetype: " << mimetype->name().latin1() << endl; + + return list.contains( mimetype->name().latin1() ); +} // canDecode + + +bool KDEMMEngine::load( const KURL& url, bool stream ) +{ + kdDebug() << "BEGIN " << k_funcinfo << endl; + + m_isStream = stream; + kdDebug() << " m_url: " << m_url << endl; + kdDebug() << " Param: stream: " << stream << endl; + kdDebug() << " Param: url " << url << endl; + + if ( !url.isLocalFile() ) { // for now + return false; + } + + if ( m_url == url ) { + return true; + } else { + stop(); + } + m_url = url; + // the KDEMM SimplePlayer dows the loading in the play method + + m_state = Engine::Idle; + + kdDebug() << "END " << k_funcinfo << endl; + return true; +} // load + + +bool KDEMMEngine::play( unsigned int offset) +{ + kdDebug() << "BEGIN " << k_funcinfo << endl; + kdDebug() << " param: offset " << offset << endl; + + m_player->play(m_url); + m_pPlayingTimer->start(KDEMM_TIMER, false); + m_state = Engine::Playing; + emit stateChanged( Engine::Playing ); + + kdDebug() << "END " << k_funcinfo << endl; + return true; +} // play + + +/* + * return current position in milli seconds +*/ +uint KDEMMEngine::position() const +{ + uint pos=0; + //kdDebug() << "BEGIN " << k_funcinfo << endl; + + //kdDebug() << " totalTime: " << m_player->totalTime() << endl; + //kdDebug() << " currentTime: " << m_player->currentTime() << endl; + + pos = m_player->currentTime(); + + //kdDebug() << "END " << k_funcinfo << endl; + return (pos); +} // position + + +////////////////////////////////////////////////////////////////////// + +void KDEMMEngine::stop() +{ + kdDebug() << "BEGIN " << k_funcinfo << endl; + + //switch xfade channels +/* m_xfadeCurrent = ( m_xfadeCurrent == "invalue1" ) ? "invalue2" : "invalue1"; + + if ( m_xfadeValue == 0.0 ) + m_xfadeValue = 1.0; +*/ + + m_player->stop(); + m_pPlayingTimer->stop(); + + m_state = Engine::Idle; // Empty + emit stateChanged( m_state ); + kdDebug() << "END " << k_funcinfo << endl; +} + + +void KDEMMEngine::pause() +{ + kdDebug() << "BEGIN " << k_funcinfo << endl; + + // KDEMM: pause() cannot do un-pause, do it manually + if (m_state == Engine::Paused){ + m_player->play(m_url); + m_pPlayingTimer->start(KDEMM_TIMER, false); + m_state = Engine::Playing; + } else { + m_player->pause(); + m_pPlayingTimer->stop(); + m_state = Engine::Paused; + } + + emit stateChanged( m_state ); + kdDebug() << "END " << k_funcinfo << endl; +} // pause + + +void KDEMMEngine::seek( unsigned int ms ) +{ + kdDebug() << "BEGIN " << k_funcinfo << endl; + kdDebug() << " param: ms " << ms << endl; + m_player->seek(ms); + kdDebug() << "END " << k_funcinfo << endl; +} // seek + + +void KDEMMEngine::setVolumeSW( unsigned int percent ) +{ + kdDebug() << "BEGIN " << k_funcinfo << endl; + kdDebug() << " Param: percent " << percent << endl; + + + float vol = percent*0.01; + kdDebug() << " setting vol to " << vol << endl; + m_player->setVolume(vol); + + kdDebug() << "END " << k_funcinfo << endl; +} // setVolumeSW + +//////////////////////////////////////////////////////////////////////////////// +// PRIVATE METHODS +//////////////////////////////////////////////////////////////////////////////// + +void KDEMMEngine::playingTimeout() //SLOT +{ + //kdDebug() << "BEGIN " << k_funcinfo << endl; + if( !m_player->isPlaying() ) { + m_pPlayingTimer->stop(); + m_state = Engine::Idle; + emit trackEnded(); + } + //kdDebug() << "END " << k_funcinfo << endl; +} // playingTimeout + +#include "kdemmengine.moc" + diff --git a/amarok/src/engine/kdemm/kdemmengine.h b/amarok/src/engine/kdemm/kdemmengine.h new file mode 100644 index 00000000..42bb23fb --- /dev/null +++ b/amarok/src/engine/kdemm/kdemmengine.h @@ -0,0 +1,83 @@ +/*************************************************************************** + kdemmengine.h - KDEMM audio interface + ------------------- +begin : Jul 04 2004 +copyright : (C) 2004 by Roland Gigler +email : rolandg@web.de +what : interface to KDE Multimedia (KDEMM) +***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#ifndef AMAROK_KDEMMENGINE_H +#define AMAROK_KDEMMENGINE_H + +#include "enginebase.h" + +#include <vector> + +#include <qguardedptr.h> +#include <qmap.h> +#include <qstringlist.h> +#include <qwidget.h> + + +class QTimer; +class KURL; + +namespace KDE { namespace Multimedia { class SimplePlayer; } } + +class KDEMMEngine : public Engine::Base +{ + Q_OBJECT + + public: + KDEMMEngine(); + ~KDEMMEngine(); + + bool init(); + + bool initMixer( bool hardware ); + bool canDecode( const KURL& ) const; + uint position() const; + Engine::State state() const {return m_state;} +// std::vector<float>* scope(); +// bool decoderConfigurable(); +// void configureDecoder(); + bool supportsXFade() const { return false; } + + public slots: + bool load( const KURL&, bool stream ); + bool play( unsigned int offset = 0); + void stop(); + void pause(); + void seek( unsigned int ms ); + void setVolumeSW( unsigned int percent ); + private slots: + void playingTimeout(); + private: + //void startXfade(); + ///////////////////////////////////////////////////////////////////////////////////// + // ATTRIBUTES + ///////////////////////////////////////////////////////////////////////////////////// + Engine::State m_state; + //long m_scopeId; + //int m_scopeSize; + //bool m_xfadeFadeout; + //float m_xfadeValue; + //QString m_xfadeCurrent; + KURL m_url; + KDE::Multimedia::SimplePlayer *m_player; + + QTimer* m_pPlayingTimer; +}; + +#endif // AMAROK_KDEMM_ENGINE + diff --git a/amarok/src/engine/mas/HOWTO b/amarok/src/engine/mas/HOWTO new file mode 100644 index 00000000..f5977805 --- /dev/null +++ b/amarok/src/engine/mas/HOWTO @@ -0,0 +1,29 @@ +HOWTO set up the MAS support + + +donwload MAS code from their website www.mediaapplicationserver.net + - mas-0.6.3.tar.gz + - mas-devtools-0.6.3.tar.gz + - mas-control-apps-0.6.3.tar.gz + or get them from their CVS + + - mas-codec_mp1a_mad-0.6.3.tar.gz (!not in CVS ??) + + +compile, install + +add /usr/local/mas/lib to /etc/ld.so.conf +call ldconfig +edit /usr/local/mas/mas-launch + fix 1st line: #!/bin/sh + move line 12 to line 25: export LD_LIBRARY_PATH # somehow i need this + +start mas-launch (superuser) + +you can find the logfile in /usr/local/mas/log + + +start masplayer for testing + + + diff --git a/amarok/src/engine/mas/Makefile.am b/amarok/src/engine/mas/Makefile.am new file mode 100644 index 00000000..2a56d4e6 --- /dev/null +++ b/amarok/src/engine/mas/Makefile.am @@ -0,0 +1,28 @@ +kde_module_LTLIBRARIES = libamarok_masengine_plugin.la +kde_services_DATA = amarok_masengine_plugin.desktop + +INCLUDES = \ + -I$(top_srcdir)/amarok/src/engine \ + -I$(top_srcdir)/amarok/src/plugin \ + -I$(top_srcdir)/amarok/src \ + $(CFLAGS_MAS) $(all_includes) + +libamarok_masengine_plugin_la_LIBADD = \ + $(top_builddir)/amarok/src/engine/libengine.la \ + $(top_builddir)/amarok/src/plugin/libplugin.la \ + $(LIB_KFILE) $(LIB_KDEUI) $(LIB_KDECORE) \ + $(LIBS_MAS) + +libamarok_masengine_plugin_la_SOURCES = \ + masengine.cpp + +libamarok_masengine_plugin_la_LDFLAGS = \ + -module \ + -no-undefined \ + $(KDE_PLUGIN) \ + $(all_libraries) + +METASOURCES = AUTO + + + diff --git a/amarok/src/engine/mas/TODO b/amarok/src/engine/mas/TODO new file mode 100644 index 00000000..a717754a --- /dev/null +++ b/amarok/src/engine/mas/TODO @@ -0,0 +1,12 @@ +TODO + +- ask MAS which mimetypes it can play (MAS) +- get/set position on source device (MAS) + (position is workarounded in the Engine. Grrgh) +- more decoders (OGG, ...) (MAS) + +- CrossFading ? +- scope ? +- configuration ? +- network ? + diff --git a/amarok/src/engine/mas/amarok_masengine_plugin.desktop b/amarok/src/engine/mas/amarok_masengine_plugin.desktop new file mode 100644 index 00000000..b2fdcdd9 --- /dev/null +++ b/amarok/src/engine/mas/amarok_masengine_plugin.desktop @@ -0,0 +1,119 @@ +[Desktop Entry] +Encoding=UTF-8 +Type=Service +Name=MAS Engine +Name[af]=MAS Enjin +Name[ar]=محرك MAS +Name[bg]=MAS +Name[bn]=এম-এ-এস ইঞ্জিন +Name[br]=Keflusker MAS +Name[ca]=Motor MAS +Name[cs]=MAS systém +Name[da]=MAS-motor +Name[de]=MAS +Name[el]=Μηχανή MAS +Name[eo]=MAS Ilo +Name[es]=Motor MAS +Name[et]=MAS mootor +Name[eu]=MAS motorea +Name[fa]=موتور MAS +Name[fi]=MAS +Name[fr]=Moteur MAS +Name[ga]=Inneall MAS +Name[gl]=Motor MAS +Name[he]=מנוע שמע MAS +Name[hi]=एमएएस इंजिन +Name[hu]=MAS alrendszer +Name[is]=MAS vél +Name[it]=Motore MAS +Name[ja]=MAS エンジン +Name[ka]=MAS dრავა +Name[km]=ម៉ាស៊ីន MAS +Name[lt]=MAS variklis +Name[mk]=MAS-машина +Name[ms]=Enjin MAS +Name[nb]=MAS-motor +Name[nds]=MAS +Name[ne]=मास इन्जिन +Name[nn]=MAS-motor +Name[pa]=MAS ਇੰਜਣ +Name[pl]=Wyjście MAS +Name[pt]=Motor MAS +Name[pt_BR]=Mecanismo MAS +Name[ru]=MAS +Name[se]=MAS-mohtor +Name[sl]=Pogon MAS +Name[sq]=Motor MAS +Name[sr]=Мотор MAS +Name[sr@Latn]=Motor MAS +Name[ss]=Motor MAS +Name[sv]=MAS-gränssnitt +Name[ta]=MAS என்ஜின் +Name[tg]=Муҳаррики MAS +Name[tr]=MAS Motoru +Name[uk]=Рушій MAS +Name[uz]=MAS tizimi +Name[uz@cyrillic]=MAS тизими +Name[wa]=Éndjin MAS +Name[zh_CN]=MAS 引擎 +Name[zh_TW]=MAS 解碼引擎 +Comment=Plugin for Amarok +Comment[af]=Inprop module vir Amarok +Comment[ar]= قابس ( برنامج مضاف الى) AmaroK +Comment[bg]=Приставка за Amarok +Comment[bn]=আমারক-এর জন্য প্লাগিন +Comment[br]=Lugent evit Amarok +Comment[ca]=Connector per l'Amarok +Comment[cs]=Modul pro AmaroK +Comment[de]=Modul für Amarok +Comment[el]=Πρόσθετο για το AmaroK +Comment[eo]=Kromaĵo por Amarok +Comment[es]=Extensión para Amarok +Comment[et]=Amaroki plugin +Comment[fa]=وصله برای amaroK +Comment[fi]=Amarok-liitännäinen +Comment[fr]=Module pour Amarok +Comment[ga]=Breiseán AmaroK +Comment[gl]=Extensión para Amarok +Comment[hu]=Bővítőmodul az Amarokhoz +Comment[is]=Íforrit fyrir Amarok +Comment[it]=Plugin per Amarok +Comment[ja]=Amarok のためのプラグイン +Comment[ka]=მოდული Amarok-ისთვის +Comment[km]=កម្មវិធី​ជំនួយ​សម្រាប់ Amarok +Comment[lt]=Amarok įskiepis +Comment[mk]=Приклучок за Амарок +Comment[nb]=Programtillegg for Amarok +Comment[nds]=Moduul för Amarok +Comment[ne]=अमारोकका लागि प्लगइन +Comment[nl]=Plugin voor Amarok +Comment[nn]=Programtillegg for Amarok +Comment[pa]=ਅਮਰੋਕ ਲਈ ਪਲੱਗਇਨ +Comment[pl]=Wtyczka Amaroka +Comment[pt]='Plugin' para o Amarok +Comment[pt_BR]=Plugin para o Amarok +Comment[ru]=Модуль amaroK +Comment[se]=Lassemoduvla Amarok:ii +Comment[sk]=Amarok modul +Comment[sr]=Прикључак за Amarok +Comment[sr@Latn]=Priključak za Amarok +Comment[sv]=Insticksprogram för Amarok +Comment[th]=โปรแกรมเสริมสำหรับ Amarok +Comment[tr]=Amarok için Eklenti +Comment[uk]=Втулок для Amarok +Comment[uz]=Amarok uchun plagin +Comment[uz@cyrillic]=Amarok учун плагин +Comment[wa]=Tchôke-divins po Amarok +Comment[zh_CN]=Amarok 插件 +Comment[zh_TW]=amaroK 插件 + +ServiceTypes=Amarok/Plugin +X-KDE-Library=libamarok_masengine_plugin +X-KDE-Amarok-name=MAS Engine +X-KDE-Amarok-plugintype=engine +X-KDE-Amarok-authors=Roland Gigler +X-KDE-Amarok-email=rolandg@web.de +X-KDE-Amarok-rank=0 +X-KDE-Amarok-version=1 +X-KDE-Amarok-framework-version=32 + diff --git a/amarok/src/engine/mas/masengine.cpp b/amarok/src/engine/mas/masengine.cpp new file mode 100644 index 00000000..3abe6f9c --- /dev/null +++ b/amarok/src/engine/mas/masengine.cpp @@ -0,0 +1,591 @@ +/*************************************************************************** + masengine.cpp - MAS audio interface + ------------------- +begin : 2004-07-20 +copyright : (C) 2004-05 by Roland Gigler +email : rolandg@web.de +what : interface to the Media Application Server (MAS) +***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#define DEBUG_PREFIX "MAS-Engine" + +#include "debug.h" +#include "enginebase.h" +#include "engineobserver.h" + +#include <assert.h> +#include <math.h> //setVolume(), timerEvent() +#include <string> +//#include <vector> + +#include <qtimer.h> +#include <qfile.h> + +#include <kapplication.h> +#include <kconfig.h> +#include <kfileitem.h> +#include <kgenericfactory.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <kmimetype.h> +#include <kstandarddirs.h> +#include <kurl.h> + +#include "masengine.h" +//#define DB_CUTOFF -40.0 +#define BUFFER_TIME_MS 300 +#define POSTOUT_TIME_MS 100 +//#define QUERY_MIX_VOLUME 0 +//#define QUERY_MIX_EPSILON 5 + + + +AMAROK_EXPORT_PLUGIN( MasEngine ) + + +MasEngine::MasEngine() + : Engine::Base() + , m_inited (false) +// , m_scopeId( 0 ) +// , m_xfadeFadeout( false ) +// , m_xfadeValue( 0.0 ) +// , m_xfadeCurrent( "invalue2" ) + , m_lastKnownPosition( 0 ) + , m_state( Engine::Empty ) + , m_pPlayingTimer( new QTimer( this ) ) + +{ + DEBUG_FUNC_INFO + + // NOT SUPPORTED + //addPluginProperty( "HasConfigure", "true" ); + //addPluginProperty( "HasCrossfading", "true" ); + //addPluginProperty( "HasEqualizer", "true" ); +} + + +MasEngine::~MasEngine() +{ + DEBUG_BLOCK + + if ( m_inited ) stop(); + m_pPlayingTimer->stop(); + killTimers(); +} + + +bool MasEngine::init() +{ + DEBUG_BLOCK + + if (!masinit() ) { + KMessageBox::error( 0, i18n("<h3>Amarok could not initialise MAS.</h3>" + "<p>Check for a running mas daemon.</p>") ); + error() << " connecting to MAS daemon failed. Aborting. " << endl; + debug() << " Please restart amarok." << endl; + debug() << k_funcinfo << " returns false !" << endl; + return false; + } + m_inited=true; // we connected to MAS + + connect ( m_pPlayingTimer, SIGNAL( timeout() ), this, SLOT( playingTimeout() ) ); + + emit statusText( "MAS Engine inited :-)"); + return true; +} + + +//////////////////////////////////////////////////////////////////////////////// +// PUBLIC METHODS +//////////////////////////////////////////////////////////////////////////////// + +bool MasEngine::canDecode( const KURL &url ) const +{ + DEBUG_BLOCK + + QStringList list; + bool playable; + + debug() << " Param: url: " << url << endl; + //debug() << " url.protocol() >" << url.protocol() <<"<"<< endl; + + if (url.protocol() == "http" ) return false; + + // TODO determine list of supported MimeTypes/Extensions from MAS + list += QString("audio/x-mp3"); + + KFileItem fileItem( KFileItem::Unknown, KFileItem::Unknown, url, false ); //false = determineMimeType straight away + KMimeType::Ptr mimetype = fileItem.determineMimeType(); + debug() << "mimetype: " << mimetype->name().latin1() << endl; + + playable = list.contains( mimetype->name().latin1() ); + if ( !playable ) + warning() << "Mimetype is not playable by MAS (" << url << ")" << endl; + + return playable; +} // canDecode + + +bool MasEngine::load( const KURL& url, bool stream ) +{ + DEBUG_BLOCK + struct mas_package pkg; + char pbuf[10240]; + int pos = 0; + + m_isStream = stream; + debug() << " m_url: " << m_url << endl; + debug() << " Param: stream: " << stream << endl; + debug() << " Param: url " << url << endl; + + if ( !url.isLocalFile() ) { // for now + debug() << " only local files are supported (for now)" << endl; + return false; + } + + if ( !canDecode(url) ) { + debug() << " cannot decode!" << endl; + return false; + } + + if ( m_url == url ) { + return true; + } else { + stop(); + } + m_url = url; + + /* send fresh data to MAS */; + masc_setup_package( &pkg, pbuf, sizeof pbuf, MASC_PACKAGE_STATIC ); + masc_pushk_int16( &pkg, (char*)"pos", pos ); + //masc_push_string( &pkg, (char *)m_url.path().latin1() ); + + QCString cs= QFile::encodeName( m_url.path()); + const char *pcs = cs; + masc_push_string( &pkg, (char *)pcs); + masc_finalize_package( &pkg ); + + mas_set( m_mp1a_source_device, (char*)"playlist", &pkg ); + masc_strike_package( &pkg ); + + mas_dev_show_state (m_mp1a_source_device); + mas_source_flush( m_codec ); + + m_lastKnownPosition = 0; + m_state = Engine::Idle; + + return true; +} // load + + +bool MasEngine::play( unsigned int offset) +{ + DEBUG_BLOCK + struct mas_package pkg; + char pbuf[10240]; + + debug() << " param: offset " << offset << endl; + if ( m_state != Engine::Playing ) { + /* change the track */ + masc_setup_package( &pkg, pbuf, sizeof pbuf, MASC_PACKAGE_STATIC ); + masc_pushk_int16( &pkg, (char*)"pos", 1 ); + masc_finalize_package( &pkg ); + mas_set( m_mp1a_source_device, (char*)"ctrack", &pkg ); + masc_finalize_package( &pkg ); + + mas_source_flush( m_codec ); + mas_source_play( m_mp1a_source_device ); + mas_source_play_on_mark( m_sbuf ); + + debug() << "mas_source_play()" << endl; + } + + m_pPlayingTimer->start(MAS_TIMER, false); + + m_state = Engine::Playing; + emit stateChanged( Engine::Playing ); + + return true; +} // play +uint +MasEngine::length() const +{ + DEBUG_BLOCK + char pbuf[128]; + struct mas_package pkg; + struct mas_package nugget; + float trklen; + + masc_setup_package( &pkg, pbuf, sizeof pbuf, MASC_PACKAGE_STATIC ); + masc_pushk_int16( &pkg, (char *)"pos", 1 ); + masc_finalize_package( &pkg ); + + mas_get( m_mp1a_source_device, (char *)"trklen", &pkg, &nugget ); + masc_strike_package( &pkg ); + + masc_pullk_float( &nugget, (char *)"trklen", &trklen ); + masc_strike_package( &nugget ); + + debug() << "trklen: " << trklen << endl; + return uint(trklen*1000); +} // position + +/* + * return current position in milli seconds +*/ +uint MasEngine::position() const +{ + return m_lastKnownPosition; +} // position + + +////////////////////////////////////////////////////////////////////// + + +void MasEngine::playingTimeout() //SLOT +{ + //DEBUG_BLOCK + m_lastKnownPosition += MAS_TIMER; + + // ask MAS if it's still playing + struct mas_package nugget; + int16 pos; + mas_get( m_mp1a_source_device, (char *)"ctrack", NULL, &nugget ); + masc_pullk_int16( &nugget, (char *)"pos", &pos ); + masc_strike_package( &nugget ); + + if ( pos == 0 ) { + m_pPlayingTimer->stop(); + + m_state = Engine::Idle; + emit trackEnded(); + } + +} // playingTimeout //SLOT + + +void MasEngine::stop() +{ + DEBUG_BLOCK + + //switch xfade channels +/* m_xfadeCurrent = ( m_xfadeCurrent == "invalue1" ) ? "invalue2" : "invalue1"; + + if ( m_xfadeValue == 0.0 ) + m_xfadeValue = 1.0; +*/ + mas_source_stop( m_mp1a_source_device ); + mas_source_stop( m_sbuf ); + debug() << "performed: mas_source_stop()" << endl; + + m_pPlayingTimer->stop(); + m_state = Engine::Empty; + m_lastKnownPosition = 0; + + //emit stateChanged( m_state ); +} + + +void MasEngine::pause() +{ + DEBUG_BLOCK + + if(m_state == Engine::Paused) { + mas_source_play( m_mp1a_source_device ); + mas_source_play( m_sbuf ); + debug() << "performed: mas_source_play()" << endl; + + m_state = Engine::Playing; + m_pPlayingTimer->start(MAS_TIMER, false); + + } else { + mas_source_pause( m_mp1a_source_device ); + mas_source_pause( m_sbuf ); + debug() << "performed: mas_source_pause()" << endl; + + m_state = Engine::Paused; + m_pPlayingTimer->stop(); + } + + emit stateChanged( m_state ); +} // pause + + +// TODO depends on MAS support +void MasEngine::seek( unsigned int ms ) +{ + //DEBUG_BLOCK + AMAROK_NOTIMPLEMENTED + //debug() << " param: ms " << ms << endl; +} // seek + + +void MasEngine::setVolumeSW( unsigned int percent ) +{ + DEBUG_BLOCK + debug() << " Param: percent " << percent << endl; + +#ifdef USE_MIX_VOLUME_GENERIC + // MAS takes values from 0 to 128 + int16 vol = (int16)(percent*1.28); + debug() << " setting vol to " << vol << endl; + + // the more generic way + struct mas_package pkg; + char buffer[128]; + + masc_setup_package( &pkg, buffer, sizeof buffer, MASC_PACKAGE_STATIC ); + masc_push_int32( &pkg, m_mix_sink->portnum ); + masc_push_uint16( &pkg, vol ); + masc_finalize_package( &pkg ); + + mas_set( m_mix_device, (char *)"multiplier", &pkg ); + masc_strike_package( &pkg ); + debug() << " after mas_set" << endl; +#else + // use the mix api + double vol = percent*0.01; + debug() << " setting vol to " << vol << endl; + mas_mix_attenuate_linear( m_mix_device, m_mix_sink, vol); +#endif + +} // setVolumeSW + +//////////////////////////////////////////////////////////////////////////////// +// PRIVATE METHODS +//////////////////////////////////////////////////////////////////////////////// +/* +void MasEngine::timerEvent( QTimerEvent* ) +{ + if ( m_xfadeValue > 0.0 ) + { + m_xfadeValue -= ( m_xfadeLength ) ? 1.0 / m_xfadeLength * ARTS_TIMER : 1.0; + + if ( m_xfadeValue <= 0.0 ) + { + m_xfadeValue = 0.0; + } + float value; + if ( m_xfadeFadeout ) + value = 1.0 - log10( ( 1.0 - m_xfadeValue ) * 9.0 + 1.0 ); + else + value = log10( m_xfadeValue * 9.0 + 1.0 ); + + } +} +*/ + +bool MasEngine::masinit() +{ + DEBUG_BLOCK + int32 err; + + masc_log_verbosity( MAS_VERBLVL_DEBUG ); + masc_log_message( 0, (char*)"amarok/MASengine" ); + masc_log_message( 0, (char*)"tries to plays audio using MAS ;-)"); + masc_log_message( 0, (char*)"" ); + + err = mas_init(); + debug() << " mas_init err:" << err << endl; + if (err < 0) + { + warning() << "MAS daemon not running. Starting! Please provide password..." << endl; + // masd seems not to be running, let's try to run it + QCString cmdline; + cmdline = QFile::encodeName( KStandardDirs::findExe( QString::fromLatin1( "kdesu" ) ) ); + // TODO !!!hardcoded path + cmdline += " -n -f /usr/local/mas/log/mas-1.log -c "; + cmdline += "/usr/local/mas/bin/mas-launch"; + + debug() << " cmdline: " << cmdline << endl; + int status = ::system( cmdline ); + debug() << " status: " << status << endl; + + debug() << " give the MAS daemon some time (3s) ... " << endl; + ::sleep( 3 ); + + if ( status != -1 && WIFEXITED( status ) ) + { + int time = 0; + do + { + debug() << " time: " << time << endl; + // every time it fails, we should wait a little longer + // between tries + ::sleep( 1 + time / 2 ); + err = mas_init(); + debug() << " mas_init err: " << err << endl; + } + while ( ++time < 5 && ( err < 0 ) ); + } + } + + if (err < 0) + { + QString text = "Connection to MAS daemon FAILED. Please check your installation."; + error() << text << endl; + emit statusText( text ); + return false; + } + debug() << "Connection to MAS daemon is up :-)" << endl; + + struct mas_data_characteristic* dc; + struct mas_package nugget; + mas_device_t anx; + mas_port_t tmp_source; + mas_channel_t local; + + err = mas_get_local_control_channel( &local ); + if ( err < 0 ) masc_logerror( err, "getting local control channel" ); + debug() << " after mas_get_local_control_channel" << endl; + + /* Get the id of the sample clock provided by the anx device -- if + we can! */ + err = mas_asm_get_device_by_name( (char*)"anx", &anx ); + mas_assert( err >= 0, (char*)"Couldn't get anx device" ); + debug() << " after mas_asm_get_device_by_name" << endl; + + m_sink_clkid = 0; + err = mas_get( anx, (char*)"mc_clkid", NULL, &nugget ); + if ( err >= 0 ) + { + masc_pull_int32( &nugget, &m_sink_clkid ); + masc_strike_package( &nugget ); + } + debug() <<" after mas_get (mc_clkid)" << endl; + + /* CODEC */ + err = mas_asm_instantiate_device( (char*)"codec_mp1a_mad", 0, 0, &m_codec ); + if (err < 0 ) + { + masc_log_message( MAS_VERBLVL_INFO, (char*)"Couldn't instantiate mp1a_mad codec, trying mp1a codec."); + err = mas_asm_instantiate_device( (char*)"codec_mp1a", 0, 0, &m_codec ); + mas_assert( err >= 0, (char*)"Couldn't instantiate MPEG codec"); + } + debug() << " after mas_asm_instantiate_device (codec_mp1a_mad)" << endl; + + /* source - instantiated on the local MAS server */ + err = mas_asm_instantiate_device_on_channel( (char*)"source_mp1a", 0, 0, &m_mp1a_source_device, local ); + mas_assert( err >= 0, (char*)"Couldn't instantiate source_mp1a" ); + + /* buffer */ + err = mas_asm_instantiate_device( (char*)"sbuf", 0, 0, &m_sbuf ); + mas_assert( err >= 0, (char*)"Couldn't instantiate sbuf device" ); + debug() << " after mas_asm_instantiate_device (sbuf)" << endl; + + m_visual = NULL; +#ifdef USE_VISUAL + err = mas_asm_instantiate_device( (char*)"visual", 0, 0, &m_visual ); + if ( err < 0 ) + { + masc_log_message( 0, (char*)"Couldn't instantiate visual device. It's okay, I just won't use it." ); + m_visual = NULL; + } + debug() << " after mas_asm_instantiate_device (visual)" << endl; +#endif + + + /* get a handle to the mixer */ + err = mas_asm_get_device_by_name( (char*)"mix", &m_mix_device ); + mas_assert( err >= 0, (char*)"Couldn't get mixer device" ); + debug() << " after mas_asm_get_device_by_name (mix)" << endl; + + /* start making connections: + * + * source->codec->visual->mix + */ + + err = mas_asm_connect_devices( m_mp1a_source_device, m_codec, (char*)"source", (char*)"sink" ); + mas_assert( err >= 0, "Couldn't connect MPEG source to MPEG codec" ); + + dc = masc_make_audio_basic_dc( MAS_LINEAR_FMT, 44100, 20, 2, MAS_HOST_ENDIAN_FMT ); + mas_assert( dc, "Couldn't create audio data characteristic." ); + + err = mas_asm_get_port_by_name( m_mix_device, (char*)"default_mix_sink", &m_mix_sink ); + mas_assert( err >= 0, "Couldn't get default mixer sink" ); + debug() << " m_mix_sink:" << m_mix_sink << endl; + + err = mas_asm_connect_devices_dc( m_codec, m_sbuf, (char*)"source", (char*)"sink", dc ); + mas_assert( err >= 0, "Couldn't connect MPEG codec to sbuf" ); + debug() << " after mas_asm_connect_device_dc (source, sink)" << endl; + + if (m_visual != NULL) + { + err = mas_asm_connect_devices_dc( m_sbuf, m_visual, (char*)"source", (char*)"sink", dc ); + mas_assert( err >= 0, "Couldn't connect sbuf to visual device." ); + + err = mas_asm_get_port_by_name( m_visual, (char*)"source", &tmp_source ); + mas_assert( err >= 0, "Couldn't get visual source port" ); + + err = mas_asm_connect_source_sink( tmp_source, m_mix_sink, dc ); + mas_assert( err >= 0, "Couldn't connect visual device to mixer sink." ); + } + else + { + err = mas_asm_get_port_by_name( m_sbuf, (char*)"source", &tmp_source ); + mas_assert( err >= 0, "Couldn't get sbuf source port" ); + + err = mas_asm_connect_source_sink( tmp_source, m_mix_sink, dc ); + mas_assert( err >= 0, "Couldn't connect sbuf to mix device." ); + } + debug() << " after ... visual ..." << endl; + + /* set the buffer time */ + masc_setup_package( &nugget, NULL, 0, 0 ); + masc_pushk_uint32( &nugget, (char*)"buftime_ms", BUFFER_TIME_MS ); + masc_finalize_package( &nugget ); + mas_set( m_sbuf, (char*)"buftime_ms", &nugget ); + masc_strike_package( &nugget ); + + debug() << " after ... masc_strike_package ..." << endl; + + masc_setup_package( &nugget, NULL, 0, 0 ); + masc_pushk_uint32( &nugget, "postout_time_ms", POSTOUT_TIME_MS ); + masc_finalize_package( &nugget ); + mas_set( m_sbuf, "postout_time_ms", &nugget ); + masc_strike_package( &nugget ); + + /* If we can use a sample clock, let's do it... */ + if ( m_sink_clkid > 0 ) + { + mas_get_mc_device( &m_sink_mc ); + mas_get_mc_device_on_channel( &m_source_mc, local ); + debug() << " after ... masc_get_mc_device_on_channel ..." << endl; + + //get_measured_sample_freq(); +/* + m_source_clkid = mas_mc_add_fixed_clock( "player", m_measured_sample_freq, local ); + printf (" after ... masc_mc_add_fixed_clock ...\n"); + masc_log_message( 0, "got clock: %d", m_source_clkid ); + */ + /* the file source device uses the file clock */ +/* masc_setup_package( &nugget, NULL, 0, 0 ); + masc_pushk_int32( &nugget, "mc_clkid", m_source_clkid ); + masc_finalize_package( &nugget ); + mas_set( mp1a_source_device, "mc_clkid", &nugget ); + masc_strike_package( &nugget ); +*/ + /* and the sbuf, which could be on a different machine, uses + the sample clock from that machine's anx device. */ +/* masc_setup_package( &nugget, NULL, 0, 0 ); + masc_pushk_int32( &nugget, "mc_clkid", m_sink_clkid ); + masc_finalize_package( &nugget ); + mas_set( m_sbuf, "mc_clkid", &nugget ); + masc_strike_package( &nugget ); +*/ + } + + return true; +} // masinit + +#include "masengine.moc" + diff --git a/amarok/src/engine/mas/masengine.h b/amarok/src/engine/mas/masengine.h new file mode 100644 index 00000000..15c4f6ca --- /dev/null +++ b/amarok/src/engine/mas/masengine.h @@ -0,0 +1,108 @@ +/*************************************************************************** + masengine.h - Media Application Server (MAS) audio interface + ------------------- +begin : Jul 04 2004 +copyright : (C) 2004 by Roland Gigler +email : rolandg@web.de +what : interface to the Media Application Server (MAS) +***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#ifndef AMAROK_MASENGINE_H +#define AMAROK_MASENGINE_H + +#include "enginebase.h" + +//#include <vector> + +#ifdef __cplusplus +extern "C" { +#endif + #include <mas/mas.h> + #include <mas/mas_core.h> +#ifdef __cplusplus +} +#endif + + +class QTimer; +class KURL; + +class MasEngine : public Engine::Base +{ + Q_OBJECT + + public: + MasEngine(); + ~MasEngine(); + + bool init(); + + bool initMixer( bool hardware ); + bool canDecode( const KURL& ) const; + uint position() const; + uint length() const; + Engine::State state() const {return m_state;} +/* + const Engine::Scope& scope(); + bool decoderConfigurable(); + void configureDecoder(); + */ + bool supportsXFade() const { return false; } + + public slots: + bool load( const KURL&, bool stream ); + bool play( unsigned int offset = 0); + void stop(); + void pause(); + + void seek( unsigned int ms ); + void setVolumeSW( unsigned int percent ); + private slots: + void playingTimeout(); + + private: + //void startXfade(); + //void timerEvent( QTimerEvent* ); + bool masinit(); + ///////////////////////////////////////////////////////////////////////////////////// + // ATTRIBUTES + ///////////////////////////////////////////////////////////////////////////////////// + static const int MAS_TIMER = 250; //ms + //static const int TIMEOUT = 4000; //ms FIXME make option? + bool m_inited; + uint m_lastKnownPosition; + + Engine::State m_state; + //long m_scopeId; + //int m_scopeSize; + //bool m_xfadeFadeout; + //float m_xfadeValue; + //QString m_xfadeCurrent; + QTimer* m_pPlayingTimer; + + KURL m_url; + + mas_device_t m_mp1a_source_device; + mas_device_t m_visual; + mas_device_t m_sbuf; + mas_device_t m_codec; + mas_device_t m_mix_device; + mas_port_t m_mix_sink; + mas_device_t m_sink_mc; + mas_device_t m_source_mc; + int32 m_sink_clkid; + //int32 m_source_clkid; + //double m_measured_sample_freq; +}; + +#endif // AMAROK_MASENGINE_H + diff --git a/amarok/src/engine/nmm/HostList.cpp b/amarok/src/engine/nmm/HostList.cpp new file mode 100644 index 00000000..c4a0dbf7 --- /dev/null +++ b/amarok/src/engine/nmm/HostList.cpp @@ -0,0 +1,156 @@ +/* NMM - Network-Integrated Multimedia Middleware + * + * Copyright (C) 2006 + * NMM work group, + * Computer Graphics Lab, + * Saarland University, Germany + * http://www.networkmultimedia.org + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301 + * USA + */ + +#include "HostList.h" + +#include <qcursor.h> +#include <qheader.h> +#include <klocale.h> + +#include "debug.h" +#include "HostListItem.h" + +HostList::HostList( QWidget *parent, const char *name ) + : KListView( parent, name ), + m_read_only( false ), + m_hoveredVolume(0) +{ + // TODO: item should be activated on mouse click + setMouseTracking( true ); + setAllColumnsShowFocus( true ); + + addColumn( i18n("Hostname") ); + addColumn( i18n("Video" ) ); + addColumn( i18n("Audio" ) ); + addColumn( i18n("Volume" ), 113 ); + header()->setResizeEnabled(false, 3); + addColumn( i18n("Status" ) ); + addColumn( i18n("Playback" ) ); + + setColumnAlignment( HostListItem::Hostname, Qt::AlignCenter ); + setColumnAlignment( HostListItem::Video, Qt::AlignCenter ); + setColumnAlignment( HostListItem::Audio, Qt::AlignCenter ); + setColumnAlignment( HostListItem::Volume, Qt::AlignCenter ); + setColumnAlignment( HostListItem::Status, Qt::AlignLeft ); +} + +HostList::~HostList() +{} + +void HostList::notifyHostError( QString hostname, int error) +{ + QListViewItemIterator it( this ); + HostListItem *host; + while( it.current() ) { + host = static_cast<HostListItem*>( it.current() ); + if( host->text(HostListItem::Hostname) == hostname ) + { + host->setText( HostListItem::Hostname, hostname ); + host->setStatus( error ); + host->repaint(); + return; + } + ++it; + } +} + +void HostList::contentsMousePressEvent( QMouseEvent *e) +{ + HostListItem *item = static_cast<HostListItem*>( itemAt( contentsToViewport( e->pos() ) ) ); + if( !( e->state() & Qt::ControlButton || e->state() & Qt::ShiftButton ) && ( e->button() & Qt::LeftButton ) && item) + { + // video column + if( !m_read_only && + e->pos().x() > header()->sectionPos( HostListItem::Video ) && + e->pos().x() < header()->sectionPos( HostListItem::Video ) + header()->sectionSize( HostListItem::Video ) ) + { + item->toggleVideo(); + item->updateColumn( HostListItem::Video ); + emit viewChanged(); + } + // audio column + else + if( !m_read_only && + e->pos().x() > header()->sectionPos( HostListItem::Audio ) && + e->pos().x() < header()->sectionPos( HostListItem::Audio ) + header()->sectionSize( HostListItem::Audio ) ) + { + item->toggleAudio(); + item->updateColumn( HostListItem::Audio ); + emit viewChanged(); + } + // status column + else + if( e->pos().x() > header()->sectionPos( HostListItem::Status ) && + e->pos().x() < header()->sectionPos( HostListItem::Status ) + header()->sectionSize( HostListItem::Status ) ) + { + item->statusToolTip(); + } + else // set new volume for item + if( e->pos().x() > header()->sectionPos( HostListItem::Volume ) && + e->pos().x() < header()->sectionPos( HostListItem::Volume ) + header()->sectionSize( HostListItem::Volume ) ) + { + int vol = e->pos().x(); + vol -= header()->sectionPos( HostListItem::Volume ); + item->setVolume( item->volumeAtPosition( vol ) ); + } + else + KListView::contentsMousePressEvent( e ); + } + else + KListView::contentsMousePressEvent( e ); +} + +void HostList::contentsMouseMoveEvent( QMouseEvent *e ) +{ + if( e ) + KListView::contentsMouseMoveEvent( e ); + + HostListItem *prev = m_hoveredVolume; + const QPoint pos = e ? e->pos() : viewportToContents( viewport()->mapFromGlobal( QCursor::pos() ) ); + + HostListItem *item = static_cast<HostListItem*>( itemAt( contentsToViewport( pos ) ) ); + if( item && pos.x() > header()->sectionPos( HostListItem::Volume ) && + pos.x() < header()->sectionPos( HostListItem::Volume ) + header()->sectionSize( HostListItem::Volume ) ) + { + m_hoveredVolume = item; + m_hoveredVolume->updateColumn( HostListItem::Volume ); + } + else + m_hoveredVolume = 0; + + if( prev ) + prev->updateColumn( HostListItem::Volume ); +} + +void HostList::leaveEvent( QEvent *e ) +{ + KListView::leaveEvent( e ); + + HostListItem *prev = m_hoveredVolume; + m_hoveredVolume = 0; + if( prev ) + prev->updateColumn( HostListItem::Volume ); +} + +#include "HostList.moc" diff --git a/amarok/src/engine/nmm/HostList.h b/amarok/src/engine/nmm/HostList.h new file mode 100644 index 00000000..45d0a590 --- /dev/null +++ b/amarok/src/engine/nmm/HostList.h @@ -0,0 +1,63 @@ +/* NMM - Network-Integrated Multimedia Middleware + * + * Copyright (C) 2006 + * NMM work group, + * Computer Graphics Lab, + * Saarland University, Germany + * http://www.networkmultimedia.org + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301 + * USA + */ + +#ifndef HOSTLIST_H +#define HOSTLIST_H + +#include <klistview.h> + +class HostListItem; + +class HostList : public KListView +{ + Q_OBJECT + + public: + HostList( QWidget*, const char* ); + ~HostList(); + + void setReadOnly( bool read_only ) { m_read_only = read_only; } + bool readOnly() const { return m_read_only; } + + void notifyHostError( QString, int); + + friend class HostListItem; + + signals: + /** + * Emitted when audio of video toggle changes. + */ + void viewChanged(); + + protected slots: + void contentsMousePressEvent( QMouseEvent *e ); + void contentsMouseMoveEvent( QMouseEvent *e = 0 ); + void leaveEvent( QEvent *e ); + + private: + bool m_read_only; + HostListItem *m_hoveredVolume; //if the mouse is hovering over the volume of an item +}; + +#endif diff --git a/amarok/src/engine/nmm/HostListItem.cpp b/amarok/src/engine/nmm/HostListItem.cpp new file mode 100644 index 00000000..915632e7 --- /dev/null +++ b/amarok/src/engine/nmm/HostListItem.cpp @@ -0,0 +1,266 @@ +/* NMM - Network-Integrated Multimedia Middleware + * + * Copyright (C) 2005-2006 + * NMM work group, + * Computer Graphics Lab, + * Saarland University, Germany + * http://www.networkmultimedia.org + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301 + * USA + */ + +#include "HostListItem.h" + +#include <qbitmap.h> +#include <qfont.h> +#include <qheader.h> +#include <qpainter.h> +#include <qwhatsthis.h> + +#include <kglobal.h> +#include <kiconloader.h> +#include <klocale.h> +#include <kpixmap.h> +#include <kpixmapeffect.h> +#include <kstandarddirs.h> + +#include "debug.h" +#include "HostList.h" +#include "nmm_engine.h" + +HostListItem::HostListItem( QListView *parent, QString hostname, bool audio, bool video, int volume, int status, bool read_only ) + : KListViewItem( parent ), + m_audio( audio ), + m_video( video ), + m_volume( volume ), + m_status( status ), + m_read_only( read_only ) +{ + setText( HostListItem::Hostname, hostname); + + setPixmap( HostListItem::Status, SmallIcon("info") ); + setText( HostListItem::Status, i18n("Unknown") ); + setPixmap( HostListItem::Playback, SmallIcon("info") ); + setText( HostListItem::Playback, i18n("Unknown") ); + + if( 24 /*m_pixmapInset.height()*/ > height() ) + this->setHeight( 24 /*m_pixmapInset.height()*/ ); +} + +HostListItem::~HostListItem() +{ +} + +int HostListItem::volumeAtPosition( int x ) +{ + if( x > 106 ) + return 100; + else if ( x < 6 ) + return -100; + else + return (x - 56) * 2; +} + + +void HostListItem::updateColumn( int column ) const +{ + const QRect r = listView()->itemRect( this ); + if( !r.isValid() ) + return; + + listView()->viewport()->update( listView()->header()->sectionPos( column ) - listView()->contentsX() + 1, + r.y() + 1, + listView()->header()->sectionSize( column ) - 2, height() - 2 ); +} + +void HostListItem::statusToolTip() +{ + QWhatsThis::display( prettyStatus( m_status ) ); +} + +QString HostListItem::prettyStatus( int error ) +{ + QString st; + + debug() << "### ERROR code : " << error << endl; + + st = "<html><body>"; + + if(!error) + st += i18n("So far no status available for this host entry.<br/>Probably this means the host has not been used yet for playback."); + + + if( error & NmmEngine::ERROR_PLAYBACKNODE ) + // TODO distinguish between ALSAPlaybackNode and PlaybackNode + st += i18n("An error appeared during audio playback initialization. Make sure the <b>PlaybackNode</b> is present on your system. If it is present, the command <b>serverregistry -s</b> in a console will list <b>PlaybackNode</b> as <b>available</b>.<br/>"); + + if( error & NmmEngine::ERROR_DISPLAYNODE ) + st += i18n("An error appeared during video playback initialization. Make sure the <b>XDisplayNode</b> is present on your system. If it is present, the command <b>serverregistry -s</b> in a console will list <b>XDisplayNode</b> as <b>available</b>.<br/>"); + + if( error ) + st += i18n("In general have a look at the <a href=\"http://www.networkmultimedia.org/Download/Binary/index.html#configure\">Configuration and tests</a> instructions."); + + st += "</body></html>"; + return st; +} + +void HostListItem::paintCell(QPainter * p, const QColorGroup & cg, int column, int width, int align ) +{ + QColorGroup m_cg( cg ); + + // TODO: reuse icons? + if( column == HostListItem::Video ) + { + if( m_video ) { // video ? + if( m_read_only ) + setPixmap( HostListItem::Video, SmallIcon("nmm_option_on_readonly") ); + else + setPixmap( HostListItem::Video, SmallIcon("nmm_option_on") ); + } + else + if( ! m_read_only) + setPixmap( HostListItem::Video, SmallIcon("nmm_option_off") ); + } + else if( column == HostListItem::Audio ) + { + if( m_audio ) {// audio ? + if( m_read_only ) + setPixmap( HostListItem::Audio, SmallIcon("nmm_option_on_readonly") ); + else + setPixmap( HostListItem::Audio, SmallIcon("nmm_option_on") ); + } + else + if( ! m_read_only) + setPixmap( HostListItem::Audio, SmallIcon("nmm_option_off") ); + } + else if( column == HostListItem::Status ) + { + QFont font( p->font() ); + if( ! m_status ) // Unknown + { + font.setBold( false ); + setText( HostListItem::Status , i18n("Unknown") ); + } + else if( m_status == NmmEngine::STATUS_OK ) + { + font.setBold( false ); + m_cg.setColor( QColorGroup::Text, Qt::darkGreen ); + setText( HostListItem::Status , i18n("OK") ); + } + else { // error + font.setBold( true ); + m_cg.setColor( QColorGroup::Text, Qt::red ); + setText( HostListItem::Status , i18n("Failed") ); + } + p->setFont( font ); + } + else if( column == HostListItem::Volume ) + { + QPixmap buf( width, height() ); + QColor bg = listView()->viewport()->backgroundColor(); + buf.fill( bg ); + + bitBlt( &buf, 0, 0, pixmapVolume( PixInset ) ); + + // Draw gradient + static int padding = 7; + static int vol; // pixelposition + if( this == ((HostList*)listView())->m_hoveredVolume ) + { + vol = listView()->viewportToContents( listView()->viewport()->mapFromGlobal( QCursor::pos() ) ).x(); + vol -= listView()->header()->sectionPos( HostListItem::Volume ); + } + else + vol = (m_volume / 2) + 56; + + //std::cerr << "rel vol = " << vol << std::endl; + + static int center = 56; + if( vol > center ) { + bitBlt( &buf, 0, 0, pixmapVolume( PixRight ), 0, 0, vol + 1 /* TODO: why + 1??? */ ); + } + else if ( vol < center ) { + bitBlt( &buf, vol, 0, pixmapVolume( PixLeft ), vol, 0, 56 ); + } + else + {} + + // Calculate actual volume string from pixelposition + vol = volumeAtPosition( vol ); + QString vol_text; + if( vol > 0 ) + vol_text = "+"; + vol_text += QString::number( vol ); + vol_text += '%'; + + // Draw relative volume number + QPainter p_number(&buf); + p_number.setPen( cg.buttonText() ); + QFont font; + font.setPixelSize( 9 ); + p_number.setFont( font ); + const QRect rect( 40, 0, 34, 15 ); + p_number.drawText( rect, Qt::AlignRight | Qt::AlignVCenter, vol_text ); + p_number.end(); + //bitBlt( p_number.device(), 0, 0, &buf ); + + p->drawPixmap( 0, 0, buf ); + return; + } + + KListViewItem::paintCell(p, m_cg, column, width, align); +} + +QPixmap* HostListItem::pixmapVolume( int type ) +{ + if( type == PixInset ) + { + static QPixmap m_pixmapInset( locate( "data", "amarok/images/nmm-volume-inset.png" ) ); + return &m_pixmapInset; + } + else if( type == PixRight ) + { + static QPixmap m_pixmapGradientRight = generateGradient( PixRight ); + return &m_pixmapGradientRight; + } + else if ( type == PixLeft ) + { + static QPixmap m_pixmapGradientLeft = generateGradient( PixLeft ); + return &m_pixmapGradientLeft; + } + + return 0; +} + +QPixmap HostListItem::generateGradient( int type ) +{ + QPixmap temp; + + if( type == PixRight ) + temp = QPixmap( locate( "data", "amarok/images/nmm-gradient-right.png" ) ); + else // PixLeft + temp = QPixmap( locate( "data", "amarok/images/nmm-gradient-left.png" ) ); + const QBitmap mask( temp.createHeuristicMask() ); + + KPixmap result = QPixmap( 113, 24 ); + if( type == PixRight) + KPixmapEffect::gradient( result, listView()->colorGroup().background(), listView()->colorGroup().highlight(), KPixmapEffect::HorizontalGradient ); + else + KPixmapEffect::gradient( result, listView()->colorGroup().highlight(), listView()->colorGroup().background(), KPixmapEffect::HorizontalGradient ); + + result.setMask( mask); + return result; +} diff --git a/amarok/src/engine/nmm/HostListItem.h b/amarok/src/engine/nmm/HostListItem.h new file mode 100644 index 00000000..9149fd2c --- /dev/null +++ b/amarok/src/engine/nmm/HostListItem.h @@ -0,0 +1,90 @@ +/* NMM - Network-Integrated Multimedia Middleware + * + * Copyright (C) 2005-2006 + * NMM work group, + * Computer Graphics Lab, + * Saarland University, Germany + * http://www.networkmultimedia.org + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301 + * USA + */ + +#ifndef HOSTLISTITEM_H +#define HOSTLISTITEM_H + +#include <klistview.h> +#include <qpixmap.h> + +class HostListItem : public KListViewItem { + public: + enum Column + { + Hostname = 0, + Video, + Audio, + Volume, + Status, + Playback + }; + + HostListItem( QListView*, QString hostname, bool audio = true, bool video = true, int volume = 0, int status = 0, bool read_only = false); + ~HostListItem(); + + bool isVideoEnabled() const { return m_video; } + void toggleVideo() { m_video = !m_video; } + + bool isAudioEnabled() const { return m_audio; } + void toggleAudio() { m_audio = !m_audio; } + + void setStatus( int s ) { m_status = s; } + int status() const { return m_status; } + + void setVolume( int v ) { m_volume = v; } + int volume() const { return m_volume; } + int volumeAtPosition( int ); + + void updateColumn( int column ) const; + + /** + * Shows extended status text in a QWhatsThis widget. + * \todo handle different error scenarios + */ + void statusToolTip(); + + /** + * Create detailed status message. + * \todo make it user friendly/understandable + * \todo right place for this method? + */ + static QString prettyStatus( int ); + + protected: + void paintCell( QPainter * painter, const QColorGroup & cg, int column, int width, int align ); + + private: + enum { PixInset, PixLeft, PixRight }; + QPixmap* pixmapVolume( int ); + QPixmap generateGradient( int ); + + bool m_audio; + bool m_video; + int m_volume; + int m_status; + bool m_read_only; + +}; + +#endif diff --git a/amarok/src/engine/nmm/Makefile.am b/amarok/src/engine/nmm/Makefile.am new file mode 100644 index 00000000..a0748ed8 --- /dev/null +++ b/amarok/src/engine/nmm/Makefile.am @@ -0,0 +1,47 @@ +imagesdir = $(kde_datadir)/amarok/images +images_DATA = \ + nmm-gradient-left.png \ + nmm-gradient-right.png \ + nmm-volume-inset.png + +kde_module_LTLIBRARIES = \ + libamarok_nmmengine_plugin.la + +INCLUDES = \ + -I$(top_srcdir)/amarok/src \ + $(CFLAGS_NMM) \ + $(all_includes) + +libamarok_nmmengine_plugin_la_LIBADD = \ + $(top_builddir)/amarok/src/libamarok.la \ + $(top_builddir)/amarok/src/plugin/libplugin.la \ + $(LIB_KDECORE) $(LIB_KFILE) \ + -lnmmbase -lnmmgraphmgr -lnmmiprogress -lnmmiaudiodevice -lnmmigeneral + +libamarok_nmmengine_plugin_la_SOURCES = \ + nmm_engine.cpp \ + nmm_configdialog.cpp \ + nmm_configdialogbase.ui \ + NmmLocation.cpp \ + HostList.cpp \ + HostListItem.cpp \ + ServerregistryPing.cpp \ + nmm_kdeconfig.kcfgc + +libamarok_nmmengine_plugin_la_LDFLAGS = \ + -module \ + $(KDE_PLUGIN) \ + $(LDFLAGS_NMM) \ + $(all_libraries) + +METASOURCES = \ + AUTO + +KDE_CXXFLAGS = $(USE_EXCEPTIONS) + +kde_services_DATA = \ + amarok_nmmengine_plugin.desktop + +kde_kcfg_DATA = nmm_kdeconfig.kcfg + +SUBDIRS = icons diff --git a/amarok/src/engine/nmm/NmmLocation.cpp b/amarok/src/engine/nmm/NmmLocation.cpp new file mode 100644 index 00000000..6ccd3123 --- /dev/null +++ b/amarok/src/engine/nmm/NmmLocation.cpp @@ -0,0 +1,59 @@ +/* NMM - Network-Integrated Multimedia Middleware + * + * Copyright (C) 2006 + * NMM work group, + * Computer Graphics Lab, + * Saarland University, Germany + * http://www.networkmultimedia.org + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301 + * USA + */ + +#include "NmmLocation.h" + +NmmLocation::NmmLocation() +{} + +NmmLocation::NmmLocation( QString hostname, bool audio, bool video, int volume, int status ) + : m_hostname(hostname), + m_audio(audio), + m_video(video), + m_volume(volume), + m_status( status) +{ +} + +NmmLocation::NmmLocation( const NmmLocation &old ) +{ + m_hostname = old.hostname(); + m_audio = old.audio(); + m_video = old.video(); + m_volume = old.volume(); + m_status = old.status(); +} + +NmmLocation::~NmmLocation() +{} + +QString NmmLocation::hostname() const +{ + return m_hostname; +} + +void NmmLocation::setHostname(QString hostname) +{ + m_hostname = hostname; +} diff --git a/amarok/src/engine/nmm/NmmLocation.h b/amarok/src/engine/nmm/NmmLocation.h new file mode 100644 index 00000000..3782ba1b --- /dev/null +++ b/amarok/src/engine/nmm/NmmLocation.h @@ -0,0 +1,60 @@ +/* NMM - Network-Integrated Multimedia Middleware + * + * Copyright (C) 2006 + * NMM work group, + * Computer Graphics Lab, + * Saarland University, Germany + * http://www.networkmultimedia.org + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301 + * USA + */ + +#ifndef NMMLOCATION_H +#define NMMLOCATION_H + +#include <qstring.h> + +class NmmLocation { + public: + NmmLocation(); + NmmLocation(QString hostname, bool audio, bool video, int volume, int status ); + NmmLocation(const NmmLocation&); + ~NmmLocation(); + + QString hostname() const; + void setHostname(QString); + + bool audio() const { return m_audio; } + void setAudio( bool audio ) { m_audio = audio; } + + bool video() const { return m_video; } + void setVideo( bool video ) { m_video = video; } + + int volume() const { return m_volume; } + void setVolume( int v ) { m_volume = v; } + + void setStatus( int s ) { m_status = s; } + int status() const { return m_status; } + + private: + QString m_hostname; + bool m_audio; + bool m_video; + int m_volume; + int m_status; +}; + +#endif diff --git a/amarok/src/engine/nmm/ServerregistryPing.cpp b/amarok/src/engine/nmm/ServerregistryPing.cpp new file mode 100644 index 00000000..7a6d2684 --- /dev/null +++ b/amarok/src/engine/nmm/ServerregistryPing.cpp @@ -0,0 +1,60 @@ +/* NMM - Network-Integrated Multimedia Middleware + * + * Copyright (C) 2002-2006 + * NMM work group, + * Computer Graphics Lab, + * Saarland University, Germany + * http://www.networkmultimedia.org + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301 + * USA + */ + +#include "ServerregistryPing.h" + +#include "debug.h" + +ServerregistryPing::ServerregistryPing(const QString &host, Q_UINT16 port) + : QSocket() +{ + connect( this, SIGNAL(connected()), + SLOT(socketConnected()) ); + connect( this, SIGNAL(connectionClosed()), + SLOT(socketConnectionClosed()) ); + connect( this, SIGNAL(error(int)), + SLOT(socketError(int)) ); + + connectToHost(host, port); +} + +void ServerregistryPing::socketConnected() +{ + DEBUG_FUNC_INFO + emit registryAvailable( true ); +} + +void ServerregistryPing::socketConnectionClosed() +{ + DEBUG_FUNC_INFO + emit registryAvailable( false ); +} + +void ServerregistryPing::socketError( int ) +{ + DEBUG_FUNC_INFO + emit registryAvailable( false ); +} + +#include "ServerregistryPing.moc" diff --git a/amarok/src/engine/nmm/ServerregistryPing.h b/amarok/src/engine/nmm/ServerregistryPing.h new file mode 100644 index 00000000..06313d48 --- /dev/null +++ b/amarok/src/engine/nmm/ServerregistryPing.h @@ -0,0 +1,55 @@ +/* NMM - Network-Integrated Multimedia Middleware + * + * Copyright (C) 2002-2006 + * NMM work group, + * Computer Graphics Lab, + * Saarland University, Germany + * http://www.networkmultimedia.org + * + * Maintainer: Robert Gogolok <gogo@graphics.cs.uni-sb.de> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301 + * USA + */ + +#ifndef SERVERREGISTRYPING_H +#define SERVERREGISTRYPING_H + +#include <qsocket.h> + +/** + * Connects to a remote host on the default NMM serverregistry port. + */ +class ServerregistryPing + : public QSocket +{ + Q_OBJECT + + public: + ServerregistryPing(const QString & host, Q_UINT16 port = 22801); + + private slots: + void socketConnected(); + void socketConnectionClosed(); + void socketError(int); + + signals: + /** + * This signal is emitted when the serverregistry gets available/unavailable. + */ + void registryAvailable(bool); +}; + +#endif diff --git a/amarok/src/engine/nmm/amarok_nmmengine_plugin.desktop b/amarok/src/engine/nmm/amarok_nmmengine_plugin.desktop new file mode 100644 index 00000000..c088d3a0 --- /dev/null +++ b/amarok/src/engine/nmm/amarok_nmmengine_plugin.desktop @@ -0,0 +1,122 @@ +[Desktop Entry] +Encoding=UTF-8 +Type=Service +Name=NMM Engine +Name[af]=NMM Enjin +Name[ar]=محرك NMM +Name[bg]=NMM +Name[bn]=এনএমএম ইঞ্জিন +Name[br]=Keflusker NMM +Name[ca]=Motor NMM +Name[cs]=NMM +Name[da]=NMM-motor +Name[de]=NMM +Name[el]=Μηχανή NMM +Name[eo]=NMM Ilo +Name[es]=Motor NMM +Name[et]=NMM mootor +Name[eu]=NMM motorea +Name[fa]=موتور NMM +Name[fi]=NMM +Name[fr]=Moteur NMM +Name[ga]=Inneall NMM +Name[gl]=Motor NMM +Name[he]=מנוע שמע NMM +Name[hi]=एनएमएम इंजिन +Name[hu]=NMM alrendszer +Name[is]=NMM vél +Name[it]=Motore NMM +Name[ja]=NMM エンジン +Name[ka]=NMM dრავა +Name[km]=ម៉ាស៊ីន NMM +Name[lt]=NMM variklis +Name[mk]=NMM-машина +Name[ms]=Enjin NMM +Name[nb]=NMM-motor +Name[nds]=NMM +Name[ne]=NMM इन्जिन +Name[nn]=NMM-motor +Name[pa]=NMM ਇੰਜਣ +Name[pl]=Wyjście NMM +Name[pt]=Motor NMM +Name[pt_BR]=Mecanismo NMM +Name[ru]=NMM +Name[se]=NMM-mohtor +Name[sl]=Pogon NMM +Name[sq]=Motor NMM +Name[sr]=Мотор NMM +Name[sr@Latn]=Motor NMM +Name[ss]=Motor NMM +Name[sv]=NMM-gränssnitt +Name[ta]=NMM பொறி +Name[tg]=Муҳаррики NMM +Name[tr]=NMM Motoru +Name[uk]=Рушій NMM +Name[uz]=NMM tizimi +Name[uz@cyrillic]=NMM тизими +Name[wa]=Éndjin NMM +Name[zh_CN]=NMM 引擎 +Name[zh_TW]=NMM 解碼引擎 +X-KDE-Library=libamarok_nmmengine_plugin +Comment=Amarok plugin +Comment[af]=Amarok inprop module +Comment[ar]=قابس ( برنامج مضاف الى ) AmaroK +Comment[bg]=Приставка за Amarok +Comment[bn]=আমারক প্লাগিন +Comment[br]=Lugent Amarok +Comment[ca]=Connector de l'Amarok +Comment[cs]=Amarok modul +Comment[da]=Plugin for Amarok +Comment[de]=Amarok-Modul +Comment[el]=Πρόσθετο AmaroK +Comment[eo]=Amarok kromaĵo +Comment[es]=Extensión de Amarok +Comment[et]=Amaroki plugin +Comment[fa]=وصلۀ amaroK +Comment[fi]=Amarok-liitännäinen +Comment[fr]=Module Amarok +Comment[ga]=Breiseán AmaroK +Comment[gl]=Extensión para Amarok +Comment[hu]=Amarok-bővítőmodul +Comment[is]=Amarok íforrit +Comment[it]=Plugin di Amarok +Comment[ja]=Amarok プラグイン +Comment[ka]=Amarok მოდული +Comment[km]=កម្មវិធី​ជំនួយ Amarok +Comment[lt]=Amarok priedas +Comment[mk]=Приклучок за Амарок +Comment[ms]=Plugin Amarok +Comment[nb]=Programtillegg for Amarok +Comment[nds]=Amarok-Moduul +Comment[ne]=अमारोक प्लगइन +Comment[nl]=Amarok-plugin +Comment[nn]=Amarok-tillegg +Comment[pa]=ਅਮਰੋਕ ਪਲੱਗਇਨ +Comment[pl]=Wtyczka Amaroka +Comment[pt]='Plugin' para o Amarok +Comment[pt_BR]=Plugin do Amarok +Comment[ru]=Модуль amaroK +Comment[se]=Amarok-lassemoduvla +Comment[sk]=Amarok modul +Comment[sr]=Прикључак за Amarok +Comment[sr@Latn]=Priključak za Amarok +Comment[sv]=Insticksprogram för Amarok +Comment[th]=โปรแกรมเสริมของ Amarok +Comment[tr]=Amarok eklentisi +Comment[uk]=Втулок Amarok +Comment[uz]=Amarok plagini +Comment[uz@cyrillic]=Amarok плагини +Comment[wa]=Tchôke-divins Amarok +Comment[zh_CN]=Amarok 插件 +Comment[zh_TW]=AmaroK 插件 +ServiceTypes=Amarok/Plugin + +X-KDE-Amarok-plugintype=engine +X-KDE-Amarok-name=nmm-engine +X-KDE-Amarok-authors=NMM work group http://www.networkmultimedia.org +X-KDE-Amarok-email=nmm-dev@graphics.cs.uni-sb.de +X-KDE-Amarok-rank=50 +X-KDE-Amarok-version=1 +X-KDE-Amarok-framework-version=32 + + diff --git a/amarok/src/engine/nmm/icons/Makefile.am b/amarok/src/engine/nmm/icons/Makefile.am new file mode 100644 index 00000000..0969cf7c --- /dev/null +++ b/amarok/src/engine/nmm/icons/Makefile.am @@ -0,0 +1,2 @@ +nmmicondir = $(kde_datadir)/amarok/icons +nmmicon_ICON = AUTO diff --git a/amarok/src/engine/nmm/icons/hi16-action-nmm_option_off.png b/amarok/src/engine/nmm/icons/hi16-action-nmm_option_off.png new file mode 100644 index 0000000000000000000000000000000000000000..678edcb279e5d886a725e20483f8c9e1de193815 GIT binary patch literal 113 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`oCO|{#S9GG!XV7ZFl&wkP*B;^ z#WBR<baF~U!jJO~iVKc3{P&r1%P#A{3dY534h+gmZ%Q&;HD-}~`J}cRsGPym)z4*} HQ$iB}eVZQ2 literal 0 HcmV?d00001 diff --git a/amarok/src/engine/nmm/icons/hi16-action-nmm_option_on.png b/amarok/src/engine/nmm/icons/hi16-action-nmm_option_on.png new file mode 100644 index 0000000000000000000000000000000000000000..799b88d867a86405d7da8390230f16a6f8e8a521 GIT binary patch literal 578 zcmV-I0=@l-P)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV00009a7bBm000XU z000XU0RWnu7ytkP^hrcPR5;6}lRaotQ5462_vSUGF~-HzL^M=vp|l_r6tRe#4$`5B zf{2Sw!Nn?wSrpxBsUK?z-CQM$(xKQsiXR;`YL^CE3>0hflC;*C<R$6LyAFAUD6J8C z;Bera!|xpK{T~4+c4(~t0nJO{?087UOApnZ=^^FzYRf<;ptS!OaIY)E9?zLHu~-bZ z<zsKyeJGc-`T<%2x;;T6N3MbS9deZ=ii?6=QoRG>0I~&Oq-Pk-<%R4l2qF0PQUToF zY>-Vc2=oK(PHw>8!};(9C?r8hSbGnZ3~}Ji9{}I}UA3cw;q-~iT?t^!sg3nU8y!Cq zKwXfYQWg+xUQq++zPaJLc;y%!p#WC3e*MGqlnk8WWXEY(odek>Fm$M`69c|910+D` zoXyO~NrVtwdOCo|uSNQzpe!hU1*;6kj4A;S{(dA0U=^Xg&qROmmQe5jgA<4G_ky7} zre|I$>J>%-V@trNC<+@^JLk!#exV&VuuVZV3$-=K&yfZmZS_qOAUn(B)HBe0FjXC= zY!}Gu8nR8WGO17&_kq&30O0PzD2v4<aP3x9vta(*ptKC@^DF=p+X-b8U|MCaCT?LX z3o_FnU4oT{Xhwj5qmyRe2Oykd5)SCe!6AmF24)6wulWqz_=i$;vD0(@4dUCfr4RCA Q>Hq)$07*qoM6N<$f@}Qzd;kCd literal 0 HcmV?d00001 diff --git a/amarok/src/engine/nmm/icons/hi16-action-nmm_option_on_readonly.png b/amarok/src/engine/nmm/icons/hi16-action-nmm_option_on_readonly.png new file mode 100644 index 0000000000000000000000000000000000000000..c5715bf8e85eb6abf19f6ef31d0d70db13ceccef GIT binary patch literal 639 zcmV-_0)YLAP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV00006VoOIv0RI60 z0RN!9r;`8x010qNS#tmY3ljhU3ljkVnw%H_00JLLL_t(I%axN|NRv?*$A9O2U!7aF z=woy1LJTxSAqbLcST|8Yco9)h(M?1UK~Pb&n=ZOjG3Y9Y*md2MH$@kj4k=rpt9Fr+ zmNk{_opa5#@4egUVjGNQXb(IbIOl(U=ixm62R0U$feBNy7Sv2?5NPK=!lqeB9DC%H z!>IvzXY{0Ov}i$~4M5!hU|{z(4z-+1GWp~O@uf78Z}BbZXki?f5$jy;Xmar7$Ou|K z4$E&LIV+g+$oGH?Kv@US-+3KPwLoMXgb*yglmNG@0;Ohk0DA#*WzS{p<W%cvNX&px zAT|lvFvGyRT0n1eiz&aSuQjy)e7heQs+<jVxyy=QexN7_y_N>xt}Rgmj(6NBsAn$h z!|t?U<ce3Pp3N(^UJlw1!-@}-27&H{e4NL?*IIz0G;-8=qQuz8R|p|E`@lt$RlCyd z25CU@I~Zxu$7Bk)SN}<h+Jx0eg1I|`_r(NG$4>e@yRdeF?k&F_ej_Pn83gpd0EGmX zKDm*UV5KcUbpAJ5k4`Bg$VDI@gQ$-XaDS~fsla0DCz~6$;o9B<bD1fM4LO!a8YmSd z!RL}}h+BYfEkL!6cc%xLP0oS3Ns@~|X0<>n2=M^ZfahOEsRA-anv4FMl%xUSQBYLD zQrR^9fUV+_+T2k;3k%r@Thj|1$mtOCLp;D|;Aur%pLQTtKMQ8yA&>&1zy}~ysdTMe Z{R4&p#?PUq)C~Xt002ovPDHLkV1lZR6zc#0 literal 0 HcmV?d00001 diff --git a/amarok/src/engine/nmm/nmm-gradient-left.png b/amarok/src/engine/nmm/nmm-gradient-left.png new file mode 100644 index 0000000000000000000000000000000000000000..08c98701d186cde2d5d9a6a9ef4b3bbc35e02866 GIT binary patch literal 182 zcmeAS@N?(olHy`uVBq!ia0vp^g+MI9!3HGHwhB1`DYhhUcNd2LAh=-f^2tCE&H|6f zVg?3oVGw3ym^DWNC>ZAH;uw-~@9kAXz5@mvERHAs|L@864t({)=5kWthSt?UB@92N z|C91N`=)>Q+&3RO-gCFu+AZ3!P>_|cU*i+2;8ET_jl2SvL}%rDVqZQk?f?P~O|hxf V(n1DTA7_BHd%F6$taD0e0s!ClJ$V2C literal 0 HcmV?d00001 diff --git a/amarok/src/engine/nmm/nmm-gradient-right.png b/amarok/src/engine/nmm/nmm-gradient-right.png new file mode 100644 index 0000000000000000000000000000000000000000..043852d79a6c7111401407031bb9de82cca6dc95 GIT binary patch literal 184 zcmeAS@N?(olHy`uVBq!ia0vp^g+MI9!3HGHwhB1`DYhhUcNd2LAh=-f^2tCE&H|6f zVg?3oVGw3ym^DWNC>Y`C;uw-~@9mY1f(-^dERHi5{-5?#go(+>^PcX*{AbEQB@8Q4 zXD(RQbEbU7*PE}*!uKZBcizyM(b4PNdBbK>M{`oAg~GHB=O$U{!lWI`PtWY?=;+`G Y&Y8YbT1cZ$)E{Jkr>mdKI;Vst0Q(R<RR910 literal 0 HcmV?d00001 diff --git a/amarok/src/engine/nmm/nmm-volume-inset.png b/amarok/src/engine/nmm/nmm-volume-inset.png new file mode 100644 index 0000000000000000000000000000000000000000..324018b73f8b3098bcf1a9e9441ff1644b5f0294 GIT binary patch literal 931 zcmV;U16=%xP)<h;3K|Lk000e1NJLTq0040S000;W1^@s6&4m&|00006VoOIv0RI60 z0RN!9r;`8x010qNS#tmY3ljhU3ljkVnw%H_00TowL_t(&-tCynZWA#Wg}<3jlhUMt zwonOm1(Zr$#Exg-N%|Chv@Td6v8Y7FMIZsarqedf#b$FXEXy-VOKBVMk$%}xW)j=y zKgYif2?+@a2?+@a2?+@a2?@V3Q}bO0G=MwmTeWy?!qp;E<iYv_n5vhGI)Xc$&RSWP zkBu=Ua1NXTCyG*^t)92uyEgGrOH|m;fO((|v=s9|GtcvOyWQUDcDvia@S-F(dcEG7 zGN7qHSXP$gQgH?x0f)eWqN{go1)=soBPt{-2Rgu_;vR5U>oU^XIn@mS#KXtSkXZEl z{W;*hl$Qguio$E>fCs=MWzd|++*kb2=b<-16R`*T50I4yQHZD&U`eqE^gK7~0pGMf z1Fszb#u(G;_xsQ5xGE+X;J%dR1~ucgVYtD!fCXR`cp(*^NVQ$ySZz->eeU%`1>0Lh zmftp&uuZSu(Y!lSS4%`4YR(_D4lapDkr;Zdky2~xkYa)f479khjW@3#vnvG_-ZM?l z>mLG7f~<2{=rXj_#E&cN*<KS_xrkX%GP-@M@SMrHlB*fM13OwbXOY7S(nj7h)@!YA zsdX4fqV=+-syfXgF%uB%R2(X}5oB^hma`HpQW;qEz9v^ksGY$6Lc(S}+jG{}4wB3% zq^|^XPSTGkY8WWa^=%}GBxx2T@THYV#UG5@$g>*t=7BAT1kVQF&8gIW>e*E(G7o@V z_0KCZK6HvgiL(UU7g<Xp%-MYj>;Yc`iyK6U@@>%6sLKA0La(?Y8>$*uHm1DJ1CsN| zo?Aq0I}@qS-d08K&?CUv@>}4QXM=4$Tj<#%CBhfr%&QVd6;>D{%SIG)ZB==}-^I9z zn2HK7s~|VrclQFzc86nk=33d<*m$$Kxw)nN>QwKX$^J})oY-Dv0_%%a);Csw72GUh z6Cc$S8hNCY5BJ}~i+p{3-2m@AMGXVOOhA?iZb&96q;9l=9!H|D1bYj}HWdnvDL3=3 z6@PjKMNC;taO;#^;JkR1)p9=W_a{-}OYJSU{h(cURCqQGimE{SPn2j?^)0J%n@VhN zZ@&h%ru+(Tw=ThoF^Bw5z~^bc#Ps&WX@;HT(4R2H_yvT`SVs&g4$c4o002ovPDHLk FV1gH?sc`@R literal 0 HcmV?d00001 diff --git a/amarok/src/engine/nmm/nmm_configdialog.cpp b/amarok/src/engine/nmm/nmm_configdialog.cpp new file mode 100644 index 00000000..6c13b203 --- /dev/null +++ b/amarok/src/engine/nmm/nmm_configdialog.cpp @@ -0,0 +1,258 @@ +/* NMM - Network-Integrated Multimedia Middleware + * + * Copyright (C) 2005-2006 + * NMM work group, + * Computer Graphics Lab, + * Saarland University, Germany + * http://www.networkmultimedia.org + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301 + * USA + */ + +#include "nmm_configdialog.h" +#include "nmm_engine.h" +#include "nmm_kdeconfig.h" +#include "HostList.h" +#include "HostListItem.h" +#include "debug.h" + +#include <qbuttongroup.h> +#include <qinputdialog.h> +#include <qlayout.h> +#include <qlistbox.h> +#include <qpushbutton.h> +#include <qradiobutton.h> +#include <qwidgetstack.h> +#include <kcombobox.h> + +NmmConfigDialog::NmmConfigDialog() + : PluginConfig(), + current_audio_group_selection(-1), + m_environment_list(NULL), + m_user_list(NULL), + m_host_list_modified(false) +{ + kdDebug() << k_funcinfo << endl; + + + m_view = new NmmConfigDialogBase(); + + /* create host list widget stack */ + m_environment_list = new HostList( m_view->audioGroup, "TheEnvironmentList" ); + m_environment_list->setReadOnly( true ); + m_environment_list->setSelectionMode( QListView::NoSelection ); + + m_user_list = new HostList( m_view->audioGroup, "TheUserList" ); + m_user_list->setSelectionMode( QListView::Single ); + connect( m_user_list, SIGNAL( viewChanged() ), this, SLOT( hostListModified() ) ); + + m_view->hostListStack->addWidget( m_environment_list ); + m_view->hostListStack->addWidget( m_user_list ); + // show disabled user list by default if 'localhost only' selected + m_view->hostListStack->raiseWidget( m_user_list ); + + /* restore saved output plugin */ + if ( NmmKDEConfig::audioOutputPlugin() == "ALSAPlaybackNode" ) + m_view->audioPlaybackNode->setCurrentItem( 1 ); + + /* restore selected audioGroup selection */ + m_view->audioGroup->setButton( NmmKDEConfig::location() ); + clickedAudioGroup( NmmKDEConfig::location() ); + createHostLists(); + + /* connect 'Add...' and 'Remove' buttons */ + connect( m_view->addLocationButton, SIGNAL( released() ), SLOT( addHost() ) ); + connect( m_view->removeHostButton, SIGNAL( released() ), SLOT( removeHost() ) ); + connect( m_user_list, SIGNAL( selectionChanged() ) , this, SLOT( enableRemoveButton() ) ); + + /* connect audioGroup selection */ + connect( m_view->audioGroup, SIGNAL( released(int) ), SLOT( clickedAudioGroup(int) ) ); + + PluginConfig::connect( m_view->audioPlaybackNode, SIGNAL( activated( int ) ), SIGNAL( viewChanged() ) ); + PluginConfig::connect( m_view->audioGroup, SIGNAL( released( int ) ), SIGNAL( viewChanged() ) ); + +// connect( this, SIGNAL( viewChanged() ) ) +} + +NmmConfigDialog::~NmmConfigDialog() +{ + kdDebug() << k_funcinfo << endl; + delete m_view; +} + + +bool NmmConfigDialog::hasChanged() const +{ + return NmmKDEConfig::audioOutputPlugin() != m_view->audioPlaybackNode->currentText() || + NmmKDEConfig::location() != m_view->audioGroup->selectedId() || + m_host_list_modified; +} + +bool NmmConfigDialog::isDefault() const +{ + return false; +} + +void NmmConfigDialog::save() +{ + DEBUG_BLOCK + + if( hasChanged() ) + { + NmmKDEConfig::setAudioOutputPlugin( m_view->audioPlaybackNode->currentText() ); + debug() << "saved audio output plugin" << endl; + NmmKDEConfig::setLocation( m_view->audioGroup->selectedId() ); + debug() << "saved current location selection" << endl; + + /* store volume for AUDIO_HOSTS */ + //NmmEngine::instance()->setEnvironmentHostList( tmp_environment_list ); + + /* save user host list and toggle states for audio, video */ + QValueList<NmmLocation> tmp_user_list; + + QListViewItemIterator it( m_user_list ); + HostListItem *host; + while( it.current() ) { + host = static_cast<HostListItem*>( it.current() ); + tmp_user_list.append( NmmLocation(host->text(HostListItem::Hostname), host->isAudioEnabled(), host->isVideoEnabled(), /* TODO: host->volume()*/ 0, host->status() ) ); + ++it; + } + + NmmEngine::instance()->setUserHostList( tmp_user_list ); + + QStringList hosts; + QStringList audio_hosts; + QStringList video_hosts; + QValueList<NmmLocation>::iterator it_n; + for( it_n = tmp_user_list.begin(); it_n != tmp_user_list.end(); ++it_n ) + { + debug() << "saved user host" << endl; + hosts.append( (*it_n).hostname() ); + if( (*it_n).audio() ) + audio_hosts.append( "1" ); + else + audio_hosts.append( "0" ); + if( (*it_n).video() ) + video_hosts.append( "1" ); + else + video_hosts.append( "0" ); + // TODO: save volume + } + NmmKDEConfig::setHostList( hosts ); + NmmKDEConfig::setAudioToggle( audio_hosts ); + NmmKDEConfig::setVideoToggle( video_hosts ); + debug() << "saved user host list with toggle states for audio and video" << endl; + + NmmKDEConfig::writeConfig(); + + m_host_list_modified = false; + } +} + +void NmmConfigDialog::addHost() +{ + bool ok; + QString hostname = QInputDialog::getText( + "New NMM sink host", "Enter hostname to add:", QLineEdit::Normal, + QString::null, &ok, NULL); + if( ok && !hostname.isEmpty() ) + { + new HostListItem( m_user_list, hostname ); + if( m_user_list->currentItem() ) + m_view->removeHostButton->setEnabled( true ); + hostListModified(); + } +} + +void NmmConfigDialog::removeHost() +{ + m_user_list->takeItem( m_user_list->currentItem() ); + if( !m_user_list->currentItem() ) + m_view->removeHostButton->setEnabled( false ); + hostListModified(); +} + +void NmmConfigDialog::clickedAudioGroup( int new_selection ) +{ + if( current_audio_group_selection == new_selection || new_selection > 2 ) + return; + + /* localhost only, disable host list */ + if( new_selection == 0 ) { + m_view->hostListStack->setDisabled(true); + m_view->addLocationButton->setEnabled( false ); + m_view->removeHostButton->setEnabled( false ); + } + /* environment host list + * disable 'Add...' and 'Remove' buttons, load environment host list + */ + else if( new_selection == 1 ) { + m_view->hostListStack->setEnabled( true ); + m_view->addLocationButton->setEnabled( false ); + m_view->removeHostButton->setEnabled( false ); + m_view->hostListStack->raiseWidget( m_environment_list ); + } + /* user host list + * enable all widgets for host list, load user host list + */ + else if( new_selection == 2 ) { + m_view->hostListStack->setEnabled( true ); + m_view->addLocationButton->setEnabled( true ); + if( !m_user_list->currentItem() ) + m_view->removeHostButton->setEnabled( false ); + else + m_view->removeHostButton->setEnabled( true ); + m_view->hostListStack->raiseWidget( m_user_list ); + } + + current_audio_group_selection = new_selection; +} + +void NmmConfigDialog::notifyHostError( QString hostname, int error ) +{ + m_user_list->notifyHostError( hostname, error ); + m_environment_list->notifyHostError( hostname, error ); +} + +void NmmConfigDialog::enableRemoveButton() +{ + + m_view->removeHostButton->setEnabled( true ); +} + +void NmmConfigDialog::hostListModified() +{ + m_host_list_modified = true; + emit viewChanged(); +} + +void NmmConfigDialog::createHostLists() +{ + DEBUG_BLOCK + + QValueList<NmmLocation>::iterator it; + QValueList<NmmLocation> list; + + list = NmmEngine::instance()->environmentHostList(); + for( it = list.begin(); it != list.end(); ++it ) + new HostListItem( m_environment_list, (*it).hostname(), (*it).audio(), (*it).video(), 0, (*it).status(), true ); + + list = NmmEngine::instance()->userHostList(); + for( it = list.begin(); it != list.end(); ++it ) + new HostListItem( m_user_list, (*it).hostname(), (*it).audio(), (*it).video(), 0, (*it).status() ); +} + +#include "nmm_configdialog.moc" diff --git a/amarok/src/engine/nmm/nmm_configdialog.h b/amarok/src/engine/nmm/nmm_configdialog.h new file mode 100644 index 00000000..97ffd1a1 --- /dev/null +++ b/amarok/src/engine/nmm/nmm_configdialog.h @@ -0,0 +1,125 @@ +/* NMM - Network-Integrated Multimedia Middleware + * + * Copyright (C) 2005-2006 + * NMM work group, + * Computer Graphics Lab, + * Saarland University, Germany + * http://www.networkmultimedia.org + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301 + * USA + */ + + +#ifndef NMMCONFIGDIALOG_H +#define NMMCONFIGDIALOG_H + +#include "nmm_configdialogbase.h" +#include "plugin/pluginconfig.h" + +#include "qobject.h" + +class HostList; +class HostListItem; +class NmmLocation; + +class NmmConfigDialog : public Amarok::PluginConfig +{ + Q_OBJECT + + public: + NmmConfigDialog(); + ~NmmConfigDialog(); + + QWidget* view() { return m_view; } + + // \todo doesn't work the intended way + bool hasChanged() const; + + bool isDefault() const; + + public slots: + + void save(); + + /** + * Adds a host to the user list. + */ + void addHost(); + + /** + * Removes current selected host entry from user list. + */ + void removeHost(); + + /** + * Called when a radio button in audioGroup was clicked. + */ + void clickedAudioGroup( int ); + + /** + * Updates status column for m_user_list and m_environment_list + * to reflect that an error occurred for a host. + * \param hostname host the error is related to + * \param error error identification, see NMMEngineException::Error + */ + void notifyHostError( QString hostname, int error ); + + private slots: + /** + * Enables 'Remove ' host button if a HostListItem is selected. + */ + void enableRemoveButton(); + + /** + * Called when user host list gets modified. + * So either a host entry has been deleted/added + * or the audio/video toggle has changed. + */ + void hostListModified(); + + private: + /** + * Fills user and environment host on config dialog init. + */ + void createHostLists(); + + /** + * Designer ui configuration dialog. + */ + NmmConfigDialogBase* m_view; + + /** + * Current audio group selection. + */ + int current_audio_group_selection; + + /** + * Host list showing read-only environment list. + */ + HostList *m_environment_list; + + /** + * Host list create by the user. + */ + HostList *m_user_list; + + /** + * True if user host list was modified. + */ + bool m_host_list_modified; +}; + +#endif diff --git a/amarok/src/engine/nmm/nmm_configdialogbase.ui b/amarok/src/engine/nmm/nmm_configdialogbase.ui new file mode 100644 index 00000000..99440241 --- /dev/null +++ b/amarok/src/engine/nmm/nmm_configdialogbase.ui @@ -0,0 +1,227 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>NmmConfigDialogBase</class> +<widget class="QWidget"> + <property name="name"> + <cstring>NmmConfigDialogBase</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>581</width> + <height>362</height> + </rect> + </property> + <property name="caption"> + <string>NMM Engine Configuration - Amarok</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout6</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel2</cstring> + </property> + <property name="text"> + <string>Audio plugin:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>deviceComboBox</cstring> + </property> + </widget> + <widget class="QComboBox"> + <item> + <property name="text"> + <string>Playback node</string> + </property> + </item> + <item> + <property name="text"> + <string>ALSA playback node</string> + </property> + </item> + <property name="name"> + <cstring>audioPlaybackNode</cstring> + </property> + <property name="whatsThis" stdset="0"> + <string>Selects audio output plugin. PlaybackNode uses the Open Sound System (<b>OSS</b>). ALSAPlaybackNode uses the Advanced Linux Sound Architecture (<b>ALSA</b>).</string> + </property> + </widget> + </hbox> + </widget> + <spacer> + <property name="name"> + <cstring>spacer2</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Fixed</enum> + </property> + <property name="sizeHint"> + <size> + <width>16</width> + <height>16</height> + </size> + </property> + </spacer> + <widget class="QButtonGroup"> + <property name="name"> + <cstring>audioGroup</cstring> + </property> + <property name="title"> + <string>Video,Audio Location</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QRadioButton"> + <property name="name"> + <cstring>localhostButton</cstring> + </property> + <property name="text"> + <string>Localhost only</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Plays audio and video on the machine running Amarok.</string> + </property> + </widget> + <widget class="QRadioButton"> + <property name="name"> + <cstring>environmentButton</cstring> + </property> + <property name="text"> + <string>Environment variables</string> + </property> + <property name="toolTip" stdset="0"> + <string>Environment variables are AUDIO_HOSTS and VIDEO_HOSTS.</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Reads the environment variables <b>AUDIO_HOSTS</b> and <b>VIDEO_HOSTS</b> to determine the audio and video playback locations. The playback locations will be shown in the host list below. The list is <b>read-only</b>. + +<h3>Example</h3> +AUDIO_HOSTS=desktop:laptop:kitchen<br> +VIDEO_HOSTS=laptop<br> +<br> +This setting will enable audio on the three hosts desktop, laptop and kitchen, and video only on host laptop.</string> + </property> + </widget> + <widget class="QRadioButton"> + <property name="name"> + <cstring>hostListButton</cstring> + </property> + <property name="text"> + <string>Host list</string> + </property> + <property name="whatsThis" stdset="0"> + <string>If selected you can add and remove hosts in the list below and enable audio and video for each host.</string> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout5</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QWidgetStack"> + <property name="name"> + <cstring>hostListStack</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>1</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <widget class="QWidget"> + <property name="name"> + <cstring>WStackPage</cstring> + </property> + <attribute name="id"> + <number>0</number> + </attribute> + </widget> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout13</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QPushButton"> + <property name="name"> + <cstring>addLocationButton</cstring> + </property> + <property name="text"> + <string>Add...</string> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>removeHostButton</cstring> + </property> + <property name="text"> + <string>R&emove</string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer1</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>1</height> + </size> + </property> + </spacer> + </vbox> + </widget> + </hbox> + </widget> + </vbox> + </widget> + <spacer> + <property name="name"> + <cstring>spacer3</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Minimum</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>16</height> + </size> + </property> + </spacer> + </vbox> +</widget> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/amarok/src/engine/nmm/nmm_engine.cpp b/amarok/src/engine/nmm/nmm_engine.cpp new file mode 100644 index 00000000..b2ef176d --- /dev/null +++ b/amarok/src/engine/nmm/nmm_engine.cpp @@ -0,0 +1,712 @@ +/* NMM - Network-Integrated Multimedia Middleware + * + * Copyright (C) 2002-2006 + * NMM work group, + * Computer Graphics Lab, + * Saarland University, Germany + * http://www.networkmultimedia.org + * + * Maintainer: Robert Gogolok <gogo@graphics.cs.uni-sb.de> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301 + * USA + */ + +#include "nmm_engine.h" + +#include "nmm_kdeconfig.h" +#include "nmm_configdialog.h" +#include "HostListItem.h" +#include "debug.h" +#include "plugin/plugin.h" + +#include <nmm/base/graph/GraphBuilder2.hpp> +#include <nmm/base/registry/NodeDescription.hpp> +#include <nmm/base/ProxyApplication.hpp> +#include <nmm/interfaces/base/sync/ISynchronizedSink.hpp> +#include <nmm/interfaces/file/ISeekable.hpp> +#include <nmm/interfaces/file/ITrack.hpp> +#include <nmm/interfaces/file/IBufferSize.hpp> +#include <nmm/interfaces/general/progress/IProgressListener.hpp> +#include <nmm/interfaces/general/progress/IProgress.hpp> +#include <nmm/interfaces/general/ITrackDuration.hpp> +#include <nmm/interfaces/device/audio/IAudioDevice.hpp> +#include <nmm/base/ProxyObject.hpp> +#include <nmm/utils/NMMConfig.hpp> + +#include <qapplication.h> +#include <qtimer.h> + +#include <kfileitem.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <kmimetype.h> +#include <iostream> +#include <kurl.h> + +NmmEngine* NmmEngine::s_instance; + +AMAROK_EXPORT_PLUGIN( NmmEngine ) + +NmmEngine::NmmEngine() + : Engine::Base(), + __position(0), + __track_length(0), + __state(Engine::Empty), + __app(NULL), + __endTrack_listener(this, &NmmEngine::endTrack), + __syncReset_listener(this, &NmmEngine::syncReset), + __setProgress_listener(this, &NmmEngine::setProgress), + __trackDuration_listener(this, &NmmEngine::trackDuration), + __composite(NULL), + __playback(NULL), + __display(NULL), + __av_sync(NULL), + __synchronizer(NULL), + __with_video(false), + __seeking(false), + m_localhostonly_errordialog(false) +{ + addPluginProperty( "HasConfigure", "true" ); +} + +bool NmmEngine::init() +{ + DEBUG_BLOCK + + s_instance = this; + + // disable debug and warning streams + NamedObject::getGlobalInstance().setDebugStream(NULL, NamedObject::ALL_LEVELS); + NamedObject::getGlobalInstance().setWarningStream(NULL, NamedObject::ALL_LEVELS); + + // create new NMM application object + __app = ProxyApplication::getApplication(0, 0); + + createEnvironmentHostList(); + createUserHostList(); + + connect( this, SIGNAL( hostError( QString, int ) ), SLOT( notifyHostError( QString, int ) ) ); + + return true; +} + +NmmEngine::~NmmEngine() +{ + // stop all nodes + stop(); + + // delete application object + if (__app) + delete __app; +} + +void NmmEngine::checkSecurity() +{ + const char* home(getenv("HOME")); + NMMConfig nmmconfig( string(home) + string("/.nmmrc") ); + + bool readpaths_set = false; + bool writepaths_set = false; + string optionvalue(""); + + nmmconfig.getValue("allowedreadpaths", optionvalue); + if( !optionvalue.empty() ) + readpaths_set = true; + + optionvalue = ""; + nmmconfig.getValue("allowedwritepaths", optionvalue); + if( !optionvalue.empty() ) + writepaths_set = true; + + QString str; + str += "<html><body>"; + str += "Your current NMM setup is insecure.<br/><br/>"; + str += "The file <b>.nmmrc</b> in your home directory restricts read and write access for NMM to certain paths.<br/><br/>"; + + if( !readpaths_set ) + str += "<b>allowedreadpaths option is not set</b>. NMM plugins are therefore allowed to read every file the process running NMM is allowed.<br/>"; + + if( !writepaths_set ) + str += "<b>allowedwritepaths option is not set</b>. NMM plugins are therefore allowed to write every file or directory the process running NMM is allowed.<br/>"; + + str += "<br/>See <a href=\"http://www.networkmultimedia.org/Download/\">http://www.networkmultimedia.org/Download/</a> for general security instructions in the correspoding <i>configure and test NMM</i> section depending on your chosen installation method."; + str += "</body></html>"; + + if( !writepaths_set || !readpaths_set ) + KMessageBox::information(0, str, i18n( "Insecure NMM setup" ), "insecureNmmSetup", KMessageBox::AllowLink ); +} + +void NmmEngine::notifyHostError( QString hostname, int error ) +{ + DEBUG_BLOCK + + for( QValueList<NmmLocation>::Iterator it = tmp_user_list.begin(); it != tmp_user_list.end(); ++it ) { + if( (*it).hostname() == hostname ) { + (*it).setStatus( error ); + break; + } + } + + for( QValueList<NmmLocation>::Iterator it = tmp_environment_list.begin(); it != tmp_environment_list.end(); ++it ) { + if( (*it).hostname() == hostname ) { + (*it).setStatus( error ); + break; + } + } + +} + +Engine::State NmmEngine::state() const +{ + return __state; +} + +Amarok::PluginConfig* NmmEngine::configure() const +{ + NmmConfigDialog* dialog = new NmmConfigDialog(); + connect( this, SIGNAL( hostError( QString, int ) ), dialog, SLOT( notifyHostError(QString, int ) ) ); + return dialog; +} + +bool NmmEngine::load(const KURL& url, bool stream) +{ + DEBUG_BLOCK + + static int error; + error = STATUS_UNKNOWN; + + // check security options + static bool already_checked = false; + if( !already_checked) { + QTimer::singleShot(100, this, SLOT( checkSecurity() ) ); + already_checked = true; + } + + // Don't play a track if 'localhost only' error dialog is being shown + if( m_localhostonly_errordialog ) + return false; + + // play only local files + if( !url.isLocalFile() ) { + debug() << "Currently NMM engine can only play local files!" << endl; + return false; + } + + Engine::Base::load(url, stream); + + cleanup(); + + // make the GraphBuilder construct an appropriate graph for the given URL + try { + QStringList hosts; + + // node for audio playback + NodeDescription playback_nd("PlaybackNode"); + // ALSA or OSS + if( NmmKDEConfig::audioOutputPlugin() == "ALSAPlaybackNode" ) + playback_nd = NodeDescription("ALSAPlaybackNode"); + + // TODO: currently we only support one host for audio playback + if( !(hosts = getSinkHosts()).empty() ) + playback_nd.setLocation( hosts.first().ascii() ); + + // node for video playback + NodeDescription display_nd("XDisplayNode"); + + // TODO: currently we only support one host for video playback + if( !(hosts = getSinkHosts( false )).empty() ) + display_nd.setLocation( hosts.first().ascii() ); + + GraphBuilder2 gb; + + // convert the URL to a valid NMM url + if(!gb.setURL("file://" + string(url.path().ascii()))) + throw Exception("Invalid URL given"); + + ClientRegistry& registry = __app->getRegistry(); + // requst playback and audio node {{{ + { + //debug() << "##############> ClientRegistry " << endl; + RegistryLock lock(registry); + + // get a playback node interface from the registry + try { + list<Response> playback_response = registry.initRequest(playback_nd); + if (playback_response.empty()) // playback node not available + throw( NMMEngineException( playback_nd.getLocation(), NmmEngine::ERROR_PLAYBACKNODE ) ); + + __playback = registry.requestNode( playback_response.front() ); + } + catch( RegistryException ) { + error = NmmEngine::ERROR_PLAYBACKNODE; + throw( NMMEngineException( playback_nd.getLocation(), NmmEngine::ERROR_PLAYBACKNODE ) ); + } + catch(...) { + error = NmmEngine::ERROR_PLAYBACKNODE; + throw; + } + + // get a display node interface from the registry + try { + list<Response> display_response = registry.initRequest(display_nd); + if (display_response.empty()) // Display Node not available + throw NMMEngineException( display_nd.getLocation(), NmmEngine::ERROR_DISPLAYNODE ); + + __display = registry.requestNode(display_response.front()); + } + catch( RegistryException ) { + error = NmmEngine::ERROR_DISPLAYNODE; + throw NMMEngineException( display_nd.getLocation(), NmmEngine::ERROR_DISPLAYNODE ); + } + catch(...) { + error = NmmEngine::ERROR_DISPLAYNODE; + throw; + } + + //debug() << "##############< ClientRegistry " << endl; + }//}}} + + __av_sync = new MultiAudioVideoSynchronizer(); + __synchronizer = __av_sync->getCheckedInterface<IMultiAudioVideoSynchronizer>(); + + // initialize the GraphBuilder + gb.setMultiAudioVideoSynchronizer(__synchronizer); + gb.setAudioSink(__playback); + gb.setVideoSink(__display); + gb.setDemuxAudioJackTag("audio"); + gb.setDemuxVideoJackTag("video"); + + // create the graph represented by a composite node + __composite = gb.createGraph(*__app); + + // if the display node is connected we know we will play a video TODO: what about video without audio? + __with_video = __display->isInputConnected(); + debug() << "NMM video playback? " << __with_video << endl; + + // set volume for playback node + setVolume( m_volume ); + + // register the needed event listeners at the display node if video enabled + if(__with_video) { + __display->getParentObject()->registerEventListener(ISyncReset::syncReset_event, &__syncReset_listener); + __display->getParentObject()->registerEventListener(IProgressListener::setProgress_event, &__setProgress_listener); + __display->getParentObject()->registerEventListener(ITrack::endTrack_event, &__endTrack_listener); + + } + else { // in other case at the playback node + __playback->getParentObject()->registerEventListener(ISyncReset::syncReset_event, &__syncReset_listener); + __playback->getParentObject()->registerEventListener(IProgressListener::setProgress_event, &__setProgress_listener); + __playback->getParentObject()->registerEventListener(ITrack::endTrack_event, &__endTrack_listener); + + + (__playback->getParentObject()->getCheckedInterface<ISynchronizedSink>())->setSynchronized(false); + } + + __playback->getParentObject()->registerEventListener(ITrackDuration::trackDuration_event, &__trackDuration_listener); + __display->getParentObject()->registerEventListener(ITrackDuration::trackDuration_event, &__trackDuration_listener); + + // Tell the node that implements the IProgress interface to send progress events frequently. + IProgress_var progress(__composite->getInterface<IProgress>()); + if (progress.get()) { + progress->sendProgressInformation(true); + progress->setProgressInterval(1); + } + + // minimize the buffer size to increase the frequency of progress events + IBufferSize_var buffer_size(__composite->getInterface<IBufferSize>()); + if (buffer_size.get()) { + buffer_size->setBufferSize(1000); + } + + // we don't know the track length yet - we have to wait for the trackDuration event + __track_length = 0; + + __seeking = false; + + // finally start the graph + if(__playback->isActivated()) + __playback->reachStarted(); + if(__display->isActivated()) + __display->reachStarted(); + + __composite->reachStarted(); + + return true; + } + catch ( const NMMEngineException e) { + QString host = e.hostname.c_str(); + emit hostError(host, error); + emit statusText( i18n("NMM engine: Stopping playback...") ); + } + catch (const Exception& e) { + cerr << e << endl; + QString status = e.getComment().c_str() ; + emit statusText( QString( i18n("NMM engine: ") ) + status ); + } + catch(...) { + emit statusText( i18n("NMM engine: Something went wrong...") ); + } + + // loading failed, clean up + cleanup(); + + // if 'Localhost only' playback, show user an error message + // and explanation how to test current NMM setup + if( NmmKDEConfig::location() == NmmKDEConfig::EnumLocation::LocalhostOnly ) + { + m_localhostonly_errordialog = true; + QString detailed_status = HostListItem::prettyStatus( error ); + KMessageBox::detailedError( 0, i18n("Local NMM playback failed."), detailed_status, i18n("Error"), KMessageBox::AllowLink ); + m_localhostonly_errordialog = false; + } + + return false; +} + +bool NmmEngine::play(uint) +{ + DEBUG_BLOCK + + if (!__composite) + return false; + + // TODO: seek to the last position if 'resume playback on startup' is enabled + + __synchronizer->wakeup(); + __state = Engine::Playing; + emit stateChanged(Engine::Playing); + + return true; +} + +void NmmEngine::cleanup() +{ + DEBUG_BLOCK + + // remove all event listeners + if(__display && __with_video ) { + __display->getParentObject()->removeEventListener(&__setProgress_listener); + __display->getParentObject()->removeEventListener(&__endTrack_listener); + __display->getParentObject()->removeEventListener(&__syncReset_listener); + + if( __playback ) + __playback->getParentObject()->removeEventListener(&__trackDuration_listener); + __display->getParentObject()->removeEventListener(&__trackDuration_listener); + + debug() << "removed event listener for __display" << endl; + } + else if (__playback ) { + __playback->getParentObject()->removeEventListener(&__setProgress_listener); + __playback->getParentObject()->removeEventListener(&__endTrack_listener); + __playback->getParentObject()->removeEventListener(&__syncReset_listener); + + __playback->getParentObject()->removeEventListener(&__trackDuration_listener); + if( __display ) + __display->getParentObject()->removeEventListener(&__trackDuration_listener); + + debug() << "removed event listener for __playback" << endl; + } + + if( __composite && __composite->isStarted() ) { + __composite->reachActivated(); + debug() << "__composite STARTED -> ACTIVATED" << endl; + } + + if( __playback && __playback->isStarted() ) { + __playback->reachActivated(); + debug() << "__playback STARTED -> ACTIVATED " << endl; + } + + if( __display && __display->isStarted() ) { + __display->reachActivated(); + debug() << "__display STARTED -> ACTIVATED " << endl; + } + + if( __composite && __composite->isActivated() ) { + __composite->flush(); + __composite->reachConstructed(); + debug() << "__composite ACTIVATED -> CONSTRUCTED " << endl; + } + + if( __playback && __playback->isActivated() ) { + __playback->flush(); + __playback->reachConstructed(); + debug() << "__playback ACTIVATED -> CONSTRUCTED " << endl; + } + + if( __display && __display->isActivated() ) { + __display->flush(); + __display->reachConstructed(); + debug() << "__display ACTIVATED -> CONSTRUCTED " << endl; + } + + // release the playback and video node + ClientRegistry& registry = __app->getRegistry(); + { + RegistryLock lock(registry); + if(__playback) { + registry.releaseNode(*__playback); + debug() << "RELEASED __playback node" << endl; + } + if(__display) { + registry.releaseNode(*__display); + debug() << "RELEASED __display node" << endl; + } + } + + delete __composite; + __composite = NULL; + delete __playback; + __playback = NULL; + delete __display; + __display = NULL; + + __with_video = false; + + delete __synchronizer; + __synchronizer = NULL; + delete __av_sync; + __av_sync = NULL; + + __position = 0; + __state = Engine::Idle; +} + +void NmmEngine::createEnvironmentHostList() +{ + QString hosts = getenv("AUDIO_HOSTS"); + QStringList list = QStringList::split(":", hosts ); + + /* merge audio hosts */ + for( QStringList::Iterator it = list.begin(); it != list.end(); ++it ) { + tmp_environment_list.append( NmmLocation( (*it), true, false, 0, NmmEngine::STATUS_UNKNOWN ) ); + } + + /* merge video hosts */ + hosts = getenv("VIDEO_HOSTS"); + list = QStringList::split(":", hosts ); + bool found = false; + for( QStringList::Iterator it = list.begin(); it != list.end(); ++it ) { + + found = false; + for( QValueList<NmmLocation>::Iterator it_t = tmp_environment_list.begin(); it_t != tmp_environment_list.end(); ++it_t ) { + if( (*it_t).hostname() == *it ) { + (*it_t).setVideo(true); + found = true; + break; + } + } + + if( !found ) + tmp_environment_list.append( NmmLocation( (*it), false, true, 0, NmmEngine::STATUS_UNKNOWN ) ); + } + + //debug() << "### ENVIRONMENT" << endl; + //for( QValueList<NmmLocation>::Iterator it = tmp_environment_list.begin(); it != tmp_environment_list.end(); ++it ) { + //debug() << "### hostname " << (*it).hostname() << endl; + //debug() << "### audio " << (*it).audio() << endl; + //debug() << "### video " << (*it).video() << endl; + //debug() << "#########################" << endl; + //} +} + +void NmmEngine::createUserHostList() +{ + QStringList hosts = NmmKDEConfig::hostList(); + QStringList audio_list = NmmKDEConfig::audioToggle(); + QStringList video_list = NmmKDEConfig::videoToggle(); + + bool audio = false; + bool video = false; + + unsigned int size = hosts.size(); + for(unsigned int i = 0; i < size; i++ ) { + if( audio_list[i] == "1") + audio = true; + else + audio = false; + + if( video_list[i] == "1") + video = true; + else + video = false; + + tmp_user_list.append( NmmLocation( hosts[i], audio, video, /* TODO: volume */0, NmmEngine::STATUS_UNKNOWN ) ); + } +} + +void NmmEngine::stop() +{ + DEBUG_BLOCK + + cleanup(); + + __state = Engine::Empty; + emit stateChanged(Engine::Empty); +} + +void NmmEngine::pause() +{ + if (!__composite) + return; + + debug() << "pause()" << endl; + if( __state == Engine::Playing ) { + __synchronizer->pause(); + __state = Engine::Paused; + emit stateChanged(Engine::Paused); + } + else if ( __state == Engine::Paused ) { + __synchronizer->wakeup(); + __state = Engine::Playing; + emit stateChanged(Engine::Playing); + } +} + +void NmmEngine::seek(uint ms) +{ + if (!__track_length) + return; + + __seeking = true; + __position = ms; + + ISeekable_var seek(__composite->getCheckedInterface<ISeekable>()); + if (seek.get()) + seek->seekPercentTo(Rational(ms, __track_length)); +} + +void NmmEngine::endOfStreamReached() +{ + DEBUG_BLOCK + emit trackEnded(); +} + +uint NmmEngine::position() const +{ + return __position; +} + +uint NmmEngine::length() const +{ + return __track_length; +} + +bool NmmEngine::canDecode(const KURL& url) const +{ + static QStringList types; + + if (url.protocol() == "http" ) return false; + + // the following MIME types can be decoded + types += QString("audio/x-mp3"); + types += QString("audio/x-wav"); + types += QString("audio/ac3"); + types += QString("audio/vorbis"); + types += QString("video/mpeg"); + types += QString("video/x-msvideo"); + types += QString("video/x-ogm"); + + KFileItem fileItem( KFileItem::Unknown, KFileItem::Unknown, url, false ); //false = determineMimeType straight away + KMimeType::Ptr mimetype = fileItem.determineMimeType(); + + return types.contains(mimetype->name()); +} + + +void NmmEngine::setVolumeSW(uint percent) +{ + if( __playback ) + { + IAudioDevice_var audio(__playback->getParentObject()->getCheckedInterface<IAudioDevice>()); + audio->setVolume( percent ); + } +} + +QStringList NmmEngine::getSinkHosts( bool audio ) +{ + QStringList hosts; + // TODO: redundant code... + + // read locations from environment variable + if( NmmKDEConfig::location() == NmmKDEConfig::EnumLocation::EnvironmentVariable ) + { + for( QValueList<NmmLocation>::Iterator it = tmp_environment_list.begin(); it != tmp_environment_list.end(); ++it ) { + if( audio && (*it).audio() ) + hosts.append( (*it).hostname() ); + else if( !audio && (*it).video() ) + hosts.append( (*it).hostname() ); + } + //debug() << "locations from environment variable are => " << hosts << endl; + return hosts; + + } + // read locations from host list + else if( NmmKDEConfig::location() == NmmKDEConfig::EnumLocation::HostList ) + { + for( QValueList<NmmLocation>::Iterator it = tmp_user_list.begin(); it != tmp_user_list.end(); ++it ) { + if( audio && (*it).audio() ) + hosts.append( (*it).hostname() ); + else if( !audio && (*it).video() ) + hosts.append( (*it).hostname() ); + } + + return hosts; + } + + // localhost only + return hosts; +} + +Result NmmEngine::setProgress(u_int64_t& numerator, u_int64_t& denominator) +{ + // compute the track position in milliseconds + u_int64_t position = numerator * __track_length / denominator; + + if (__seeking) + return SUCCESS; + + __position = position; + + return SUCCESS; +} + +Result NmmEngine::endTrack() +{ + __state = Engine::Idle; + __position = 0; + + // cleanup after this method returned + QTimer::singleShot( 0, instance(), SLOT( endOfStreamReached() ) ); + + return SUCCESS; +} + +Result NmmEngine::syncReset() +{ + __seeking = false; + return SUCCESS; +} + +Result NmmEngine::trackDuration(Interval& duration) +{ + // we got the duration of the track, so let's convert it to milliseconds + __track_length = duration.sec * 1000 + duration.nsec / 1000; + kdDebug() << "NmmEngine::trackDuration " << __track_length << endl; + return SUCCESS; +} + +#include "nmm_engine.moc" diff --git a/amarok/src/engine/nmm/nmm_engine.h b/amarok/src/engine/nmm/nmm_engine.h new file mode 100644 index 00000000..dad6887c --- /dev/null +++ b/amarok/src/engine/nmm/nmm_engine.h @@ -0,0 +1,264 @@ +/* NMM - Network-Integrated Multimedia Middleware + * + * Copyright (C) 2002-2006 + * NMM work group, + * Computer Graphics Lab, + * Saarland University, Germany + * http://www.networkmultimedia.org + * + * Maintainer: Robert Gogolok <gogo@graphics.cs.uni-sb.de> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301 + * USA + */ + + +#ifndef NMM_ENGINE_H +#define NMM_ENGINE_H + +#include <config.h> + +#include "enginebase.h" +#include "NmmLocation.h" +#include <nmm/base/graph/CompositeNode.hpp> +#include <nmm/base/NMMApplication.hpp> +#include <nmm/base/EDObject.hpp> +#include <nmm/base/sync/MultiAudioVideoSynchronizer.hpp> + +using namespace NMM; + +/** + * \todo currently every song/video change means a stop and restart of the NMM environment. + */ +class NmmEngine : public Engine::Base +{ +Q_OBJECT +public: + NmmEngine(); + ~NmmEngine(); + + bool init(); + bool canDecode(const KURL&) const; + + uint position() const; + + uint length() const; + + Engine::State state() const; + + Amarok::PluginConfig* configure() const; + + QValueList<NmmLocation> environmentHostList() const {return tmp_environment_list;} + void setEnvironmentHostList(QValueList<NmmLocation> list) { tmp_environment_list = list;} + + QValueList<NmmLocation> userHostList() const {return tmp_user_list;} + void setUserHostList(QValueList<NmmLocation> list) { tmp_user_list = list;} + + + static NmmEngine* instance() { return s_instance; } + +public slots: + bool load(const KURL&, bool stream = false); + bool play(unsigned int offset = 0); + void stop(); + void pause(); + void seek(uint); + +private slots: + void endOfStreamReached(); + + /** + * Checks for local NMM security options. + * Warns user if ~/.nmmrc doesn't have + * allowedwritepaths or allowedreadpaths set. + * TODO: Should be called on NMM engine load and not from ::load. + */ + void checkSecurity(); + + /** + * Updates error type in tmp_environment_list and tmp_user_list for a host. + * \param hostname host the error is related to + * \param error error identification, see NMMEngineException::Error + */ + void notifyHostError( QString hostname, int error ); + +signals: + /** + * Emitted when an error occurred during NMM setup. + * \param hostname host the error is related to + * \param error error identification, see NMMEngineException::Error + */ + void hostError( QString hostname, int error ); + +protected: + void setVolumeSW( uint ); + +private: + + /** + * Resets current NMM environment and other configurations. + */ + void cleanup(); + + /** + * \todo document + */ + void createEnvironmentHostList(); + + /** + * \todo document + */ + void createUserHostList(); + + /** + * Returns sink locations for audio/video playback. + * + * \param audio return audio locations, else video locations + * + * \return the audio/video locations + */ + QStringList getSinkHosts( bool audio = true ); + + /** + * This method is called when a setProgress event is received. The two parameters represent a rational number + * (numerator and denominator) containing the amount of progress as a value between 0 and 1. + */ + Result setProgress(u_int64_t&, u_int64_t&); + + /** + * This method is called when an endTrack event is received. + */ + Result endTrack(); + + /** + * This method is called when a syncReset event is received. When an NMM source node has finished seeking, such an event is + * sent. Here, it is used to prevent the engine from updating the track position while receiving setProgress events before seeking + * is done, since these setProgress events may contain old progress information. Otherwise, the progress slider would jump + * back and forth... + */ + Result syncReset(); + + /** + * This method is called when a trackDuration event is received. The duration of a track is represented by + * an Interval that contains the time in seconds and nanoseconds. + */ + Result trackDuration(Interval& duration); + + /** + * The current track position in milliseconds. + */ + u_int64_t __position; + + /** + * The length of the track in milliseconds. + */ + u_int64_t __track_length; + + /** + * The current engine state + */ + Engine::State __state; + + /** + * The NMM application object. + */ + NMMApplication* __app; + + /** + * Event listeners for various NMM events. + */ + TEDObject0<NmmEngine> __endTrack_listener; + TEDObject0<NmmEngine> __syncReset_listener; + TEDObject2<NmmEngine, u_int64_t, u_int64_t> __setProgress_listener; + TEDObject1<NmmEngine, Interval> __trackDuration_listener; + + /** + * The composite node that contains the graph created by the GraphBuilder. + */ + CompositeNode* __composite; + + /** + * The node for audio playback + * where the various events like endTrack, setProgress etc. are caught + * if video is disabled. + */ + INode* __playback; + + /** + * The display node for video playback + * where the various events like endTrack, setProgress etc. are caught + * if video is enabled. + */ + INode* __display; + + /** + * synchronizer for graph builder + */ + MultiAudioVideoSynchronizer* __av_sync; + + /** + * synchronizer interface + */ + IMultiAudioVideoSynchronizer* __synchronizer; + + /** + * Indicates whether we are playing a video. + */ + bool __with_video; + + /** + * This flag is set during seeking. + */ + bool __seeking; + + /** + * Used to determine whether an errorDialog is being displayed + * in 'localhost only' mode. No track should be played till + * the user clicked 'Ok'. + */ + bool m_localhostonly_errordialog; + + /** + * Environment variables host list. + * Only read on startup, volume can be changed via settings dialog. + */ + QValueList<NmmLocation> tmp_environment_list; + + /** + * User host list. + */ + QValueList<NmmLocation> tmp_user_list; + + static NmmEngine* s_instance; + +public: + enum HostStatus { + STATUS_UNKNOWN = 0, + STATUS_OK = 1 << 0, + ERROR_PLAYBACKNODE = 1 << 1, + ERROR_DISPLAYNODE = 1 << 2, + }; +}; + +class NMMEngineException { + public: + NMMEngineException(std::string _hostname, int _error) + : hostname( _hostname ), error( _error ) {} + + std::string hostname; + int error; +}; + +#endif diff --git a/amarok/src/engine/nmm/nmm_kdeconfig.kcfg b/amarok/src/engine/nmm/nmm_kdeconfig.kcfg new file mode 100644 index 00000000..114d3b8b --- /dev/null +++ b/amarok/src/engine/nmm/nmm_kdeconfig.kcfg @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Authors: Joerg Bakker, joerg@hakker.de --> +<!-- Robert Gogolok, gogo@graphics.cs.uni-sb.de --> +<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0 + http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" > + <kcfgfile name="amarokrc"/> + + <group name="NMM-Engine"> + <entry key="Audio Output Plugin" type="String"> + <label>Audio output method to use</label> + <whatsthis>Select the audio output plugin.</whatsthis> + <default>PlaybackNode</default> + </entry> + <entry key="Location" type="Enum"> + <label>Type of source for audio and video location</label> + <whatsthis>Type of location of audio and video sink: environment variable, fixed host name or localhost only.</whatsthis> + <choices> + <choice name="LocalhostOnly"/> + <choice name="EnvironmentVariable"/> + <choice name="HostList"/> + </choices> + <default>LocalhostOnly</default> + </entry> + <entry key="Host List" type="StringList"> + <label>Hostnames of audio and video sinks</label> + <whatsthis>Names of hosts where your audio and video sink can be located if Location equals SinkHostName.</whatsthis> + <default></default> + </entry> + <entry key="Audio Toggle" type="StringList"> + <label>Toggle for audio playback</label> + <whatsthis>Indicates for every host in Host List whether audio is enabled/disabled.</whatsthis> + <default></default> + </entry> + <entry key="Video Toggle" type="StringList"> + <label>Toggle for video playback</label> + <whatsthis>Indicates for every host in Host List whether video is enabled/disabled.</whatsthis> + <default></default> + </entry> + </group> +</kcfg> diff --git a/amarok/src/engine/nmm/nmm_kdeconfig.kcfgc b/amarok/src/engine/nmm/nmm_kdeconfig.kcfgc new file mode 100644 index 00000000..8db0f8c5 --- /dev/null +++ b/amarok/src/engine/nmm/nmm_kdeconfig.kcfgc @@ -0,0 +1,7 @@ +# Code generation options for kconfig_compiler +File=nmm_kdeconfig.kcfg +ClassName=NmmKDEConfig +Singleton=true +Mutators=true +MemberVariables=private +GlobalEnums=tru diff --git a/amarok/src/engine/void/Makefile.am b/amarok/src/engine/void/Makefile.am new file mode 100644 index 00000000..ea3b5b0c --- /dev/null +++ b/amarok/src/engine/void/Makefile.am @@ -0,0 +1,25 @@ +kde_module_LTLIBRARIES = \ + libamarok_void-engine_plugin.la + +INCLUDES = \ + -I$(top_srcdir)/amarok/src/plugin \ + -I$(top_srcdir)/amarok/src \ + $(all_includes) + +libamarok_void_engine_plugin_la_LIBADD = \ + $(top_builddir)/amarok/src/libamarok.la \ + $(top_builddir)/amarok/src/plugin/libplugin.la \ + $(LIB_KDECORE) $(LIB_QT) + +libamarok_void_engine_plugin_la_SOURCES = \ + void-engine.cpp + +libamarok_void_engine_plugin_la_LDFLAGS = \ + $(KDE_PLUGIN) \ + $(all_libraries) + +METASOURCES = \ + AUTO + +kde_services_DATA = \ + amarok_void-engine_plugin.desktop diff --git a/amarok/src/engine/void/amarok_void-engine_plugin.desktop b/amarok/src/engine/void/amarok_void-engine_plugin.desktop new file mode 100644 index 00000000..c3338528 --- /dev/null +++ b/amarok/src/engine/void/amarok_void-engine_plugin.desktop @@ -0,0 +1,118 @@ +[Desktop Entry] +Encoding=UTF-8 +Type=Service +Name=<no engine> +Name[af]=<geen enjin> +Name[ar]= <ليس هناك محرك صوتي > +Name[bg]=<без система> +Name[bn]=<কোনও ইঞ্জিন নয়> +Name[br]=<keflusker ebet> +Name[ca]=<sense motor> +Name[cs]=<žádný systém> +Name[da]=<ingen motor> +Name[de]=Keine Audio-Ausgabe +Name[el]=<χωρίς μηχανή> +Name[eo]=<neniu ilo> +Name[et]=<mootor puudub> +Name[eu]=<motorerik ez> +Name[fa]=<بدون موتور> +Name[fi]=<ei järjestelmää> +Name[fr]=<aucun moteur> +Name[ga]=<gan inneall> +Name[gl]=<sen motor> +Name[he]=<ללא מנוע שמע> +Name[hi]=<कोई इंजिन नहीं> +Name[hu]=<nincs alrendszer> +Name[is]=<engin vél> +Name[it]=<nessun motore> +Name[ja]=<エンジンなし> +Name[ka]=<ძრავის გარეშე> +Name[km]=<គ្មាន​ម៉ាស៊ីន> +Name[lt]=<nėra> +Name[mk]=<нема машина> +Name[mn]=<нема машина> +Name[ms]=<tiada enjin> +Name[nb]=<ingen motor> +Name[nds]=<keen> +Name[nl]=<geen engine> +Name[nn]=<ingen motor> +Name[pl]=<brak wyjścia> +Name[pt]=<sem motor> +Name[pt_BR]=<nenhum mecanismo> +Name[ru]=<нет> +Name[se]=<ii makkárge mohtor> +Name[sk]=<žiadny engine> +Name[sl]=<brez pogona> +Name[sq]=<nema motora> +Name[sr]=<нема мотора> +Name[sr@Latn]=<nema motora> +Name[ss]=<nema motora> +Name[sv]=<inget gränssnitt> +Name[th]=<ไม่มีโปรแกรมประมวลผลเสียง> +Name[tr]=<motor yok> +Name[uk]=<немає рушія> +Name[uz]=<yoʻq> +Name[uz@cyrillic]=<йўқ> +Name[zh_CN]=<无引擎> +Name[zh_TW]=<不使用解碼引擎> +X-KDE-Library=libamarok_void-engine_plugin +Comment=Plugin for Amarok +Comment[af]=Inprop module vir Amarok +Comment[ar]= قابس ( برنامج مضاف الى) AmaroK +Comment[bg]=Приставка за Amarok +Comment[bn]=আমারক-এর জন্য প্লাগিন +Comment[br]=Lugent evit Amarok +Comment[ca]=Connector per l'Amarok +Comment[cs]=Modul pro AmaroK +Comment[de]=Modul für Amarok +Comment[el]=Πρόσθετο για το AmaroK +Comment[eo]=Kromaĵo por Amarok +Comment[es]=Extensión para Amarok +Comment[et]=Amaroki plugin +Comment[fa]=وصله برای amaroK +Comment[fi]=Amarok-liitännäinen +Comment[fr]=Module pour Amarok +Comment[ga]=Breiseán AmaroK +Comment[gl]=Extensión para Amarok +Comment[hu]=Bővítőmodul az Amarokhoz +Comment[is]=Íforrit fyrir Amarok +Comment[it]=Plugin per Amarok +Comment[ja]=Amarok のためのプラグイン +Comment[ka]=მოდული Amarok-ისთვის +Comment[km]=កម្មវិធី​ជំនួយ​សម្រាប់ Amarok +Comment[lt]=Amarok įskiepis +Comment[mk]=Приклучок за Амарок +Comment[nb]=Programtillegg for Amarok +Comment[nds]=Moduul för Amarok +Comment[ne]=अमारोकका लागि प्लगइन +Comment[nl]=Plugin voor Amarok +Comment[nn]=Programtillegg for Amarok +Comment[pa]=ਅਮਰੋਕ ਲਈ ਪਲੱਗਇਨ +Comment[pl]=Wtyczka Amaroka +Comment[pt]='Plugin' para o Amarok +Comment[pt_BR]=Plugin para o Amarok +Comment[ru]=Модуль amaroK +Comment[se]=Lassemoduvla Amarok:ii +Comment[sk]=Amarok modul +Comment[sr]=Прикључак за Amarok +Comment[sr@Latn]=Priključak za Amarok +Comment[sv]=Insticksprogram för Amarok +Comment[th]=โปรแกรมเสริมสำหรับ Amarok +Comment[tr]=Amarok için Eklenti +Comment[uk]=Втулок для Amarok +Comment[uz]=Amarok uchun plagin +Comment[uz@cyrillic]=Amarok учун плагин +Comment[wa]=Tchôke-divins po Amarok +Comment[zh_CN]=Amarok 插件 +Comment[zh_TW]=amaroK 插件 +ServiceTypes=Amarok/Plugin + +X-KDE-Amarok-plugintype=engine +X-KDE-Amarok-name=void-engine +X-KDE-Amarok-authors=Max Howell,Mark Kretschmann +X-KDE-Amarok-email=max.howell@methylblue.com +X-KDE-Amarok-rank=1 +X-KDE-Amarok-version=1 +X-KDE-Amarok-framework-version=32 + + diff --git a/amarok/src/engine/void/void-engine.cpp b/amarok/src/engine/void/void-engine.cpp new file mode 100644 index 00000000..394c8310 --- /dev/null +++ b/amarok/src/engine/void/void-engine.cpp @@ -0,0 +1,33 @@ +/*************************************************************************** + void-engine.h - Dummy engine plugin + +copyright : (C) 2003 by Max Howell <max.howell@methylblue.com> +copyright : (C) 2004 by Mark Kretschmann <markey@web.de> +***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#include "void-engine.h" + +#include <klocale.h> + + +AMAROK_EXPORT_PLUGIN( VoidEngine ) + + +bool +VoidEngine::load( const KURL& url, bool stream ) +{ + Engine::Base::load( url, stream ); + emit statusText( i18n( "Error: No engine loaded, cannot start playback." ) ); + + return false; +} + diff --git a/amarok/src/engine/void/void-engine.h b/amarok/src/engine/void/void-engine.h new file mode 100644 index 00000000..b5d311d0 --- /dev/null +++ b/amarok/src/engine/void/void-engine.h @@ -0,0 +1,39 @@ +/*************************************************************************** + void-engine.cpp - Dummy engine plugin + +copyright : (C) 2003 by Max Howell <max.howell@methylblue.com> +copyright : (C) 2004 by Mark Kretschmann <markey@web.de> +***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#include "enginebase.h" + +class VoidEngine : public Engine::Base +{ + //Does nothing, just here to prevent crashes on startup + //and in case no engines are found + + virtual bool init() { return true; } + virtual bool canDecode( const KURL& ) const { return false; } + virtual uint position() const { return 0; } + virtual bool load( const KURL&, bool ); + virtual bool play( uint ) { return false; } + virtual void stop() {} + virtual void pause() {} + virtual void unpause() {} + virtual void setVolumeSW( uint ) {} + virtual void seek( uint ) {} + + virtual Engine::State state() const { return Engine::Empty; } + +public: VoidEngine() : EngineBase() {} +}; + diff --git a/amarok/src/engine/xine/Makefile.am b/amarok/src/engine/xine/Makefile.am new file mode 100644 index 00000000..2a551f7f --- /dev/null +++ b/amarok/src/engine/xine/Makefile.am @@ -0,0 +1,31 @@ +kde_module_LTLIBRARIES = libamarok_xine-engine.la +kde_services_DATA = amarok_xine-engine.desktop + +INCLUDES = \ + -I$(top_srcdir)/amarok/src \ + -I$(top_srcdir)/amarok/src/amarokcore \ + -I$(top_builddir)/amarok/src/amarokcore \ + $(XINE_CFLAGS) \ + $(all_includes) + +libamarok_xine_engine_la_LIBADD = \ + $(top_builddir)/amarok/src/libamarok.la \ + $(top_builddir)/amarok/src/plugin/libplugin.la \ + -lkdeui -lkdecore \ + $(XINE_LIBS) $(LIB_QT) + +libamarok_xine_engine_la_SOURCES = \ + xine-scope.c \ + xinecfg.kcfgc \ + xine-engine.cpp \ + xineconfigbase.ui \ + xine-config.cpp + +libamarok_xine_engine_la_LDFLAGS = \ + $(KDE_PLUGIN) \ + $(all_libraries) + +kde_kcfg_DATA = \ + xinecfg.kcfg + +METASOURCES = AUTO diff --git a/amarok/src/engine/xine/amarok_xine-engine.desktop b/amarok/src/engine/xine/amarok_xine-engine.desktop new file mode 100644 index 00000000..ee732da9 --- /dev/null +++ b/amarok/src/engine/xine/amarok_xine-engine.desktop @@ -0,0 +1,121 @@ +[Desktop Entry] +Encoding=UTF-8 +Type=Service +Name=xine Engine +Name[af]=XINE enjin +Name[ar]=محرك xine +Name[bg]=Xine +Name[bn]=জাইন ইঞ্জিন +Name[br]=Keflusker xine +Name[ca]=Motor xine +Name[cs]=xine +Name[da]=xine-motor +Name[de]=xine +Name[el]=Μηχανή xine +Name[eo]=xine Ilo +Name[es]=Motor xine +Name[et]=xine mootor +Name[eu]=xine motorea +Name[fa]=موتور xine +Name[fi]=xine +Name[fr]=Moteur xine +Name[ga]=Inneall xine +Name[gl]=Motor Xine +Name[he]=מנוע שמע xine +Name[hi]=एक्जाइन इंजिन +Name[hu]=Xine alrendszer +Name[is]=xine vél +Name[it]=Motore xine +Name[ja]=xine エンジン +Name[ka]=xine dრავა +Name[km]=ម៉ាស៊ីន xine +Name[lt]=Xine variklis +Name[mk]=xine-машина +Name[ms]=Enjin xine +Name[nb]=xine-motor +Name[nds]=Xine +Name[ne]=जाइन इन्जिन +Name[nl]=xine-engine +Name[nn]=xine-motor +Name[pa]=xine ਇੰਜਣ +Name[pl]=Wyjście xine +Name[pt]=Motor Xine +Name[pt_BR]=Mecanismo Xine +Name[ru]=Xine +Name[se]=xine-mohtor +Name[sl]=Pogon xine +Name[sq]=Motor Xine +Name[sr]=Мотор Xine +Name[sr@Latn]=Motor Xine +Name[ss]=Motor Xine +Name[sv]=Xine-gränssnitt +Name[ta]=xine என்ஜின் +Name[tg]=Муҳаррики xine +Name[tr]=xine Motoru +Name[uk]=Рушій xine +Name[uz]=xine tizimi +Name[uz@cyrillic]=xine тизими +Name[wa]=Éndjin xine +Name[zh_CN]=xine 引擎 +Name[zh_TW]=xine 解碼引擎 +X-KDE-Library=libamarok_xine-engine +Comment=Plugin for Amarok +Comment[af]=Inprop module vir Amarok +Comment[ar]= قابس ( برنامج مضاف الى) AmaroK +Comment[bg]=Приставка за Amarok +Comment[bn]=আমারক-এর জন্য প্লাগিন +Comment[br]=Lugent evit Amarok +Comment[ca]=Connector per l'Amarok +Comment[cs]=Modul pro AmaroK +Comment[de]=Modul für Amarok +Comment[el]=Πρόσθετο για το AmaroK +Comment[eo]=Kromaĵo por Amarok +Comment[es]=Extensión para Amarok +Comment[et]=Amaroki plugin +Comment[fa]=وصله برای amaroK +Comment[fi]=Amarok-liitännäinen +Comment[fr]=Module pour Amarok +Comment[ga]=Breiseán AmaroK +Comment[gl]=Extensión para Amarok +Comment[hu]=Bővítőmodul az Amarokhoz +Comment[is]=Íforrit fyrir Amarok +Comment[it]=Plugin per Amarok +Comment[ja]=Amarok のためのプラグイン +Comment[ka]=მოდული Amarok-ისთვის +Comment[km]=កម្មវិធី​ជំនួយ​សម្រាប់ Amarok +Comment[lt]=Amarok įskiepis +Comment[mk]=Приклучок за Амарок +Comment[nb]=Programtillegg for Amarok +Comment[nds]=Moduul för Amarok +Comment[ne]=अमारोकका लागि प्लगइन +Comment[nl]=Plugin voor Amarok +Comment[nn]=Programtillegg for Amarok +Comment[pa]=ਅਮਰੋਕ ਲਈ ਪਲੱਗਇਨ +Comment[pl]=Wtyczka Amaroka +Comment[pt]='Plugin' para o Amarok +Comment[pt_BR]=Plugin para o Amarok +Comment[ru]=Модуль amaroK +Comment[se]=Lassemoduvla Amarok:ii +Comment[sk]=Amarok modul +Comment[sr]=Прикључак за Amarok +Comment[sr@Latn]=Priključak za Amarok +Comment[sv]=Insticksprogram för Amarok +Comment[th]=โปรแกรมเสริมสำหรับ Amarok +Comment[tr]=Amarok için Eklenti +Comment[uk]=Втулок для Amarok +Comment[uz]=Amarok uchun plagin +Comment[uz@cyrillic]=Amarok учун плагин +Comment[wa]=Tchôke-divins po Amarok +Comment[zh_CN]=Amarok 插件 +Comment[zh_TW]=amaroK 插件 +ServiceTypes=Amarok/Plugin + +X-KDE-Amarok-plugintype=engine +X-KDE-Amarok-name=xine-engine +X-KDE-Amarok-authors=Max Howell +X-KDE-Amarok-email=max.howell@methylblue.com +X-KDE-Amarok-rank=255 +X-KDE-Amarok-version=1 +X-KDE-Amarok-framework-version=32 + + diff --git a/amarok/src/engine/xine/amarok_xine-mp3_install.desktop b/amarok/src/engine/xine/amarok_xine-mp3_install.desktop new file mode 100644 index 00000000..172d49ea --- /dev/null +++ b/amarok/src/engine/xine/amarok_xine-mp3_install.desktop @@ -0,0 +1,9 @@ +[Desktop Entry] +Encoding=UTF-8 +Type=Service +ServiceTypes=Amarok/CodecInstall +X-KDE-Amarok-codec=mp3 +X-KDE-Amarok-engine=xine-engine +Exec=/opt/kde3/lib/amarok/install-mp3 + + diff --git a/amarok/src/engine/xine/xine-config.cpp b/amarok/src/engine/xine/xine-config.cpp new file mode 100644 index 00000000..95295d3f --- /dev/null +++ b/amarok/src/engine/xine/xine-config.cpp @@ -0,0 +1,310 @@ +//(C) 2005 Ian Monroe <ian@monroe.nu> +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#include "debug.h" +#include "xine-config.h" +#include "xinecfg.h" + +#include <kcombobox.h> +#include <klocale.h> +#include <knuminput.h> +#include <kstandarddirs.h> + +#include <qgroupbox.h> +#include <qlabel.h> +#include <qpixmap.h> + +//////////////////// +/// XineGeneralEntry +//////////////////// +XineGeneralEntry::XineGeneralEntry(const QString& key, xine_t *xine, XineConfigDialog* xcf) + : m_valueChanged(false) + , m_key(key) + , m_xine(xine) +{ + debug() << "new entry " << m_key << endl; + connect(this, SIGNAL(viewChanged()), xcf, SIGNAL(viewChanged() ) ); +} + + +void XineGeneralEntry::entryChanged() +{ + m_valueChanged = true; + emit viewChanged(); +} + +///////////////// +/// Function saveXineEntry +///////////////// +template<class T, class Functor> +void saveXineEntry(Functor& storeEntry, T val, const QString& key, xine_t *xine) +{ + if(xine) debug() << "its not null " << key << ' ' << val << endl; + xine_cfg_entry_t ent; + if(xine_config_lookup_entry(xine, key.ascii(), &ent)) + { + storeEntry(&ent, val); + xine_config_update_entry(xine, &ent); + } + else + debug()<<"Error saving " << val << " with key " << key; + +} +////////////////// +/// Functors +////////////////// +void +XineIntFunctor::operator()( xine_cfg_entry_t* ent, int val ) +{ + ent->num_value = val; +} + + +void +XineStrFunctor::operator()( xine_cfg_entry_t* ent, const QString& val ) +{ + ent->str_value = const_cast<char*>(val.ascii()); +} + +//////////////////// +/// XineStrEntry +//////////////////// +XineStrEntry::XineStrEntry(QLineEdit* input, const QCString & key, xine_t *xine, XineConfigDialog* xcf) + : XineGeneralEntry(key,xine,xcf) +{ + xine_cfg_entry_t ent; + if( xine_config_lookup_entry(m_xine, m_key.ascii(), &ent) ) + { + input->setText(ent.str_value); + m_val = ent.str_value; + } + connect( input, SIGNAL( textChanged( const QString & ) ), this, SLOT( entryChanged(const QString &) ) ); +} + + +void +XineStrEntry::save() +{ + XineStrFunctor func; + saveXineEntry(func, m_val, m_key, m_xine); + m_valueChanged = false; +} + + +void +XineStrEntry::entryChanged(const QString & val) +{ + m_val = val; + XineGeneralEntry::entryChanged(); +} + +//////////////////// +/// XineIntEntry +//////////////////// +XineIntEntry::XineIntEntry(KIntSpinBox* input, const QCString & key, xine_t *xine, XineConfigDialog* xcf) + : XineGeneralEntry(key,xine,xcf) +{ + xine_cfg_entry_t ent; + if(xine_config_lookup_entry(m_xine, m_key.ascii(), &ent)) + { + input->setValue(ent.num_value); + m_val = ent.num_value; + } + connect( input, SIGNAL( valueChanged( int ) ), this, SLOT( entryChanged( int ) ) ); +} + + +XineIntEntry::XineIntEntry(const QString& key, xine_t *xine, XineConfigDialog* xcf) + : XineGeneralEntry(key,xine,xcf) +{ } + + +void +XineIntEntry::save() +{ + XineIntFunctor func; + saveXineEntry(func, m_val, m_key, m_xine); + m_valueChanged = false; +} + + +void +XineIntEntry::entryChanged(int val) +{ + m_val = val; + XineGeneralEntry::entryChanged(); +} + +//////////////////// +/// XineEnumEntry +//////////////////// +XineEnumEntry::XineEnumEntry(QComboBox* input, const QCString & key, xine_t *xine, XineConfigDialog* xcf) + : XineIntEntry(key,xine,xcf) +{ + input->clear(); + xine_cfg_entry_t ent; + if(xine_config_lookup_entry(m_xine, m_key.ascii(), &ent)) + { + for( int i = 0; ent.enum_values[i]; ++i ) + { + input->insertItem( QString::fromLocal8Bit( ent.enum_values[i] ) ); + input->setCurrentItem( ent.num_value ); + m_val = ent.num_value; + } + } + connect( input, SIGNAL( activated( int ) ), this, SLOT( entryChanged( int ) ) ); +} + +/////////////////////// +/// XineConfigDialog +/////////////////////// + +XineConfigDialog::XineConfigDialog( const xine_t* const xine) + : PluginConfig() + , m_xine (const_cast<xine_t*>(xine)) +{ + m_view = new XineConfigBase(); + m_view->xineLogo->setPixmap( QPixmap( locate( "data", "amarok/images/xine_logo.png" ) ) ); + //sound output combo box + m_view->deviceComboBox->insertItem(i18n("Autodetect")); + const char* const* drivers = xine_list_audio_output_plugins(m_xine); + for(int i =0; drivers[i]; ++i) + { + if(qstrcmp(drivers[i],"none") != 0) //returns 0 if equal + m_view->deviceComboBox->insertItem(drivers[i]); + } + + connect( m_view->deviceComboBox, SIGNAL( activated( int ) ), SIGNAL( viewChanged() ) ); + m_entries.setAutoDelete(true); + m_view->deviceComboBox->setCurrentItem( (XineCfg::outputPlugin() == "auto" ) ? "Autodetect" : XineCfg::outputPlugin() ); + init(); + showHidePluginConfigs(); +} + + +XineConfigDialog::~XineConfigDialog() +{ + XineCfg::writeConfig(); + delete m_view; +} + + +void +XineConfigDialog::init() +{ + #define add(X) m_entries.append(X) + add(new XineStrEntry(m_view->hostLineEdit, "media.network.http_proxy_host", m_xine, this)); + add(new XineIntEntry(m_view->portIntBox,"media.network.http_proxy_port", m_xine, this)); + add(new XineStrEntry(m_view->userLineEdit, "media.network.http_proxy_user", m_xine, this)); + add(new XineStrEntry(m_view->passLineEdit, "media.network.http_proxy_password", m_xine, this)); + //alsaGroupBox + add(new XineStrEntry(m_view->monoLineEdit, "audio.device.alsa_default_device", m_xine,this)); + add(new XineStrEntry(m_view->stereoLineEdit, "audio.device.alsa_front_device", m_xine,this)); + add(new XineStrEntry(m_view->chan4LineEdit, "audio.device.alsa_surround40_device", m_xine, this)); + add(new XineStrEntry(m_view->chan5LineEdit, "audio.device.alsa_surround51_device", m_xine, this)); + //ossGroupBox + add(new XineEnumEntry(m_view->ossDeviceComboBox, "audio.device.oss_device_name", m_xine,this)); + add(new XineEnumEntry(m_view->speakerComboBox, "audio.output.speaker_arrangement", m_xine, this)); + // audiocdGroupBox + add(new XineStrEntry(m_view->audiocd_device, "media.audio_cd.device", m_xine, this)); + add(new XineStrEntry(m_view->cddb_server, "media.audio_cd.cddb_server", m_xine, this)); + add(new XineIntEntry(m_view->cddb_port, "media.audio_cd.cddb_port", m_xine, this)); + add(new XineStrEntry(m_view->cddb_cache_dir, "media.audio_cd.cddb_cachedir", m_xine, this)); + #undef add +} + + +void +XineConfigDialog::showHidePluginConfigs() const +{ + if(m_view->deviceComboBox->currentText() == "alsa") + { + m_view->alsaGroupBox->show(); + m_view->ossGroupBox->hide(); + if(XineCfg::outputPlugin() == "alsa") + m_view->alsaGroupBox->setEnabled(true); + else + m_view->alsaGroupBox->setEnabled(false); + } + else if(m_view->deviceComboBox->currentText() == "oss") + { + m_view->alsaGroupBox->hide(); + m_view->ossGroupBox->show(); + if(XineCfg::outputPlugin() == "oss") + m_view->ossGroupBox->setEnabled(true); + else + m_view->ossGroupBox->setEnabled(false); + } + else + { + m_view->alsaGroupBox->hide(); + m_view->ossGroupBox->hide(); + m_view->alsaGroupBox->setEnabled(false); + m_view->ossGroupBox->setEnabled(false); + } +} + + +bool +XineConfigDialog::hasChanged() const +{ + showHidePluginConfigs(); + if(XineCfg::outputPlugin() != ((m_view->deviceComboBox->currentItem() == 0) ? "auto" : m_view->deviceComboBox->currentText())) + return true; + + QPtrListIterator<XineGeneralEntry> it( m_entries ); + XineGeneralEntry* entry; + while( (entry = it.current()) != 0 ) + { + ++it; + if(entry->hasChanged()) + return true; + } + return false; +} + + +bool +XineConfigDialog::isDefault() const +{ + return false; +} + + +void +XineConfigDialog::reset(xine_t* xine) //SLOT +{ + debug() << &m_xine << " " << &xine << endl; + m_entries.clear(); + m_xine = xine; + debug() << "m_entries now empty " << m_entries.isEmpty() << endl; + init(); +} + + +void +XineConfigDialog::save()//SLOT +{ + if(hasChanged()) + { + //its not Autodetect really, its just auto + XineCfg::setOutputPlugin((m_view->deviceComboBox->currentItem() == 0) + ? "auto" : m_view->deviceComboBox->currentText()); + XineGeneralEntry* entry; + for(entry = m_entries.first(); entry; entry=m_entries.next()) + { + if(entry->hasChanged()) + entry->save(); + } + emit settingsSaved(); + } +} + +#include "xine-config.moc" diff --git a/amarok/src/engine/xine/xine-config.h b/amarok/src/engine/xine/xine-config.h new file mode 100644 index 00000000..789b4942 --- /dev/null +++ b/amarok/src/engine/xine/xine-config.h @@ -0,0 +1,131 @@ +//Copyright: (C) 2004 Max Howell, <max.howell@methylblue.com> +//Copyright: (C) 2003-2004 J. Kofler, <kaffeine@gmx.net> +//Copyright: (C) 2005 Ian Monroe +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#ifndef XINECONFIG_H +#define XINECONFIG_H + +#include "plugin/pluginconfig.h" +#include "xineconfigbase.h" + +#include <xine.h> + +class XineConfigDialog; +class KLineEdit; + +class XineGeneralEntry : public QObject +{ + Q_OBJECT + + public: + virtual void save() = 0; + bool hasChanged() const { return m_valueChanged; }; + + signals: + void viewChanged(); + + protected: + XineGeneralEntry(const QString& key, xine_t *xine, XineConfigDialog* xcf); + void entryChanged(); + + bool m_valueChanged; + QString m_key; + xine_t *m_xine; +}; + + +class XineStrFunctor +{ + public: + void operator()( xine_cfg_entry_t* ent, const QString& val ); +}; + + +class XineIntFunctor +{ + public: + void operator()( xine_cfg_entry_t* ent, int val ); +}; + + +template<class T, class Functor> +void saveXineEntry(Functor& storeEntry, T val, const QString& key, xine_t *xine); + + +class XineStrEntry : public XineGeneralEntry +{ + Q_OBJECT + + public: + XineStrEntry(QLineEdit* input, const QCString & key, xine_t *m_xine, XineConfigDialog* xcf); + void save(); + + private slots: + void entryChanged(const QString& newEntry); + + private: + QString m_val; +}; + + +class XineIntEntry : public XineGeneralEntry +{ + Q_OBJECT + + public: + XineIntEntry(KIntSpinBox* input, const QCString & key, xine_t *xine, XineConfigDialog* xcf); + XineIntEntry(const QString& key, xine_t *xine, XineConfigDialog* xcf); + void save(); + + protected slots: + void entryChanged(int newEntry); + + protected: + int m_val; +}; + + +class XineEnumEntry : public XineIntEntry +{ + Q_OBJECT +public: + XineEnumEntry(QComboBox* input, const QCString & key, xine_t *xine, XineConfigDialog* xcf); +}; + + +class XineConfigDialog : public Amarok::PluginConfig +{ + Q_OBJECT + + public: + XineConfigDialog( const xine_t* const xine); + ~XineConfigDialog(); + QWidget* view() { return m_view; } + /** Return true if any of the view settings are different to the currently saved state */ + bool hasChanged() const; + /** Return true if all view settings are in their default states */ + bool isDefault() const; + + public slots: + /** Save view state using, eg KConfig */ + void save(); + void reset(xine_t *xine); + + private: + /** All data structures with m_xine initiated **/ + void init(); + void showHidePluginConfigs() const; + xine_t *m_xine; + QPtrList<XineGeneralEntry> m_entries; + XineConfigBase* m_view; +}; + +#endif diff --git a/amarok/src/engine/xine/xine-engine.cpp b/amarok/src/engine/xine/xine-engine.cpp new file mode 100644 index 00000000..05412aca --- /dev/null +++ b/amarok/src/engine/xine/xine-engine.cpp @@ -0,0 +1,1342 @@ +/*************************************************************************** + * Copyright (C) 2005 Christophe Thommeret <hftom@free.fr> * + * (C) 2005 Ian Monroe <ian@monroe.nu> * + * (C) 2005,6 Mark Kretschmann <markey@web.de> * + * (C) 2004,5 Max Howell <max.howell@methylblue.com> * + * (C) 2003,4 J. Kofler <kaffeine@gmx.net> * + * * + * 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. * + * * + ***************************************************************************/ + +#define DEBUG_PREFIX "xine-engine" + +#include "xine-config.h" +#include "xinecfg.h" +#include "xine-engine.h" +#include "amarok.h" +#include "amarokconfig.h" +//these files are from libamarok +#include "playlist.h" +#include "enginecontroller.h" + +AMAROK_EXPORT_PLUGIN( XineEngine ) + +#include <climits> +#include <cstdlib> +#include <cmath> +#include "debug.h" + +#include <klocale.h> +#include <kmessagebox.h> +#include <kstandarddirs.h> + +#include <qapplication.h> +#include <qdir.h> + +extern "C" +{ + #include <unistd.h> + #include "xine-scope.h" +} + +#ifndef LLONG_MAX +#define LLONG_MAX 9223372036854775807LL +#endif + + +//define this to use xine in a more standard way +//#define XINE_SAFE_MODE + + +///some logging static globals +namespace Log +{ + static uint bufferCount = 0; + static uint scopeCallCount = 1; //prevent divideByZero + static uint noSuitableBuffer = 0; +} + +///returns the configuration we will use. there is no KInstance, so using this hacked up method. +//static inline QCString configPath() { return QFile::encodeName(KStandardDirs().localkdedir() + KStandardDirs::kde_default("data") + "amarok/xine-config"); } +static inline QCString configPath() { return QFile::encodeName(locate( "data", "amarok/") + "xine-config" ); } +static Fader *s_fader = 0; +static OutFader *s_outfader = 0; + + +XineEngine::XineEngine() + : EngineBase() + , m_xine( 0 ) + , m_stream( 0 ) + , m_audioPort( 0 ) + , m_eventQueue( 0 ) + , m_post( 0 ) + , m_preamp( 1.0 ) + , m_stopFader( false ) + , m_fadeOutRunning ( false ) + , m_equalizerEnabled( false ) +{ + addPluginProperty( "HasConfigure", "true" ); + addPluginProperty( "HasEqualizer", "true" ); + #ifndef __NetBSD__ // NetBSD does not offer audio mixing + addPluginProperty( "HasCrossfade", "true" ); + #endif + addPluginProperty("HasCDDA", "true"); // new property + debug() << "hello" << endl; + +} + +XineEngine::~XineEngine() +{ + // Wait until the fader thread is done + if( s_fader ) { + m_stopFader = true; + s_fader->resume(); // safety call if the engine is in the pause state + s_fader->wait(); + } + + delete s_fader; + delete s_outfader; + + if( AmarokConfig::fadeoutOnExit() ) { + bool terminateFader = false; + fadeOut( AmarokConfig::fadeoutLength(), &terminateFader, true ); // true == exiting + } + + if( m_xine ) xine_config_save( m_xine, configPath() ); + + if( m_stream ) xine_close( m_stream ); + if( m_eventQueue ) xine_event_dispose_queue( m_eventQueue ); + if( m_stream ) xine_dispose( m_stream ); + if( m_audioPort ) xine_close_audio_driver( m_xine, m_audioPort ); + if( m_post ) xine_post_dispose( m_xine, m_post ); + if( m_xine ) xine_exit( m_xine ); + + debug() << "xine closed\n"; + + debug() << "Scope statistics:\n" + << " Average list size: " << Log::bufferCount / Log::scopeCallCount << endl + << " Buffer failure: " << double(Log::noSuitableBuffer*100) / Log::scopeCallCount << "%\n"; +} + +bool +XineEngine::init() +{ + DEBUG_BLOCK + + debug() << "'Bringing joy to small mexican gerbils, a few weeks at a time.'\n"; + + m_xine = xine_new(); + + if( !m_xine ) { + KMessageBox::error( 0, i18n("Amarok could not initialize xine.") ); + return false; + } + + #ifdef XINE_SAFE_MODE + xine_engine_set_param( m_xine, XINE_ENGINE_PARAM_VERBOSITY, 99 ); + #endif + + xine_config_load( m_xine, configPath() ); + debug() << "w00t" << configPath() << endl; + + xine_init( m_xine ); + + makeNewStream(); + + #ifndef XINE_SAFE_MODE + startTimer( 200 ); //prunes the scope + #endif + + return true; +} + +bool +XineEngine::makeNewStream() +{ + m_currentAudioPlugin = XineCfg::outputPlugin(); + + m_audioPort = xine_open_audio_driver( m_xine, XineCfg::outputPlugin().local8Bit(), NULL ); + if( !m_audioPort ) { + //TODO make engine method that is the same but parents the dialog for us + KMessageBox::error( 0, i18n("xine was unable to initialize any audio drivers.") ); + return false; + } + + m_stream = xine_stream_new( m_xine, m_audioPort, NULL ); + if( !m_stream ) { + xine_close_audio_driver( m_xine, m_audioPort ); + m_audioPort = NULL; + KMessageBox::error( 0, i18n("Amarok could not create a new xine stream.") ); + return false; + } + + if( m_eventQueue ) + xine_event_dispose_queue( m_eventQueue ); + + xine_event_create_listener_thread( + m_eventQueue = xine_event_new_queue( m_stream ), + &XineEngine::XineEventListener, + (void*)this ); + + #ifndef XINE_SAFE_MODE + //implemented in xine-scope.h + m_post = scope_plugin_new( m_xine, m_audioPort ); + + xine_set_param( m_stream, XINE_PARAM_METRONOM_PREBUFFER, 6000 ); + xine_set_param( m_stream, XINE_PARAM_IGNORE_VIDEO, 1 ); + #endif +#ifdef XINE_PARAM_EARLY_FINISHED_EVENT + if ( xine_check_version(1,1,1) && !(m_xfadeLength > 0) ) { + // enable gapless playback + debug() << "gapless playback enabled." << endl; + //xine_set_param(m_stream, XINE_PARAM_EARLY_FINISHED_EVENT, 1 ); + } +#endif + return true; +} + +// Makes sure an audio port and a stream exist. +bool +XineEngine::ensureStream() +{ + if( !m_stream ) + return makeNewStream(); + + return true; +} + +bool +XineEngine::load( const KURL &url, bool isStream ) +{ + DEBUG_BLOCK + + if( !ensureStream() ) + return false; + + Engine::Base::load( url, isStream ); + + if( s_outfader ) { + s_outfader->finish(); + delete s_outfader; + } + + if( m_xfadeLength > 0 && xine_get_status( m_stream ) == XINE_STATUS_PLAY && + url.isLocalFile() && + xine_get_param( m_stream, XINE_PARAM_SPEED ) != XINE_SPEED_PAUSE && + ( m_xfadeNextTrack || //set by engine controller when switching tracks automatically + (uint) AmarokConfig::crossfadeType() == 0 || //crossfade always + (uint) AmarokConfig::crossfadeType() == 2 ) ) //crossfade when switching tracks manually + { + m_xfadeNextTrack = false; + // Stop a probably running fader + if( s_fader ) { + m_stopFader = true; + s_fader->finish(); // makes the fader stop abruptly + delete s_fader; + } + s_fader = new Fader( this, m_xfadeLength ); + setEqualizerParameters( m_intPreamp, m_equalizerGains ); + } + + // for users who stubbonly refuse to use DMIX or buy a good soundcard + // why doesn't xine do this? I cannot say. + xine_close( m_stream ); + + debug() << "Before xine_open() *****" << endl; + + if( xine_open( m_stream, QFile::encodeName( url.url() ) ) ) + { + debug() << "After xine_open() *****" << endl; + + #ifndef XINE_SAFE_MODE + //we must ensure the scope is pruned of old buffers + timerEvent( 0 ); + + xine_post_out_t *source = xine_get_audio_source( m_stream ); + xine_post_in_t *target = (xine_post_in_t*)xine_post_input( m_post, const_cast<char*>("audio in") ); + xine_post_wire( source, target ); + #endif + + playlistChanged(); + + return true; + } + else + { + #ifdef XINE_PARAM_GAPLESS_SWITCH + if ( xine_check_version(1,1,1) && !(m_xfadeLength > 0) ) + xine_set_param( m_stream, XINE_PARAM_GAPLESS_SWITCH, 0); + #endif + } + + // FAILURE to load! + //s_fader will delete itself + determineAndShowErrorMessage(); + + return false; +} + +bool +XineEngine::play( uint offset ) +{ + DEBUG_BLOCK + + if( !ensureStream() ) + return false; + + const bool has_audio = xine_get_stream_info( m_stream, XINE_STREAM_INFO_HAS_AUDIO ); + const bool audio_handled = xine_get_stream_info( m_stream, XINE_STREAM_INFO_AUDIO_HANDLED ); + + if (has_audio && audio_handled && xine_play( m_stream, 0, offset )) + { + if( s_fader ) + s_fader->start( QThread::LowestPriority ); + + emit stateChanged( Engine::Playing ); + + return true; + } + + //we need to stop the track that is prepped for crossfade + delete s_fader; + + emit stateChanged( Engine::Empty ); + + determineAndShowErrorMessage(); + + xine_close( m_stream ); + + return false; +} + +#include "statusbar/statusbar.h" + +void +XineEngine::determineAndShowErrorMessage() +{ + DEBUG_BLOCK + + QString body; + + debug() << "xine_get_error()\n"; + switch (xine_get_error( m_stream )) { + case XINE_ERROR_NO_INPUT_PLUGIN: + body = i18n("No suitable input plugin. This often means that the url's protocol is not supported. Network failures are other possible causes."); + break; + + case XINE_ERROR_NO_DEMUX_PLUGIN: + body = i18n("No suitable demux plugin. This often means that the file format is not supported."); + break; + + case XINE_ERROR_DEMUX_FAILED: + body = i18n("Demuxing failed."); + break; + + case XINE_ERROR_INPUT_FAILED: + body = i18n("Could not open file."); + break; + + case XINE_ERROR_MALFORMED_MRL: + body = i18n("The location is malformed."); + break; + + case XINE_ERROR_NONE: + // xine is thick. xine doesn't think there is an error + // but there may be! We check for other errors below. + + default: + if (!xine_get_stream_info( m_stream, XINE_STREAM_INFO_AUDIO_HANDLED )) + { + // xine can read the plugin but it didn't find any codec + // THUS xine=daft for telling us it could handle the format in canDecode! + body = i18n("There is no available decoder."); + QString const ext = Amarok::extension( m_url.url() ).lower(); + if (ext == "mp3" && EngineController::installDistroCodec( "xine-engine" )) + return; + } + else if (!xine_get_stream_info( m_stream, XINE_STREAM_INFO_HAS_AUDIO )) + body = i18n("There is no audio channel!"); + break; + } + + Amarok::StatusBar::instance()->longMessage( + "<b>" + i18n("Error Loading Media") + "</b><p>" + body + "<p>" + m_url.prettyURL(), + KDE::StatusBar::Error ); +} + +void +XineEngine::stop() +{ + if( s_fader && s_fader->running() ) + s_fader->resume(); // safety call if the engine is in the pause state + + if ( !m_stream ) + return; + + if( AmarokConfig::fadeout() && !m_fadeOutRunning || state() == Engine::Paused ) + { + s_outfader = new OutFader( this, AmarokConfig::fadeoutLength() ); + s_outfader->start(); + ::usleep( 100 ); //to be sure engine state won't be changed before it is checked in fadeOut() + m_url = KURL(); //to ensure we return Empty from state() + + std::fill( m_scope.begin(), m_scope.end(), 0 ); + } + else if( !m_fadeOutRunning ) + { + xine_stop( m_stream ); + xine_close( m_stream ); + xine_set_param( m_stream, XINE_PARAM_AUDIO_CLOSE_DEVICE, 1); + } + + emit stateChanged( Engine::Empty ); +} + +void +XineEngine::pause() +{ + if ( !m_stream ) + return; + + if( xine_get_param( m_stream, XINE_PARAM_SPEED ) != XINE_SPEED_PAUSE ) + { + if( s_fader && s_fader->running() ) + s_fader->pause(); + + xine_set_param( m_stream, XINE_PARAM_SPEED, XINE_SPEED_PAUSE ); + xine_set_param( m_stream, XINE_PARAM_AUDIO_CLOSE_DEVICE, 1); + emit stateChanged( Engine::Paused ); + + } +} + +void +XineEngine::unpause() +{ + if ( !m_stream ) + return; + + if( xine_get_param( m_stream, XINE_PARAM_SPEED ) == XINE_SPEED_PAUSE ) + { + if( s_fader && s_fader->running() ) + s_fader->resume(); + + xine_set_param( m_stream, XINE_PARAM_SPEED, XINE_SPEED_NORMAL ); + emit stateChanged( Engine::Playing ); + } +} + +Engine::State +XineEngine::state() const +{ + if ( !m_stream || m_fadeOutRunning ) + return Engine::Empty; + + switch( xine_get_status( m_stream ) ) + { + case XINE_STATUS_PLAY: return xine_get_param( m_stream, XINE_PARAM_SPEED ) != XINE_SPEED_PAUSE ? Engine::Playing : Engine::Paused; + case XINE_STATUS_IDLE: return Engine::Empty; + case XINE_STATUS_STOP: + default: return m_url.isEmpty() ? Engine::Empty : Engine::Idle; + } +} + +uint +XineEngine::position() const +{ + if ( state() == Engine::Empty ) + return 0; + + int pos; + int time = 0; + int length; + + // Workaround for problems when you seek too quickly, see BUG 99808 + int tmp = 0, i = 0; + while( ++i < 4 ) + { + xine_get_pos_length( m_stream, &pos, &time, &length ); + if( time > tmp ) break; + usleep( 100000 ); + } + + // Here we check for new metadata periodically, because xine does not emit an event + // in all cases (e.g. with ogg streams). See BUG 122505 + if ( state() != Engine::Idle && state() != Engine::Empty ) + { + const Engine::SimpleMetaBundle bundle = fetchMetaData(); + if( bundle.title != m_currentBundle.title || bundle.artist != m_currentBundle.artist ) { + debug() << "Metadata received." << endl; + m_currentBundle = bundle; + + XineEngine* p = const_cast<XineEngine*>( this ); + p->emit metaData( bundle ); + } + } + + return time; +} + +uint +XineEngine::length() const +{ + if ( !m_stream ) + return 0; + + // xine often delivers nonsense values for VBR files and such, so we only + // use the length for remote files + + if( m_url.isLocalFile() ) + return 0; + + else { + int pos; + int time; + int length = 0; + + xine_get_pos_length( m_stream, &pos, &time, &length ); + if( length < 0 ) + length=0; + + return length; + } +} + +void +XineEngine::seek( uint ms ) +{ + if( !ensureStream() ) + return; + + if( xine_get_param( m_stream, XINE_PARAM_SPEED ) == XINE_SPEED_PAUSE ) { + // FIXME this is a xine API issue really, they need to add a seek function + xine_play( m_stream, 0, (int)ms ); + xine_set_param( m_stream, XINE_PARAM_SPEED, XINE_SPEED_PAUSE ); + } + else + xine_play( m_stream, 0, (int)ms ); +} + +void +XineEngine::setVolumeSW( uint vol ) +{ + if ( !m_stream ) + return; + if( !s_fader ) + xine_set_param( m_stream, XINE_PARAM_AUDIO_AMP_LEVEL, static_cast<uint>( vol * m_preamp ) ); +} + +void +XineEngine::fadeOut( uint fadeLength, bool* terminate, bool exiting ) +{ + if( m_fadeOutRunning ) //Let us not start another fadeout... + return; + + m_fadeOutRunning = !m_fadeOutRunning; + const bool isPlaying = m_stream && ( xine_get_status( m_stream ) == XINE_STATUS_PLAY ); + const float originalVol = Engine::Base::makeVolumeLogarithmic( m_volume ) * m_preamp; + + // On shutdown, limit fadeout to 3 secs max, so that we don't risk getting killed + const int length = exiting ? QMIN( fadeLength, 3000 ) : fadeLength; + + if( length > 0 && isPlaying ) + { + // fader-class doesn't work in this spot as is, so some parts need to be copied here... (ugly) + uint stepsCount = length < 1000 ? length / 10 : 100; + uint stepSizeUs = (int)( 1000.0 * (float)length / (float)stepsCount ); + + ::usleep( stepSizeUs ); + QTime t; + t.start(); + float mix = 0.0; + while ( mix < 1.0 ) + { + if( *terminate ) break; + + ::usleep( stepSizeUs ); + float vol = Engine::Base::makeVolumeLogarithmic( m_volume ) * m_preamp; + float mix = (float)t.elapsed() / (float)length; + if ( mix > 1.0 ) + { + break; + } + if ( m_stream ) + { + float v = 4.0 * (1.0 - mix) / 3.0; + xine_set_param( m_stream, XINE_PARAM_AUDIO_AMP_LEVEL, (uint)( v < 1.0 ? vol * v : vol ) ); + } + } + } + if( m_fadeOutRunning && m_stream ) + xine_set_param( m_stream, XINE_PARAM_AUDIO_AMP_LEVEL, (uint) originalVol ); + m_fadeOutRunning = !m_fadeOutRunning; +} + +void +XineEngine::setEqualizerEnabled( bool enable ) +{ + if ( !m_stream ) + return; + + m_equalizerEnabled = enable; + + if( !enable ) { + QValueList<int> gains; + for( uint x = 0; x < 10; x++ ) + gains << -101; // sets eq gains to zero. + + setEqualizerParameters( 0, gains ); + } +} + +/* + sets the eq params for xine engine - have to rescale eq params to fitting range (adapted from kaffeine and xfmedia) + + preamp + pre: (-100..100) + post: (0.1..1.9) - this is not really a preamp but we use the xine preamp parameter for our normal volume. so we make a postamp. + + gains + pre: (-100..100) + post: (1..200) - (1 = down, 100 = middle, 200 = up, 0 = off) + */ +void +XineEngine::setEqualizerParameters( int preamp, const QValueList<int> &gains ) +{ + if ( !m_stream ) + return; + + m_equalizerGains = gains; + m_intPreamp = preamp; + QValueList<int>::ConstIterator it = gains.begin(); + + xine_set_param( m_stream, XINE_PARAM_EQ_30HZ, int( (*it )*0.995 + 100 ) ); + xine_set_param( m_stream, XINE_PARAM_EQ_60HZ, int( (*++it)*0.995 + 100 ) ); + xine_set_param( m_stream, XINE_PARAM_EQ_125HZ, int( (*++it)*0.995 + 100 ) ); + xine_set_param( m_stream, XINE_PARAM_EQ_250HZ, int( (*++it)*0.995 + 100 ) ); + xine_set_param( m_stream, XINE_PARAM_EQ_500HZ, int( (*++it)*0.995 + 100 ) ); + xine_set_param( m_stream, XINE_PARAM_EQ_1000HZ, int( (*++it)*0.995 + 100 ) ); + xine_set_param( m_stream, XINE_PARAM_EQ_2000HZ, int( (*++it)*0.995 + 100 ) ); + xine_set_param( m_stream, XINE_PARAM_EQ_4000HZ, int( (*++it)*0.995 + 100 ) ); + xine_set_param( m_stream, XINE_PARAM_EQ_8000HZ, int( (*++it)*0.995 + 100 ) ); + xine_set_param( m_stream, XINE_PARAM_EQ_16000HZ, int( (*++it)*0.995 + 100 ) ); + + m_preamp = ( preamp - 0.1 * preamp + 100 ) / 100.0; + setVolume( m_volume ); +} + +bool +XineEngine::canDecode( const KURL &url ) const +{ + static QStringList list; + if(list.isEmpty()) + { + char* exts = xine_get_file_extensions( m_xine ); + list = QStringList::split( ' ', exts ); + free( exts ); exts = NULL; + //images + list.remove("png"); + list.remove("jpg"); + list.remove("jpeg"); + list.remove("gif"); + list.remove("ilbm"); + list.remove("iff"); + //subtitles + list.remove("asc"); + list.remove("txt"); + list.remove("sub"); + list.remove("srt"); + list.remove("smi"); + list.remove("ssa"); +//HACK we also check for m4a because xine plays them but +//for some reason doesn't return the extension + if(!list.contains("m4a")) + list << "m4a"; + } + + if (url.protocol() == "cdda") + // play audio CDs pls + return true; + + QString path = url.path(); + + // partial downloads from Konqi and other browsers + // tend to have a .part extension + if (path.endsWith( ".part" )) + path = path.left( path.length() - 5 ); + + const QString ext = path.mid( path.findRev( '.' ) + 1 ).lower(); + + return list.contains( ext ); +} + +const Engine::Scope& +XineEngine::scope() +{ + if( !m_post || !m_stream || xine_get_status( m_stream ) != XINE_STATUS_PLAY ) + return m_scope; + + MyNode* const myList = scope_plugin_list( m_post ); + metronom_t* const myMetronom = scope_plugin_metronom( m_post ); + const int myChannels = scope_plugin_channels( m_post ); + int scopeidx = 0; + + if (myChannels > 2) + return m_scope; + + //prune the buffer list and update m_currentVpts + timerEvent( 0 ); + + for( int n, frame = 0; frame < 512; ) + { + MyNode *best_node = 0; + + for( MyNode *node = myList->next; node != myList; node = node->next, Log::bufferCount++ ) + if( node->vpts <= m_currentVpts && (!best_node || node->vpts > best_node->vpts) ) + best_node = node; + + if( !best_node || best_node->vpts_end < m_currentVpts ) { + Log::noSuitableBuffer++; break; } + + int64_t + diff = m_currentVpts; + diff -= best_node->vpts; + diff *= 1<<16; + diff /= myMetronom->pts_per_smpls; + + const int16_t* + data16 = best_node->mem; + data16 += diff; + + diff += diff % myChannels; //important correction to ensure we don't overflow the buffer + diff /= myChannels; //use units of frames, not samples + + //calculate the number of available samples in this buffer + n = best_node->num_frames; + n -= diff; + n += frame; //clipping for # of frames we need + + if( n > 512 ) + n = 512; //we don't want more than 512 frames + + for( int a, c; frame < n; ++frame, data16 += myChannels ) { + for( a = c = 0; c < myChannels; ++c ) + { + // we now give interleaved pcm to the scope + m_scope[scopeidx++] = data16[c]; + if (myChannels == 1) // duplicate mono samples + m_scope[scopeidx++] = data16[c]; + } + } + + m_currentVpts = best_node->vpts_end; + m_currentVpts++; //FIXME needs to be done for some reason, or you get situations where it uses same buffer again and again + } + + Log::scopeCallCount++; + + return m_scope; +} + +void +XineEngine::timerEvent( QTimerEvent* ) +{ + if ( !m_stream ) + return; + + //here we prune the buffer list regularly + + MyNode *myList = scope_plugin_list( m_post ); + + if ( ! myList ) return; + + //we operate on a subset of the list for thread-safety + MyNode * const first_node = myList->next; + MyNode const * const list_end = myList; + + m_currentVpts = (xine_get_status( m_stream ) == XINE_STATUS_PLAY) + ? xine_get_current_vpts( m_stream ) + : LLONG_MAX; //if state is not playing OR paused, empty the list + //: std::numeric_limits<int64_t>::max(); //TODO don't support crappy gcc 2.95 + + for( MyNode *prev = first_node, *node = first_node->next; node != list_end; node = node->next ) + { + //we never delete first_node + //this maintains thread-safety + if( node->vpts_end < m_currentVpts ) { + prev->next = node->next; + + free( node->mem ); + free( node ); + + node = prev; + } + + prev = node; + } +} + +Amarok::PluginConfig* +XineEngine::configure() const +{ + XineConfigDialog* xcf = new XineConfigDialog( m_xine ); + connect(xcf, SIGNAL( settingsSaved() ), this, SLOT( configChanged() )); + connect(this, SIGNAL( resetConfig(xine_t*) ), xcf, SLOT( reset(xine_t*) )); + return xcf; +} + +void +XineEngine::customEvent( QCustomEvent *e ) +{ + #define message static_cast<QString*>(e->data()) + + switch( e->type() ) + { + case 3000: //XINE_EVENT_UI_PLAYBACK_FINISHED + emit trackEnded(); + break; + + case 3001: + emit infoMessage( (*message).arg( m_url.prettyURL() ) ); + delete message; + break; + + case 3002: + emit statusText( *message ); + delete message; + break; + + case 3003: { //meta info has changed + debug() << "Metadata received." << endl; + const Engine::SimpleMetaBundle bundle = fetchMetaData(); + m_currentBundle = bundle; + emit metaData( bundle ); + } break; + + case 3004: + emit statusText( i18n("Redirecting to: ").arg( *message ) ); + load( KURL( *message ), false ); + play(); + delete message; + break; + case 3005: + emit lastFmTrackChange(); + break; + default: + ; + } + + #undef message +} +//SLOT +void XineEngine::configChanged() +{ + //reset xine to load new audio plugin + if( m_currentAudioPlugin != XineCfg::outputPlugin() ) + { + stop(); + xine_config_save( m_xine, configPath() ); + if( m_stream ) xine_close( m_stream ); + if( m_eventQueue ) xine_event_dispose_queue( m_eventQueue ); + m_eventQueue = NULL; + if( m_stream ) xine_dispose( m_stream ); + m_stream = NULL; + if( m_audioPort ) xine_close_audio_driver( m_xine, m_audioPort ); + m_audioPort = NULL; + if( m_post ) xine_post_dispose( m_xine, m_post ); + m_post = NULL; + if( m_xine ) xine_exit( m_xine ); + m_xine = NULL; + init(); + setEqualizerEnabled( m_equalizerEnabled ); + if( m_equalizerEnabled ) + setEqualizerParameters( m_intPreamp, m_equalizerGains ); + emit resetConfig(m_xine); + } +} + +//SLOT +void +XineEngine::playlistChanged() +{ + #ifdef XINE_PARAM_EARLY_FINISHED_EVENT + #ifdef XINE_PARAM_GAPLESS_SWITCH + if ( xine_check_version(1,1,1) && !(m_xfadeLength > 0) + && m_url.isLocalFile() && Playlist::instance()->isTrackAfter() ) + { + xine_set_param(m_stream, XINE_PARAM_EARLY_FINISHED_EVENT, 1 ); + debug() << "XINE_PARAM_EARLY_FINISHED_EVENT enabled" << endl; + } + else + { + //we don't want an early finish event if there is no track after the current one + xine_set_param(m_stream, XINE_PARAM_EARLY_FINISHED_EVENT, 0 ); + debug() << "XINE_PARAM_EARLY_FINISHED_EVENT disabled" << endl; + } + #endif + #endif +} + +static time_t last_error_time = 0; // hysteresis on xine errors +static int last_error = XINE_MSG_NO_ERROR; + +void +XineEngine::XineEventListener( void *p, const xine_event_t* xineEvent ) +{ + time_t current; + + if( !p ) return; + + #define xe static_cast<XineEngine*>(p) + + switch( xineEvent->type ) + { + case XINE_EVENT_UI_SET_TITLE: + + debug() << "XINE_EVENT_UI_SET_TITLE\n"; + + QApplication::postEvent( xe, new QCustomEvent( 3003 ) ); + + break; + + case XINE_EVENT_UI_PLAYBACK_FINISHED: + debug() << "XINE_EVENT_UI_PLAYBACK_FINISHED\n"; + + #ifdef XINE_PARAM_GAPLESS_SWITCH + if ( xine_check_version(1,1,1) && xe->m_url.isLocalFile() //Remote media break with gapless + //don't prepare for a track that isn't coming + && Playlist::instance() + && Playlist::instance()->isTrackAfter() + && !AmarokConfig::crossfade() ) + xine_set_param( xe->m_stream, XINE_PARAM_GAPLESS_SWITCH, 1); + #endif + //emit signal from GUI thread + QApplication::postEvent( xe, new QCustomEvent(3000) ); + break; + + case XINE_EVENT_PROGRESS: { + xine_progress_data_t* pd = (xine_progress_data_t*)xineEvent->data; + + QString + msg = "%1 %2%"; + msg = msg.arg( QString::fromUtf8( pd->description ) ) + .arg( KGlobal::locale()->formatNumber( pd->percent, 0 ) ); + + QCustomEvent *e = new QCustomEvent( 3002 ); + e->setData( new QString( msg ) ); + + QApplication::postEvent( xe, e ); + + } break; + + case XINE_EVENT_MRL_REFERENCE: { + /// xine has read the stream and found it actually links to something else + /// so we need to play that instead + + QString message = QString::fromUtf8( static_cast<xine_mrl_reference_data_t*>(xineEvent->data)->mrl ); + QCustomEvent *e = new QCustomEvent( 3004 ); + e->setData( new QString( message ) ); + + QApplication::postEvent( xe, e ); + + } break; + + case XINE_EVENT_UI_MESSAGE: + { + debug() << "message received from xine\n"; + + xine_ui_message_data_t *data = (xine_ui_message_data_t *)xineEvent->data; + QString message; + + switch( data->type ) + { + case XINE_MSG_NO_ERROR: + { + //series of \0 separated strings, terminated with a \0\0 + char str[2000]; + char *p = str; + for( char *msg = data->messages; !(*msg == '\0' && *(msg+1) == '\0'); ++msg, ++p ) + *p = *msg == '\0' ? '\n' : *msg; + *p = '\0'; + + debug() << str << endl; + + break; + } + + case XINE_MSG_ENCRYPTED_SOURCE: + break; + + case XINE_MSG_UNKNOWN_HOST: + message = i18n("The host is unknown for the URL: <i>%1</i>"); goto param; + case XINE_MSG_UNKNOWN_DEVICE: + message = i18n("The device name you specified seems invalid."); goto param; + case XINE_MSG_NETWORK_UNREACHABLE: + message = i18n("The network appears unreachable."); goto param; + case XINE_MSG_AUDIO_OUT_UNAVAILABLE: + message = i18n("Audio output unavailable; the device is busy."); goto param; + case XINE_MSG_CONNECTION_REFUSED: + message = i18n("The connection was refused for the URL: <i>%1</i>"); goto param; + case XINE_MSG_FILE_NOT_FOUND: + message = i18n("xine could not find the URL: <i>%1</i>"); goto param; + case XINE_MSG_PERMISSION_ERROR: + message = i18n("Access was denied for the URL: <i>%1</i>"); goto param; + case XINE_MSG_READ_ERROR: + message = i18n("The source cannot be read for the URL: <i>%1</i>"); goto param; + case XINE_MSG_LIBRARY_LOAD_ERROR: + message = i18n("A problem occurred while loading a library or decoder."); goto param; + + case XINE_MSG_GENERAL_WARNING: + message = i18n("General Warning"); goto explain; + case XINE_MSG_SECURITY: + message = i18n("Security Warning"); goto explain; + default: + message = i18n("Unknown Error"); goto explain; + + + explain: + + // Don't flood the user with error messages + if( (last_error_time + 10) > time( ¤t ) && + data->type == last_error ) + { + last_error_time = current; + return; + } + last_error_time = current; + last_error = data->type; + + if( data->explanation ) + { + message.prepend( "<b>" ); + message += "</b>:<p>"; + message += QString::fromUtf8( (char*)data + data->explanation ); + } + else break; //if no explanation then why bother! + + //FALL THROUGH + + param: + + // Don't flood the user with error messages + if((last_error_time + 10) > time(¤t) && + data->type == last_error) + { + last_error_time = current; + return; + } + last_error_time = current; + last_error = data->type; + + message.prepend( "<p>" ); + message += "<p>"; + + if(data->explanation) + { + message += "xine parameters: <i>"; + message += QString::fromUtf8( (char*)data + data->parameters ); + message += "</i>"; + } + else message += i18n("Sorry, no additional information is available."); + + QApplication::postEvent( xe, new QCustomEvent(QEvent::Type(3001), new QString(message)) ); + } + + } //case + case XINE_EVENT_UI_CHANNELS_CHANGED: //Flameeyes used this for last.fm track changes + QApplication::postEvent( xe, new QCustomEvent(QEvent::Type(3005) ) ); + break; + } //switch + + #undef xe +} + +Engine::SimpleMetaBundle +XineEngine::fetchMetaData() const +{ + Engine::SimpleMetaBundle bundle; + bundle.title = QString::fromUtf8( xine_get_meta_info( m_stream, XINE_META_INFO_TITLE ) ); + bundle.artist = QString::fromUtf8( xine_get_meta_info( m_stream, XINE_META_INFO_ARTIST ) ); + bundle.album = QString::fromUtf8( xine_get_meta_info( m_stream, XINE_META_INFO_ALBUM ) ); + bundle.comment = QString::fromUtf8( xine_get_meta_info( m_stream, XINE_META_INFO_COMMENT ) ); + bundle.genre = QString::fromUtf8( xine_get_meta_info( m_stream, XINE_META_INFO_GENRE ) ); + bundle.bitrate = QString::number( xine_get_stream_info( m_stream, XINE_STREAM_INFO_AUDIO_BITRATE ) / 1000 ); + bundle.samplerate = QString::number( xine_get_stream_info( m_stream, XINE_STREAM_INFO_AUDIO_SAMPLERATE ) ); + bundle.year = QString::fromUtf8( xine_get_meta_info( m_stream, XINE_META_INFO_YEAR ) ); + bundle.tracknr = QString::fromUtf8( xine_get_meta_info( m_stream, XINE_META_INFO_TRACK_NUMBER ) ); + + return bundle; +} + +bool XineEngine::metaDataForUrl(const KURL &url, Engine::SimpleMetaBundle &b) +{ + bool result = false; + xine_stream_t* tmpstream = xine_stream_new(m_xine, NULL, NULL); + if (xine_open(tmpstream, QFile::encodeName(url.url()))) { + QString audioCodec = QString::fromUtf8(xine_get_meta_info(tmpstream, XINE_META_INFO_SYSTEMLAYER)); + + if (audioCodec == "CDDA") { + QString title = QString::fromUtf8( + xine_get_meta_info(tmpstream, XINE_META_INFO_TITLE)); + if ((!title.isNull()) && (!title.isEmpty())) { //no meta info + b.title = title; + b.artist = + QString::fromUtf8( + xine_get_meta_info(tmpstream, XINE_META_INFO_ARTIST)); + b.album = + QString::fromUtf8( + xine_get_meta_info(tmpstream, XINE_META_INFO_ALBUM)); + b.genre = + QString::fromUtf8( + xine_get_meta_info(tmpstream, XINE_META_INFO_GENRE)); + b.year = + QString::fromUtf8( + xine_get_meta_info(tmpstream, XINE_META_INFO_YEAR)); + b.tracknr = + QString::fromUtf8( + xine_get_meta_info(tmpstream, XINE_META_INFO_TRACK_NUMBER)); + if( b.tracknr.isEmpty() ) + b.tracknr = url.filename(); + } else { + b.title = QString(i18n("Track %1")).arg(url.filename()); + b.album = i18n("AudioCD"); + } + } + + if (audioCodec == "CDDA" || audioCodec == "WAV") { + result = true; + int samplerate = xine_get_stream_info( tmpstream, XINE_STREAM_INFO_AUDIO_SAMPLERATE ); + + // xine would provide a XINE_STREAM_INFO_AUDIO_BITRATE, but unfortunately not for CDDA or WAV + // so we calculate the bitrate by our own + int bitsPerSample = xine_get_stream_info( tmpstream, XINE_STREAM_INFO_AUDIO_BITS ); + int nbrChannels = xine_get_stream_info( tmpstream, XINE_STREAM_INFO_AUDIO_CHANNELS ); + int bitrate = (samplerate * bitsPerSample * nbrChannels) / 1000; + + b.bitrate = QString::number(bitrate); + b.samplerate = QString::number(samplerate); + int pos, time, length = 0; + xine_get_pos_length(tmpstream, &pos, &time, &length); + b.length = QString::number(length / 1000); + } + xine_close(tmpstream); + } + xine_dispose(tmpstream); + return result; +} + +bool XineEngine::getAudioCDContents(const QString &device, KURL::List &urls) +{ + char **xine_urls = NULL; + int num; + int i = 0; + + if (!device.isNull()) { + debug() << "xine-engine setting CD Device to: " << device << endl; + xine_cfg_entry_t config; + if (!xine_config_lookup_entry(m_xine, "input.cdda_device", &config)) { + emit statusText(i18n("Failed CD device lookup in xine engine")); + return false; + } + config.str_value = (char *)device.latin1(); + xine_config_update_entry(m_xine, &config); + } + + emit statusText(i18n("Getting AudioCD contents...")); + + xine_urls = xine_get_autoplay_mrls(m_xine, "CD", &num); + + if (xine_urls) { + while (xine_urls[i]) { + urls << KURL(xine_urls[i]); + ++i; + } + } + else emit statusText(i18n("Could not read AudioCD")); + + return true; +} + +bool XineEngine::flushBuffer() +{ + return false; +} + +bool XineEngine::lastFmProxyRequired() +{ + return !( xine_check_version(1,1,9) ); +} + +////////////////////////////////////////////////////////////////////////////// +/// class Fader +////////////////////////////////////////////////////////////////////////////// + +Fader::Fader( XineEngine *engine, uint fadeMs ) + : QObject( engine ) + , QThread() + , m_engine( engine ) + , m_xine( engine->m_xine ) + , m_decrease( engine->m_stream ) + , m_increase( 0 ) + , m_port( engine->m_audioPort ) + , m_post( engine->m_post ) + , m_fadeLength( fadeMs ) + , m_paused( false ) + , m_terminated( false ) +{ + DEBUG_BLOCK + + if( engine->makeNewStream() ) + { + m_increase = engine->m_stream; + + xine_set_param( m_increase, XINE_PARAM_AUDIO_AMP_LEVEL, 0 ); + } + else { + s_fader = 0; + deleteLater(); + } +} + +Fader::~Fader() +{ + DEBUG_BLOCK + + wait(); + + xine_close( m_decrease ); + xine_dispose( m_decrease ); + xine_close_audio_driver( m_xine, m_port ); + if( m_post ) xine_post_dispose( m_xine, m_post ); + + if( !m_engine->m_stopFader ) + m_engine->setVolume( m_engine->volume() ); + + m_engine->m_stopFader = false; + s_fader = 0; +} + +void +Fader::run() +{ + DEBUG_BLOCK + + // do a volume change in 100 steps (or every 10ms) + uint stepsCount = m_fadeLength < 1000 ? m_fadeLength / 10 : 100; + uint stepSizeUs = (int)( 1000.0 * (float)m_fadeLength / (float)stepsCount ); + + float mix = 0.0; + float elapsedUs = 0.0; + while ( mix < 1.0 ) + { + if ( m_terminated ) + break; + // sleep a constant amount of time + QThread::usleep( stepSizeUs ); + + if ( m_paused ) + continue; + + elapsedUs += stepSizeUs; + + // get volume (amarok main * equalizer preamp) + float vol = Engine::Base::makeVolumeLogarithmic( m_engine->m_volume ) * m_engine->m_preamp; + + // compute the mix factor as the percentage of time spent since fade begun + float mix = (elapsedUs / 1000.0) / (float)m_fadeLength; + if ( mix > 1.0 ) + { + if ( m_increase ) + xine_set_param( m_increase, XINE_PARAM_AUDIO_AMP_LEVEL, (uint)vol ); + break; + } + + // change volume of streams (using dj-like cross-fade profile) + if ( m_decrease ) + { + //xine_set_param( m_decrease, XINE_PARAM_AUDIO_AMP_LEVEL, (uint)(vol * (1.0 - mix)) ); // linear + float v = 4.0 * (1.0 - mix) / 3.0; + xine_set_param( m_decrease, XINE_PARAM_AUDIO_AMP_LEVEL, (uint)( v < 1.0 ? vol * v : vol ) ); + } + if ( m_increase ) + { + //xine_set_param( m_increase, XINE_PARAM_AUDIO_AMP_LEVEL, (uint)(vol * mix) ); //linear + float v = 4.0 * mix / 3.0; + xine_set_param( m_increase, XINE_PARAM_AUDIO_AMP_LEVEL, (uint)( v < 1.0 ? vol * v : vol ) ); + } + } + + //stop using cpu! + xine_stop( m_decrease ); + + deleteLater(); +} + +void +Fader::pause() +{ + m_paused = true; +} + +void +Fader::resume() +{ + m_paused = false; +} + +void +Fader::finish() +{ + DEBUG_BLOCK + m_terminated = true; +} + +////////////////////////////////////////////////////////////////////////////// +/// class OutFader +////////////////////////////////////////////////////////////////////////////// + +OutFader::OutFader( XineEngine *engine, uint fadeLength ) + : QObject( engine ) + , QThread() + , m_engine( engine ) + , m_terminated( false ) + , m_fadeLength( fadeLength ) +{ + DEBUG_BLOCK +} + +OutFader::~OutFader() +{ + DEBUG_BLOCK + + wait(); + + s_outfader = 0; +} + +void +OutFader::run() +{ + DEBUG_BLOCK + + m_engine->fadeOut( m_fadeLength, &m_terminated ); + + xine_stop( m_engine->m_stream ); + xine_close( m_engine->m_stream ); + xine_set_param( m_engine->m_stream, XINE_PARAM_AUDIO_CLOSE_DEVICE, 1); + + deleteLater(); +} + +void +OutFader::finish() +{ + DEBUG_BLOCK + m_terminated = true; +} + +#include "xine-engine.moc" diff --git a/amarok/src/engine/xine/xine-engine.h b/amarok/src/engine/xine/xine-engine.h new file mode 100644 index 00000000..fb4d3ecb --- /dev/null +++ b/amarok/src/engine/xine/xine-engine.h @@ -0,0 +1,141 @@ +/*************************************************************************** + * Copyright (C) 2004,5 Max Howell <max.howell@methylblue.com> * + * * + * 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. * + * * + ***************************************************************************/ + +#ifndef XINE_ENGINE_H +#define XINE_ENGINE_H + +#include "enginebase.h" +#include <qthread.h> + +extern "C" +{ + #include <sys/types.h> + #include <xine.h> +} + +class XineConfigDialog; + +class XineEngine : public Engine::Base +{ + Q_OBJECT + + friend class Fader; + friend class OutFader; + + ~XineEngine(); + + virtual bool init(); + virtual bool canDecode( const KURL& ) const; + virtual bool load( const KURL &url, bool stream ); + virtual bool play( uint = 0 ); + virtual void stop(); + virtual void pause(); + virtual void unpause(); + virtual uint position() const; + virtual uint length() const; + virtual void seek( uint ); + + virtual bool metaDataForUrl(const KURL &url, Engine::SimpleMetaBundle &b); + virtual bool getAudioCDContents(const QString &device, KURL::List &urls); + virtual bool flushBuffer(); + + virtual Engine::State state() const; + virtual const Engine::Scope &scope(); + + virtual Amarok::PluginConfig *configure() const; + virtual void setEqualizerEnabled( bool ); + virtual void setEqualizerParameters( int preamp, const QValueList<int>& ); + virtual void setVolumeSW( uint ); + virtual void fadeOut( uint fadeLength, bool* terminate, bool exiting = false ); + + static void XineEventListener( void*, const xine_event_t* ); + virtual void customEvent( QCustomEvent* ); + virtual void timerEvent( QTimerEvent* ); + + virtual void playlistChanged(); + + Engine::SimpleMetaBundle fetchMetaData() const; + + virtual bool lastFmProxyRequired(); + + bool makeNewStream(); + bool ensureStream(); + + void determineAndShowErrorMessage(); //call after failure to load/play + + xine_t *m_xine; + xine_stream_t *m_stream; + xine_audio_port_t *m_audioPort; + xine_event_queue_t *m_eventQueue; + xine_post_t *m_post; + + int64_t m_currentVpts; + float m_preamp; + + bool m_stopFader; + bool m_fadeOutRunning; + + QString m_currentAudioPlugin; //to see if audio plugin has been changed + XineConfigDialog* m_configDialog; + //need to save these for when the audio plugin is changed and xine reloaded + bool m_equalizerEnabled; + int m_intPreamp; + QValueList<int> m_equalizerGains; + + mutable Engine::SimpleMetaBundle m_currentBundle; + +private slots: + void configChanged(); + +public: + XineEngine(); + +signals: + void resetConfig(xine_t *xine); +}; + +class Fader : public QObject, public QThread +{ + XineEngine *m_engine; + xine_t *m_xine; + xine_stream_t *m_decrease; + xine_stream_t *m_increase; + xine_audio_port_t *m_port; + xine_post_t *m_post; + uint m_fadeLength; + bool m_paused; + bool m_terminated; + + virtual void run(); + +public: + Fader( XineEngine *, uint fadeLengthMs ); + ~Fader(); + void pause(); + void resume(); + void finish(); +}; + +class OutFader : public QObject, public QThread +{ + XineEngine *m_engine; + bool m_terminated; + uint m_fadeLength; + + virtual void run(); + +public: + OutFader( XineEngine *, uint fadeLengthMs ); + ~OutFader(); + + void finish(); +}; + +#endif diff --git a/amarok/src/engine/xine/xine-scope.c b/amarok/src/engine/xine/xine-scope.c new file mode 100644 index 00000000..8ff422e9 --- /dev/null +++ b/amarok/src/engine/xine/xine-scope.c @@ -0,0 +1,186 @@ +/* Author: Max Howell <max.howell@methylblue.com>, (C) 2004 + Copyright: See COPYING file that comes with this distribution + + This has to be a c file or for some reason it won't link! (GCC 3.4.1) +*/ + +/* gcc doesn't like inline for me */ +#define inline +/* need access to port_ticket */ +#define XINE_ENGINE_INTERNAL + +#include "xine-scope.h" +#include <xine/post.h> +#include <xine/xine_internal.h> + +typedef struct scope_plugin_s scope_plugin_t; + +struct scope_plugin_s +{ + post_plugin_t post; + + metronom_t metronom; + int channels; + MyNode *list; +}; + +/************************* + * post plugin functions * + *************************/ + +static int +scope_port_open( xine_audio_port_t *port_gen, xine_stream_t *stream, uint32_t bits, uint32_t rate, int mode ) +{ + #define port ((post_audio_port_t*)port_gen) + #define this ((scope_plugin_t*)((post_audio_port_t*)port_gen)->post) + + _x_post_rewire( (post_plugin_t*)port->post ); + _x_post_inc_usage( port ); + + port->stream = stream; + port->bits = bits; + port->rate = rate; + port->mode = mode; + + this->channels = _x_ao_mode2channels( mode ); + + return port->original_port->open( port->original_port, stream, bits, rate, mode ); +} + +static void +scope_port_close( xine_audio_port_t *port_gen, xine_stream_t *stream ) +{ + MyNode *node; + + /* ensure the buffers are deleted during the next XineEngine::timerEvent() */ + for( node = this->list->next; node != this->list; node = node->next ) + node->vpts = node->vpts_end = -1; + + port->stream = NULL; + port->original_port->close( port->original_port, stream ); + + _x_post_dec_usage( port ); +} + +static void +scope_port_put_buffer( xine_audio_port_t *port_gen, audio_buffer_t *buf, xine_stream_t *stream ) +{ +/* FIXME With 8-bit samples the scope won't work correctly. For a special 8-bit code path, + the sample size could be checked like this: if( port->bits == 8 ) */ + + const int num_samples = buf->num_frames * this->channels; + metronom_t *myMetronom = &this->metronom; + MyNode *new_node; + + /* I keep my own metronom because xine wouldn't for some reason */ + memcpy( &this->metronom, stream->metronom, sizeof(metronom_t) ); + + new_node = malloc( sizeof(MyNode) ); + new_node->vpts = myMetronom->got_audio_samples( myMetronom, buf->vpts, buf->num_frames ); + new_node->num_frames = buf->num_frames; + new_node->mem = malloc( num_samples * 2 ); + memcpy( new_node->mem, buf->mem, num_samples * 2 ); + + { + int64_t + K = myMetronom->pts_per_smpls; /*smpls = 1<<16 samples*/ + K *= num_samples; + K /= (1<<16); + K += new_node->vpts; + + new_node->vpts_end = K; + } + + port->original_port->put_buffer( port->original_port, buf, stream ); + + /* finally we should append the current buffer to the list + * this is thread-safe due to the way we handle the list in the GUI thread */ + new_node->next = this->list->next; + this->list->next = new_node; + + #undef port + #undef this +} + +static void +scope_dispose( post_plugin_t *this ) +{ + MyNode *list = ((scope_plugin_t*)this)->list; + MyNode *prev; + MyNode *node = list; + + /* Free all elements of the list (a ring buffer) */ + do { + prev = node->next; + + free( node->mem ); + free( node ); + + node = prev; + } + while( node != list ); + + + free( this ); +} + + +/************************ + * plugin init function * + ************************/ + +xine_post_t* +scope_plugin_new( xine_t *xine, xine_audio_port_t *audio_target ) +{ + scope_plugin_t *scope_plugin = xine_xmalloc( sizeof(scope_plugin_t) ); + post_plugin_t *post_plugin = (post_plugin_t*)scope_plugin; + + { + post_in_t *input; + post_out_t *output; + post_audio_port_t *port; + + _x_post_init( post_plugin, 1, 0 ); + + port = _x_post_intercept_audio_port( post_plugin, audio_target, &input, &output ); + port->new_port.open = scope_port_open; + port->new_port.close = scope_port_close; + port->new_port.put_buffer = scope_port_put_buffer; + + post_plugin->xine_post.audio_input[0] = &port->new_port; + post_plugin->xine_post.type = PLUGIN_POST; + + post_plugin->dispose = scope_dispose; + } + + /* code is straight from xine_init_post() + can't use that function as it only dlopens the plugins + and our plugin is statically linked in */ + + post_plugin->running_ticket = xine->port_ticket; + post_plugin->xine = xine; + + /* scope_plugin_t init */ + scope_plugin->list = xine_xmalloc( sizeof(MyNode) ); + scope_plugin->list->next = scope_plugin->list; + + return &post_plugin->xine_post; +} + +MyNode* +scope_plugin_list( void *post ) +{ + return ((scope_plugin_t*)post)->list; +} + +int +scope_plugin_channels( void *post ) +{ + return ((scope_plugin_t*)post)->channels; +} + +metronom_t* +scope_plugin_metronom( void *post ) +{ + return &((scope_plugin_t*)post)->metronom; +} diff --git a/amarok/src/engine/xine/xine-scope.h b/amarok/src/engine/xine/xine-scope.h new file mode 100644 index 00000000..ce2d7ee7 --- /dev/null +++ b/amarok/src/engine/xine/xine-scope.h @@ -0,0 +1,50 @@ +/* Author: Max Howell <max.howell@methylblue.com>, (C) 2004 + Copyright: See COPYING file that comes with this distribution + + This has to be a c file or for some reason it won't link! (GCC 3.4.1) +*/ + +#ifndef XINESCOPE_H +#define XINESCOPE_H + +/* need access to some stuff for scope time stamping */ +#define METRONOM_INTERNAL + +#include <sys/types.h> +#include <xine/metronom.h> + +typedef struct my_node_s MyNode; + +struct my_node_s +{ + MyNode *next; + int16_t *mem; + int num_frames; + int64_t vpts; + int64_t vpts_end; +}; + +#ifdef __cplusplus +extern "C" +{ +#endif + xine_post_t* + scope_plugin_new( xine_t*, xine_audio_port_t* ); + + /* we sacrifice type-safety here because some GCCs appear broken + * and choke on redefining the xine_post_t typedef + */ + + MyNode* + scope_plugin_list( void* ); + + int + scope_plugin_channels( void* ); + + metronom_t* + scope_plugin_metronom( void* ); +#ifdef __cplusplus +} +#endif + +#endif diff --git a/amarok/src/engine/xine/xinecfg.kcfg b/amarok/src/engine/xine/xinecfg.kcfg new file mode 100644 index 00000000..8f825f52 --- /dev/null +++ b/amarok/src/engine/xine/xinecfg.kcfg @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Author: Ian Monroe, ian@monroe.nu --> +<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0 + http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" > + <kcfgfile name="amarokrc"/> + + <group name="Xine-Engine"> + <entry key="Output Plugin" type="String"> + <label>Sound output method to use</label> + <whatsthis>Select the sound output plugin.</whatsthis> + <default>auto</default> + </entry> + <entry key="Custom Device" type="Bool"> + <label>Enable a custom device</label> + <whatsthis>If selected, enables the setting of a custom audio device. Otherwise the default is used.</whatsthis> + <default>false</default> + </entry> + </group> + +</kcfg> + diff --git a/amarok/src/engine/xine/xinecfg.kcfgc b/amarok/src/engine/xine/xinecfg.kcfgc new file mode 100644 index 00000000..127cdf81 --- /dev/null +++ b/amarok/src/engine/xine/xinecfg.kcfgc @@ -0,0 +1,7 @@ +# Code generation options for kconfig_compiler +File=xinecfg.kcfg +ClassName=XineCfg +Singleton=true +Mutators=true +MemberVariables=private + diff --git a/amarok/src/engine/xine/xineconfigbase.ui b/amarok/src/engine/xine/xineconfigbase.ui new file mode 100644 index 00000000..c3925ec0 --- /dev/null +++ b/amarok/src/engine/xine/xineconfigbase.ui @@ -0,0 +1,506 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>XineConfigBase</class> +<widget class="QWidget"> + <property name="name"> + <cstring>XineConfigBase</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>437</width> + <height>458</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="caption"> + <string>Xine Configure</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout11</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>xineLogo</cstring> + </property> + <property name="paletteForegroundColor"> + <color> + <red>0</red> + <green>0</green> + <blue>0</blue> + </color> + </property> + <property name="paletteBackgroundColor"> + <color> + <red>255</red> + <green>255</green> + <blue>255</blue> + </color> + </property> + <property name="frameShape"> + <enum>StyledPanel</enum> + </property> + <property name="frameShadow"> + <enum>Raised</enum> + </property> + <property name="margin"> + <number>1</number> + </property> + <property name="text"> + <string></string> + </property> + <property name="alignment"> + <set>AlignCenter</set> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout10</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout10</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout2</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel2</cstring> + </property> + <property name="text"> + <string>&Output plugin:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>deviceComboBox</cstring> + </property> + </widget> + <widget class="KComboBox"> + <property name="name"> + <cstring>deviceComboBox</cstring> + </property> + </widget> + </hbox> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel3</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>1</hsizetype> + <vsizetype>1</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Sound device may be modified after the output plugin has been changed to ALSA or OSS.</string> + </property> + <property name="alignment"> + <set>WordBreak|AlignVCenter</set> + </property> + </widget> + <widget class="QGroupBox"> + <property name="name"> + <cstring>alsaGroupBox</cstring> + </property> + <property name="title"> + <string>ALSA Device Configuration</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel2_3</cstring> + </property> + <property name="text"> + <string>&Mono:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>monoLineEdit</cstring> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel3_3</cstring> + </property> + <property name="text"> + <string>&Stereo:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>stereoLineEdit</cstring> + </property> + </widget> + <widget class="QLabel" row="0" column="2"> + <property name="name"> + <cstring>textLabel4_2</cstring> + </property> + <property name="text"> + <string>&4 Channels:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>chan4LineEdit</cstring> + </property> + </widget> + <widget class="QLabel" row="1" column="2"> + <property name="name"> + <cstring>chan5Label</cstring> + </property> + <property name="text"> + <string>&6 Channels:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>chan5LineEdit</cstring> + </property> + </widget> + <widget class="QLineEdit" row="0" column="1"> + <property name="name"> + <cstring>monoLineEdit</cstring> + </property> + </widget> + <widget class="QLineEdit" row="1" column="1"> + <property name="name"> + <cstring>stereoLineEdit</cstring> + </property> + </widget> + <widget class="QLineEdit" row="0" column="3"> + <property name="name"> + <cstring>chan4LineEdit</cstring> + </property> + </widget> + <widget class="QLineEdit" row="1" column="3"> + <property name="name"> + <cstring>chan5LineEdit</cstring> + </property> + </widget> + </grid> + </widget> + <widget class="QGroupBox"> + <property name="name"> + <cstring>ossGroupBox</cstring> + </property> + <property name="title"> + <string>OSS Device Configuration</string> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>&Device:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>ossDeviceComboBox</cstring> + </property> + </widget> + <widget class="QComboBox"> + <property name="name"> + <cstring>ossDeviceComboBox</cstring> + </property> + </widget> + </hbox> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout5</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1_2</cstring> + </property> + <property name="text"> + <string>Speaker &arrangement:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>speakerComboBox</cstring> + </property> + </widget> + <widget class="QComboBox"> + <property name="name"> + <cstring>speakerComboBox</cstring> + </property> + </widget> + </hbox> + </widget> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBox1</cstring> + </property> + <property name="title"> + <string>HTTP Proxy for Streaming</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout4</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel2_2</cstring> + </property> + <property name="text"> + <string>&Host:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>hostLineEdit</cstring> + </property> + </widget> + <widget class="QLineEdit"> + <property name="name"> + <cstring>hostLineEdit</cstring> + </property> + </widget> + <widget class="KIntSpinBox"> + <property name="name"> + <cstring>portIntBox</cstring> + </property> + <property name="maxValue"> + <number>65534</number> + </property> + <property name="minValue"> + <number>1</number> + </property> + <property name="value"> + <number>60000</number> + </property> + </widget> + </hbox> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout2</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel3_2</cstring> + </property> + <property name="text"> + <string>&User:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>userLineEdit</cstring> + </property> + </widget> + <widget class="QLineEdit"> + <property name="name"> + <cstring>userLineEdit</cstring> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel4</cstring> + </property> + <property name="text"> + <string>&Password:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>passLineEdit</cstring> + </property> + </widget> + <widget class="QLineEdit"> + <property name="name"> + <cstring>passLineEdit</cstring> + </property> + <property name="echoMode"> + <enum>Password</enum> + </property> + </widget> + </hbox> + </widget> + </vbox> + </widget> + <widget class="QGroupBox"> + <property name="name"> + <cstring>audiocdGroupBox</cstring> + </property> + <property name="title"> + <string>Audio CD Configuration</string> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout11</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel2_3_2</cstring> + </property> + <property name="text"> + <string>Default device:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>monoLineEdit</cstring> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel2_2_2</cstring> + </property> + <property name="text"> + <string>CDDB Server:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>hostLineEdit</cstring> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel2_3_2_2</cstring> + </property> + <property name="text"> + <string>CDDB Cache dir:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>monoLineEdit</cstring> + </property> + </widget> + </vbox> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout12</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLineEdit"> + <property name="name"> + <cstring>audiocd_device</cstring> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout10</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLineEdit"> + <property name="name"> + <cstring>cddb_server</cstring> + </property> + </widget> + <widget class="KIntSpinBox"> + <property name="name"> + <cstring>cddb_port</cstring> + </property> + <property name="maxValue"> + <number>65534</number> + </property> + <property name="minValue"> + <number>1</number> + </property> + <property name="value"> + <number>60000</number> + </property> + </widget> + </hbox> + </widget> + <widget class="QLineEdit"> + <property name="name"> + <cstring>cddb_cache_dir</cstring> + </property> + <property name="text"> + <string></string> + </property> + </widget> + </vbox> + </widget> + </hbox> + </widget> + </vbox> + </widget> + <spacer> + <property name="name"> + <cstring>spacer3</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>16</height> + </size> + </property> + </spacer> + </vbox> + </widget> + </hbox> + </widget> + </vbox> +</widget> +<includes> + <include location="local" impldecl="in declaration">plugin/pluginconfig.h</include> +</includes> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/amarok/src/engine/yauap/Makefile.am b/amarok/src/engine/yauap/Makefile.am new file mode 100644 index 00000000..54538d6b --- /dev/null +++ b/amarok/src/engine/yauap/Makefile.am @@ -0,0 +1,29 @@ +kde_module_LTLIBRARIES = \ + libamarok_yauap-engine_plugin.la + +INCLUDES = \ + -I$(top_srcdir)/amarok/src/plugin \ + -I$(top_srcdir)/amarok/src \ + $(CFLAGS_YAUAP) \ + $(all_includes) + + + +libamarok_yauap_engine_plugin_la_LIBADD = \ + $(top_builddir)/amarok/src/libamarok.la \ + $(top_builddir)/amarok/src/plugin/libplugin.la \ + $(LIB_YAUAP)\ + $(LIB_KDECORE) $(LIB_QT) + + +libamarok_yauap_engine_plugin_la_SOURCES = \ + yauap-engine.cpp + +libamarok_yauap_engine_plugin_la_LDFLAGS = \ + $(KDE_PLUGIN) \ + $(all_libraries) + +METASOURCES = AUTO + +kde_services_DATA = \ + amarok_yauap-engine_plugin.desktop diff --git a/amarok/src/engine/yauap/amarok_yauap-engine_plugin.desktop b/amarok/src/engine/yauap/amarok_yauap-engine_plugin.desktop new file mode 100644 index 00000000..d420385b --- /dev/null +++ b/amarok/src/engine/yauap/amarok_yauap-engine_plugin.desktop @@ -0,0 +1,109 @@ +[Desktop Entry] +Encoding=UTF-8 +Type=Service +Name=yauap engine +Name[af]=yauap enjin +Name[bg]=Yauap +Name[bn]=yauap ইঞ্জিন +Name[ca]=Motor yauap +Name[cs]=yauap +Name[da]=Yauap-grænseflade +Name[de]=Yauap +Name[el]=μηχανή yauap +Name[eo]=yauap ilo +Name[es]=motor yauap +Name[et]=yauap mootor +Name[fa]=موتور yauap +Name[fi]=yauap +Name[fr]=Moteur yauap +Name[ga]=inneall yauap +Name[hu]=Yauap alrendszer +Name[is]=yauap vél +Name[it]=Motore yauap +Name[ja]=yauap エンジン +Name[ka]=yauap ძრავი +Name[km]=ម៉ាស៊ីន yauap +Name[lt]=yauap variklis +Name[mk]=yauap-машина +Name[ms]=Enjin yauap +Name[nb]=yauap-motor +Name[nds]=Yauap +Name[ne]=yauap इन्जिन +Name[nl]=yauap-engine +Name[nn]=yauap-motor +Name[pa]=yauap ਇੰਜਣ +Name[pl]=moduł yauap +Name[pt]=Motor yauap +Name[pt_BR]=Mecanismo Yauap +Name[se]=yauap-mohtor +Name[sr]=Мотора yauap +Name[sr@Latn]=Motora yauap +Name[sv]=Yauap-gränssnitt +Name[tr]=yauap motoru +Name[uk]=Рушій yauap +Name[uz]=yauap tizimi +Name[uz@cyrillic]=yauap тизими +Name[wa]=Éndjin yauap +Name[zh_CN]=yauap 引擎 +Name[zh_TW]=yauap 引擎 +X-KDE-Library=libamarok_yauap-engine_plugin +Comment=Plugin for Amarok +Comment[af]=Inprop module vir Amarok +Comment[ar]= قابس ( برنامج مضاف الى) AmaroK +Comment[bg]=Приставка за Amarok +Comment[bn]=আমারক-এর জন্য প্লাগিন +Comment[br]=Lugent evit Amarok +Comment[ca]=Connector per l'Amarok +Comment[cs]=Modul pro AmaroK +Comment[de]=Modul für Amarok +Comment[el]=Πρόσθετο για το AmaroK +Comment[eo]=Kromaĵo por Amarok +Comment[es]=Extensión para Amarok +Comment[et]=Amaroki plugin +Comment[fa]=وصله برای amaroK +Comment[fi]=Amarok-liitännäinen +Comment[fr]=Module pour Amarok +Comment[ga]=Breiseán AmaroK +Comment[gl]=Extensión para Amarok +Comment[hu]=Bővítőmodul az Amarokhoz +Comment[is]=Íforrit fyrir Amarok +Comment[it]=Plugin per Amarok +Comment[ja]=Amarok のためのプラグイン +Comment[ka]=მოდული Amarok-ისთვის +Comment[km]=កម្មវិធី​ជំនួយ​សម្រាប់ Amarok +Comment[lt]=Amarok įskiepis +Comment[mk]=Приклучок за Амарок +Comment[nb]=Programtillegg for Amarok +Comment[nds]=Moduul för Amarok +Comment[ne]=अमारोकका लागि प्लगइन +Comment[nl]=Plugin voor Amarok +Comment[nn]=Programtillegg for Amarok +Comment[pa]=ਅਮਰੋਕ ਲਈ ਪਲੱਗਇਨ +Comment[pl]=Wtyczka Amaroka +Comment[pt]='Plugin' para o Amarok +Comment[pt_BR]=Plugin para o Amarok +Comment[ru]=Модуль amaroK +Comment[se]=Lassemoduvla Amarok:ii +Comment[sk]=Amarok modul +Comment[sr]=Прикључак за Amarok +Comment[sr@Latn]=Priključak za Amarok +Comment[sv]=Insticksprogram för Amarok +Comment[th]=โปรแกรมเสริมสำหรับ Amarok +Comment[tr]=Amarok için Eklenti +Comment[uk]=Втулок для Amarok +Comment[uz]=Amarok uchun plagin +Comment[uz@cyrillic]=Amarok учун плагин +Comment[wa]=Tchôke-divins po Amarok +Comment[zh_CN]=Amarok 插件 +Comment[zh_TW]=amaroK 插件 +ServiceTypes=Amarok/Plugin + +X-KDE-Amarok-plugintype=engine +X-KDE-Amarok-name=yauap-engine +X-KDE-Amarok-authors=Sascha Sommer +X-KDE-Amarok-email=ssommer@suse.de +X-KDE-Amarok-rank=1 +X-KDE-Amarok-version=1 +X-KDE-Amarok-framework-version=32 + + diff --git a/amarok/src/engine/yauap/yauap-engine.cpp b/amarok/src/engine/yauap/yauap-engine.cpp new file mode 100644 index 00000000..0a4fb9d0 --- /dev/null +++ b/amarok/src/engine/yauap/yauap-engine.cpp @@ -0,0 +1,676 @@ +/*************************************************************************** + yauap-engine.h - yauap engine plugin + +copyright : (C) 2006 by Sascha Sommer <saschasommer@freenet.de> +***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#include <qapplication.h> + +#include <klocale.h> +#include <iostream> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <stdlib.h> + +#define DBUS_API_SUBJECT_TO_CHANGE +#include <dbus/connection.h> + +//#define MANUAL_YAUAP_START +#define YAUAP_DBUS_SERVICE "org.yauap.CommandService" +#define YAUAP_DBUS_PATH "/yauapObject" +#define YAUAP_DBUS_INTERFACE "org.yauap.CommandInterface" + +#include "yauap-engine.h" +#include "debug.h" + + + +AMAROK_EXPORT_PLUGIN( yauapEngine ) + +/* signal handler for DBus signals */ +static DBusHandlerResult +signal_handler( DBusConnection * /*con*/, DBusMessage *msg, void *data ) +{ + yauapEngine* engine = (yauapEngine*)data; + const char *objectpath = dbus_message_get_path(msg); + const char *member = dbus_message_get_member(msg); + const char *interface = dbus_message_get_interface(msg); + bool handled = true; + + debug() << "SIGNAL member " << member << " interface " << interface << " objpath " << objectpath << endl; + + if (dbus_message_is_signal( msg, "org.yauap.CommandInterface", "MetadataSignal")) + QApplication::postEvent(engine, new QCustomEvent(3004)); + else if(dbus_message_is_signal( msg, "org.yauap.CommandInterface", "EosSignal")) { + if (engine->m_state == Engine::Playing) + QApplication::postEvent(engine, new QCustomEvent(3000)); + } + else if(dbus_message_is_signal( msg, "org.yauap.CommandInterface", "ErrorSignal")) + { + char* text = NULL; + DBusError error; + dbus_error_init(&error); + if(dbus_message_get_args( msg, &error, DBUS_TYPE_STRING, &text, DBUS_TYPE_INVALID)) { + QCustomEvent* e = new QCustomEvent(3002); + e->setData(new QString(text)); + QApplication::postEvent(engine, e); + } + } + else + handled = false; + + return (handled ? DBUS_HANDLER_RESULT_HANDLED : DBUS_HANDLER_RESULT_NOT_YET_HANDLED); +} + +int +yauapProcess::commSetupDoneC() +{ + int r = Amarok::Process::commSetupDoneC(); + int fd = open("/dev/null", O_RDWR); + dup2(fd, 1); + dup2(fd, 2); + close(fd); + return r; +} + +/* create a qt dbus connection that will receive the signals */ +bool +DBusConnection::open() +{ + DBusError error; + dbus_error_init( &error ); + + debug() << " connecting to dbus" << endl; + + // close open connection + close(); + + /* connect to session bus */ + dbus_connection = dbus_bus_get_private( DBUS_BUS_SESSION, &error ); /* dbus_bus_get somehow doesn't work here */ + if( dbus_error_is_set(&error) ) + { + debug() << "unable to connect to DBUS." << endl; + dbus_error_free(&error); + return false; + } + dbus_connection_set_exit_on_disconnect( dbus_connection, false ); + + /* create qt connection */ + qt_connection = new DBusQt::Connection( this ); + qt_connection->dbus_connection_setup_with_qt_main( dbus_connection ); + + if ( !dbus_connection_add_filter(dbus_connection, signal_handler, context, NULL) ) + { + debug() << "Failed to add filter function." << endl; + return false; + } + + /* Match for DBUS_INTERFACE_DBUS */ + dbus_bus_add_match( dbus_connection, "type='signal',interface='org.yauap.CommandInterface'", &error); + if ( dbus_error_is_set( &error ) ) + { + debug() << "Error adding match, " << error.name << " " << error.message; + dbus_error_free (&error); + return false; + } + + debug() << " connected " << endl; + return true; +} + +/* close qt dbus connection */ +void +DBusConnection::close() +{ + debug() << "close DBusConnection" << endl; + + if( dbus_connection ) + dbus_connection_close( dbus_connection ); + + if(qt_connection) + qt_connection->close(); + + /* DBusConnection::open () calls dbus_bus_get (), we need to unref the connection */ + debug() << "calling dbus connection close" << endl; + + dbus_connection = NULL; + qt_connection = NULL; + debug() << "DBusConnection closed" << endl; +} + + +DBusConnection::DBusConnection( yauapEngine* c ) +{ + qt_connection = NULL; + dbus_connection = NULL; + context = c; +} + +DBusConnection::~DBusConnection() +{ + close(); +} + +bool +DBusConnection::send(const char *method, int first_arg_type, ...) +{ + dbus_uint32_t serial = 0; + bool ret = false; + + QMutexLocker lock(&m_mutex); + + DBusMessage* msg = dbus_message_new_method_call( + YAUAP_DBUS_SERVICE, YAUAP_DBUS_PATH, YAUAP_DBUS_INTERFACE, + method); + + if (msg) { + va_list ap; + + va_start(ap, first_arg_type); + dbus_message_append_args_valist(msg, first_arg_type, ap); + va_end(ap); + + ret = dbus_connection_send(dbus_connection, msg, &serial); + dbus_message_unref (msg); + } + + return ret; +} + +int +DBusConnection::call(const char *method, int first_arg_type, ...) +{ + dbus_uint32_t ret = -1; + + va_list ap; + va_start (ap, first_arg_type); + DBusMessage* msg = send_with_reply(method, first_arg_type, ap); + va_end (ap); + + if (msg) { + DBusMessageIter args; + if (dbus_message_iter_init(msg, &args) && + (DBUS_TYPE_INT32 == dbus_message_iter_get_arg_type(&args) || + DBUS_TYPE_UINT32 == dbus_message_iter_get_arg_type(&args))) + dbus_message_iter_get_basic(&args, &ret); + + dbus_message_unref (msg); + } + + return ret; +} + +DBusMessage* +DBusConnection::send_with_reply(const char* method, int first_arg_type, ...) +{ + va_list ap; + va_start(ap, first_arg_type); + DBusMessage* msg = send_with_reply(method, first_arg_type, ap); + va_end(ap); + return msg; +} + +DBusMessage* +DBusConnection::send_with_reply(const char* method, int first_arg_type, va_list ap) +{ + QMutexLocker lock(&m_mutex); + + DBusMessage* msg = dbus_message_new_method_call( + YAUAP_DBUS_SERVICE, YAUAP_DBUS_PATH, YAUAP_DBUS_INTERFACE, method); + + if (msg) { + DBusError error; + dbus_error_init(&error); + + dbus_message_append_args_valist(msg, first_arg_type, ap); + + DBusMessage* oldmsg = msg; + msg = dbus_connection_send_with_reply_and_block(dbus_connection, oldmsg, -1, &error); + DBusDispatchStatus status; + while ((status = dbus_connection_get_dispatch_status(dbus_connection)) == DBUS_DISPATCH_DATA_REMAINS) + dbus_connection_dispatch (dbus_connection); + dbus_message_unref (oldmsg); + + if (!msg) + debug() << "dbus error while waiting for reply: " << error.message << endl; + } + + return msg; +} + + +/* emit state change signal */ +void +yauapEngine::change_state( Engine::State state ) +{ + m_state = state; + emit stateChanged(m_state); +} + + +/* destroy engine */ +yauapEngine::~yauapEngine() +{ + /* make sure we really stopped */ + stop(); + + /* quit the player */ + if ( !con->send("quit", DBUS_TYPE_INVALID) ) + debug() << "quit failed " << endl; + + delete con; +} + +/* fetch metadata from yauap */ +void +yauapEngine::update_metadata() +{ + Engine::SimpleMetaBundle* bndl = new Engine::SimpleMetaBundle; + debug() << " emit metadata change " << endl; + + DBusMessage* msg = con->send_with_reply("get_metadata", DBUS_TYPE_INVALID); + if (msg) { + DBusMessageIter args; + if (dbus_message_iter_init(msg, &args) && DBUS_TYPE_ARRAY == + dbus_message_iter_get_arg_type(&args)) { + DBusMessageIter sub; + dbus_message_iter_recurse(&args, &sub); + dbus_message_iter_next(&args); + while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING) { + char* reply_ptr = 0; + dbus_message_iter_get_basic(&sub, &reply_ptr); + dbus_message_iter_next(&sub); + + debug() << "reply_ptr: " << reply_ptr << endl; + +#define ASSIGN(a,b) if(!strncmp(reply_ptr,b,strlen(b)) && strlen(reply_ptr + strlen(b) + 1)){ \ + bndl->a = reply_ptr + strlen(b) + 1; \ + continue; \ + } + + ASSIGN( title, "title" ) + ASSIGN( artist, "artist" ) + ASSIGN( album, "album" ) + ASSIGN( comment, "comment" ) + ASSIGN( genre, "genre" ) + ASSIGN( samplerate, "samplerate" ) + ASSIGN( year, "date" ) + ASSIGN( tracknr, "track-number" ) + ASSIGN( length, "length" ) + ASSIGN( bitrate, "bitrate" ) +#undef ASSIGN + } + } + dbus_message_unref(msg); + } + + debug() << "title:" << bndl->title << endl; + debug() << "artist:" << bndl->artist << endl; + debug() << "album:" << bndl->album << endl; + debug() << "comment:" << bndl->comment << endl; + debug() << "genre:" << bndl->genre << endl; + debug() << "samplerate:" << bndl->samplerate << endl; + debug() << "year:" << bndl->year << endl; + debug() << "tracknr:" << bndl->tracknr << endl; + debug() << "length:" << bndl->length << endl; + debug() << "bitrate:" << bndl->bitrate << endl; + + + /* do not overwrite manually generated metadata from audio cds */ + if(bndl->title.isEmpty() && loaded_url.protocol() == "cdda") + return; + + QCustomEvent* e = new QCustomEvent(3003); + e->setData(bndl); + QApplication::postEvent(this, e); +} + + +/* fetch current sample buffer from yauap */ +const Engine::Scope & +yauapEngine::scope() +{ + int len = 0; + dbus_int16_t* data = 0; + + DBusMessage* msg = con->send_with_reply("get_scopedata", DBUS_TYPE_INVALID); + if (msg) { + DBusMessageIter args; + if (dbus_message_iter_init(msg, &args) && DBUS_TYPE_ARRAY == + dbus_message_iter_get_arg_type(&args)) { + DBusMessageIter sub; + dbus_message_iter_recurse(&args, &sub); + dbus_message_iter_next(&args); + dbus_message_iter_get_fixed_array(&sub,&data,&len); + } + dbus_message_unref(msg); + } + + /* 2 channel 16 bit samples */ + if(len == SCOPESIZE * 2) + { + for( int i=0; i < SCOPESIZE ; i++) + m_scope[i] = data[i]; + }else + debug() << "get_scopedata returned the wrong amount of data " << len << endl; + + return m_scope; +} + +void +yauapEngine::yauapProcessExited() +{ + debug() << "yauapProcessExited!!!!!" << endl; + + closeDbusConnection(); + initDbusConnection(); +} + +void +yauapEngine::closeDbusConnection() +{ + /* destroy Qt DBus connection */ + delete con; + con = 0; + + /* kill yauap */ +#ifndef MANUAL_YAUAP_START + helper.kill(); +#endif +} + +bool +yauapEngine::initDbusConnection() +{ +#ifndef MANUAL_YAUAP_START + /* start yauap in slave mode */ + helper.clearArguments(); + helper << "yauap" << "-noexit"; + + if( !helper.start(KProcess::NotifyOnExit, KProcess::All)) + { + debug() << "could not start yauap " << endl; + emit statusText( i18n( "could not start yauap" ) ); + return false; + } +#endif + + /* create and open qt DBus connection so that we are able to receive signals */ + con = new DBusConnection( this ); + if (!con->open()) + { + debug() << "could not connect to dbus" << endl; + emit statusText( i18n( "Error: could not connect to dbus" ) ); + return false; + } + + /* makes sure the player is stopped: retry the call a few times because it takes some + time until yauap registered its dbus service */ + for( int i=0; i < 2; ++i ) { + if( con->send("stop", DBUS_TYPE_INVALID) >= 0 ) + return true; + usleep(20 * 1000); + } + + return false; +} + +#if 0 +void +yauapEngine::handleDbusError(const char* method) +{ + debug() << method << " failed " << endl; + + closeDbusConnection(); + initDbusConnection(); +} +#endif + +void +yauapEngine::customEvent(QCustomEvent *e) +{ + QString* message = static_cast<QString*>(e->data()); + + switch (e->type()) { + case 3000: + m_state = Engine::Idle; + emit trackEnded(); + break; + case 3002: + emit statusText(*message); + delete message; + break; + case 3003: + { + Engine::SimpleMetaBundle* bundle = static_cast<Engine::SimpleMetaBundle*>(e->data()); + emit metaData(*bundle); + delete bundle; + break; + } + case 3004: + update_metadata(); + break; + default: + ; + } +} + +/* init engine */ +bool +yauapEngine::init() +{ + debug() << "In init" << endl; + + m_state = Engine::Idle; + + connect(&helper, SIGNAL(processExited(KProcess*)), SLOT(yauapProcessExited())); + + if (initDbusConnection()) + return true; + + emit statusText( i18n( "Error: timed out waiting for yauap" ) ); + return false; +} + +/* check if the given url can be decoded */ +bool +yauapEngine::canDecode( const KURL &kurl ) const +{ + QCString qurl = kurl.url().utf8(); + const char* url = qurl.data(); + + return con->call("can_decode", DBUS_TYPE_STRING, &url, DBUS_TYPE_INVALID) > 0; +} + +/* load a new track FIXME: implement cross fading */ +bool +yauapEngine::load( const KURL &url, bool isStream ) +{ + QString qurl = url.url(); + const char* curl = qurl.ascii(); + + m_isStream = isStream; + + Engine::Base::load( url, isStream || url.protocol() == "http" ); + change_state(Engine::Idle); + + if (!curl || !con->call("load", DBUS_TYPE_STRING, &curl, DBUS_TYPE_INVALID)) + return false; + + loaded_url = url; + return true; +} + +/* set volume */ +void +yauapEngine::setVolumeSW( uint volume ) +{ + dbus_uint32_t dbus_volume = volume; + int ret; + + debug() << "In setVolumeSW " << volume << endl; + + ret = con->send("set_volume", DBUS_TYPE_UINT32, &dbus_volume, DBUS_TYPE_INVALID); + debug() << "=> " << ret << endl; +} + +/* start playback */ +bool +yauapEngine::play( uint offset ) +{ + dbus_uint32_t dbus_offset = offset; + + debug() << "In play" << endl; + + if (con->send("start", DBUS_TYPE_UINT32, &dbus_offset, DBUS_TYPE_INVALID) > 0) + { + change_state( Engine::Playing ); + return true; + } + + change_state( Engine::Empty ); + return false; +} + +/* stop playback */ +void +yauapEngine::stop() +{ + change_state( Engine::Empty ); + + if (con->send("stop", DBUS_TYPE_INVALID) <= 0) + { + debug() << "stop failed " << endl; + return; + } + + change_state(Engine::Empty); +} + + +/* pause playback */ +void +yauapEngine::pause() +{ + debug() << "In pause " << endl; + + if (!con->call("pause", DBUS_TYPE_INVALID)) + return; + + if( state() == Engine::Playing ) + change_state( Engine::Paused ); + else + change_state( Engine::Playing ); +} + +/* unpause playback */ +void +yauapEngine::unpause() +{ + pause(); +} + +/* get track length in ms */ +uint +yauapEngine::length() const +{ + debug() << "In length " << endl; + + int length = con->call("get_length", DBUS_TYPE_INVALID); + if (length < 0) return 0; + + debug() << "length is => " << length << endl; + return (uint) length; +} + +/* get current position */ +uint +yauapEngine::position() const +{ + int position = 0; + + position = con->call("get_position", DBUS_TYPE_INVALID); + + if (position < 0) position = 0; + return (uint) position; +} + +/* seek to offset in ms */ +void +yauapEngine::seek( uint offset ) +{ + dbus_uint32_t dbus_offset = offset; + + if (!con->send("seek", DBUS_TYPE_UINT32, &dbus_offset, DBUS_TYPE_INVALID)) + debug() << "seek failed " << endl; +} + +bool +yauapEngine::getAudioCDContents(const QString &device, KURL::List &urls) +{ + debug() << "Getting AudioCD contents..." << endl; + + QCString cdevice = device.latin1(); + const char* cdevice_ptr = cdevice.data(); + + DBusMessage* msg = con->send_with_reply("get_audio_cd_contents", + DBUS_TYPE_STRING, &cdevice_ptr, DBUS_TYPE_INVALID); + if (msg) { + DBusMessageIter args; + int i = 0; + + if (dbus_message_iter_init(msg, &args) && DBUS_TYPE_ARRAY == + dbus_message_iter_get_arg_type(&args)) { + DBusMessageIter sub; + dbus_message_iter_recurse(&args, &sub); + dbus_message_iter_next(&args); + while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING) { + char* reply_ptr = 0; + dbus_message_iter_get_basic(&sub, &reply_ptr); + dbus_message_iter_next(&sub); + + debug() << "reply_ptr: " << reply_ptr << endl; + + Engine::SimpleMetaBundle b; + char* saveptr; + KURL url = QString("cdda://").append( strtok_r(reply_ptr,"=",&saveptr)); + urls << url; + debug() << url << endl; + b.title = QString( i18n( "Track %1" ) ).arg( i+1 ); + b.length = strtok_r(NULL,"=",&saveptr); + b.album = "AudioCD"; + b.tracknr = i+1; + b.samplerate = "44100"; + b.bitrate = "1411"; + cd_tracks.push_back(b); + ++i; + } + } + dbus_message_unref(msg); + } + + return true; +} + +bool +yauapEngine::metaDataForUrl(const KURL &url, Engine::SimpleMetaBundle &b) +{ + if ( url.protocol() == "cdda" ) + { + b = cd_tracks[url.host().toUInt()-1]; + return true; + } + return false; +} + +#include "yauap-engine.moc" diff --git a/amarok/src/engine/yauap/yauap-engine.h b/amarok/src/engine/yauap/yauap-engine.h new file mode 100644 index 00000000..1a3a71d9 --- /dev/null +++ b/amarok/src/engine/yauap/yauap-engine.h @@ -0,0 +1,107 @@ +/*************************************************************************** + yauap-engine.h - yauap engine plugin + +copyright : (C) 2006 by Sascha Sommer <ssommer@suse.de> +***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#ifndef AMAROK_YAUAP_ENGINE_H +#define AMAROK_YAUAP_ENGINE_H + +#define DBUS_API_SUBJECT_TO_CHANGE +#include <dbus/connection.h> + +#include <amarok.h> + +#include "enginebase.h" +#include "debug.h" + +class yauapEngine; + +class DBusConnection : public QObject +{ + friend class yauapEngine; + + DBusQt::Connection *qt_connection; + DBusConnection *dbus_connection; + yauapEngine *context; + QMutex m_mutex; + +public: + bool open(); + void close(); + DBusConnection( yauapEngine *context ); + ~DBusConnection(); + + bool send(const char *method, int first_arg_type, ...); + DBusMessage* send_with_reply(const char* method, int first_arg_type, ...); + DBusMessage* send_with_reply(const char* method, int first_arg_type, va_list); + int call(const char *method, int first_arg_type, ...); +}; + +class yauapProcess : public Amarok::Process +{ +public: + yauapProcess(QObject* parent) : Amarok::Process(parent) {} + + virtual int commSetupDoneC(); +}; + +static DBusHandlerResult signal_handler( DBusConnection *, DBusMessage *, void *); + +class yauapEngine : public Engine::Base +{ + Q_OBJECT + + friend class DBusConnection; + + friend DBusHandlerResult signal_handler( DBusConnection *, DBusMessage *, void *); + + virtual ~yauapEngine(); + virtual bool init(); + virtual bool canDecode( const KURL& ) const; + virtual uint position() const ; + virtual bool load( const KURL&, bool ); + virtual bool play( uint ); + virtual void stop(); + virtual void pause(); + virtual void unpause(); + virtual void setVolumeSW( uint ); + virtual void seek( uint ); + virtual uint length() const ; + virtual Engine::State state() const { return m_state; } + virtual const Engine::Scope &scope(); + virtual bool getAudioCDContents(const QString &device, KURL::List &urls); + virtual bool metaDataForUrl(const KURL &url, Engine::SimpleMetaBundle &b); +public: + yauapEngine() : EngineBase(), helper(0) {} + /* these need to be public because they are called from the dbus signal handler */ + void update_metadata(); + void update_scope(); + virtual void customEvent(QCustomEvent*); + +private slots: + void yauapProcessExited(); + +private: + KURL loaded_url; + std::vector<Engine::SimpleMetaBundle> cd_tracks; + void change_state( Engine::State ); + bool initDbusConnection(); + void closeDbusConnection(); + + Engine::State m_state; + DBusConnection *con; + /* helper process to start */ + yauapProcess helper; +}; + +#endif diff --git a/amarok/src/engine_fwd.h b/amarok/src/engine_fwd.h new file mode 100644 index 00000000..30068a8b --- /dev/null +++ b/amarok/src/engine_fwd.h @@ -0,0 +1,26 @@ + +#ifndef ENGINE_FWD_H +#define ENGINE_FWD_H + +/// Used by eg engineobserver.h, and thus we reduce header dependencies on enginebase.h + +namespace Engine +{ + class SimpleMetaBundle; + class Base; + + /** + * You should return: + * Playing when playing, + * Paused when paused + * Idle when you still have a URL loaded (ie you have not been told to stop()) + * Empty when you have been told to stop(), or an error occurred and you stopped yourself + * + * It is vital to be Idle just after the track has ended! + */ + enum State { Empty, Idle, Playing, Paused }; +} + +typedef Engine::Base EngineBase; + +#endif diff --git a/amarok/src/enginebase.cpp b/amarok/src/enginebase.cpp new file mode 100644 index 00000000..eaacc6e1 --- /dev/null +++ b/amarok/src/enginebase.cpp @@ -0,0 +1,53 @@ +//Copyright: (C) 2003 Mark Kretschmann +// (C) 2004,2005 Max Howell, <max.howell@methylblue.com> +//License: See COPYING + +#include "enginebase.h" + +#include <cmath> + + +Engine::Base::Base() + : Amarok::Plugin() + , m_xfadeLength( 0 ) + , m_xfadeNextTrack( false ) + , m_volume( 50 ) + , m_scope( SCOPESIZE ) + , m_isStream( false ) +{} + + +Engine::Base::~Base() +{ +} + +////////////////////////////////////////////////////////////////////// + + +bool +Engine::Base::load( const KURL &url, bool stream ) +{ + m_url = url; + m_isStream = stream; + + return true; +} + + +void Engine::Base::setVolume( uint value ) +{ + m_volume = value; + + setVolumeSW( makeVolumeLogarithmic( value ) ); +} + + +uint +Engine::Base::makeVolumeLogarithmic( uint volume ) // static +{ + // We're using a logarithmic function to make the volume ramp more natural. + return static_cast<uint>( 100 - 100.0 * std::log10( ( 100 - volume ) * 0.09 + 1.0 ) ); +} + + +#include "enginebase.moc" diff --git a/amarok/src/enginebase.h b/amarok/src/enginebase.h new file mode 100644 index 00000000..f47858a5 --- /dev/null +++ b/amarok/src/enginebase.h @@ -0,0 +1,282 @@ +//Copyright: (C) 2003 Mark Kretschmann +// (C) 2004 Max Howell, <max.howell@methylblue.com> +//License: See COPYING + +#ifndef AMAROK_ENGINEBASE_H +#define AMAROK_ENGINEBASE_H + +#include "plugin/plugin.h" //baseclass +#include "amarok_export.h" + +#include <kurl.h> +#include <qobject.h> //baseclass +#include <qvaluelist.h> //stack alloc +#include <vector> + +#include <sys/types.h> + + +/** + * @class Engine::Base + * @author Mark Kretshmann + * @author Max Howell + * + * This is an abstract base class that you need to derive when making your own backends. + * It is typdefed to EngineBase for your conveniece. + * + * The only key thing to get right is what to return from state(), as some Amarok + * behaviour is dependent on you returning the right state at the right time. + * + * Empty = No URL loaded and ready to play + * Idle = URL ready for play, but not playing, so before AND after playback + * Playing = Playing a stream + * Paused = Stream playback is paused + * + * Not returning idle when you have reached End-Of-Stream but Amarok has not told you + * to stop would be bad because some components behave differently when the engine is + * Empty or not. You are Idle because you still have a URL assigned. + * + * load( KURL ) is a key function because after this point your engine is loaded, and + * Amarok will expect you to be able to play the URL until stop() or another load() is + * called. + * + * You must handle your own media, do not rely on Amarok to call stop() before play() etc. + * + * At this time, emitting stateChanged( Engine::Idle ) is not necessary, otherwise you should + * let Amarok know of state changes so it updates the UI correctly. + * + * Basically, reimplement everything virtual and ensure you emit stateChanged() correctly, + * try not to block in any function that is called by Amarok, try to keep the user informed + * with emit statusText() + * + * Only canDecode() needs to be thread-safe. Everything else is only called from the GUI thread. + */ + +#include "engine_fwd.h" + +namespace Engine +{ + typedef std::vector<int16_t> Scope; + + class LIBAMAROK_EXPORT Base : public QObject, public Amarok::Plugin + { + Q_OBJECT + + signals: + /** Emitted when end of current track is reached. */ + void trackEnded(); + + /** Transmits status message, the message disappears after ~2s. */ + void statusText( const QString& ); + + /** + * Shows a long message in a non-invasive manner, you should prefer + * this over KMessageBoxes, but do use KMessageBox when you must + * interrupt the user or the message is very important. + */ + void infoMessage( const QString& ); + + /** Transmits metadata package. */ + void metaData( const Engine::SimpleMetaBundle& ); + + /** Signals that a SYNC has been recieved, and new last.fm data needs to be downloaded */ + void lastFmTrackChange(); + + /** Signals a change in the engine's state. */ + void stateChanged( Engine::State ); + + /** Shows Amarok config dialog at specified page */ + void showConfigDialog( const QCString& ); + + public: + virtual ~Base(); + + /** + * Initializes the engine. Must be called after the engine was loaded. + * @return True if initialization was successful. + */ + virtual bool init() = 0; + + /** + * Determines if the engine is able to play a given URL. + * @param url The URL of the file/stream. + * @return True if we can play the URL. + */ + virtual bool canDecode( const KURL &url ) const = 0; + + /** + * Determines if current track is a stream. + * @return True if track is a stream. + */ + inline bool isStream() { return m_isStream; } + + /** + * Load new track for playing. + * @param url URL to be played. + * @param stream True if URL is a stream. + * @return True for success. + */ + virtual bool load( const KURL &url, bool stream = false ); + + /** + * Load new track and start Playback. Convenience function for Amarok to use. + * @param url URL to be played. + * @param stream True if URL is a stream. + * @return True for success. + */ + bool play( const KURL &u, bool stream = false ) { return load( u, stream ) && play(); } + + /** + * Start playback. + * @param offset Start playing at @p msec position. + * @return True for success. + */ + virtual bool play( uint offset = 0 ) = 0; + + /** Stops playback */ + virtual void stop() = 0; + + /** Pauses playback */ + virtual void pause() = 0; + + /** Resumes playback if paused */ + virtual void unpause() = 0; + + /** + * Get current engine status. + * @return the correct State as described at the enum + */ + virtual State state() const = 0; + + /** Get time position (msec). */ + virtual uint position() const = 0; + + /** Get track length (msec). */ + virtual uint length() const { return 0; } + + /** + * Jump to new time position. + * @param ms New position. + */ + virtual void seek( uint ms ) = 0; + + /** + * Determines whether media is currently loaded. + * @return True if media is loaded, system is ready to play. + */ + inline bool loaded() const { return state() != Empty; } + + inline uint volume() const { return m_volume; } + + /** + * Fetch the current audio sample buffer. + * @return Audio sample buffer. + */ + virtual const Scope &scope() { return m_scope; }; + + /** + * Set new volume value. + * @param value Volume in range 0 to 100. + */ + void setVolume( uint value ); + + /** Set new crossfade length (msec) */ + void setXfadeLength( int value ) { m_xfadeLength = value; } + + /** Set whether to crossfade the next track + * Used when the engine is switching tracks automatically + * instead of manually. + */ + void setXFadeNextTrack( bool enable ) { m_xfadeNextTrack = enable; } + + /** Set whether equalizer is enabled + * You don't need to cache the parameters, setEqualizerParameters is called straight after this + * function, _always_. + */ + virtual void setEqualizerEnabled( bool ) {}; + + /** Set equalizer parameters, all in range -100..100, where 0 = no adjustment + * @param preamp the preamplification value + * @param bandGains a list of 10 integers, ascending in frequency, the exact frequencies you amplify + * are not too-important at this time + */ + virtual void setEqualizerParameters( int /*preamp*/, const QValueList<int> &/*bandGains*/ ) {}; + + + /** Tries to retrieve metadata for the given url (called only if url + * is not in the collection). The intended usage is to retrieve + * information for AudiCD tracks when they are added to the playlist + * (i.e. before they are actually played) + * @param url the url of the item + * @param bundle the SimpleMetaBundle to fill + * @return true if metadata found, false otherwise + */ + virtual bool metaDataForUrl(const KURL &, Engine::SimpleMetaBundle &) + { return false; } + + /** returns true if this engine performs some special action to play + * audio cds: in this case, the KURL::List is filled with the urls of + * the songs in the cd... + * + * @param device the cdrom device , with QString::null meaning use engine-specific default value + * @param urls the list of urls for AudioCD tracks to fill + * @return true if the engine has the feature of reading from audio cds, false otherwise (note that this should return true also in case of error if the engine is capable of reading audio cds in general...) + * */ + virtual bool getAudioCDContents(const QString &, KURL::List &) + { return false; } + + /** + * Whether amarok_proxy.rb is needed for last.fm files. + * @return true if engine doesn't handle 'SYNC' messages in the stream from last.fm. + False if (like >=libxine-1.1.8) it does. + */ + virtual bool lastFmProxyRequired() { return true; } + + /** flush the current stream buffer */ + virtual bool flushBuffer() { return false; } + + /** allow the engine to perform necessary work on changes in the playlist **/ + virtual void playlistChanged() { }; + + protected: + Base(); + + /** Shows the Amarok configuration dialog at the engine page */ + void showEngineConfigDialog() { emit showConfigDialog( "Engine" ); } + + virtual void setVolumeSW( uint percent ) = 0; + + /** Converts master volume to a logarithmic scale */ + static uint makeVolumeLogarithmic( uint volume ); + + Base( const Base& ); //disable copy constructor + const Base &operator=( const Base& ); //disable copy constructor + + int m_xfadeLength; + bool m_xfadeNextTrack; + + protected: + static const int SCOPESIZE = 1024; + uint m_volume; + KURL m_url; + Scope m_scope; + bool m_isStream; + }; + + + class SimpleMetaBundle { + public: + QString title; + QString artist; + QString album; + QString comment; + QString genre; + QString bitrate; + QString samplerate; + QString length; + QString year; + QString tracknr; + }; +} + +#endif diff --git a/amarok/src/enginecontroller.cpp b/amarok/src/enginecontroller.cpp new file mode 100644 index 00000000..e16ab2f0 --- /dev/null +++ b/amarok/src/enginecontroller.cpp @@ -0,0 +1,802 @@ +/*************************************************************************** + * Copyright (C) 2004 Frederik Holljen <fh@ez.no> * + * (C) 2004,5 Max Howell <max.howell@methylblue.com> * + * (C) 2004,5 Mark Kretschmann * + * (C) 2006 Ian Monroe * + * * + * 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. * + * * + ***************************************************************************/ + +#define DEBUG_PREFIX "controller" + +#include "amarok.h" +#include "amarokconfig.h" +#include "debug.h" +#include "enginebase.h" +#include "enginecontroller.h" +#include "lastfm.h" +#include "mediabrowser.h" +#include "playlist.h" +#include "playlistloader.h" +#include "pluginmanager.h" +#include "statusbar.h" + +#include <qfile.h> +#include <qobjectlist.h> +#include <qtimer.h> + +#include <kapplication.h> +#include <kfileitem.h> +#include <kio/global.h> +#include <kio/job.h> +#include <kmessagebox.h> +#include <krun.h> + +#include <cstdlib> + + +EngineController::ExtensionCache EngineController::s_extensionCache; + + +EngineController* +EngineController::instance() +{ + //will only be instantiated the first time this function is called + //will work with the inline directive + static EngineController Instance; + + return &Instance; +} + + +EngineController::EngineController() + : m_engine( 0 ) + , m_voidEngine( 0 ) + , m_delayTime( 0 ) + , m_muteVolume( 0 ) + , m_xFadeThisTrack( false ) + , m_timer( new QTimer( this ) ) + , m_playFailureCount( 0 ) + , m_lastFm( false ) + , m_positionOffset( 0 ) + , m_lastPositionOffset( 0 ) +{ + m_voidEngine = m_engine = loadEngine( "void-engine" ); + + connect( m_timer, SIGNAL( timeout() ), SLOT( slotMainTimer() ) ); +} + +EngineController::~EngineController() +{ + DEBUG_FUNC_INFO //we like to know when singletons are destroyed +} + + +////////////////////////////////////////////////////////////////////////////////////////// +// PUBLIC +////////////////////////////////////////////////////////////////////////////////////////// + +EngineBase* +EngineController::loadEngine() //static +{ + /// always returns a valid pointer to EngineBase + + DEBUG_BLOCK + //TODO remember song position, and resume playback + + // new engine, new ext cache required + extensionCache().clear(); + + if( m_engine != m_voidEngine ) { + EngineBase *oldEngine = m_engine; + + // we assign this first for thread-safety, + // EngineController::engine() must always return an engine! + m_engine = m_voidEngine; + + // we unload the old engine first because there are a number of + // bugs associated with keeping one engine loaded while loading + // another, eg xine-engine can't init(), and aRts-engine crashes + PluginManager::unload( oldEngine ); + + // the engine is not required to do this when we unload it but + // we need to do it to ensure Amarok looks correct. + // We don't do this for the void-engine because that + // means Amarok sets all components to empty on startup, which is + // their responsibility. + slotStateChanged( Engine::Empty ); + } + + m_engine = loadEngine( AmarokConfig::soundSystem() ); + + const QString engineName = PluginManager::getService( m_engine )->property( "X-KDE-Amarok-name" ).toString(); + + if( !AmarokConfig::soundSystem().isEmpty() && engineName != AmarokConfig::soundSystem() ) { + //AmarokConfig::soundSystem() is empty on the first-ever-run + + Amarok::StatusBar::instance()->longMessage( i18n( + "Sorry, the '%1' could not be loaded, instead we have loaded the '%2'." ) + .arg( AmarokConfig::soundSystem() ) + .arg( engineName ), + KDE::StatusBar::Sorry ); + + AmarokConfig::setSoundSystem( engineName ); + } + + // Important: Make sure soundSystem is not empty + if( AmarokConfig::soundSystem().isEmpty() ) + AmarokConfig::setSoundSystem( engineName ); + + return m_engine; +} + +#include <qvaluevector.h> +EngineBase* +EngineController::loadEngine( const QString &engineName ) +{ + /// always returns a valid plugin (exits if it can't get one) + + DEBUG_BLOCK + + QString query = "[X-KDE-Amarok-plugintype] == 'engine' and [X-KDE-Amarok-name] != '%1'"; + KTrader::OfferList offers = PluginManager::query( query.arg( engineName ) ); + + // sort by rank, QValueList::operator[] is O(n), so this is quite inefficient + #define rank( x ) (x)->property( "X-KDE-Amarok-rank" ).toInt() + for( int n = offers.count()-1, i = 0; i < n; i++ ) + for( int j = n; j > i; j-- ) + if( rank( offers[j] ) > rank( offers[j-1] ) ) + qSwap( offers[j], offers[j-1] ); + #undef rank + + // this is the actual engine we want + query = "[X-KDE-Amarok-plugintype] == 'engine' and [X-KDE-Amarok-name] == '%1'"; + offers = PluginManager::query( query.arg( engineName ) ) + offers; + + foreachType( KTrader::OfferList, offers ) { + Amarok::Plugin *plugin = PluginManager::createFromService( *it ); + + if( plugin ) { + QObject *bar = Amarok::StatusBar::instance(); + EngineBase *engine = static_cast<EngineBase*>( plugin ); + + connect( engine, SIGNAL(stateChanged( Engine::State )), + this, SLOT(slotStateChanged( Engine::State )) ); + connect( engine, SIGNAL(trackEnded()), + this, SLOT(slotTrackEnded()) ); + if( bar ) + { + connect( engine, SIGNAL(statusText( const QString& )), + bar, SLOT(shortMessage( const QString& )) ); + connect( engine, SIGNAL(infoMessage( const QString& )), + bar, SLOT(longMessage( const QString& )) ); + } + connect( engine, SIGNAL(metaData( const Engine::SimpleMetaBundle& )), + this, SLOT(slotEngineMetaData( const Engine::SimpleMetaBundle& )) ); + connect( engine, SIGNAL(showConfigDialog( const QCString& )), + kapp, SLOT(slotConfigAmarok( const QCString& )) ); + + if( engine->init() ) + return engine; + else + warning() << "Could not init() an engine\n"; + } + } + + KRun::runCommand( "kbuildsycoca" ); + + KMessageBox::error( 0, i18n( + "<p>Amarok could not find any sound-engine plugins. " + "Amarok is now updating the KDE configuration database. Please wait a couple of minutes, then restart Amarok.</p>" + "<p>If this does not help, " + "it is likely that Amarok is installed under the wrong prefix, please fix your installation using:<pre>" + "$ cd /path/to/amarok/source-code/<br>" + "$ su -c \"make uninstall\"<br>" + "$ ./configure --prefix=`kde-config --prefix` && su -c \"make install\"<br>" + "$ kbuildsycoca<br>" + "$ amarok</pre>" + "More information can be found in the README file. For further assistance join us at #amarok on irc.freenode.net.</p>" ) ); + + // don't use QApplication::exit, as the eventloop may not have started yet + std::exit( EXIT_SUCCESS ); + + // Not executed, just here to prevent compiler warning + return 0; +} + + +bool EngineController::canDecode( const KURL &url ) //static +{ + //NOTE this function must be thread-safe + //TODO a KFileItem version? <- presumably so we can mimetype check + + const QString fileName = url.fileName(); + const QString ext = Amarok::extension( fileName ); + + if ( PlaylistFile::isPlaylistFile( fileName ) ) return false; + + // Ignore protocols "fetchcover" and "musicbrainz", they're not local but we don't really want them in the playlist :) + if ( url.protocol() == "fetchcover" || url.protocol() == "musicbrainz" ) return false; + + // Accept non-local files, since we can't test them for validity at this point + // TODO actually, only accept unconditionally http stuff + // TODO this actually makes things like "Blarrghgjhjh:!!!" automatically get inserted + // into the playlist + // TODO remove for Amarok 1.3 and above silly checks, instead check for http type servers + if ( !url.isLocalFile() ) return true; + + // If extension is already in the cache, return cache result + if ( extensionCache().contains( ext ) ) + return s_extensionCache[ext]; + + // If file has 0 bytes, ignore it and return false, not to infect the cache with corrupt files. + // TODO also ignore files that are too small? + KFileItem f( KFileItem::Unknown, KFileItem::Unknown, url, false ); + if ( !f.size() ) + return false; + + const bool valid = engine()->canDecode( url ); + + if( engine() != EngineController::instance()->m_voidEngine ) + { + //we special case this as otherwise users hate us + if ( !valid && ext.lower() == "mp3"){ + QCustomEvent * e = new QCustomEvent( 2000 ); + QApplication::postEvent( Amarok::StatusBar::instance(), e ); + } + + // Cache this result for the next lookup + if ( !ext.isEmpty() ) + extensionCache().insert( ext, valid ); + } + + return valid; +} + +void EngineController::unplayableNotification() { + + if( !installDistroCodec(AmarokConfig::soundSystem())) + Amarok::StatusBar::instance()->longMessageThreadSafe( + i18n( "<p>The %1 claims it <b>cannot</b> play MP3 files." + "<p>You may want to choose a different engine from the <i>Configure Dialog</i>, or examine " + "the installation of the multimedia-framework that the current engine uses. " + "<p>You may find useful information in the <i>FAQ</i> section of the <i>Amarok HandBook</i>." ) + .arg( AmarokConfig::soundSystem() ), KDE::StatusBar::Error ); +} + +bool EngineController::installDistroCodec( const QString& engine /*Filetype type*/) +{ + KService::Ptr service = KTrader::self()->query( "Amarok/CodecInstall" + , QString("[X-KDE-Amarok-codec] == 'mp3' and [X-KDE-Amarok-engine] == '%1'").arg(engine) ).first(); + if( service ) + { + QString installScript = service->exec(); + if( !installScript.isNull() ) //just a sanity check + { + KGuiItem installButton( i18n( "Install MP3 Support" ) ); + if(KMessageBox::questionYesNo(PlaylistWindow::self() + , i18n("Amarok currently cannot play MP3 files.") + , i18n( "No MP3 Support" ) + , installButton + , KStdGuiItem::no() + , "codecInstallWarning" ) == KMessageBox::Yes ) + { + KRun::runCommand(installScript); + return true; + } + } + } +return false; +} + +void EngineController::restoreSession() +{ + //here we restore the session + //however, do note, this is always done, KDE session management is not involved + + if( !AmarokConfig::resumeTrack().isEmpty() ) + { + const KURL url = AmarokConfig::resumeTrack(); + + play( MetaBundle( url ), AmarokConfig::resumeTime() ); + } +} + + +void EngineController::endSession() +{ + //only update song stats, when we're not going to resume it + if ( !AmarokConfig::resumePlayback() ) + { + trackEnded( trackPosition(), m_bundle.length() * 1000, "quit" ); + } + + PluginManager::unload( m_voidEngine ); + m_voidEngine = 0; +} + +////////////////////////////////////////////////////////////////////////////////////////// +// PUBLIC SLOTS +////////////////////////////////////////////////////////////////////////////////////////// + +void EngineController::previous() //SLOT +{ + emit orderPrevious(); +} + + +void EngineController::next( bool forceNext ) //SLOT +{ + m_previousUrl = m_bundle.url(); + m_isTiming = false; + emit orderNext(forceNext); +} + + +void EngineController::play() //SLOT +{ + if ( m_engine->state() == Engine::Paused ) + { + m_engine->unpause(); + } + else emit orderCurrent(); +} + + +void EngineController::play( const MetaBundle &bundle, uint offset ) +{ + DEBUG_BLOCK + + KURL url = bundle.url(); + // don't destroy connection if we need to change station + if( url.protocol() != "lastfm" && LastFm::Controller::instance()->isPlaying() ) + { + m_engine->stop(); + LastFm::Controller::instance()->playbackStopped(); + } + m_lastFm = false; + //Holds the time since we started trying to play non-existent files + //so we know when to abort + static QTime failure_time; + if ( !m_playFailureCount ) + failure_time.start(); + + debug() << "Loading URL: " << url.url() << endl; + m_lastMetadata.clear(); + + //TODO bummer why'd I do it this way? it should _not_ be in play! + //let Amarok know that the previous track is no longer playing + if ( m_timer->isActive() ) + trackEnded( trackPosition(), m_bundle.length() * 1000, "change" ); + + if ( url.isLocalFile() ) { + // does the file really exist? the playlist entry might be old + if ( ! QFile::exists( url.path()) ) { + //debug() << " file >" << url.path() << "< does not exist!" << endl; + Amarok::StatusBar::instance()->shortMessage( i18n("Local file does not exist.") ); + goto some_kind_of_failure; + } + } + else + { + if( url.protocol() == "cdda" ) + Amarok::StatusBar::instance()->shortMessage( i18n("Starting CD Audio track...") ); + else + Amarok::StatusBar::instance()->shortMessage( i18n("Connecting to stream source...") ); + debug() << "Connecting to protocol: " << url.protocol() << endl; + } + + // WebDAV protocol is HTTP with extensions (and the "webdav" scheme + // is a KDE-ism anyway). Most engines cope with HTTP streaming, but + // not through KIO, so they don't support KDE-isms. + if ( url.protocol() == "webdav" ) + url.setProtocol( "http" ); + else if ( url.protocol() == "webdavs" ) + url.setProtocol( "https" ); + + // streams from last.fm should be handled by our proxy, in order to authenticate with the server + else if ( url.protocol() == "lastfm" ) + { + if( LastFm::Controller::instance()->isPlaying() ) + { + if (LastFm::Controller::instance()->changeStation( url.url() ) == -1) + // Request was canceled, return immediately. + return; + connect( m_engine, SIGNAL( lastFmTrackChange() ), LastFm::Controller::instance()->getService() + , SLOT( requestMetaData() ) ); + connect( LastFm::Controller::instance()->getService(), SIGNAL( metaDataResult( const MetaBundle& ) ), + this, SLOT( slotStreamMetaData( const MetaBundle& ) ) ); + return; + } + else + { + url = LastFm::Controller::instance()->getNewProxy( url.url(), m_engine->lastFmProxyRequired() ); + if( url.isEmpty() ) + goto some_kind_of_failure; + else if ( !url.isValid() && url.url() == "lastfm://" ) + // Request was canceled, return immediately. + return; + + m_lastFm = true; + connect( m_engine, SIGNAL( lastFmTrackChange() ), LastFm::Controller::instance()->getService() + , SLOT( requestMetaData() ) ); + connect( LastFm::Controller::instance()->getService(), SIGNAL( metaDataResult( const MetaBundle& ) ), + this, SLOT( slotStreamMetaData( const MetaBundle& ) ) ); + } + debug() << "New URL is " << url.url() << endl; + } + else if (url.protocol() == "daap" ) + { + KURL newUrl = MediaBrowser::instance()->getProxyUrl( url ); + if( !newUrl.isEmpty() ) + { + debug() << newUrl << endl; + url = newUrl; + } + else + return; + } + + if( m_engine->load( url, url.protocol() == "http" || url.protocol() == "rtsp" ) ) + { + //assign bundle now so that it is available when the engine + //emits stateChanged( Playing ) + if( !m_bundle.url().path().isEmpty() ) //wasn't playing before + m_previousUrl = m_bundle.url(); + else + m_previousUrl = bundle.url(); + m_bundle = bundle; + + if( m_engine->play( offset ) ) + { + //Reset failure count as we are now successfully playing a song + m_playFailureCount = 0; + + // Ask engine for track length, if available. It's more reliable than TagLib. + const uint trackLength = m_engine->length() / 1000; + if ( trackLength ) m_bundle.setLength( trackLength ); + + m_xFadeThisTrack = !m_engine->isStream() && !(url.protocol() == "cdda") && + m_bundle.length()*1000 - offset - AmarokConfig::crossfadeLength()*2 > 0; + + newMetaDataNotify( m_bundle, true /* track change */ ); + return; + } + } + + some_kind_of_failure: + debug() << "Failed to play this track." << endl; + + ++m_playFailureCount; + + //Code to skip to next track if playback fails: + // + //* The failure counter is reset if a track plays successfully or if playback is + // stopped, for whatever reason. + //* For normal playback, the attempt to play is stopped at the end of the playlist + //* For repeat playlist , a whole playlist worth of songs is tried + //* For repeat album, the number of songs tried is the number of tracks from the + // album that are in the playlist. + //* For repeat track, no attempts are made + //* For the nmm engine, no attempts are made (necessary? / FIXME) + //* To prevent GUI freezes we don't try to play again after 0.5s of failure + int totalTracks = Playlist::instance()->totalTrackCount(); + int currentTrack = Playlist::instance()->currentTrackIndex(); + if ( ( ( Amarok::repeatPlaylist() && static_cast<int>(m_playFailureCount) < totalTracks ) + || ( Amarok::repeatNone() && currentTrack != totalTracks - 1 ) + || ( Amarok::repeatAlbum() && m_playFailureCount < Playlist::instance()->repeatAlbumTrackCount() ) ) + && AmarokConfig::soundSystem() != "nmm-engine" + && failure_time.elapsed() < 500 ) + { + + debug() << "Skipping to next track." << endl; + + // The test for loaded must be done _before_ next is called + if ( !m_engine->loaded() ) + { + //False gives behaviour as if track played successfully + next( false ); + QTimer::singleShot( 0, this, SLOT(play()) ); + } + else + { + //False gives behaviour as if track played successfully + next( false ); + } + } + else + { + //Stop playback, including resetting failure count (as all new failures are + //treated as independent after playback is stopped) + stop(); + } +} + + +void EngineController::pause() //SLOT +{ + if ( m_engine->loaded() && !LastFm::Controller::instance()->isPlaying() ) + m_engine->pause(); +} + + +void EngineController::stop() //SLOT +{ + //Reset failure counter as after stop, everything else is unrelated + m_playFailureCount = 0; + + //let Amarok know that the previous track is no longer playing + trackEnded( trackPosition(), m_bundle.length() * 1000, "stop" ); + + //Remove requirement for track to be loaded for stop to be called (fixes gltiches + //where stop never properly happens if call to m_engine->load fails in play) + //if ( m_engine->loaded() ) + m_engine->stop(); +} + + +void EngineController::playPause() //SLOT +{ + //this is used by the TrayIcon, PlayPauseAction and DCOP + + if( m_engine->state() == Engine::Playing ) + { + pause(); + } + else if( m_engine->state() == Engine::Paused ) + { + if ( m_engine->loaded() ) + m_engine->unpause(); + } + else + play(); +} + + +void EngineController::seek( int ms ) //SLOT +{ + if( bundle().length() > 0 ) + { + trackPositionChangedNotify( ms, true ); /* User seek */ + engine()->seek( ms ); + } +} + + +void EngineController::seekRelative( int ms ) //SLOT +{ + if( m_engine->state() != Engine::Empty ) + { + int newPos = m_engine->position() + ms; + seek( newPos <= 0 ? 1 : newPos ); + } +} + + +void EngineController::seekForward( int ms ) +{ + seekRelative( ms ); +} + + +void EngineController::seekBackward( int ms ) +{ + seekRelative( -ms ); +} + + +int EngineController::increaseVolume( int ticks ) //SLOT +{ + return setVolume( m_engine->volume() + ticks ); +} + + +int EngineController::decreaseVolume( int ticks ) //SLOT +{ + return setVolume( m_engine->volume() - ticks ); +} + + +int EngineController::setVolume( int percent ) //SLOT +{ + m_muteVolume = 0; + + if( percent < 0 ) percent = 0; + if( percent > 100 ) percent = 100; + + if( (uint)percent != m_engine->volume() ) + { + m_engine->setVolume( (uint)percent ); + + percent = m_engine->volume(); + AmarokConfig::setMasterVolume( percent ); + volumeChangedNotify( percent ); + return percent; + } + else // Still notify + { + volumeChangedNotify( percent ); + } + + return m_engine->volume(); +} + + +void EngineController::mute() //SLOT +{ + if( m_muteVolume == 0 ) + { + int saveVolume = m_engine->volume(); + setVolume( 0 ); + m_muteVolume = saveVolume; + } + else + { + setVolume( m_muteVolume ); + m_muteVolume = 0; + } +} + + +const MetaBundle& +EngineController::bundle() const +{ + static MetaBundle null; + return m_engine->state() == Engine::Empty ? null : m_bundle; +} + + +void EngineController::slotStreamMetaData( const MetaBundle &bundle ) //SLOT +{ + // Prevent spamming by ignoring repeated identical data (some servers repeat it every 10 seconds) + if ( m_lastMetadata.contains( bundle ) ) + return; + + // We compare the new item with the last two items, because mth.house currently cycles + // two messages alternating, which gets very annoying + if ( m_lastMetadata.count() == 2 ) + m_lastMetadata.pop_front(); + + m_lastMetadata << bundle; + + m_previousUrl = m_bundle.url(); + m_bundle = bundle; + m_lastPositionOffset = m_positionOffset; + if( m_lastFm ) + m_positionOffset = m_engine->position(); + else + m_positionOffset = 0; + newMetaDataNotify( m_bundle, false /* not a new track */ ); +} + +void EngineController::currentTrackMetaDataChanged( const MetaBundle& bundle ) +{ + m_previousUrl = m_bundle.url(); + m_bundle = bundle; + newMetaDataNotify( bundle, false /* no track change */ ); +} + +////////////////////////////////////////////////////////////////////////////////////////// +// PRIVATE SLOTS +////////////////////////////////////////////////////////////////////////////////////////// + +void EngineController::slotEngineMetaData( const Engine::SimpleMetaBundle &simpleBundle ) //SLOT +{ + if ( !m_bundle.url().isLocalFile() ) + { + MetaBundle bundle = m_bundle; + bundle.setArtist( simpleBundle.artist ); + bundle.setTitle( simpleBundle.title ); + bundle.setComment( simpleBundle.comment ); + bundle.setAlbum( simpleBundle.album ); + + if( !simpleBundle.genre.isEmpty() ) + bundle.setGenre( simpleBundle.genre ); + if( !simpleBundle.bitrate.isEmpty() ) + bundle.setBitrate( simpleBundle.bitrate.toInt() ); + if( !simpleBundle.samplerate.isEmpty() ) + bundle.setSampleRate( simpleBundle.samplerate.toInt() ); + if( !simpleBundle.year.isEmpty() ) + bundle.setYear( simpleBundle.year.toInt() ); + if( !simpleBundle.tracknr.isEmpty() ) + bundle.setTrack( simpleBundle.tracknr.toInt() ); + + slotStreamMetaData( bundle ); + } +} + + +void EngineController::slotMainTimer() //SLOT +{ + const uint position = trackPosition(); + + trackPositionChangedNotify( position ); + + // Crossfading + if ( m_engine->state() == Engine::Playing && + AmarokConfig::crossfade() && m_xFadeThisTrack && + m_engine->hasPluginProperty( "HasCrossfade" ) && + Playlist::instance()->stopAfterMode() != Playlist::StopAfterCurrent && + ( (uint) AmarokConfig::crossfadeType() == 0 || //Always or... + (uint) AmarokConfig::crossfadeType() == 1 ) && //...automatic track change only + Playlist::instance()->isTrackAfter() && + m_bundle.length()*1000 - position < (uint) AmarokConfig::crossfadeLength() ) + { + debug() << "Crossfading to next track...\n"; + m_engine->setXFadeNextTrack( true ); + trackFinished(); + } + else if ( m_engine->state() == Engine::Playing && + AmarokConfig::fadeout() && + Playlist::instance()->stopAfterMode() == Playlist::StopAfterCurrent && + m_bundle.length()*1000 - position < (uint) AmarokConfig::fadeoutLength() ) + { + m_engine->stop(); + } +} + + +void EngineController::slotTrackEnded() //SLOT +{ + if ( AmarokConfig::trackDelayLength() > 0 ) + { + //FIXME not perfect + if ( !m_isTiming ) + { + QTimer::singleShot( AmarokConfig::trackDelayLength(), this, SLOT(trackFinished()) ); + m_isTiming = true; + } + + } + else trackFinished(); +} + + +void EngineController::slotStateChanged( Engine::State newState ) //SLOT +{ + + switch( newState ) + { + case Engine::Empty: + + //FALL THROUGH... + + case Engine::Paused: + + m_timer->stop(); + break; + + case Engine::Playing: + + m_timer->start( MAIN_TIMER ); + break; + + default: + ; + } + + stateChangedNotify( newState ); +} + +uint EngineController::trackPosition() const +{ + const uint buffertime = 5000; // worked for me with xine engine over 1 mbit dsl + if( !m_engine ) + return 0; + uint pos = m_engine->position(); + if( !m_lastFm ) + return pos; + + if( m_positionOffset + buffertime <= pos ) + return pos - m_positionOffset - buffertime; + if( m_lastPositionOffset + buffertime <= pos ) + return pos - m_lastPositionOffset - buffertime; + return pos; +} + + +#include "enginecontroller.moc" diff --git a/amarok/src/enginecontroller.h b/amarok/src/enginecontroller.h new file mode 100644 index 00000000..b85af21d --- /dev/null +++ b/amarok/src/enginecontroller.h @@ -0,0 +1,143 @@ +/*************************************************************************** + * Copyright (C) 2004 Frederik Holljen <fh@ez.no> * + * (C) 2004,5 Max Howell <max.howell@methylblue.com> * + * (C) 2004,5 Mark Kretschmann * + * * + * 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. * + * * + ***************************************************************************/ + +#ifndef AMAROK_ENGINECONTROLLER_H +#define AMAROK_ENGINECONTROLLER_H + +#include "enginebase.h" +#include "engineobserver.h" +#include "metabundle.h" + +#include <qmap.h> +#include <qobject.h> +#include <qvaluelist.h> + +class QTimer; + +namespace KIO { class Job; } + + +/** + * This class captures Amarok specific behaviour for some common features. + * Accessing the engine directly is perfectly legal but on your own risk. + * TODO: Hide proxy stuff! + */ + +class EngineController : public QObject, public EngineSubject +{ + Q_OBJECT + +public: + typedef QMap<QString, bool> ExtensionCache; + + // plugins have their own static space, so calling instance + // from a plugin won't do any good. you'll only get a new + // instance with a voidEngine + static EngineController* instance(); + static EngineBase* engine() { return instance()->m_engine; } + static bool canDecode( const KURL& ); + static ExtensionCache& extensionCache() { return s_extensionCache; } + static QString engineProperty( const QString& key ) { return engine()->pluginProperty( key ); } + static bool hasEngineProperty( const QString& key ) { return engine()->hasPluginProperty( key ); } + + uint trackPosition() const; + + EngineBase* loadEngine(); + void unplayableNotification(); + + uint trackLength() const { return m_bundle.length() * 1000; } + const MetaBundle &bundle() const; + KURL previousURL() const { return m_previousUrl; } + KURL playingURL() const { return bundle().url(); } + + void restoreSession(); + void endSession(); + + void updateBundleRating( const int rating ) { m_bundle.setRating(rating); } //Can't update metabundle rating from bundle(), d'oh + + //xx000, xx100, xx200, so at most will be 200ms delay before time displays are updated + static const int MAIN_TIMER = 300; + + /*enum Filetype { MP3 };*/ //assuming MP3 for time being + LIBAMAROK_EXPORT static bool installDistroCodec(const QString& engine /*Filetype type*/); + +public slots: + void previous(); + // forceNext make we go to next track even if Repeat Track is on + //NOTE If the track ended normally, call next(false) ! + void next( const bool forceNext = true ); + void trackFinished() { next(false); }; + void play(); + void play( const MetaBundle&, uint offset = 0 ); + void pause(); + void stop(); + void playPause(); //pauses if playing, plays if paused or stopped + + void seek( int ms ); + void seekRelative( int ms ); + void seekForward( int ms = 10000 ); + void seekBackward( int ms = 10000 ); + + int increaseVolume( int ticks = 100/25 ); + int decreaseVolume( int ticks = 100/25 ); + int setVolume( int percent ); + + void mute(); + + void playlistChanged() { m_engine->playlistChanged(); } + + void slotStreamMetaData( const MetaBundle &bundle ); + void currentTrackMetaDataChanged( const MetaBundle& bundle ); + +signals: + void orderPrevious(); + void orderCurrent(); + void orderNext( const bool ); + void statusText( const QString& ); + +private slots: + void slotEngineMetaData( const Engine::SimpleMetaBundle& ); + void slotMainTimer(); + void slotTrackEnded(); + void slotStateChanged( Engine::State ); + +protected: + EngineController(); + ~EngineController(); + + // undefined + EngineController( const EngineController& ); + EngineController &operator=( const EngineController& ); + +private: + static ExtensionCache s_extensionCache; + + EngineBase* loadEngine( const QString &engineName ); + + EngineBase* m_engine; + EngineBase* m_voidEngine; + MetaBundle m_bundle; + KURL m_previousUrl; + BundleList m_lastMetadata; + long m_delayTime; + int m_muteVolume; + bool m_xFadeThisTrack; + bool m_isTiming; + QTimer* m_timer; + uint m_playFailureCount; + // try to correct start time for tracks from last.fm streams + bool m_lastFm; + uint m_positionOffset, m_lastPositionOffset; +}; + + +#endif diff --git a/amarok/src/engineobserver.cpp b/amarok/src/engineobserver.cpp new file mode 100644 index 00000000..082a0c48 --- /dev/null +++ b/amarok/src/engineobserver.cpp @@ -0,0 +1,151 @@ +/*************************************************************************** + engineobserver.cpp - Observer pattern for engine + ------------------- +begin : Mar 14 2003 +copyright : (C) 2003 by Frederik Holljen +email : fh@ez.no +***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#include "debug.h" +#include "collectiondb.h" +#include "engineobserver.h" +#include "metabundle.h" +#include "podcastbundle.h" +#include <qptrlist.h> + + +////////////////////////////////////////////////////////////////////////////////////////// +/// CLASS EngineObserver +////////////////////////////////////////////////////////////////////////////////////////// + +EngineObserver::EngineObserver() + : m_subject( 0 ) +{} + +EngineObserver::EngineObserver( EngineSubject *s ) + : m_subject( s ) +{ + m_subject->attach( this ); +} + +EngineObserver::~EngineObserver() +{ + if ( m_subject ) + m_subject->detach( this ); +} + + +////////////////////////////////////////////////////////////////////////////////////////// +/// CLASS EngineSubject +////////////////////////////////////////////////////////////////////////////////////////// + +EngineSubject::EngineSubject() + : m_oldEngineState( Engine::Empty ) +{} + +EngineSubject::~EngineSubject() +{} + + +void EngineSubject::stateChangedNotify( Engine::State state ) +{ + DEBUG_BLOCK + + QPtrListIterator<EngineObserver> it( Observers ); + EngineObserver *observer; + while( ( observer = it.current() ) != 0 ) + { + ++it; + observer->engineStateChanged( state, m_oldEngineState ); + } + + m_oldEngineState = state; +} + + +void EngineSubject::newMetaDataNotify( const MetaBundle &bundle, bool trackChanged ) +{ + DEBUG_BLOCK + + QPtrListIterator<EngineObserver> it( Observers ); + EngineObserver *observer; + + PodcastEpisodeBundle peb; + MetaBundle b( bundle ); + if( CollectionDB::instance()->getPodcastEpisodeBundle( bundle.url(), &peb ) ) + { + b.setPodcastBundle( peb ); + } + + while( ( observer = it.current() ) != 0 ) + { + ++it; + observer->engineNewMetaData( b, trackChanged ); + } +} + + +void EngineSubject::trackEnded( int finalPosition, int trackLength, const QString &reason ) +{ + for( QPtrListIterator<EngineObserver> it( Observers ); *it; ++it ) + (*it)->engineTrackEnded( finalPosition, trackLength, reason ); +} + + +void EngineSubject::volumeChangedNotify( int percent ) +{ + QPtrListIterator<EngineObserver> it( Observers ); + EngineObserver *observer; + while( ( observer = it.current() ) != 0 ) + { + ++it; + observer->engineVolumeChanged( percent ); + } +} + + +void EngineSubject::trackPositionChangedNotify( long position, bool userSeek ) +{ + QPtrListIterator<EngineObserver> it( Observers ); + EngineObserver *observer; + while( ( observer = it.current() ) != 0 ) + { + ++it; + observer->engineTrackPositionChanged( position, userSeek ); + } +} + + +void EngineSubject::trackLengthChangedNotify( long length ) +{ + QPtrListIterator<EngineObserver> it( Observers ); + EngineObserver *observer; + while( ( observer = it.current() ) != 0 ) + { + ++it; + observer->engineTrackLengthChanged( length ); + } +} + + +void EngineSubject::attach( EngineObserver *observer ) +{ + if( !observer || Observers.find( observer ) != -1 ) + return; + Observers.append( observer ); +} + + +void EngineSubject::detach( EngineObserver *observer ) +{ + if( Observers.find( observer ) != -1 ) Observers.remove(); +} diff --git a/amarok/src/engineobserver.h b/amarok/src/engineobserver.h new file mode 100644 index 00000000..a48419a4 --- /dev/null +++ b/amarok/src/engineobserver.h @@ -0,0 +1,76 @@ +/*************************************************************************** + engineobserver.h - Observer pattern for engine + ------------------- +begin : Mar 14 2003 +copyright : (C) 2003 by Frederik Holljen +email : fh@ez.no +***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#ifndef AMAROK_ENGINEOBSERVER_H +#define AMAROK_ENGINEOBSERVER_H + +#include "engine_fwd.h" + +class EngineSubject; +class MetaBundle; +class QString; + +/** + * if you want to observe the engine, inherit from this class and attach yourself to + * the engine with attach + * Note that all positional information and times are in milliseconds + */ +class EngineObserver +{ +public: + EngineObserver(); + EngineObserver( EngineSubject* ); + virtual ~EngineObserver(); + virtual void engineStateChanged( Engine::State /*state*/, Engine::State /*oldState*/ = Engine::Empty ) {} + virtual void engineNewMetaData( const MetaBundle &/*bundle*/, bool /*trackChanged*/ ) {} + virtual void engineTrackEnded( int /*finalPosition*/, int /*trackLength*/, const QString &/*reason*/ ) {} + virtual void engineVolumeChanged( int /*percent*/ ) {} + virtual void engineTrackPositionChanged( long /*position*/ , bool /*userSeek*/ ) {} + virtual void engineTrackLengthChanged( long /*length*/ ) {} + +private: + EngineSubject *m_subject; +}; + +#include <qptrlist.h> +/** + * Inherited by EngineController. + * Notify observer functionality is captured in this class. + */ +class EngineSubject +{ +public: + void attach( EngineObserver *observer ); + void detach( EngineObserver *observer ); + +protected: + EngineSubject(); + virtual ~EngineSubject(); + void stateChangedNotify( Engine::State /*state*/ ); + void newMetaDataNotify( const MetaBundle &/*bundle*/, bool /*trackChanged*/ ); + void trackEnded( int /*finalPosition*/, int /*trackLength*/, const QString &reason ); + void volumeChangedNotify( int /*percent*/ ); + /* userSeek means the position didn't change due to normal playback */ + void trackPositionChangedNotify( long /*position*/ , bool userSeek=false ); + void trackLengthChangedNotify( long /*length*/ ); + +private: + QPtrList<EngineObserver> Observers; + Engine::State m_oldEngineState; +}; + +#endif // AMAROK_ENGINEOBSERVER_H diff --git a/amarok/src/equalizergraph.cpp b/amarok/src/equalizergraph.cpp new file mode 100644 index 00000000..3714f4d3 --- /dev/null +++ b/amarok/src/equalizergraph.cpp @@ -0,0 +1,203 @@ +/*************************************************************************** + Graphical spline display for equalizer + + (c) 2004 Mark Kretschmann <markey@web.de> + (c) 2005 Markus Brueffer <markus@brueffer.de> + Based on code from XMMS + (c) 1998-2000 Peter Alm, Mikael Alm, Olle Hallnas, Thomas Nilsson and 4Front Technologies +***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#include "amarokconfig.h" +#include "equalizergraph.h" + +#include <qpainter.h> +#include <qpixmap.h> +#include <qvaluelist.h> + +#include <kapplication.h> + +EqualizerGraph::EqualizerGraph( QWidget* parent ) + : QWidget( parent, 0, Qt::WNoAutoErase ) + , m_backgroundPixmap( new QPixmap() ) + , m_composePixmap( new QPixmap() ) +{ +} + + +EqualizerGraph::~EqualizerGraph() +{ + delete m_backgroundPixmap; + delete m_composePixmap; +} + + +///////////////////////////////////////////////////////////////////////////////////// +// PROTECTED +///////////////////////////////////////////////////////////////////////////////////// + +void +EqualizerGraph::resizeEvent( QResizeEvent* ) +{ + drawBackground(); +} + +QSize +EqualizerGraph::sizeHint() const +{ + return QSize( 100, 60 ); +} + +void +EqualizerGraph::paintEvent( QPaintEvent* ) +{ + bitBlt( m_composePixmap, 0, 0, m_backgroundPixmap ); + + QPainter p( m_composePixmap ); + + // Draw middle line + int middleLineY = (int) ( ( height() - 1 ) / 2.0 + AmarokConfig::equalizerPreamp() * ( height() - 1 ) / 200.0 ); + QPen pen( colorGroup().dark(), 0, Qt::DotLine); + p.setPen( pen ); + p.drawLine( 8, middleLineY, width() - 1, middleLineY ); + + QColor color( colorGroup().highlight() ); + int h, s, v; + color.getHsv( &h, &s, &v ); + + int i, y, ymin, ymax, py = 0; + float x[NUM_BANDS], yf[NUM_BANDS]; + float gains[NUM_BANDS] = { 0, 0, 0, 0, 0, 0, 0, 0, 0}; + + // Don't calculate 0 and NUM_BANDS-1 for accuracy reasons + for ( i = 1; i < NUM_BANDS -1 ; i++) + x[i] = ( width() - 8 ) * i / ( NUM_BANDS -1 ) + 8; + x[ 0 ] = 8; + x[ NUM_BANDS - 1 ] = width() - 1; + + if ( AmarokConfig::equalizerEnabled() ) + for ( i = 0; i < NUM_BANDS; i++ ) + gains[i] = ( height() - 1 ) * AmarokConfig::equalizerGains()[i] / 200.0; + + init_spline( x, gains, NUM_BANDS, yf ); + + for ( i = 8; i < width(); i++ ) { + y = (int) ( ( height() - 1 ) / 2 - eval_spline( x, gains, yf, NUM_BANDS, i ) ); + + if ( y < 0 ) + y = 0; + + if ( y > height() - 1 ) + y = height() - 1; + + if ( i == 8 ) + py = y; + + if ( y < py ) { + ymin = y; + ymax = py; + } else { + ymin = py; + ymax = y; + } + + py = y; + for ( y = ymin; y <= ymax; y++ ) { + // Absolute carthesian coordinate + s = y - ( height() - 1 ) / 2; + s = QABS(s); + + // Normalise to a base of 256 + // short for: s / ( ( height() / 2.0 ) * 255; + s = (int) ( s * 510.0 / height() ); + + color.setHsv( h, 255 - s, v ); + p.setPen( color ); + + p.drawPoint( i, y ); + } + } + + p.end(); + bitBlt( this, 0, 0, m_composePixmap ); +} + + +///////////////////////////////////////////////////////////////////////////////////// +// PRIVATE +///////////////////////////////////////////////////////////////////////////////////// + +void +EqualizerGraph::drawBackground() +{ + m_backgroundPixmap->resize( size() ); + m_composePixmap->resize( size() ); + + m_backgroundPixmap->fill( colorGroup().background().dark( 105 ) ); + QPainter p( m_backgroundPixmap ); + + // Erase background for scale + p.fillRect( 0, 0, 7, height() -1, colorGroup().background()); + + // Draw scale + p.setPen( colorGroup().shadow() ); + p.drawLine( 7, 0, 7, height() - 1 ); + p.drawLine( 0, 0, 7, 0 ); + p.drawLine( 0, height() / 2 - 1, 7, height() / 2 - 1 ); + p.drawLine( 0, height() - 1, 7, height() - 1 ); +} + + +void +EqualizerGraph::init_spline( float* x, float* y, int n, float* y2 ) +{ + int i, k; + float p, qn, sig, un; + QMemArray<float> u(n * sizeof(float)); + + y2[ 0 ] = u[ 0 ] = 0.0; + + for ( i = 1; i < n - 1; i++ ) { + sig = ( (float)x[i] - x[i-1] ) / ( (float)x[i+1] - x[i-1] ); + p = sig * y2[i-1] + 2.0; + y2[i] = ( sig - 1.0 ) / p; + u[i] = ( ( (float)y[i+1] - y[i] ) / ( x[i+1] - x[i] ) ) - ( ( (float)y[i] - y[i-1] ) / ( x[i] - x[i-1] ) ); + u[i] = ( 6.0 * u[i] / ( x[i+1] - x[i-1] ) - sig * u[i-1] ) / p; + } + qn = un = 0.0; + + y2[n-1] = ( un - qn * u[n-2] ) / ( qn * y2[n-2] + 1.0 ); + for ( k = n - 2; k >= 0; k-- ) + y2[k] = y2[k] * y2[k+1] + u[k]; +} + + +float +EqualizerGraph::eval_spline( float xa[], float ya[], float y2a[], int n, float x ) +{ + int klo, khi, k; + float h, b, a; + + klo = 0; + khi = n - 1; + while ( khi - klo > 1 ) { + k = ( khi + klo ) >> 1; + if ( xa[k] > x ) + khi = k; + else + klo = k; + } + h = xa[khi] - xa[klo]; + a = ( xa[khi] - x ) / h; + b = ( x - xa[klo] ) / h; + return ( a * ya[klo] + b * ya[khi] + ( ( a*a*a - a ) * y2a[klo] + ( b*b*b - b ) * y2a[khi] ) * ( h*h ) / 6.0 ); +} + diff --git a/amarok/src/equalizergraph.h b/amarok/src/equalizergraph.h new file mode 100644 index 00000000..df76824f --- /dev/null +++ b/amarok/src/equalizergraph.h @@ -0,0 +1,50 @@ +/*************************************************************************** + Graphical spline display for equalizer + + (c) 2004 Mark Kretschmann <markey@web.de> + Based on code from XMMS + (c) 1998-2000 Peter Alm, Mikael Alm, Olle Hallnas, Thomas Nilsson and 4Front Technologies +***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#ifndef AMAROK_EQUALIZERGRAPH_H +#define AMAROK_EQUALIZERGRAPH_H + +#include <qwidget.h> //baseclass + +class QPixmap; + + +class EqualizerGraph : public QWidget +{ + public: + EqualizerGraph( QWidget* parent ); + ~EqualizerGraph(); + QSize sizeHint() const; + + protected: + void resizeEvent( QResizeEvent* ); + void paintEvent( QPaintEvent* ); + + private: + static const int NUM_BANDS = 10; + + void drawBackground(); + + void init_spline( float* x, float* y, int n, float* y2 ); + float eval_spline( float xa[], float ya[], float y2a[], int n, float x ); + + QPixmap* m_backgroundPixmap; + QPixmap* m_composePixmap; +}; + + +#endif /*AMAROK_EQUALIZERGRAPH_H*/ diff --git a/amarok/src/equalizerpresetmanager.cpp b/amarok/src/equalizerpresetmanager.cpp new file mode 100644 index 00000000..62c3ffb9 --- /dev/null +++ b/amarok/src/equalizerpresetmanager.cpp @@ -0,0 +1,196 @@ +/*************************************************************************** + * Copyright (C) 2005 by Markus Brueffer <markus@brueffer.de> * + * * + * 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 program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#include "equalizerpresetmanager.h" + +#include <qdom.h> +#include <qfile.h> +#include <qlayout.h> +#include <qpushbutton.h> +#include <qvbox.h> + +#include <kapplication.h> +#include <kinputdialog.h> +#include <klistview.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <kstandarddirs.h> //locate() + +EqualizerPresetManager::EqualizerPresetManager( QWidget *parent, const char *name ) + : KDialogBase( parent, name, true, i18n("Presets"), Ok | Cancel | Default, Ok, true ) +{ + QWidget *mainWidget = new QWidget( this ); + setMainWidget( mainWidget ); + QHBoxLayout *mainLayout = new QHBoxLayout( mainWidget, 0, spacingHint() ); + + m_presetsView = new KListView( mainWidget, "presetListView" ); + m_presetsView->addColumn( i18n( "Presets" ) ); + m_presetsView->setFullWidth( true ); + connect(m_presetsView, SIGNAL( selectionChanged() ), SLOT( updateButtonState() )); + connect(m_presetsView, SIGNAL( doubleClicked ( QListViewItem*, const QPoint&, int ) ), SLOT( slotRename() )); + mainLayout->addWidget( m_presetsView ); + + QVBoxLayout* buttonsLayout = new QVBoxLayout( mainLayout ); + + m_renameBtn = new QPushButton( i18n("&Rename"), mainWidget, "renameBtn" ); + m_deleteBtn = new QPushButton( i18n("&Delete"), mainWidget, "deleteBtn" ); + + buttonsLayout->addWidget( m_renameBtn ); + buttonsLayout->addWidget( m_deleteBtn ); + + connect(m_renameBtn, SIGNAL( clicked() ), SLOT( slotRename() )); + connect(m_deleteBtn, SIGNAL( clicked() ), SLOT( slotDelete() )); + connect(this, SIGNAL( defaultClicked() ), SLOT( slotDefault() )); + + QSpacerItem* spacer = new QSpacerItem( 20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding ); + buttonsLayout->addItem( spacer ); + + updateButtonState(); + + resize( QSize(300, 250).expandedTo(minimumSizeHint()) ); +} + + +EqualizerPresetManager::~EqualizerPresetManager() +{ +} + +void +EqualizerPresetManager::setPresets(QMap< QString, QValueList<int> > presets) +{ + if ( presets.empty() ) + return; + + m_presets = presets; + m_presetsView->clear(); + + QMap< QString, QValueList<int> >::Iterator end = presets.end(); + for ( QMap< QString, QValueList<int> >::Iterator it = presets.begin(); it != end; ++it ) + if ( it.key() != i18n( "Zero" ) && it.key() != i18n( "Manual" ) ) // Don't add 'Manual' and 'Zero' + new KListViewItem( m_presetsView, it.key() ); +} + +QMap< QString, QValueList<int> > +EqualizerPresetManager::presets() +{ + return m_presets; +} + +void +EqualizerPresetManager::slotRename() +{ + bool ok; + QListViewItem* item = m_presetsView->selectedItem(); + const QString title = KInputDialog::getText( i18n("Rename Equalizer Preset"), + i18n("Enter new preset name:"), item->text(0), &ok, this); + + if ( ok && item->text(0) != title ) { + // Check if the new preset title exists + if ( m_presets.find( title ) != m_presets.end() ) { + int button = KMessageBox::warningYesNo( this, i18n( "A preset with the name %1 already exists. Overwrite?" ).arg( title ) ); + + if ( button != KMessageBox::Yes ) + return; + } + + m_presets[ title ] = m_presets[ item->text(0)]; + m_presets.remove( item->text(0) ); + item->setText(0, title); + } +} + +void +EqualizerPresetManager::slotDefault() +{ + int button = KMessageBox::warningYesNo( this, i18n( "All presets will be deleted and defaults will be restored. Are you sure?" ) ); + + if ( button != KMessageBox::Yes ) + return; + + // Preserve the 'Manual' preset + QValueList<int> manualGains = m_presets[ i18n("Manual") ]; + + // Delete all presets + m_presets.clear(); + + // Create predefined presets 'Zero' and 'Manual' + QValueList<int> zeroGains; + zeroGains << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0; + m_presets[ i18n("Zero") ] = zeroGains; + m_presets[ i18n("Manual") ] = manualGains; + + // Load the default presets + QFile file( locate( "data", "amarok/data/equalizer_presets.xml" ) ); + + QTextStream stream( &file ); + stream.setEncoding( QTextStream::UnicodeUTF8 ); + + QDomDocument d; + + if( !file.open( IO_ReadOnly ) || !d.setContent( stream.read() ) ) + return; + + QDomNode n = d.namedItem( "equalizerpresets" ).namedItem("preset"); + + for( ; !n.isNull(); n = n.nextSibling() ) + { + QDomElement e = n.toElement(); + QString title = e.attribute( "name" ); + + QValueList<int> gains; + gains << e.namedItem( "b0" ).toElement().text().toInt(); + gains << e.namedItem( "b1" ).toElement().text().toInt(); + gains << e.namedItem( "b2" ).toElement().text().toInt(); + gains << e.namedItem( "b3" ).toElement().text().toInt(); + gains << e.namedItem( "b4" ).toElement().text().toInt(); + gains << e.namedItem( "b5" ).toElement().text().toInt(); + gains << e.namedItem( "b6" ).toElement().text().toInt(); + gains << e.namedItem( "b7" ).toElement().text().toInt(); + gains << e.namedItem( "b8" ).toElement().text().toInt(); + gains << e.namedItem( "b9" ).toElement().text().toInt(); + + m_presets[ title ] = gains; + } + + file.close(); + + // Update listview + setPresets( m_presets ); +} + +void +EqualizerPresetManager::slotDelete() +{ + QListViewItem* item = m_presetsView->selectedItem(); + + m_presets.remove( item->text(0) ); + + delete item; +} + +void +EqualizerPresetManager::updateButtonState() +{ + bool selected = ( m_presetsView->selectedItem() != 0 ); + + m_deleteBtn->setEnabled( selected ); + m_renameBtn->setEnabled( selected ); +} + +#include "equalizerpresetmanager.moc" diff --git a/amarok/src/equalizerpresetmanager.h b/amarok/src/equalizerpresetmanager.h new file mode 100644 index 00000000..17dc7882 --- /dev/null +++ b/amarok/src/equalizerpresetmanager.h @@ -0,0 +1,59 @@ +/*************************************************************************** + * Copyright (C) 2005 by Markus Brueffer <markus@brueffer.de> * + * * + * 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 program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#ifndef AMAROK_EQUALIZERPRESETMANAGER_H +#define AMAROK_EQUALIZERPRESETMANAGER_H + +#include "equalizersetup.h" + +#include <kdialogbase.h> //baseclass + +class QPushButton; +class QStringList; +class KListView; + +class EqualizerPresetManager : public KDialogBase +{ + Q_OBJECT + + public: + EqualizerPresetManager( QWidget *parent = 0, const char *name = 0 ); + virtual ~EqualizerPresetManager(); + + void setPresets(QMap< QString, QValueList<int> > presets); + QMap< QString, QValueList<int> > presets(); + + private slots: + void slotRename(); + void slotDelete(); + void slotDefault(); + + void updateButtonState(); + + private: + QMap< QString, QValueList<int> > m_presets; + KListView* m_presetsView; + + //QPushButton* m_addBtn; + QPushButton* m_renameBtn; + QPushButton* m_deleteBtn; +}; + + +#endif /* AMAROK_EQUALIZERPRESETMANAGER_H */ diff --git a/amarok/src/equalizersetup.cpp b/amarok/src/equalizersetup.cpp new file mode 100644 index 00000000..ec1066c6 --- /dev/null +++ b/amarok/src/equalizersetup.cpp @@ -0,0 +1,495 @@ +/*************************************************************************** + Setup dialog for the equalizer + + (c) 2004 Mark Kretschmann <markey@web.de> + (c) 2005 Seb Ruiz <me@sebruiz.net> + (c) 2005 Markus Brueffer <markus@brueffer.de> +***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#include "amarok.h" +#include "amarokconfig.h" +#include "enginebase.h" +#include "enginecontroller.h" +#include "equalizergraph.h" +#include "equalizerpresetmanager.h" +#include "equalizersetup.h" +#include "sliderwidget.h" + +#include <qcheckbox.h> +#include <qdom.h> +#include <qfile.h> +#include <qgroupbox.h> +#include <qlabel.h> +#include <qlayout.h> +#include <qpushbutton.h> +#include <qstringlist.h> +#include <qtextstream.h> //presets +#include <qtooltip.h> +#include <qvbox.h> + +#include <kapplication.h> +#include <kcombobox.h> +#include <kiconloader.h> +#include <kinputdialog.h> //presets +#include <klocale.h> +#include <kmessagebox.h> +#include <kpopupmenu.h> +#include <kstandarddirs.h> //locate() +#include <kwin.h> + +EqualizerSetup* EqualizerSetup::s_instance = 0; + + +EqualizerSetup::EqualizerSetup() + : KDialogBase( Amarok::mainWindow(), 0, false, 0, 0, Ok, false ) +{ + using Amarok::Slider; + + s_instance = this; + + kapp->setTopWidget( this ); + setCaption( kapp->makeStdCaption( i18n( "Equalizer" ) ) ); + + // Gives the window a small title bar, and skips a taskbar entry + KWin::setType( winId(), NET::Utility ); + KWin::setState( winId(), NET::SkipTaskbar ); + + QVBox* vbox = makeVBoxMainWidget(); + vbox->setSpacing( KDialog::spacingHint() ); + + // BEGIN Presets + QHBox* presetBox = new QHBox( vbox ); + presetBox->setSpacing( KDialog::spacingHint() ); + + new QLabel( i18n("Presets:"), presetBox ); + + m_presetCombo = new KComboBox( presetBox ); + m_presetCombo->setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Preferred ) ); + + QPushButton* presetAdd = new QPushButton( presetBox ); + presetAdd->setIconSet( SmallIconSet( Amarok::icon( "add_playlist" ) ) ); + QToolTip::add( presetAdd, i18n("Add new preset") ); + connect( presetAdd, SIGNAL( clicked() ), SLOT( addPreset() ) ); + + QPushButton* presetConf = new QPushButton( presetBox ); + presetConf->setIconSet( SmallIconSet( Amarok::icon( "configure" ) ) ); + QToolTip::add( presetConf, i18n("Manage presets") ); + connect( presetConf, SIGNAL( clicked() ), SLOT( editPresets() ) ); + + loadPresets(); + connect( m_presetCombo, SIGNAL( activated(int) ), SLOT( presetChanged(int) ) ); + // END Presets + + // BEGIN GroupBox + m_groupBoxSliders = new QGroupBox( 1, Qt::Vertical, i18n("Enable Equalizer"), vbox ); + m_groupBoxSliders->setCheckable( true ); + m_groupBoxSliders->setChecked( AmarokConfig::equalizerEnabled() ); + m_groupBoxSliders->setInsideMargin( KDialog::marginHint() ); + connect( m_groupBoxSliders, SIGNAL( toggled( bool ) ), SLOT( setEqualizerEnabled( bool ) ) ); + + // Helper widget for layouting inside the groupbox + QWidget* slidersLayoutWidget = new QWidget( m_groupBoxSliders ); + slidersLayoutWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + QGridLayout* slidersGridLayout = new QGridLayout( slidersLayoutWidget, 1, 1, 0, KDialog::spacingHint() ); + // END GroupBox + + // BEGIN Preamp slider + m_slider_preamp = new Slider( Qt::Vertical, slidersLayoutWidget, 100 ); + m_slider_preamp->setMinValue( -100 ); + m_slider_preamp->setTickmarks( QSlider::Right ); + m_slider_preamp->setTickInterval( 100 ); + connect( m_slider_preamp, SIGNAL( valueChanged( int ) ), SLOT( setEqualizerParameters() ) ); + slidersGridLayout->addMultiCellWidget(m_slider_preamp, 0, 0, 0, 1, Qt::AlignHCenter ); + + QLabel* preampLabel = new QLabel( i18n("Pre-amp"), slidersLayoutWidget ); + slidersGridLayout->addMultiCellWidget(preampLabel, 1, 1 , 0, 1, Qt::AlignHCenter ); + // END + + // BEGIN Band Sliders + const char *bandLabels[] = { "30", "60", "125", "250", "500", "1k", "2k", "4k", "8k", "16k" }; + + int minWidth = 0; + QFontMetrics fm = fontMetrics(); //apparently it's an expensive call + for ( int i = 0; i < 10; i++ ) { + int w = fm.width( bandLabels[i] ); + if ( w > minWidth ) + minWidth = w; + } + + for ( int i = 0; i < 10; i++ ) { + Slider *slider = new Slider( Qt::Vertical, slidersLayoutWidget ); + QLabel *label = new QLabel( bandLabels[i], slidersLayoutWidget ); + + slider->setMinValue( -100 ); + slider->setMaxValue( +100 ); + slider->setMinimumWidth( minWidth ); + slidersGridLayout->addMultiCellWidget(slider, 0, 0, 2 * i + 2, 2 * i + 3, Qt::AlignHCenter ); + slidersGridLayout->addMultiCellWidget(label, 1, 1, 2 * i + 2, 2 * i + 3, Qt::AlignHCenter ); + m_bandSliders.append( slider ); + + connect( slider, SIGNAL( valueChanged( int ) ), SLOT( setEqualizerParameters() ) ); + connect( slider, SIGNAL( valueChanged( int ) ), SLOT( sliderChanged() ) ); + } + // END + + // BEGIN Equalizer Graph Widget + QGroupBox* graphGBox = new QGroupBox( 2, Qt::Horizontal, 0, vbox ); + graphGBox->setInsideMargin( KDialog::marginHint() ); + + QVBox* graphVBox = new QVBox( graphGBox ); + QLabel* graphLabel1 = new QLabel("+20 db", graphVBox); + QLabel* graphLabel2 = new QLabel("0 db", graphVBox); + QLabel* graphLabel3 = new QLabel("-20 db", graphVBox); + graphLabel1->setAlignment( Qt::AlignRight | Qt::AlignTop ); + graphLabel2->setAlignment( Qt::AlignRight | Qt::AlignVCenter ); + graphLabel3->setAlignment( Qt::AlignRight | Qt::AlignBottom ); + + m_equalizerGraph = new EqualizerGraph( graphGBox ); + m_equalizerGraph->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ); + // END Graph Widget + + // Fill the combobox + updatePresets( AmarokConfig::equalizerPreset() ); + + // make sure to restore the current preamp value + m_slider_preamp->setValue( AmarokConfig::equalizerPreamp() ); + + // Init sliders + presetChanged( AmarokConfig::equalizerPreset() ); +} + + +EqualizerSetup::~EqualizerSetup() +{ + savePresets(); + s_instance = 0; +} + +void +EqualizerSetup::setActive( bool active ) +{ + m_groupBoxSliders->setChecked( active ); +} + +void +EqualizerSetup::setBands( int preamp, QValueList<int> gains ) +{ + m_slider_preamp->setValue( preamp ); + + // Note: As a side effect, this automatically switches the + // preset to 'Manual', which is by intention + for ( uint i = 0; i < m_bandSliders.count(); i++ ) + m_bandSliders.at(i)->setValue( ( *gains.at(i) ) ); + + setEqualizerParameters(); +} + +void +EqualizerSetup::setPreset( QString name ) +{ + // Look for the preset id and verify name + int i, count = m_presetCombo->count(); + bool found = false; + for( i = 0; i < count; i++ ) { + if ( m_presetCombo->text( i ) == name ) { + found = true; + break; + } + } + + if ( found ) { + m_presetCombo->setCurrentItem( i ); + presetChanged( name ); + } +} + +///////////////////////////////////////////////////////////////////////////////////// +// EQUALIZER PRESETS +///////////////////////////////////////////////////////////////////////////////////// + +QString +EqualizerSetup::presetsCache() const +{ + // returns the playlists stats cache file + return Amarok::saveLocation() + "equalizerpresets_save.xml"; +} + + +void +EqualizerSetup::loadPresets() +{ + // Create predefined presets 'Zero' and 'Manual' + QValueList<int> zeroGains; + zeroGains << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0; + m_presets[ i18n("Manual") ] = zeroGains; + m_presets[ i18n("Zero") ] = zeroGains; + + QFile file( presetsCache() ); + if ( !file.exists() ) + file.setName( locate( "data", "amarok/data/equalizer_presets.xml" ) ); + + QTextStream stream( &file ); + stream.setEncoding( QTextStream::UnicodeUTF8 ); + + QDomDocument d; + + if( !file.open( IO_ReadOnly ) || !d.setContent( stream.read() ) ) { + // Everything went wrong, so at least provide the two predefined presets + updatePresets( AmarokConfig::equalizerPreset() ); + return; + } + + QDomNode n = d.namedItem( "equalizerpresets" ).namedItem("preset"); + + for( ; !n.isNull(); n = n.nextSibling() ) + { + QDomElement e = n.toElement(); + QString title = e.attribute( "name" ); + + QValueList<int> gains; + gains << e.namedItem( "b0" ).toElement().text().toInt(); + gains << e.namedItem( "b1" ).toElement().text().toInt(); + gains << e.namedItem( "b2" ).toElement().text().toInt(); + gains << e.namedItem( "b3" ).toElement().text().toInt(); + gains << e.namedItem( "b4" ).toElement().text().toInt(); + gains << e.namedItem( "b5" ).toElement().text().toInt(); + gains << e.namedItem( "b6" ).toElement().text().toInt(); + gains << e.namedItem( "b7" ).toElement().text().toInt(); + gains << e.namedItem( "b8" ).toElement().text().toInt(); + gains << e.namedItem( "b9" ).toElement().text().toInt(); + + m_presets[ title ] = gains; + } + + file.close(); +} + + +void +EqualizerSetup::savePresets() +{ + QFile file( presetsCache() ); + + if( !file.open( IO_WriteOnly ) ) return; + + QDomDocument doc; + QDomElement e = doc.createElement("equalizerpresets"); + e.setAttribute( "product", "Amarok" ); + e.setAttribute( "version", APP_VERSION ); + e.setAttribute( "formatversion", "1.1" ); + + doc.appendChild( e ); + + QStringList info; + info << "b0" << "b1" << "b2" << "b3" << "b4" + << "b5" << "b6" << "b7" << "b8" << "b9"; + + for( uint x = 0; x < m_presets.count(); x++ ) + { + const QString title = m_presetCombo->text( x ); + + // don't save the 'Zero' preset + if ( title == i18n("Zero") ) + continue; + + QValueList<int> gains = m_presets[ title ]; + + QDomElement i = doc.createElement("preset"); + i.setAttribute( "name", title ); + + QDomElement attr; + QDomText t; + for( uint y=0; y < info.count(); y++ ) + { + attr = doc.createElement( info[y] ); + t = doc.createTextNode( QString::number( gains.first() ) ); + attr.appendChild( t ); + i.appendChild( attr ); + gains.pop_front(); + } + e.appendChild( i ); + } + + QTextStream stream( &file ); + stream.setEncoding( QTextStream::UnicodeUTF8 ); + stream << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"; + stream << doc.toString(); + file.close(); +} + +void +EqualizerSetup::editPresets() +{ + EqualizerPresetManager * editor = new EqualizerPresetManager(this); + editor->setPresets(m_presets); + + if ( editor->exec() ) { + QMap< QString, QValueList<int> > presets = editor->presets(); + + QString currentTitle = m_presetCombo->currentText(); + QValueList<int> currentGains= m_presets[ currentTitle ]; + + QString newTitle = currentTitle; + + // Check if the selected item was renamed + if ( presets.find( currentTitle ) == presets.end() || currentGains != presets[ currentTitle ] ) { + + // Find the new name + QMap< QString, QValueList<int> >::Iterator end = presets.end(); + for ( QMap< QString, QValueList<int> >::Iterator it = presets.begin(); it != end; ++it ) { + if ( it.data() == currentGains ) { + newTitle = it.key(); + break; + } + } + } + + m_presets = presets; + updatePresets( newTitle ); + } + + delete editor; +} + +void +EqualizerSetup::addPreset() +{ + bool ok; + const QString title = KInputDialog::getText( i18n("Add Equalizer Preset"), + i18n("Enter preset name:"), i18n("Untitled"), &ok, this); + + if (ok) { + // Check if the new preset title exists + if ( m_presets.find( title ) != m_presets.end() ) { + int button = KMessageBox::warningYesNo( this, i18n( "A preset with the name %1 already exists. Overwrite?" ).arg( title ) ); + + if ( button != KMessageBox::Yes ) + return; + } + + // Add the new preset based on the current slider positions + QValueList <int> gains; + for ( uint i = 0; i < m_bandSliders.count(); i++ ) + gains += m_bandSliders.at( i )->value(); + m_presets[ title ] = gains; + + // Rebuild the combobox + updatePresets(title); + + // Save + setEqualizerParameters(); + } +} + +void +EqualizerSetup::updatePresets(QString selectTitle) +{ + // Save the selected item + if ( selectTitle.isEmpty() ) + selectTitle = m_presetCombo->currentText(); + + // Sort titles + QStringList titles; + QMap< QString, QValueList<int> >::Iterator end = m_presets.end(); + for ( QMap< QString, QValueList<int> >::Iterator it = m_presets.begin(); it != end; ++it ) + titles << it.key(); + + titles.sort(); + + // rebuild preset combobox and look for the previously selected title + int i = 0; + int newIndex = -1; + m_presetCombo->clear(); + QStringList::Iterator titlesEnd = titles.end(); + for ( QStringList::Iterator it = titles.begin(); it != titlesEnd; ++it ) { + m_presetCombo->insertItem( *it ); + if ( *it == selectTitle ) + newIndex = i; + if ( *it == i18n("Manual") ) + m_manualPos = i; + i++; + } + + if ( newIndex == -1 ) + newIndex = m_manualPos; + + m_presetCombo->setCurrentItem( newIndex ); +} + +///////////////////////////////////////////////////////////////////////////////////// +// PRIVATE SLOTS +///////////////////////////////////////////////////////////////////////////////////// + +void +EqualizerSetup::presetChanged( int id ) //SLOT +{ + presetChanged( m_presetCombo->text(id) ); +} + +void +EqualizerSetup::presetChanged( QString title ) //SLOT +{ + const QValueList<int> gains = m_presets[ title ]; + + for ( uint i = 0; i < m_bandSliders.count(); i++ ) { + // Block signals to prevent unwanted setting to 'Manual' + m_bandSliders.at(i)->blockSignals(true); + m_bandSliders.at(i)->setValue( ( *gains.at(i) ) ); + m_bandSliders.at(i)->blockSignals(false); + } + + setEqualizerParameters(); +} + +void +EqualizerSetup::setEqualizerEnabled( bool active ) //SLOT +{ + EngineController::engine()->setEqualizerEnabled( active ); + AmarokConfig::setEqualizerEnabled( active ); + + if( active ) + //this way the developer of the eq doesn't have to cache the eq values + setEqualizerParameters(); + else + // zero the graph + m_equalizerGraph->update(); +} + + +void +EqualizerSetup::setEqualizerParameters() //SLOT +{ + AmarokConfig::setEqualizerPreamp( m_slider_preamp->value() ); + AmarokConfig::setEqualizerPreset( m_presetCombo->currentText() ); + AmarokConfig::setEqualizerGains ( m_presets[ m_presetCombo->currentText() ] ); + + // Transfer values to the engine if the EQ is enabled + if ( AmarokConfig::equalizerEnabled() ) + EngineController::engine()->setEqualizerParameters( m_slider_preamp->value(), m_presets[ m_presetCombo->currentText() ] ); + + m_equalizerGraph->update(); +} + + +void +EqualizerSetup::sliderChanged() //SLOT +{ + m_presetCombo->setCurrentItem( m_manualPos ); + + QValueList<int> gains; + for ( uint i = 0; i < m_bandSliders.count(); i++ ) + gains += m_bandSliders.at( i )->value(); + + m_presets[ i18n("Manual") ] = gains; +} + +#include "equalizersetup.moc" diff --git a/amarok/src/equalizersetup.h b/amarok/src/equalizersetup.h new file mode 100644 index 00000000..0be7ee04 --- /dev/null +++ b/amarok/src/equalizersetup.h @@ -0,0 +1,77 @@ +/*************************************************************************** + Setup dialog for the equalizer + + (c) 2004 Mark Kretschmann <markey@web.de> + (c) 2005 Seb Ruiz <me@sebruiz.net> + (c) 2005 Markus Brueffer <markus@brueffer.de> +***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#ifndef AMAROK_EQUALIZERSETUP_H +#define AMAROK_EQUALIZERSETUP_H + +#include <qptrlist.h> //stack alloc +#include <kdialogbase.h> + +class EqualizerGraph; +class QGroupBox; +class QCheckBox; +class KComboBox; +class KPopupMenu; + +namespace Amarok { class Slider; } + + +class EqualizerSetup : public KDialogBase +{ + Q_OBJECT + + public: + static EqualizerSetup* instance() { return s_instance ? s_instance : new EqualizerSetup(); } + static bool isInstantiated() { return s_instance ? true : false; } + + EqualizerSetup(); + ~EqualizerSetup(); + + // for use by DCOP + void setActive( bool active ); + void setBands( int preamp, QValueList<int> gains ); + void setPreset( QString name ); + + private slots: + void presetChanged( int id ); + void presetChanged( QString title ); + void sliderChanged(); + void setEqualizerEnabled( bool ); + void setEqualizerParameters(); + void editPresets(); + void addPreset(); + + private: + static EqualizerSetup* s_instance; + + void loadPresets(); + void savePresets(); + void updatePresets(QString selectTitle = QString::null); + QString presetsCache() const; + + Amarok::Slider* m_slider_preamp; + EqualizerGraph* m_equalizerGraph; + QPtrList<Amarok::Slider> m_bandSliders; + + QGroupBox* m_groupBoxSliders; + KComboBox* m_presetCombo; + uint m_manualPos; + + QMap< QString, QValueList<int> > m_presets; +}; + +#endif /*AMAROK_EQUALIZERSETUP_H*/ diff --git a/amarok/src/expression.cpp b/amarok/src/expression.cpp new file mode 100644 index 00000000..bd120b38 --- /dev/null +++ b/amarok/src/expression.cpp @@ -0,0 +1,191 @@ +/* + Copyright (c) 2006 Gábor Lehel <illissius@gmail.com> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "expression.h" + +ExpressionParser::ExpressionParser( const QString &expression ) + : m_expression( expression ) + , m_state( ExpectMinus ) + , m_haveGroup( false ) + , m_inQuote( false ) + , m_inOrGroup( false ) +{ } + +ParsedExpression ExpressionParser::parse() +{ + const uint length = m_expression.length(); + for( uint pos = 0; pos < length; ++pos ) + parseChar( m_expression.constref( pos ) ); + finishedToken(); + finishedOrGroup(); + return m_parsed; +} + +ParsedExpression ExpressionParser::parse( const QString &expression ) //static +{ + ExpressionParser p( expression ); + return p.parse(); +} + +bool ExpressionParser::isAdvancedExpression( const QString &expression ) //static +{ + return ( expression.contains( '"' ) || + expression.contains( ':' ) || + expression.contains( '-' ) || + expression.contains( "AND" ) || + expression.contains( "OR" ) ); +} + +/* PRIVATE */ + +void ExpressionParser::parseChar( const QChar &c ) +{ + if( m_inQuote && c != '"' ) + m_string += c; + else if( c.isSpace() ) + handleSpace( c ); + else if( c == '-' ) + handleMinus( c ); + else if( c == ':' ) + handleColon( c ); + else if( c == '>' || c == '<' ) + handleMod( c ); + else if( c == '"' ) + handleQuote( c ); + else + handleChar( c ); +} + +void ExpressionParser::handleSpace( const QChar& ) +{ + if( m_state > ExpectMinus ) + finishedToken(); +} + +void ExpressionParser::handleMinus( const QChar &c ) +{ + if( m_state == ExpectMinus ) + { + m_element.negate = true; + m_state = ExpectField; + } + else + handleChar( c ); +} + +void ExpressionParser::handleColon( const QChar &c ) +{ + if( m_state <= ExpectField && !m_string.isEmpty() ) + { + m_element.field = m_string; + m_string = QString::null; + m_state = ExpectMod; + } + else + handleChar( c ); +} + +void ExpressionParser::handleMod( const QChar &c ) +{ + if( m_state == ExpectMod ) + { + m_element.match = ( c == '>' ) ? expression_element::More : expression_element::Less; + m_state = ExpectText; + } + else + handleChar( c ); +} + +void ExpressionParser::handleQuote( const QChar& ) +{ + if( m_inQuote ) + { + finishedElement(); + m_inQuote = false; + } + else + { + if( !m_string.isEmpty() ) + finishedToken(); + m_state = ExpectText; + m_inQuote = true; + } +} + +void ExpressionParser::handleChar( const QChar &c ) +{ + m_string += c; + if( m_state <= ExpectField ) + m_state = ExpectField; + else if( m_state <= ExpectText ) + m_state = ExpectText; +} + +void ExpressionParser::finishedToken() +{ + enum { And, Or, Neither }; + int s; + if( m_haveGroup || !m_element.field.isEmpty() ) + s = Neither; + else if( m_string == "AND" ) + s = And; + else if( m_string == "OR" ) + s = Or; + else + s = Neither; + + if( s == Neither ) + finishedElement(); + else + { + m_haveGroup = true; + + if( s == Or ) + m_inOrGroup = true; + else + finishedOrGroup(); + + m_string = QString::null; + m_state = ExpectMinus; + } +} + +void ExpressionParser::finishedElement() +{ + if( !m_inOrGroup ) + finishedOrGroup(); + m_inOrGroup = m_haveGroup = false; + m_element.text = m_string; + m_string = QString::null; + + if( !m_element.text.isEmpty() || !m_element.field.isEmpty() ) + m_or.append( m_element ); + + m_element = expression_element(); + m_state = ExpectMinus; +} + +void ExpressionParser::finishedOrGroup() +{ + if( !m_or.isEmpty() ) + m_parsed.append( m_or ); + m_or.clear(); + m_inOrGroup = false; +} + diff --git a/amarok/src/expression.h b/amarok/src/expression.h new file mode 100644 index 00000000..e24e7806 --- /dev/null +++ b/amarok/src/expression.h @@ -0,0 +1,72 @@ +/* + Copyright (c) 2006 Gábor Lehel <illissius@gmail.com> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef AMAROK_EXPRESSION_H +#define AMAROK_EXPRESSION_H + +#include <qstring.h> +#include <qvaluevector.h> + +struct expression_element +{ + QString field; + QString text; + bool negate: 1; + enum { Contains, Less, More } match: 2; + expression_element(): negate( false ), match( Contains ) { } +}; +typedef QValueVector<expression_element> or_list; + +typedef QValueVector<or_list> ParsedExpression; + +class ExpressionParser +{ + public: + ExpressionParser( const QString &expression ); + ParsedExpression parse(); + static ParsedExpression parse( const QString &expression ); + + static bool isAdvancedExpression( const QString &expression ); + + private: + void parseChar( const QChar &c ); + void handleSpace( const QChar &c ); + void handleMinus( const QChar &c ); + void handleColon( const QChar &c ); + void handleMod( const QChar &c ); + void handleQuote( const QChar &c ); + void handleChar( const QChar &c ); + void finishedToken(); + void finishedElement(); + void finishedOrGroup(); + + const QString &m_expression; + enum State { ExpectMinus, ExpectField, ExpectMod, ExpectText }; + int m_state; + bool m_haveGroup; + bool m_inQuote; + bool m_inOrGroup; + QString m_string; + expression_element m_element; + or_list m_or; + ParsedExpression m_parsed; +}; + + +#endif diff --git a/amarok/src/fht.cpp b/amarok/src/fht.cpp new file mode 100644 index 00000000..27d377b6 --- /dev/null +++ b/amarok/src/fht.cpp @@ -0,0 +1,242 @@ +// FHT - Fast Hartley Transform Class +// +// Copyright (C) 2004 Melchior FRANZ - mfranz@kde.org +// +// 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 program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA +// +// $Id: fht.cpp 581133 2006-09-05 12:14:35Z seb $ + +#include <math.h> +#include <string.h> +#include "fht.h" + + +FHT::FHT(int n) : + m_buf(0), + m_tab(0), + m_log(0) +{ + if (n < 3) { + m_num = 0; + m_exp2 = -1; + return; + } + m_exp2 = n; + m_num = 1 << n; + if (n > 3) { + m_buf = new float[m_num]; + m_tab = new float[m_num * 2]; + makeCasTable(); + } +} + + +FHT::~FHT() +{ + delete[] m_buf; + delete[] m_tab; + delete[] m_log; +} + + +void FHT::makeCasTable(void) +{ + float d, *costab, *sintab; + int ul, ndiv2 = m_num / 2; + + for (costab = m_tab, sintab = m_tab + m_num / 2 + 1, ul = 0; ul < m_num; ul++) { + d = M_PI * ul / ndiv2; + *costab = *sintab = cos(d); + + costab += 2, sintab += 2; + if (sintab > m_tab + m_num * 2) + sintab = m_tab + 1; + } +} + + +float* FHT::copy(float *d, float *s) +{ + return (float *)memcpy(d, s, m_num * sizeof(float)); +} + + +float* FHT::clear(float *d) +{ + return (float *)memset(d, 0, m_num * sizeof(float)); +} + + +void FHT::scale(float *p, float d) +{ + for (int i = 0; i < (m_num / 2); i++) + *p++ *= d; +} + + +void FHT::ewma(float *d, float *s, float w) +{ + for (int i = 0; i < (m_num / 2); i++, d++, s++) + *d = *d * w + *s * (1 - w); +} + + +void FHT::logSpectrum(float *out, float *p) +{ + int n = m_num / 2, i, j, k, *r; + if (!m_log) { + m_log = new int[n]; + float f = n / log10((double)n); + for (i = 0, r = m_log; i < n; i++, r++) { + j = int(rint(log10(i + 1.0) * f)); + *r = j >= n ? n - 1 : j; + } + } + semiLogSpectrum(p); + *out++ = *p = *p / 100; + for (k = i = 1, r = m_log; i < n; i++) { + j = *r++; + if (i == j) + *out++ = p[i]; + else { + float base = p[k - 1]; + float step = (p[j] - base) / (j - (k - 1)); + for (float corr = 0; k <= j; k++, corr += step) + *out++ = base + corr; + } + } +} + + +void FHT::semiLogSpectrum(float *p) +{ + float e; + power2(p); + for (int i = 0; i < (m_num / 2); i++, p++) { + e = 10.0 * log10(sqrt(*p * .5)); + *p = e < 0 ? 0 : e; + } +} + + +void FHT::spectrum(float *p) +{ + power2(p); + for (int i = 0; i < (m_num / 2); i++, p++) + *p = (float)sqrt(*p * .5); +} + + +void FHT::power(float *p) +{ + power2(p); + for (int i = 0; i < (m_num / 2); i++) + *p++ *= .5; +} + + +void FHT::power2(float *p) +{ + int i; + float *q; + _transform(p, m_num, 0); + + *p = (*p * *p), *p += *p, p++; + + for (i = 1, q = p + m_num - 2; i < (m_num / 2); i++, --q) + *p = (*p * *p) + (*q * *q), p++; +} + + +void FHT::transform(float *p) +{ + if (m_num == 8) + transform8(p); + else + _transform(p, m_num, 0); +} + + +void FHT::transform8(float *p) +{ + float a, b, c, d, e, f, g, h, b_f2, d_h2; + float a_c_eg, a_ce_g, ac_e_g, aceg, b_df_h, bdfh; + + a = *p++, b = *p++, c = *p++, d = *p++; + e = *p++, f = *p++, g = *p++, h = *p; + b_f2 = (b - f) * M_SQRT2; + d_h2 = (d - h) * M_SQRT2; + + a_c_eg = a - c - e + g; + a_ce_g = a - c + e - g; + ac_e_g = a + c - e - g; + aceg = a + c + e + g; + + b_df_h = b - d + f - h; + bdfh = b + d + f + h; + + *p = a_c_eg - d_h2; + *--p = a_ce_g - b_df_h; + *--p = ac_e_g - b_f2; + *--p = aceg - bdfh; + *--p = a_c_eg + d_h2; + *--p = a_ce_g + b_df_h; + *--p = ac_e_g + b_f2; + *--p = aceg + bdfh; +} + + +void FHT::_transform(float *p, int n, int k) +{ + if (n == 8) { + transform8(p + k); + return; + } + + int i, j, ndiv2 = n / 2; + float a, *t1, *t2, *t3, *t4, *ptab, *pp; + + for (i = 0, t1 = m_buf, t2 = m_buf + ndiv2, pp = &p[k]; i < ndiv2; i++) + *t1++ = *pp++, *t2++ = *pp++; + + memcpy(p + k, m_buf, sizeof(float) * n); + + _transform(p, ndiv2, k); + _transform(p, ndiv2, k + ndiv2); + + j = m_num / ndiv2 - 1; + t1 = m_buf; + t2 = t1 + ndiv2; + t3 = p + k + ndiv2; + ptab = m_tab; + pp = p + k; + + a = *ptab++ * *t3++; + a += *ptab * *pp; + ptab += j; + + *t1++ = *pp + a; + *t2++ = *pp++ - a; + + for (i = 1, t4 = p + k + n; i < ndiv2; i++, ptab += j) { + a = *ptab++ * *t3++; + a += *ptab * *--t4; + + *t1++ = *pp + a; + *t2++ = *pp++ - a; + } + memcpy(p + k, m_buf, sizeof(float) * n); +} + diff --git a/amarok/src/fht.h b/amarok/src/fht.h new file mode 100644 index 00000000..9dbab83b --- /dev/null +++ b/amarok/src/fht.h @@ -0,0 +1,119 @@ +// FHT - Fast Hartley Transform Class +// +// Copyright (C) 2004 Melchior FRANZ - mfranz@kde.org +// +// 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 program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA +// +// $Id: fht.h 439804 2005-07-28 23:45:59Z mueller $ + +#ifndef FHT_H +#define FHT_H + +/** + * Implementation of the Hartley Transform after Bracewell's discrete + * algorithm. The algorithm is subject to US patent No. 4,646,256 (1987) + * but was put into public domain by the Board of Trustees of Stanford + * University in 1994 and is now freely available[1]. + * + * [1] Computer in Physics, Vol. 9, No. 4, Jul/Aug 1995 pp 373-379 + */ +class FHT +{ + int m_exp2; + int m_num; + float *m_buf; + float *m_tab; + int *m_log; + + /** + * Create a table of "cas" (cosine and sine) values. + * Has only to be done in the constructor and saves from + * calculating the same values over and over while transforming. + */ + void makeCasTable(); + + /** + * Recursive in-place Hartley transform. For internal use only! + */ + void _transform(float *, int, int); + + public: + /** + * Prepare transform for data sets with @f$2^n@f$ numbers, whereby @f$n@f$ + * should be at least 3. Values of more than 3 need a trigonometry table. + * @see makeCasTable() + */ + FHT(int); + + ~FHT(); + inline int sizeExp() const { return m_exp2; } + inline int size() const { return m_num; } + float *copy(float *, float *); + float *clear(float *); + void scale(float *, float); + + /** + * Exponentially Weighted Moving Average (EWMA) filter. + * @param d is the filtered data. + * @param s is fresh input. + * @param w is the weighting factor. + */ + void ewma(float *d, float *s, float w); + + /** + * Logarithmic audio spectrum. Maps semi-logarithmic spectrum + * to logarithmic frequency scale, interpolates missing values. + * A logarithmic index map is calculated at the first run only. + * @param p is the input array. + * @param out is the spectrum. + */ + void logSpectrum(float *out, float *p); + + /** + * Semi-logarithmic audio spectrum. + */ + void semiLogSpectrum(float *); + + /** + * Fourier spectrum. + */ + void spectrum(float *); + + /** + * Calculates a mathematically correct FFT power spectrum. + * If further scaling is applied later, use power2 instead + * and factor the 0.5 in the final scaling factor. + * @see FHT::power2() + */ + void power(float *); + + /** + * Calculates an FFT power spectrum with doubled values as a + * result. The values need to be multiplied by 0.5 to be exact. + * Note that you only get @f$2^{n-1}@f$ power values for a data set + * of @f$2^n@f$ input values. This is the fastest transform. + * @see FHT::power() + */ + void power2(float *); + + /** + * Discrete Hartley transform of data sets with 8 values. + */ + void transform8(float *); + + void transform(float *); +}; + +#endif diff --git a/amarok/src/filebrowser.cpp b/amarok/src/filebrowser.cpp new file mode 100644 index 00000000..20e47f86 --- /dev/null +++ b/amarok/src/filebrowser.cpp @@ -0,0 +1,710 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Mark Kretschmann <markey@web.de> + Copyright (C) 2003 Alexander Dymo <cloudtemple@mksat.net> + Copyright (C) 2003 Roberto Raggi <roberto@kdevelop.org> + Copyright (C) 2001 Christoph Cullmann <cullmann@kde.org> + Copyright (C) 2001 Joseph Wenninger <jowenn@kde.org> + Copyright (C) 2001 Anders Lund <anders.lund@lund.tdcadsl.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "amarok.h" +#include "browserToolBar.h" +#include "clicklineedit.h" +#include "enginecontroller.h" +#include "filebrowser.h" +#include "k3bexporter.h" + +#include <kaction.h> +#include <kapplication.h> +#include "kbookmarkhandler.h" +#include <kdiroperator.h> +#include <kiconloader.h> +#include <kio/netaccess.h> +#include <klistview.h> +#include <klocale.h> +#include <kpopupmenu.h> +#include <kpushbutton.h> ///@see SearchPane +#include <ktoolbarbutton.h> ///@see ctor +#include <kurlcombobox.h> +#include <kurlcompletion.h> + +#include "mediabrowser.h" +#include "medium.h" +#include "mydirlister.h" +#include "mydiroperator.h" +#include "playlist.h" +#include "playlistbrowser.h" +#include "playlistloader.h" +#include "playlistwindow.h" +#include "collectionbrowser.h" +#include "statusbar.h" +#include "tagdialog.h" + +#include <qdir.h> +#include <qhbox.h> +#include <qiconview.h> +#include <qlabel.h> +#include <qtimer.h> +#include <qtooltip.h> + + + +//BEGIN Constructor/destructor + +FileBrowser::FileBrowser( const char * name, Medium * medium ) + : QVBox( 0, name ) +{ + KActionCollection *actionCollection; + SearchPane *searchPane; + + KURL *location; + + // Try to keep filebrowser working even if not in a medium context + // so if a medium object not passed in, keep earlier behavior + if (!medium) { + m_medium = 0; + location = new KURL( Amarok::config( "Filebrowser" )->readPathEntry( "Location", QDir::homeDirPath() ) ); + KFileItem *currentFolder = new KFileItem( KFileItem::Unknown, KFileItem::Unknown, *location ); + //KIO sucks, NetAccess::exists puts up a dialog and has annoying error message boxes + //if there is a problem so there is no point in using it anyways. + //so... setting the diroperator to ~ is the least sucky option + if ( !location->isLocalFile() || !currentFolder->isReadable() ) { + delete location; + location = new KURL( QDir::homeDirPath() ) ; + } + } + else{ + m_medium = medium; + location = new KURL( m_medium->mountPoint() ); + } + + KActionCollection* ac = new KActionCollection( this ); + KStdAction::selectAll( this, SLOT( selectAll() ), ac, "filebrowser_select_all" ); + + KToolBar *toolbar = new Browser::ToolBar( this ); + + { //Filter LineEdit + KToolBar* searchToolBar = new Browser::ToolBar( this ); + KToolBarButton *button = new KToolBarButton( "locationbar_erase", 0, searchToolBar ); + m_filter = new ClickLineEdit( i18n( "Enter search terms here" ), searchToolBar ); + + searchToolBar->setStretchableWidget( m_filter ); + + connect( button, SIGNAL(clicked()), m_filter, SLOT(clear()) ); + + QToolTip::add( button, i18n( "Clear search field" ) ); + QToolTip::add( m_filter, i18n( "Enter space-separated terms to search in the directory-listing" ) ); + } + + { //Directory Listing + QVBox *container; QHBox *box; + + container = new QVBox( this ); + container->setFrameStyle( m_filter->frameStyle() ); + container->setMargin( 3 ); + container->setSpacing( 2 ); + container->setBackgroundMode( Qt::PaletteBase ); + + box = new QHBox( container ); + box->setMargin( 3 ); + box->setBackgroundMode( Qt::PaletteBase ); + + //folder selection combo box + m_combo = new KURLComboBox( KURLComboBox::Directories, true, box, "path combo" ); + + if (!m_medium){ + m_combo->setCompletionObject( new KURLCompletion( KURLCompletion::DirCompletion ) ); + m_combo->setAutoDeleteCompletionObject( true ); + } + m_combo->setMaxItems( 9 ); + m_combo->setURLs( Amarok::config( "Filebrowser" )->readPathListEntry( "Dir History" ) ); + + if (!m_medium) + m_combo->lineEdit()->setText( location->path() ); + else + m_combo->lineEdit()->setText( "/" ); + + //The main widget with file listings and that + m_dir = new MyDirOperator( *location, container, m_medium ); + m_dir->setEnableDirHighlighting( true ); + m_dir->setMode( KFile::Mode((int)KFile::Files | (int)KFile::Directory) ); //allow selection of multiple files + dirs + m_dir->setOnlyDoubleClickSelectsFiles( true ); //Amarok type settings + m_dir->readConfig( Amarok::config( "Filebrowser" ) ); + m_dir->setView( KFile::Default ); //will set userconfigured view, will load URL + m_dir->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Expanding ); + m_dir->setAcceptDrops( true ); + //Automatically open folder after hovering above it...probably a good thing + //but easily disabled by commenting this line out + //Disabled for now because can't show . and .. folders. + //TODO: Find out a way to fix this? + //m_dir->setDropOptions( KFileView::AutoOpenDirs ); + + static_cast<QFrame*>(m_dir->viewWidget())->setFrameStyle( QFrame::NoFrame ); + static_cast<QIconView*>(m_dir->viewWidget())->setSpacing( 1 ); + + actionCollection = m_dir->actionCollection(); + + searchPane = new SearchPane( this ); + + setStretchFactor( container, 2 ); + } + + { + QPopupMenu* const menu = static_cast<KActionMenu*>(actionCollection->action("popupMenu"))->popupMenu(); + + menu->clear(); + menu->insertItem( SmallIconSet( Amarok::icon( "files" ) ), i18n( "&Load" ), MakePlaylist ); + menu->insertItem( SmallIconSet( Amarok::icon( "add_playlist" ) ), i18n( "&Append to Playlist" ), AppendToPlaylist ); + menu->insertItem( SmallIconSet( Amarok::icon( "queue_track" ) ), i18n( "&Queue Track" ), QueueTrack ); + menu->insertItem( SmallIconSet( Amarok::icon( "queue_track" ) ), i18n( "&Queue Tracks" ), QueueTracks ); + + menu->insertItem( SmallIconSet( Amarok::icon( "save" ) ), i18n( "&Save as Playlist..." ), SavePlaylist ); + menu->insertSeparator(); + + if (!m_medium) + menu->insertItem( SmallIconSet( Amarok::icon( "device" ) ), i18n( "&Transfer to Media Device" ), MediaDevice ); + + menu->insertItem( SmallIconSet( Amarok::icon( "collection" ) ), i18n( "&Organize Files..." ), OrganizeFiles ); + menu->insertItem( SmallIconSet( Amarok::icon( "collection" ) ), i18n( "&Copy Files to Collection..." ), CopyToCollection ); + menu->insertItem( SmallIconSet( Amarok::icon( "collection" ) ), i18n( "&Move Files to Collection..." ), MoveToCollection ); + menu->insertItem( SmallIconSet( Amarok::icon( "burn" ) ), i18n("Burn to CD..."), BurnCd ); + menu->insertSeparator(); + menu->insertItem( i18n( "&Select All Files" ), SelectAllFiles ); + menu->insertSeparator(); + actionCollection->action( "delete" )->setIcon( Amarok::icon( "remove" ) ); + actionCollection->action( "delete" )->plug( menu ); + menu->insertSeparator(); + menu->insertItem( SmallIconSet( Amarok::icon( "info" ) ), i18n( "Edit Track &Information..." ), EditTags ); + actionCollection->action( "properties" )->plug( menu ); + + menu->setItemEnabled( BurnCd, K3bExporter::isAvailable() ); + + connect( menu, SIGNAL(aboutToShow()), SLOT(prepareContextMenu()) ); + connect( menu, SIGNAL(activated( int )), SLOT(contextMenuActivated( int )) ); + } + + { + KActionMenu *a; + + a = static_cast<KActionMenu*>( actionCollection->action( "sorting menu" ) ); + a->setIcon( Amarok::icon( "configure" ) ); + a->setDelayed( false ); //TODO should be done by KDirOperator + + actionCollection->action( "delete" )->setShortcut( KShortcut( SHIFT + Key_Delete ) ); + + a = new KActionMenu( i18n("Bookmarks"), "bookmark", actionCollection, "bookmarks" ); + a->setDelayed( false ); + + new KBookmarkHandler( m_dir, a->popupMenu() ); + } + + { + if ( KAction *a = actionCollection->action( "up" ) ) + a->plug( toolbar ); + if ( KAction *a = actionCollection->action( "back" ) ) + a->plug( toolbar ); + if ( KAction *a = actionCollection->action( "forward" ) ) + a->plug( toolbar ); + if ( KAction *a = actionCollection->action( "home" ) ) + a->plug( toolbar ); + if ( KAction *a = actionCollection->action( "reload" ) ) { + a->setIcon( Amarok::icon( "refresh" ) ); + a->plug( toolbar ); + } + + toolbar->insertLineSeparator(); + + if ( KAction *a = actionCollection->action( "short view" ) ) + a->plug( toolbar ); + if ( KAction *a = actionCollection->action( "detailed view" ) ) + a->plug( toolbar ); + + toolbar->insertLineSeparator(); + + if ( KAction *a = actionCollection->action( "sorting menu" ) ) + a->plug( toolbar ); + if ( KAction *a = actionCollection->action( "bookmarks" ) ) + a->plug( toolbar ); + + + KAction *gotoCurrent = new KAction( i18n("Go To Current Track Folder"), Amarok::icon( "music" ), 0, + this, SLOT( gotoCurrentFolder() ), actionCollection ); + gotoCurrent->plug( toolbar ); + + disconnect( actionCollection->action( "up" ), SIGNAL( activated() ), m_dir, SLOT( cdUp() ) ); + connect( actionCollection->action( "up" ), SIGNAL( activated() ), m_dir, SLOT( myCdUp() ) ); + disconnect( actionCollection->action( "home" ), SIGNAL( activated() ), m_dir, SLOT( home() ) ); + connect( actionCollection->action( "home" ), SIGNAL( activated() ), m_dir, SLOT( myHome() ) ); + } + + connect( m_filter, SIGNAL(textChanged( const QString& )), SLOT(setFilter( const QString& )) ); + connect( m_combo, SIGNAL(urlActivated( const KURL& )), SLOT(setUrl( const KURL& )) ); + connect( m_combo, SIGNAL(returnPressed( const QString& )), SLOT(setUrl( const QString& )) ); + connect( m_dir, SIGNAL(viewChanged( KFileView* )), SLOT(slotViewChanged( KFileView* )) ); + connect( m_dir, SIGNAL(fileSelected( const KFileItem* )), SLOT(activate( const KFileItem* )) ); + connect( m_dir, SIGNAL(urlEntered( const KURL& )), SLOT(urlChanged( const KURL& )) ); + connect( m_dir, SIGNAL(urlEntered( const KURL& )), searchPane, SLOT(urlChanged( const KURL& )) ); + connect( m_dir, SIGNAL(dropped( const KFileItem*, QDropEvent*, const KURL::List& )), + SLOT(dropped( const KFileItem*, QDropEvent*, const KURL::List& )) ); + + setSpacing( 4 ); + setFocusProxy( m_dir ); //so the dirOperator is focused when we get focus events + // Toolbar is more than 250px wide, BrowserBar doesn't allow that. -> Resizing issues. + setMinimumWidth( 250 /* toolbar->sizeHint().width() */ ); +} + + +FileBrowser::~FileBrowser() +{ + KConfig* const c = Amarok::config( "Filebrowser" ); + + m_dir->writeConfig( c ); //uses currently set group + + c->writePathEntry( "Location", m_dir->url().url() ); + c->writePathEntry( "Dir History", m_combo->urls() ); +} + +//END Constructor/Destructor + +void FileBrowser::setUrl( const KURL &url ) +{ + m_dir->setFocus(); + if (!m_medium) + m_dir->setURL( url, true ); + else { + QString urlpath = url.isLocalFile() ? url.path() : url.prettyURL(); + KURL newURL( urlpath.prepend( m_medium->mountPoint() ).remove("..") ); + //debug() << "set-url-kurl: changing to: " << newURL.path() << endl; + m_dir->setURL( newURL, true ); + } +} + +void FileBrowser::setUrl( const QString &url ) +{ + if (!m_medium) + m_dir->setURL( KURL(url), true ); + else{ + KURL newURL( QString(url).prepend( m_medium->mountPoint() ).remove("..") ); + //debug() << "set-url-qstring: changing to: " << newURL.path() << endl; + m_dir->setURL( newURL, true ); + } +} + +//BEGIN Private Methods + +KURL::List FileBrowser::selectedItems() +{ + KURL::List list; + for( KFileItemListIterator it( m_dir->selectedItems()->count() ? *m_dir->selectedItems() : *m_dir->view()->items() ); *it; ++it ) + list.append( (*it)->url() ); + + return list; +} + +void FileBrowser::playlistFromURLs( const KURL::List &urls ) +{ + QString suggestion; + if( urls.count() == 1 && QFileInfo( urls.first().path() ).isDir() ) + suggestion = urls.first().fileName(); + else + suggestion = i18n( "Untitled" ); + const QString path = PlaylistDialog::getSaveFileName( suggestion ); + if( path.isEmpty() ) + return; + + if( PlaylistBrowser::savePlaylist( path, urls ) ) + { + //FIXME: uncomment after string freeze + //Amarok::StatusBar::instance()->shortMessage( "Playlist saved to playlist browser" ); + } +} + + +//END Private Methods + + +//BEGIN Public Slots + +void +FileBrowser::setFilter( const QString &text ) +{ + if ( text.isEmpty() ) + m_dir->clearFilter(); + + else { + QString filter; + + const QStringList terms = QStringList::split( ' ', text ); + foreach( terms ) { + filter += '*'; + filter += *it; + } + filter += '*'; + + m_dir->setNameFilter( filter ); + } + + m_dir->updateDir(); +} + +void +FileBrowser::dropped( const KFileItem* /*item*/, QDropEvent* event, const KURL::List &urls){ + //Do nothing right now + event->ignore(); + //Run into const problems iterating over the list, so copy it to a malleable one + //(besides, need to filter for local giles) + KURL::List list(urls); + + for ( KURL::List::iterator it = list.begin(); it != list.end(); ){ + if ( m_medium && !(*it).isLocalFile() ) + it = list.erase( it ); + else{ + debug() << "Dropped: " << (*it) << endl; + it++; + } + } +} + +//END Public Slots + + +//BEGIN Private Slots + +inline void +FileBrowser::urlChanged( const KURL &u ) +{ + //the DirOperator's URL has changed + + QString url = u.isLocalFile() ? u.path() : u.prettyURL(); + + if( m_medium ){ + //remove the leading mountPoint value + url.remove( 0, m_medium->mountPoint().length() ); + } + + QStringList urls = m_combo->urls(); + urls.remove( url ); + urls.prepend( url ); + + m_combo->setURLs( urls, KURLComboBox::RemoveBottom ); +} + +inline void +FileBrowser::slotViewChanged( KFileView *view ) +{ + if( view->widget()->inherits( "KListView" ) ) + { + using namespace Amarok::ColorScheme; + + static_cast<KListView*>(view->widget())->setAlternateBackground( AltBase ); + } +} + +inline void +FileBrowser::activate( const KFileItem *item ) +{ + Playlist::instance()->insertMedia( item->url(), Playlist::DefaultOptions ); +} + +inline void +FileBrowser::prepareContextMenu() +{ + const KFileItemList &items = *m_dir->selectedItems(); + static_cast<KActionMenu*>(m_dir->actionCollection()->action("popupMenu"))->popupMenu()->setItemVisible( SavePlaylist, + items.count() > 1 || ( items.count() == 1 && items.getFirst()->isDir() ) ); + static_cast<KActionMenu*>(m_dir->actionCollection()->action("popupMenu"))->popupMenu()->setItemVisible( QueueTrack, + items.count() == 1 ); + static_cast<KActionMenu*>(m_dir->actionCollection()->action("popupMenu"))->popupMenu()->setItemVisible( QueueTracks, + items.count() > 1 ); + static_cast<KActionMenu*>(m_dir->actionCollection()->action("popupMenu"))->popupMenu()->setItemVisible( MediaDevice, + MediaBrowser::isAvailable() ); + static_cast<KActionMenu*>(m_dir->actionCollection()->action("popupMenu"))->popupMenu()->setItemVisible( MoveToCollection, !CollectionDB::instance()->isDirInCollection( url().path() ) ); + static_cast<KActionMenu*>(m_dir->actionCollection()->action("popupMenu"))->popupMenu()->setItemVisible( CopyToCollection, !CollectionDB::instance()->isDirInCollection( url().path() ) ); + static_cast<KActionMenu*>(m_dir->actionCollection()->action("popupMenu"))->popupMenu()->setItemVisible( OrganizeFiles, CollectionDB::instance()->isDirInCollection( url().path() ) ); +} + +inline void +FileBrowser::contextMenuActivated( int id ) +{ + switch( id ) + { + case MakePlaylist: + Playlist::instance()->insertMedia( selectedItems(), Playlist::Replace ); + break; + + case SavePlaylist: + playlistFromURLs( selectedItems() ); + break; + + case AppendToPlaylist: + Playlist::instance()->insertMedia( selectedItems() ); + break; + + case QueueTrack: + case QueueTracks: + Playlist::instance()->insertMedia( selectedItems(), Playlist::Queue ); + break; + + case EditTags: + { + KURL::List list = Amarok::recursiveUrlExpand( selectedItems() ); + TagDialog *dialog = NULL; + if( list.count() == 1 ) + { + dialog = new TagDialog( list.first(), this ); + } + else + { + dialog = new TagDialog( list, this ); + } + dialog->show(); + } + break; + + case CopyToCollection: + CollectionView::instance()->organizeFiles( selectedItems(), i18n( "Copy Files To Collection" ), true ); + break; + + case MoveToCollection: + CollectionView::instance()->organizeFiles( selectedItems(), i18n( "Move Files To Collection" ), false ); + break; + + case OrganizeFiles: + CollectionView::instance()->organizeFiles( selectedItems(), i18n( "Organize Collection Files" ), false ); + break; + + case MediaDevice: + MediaBrowser::queue()->addURLs( selectedItems() ); + break; + + case SelectAllFiles: + selectAll(); + break; + + case BurnCd: + K3bExporter::instance()->exportTracks( selectedItems() ); + break; + } +} + +inline void +FileBrowser::gotoCurrentFolder() +{ + const KURL &url = EngineController::instance()->bundle().url(); + KURL dirURL = KURL::fromPathOrURL( url.directory() ); + + m_combo->setURL( dirURL ); + setUrl( dirURL ); +} + +//END Private Slots + +void +FileBrowser::selectAll() +{ + KFileItemList list( *m_dir->view()->items() ); + + // Select all items which represent files + for( KFileItem* item = list.first(); item; item = list.next() ) + m_dir->view()->setSelected( item, item->isFile() ); +} + +#include <kurldrag.h> +#include <qpainter.h> +#include <qsimplerichtext.h> + +class KURLView : public KListView +{ +public: + KURLView( QWidget *parent ) : KListView( parent ) + { + reinterpret_cast<QWidget*>(header())->hide(); + addColumn( QString() ); + setResizeMode( KListView::LastColumn ); + setDragEnabled( true ); + setSelectionMode( QListView::Extended ); + } + + class Item : public KListViewItem { + public: + Item( const KURL &url, KURLView *parent ) : KListViewItem( parent, url.fileName() ), m_url( url ) {} + KURL m_url; + }; + + virtual QDragObject *dragObject() + { + QPtrList<QListViewItem> items = selectedItems(); + KURL::List urls; + + for( Item *item = static_cast<Item*>( items.first() ); item; item = static_cast<Item*>( items.next() ) ) + urls += item->m_url; + + return new KURLDrag( urls, this ); + } + + virtual void viewportPaintEvent( QPaintEvent *e ) + { + KListView::viewportPaintEvent( e ); + + if ( childCount() == 0 ) { + QPainter p( viewport() ); + + if ( m_text.isEmpty() ) { + //TODO Perhaps it's time to put this in some header, as we use it in three places now + QSimpleRichText t( i18n( + "<div align=center>" + "Enter a search term above; you can use wildcards like * and ?" + "</div>" ), font() ); + + t.setWidth( width() - 50 ); + + const uint w = t.width() + 20; + const uint h = t.height() + 20; + + p.setBrush( colorGroup().background() ); + p.drawRoundRect( 15, 15, w, h, (8*200)/w, (8*200)/h ); + t.draw( &p, 20, 20, QRect(), colorGroup() ); + } + else { + p.setPen( palette().color( QPalette::Disabled, QColorGroup::Text ) ); + p.drawText( rect(), Qt::AlignCenter | Qt::WordBreak, m_text ); + } + } + } + + void unsetText() { setText( QString::null ); } + void setText( const QString &text ) { m_text = text; viewport()->update(); } + +private: + QString m_text; +}; + + + +SearchPane::SearchPane( FileBrowser *parent ) + : QVBox( parent ) + , m_lineEdit( 0 ) + , m_listView( 0 ) + , m_lister( 0 ) +{ + QFrame *container = new QVBox( this, "container" ); + container->hide(); + + { + QFrame *box = new QHBox( container ); + box->setMargin( 5 ); + box->setBackgroundMode( Qt::PaletteBase ); + + m_lineEdit = new ClickLineEdit( i18n("Search here..."), box ); + connect( m_lineEdit, SIGNAL(textChanged( const QString& )), SLOT(searchTextChanged( const QString& )) ); + + m_listView = new KURLView( container ); + + container->setFrameStyle( m_listView->frameStyle() ); + container->setMargin( 5 ); + container->setBackgroundMode( Qt::PaletteBase ); + + m_listView->setFrameStyle( QFrame::NoFrame ); + connect( m_listView, SIGNAL(executed( QListViewItem* )), SLOT(activate( QListViewItem* )) ); + } + + KPushButton *button = new KPushButton( KGuiItem( i18n("&Show Search Panel"), "find" ), this ); + button->setToggleButton( true ); + connect( button, SIGNAL(toggled( bool )), SLOT(toggle( bool )) ); + + m_lister = new MyDirLister( true /*delay mimetypes*/ ); + insertChild( m_lister ); + connect( m_lister, SIGNAL(newItems( const KFileItemList& )), SLOT(searchMatches( const KFileItemList& )) ); + connect( m_lister, SIGNAL(completed()), SLOT(searchComplete()) ); +} + +void +SearchPane::toggle( bool toggled ) +{ + if ( toggled ) + m_lineEdit->setFocus(); + + static_cast<QWidget*>(child("container"))->setShown( toggled ); +} + +void +SearchPane::urlChanged( const KURL& ) +{ + searchTextChanged( m_lineEdit->text() ); +} + +void +SearchPane::searchTextChanged( const QString &text ) +{ + //TODO if user changes search directory then we need to update the search too + + m_lister->stop(); + m_listView->clear(); + m_dirs.clear(); + + if ( text.isEmpty() ) { + m_listView->unsetText(); + return; + } + + m_filter = QRegExp( text.contains( "*" ) ? text : '*'+text+'*', false, true ); + + m_lister->openURL( searchURL() ); + + m_listView->setText( i18n( "Searching..." ) ); +} + +void +SearchPane::searchMatches( const KFileItemList &list ) +{ + for( KFileItemList::ConstIterator it = list.begin(), end = list.end(); it != end; ++it ) { + if( (*it)->isDir() ) + m_dirs += (*it)->url(); + else if( m_filter.exactMatch( (*it)->name() ) ) + new KURLView::Item( (*it)->url(), static_cast<KURLView*>( m_listView ) ); + } +} + +void +SearchPane::searchComplete() +{ + //KDirLister crashes if you call openURL() from a slot + //connected to KDirLister::complete() + //TODO fix crappy KDElibs + + QTimer::singleShot( 0, this, SLOT(_searchComplete()) ); +} + +void +SearchPane::_searchComplete() +{ + if ( !m_dirs.isEmpty() ) { + KURL url = m_dirs.first(); + m_dirs.pop_front(); + m_lister->openURL( url ); + } + else + m_listView->setText( i18n("No results found") ); //only displayed if the listview is empty +} + +void +SearchPane::activate( QListViewItem *item ) +{ + Playlist::instance()->insertMedia( static_cast<KURLView::Item*>(item)->m_url, Playlist::DirectPlay ); +} + +#include "filebrowser.moc" diff --git a/amarok/src/filebrowser.h b/amarok/src/filebrowser.h new file mode 100644 index 00000000..bd4f7be2 --- /dev/null +++ b/amarok/src/filebrowser.h @@ -0,0 +1,127 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Max Howell + Copyright (C) 2004 Mark Kretschmann <markey@web.de> + Copyright (C) 2003 Roberto Raggi <roberto@kdevelop.org> + Copyright (C) 2001 Christoph Cullmann <cullmann@kde.org> + Copyright (C) 2001 Joseph Wenninger <jowenn@kde.org> + Copyright (C) 2001 Anders Lund <anders.lund@lund.tdcadsl.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef FILESELECTOR_WIDGET_H +#define FILESELECTOR_WIDGET_H + +#include <qvbox.h> //baseclass +#include <kdiroperator.h> //some inline functions +#include <ktoolbar.h> //baseclass +#include <kurl.h> //stack allocated + +class ClickLineEdit; +class QTimer; +class KActionCollection; +class KFileItem; +class KFileView; +class KURLComboBox; +class Medium; + +//Hi! I think we ripped this from Kate, since then it's been modified somewhat + +/* + The KDev file selector presents a directory view, in which the default action is + to open the activated file. + Additinally, a toolbar for managing the kdiroperator widget + sync that to + the directory of the current file is available, as well as a filter widget + allowing to filter the displayed files using a name filter. +*/ + + +class FileBrowser : public QVBox +{ + Q_OBJECT + + enum MenuId { MakePlaylist, SavePlaylist, MediaDevice, AppendToPlaylist, QueueTrack, QueueTracks, SelectAllFiles, BurnCd, MoveToCollection, CopyToCollection, OrganizeFiles, EditTags }; + +public: + FileBrowser( const char *name = 0, Medium *medium = 0 ); + ~FileBrowser(); + + KURL url() const { return m_dir->url(); } + +public slots: + void setUrl( const KURL &url ); + void setUrl( const QString &url ); + void setFilter( const QString& ); + void dropped( const KFileItem*, QDropEvent*, const KURL::List& ); + +private slots: + void activate( const KFileItem* ); + void contextMenuActivated( int ); + void gotoCurrentFolder(); + void prepareContextMenu(); + void selectAll(); + void slotViewChanged( KFileView* ); + void urlChanged( const KURL& ); + +private: + KURL::List selectedItems(); + void playlistFromURLs( const KURL::List &urls ); + + KURLComboBox *m_combo; + KDirOperator *m_dir; + ClickLineEdit *m_filter; + Medium *m_medium; +}; + + + +#include <kfileitem.h> //KFileItemList +#include <qregexp.h> + +class KDirLister; +class KURLView; +class QLineEdit; +class QListViewItem; + +///@author Max Howell +///@short Widget for recursive searching of current FileBrowser location + +class SearchPane : public QVBox +{ + Q_OBJECT + +public: + SearchPane( FileBrowser *parent ); + +private slots: + void toggle( bool ); + void urlChanged( const KURL& ); + void searchTextChanged( const QString &text ); + void searchMatches( const KFileItemList& ); + void searchComplete(); + void _searchComplete(); + void activate( QListViewItem* ); + +private: + KURL searchURL() const { return static_cast<FileBrowser*>(parentWidget())->url(); } + + QLineEdit *m_lineEdit; + KURLView *m_listView; + KDirLister *m_lister; + QRegExp m_filter; + KURL::List m_dirs; +}; + +#endif diff --git a/amarok/src/firstrunwizard.ui b/amarok/src/firstrunwizard.ui new file mode 100644 index 00000000..2b5ace9f --- /dev/null +++ b/amarok/src/firstrunwizard.ui @@ -0,0 +1,302 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>FirstRunWizard</class> +<widget class="QWizard"> + <property name="name"> + <cstring>FirstRunWizard</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>824</width> + <height>410</height> + </rect> + </property> + <property name="caption"> + <string>First-Run Wizard</string> + </property> + <widget class="QWidget"> + <property name="name"> + <cstring>WizardPage</cstring> + </property> + <attribute name="title"> + <string></string> + </attribute> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KActiveLabel" row="0" column="0" rowspan="2" colspan="1"> + <property name="name"> + <cstring>text1</cstring> + </property> + <property name="text"> + <string><h1>Welcome to Amarok!</h1> +<p>There are many media-players around these days, this is true. Amarok however provides an aural experience so pleasurable it always has you coming back for more. What is missing from most players is an interface that does not get in your way. Amarok tries to be a little different, and at the same time intuitive. It provides a simple drag-and-drop interface that makes playlist handling simple and fun. By using Amarok we truly hope you will:</p> +<p align="center"><i><b>"Rediscover your music!"</b></i> </p></string> + </property> + </widget> + <widget class="QLabel" row="0" column="2"> + <property name="name"> + <cstring>picture1</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>4</hsizetype> + <vsizetype>4</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="frameShape"> + <enum>Box</enum> + </property> + <property name="scaledContents"> + <bool>false</bool> + </property> + </widget> + <widget class="KActiveLabel" row="2" column="0" rowspan="1" colspan="3"> + <property name="name"> + <cstring>text1_2</cstring> + </property> + <property name="text"> + <string><h2>First-run Wizard</h2> +<p>This wizard will help you setup Amarok in three easy steps. Click <i>Next</i> to begin, or if you do not like wizards, click <i>Skip</i>.</p></string> + </property> + </widget> + <spacer row="1" column="1"> + <property name="name"> + <cstring>spacer1</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </grid> + </widget> + <widget class="QWidget"> + <property name="name"> + <cstring>WizardPage</cstring> + </property> + <attribute name="title"> + <string>Locate your Music</string> + </attribute> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KActiveLabel"> + <property name="name"> + <cstring>text3</cstring> + </property> + <property name="lineWidth"> + <number>1</number> + </property> + <property name="text"> + <string><p>Please select the folders on the right where your music files are stored.</p> +<p>Doing so is highly recommended, and will enhance the features available to you.</p> +<p>If you wish, Amarok is able to monitor these folders for new files and can automatically add them to the collection.</p></string> + </property> + </widget> + </hbox> + </widget> + <widget class="QWidget"> + <property name="name"> + <cstring>WizardPage_3</cstring> + </property> + <attribute name="title"> + <string>Database Setup</string> + </attribute> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <spacer row="1" column="0" rowspan="2" colspan="1"> + <property name="name"> + <cstring>spacer15</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>257</height> + </size> + </property> + </spacer> + <spacer row="2" column="1"> + <property name="name"> + <cstring>spacer14</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>194</height> + </size> + </property> + </spacer> + <widget class="KActiveLabel" row="0" column="0"> + <property name="name"> + <cstring>dbActiveLabel</cstring> + </property> + <property name="text"> + <string>Amarok uses a database to store information about your music. If you are not sure which to use, press Next. +<p><b>MySQL</b> or <b>Postgresql</b> are faster than <b>sqlite</b>, but require additional setup.</p> +<ul> +<li><a href="http://amarok.kde.org/wiki/MySQL_HowTo">Instructions for configuring MySQL</a>.</li> +<li><a href="http://amarok.kde.org/wiki/Postgresql_HowTo">Instructions for configuring Postgresql</a>.</li> +</ul></string> + </property> + </widget> + <widget class="DbSetup" row="0" column="1" rowspan="2" colspan="1"> + <property name="name"> + <cstring>dbSetup7</cstring> + </property> + </widget> + </grid> + </widget> + <widget class="QWidget"> + <property name="name"> + <cstring>WizardPage_4</cstring> + </property> + <attribute name="title"> + <string></string> + </attribute> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KActiveLabel"> + <property name="name"> + <cstring>text4</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>7</hsizetype> + <vsizetype>7</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="maximumSize"> + <size> + <width>32767</width> + <height>32767</height> + </size> + </property> + <property name="text"> + <string><h1>Congratulations!</h1> +<p>Amarok is ready for use! When you click finish Amarok will appear and begin scanning the folders in your collection.</p> +<p>Amarok's playlist-window will show your <b>Collection</b> on the left and the <b>Playlist</b> on the right. Drag and drop music from the Collection to the Playlist and press <b>Play</b>.</p> +<p>If you want more help or a tutorial, please check out the <a href="help:/amarok">Amarok handbook</a>. We hope you enjoy using Amarok.</p> +<p align="right">The Amarok developers</p></string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer3</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>21</width> + <height>294</height> + </size> + </property> + </spacer> + <widget class="QLabel"> + <property name="name"> + <cstring>picture4</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>4</hsizetype> + <vsizetype>4</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="frameShape"> + <enum>Box</enum> + </property> + <property name="scaledContents"> + <bool>false</bool> + </property> + </widget> + </hbox> + </widget> +</widget> +<customwidgets> + <customwidget> + <class>DbSetup</class> + <header location="local">dbsetup.h</header> + <sizehint> + <width>380</width> + <height>165</height> + </sizehint> + <container>1</container> + <sizepolicy> + <hordata>3</hordata> + <verdata>3</verdata> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + <pixmap>image0</pixmap> + </customwidget> +</customwidgets> +<images> + <image name="image0"> + <data format="XBM.GZ" length="79">789c534e494dcbcc4b554829cdcdad8c2fcf4c29c95030e0524611cd48cd4ccf28010a1797249664262b2467241641a592324b8aa363156c15aab914146aadb90067111b1f</data> + </image> +</images> +<forwards> + <forward>class CollectionSetup;</forward> +</forwards> +<variables> + <variable access="public">enum Interface { XMMS, Compact };</variable> + <variable access="private">CollectionSetup* m_folderSetup;</variable> +</variables> +<slots> + <slot access="private" specifier="non virtual">invokeHandbook()</slot> + <slot>openLink( const QString & s )</slot> +</slots> +<functions> + <function access="private" specifier="non virtual">init()</function> + <function access="protected">showPage( QWidget * w )</function> + <function specifier="non virtual">writeCollectionConfig()</function> + <function specifier="non virtual" returnType="FirstRunWizard::Interface">interface()</function> +</functions> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>kactivelabel.h</includehint> + <includehint>kactivelabel.h</includehint> + <includehint>kactivelabel.h</includehint> + <includehint>kactivelabel.h</includehint> + <includehint>dbsetup.h</includehint> + <includehint>kactivelabel.h</includehint> +</includehints> +</UI> diff --git a/amarok/src/firstrunwizard.ui.h b/amarok/src/firstrunwizard.ui.h new file mode 100644 index 00000000..ea684319 --- /dev/null +++ b/amarok/src/firstrunwizard.ui.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** ui.h extension file, included from the uic-generated form implementation. +** +** If you want to add, delete, or rename functions or slots, use +** Qt Designer to update this file, preserving your code. +** +** You should not define a constructor or destructor in this file. +** Instead, write your code in functions called init() and destroy(). +** These will automatically be called by the form's constructor and +** destructor. +*****************************************************************************/ + +#include "amarok.h" +#include "config.h" +#include "directorylist.h" + +#include <kapplication.h> +#include <kconfig.h> +#include <klocale.h> +#include <qpushbutton.h> + +namespace Amarok +{ + extern QPixmap getPNG( const QString& ); + extern QPixmap getJPG( const QString& ); + + extern KConfig *config( const QString& ); +} + +void +FirstRunWizard::init() +{ + using namespace Amarok; + + //aesthetics + helpButton()->hide(); + + picture1->setPixmap( getJPG( "amarok_rocks" ) ); + picture4->setPixmap( *picture1->pixmap() ); + + WizardPageLayout_2->addWidget( m_folderSetup = new CollectionSetup( WizardPage_2 ) ); + + text4->disconnect( SIGNAL(linkClicked( const QString& )) ); + connect( text4, SIGNAL(linkClicked( const QString& )), SLOT(invokeHandbook()) ); + dbActiveLabel->disconnect( SIGNAL(linkClicked( const QString& )) ); + connect( dbActiveLabel, SIGNAL(linkClicked( const QString& )), SLOT(openLink( const QString &)) ); + setFinishEnabled ( WizardPage_4, true ); +#if !defined(USE_MYSQL) && !defined(USE_POSTGRESQL) + removePage(WizardPage_3); +#endif + +} + +void +FirstRunWizard::showPage( QWidget *w ) //virtual +{ + QWizard::showPage( w ); + + cancelButton()->setText( w == WizardPage ? i18n("&Skip") : i18n("&Cancel") ); +} + +inline void +FirstRunWizard::invokeHandbook() //SLOT +{ + // Show handbook + kapp->invokeHelp( QString::null, QString::null, 0 ); +} + +void +FirstRunWizard::writeCollectionConfig() +{ + m_folderSetup->writeConfig(); +} + +void +FirstRunWizard::openLink(const QString& s) +{ + Amarok::invokeBrowser(s); +} diff --git a/amarok/src/hi128-app-amarok.png b/amarok/src/hi128-app-amarok.png new file mode 100644 index 0000000000000000000000000000000000000000..85e4aeaaf2c61849619ae42ea60aa99e91a35320 GIT binary patch literal 13989 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 literal 0 HcmV?d00001 diff --git a/amarok/src/hi16-app-amarok.png b/amarok/src/hi16-app-amarok.png new file mode 100644 index 0000000000000000000000000000000000000000..50a761a466d1e526cdff50ea6cd14fb19a0ff3b6 GIT binary patch literal 740 zcmV<A0vr8_P)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV00004b3#c}2nYxW zd<bNS00009a7bBm000UR000UR0oc(#5&!@I8FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H10%1u+K~y-6m6J<sjA0appY#1QLxvhN5jD|qY3OC)l1OG2F0oOU z2>ETyf}~=hc2q=YBrJ3z8WO=uEhPR0W=9kWC5`EZ2x+uQT{>-3Y6k6?bSCp33uc(8 zg>QSl=l#xkzw?T!^5>Cwq7jG#Q6t0vTqOzg0**R%U&Z~BfW#99XcgfMimxW*E3RDV zw*X!ae=nxec?%)0N*mCjj_sBOB%Uxvxa%rS8|tdG-Hu2&;H~Iv^lO0=i6QOopAaK- zxyn{`Y}fp%x4KHx(am8xn`{4q0p?d(99rv_qnpFHN)yluAnd-~D8h^N%d5Pd&9(9u z35=z)9KLXu!Ql*#F7D-^GonuhCJL&oHb5N3*KS9qh41tXu<giolCOtphy=)Hv$We0 zLGb}`4-l;h`HJCy*D67|sRG9@KjhWmC*F>v%VN9MEyZ|d5)OE&3Hgdc>3q}}Ay#r5 z;PLZ!#82KNGg)BA#uXLaoVeP{u}crg<#McC=(mgz^OTC|Z#mB0=)t-F3V@pGJWpQ? z{{C+fw#OFHvU3$4PZ`7GD#@3r?4~`ZZdq?Wd@dJKV_zy>-@m1vmR(VT!65K+H(VuY zfZkN5V4Xg&iD1=?>3jrWP5pv0Jo>djDxJ50UIRD?`SXv`I+-g>3yTO1OR9f+kr+~h zJm460>^@c6p1se@l1TNm0`um~V9xBB09<+ek?#HpQKd~CyRWo~9Y*LH@Yb<-*-}I* zS7JeE7UNlE-?c$Kdv6pYbO9akk1)y9q;IZfT)&$&9%GU92HbHo-~2O774!aQoPPiX W=>U0ZHJ}&(0000<MNUMnLSTX&7fdYx literal 0 HcmV?d00001 diff --git a/amarok/src/hi22-app-amarok.png b/amarok/src/hi22-app-amarok.png new file mode 100644 index 0000000000000000000000000000000000000000..ee6fa140e4af912389751d14b404c7f8020b1c35 GIT binary patch literal 1110 zcmV-c1gZOpP)<h;3K|Lk000e1NJLTq000&M000&U1^@s6#I$TX00004b3#c}2nYxW zd<bNS00009a7bBm000fw000fw0YWI7cmMzZ8FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H11Jg-FK~y-6ebrw`TUQ*w@$b30H;FO+O=@b4)(#x|!{$Kg7%LMj z-NW`2%EVIk=cQC|QW$;N(;l~{wrjWHz`YE@!bZ`K!Pq*=oNlF5vD*D9bJ#kwtXeUN zniy|#&)Gw6qAsaFxLhvxp3nK+^Y15=QdA1jKk5g1fo`D7z5u4}YYMooqT$tw?JL2e zfAkda5l~;T*PTcLZ>wlHvM+3BISiZt2sJo);W01IhCFn(dH|SR$}ktpaARSUSi&qe zy#fsE&b|cO=qwO)Xo7*BCWd+g7!FY})tsfce0`bW@f9qo0OCMr5!ZHo`!Nt~_qiAw z?BJc_2Oo(77!JYNV*$nnJ81X00D`tb2^RgMeL$Z>6I?oZh^LyX9tjrX#@!WOe(xG8 zx6P%Khj3^DpwG6@ME_`Gv8REaCY}!Nk6Kc3{_1bM`}qt?!7p<QJP~j*(9=}<bJ0KA zr~yX-f2hI9P;X#gG`^PM&5M&n#uqRQjjWlYGvvlJO@?{{gc_Uxe&DE1VV!d1u(#rv zK1^j9xi-&oGRx%e%VcvFzFIeVOYvNrhXZw0xZQ4q5F9z|C6-tN=+>|o{xdD*Logp- z=dBMXxf@T@^<;=t#>DG3kW#UpHt~CPX5)FjxfSDoMpr50-SC6N7LoA<zMQ<zW;Vx# z!Q-6!^havk1`36g3Weh9nFQbey3QLtAyTQ@-A1<#?AdxV_Y%DS)f|7u*V)LJ1btpk zpZ|`;Mi$*6kP<8uPTj%d0VjjUg0vi{#pQAVR*_LUg+2f3_#bP0a&?xQ^AE{e3ZV&v z5d6EA#?Uo%O<+k7cE(-p9?reeMoX}n>gs9$?)+2QcvG4U^LI*;HBE#Ncw9OT0mWS@ zrNWZRzDqW?OkRI6KwE35gaXXPGJD`@fLr%7TsZwa!8(J)<~HeUzVJ^<m0(LMw)2vD zk4~$<8n@eBLbvmZoA=Vi$7v0i09N8jlaZg31RFhNt3%qjlnMcK9IT?MYVRt#_`?I@ zNfTfNn9x)-oCMAQd^WMd?Zs3XT*UKI;xZigy@owmg_w<H7@2rjd_1F~;iLwjqTw+h zA^}%pkUEc15!Zx<?$BuT8tA$X!0n|BXTMlP7Um;jTYFBoAz*QN%V5*pjIYL30R|xi zHEs<lbUyet&TGSeu~ayh7i|M5Kj<5P%fKm=qQ+W9Wz%fh6Db6=`n@#yYFRRCSzgZ< zn?`^k6%8jU!n-;+L7|fP3m@eXRt}}<C~eO$tx~8QSIUfiifKLY5<A7T+kQ30PBHz- cp8T@-4<=!x{75^RK>z>%07*qoM6N<$f@oj@ssI20 literal 0 HcmV?d00001 diff --git a/amarok/src/hi32-app-amarok.png b/amarok/src/hi32-app-amarok.png new file mode 100644 index 0000000000000000000000000000000000000000..07968313ad662817a83c134603525357b8daab11 GIT binary patch literal 2355 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 Z{{o1nI<Avv5sLr-002ovPDHLkV1k%vb=Uv^ literal 0 HcmV?d00001 diff --git a/amarok/src/hi48-app-amarok.png b/amarok/src/hi48-app-amarok.png new file mode 100644 index 0000000000000000000000000000000000000000..2af1c87a1bfc3266a56aa5c25fefe25f7714974a GIT binary patch literal 4018 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 Y1HN^mMpuhB#sB~S07*qoM6N<$f@=!V00000 literal 0 HcmV?d00001 diff --git a/amarok/src/hi64-app-amarok.png b/amarok/src/hi64-app-amarok.png new file mode 100644 index 0000000000000000000000000000000000000000..2700c4d955fd67c017398bd85fcca885c0dc0c2b GIT binary patch literal 5676 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 So2+~Q0000<MNUMnLSTYdIM6Ww literal 0 HcmV?d00001 diff --git a/amarok/src/hintlineedit.cpp b/amarok/src/hintlineedit.cpp new file mode 100644 index 00000000..3d29b76a --- /dev/null +++ b/amarok/src/hintlineedit.cpp @@ -0,0 +1,58 @@ +#include <hintlineedit.h> +#include <qvbox.h> +#include <qlabel.h> +#include <qfont.h> + +HintLineEdit::HintLineEdit( const QString &hint, const QString &text, QWidget *parent, const char *name ) + : KLineEdit( text, 0, name ) + , m_vbox( new QVBox( parent ) ) +{ + init(); + m_hint->setText( hint ); +} + +HintLineEdit::HintLineEdit( const QString &text, QWidget *parent, const char *name ) + : KLineEdit( text, 0, name ) + , m_vbox( new QVBox( parent ) ) +{ + init(); +} + +HintLineEdit::HintLineEdit( QWidget *parent, const char *name ) + : KLineEdit( 0, name ) + , m_vbox( new QVBox( parent ) ) +{ + init(); +} + +void +HintLineEdit::init() +{ + reparent( m_vbox, 0, QPoint(0,0), true ); + m_hint = new QLabel( m_vbox ); + //m_hint->setBuddy( this ); + m_hint->setFocusPolicy( NoFocus ); + QFont font; + font.setPointSize( font.pointSize() - 2); + m_hint->setFont( font ); +} + +HintLineEdit::~HintLineEdit() +{ + reparent( 0, 0, QPoint(0,0), false ); + delete m_vbox; +} + +void +HintLineEdit::setHint( const QString &hint ) +{ + m_hint->setText( hint ); +} + +QObject * +HintLineEdit::parent() +{ + return m_vbox->parent(); +} + +#include "hintlineedit.moc" diff --git a/amarok/src/hintlineedit.h b/amarok/src/hintlineedit.h new file mode 100644 index 00000000..9e3c666e --- /dev/null +++ b/amarok/src/hintlineedit.h @@ -0,0 +1,27 @@ +#ifndef HINTLINEEDIT_H +#define HINTLINEEDIT_H + +#include <klineedit.h> //baseclass + +class QVBox; +class QLabel; +class QWidget; + +class HintLineEdit : public KLineEdit +{ + Q_OBJECT + +public: + HintLineEdit( const QString &hint, const QString &text, QWidget *parent = 0, const char *name = 0 ); + HintLineEdit( const QString &text, QWidget *parent = 0, const char *name = 0 ); + HintLineEdit( QWidget *parent = 0, const char *name = 0 ); + virtual ~HintLineEdit(); + virtual QObject *parent(); + virtual void setHint( const QString &hint ); +private: + void init(); + QVBox *m_vbox; + QLabel *m_hint; +}; + +#endif diff --git a/amarok/src/htmlview.cpp b/amarok/src/htmlview.cpp new file mode 100644 index 00000000..ff3ffa44 --- /dev/null +++ b/amarok/src/htmlview.cpp @@ -0,0 +1,312 @@ +// (c) 2005 Christian Muehlhaeuser <chris@chris.de> +// (c) 2006 Seb Ruiz <me@sebruiz.net> +// License: GNU General Public License V2 + + +#include "amarok.h" +#include "amarokconfig.h" +#include "app.h" +#include "contextbrowser.h" +#include "htmlview.h" +#include "playlist.h" //appendMedia() + +#include <qclipboard.h> +#include <qfile.h> // External CSS opening +#include <qimage.h> // External CSS opening + +#include <kapplication.h> //kapp +#include <kactioncollection.h> +#include <kglobal.h> //kapp +#include <kimageeffect.h> // gradient background image +#include <kpopupmenu.h> +#include <kstandarddirs.h> //locate file +#include <ktempfile.h> + +KTempFile *HTMLView::m_bgGradientImage = 0; +KTempFile *HTMLView::m_headerGradientImage = 0; +KTempFile *HTMLView::m_shadowGradientImage = 0; +int HTMLView::m_instances = 0; + +HTMLView::HTMLView( QWidget *parentWidget, const char *widgetname, const bool DNDEnabled, const bool JScriptEnabled ) + : KHTMLPart( parentWidget, widgetname ) +{ + m_instances++; + setJavaEnabled( false ); + setPluginsEnabled( false ); + + setDNDEnabled( DNDEnabled ); + setJScriptEnabled( JScriptEnabled ); + + KActionCollection* ac = actionCollection(); + ac->setAutoConnectShortcuts( true ); + m_copy = KStdAction::copy( this, SLOT( copyText() ), ac, "htmlview_copy" ); + m_selectAll = KStdAction::selectAll( this, SLOT( selectAll() ), ac, "htmlview_select_all" ); + { + KPopupMenu m; + m_copy->plug( &m ); + m_selectAll->plug( &m ); + + m_copy->unplug( &m ); + m_selectAll->unplug( &m ); + } + + connect( this, SIGNAL( selectionChanged() ), SLOT( enableCopyAction() ) ); + enableCopyAction(); +} + + +HTMLView::~HTMLView() +{ + m_instances--; + if ( m_instances < 1 ) { + delete m_bgGradientImage; + delete m_headerGradientImage; + delete m_shadowGradientImage; + } +} + +void +HTMLView::enableCopyAction() +{ + m_copy->setEnabled( hasSelection() ); +} + +void +HTMLView::selectAll() +{ + KHTMLPart::selectAll(); +} + +void +HTMLView::copyText() +{ + QString text = selectedText(); + + // Copy both to clipboard and X11-selection + QApplication::clipboard()->setText( text, QClipboard::Clipboard ); + QApplication::clipboard()->setText( text, QClipboard::Selection ); +} + +void HTMLView::paletteChange() { + delete m_bgGradientImage; + delete m_headerGradientImage; + delete m_shadowGradientImage; + m_bgGradientImage = m_headerGradientImage = m_shadowGradientImage = 0; +} + +QString +HTMLView::loadStyleSheet() +{ + QString themeName = AmarokConfig::contextBrowserStyleSheet().latin1(); + const QString file = kapp->dirs()->findResource( "data","amarok/themes/" + themeName + "/stylesheet.css" ); + + QString styleSheet; + if ( themeName != "Default" && QFile::exists( file ) ) + { + const QString CSSLocation = kapp->dirs()->findResource( "data","amarok/themes/" + themeName + "/stylesheet.css" ); + QFile ExternalCSS( CSSLocation ); + if ( !ExternalCSS.open( IO_ReadOnly ) ) + return QString(); //FIXME: should actually return the default style sheet, then + + const QString pxSize = QString::number( ContextBrowser::instance()->fontMetrics().height() - 4 ); + const QString fontFamily = AmarokConfig::useCustomFonts() ? + AmarokConfig::contextBrowserFont().family() : + QApplication::font().family(); + const QString text = ContextBrowser::instance()->colorGroup().text().name(); + const QString link = ContextBrowser::instance()->colorGroup().link().name(); + const QString fg = ContextBrowser::instance()->colorGroup().highlightedText().name(); + const QString bg = ContextBrowser::instance()->colorGroup().highlight().name(); + const QString base = ContextBrowser::instance()->colorGroup().base().name(); + const QColor bgColor = ContextBrowser::instance()->colorGroup().highlight(); + QColor gradientColor = bgColor; + + //we have to set the color for body due to a KHTML bug + //KHTML sets the base color but not the text color + styleSheet = QString( "body { margin: 8px; font-size: %1px; color: %2; background-color: %3; font-family: %4; }" ) + .arg( pxSize ) + .arg( text ) + .arg( AmarokConfig::schemeAmarok() ? fg : gradientColor.name() ) + .arg( fontFamily ); + + QTextStream eCSSts( &ExternalCSS ); + QString tmpCSS = eCSSts.read(); + ExternalCSS.close(); + + tmpCSS.replace( "./", KURL::fromPathOrURL( CSSLocation ).directory( false ) ); + tmpCSS.replace( "AMAROK_FONTSIZE-2", pxSize ); + tmpCSS.replace( "AMAROK_FONTSIZE", pxSize ); + tmpCSS.replace( "AMAROK_FONTSIZE+2", pxSize ); + tmpCSS.replace( "AMAROK_FONTFAMILY", fontFamily ); + tmpCSS.replace( "AMAROK_TEXTCOLOR", text ); + tmpCSS.replace( "AMAROK_LINKCOLOR", link ); + tmpCSS.replace( "AMAROK_BGCOLOR", bg ); + tmpCSS.replace( "AMAROK_FGCOLOR", fg ); + tmpCSS.replace( "AMAROK_BASECOLOR", base ); + tmpCSS.replace( "AMAROK_DARKBASECOLOR", ContextBrowser::instance()->colorGroup().base().dark( 120 ).name() ); + tmpCSS.replace( "AMAROK_GRADIENTCOLOR", gradientColor.name() ); + + styleSheet += tmpCSS; + } + else + { + int pxSize = ContextBrowser::instance()->fontMetrics().height() - 4; + const QString fontFamily = AmarokConfig::useCustomFonts() ? AmarokConfig::contextBrowserFont().family() : QApplication::font().family(); + const QString text = ContextBrowser::instance()->colorGroup().text().name(); + const QString link = ContextBrowser::instance()->colorGroup().link().name(); + const QString fg = ContextBrowser::instance()->colorGroup().highlightedText().name(); + const QString bg = ContextBrowser::instance()->colorGroup().highlight().name(); + const QColor baseColor = ContextBrowser::instance()->colorGroup().base(); + const QColor bgColor = ContextBrowser::instance()->colorGroup().highlight(); + const QColor gradientColor = bgColor; + + if ( !m_bgGradientImage ) { + m_bgGradientImage = new KTempFile( locateLocal( "tmp", "gradient" ), ".png", 0600 ); + QImage image = KImageEffect::gradient( QSize( 600, 1 ), gradientColor, gradientColor.light( 130 ), KImageEffect::PipeCrossGradient ); + image.save( m_bgGradientImage->file(), "PNG" ); + m_bgGradientImage->close(); + } + + if ( !m_headerGradientImage ) { + m_headerGradientImage = new KTempFile( locateLocal( "tmp", "gradient_header" ), ".png", 0600 ); + QImage imageH = KImageEffect::unbalancedGradient( QSize( 1, 10 ), bgColor, gradientColor.light( 130 ), KImageEffect::VerticalGradient, 100, -100 ); + imageH.copy( 0, 1, 1, 9 ).save( m_headerGradientImage->file(), "PNG" ); + m_headerGradientImage->close(); + } + + if ( !m_shadowGradientImage ) { + m_shadowGradientImage = new KTempFile( locateLocal( "tmp", "gradient_shadow" ), ".png", 0600 ); + QImage imageS = KImageEffect::unbalancedGradient( QSize( 1, 10 ), baseColor, Qt::gray, KImageEffect::VerticalGradient, 100, -100 ); + imageS.save( m_shadowGradientImage->file(), "PNG" ); + m_shadowGradientImage->close(); + } + + //unlink the files for us on deletion + m_bgGradientImage->setAutoDelete( true ); + m_headerGradientImage->setAutoDelete( true ); + m_shadowGradientImage->setAutoDelete( true ); + + //we have to set the color for body due to a KHTML bug + //KHTML sets the base color but not the text color + styleSheet = QString( "body { margin: 4px; font-size: %1px; color: %2; background-color: %3; background-image: url( %4 ); background-repeat: repeat; font-family: %5; }" ) + .arg( pxSize ) + .arg( text ) + .arg( AmarokConfig::schemeAmarok() ? fg : gradientColor.name() ) + .arg( m_bgGradientImage->name() ) + .arg( fontFamily ); + + //text attributes + styleSheet += QString( "h1 { font-size: %1px; }" ).arg( pxSize + 8 ); + styleSheet += QString( "h2 { font-size: %1px; }" ).arg( pxSize + 6 ); + styleSheet += QString( "h3 { font-size: %1px; }" ).arg( pxSize + 4 ); + styleSheet += QString( "h4 { font-size: %1px; }" ).arg( pxSize + 3 ); + styleSheet += QString( "h5 { font-size: %1px; }" ).arg( pxSize + 2 ); + styleSheet += QString( "h6 { font-size: %1px; }" ).arg( pxSize + 1 ); + styleSheet += QString( "a { font-size: %1px; color: %2; }" ).arg( pxSize ).arg( text ); + styleSheet += QString( ".info { display: block; margin-left: 4px; font-weight: normal; }" ); + + styleSheet += QString( ".song a { display: block; padding: 1px 2px; font-weight: normal; text-decoration: none; }" ); + styleSheet += QString( ".song a:hover { color: %1; background-color: %2; }" ).arg( fg ).arg( bg ); + styleSheet += QString( ".song-title { font-weight: bold; }" ); + styleSheet += QString( ".song-place { font-size: %1px; font-weight: bold; }" ).arg( pxSize + 3 ); + + //box: the base container for every block (border hilighted on hover, 'A' without underlining) + styleSheet += QString( ".box { border: solid %1 1px; text-align: left; margin-bottom: 10px; }" ).arg( bg ); + styleSheet += QString( ".box a { text-decoration: none; }" ); + styleSheet += QString( ".box:hover { border: solid %1 1px; }" ).arg( text ); + + //box contents: header, body, rows and alternate-rows + styleSheet += QString( ".box-header { color: %1; background-color: %2; background-image: url( %4 ); background-repeat: repeat-x; font-size: %3px; font-weight: bold; padding: 1px 0.5em; border-bottom: 1px solid #000; }" ) + .arg( fg ) + .arg( bg ) + .arg( pxSize + 2 ) + .arg( m_headerGradientImage->name() ); + + styleSheet += QString( ".box-body { padding: 2px; background-color: %1; background-image: url( %2 ); background-repeat: repeat-x; font-size:%3px; }" ) + .arg( ContextBrowser::instance()->colorGroup().base().name() ) + .arg( m_shadowGradientImage->name() ) + .arg( pxSize ); + + //"Related Artists" related styles + styleSheet += QString( ".box-header-nav { color: %1; background-color: %2; font-size: %3px; font-weight: bold; padding: 1px 0.5em; border-bottom: 1px solid #000; text-align: right; }" ) + .arg( fg ) + .arg( bg ) + .arg( pxSize ); + + //"Albums by ..." related styles + styleSheet += QString( ".album-header:hover { color: %1; background-color: %2; cursor: pointer; }" ).arg( fg ).arg( bg ); + styleSheet += QString( ".album-header:hover a { color: %1; }" ).arg( fg ); + styleSheet += QString( ".album-body { background-color: %1; border-bottom: solid %2 1px; border-top: solid %3 1px; }" ).arg( ContextBrowser::instance()->colorGroup().base().name() ).arg( bg ).arg( bg ); + styleSheet += QString( ".album-title { font-weight: bold; }" ); + styleSheet += QString( ".album-info { float:right; padding-right:4px; font-size: %1px }" ).arg( pxSize ); + styleSheet += QString( ".album-length { float:right; padding-right:4px; font-size: %1px; clear:right; }" ).arg( pxSize ); + styleSheet += QString( ".album-image { padding-right: 4px; }" ); + styleSheet += QString( ".album-song a { display: block; padding: 1px 2px; font-weight: normal; text-decoration: none; }" ); + styleSheet += QString( ".album-song a:hover { color: %1; background-color: %2; }" ).arg( fg ).arg( bg ); + styleSheet += QString( ".album-song-trackno { font-weight: bold; }" ); + + styleSheet += QString( ".disc-separator { color: %1; border-bottom: 1px solid %2; }" ).arg( bg ).arg( bg ); + styleSheet += QString( ".disc-separator a { display: block; padding: 1px 2px; font-weight: normal; text-decoration: none; }" ); + styleSheet += QString( ".disc-separator a:hover { color: %1; background-color: %2; }" ).arg( fg ).arg( bg ); + + styleSheet += QString( ".button { width: 100%; }" ); + + //boxes used to display score (sb: score box) + styleSheet += QString( ".sbtext { text-align: right; padding: 0px 4px; }" ); + styleSheet += QString( ".sbinner { height: 8px; background-color: %1; border: solid %2 1px; }" ) + .arg( ContextBrowser::instance()->colorGroup().highlight().name() ) + .arg( ContextBrowser::instance()->colorGroup().highlightedText().name() ); + styleSheet += QString( ".sbouter { width: 52px; height: 10px; background-color: %1; border: solid %2 1px; }" ) + .arg( ContextBrowser::instance()->colorGroup().base().dark( 120 ).name() ) + .arg( ContextBrowser::instance()->colorGroup().highlight().name() ); + + styleSheet += QString( ".ratingBox { padding: 0px 4px; }" ); + styleSheet += QString( ".ratingStar { height: 0.9em; }" ); + + styleSheet += QString( ".statsBox { border-left: solid %1 1px; }" ) + .arg( ContextBrowser::instance()->colorGroup().base().dark( 120 ).name() ); + + styleSheet += QString( "#current_box-header-album { font-weight: normal; }" ); + styleSheet += QString( "#current_box-information-td { text-align: right; vertical-align: bottom; padding: 3px; }" ); + styleSheet += QString( "#current_box-largecover-td { text-align: left; width: 100px; padding: 0; vertical-align: bottom; }" ); + styleSheet += QString( "#current_box-largecover-image { padding: 4px; vertical-align: bottom; }" ); + + styleSheet += QString( "#wiki_box-body a { color: %1; }" ).arg( link ); + styleSheet += QString( "#wiki_box-body a:hover { text-decoration: underline; }" ); + + //labels in tag dialog + styleSheet += ".label a:hover { font-weight: bold; }"; + styleSheet += QString( ".label.size1 { font-size: %1px; }" ).arg( pxSize ); + styleSheet += QString( ".label.size2 { font-size: %1px; }" ).arg( pxSize + 1 ); + styleSheet += QString( ".label.size3 { font-size: %1px; }" ).arg( pxSize + 2 ); + styleSheet += QString( ".label.size4 { font-size: %1px; }" ).arg( pxSize + 3 ); + styleSheet += QString( ".label.size5 { font-size: %1px; }" ).arg( pxSize + 4); + styleSheet += QString( ".label.size6 { font-size: %1px; }" ).arg( pxSize + 5 ); + styleSheet += QString( ".label.size7 { font-size: %1px; }" ).arg( pxSize + 6 ); + styleSheet += QString( ".label.size8 { font-size: %1px; }" ).arg( pxSize + 7 ); + styleSheet += QString( ".label.size9 { font-size: %1px; }" ).arg( pxSize + 8 ); + styleSheet += QString( ".label.size10 { font-size: %1px; }" ).arg( pxSize + 9 ); + } + + return styleSheet; +} + + +void +HTMLView::set( const QString& data ) +{ + begin(); + setUserStyleSheet( loadStyleSheet() ); + write( data ); + end(); +} + + +void HTMLView::openURLRequest( const KURL &url ) +{ + // here, http urls are streams. For webpages we use externalurl + // NOTE there have been no links to streams! http now used for wiki tab. + if ( url.protocol() == "file" ) + Playlist::instance()->insertMedia( url, Playlist::DefaultOptions ); +} + +#include "htmlview.moc" diff --git a/amarok/src/htmlview.h b/amarok/src/htmlview.h new file mode 100644 index 00000000..b64dfcd8 --- /dev/null +++ b/amarok/src/htmlview.h @@ -0,0 +1,44 @@ +// (c) 2005 Christian Muehlhaeuser <chris@chris.de> +// (c) 2006 Seb Ruiz <me@sebruiz.net> +// License: GNU General Public License V2 + + +#ifndef AMAROK_HTMLVIEW_H +#define AMAROK_HTMLVIEW_H + +#include <khtml_events.h> +#include <khtml_part.h> +#include <khtmlview.h> + +class KAction; +class KTempFile; + +class HTMLView : public KHTMLPart +{ + Q_OBJECT + + public: + HTMLView( QWidget *parentWidget = 0, const char *widgetname = 0, const bool DNDEnabled = false, const bool JScriptEnabled = true ); + ~HTMLView(); + + static QString loadStyleSheet(); + static void openURLRequest(const KURL &url ); + void set( const QString& data ); + static void paletteChange(); + + private: + static KTempFile *m_bgGradientImage; + static KTempFile *m_headerGradientImage; + static KTempFile *m_shadowGradientImage; + static int m_instances; + + KAction *m_selectAll; + KAction *m_copy; + + private slots: + void enableCopyAction(); + void selectAll(); + void copyText(); +}; + +#endif /* AMAROK_HTMLVIEW_H */ diff --git a/amarok/src/iconloader.cpp b/amarok/src/iconloader.cpp new file mode 100644 index 00000000..9dea3659 --- /dev/null +++ b/amarok/src/iconloader.cpp @@ -0,0 +1,126 @@ +/*************************************************************************** + * Copyright (C) 2006 by Mark Kretschmann <markey@web.de> * + * * + * 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 program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#include "amarok.h" +#include "amarokconfig.h" + +#include <qmap.h> + + +QString +Amarok::icon( const QString& name ) //declared in amarok.h +{ + // We map our Amarok icon theme names to system icons, instead of using the same + // naming scheme. This has two advantages: + // 1. Our icons can have simpler and more meaningful names + // 2. We can map several of our icons to one system icon, if necessary + static QMap<QString, QString> iconMap; + + if( iconMap.empty() ) { + iconMap["add_lyrics"] = "edit_add"; + iconMap["add_playlist"] = "1downarrow"; + iconMap["album"] = "cdrom_unmount"; + iconMap["artist"] = "personal"; + iconMap["audioscrobbler"] = "audioscrobbler"; + iconMap["love"] = "bookmark"; + iconMap["back"] = "player_start"; + iconMap["burn"] = "cdrom_unmount"; + iconMap["change_language"] = "configure"; + iconMap["clock"] = "history"; + iconMap["collection"] = "collection"; + iconMap["configure"] = "configure"; + iconMap["covermanager"] = "covermanager"; + iconMap["device"] = "usbpendrive_unmount"; + iconMap["download"] = "khtml_kget"; + iconMap["dynamic"] = "dynamic"; + iconMap["edit"] = "edit"; + iconMap["editcopy"] = "editcopy"; + iconMap["equalizer"] = "equalizer"; + iconMap["external"] = "exec"; + iconMap["fastforward"] = "2rightarrow"; + iconMap["favourite_genres"] = "kfm"; + iconMap["files"] = "folder"; + iconMap["files2"] = "folder_red"; + iconMap["info"] = "info"; + iconMap["lyrics"] = "document"; + iconMap["magnatune"] = "cd"; + iconMap["mostplayed"] = "favorites"; + iconMap["music"] = "today"; + iconMap["next"] = "player_end"; + iconMap["pause"] = "player_pause"; + iconMap["play"] = "player_play"; + iconMap["playlist"] = "player_playlist_2"; + iconMap["playlist_clear"] = "view_remove"; + iconMap["playlist_refresh"] = "rebuild"; + iconMap["queue"] = "goto"; + iconMap["queue_track"] = "2rightarrow"; + iconMap["dequeue_track"] = "2leftarrow"; + iconMap["random"] = "random"; + iconMap["random_album"] = "cd"; + iconMap["random_no"] = "forward"; + iconMap["random_track"] = "random"; + iconMap["redo"] = "redo"; + iconMap["refresh"] = "reload"; + iconMap["remove"] = "editdelete"; + iconMap["remove_from_playlist"] = "remove"; + iconMap["repeat_album"] = "cdrom_unmount"; + iconMap["repeat_no"] = "bottom"; + iconMap["repeat_playlist"] = "repeat_playlist"; + iconMap["repeat_track"] = "repeat_track"; + iconMap["rescan"] = "reload"; + iconMap["rewind"] = "2leftarrow"; + iconMap["save"] = "filesave"; + iconMap["scripts"] = "pencil"; + iconMap["search"] = "find"; + iconMap["settings_engine"] = "amarok"; + iconMap["settings_general"] = "misc"; + iconMap["settings_indicator"] = "tv"; + iconMap["settings_playback"] = "kmix"; + iconMap["settings_view"] = "colors"; + iconMap["stop"] = "player_stop"; + iconMap["podcast"] = "podcast"; + iconMap["podcast2"] = "podcast_new"; + iconMap["track"] = "sound"; + iconMap["undo"] = "undo"; + iconMap["visualizations"] = "visualizations"; + iconMap["zoom"] = "find"; + } + + static QMap<QString, QString> amarokMap; + if( amarokMap.empty() ) { + amarokMap["queue_track"] = "fastforward"; + amarokMap["dequeue_track"] = "rewind"; + } + + if( iconMap.contains( name ) ) + { + if( AmarokConfig::useCustomIconTheme() ) + { + if( amarokMap.contains( name ) ) + return QString( "amarok_" ) + amarokMap[name]; + return QString( "amarok_" ) + name; + } + else + return iconMap[name]; + } + + return name; +} + + diff --git a/amarok/src/images/Makefile.am b/amarok/src/images/Makefile.am new file mode 100644 index 00000000..3b204121 --- /dev/null +++ b/amarok/src/images/Makefile.am @@ -0,0 +1,50 @@ +SUBDIRS = \ + icons + +imagesdir = \ + $(kde_datadir)/amarok/images + +images_DATA = \ + amarok_cut.png \ + amarok_rocks.jpg \ + b_next.png \ + b_pause.png \ + b_play.png \ + b_prev.png \ + b_stop.png \ + back_stars_grey.png \ + currenttrack_bar_left.png \ + currenttrack_bar_mid.png \ + currenttrack_bar_right.png \ + currenttrack_play.png \ + currenttrack_pause.png \ + currenttrack_stop.png \ + currenttrack_stop_small.png \ + currenttrack_repeat.png \ + currenttrack_repeat_small.png \ + eq_active2.png \ + eq_inactive2.png \ + lastfm.png \ + loading1.png \ + loading2.png \ + menu_sidepixmap.png \ + more_albums.png \ + musicbrainz.png \ + nocover.png \ + pl_active2.png \ + pl_inactive2.png \ + shadow_albumcover.png \ + sbinner_stars.png \ + smallstar.png \ + splash_screen.jpg \ + star.png \ + time_minus.png \ + time_plus.png \ + vol_speaker.png \ + volumeslider-gradient.png \ + volumeslider-handle.png \ + volumeslider-handle_glow.png \ + volumeslider-inset.png \ + wizard_compact.png \ + wizard_xmms.png \ + xine_logo.png diff --git a/amarok/src/images/amarok_cut.png b/amarok/src/images/amarok_cut.png new file mode 100644 index 0000000000000000000000000000000000000000..9aa15acf9c73305283921ee1d0704babb0e9da68 GIT binary patch literal 6279 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 literal 0 HcmV?d00001 diff --git a/amarok/src/images/amarok_icon.svg b/amarok/src/images/amarok_icon.svg new file mode 100644 index 00000000..60a267b6 --- /dev/null +++ b/amarok/src/images/amarok_icon.svg @@ -0,0 +1,933 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://web.resource.org/cc/" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="128" + height="128" + viewBox="0 0 854.238 854.238" + overflow="visible" + enable-background="new 0 0 854.238 854.238" + xml:space="preserve" + id="svg2" + sodipodi:version="0.32" + inkscape:version="0.45" + sodipodi:docname="amarok_icon_oxygen.svg" + sodipodi:docbase="/home/knome/Work/Amarok logo" + inkscape:output_extension="org.inkscape.output.svg.inkscape" + sodipodi:modified="TRUE"><sodipodi:namedview + inkscape:window-height="688" + inkscape:window-width="1258" + inkscape:pageshadow="2" + inkscape:pageopacity="0.0" + guidetolerance="10.0" + gridtolerance="10.0" + objecttolerance="10.0" + borderopacity="1.0" + bordercolor="#666666" + pagecolor="#ffffff" + id="base" + inkscape:zoom="2.5708446" + inkscape:cx="168.23042" + inkscape:cy="57.765811" + inkscape:window-x="1280" + inkscape:window-y="26" + inkscape:current-layer="svg2" /> + <metadata + id="metadata7"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + </cc:Work> + </rdf:RDF> + </metadata> + <defs + id="defs217"><radialGradient + gradientTransform="matrix(1.1256295,0,0,1.1256295,-41.873391,-39.54673)" + id="radialGradient29545" + cx="375.21439" + cy="783.16357" + r="300.5752" + fx="375.21439" + fy="783.16357" + gradientUnits="userSpaceOnUse"> + <stop + offset="0.0112" + style="stop-color:#eeeeef;stop-opacity:0.94117647;" + id="stop29547" /> + <stop + offset="0.29210001" + style="stop-color:#6193cf;stop-opacity:1;" + id="stop29549" /> + <stop + offset="0.45750001" + style="stop-color:#2c72c7;stop-opacity:1;" + id="stop29551" /> + <stop + offset="0.6767" + style="stop-color:#0057ae;stop-opacity:1;" + id="stop29553" /> + <stop + offset="0.8653" + style="stop-color:#004d9a;stop-opacity:1;" + id="stop29555" /> + <stop + offset="1" + style="stop-color:#00438a;stop-opacity:1;" + id="stop29557" /> + </radialGradient><linearGradient + id="linearGradient5498"><stop + style="stop-color:#eeeeee;stop-opacity:1;" + offset="0" + id="stop5500" /><stop + style="stop-color:#eeeeee;stop-opacity:0;" + offset="1" + id="stop5502" /></linearGradient><radialGradient + gradientUnits="userSpaceOnUse" + fy="783.16357" + fx="375.21439" + r="300.5752" + cy="783.16357" + cx="375.21439" + id="XMLID_25_" + gradientTransform="matrix(1.1256295,0,0,1.1256295,-41.873391,-39.54673)"> + <stop + id="stop46" + style="stop-color:#D2E8EC" + offset="0.0112" /> + <stop + id="stop48" + style="stop-color:#60A8CE" + offset="0.2921" /> + <stop + id="stop50" + style="stop-color:#4680A8" + offset="0.4575" /> + <stop + id="stop52" + style="stop-color:#2F5C84" + offset="0.6767" /> + <stop + id="stop54" + style="stop-color:#234970" + offset="0.8653" /> + <stop + id="stop56" + style="stop-color:#1F426A" + offset="1" /> + </radialGradient><mask + id="XMLID_26_" + height="44.813" + width="93.126" + y="461.543" + x="155.705" + maskUnits="userSpaceOnUse"> + <g + id="g69"> + <linearGradient + y2="466.75449" + x2="200.0733" + y1="562.85791" + x1="211.7222" + gradientUnits="userSpaceOnUse" + id="XMLID_1_"> + <stop + id="stop72" + style="stop-color:#FFFFFF" + offset="0" /> + <stop + id="stop74" + style="stop-color:#000000" + offset="1" /> + </linearGradient> + <ellipse + style="fill:url(#XMLID_1_)" + id="ellipse76" + ry="59.682999" + rx="80.542" + cy="495.98801" + cx="203.617" /> + </g> + </mask><mask + id="XMLID_2_" + height="127.553" + width="156.907" + y="425.034" + x="101.57" + maskUnits="userSpaceOnUse"> + <g + id="g86"> + <linearGradient + y2="563.47321" + x2="186.92191" + y1="397.62061" + x1="206.09081" + gradientUnits="userSpaceOnUse" + id="XMLID_3_"> + <stop + id="stop89" + style="stop-color:#FFFFFF" + offset="0" /> + <stop + id="stop91" + style="stop-color:#CBCBCB" + offset="0.1111" /> + <stop + id="stop93" + style="stop-color:#969696" + offset="0.2396" /> + <stop + id="stop95" + style="stop-color:#686868" + offset="0.3698" /> + <stop + id="stop97" + style="stop-color:#424242" + offset="0.4991" /> + <stop + id="stop99" + style="stop-color:#252525" + offset="0.6273" /> + <stop + id="stop101" + style="stop-color:#111111" + offset="0.7542" /> + <stop + id="stop103" + style="stop-color:#040404" + offset="0.8792" /> + <stop + id="stop105" + style="stop-color:#000000" + offset="1" /> + </linearGradient> + <ellipse + style="fill:url(#XMLID_3_)" + id="ellipse107" + ry="123.604" + rx="160.94" + cy="495.62299" + cx="194.76401" /> + </g> + </mask><linearGradient + gradientTransform="matrix(-1.1256295,0,0,1.1256295,1028.9091,-39.54673)" + y2="643.30768" + x2="480.12021" + y1="815.45459" + x1="429.84909" + gradientUnits="userSpaceOnUse" + id="XMLID_4_"> + <stop + id="stop112" + style="stop-color:#FFFFFF" + offset="0" /> + <stop + id="stop114" + style="stop-color:#E5EAF2" + offset="1" /> + </linearGradient><linearGradient + y2="560.35498" + x2="278.14471" + y1="634.63232" + x1="220.9512" + gradientUnits="userSpaceOnUse" + id="XMLID_5_"> + <stop + id="stop119" + style="stop-color:#FFFFFF" + offset="0" /> + <stop + id="stop121" + style="stop-color:#193D6B" + offset="1" /> + </linearGradient><linearGradient + y2="565.86761" + x2="328.18469" + y1="684.01898" + x1="237.20799" + gradientUnits="userSpaceOnUse" + id="XMLID_6_"> + <stop + id="stop126" + style="stop-color:#FFFFFF" + offset="0" /> + <stop + id="stop128" + style="stop-color:#193D6B" + offset="1" /> + </linearGradient><linearGradient + y2="612.60522" + x2="335.09619" + y1="704.23578" + x1="264.5405" + gradientUnits="userSpaceOnUse" + id="XMLID_7_"> + <stop + id="stop133" + style="stop-color:#FFFFFF" + offset="0" /> + <stop + id="stop135" + style="stop-color:#193D6B" + offset="1" /> + </linearGradient><linearGradient + y2="626.00598" + x2="373.12509" + y1="713.54639" + x1="319.186" + gradientUnits="userSpaceOnUse" + id="XMLID_8_"> + <stop + id="stop140" + style="stop-color:#FFFFFF" + offset="0" /> + <stop + id="stop142" + style="stop-color:#193D6B" + offset="1" /> + </linearGradient><linearGradient + y2="688.75092" + x2="409.85471" + y1="790.51508" + x1="331.49609" + gradientUnits="userSpaceOnUse" + id="XMLID_9_"> + <stop + id="stop147" + style="stop-color:#FFFFFF" + offset="0" /> + <stop + id="stop149" + style="stop-color:#193D6B" + offset="1" /> + </linearGradient><mask + id="XMLID_10_" + height="527.73" + width="447.197" + y="217.631" + x="191.541" + maskUnits="userSpaceOnUse"> + <g + id="g159"> + <radialGradient + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.931,0,0,0.9299,28.7459,21.6629)" + fy="155.23289" + fx="657.2124" + r="606.95343" + cy="155.23289" + cx="657.2124" + id="XMLID_11_"> + <stop + id="stop162" + style="stop-color:#C9C9C8" + offset="0" /> + <stop + id="stop164" + style="stop-color:#C4C4C4" + offset="0.0881" /> + <stop + id="stop166" + style="stop-color:#B8B8B7" + offset="0.2192" /> + <stop + id="stop168" + style="stop-color:#A1A0A0" + offset="0.3769" /> + <stop + id="stop170" + style="stop-color:#7F7F7E" + offset="0.5556" /> + <stop + id="stop172" + style="stop-color:#4F4F4E" + offset="0.7517" /> + <stop + id="stop174" + style="stop-color:#121212" + offset="0.9595" /> + <stop + id="stop176" + style="stop-color:#000000" + offset="1" /> + </radialGradient> + <circle + style="fill:url(#XMLID_11_)" + id="circle178" + r="302.95401" + cy="444.22601" + cx="408.427" /> + </g> + </mask><mask + id="XMLID_13_" + height="187.183" + width="252.506" + y="279.317" + x="243.061" + maskUnits="userSpaceOnUse"> + <g + style="filter:url(#Adobe_OpacityMaskFilter_4_)" + id="g193"> + <linearGradient + y2="417.81519" + x2="372.38071" + y1="266.93359" + x1="340.0488" + gradientUnits="userSpaceOnUse" + id="XMLID_14_"> + <stop + id="stop196" + style="stop-color:#FFFFFF" + offset="0" /> + <stop + id="stop198" + style="stop-color:#F8F8F8" + offset="0.0867" /> + <stop + id="stop200" + style="stop-color:#E4E4E4" + offset="0.2156" /> + <stop + id="stop202" + style="stop-color:#C2C2C2" + offset="0.3708" /> + <stop + id="stop204" + style="stop-color:#949494" + offset="0.5465" /> + <stop + id="stop206" + style="stop-color:#595959" + offset="0.7394" /> + <stop + id="stop208" + style="stop-color:#151515" + offset="0.9438" /> + <stop + id="stop210" + style="stop-color:#000000" + offset="0.9944" /> + </linearGradient> + <ellipse + style="fill:url(#XMLID_14_)" + id="ellipse212" + ry="183.32201" + rx="215.32401" + cy="374.28201" + cx="363.052" /> + </g> + </mask><mask + id="XMLID_23_" + height="587.491" + width="576.609" + y="97.149" + x="122.963" + maskUnits="userSpaceOnUse"> + <g + style="filter:url(#Adobe_OpacityMaskFilter)" + id="g31"> + <radialGradient + gradientUnits="userSpaceOnUse" + fy="-20.899401" + fx="88.977501" + r="673.11609" + cy="-20.899401" + cx="88.977501" + id="XMLID_24_"> + <stop + id="stop34" + style="stop-color:#FFFFFF" + offset="0.309" /> + <stop + id="stop36" + style="stop-color:#000000" + offset="1" /> + </radialGradient> + <ellipse + style="fill:url(#XMLID_24_)" + id="ellipse38" + ry="349.10599" + rx="512.93402" + cy="286.172" + cx="295.89499" /> + </g> + </mask><radialGradient + id="XMLID_22_" + cx="622.72803" + cy="621.17328" + r="510.71881" + fx="622.72803" + fy="621.17328" + gradientUnits="userSpaceOnUse"> + <stop + offset="0" + style="stop-color:#FFFFFF" + id="stop14" /> + <stop + offset="0.2814" + style="stop-color:#E6E6E6" + id="stop16" /> + <stop + offset="0.7515" + style="stop-color:#C0C0C0" + id="stop18" /> + <stop + offset="1" + style="stop-color:#B2B2B2" + id="stop20" /> + </radialGradient><linearGradient + inkscape:collect="always" + xlink:href="#XMLID_9_" + id="linearGradient12328" + gradientUnits="userSpaceOnUse" + x1="331.49609" + y1="790.51508" + x2="409.85471" + y2="688.75092" + gradientTransform="matrix(1.1256295,0,0,1.1256295,-41.873391,-39.54673)" /><linearGradient + inkscape:collect="always" + xlink:href="#XMLID_8_" + id="linearGradient12331" + gradientUnits="userSpaceOnUse" + x1="319.186" + y1="713.54639" + x2="373.12509" + y2="626.00598" + gradientTransform="matrix(1.1256295,0,0,1.1256295,-41.873391,-39.54673)" /><linearGradient + inkscape:collect="always" + xlink:href="#XMLID_7_" + id="linearGradient12334" + gradientUnits="userSpaceOnUse" + x1="264.5405" + y1="704.23578" + x2="335.09619" + y2="612.60522" + gradientTransform="matrix(1.1256295,0,0,1.1256295,-41.873391,-39.54673)" /><linearGradient + inkscape:collect="always" + xlink:href="#XMLID_6_" + id="linearGradient12337" + gradientUnits="userSpaceOnUse" + x1="237.20799" + y1="684.01898" + x2="328.18469" + y2="565.86761" + gradientTransform="matrix(1.1256295,0,0,1.1256295,-41.873391,-39.54673)" /><linearGradient + inkscape:collect="always" + xlink:href="#XMLID_5_" + id="linearGradient12340" + gradientUnits="userSpaceOnUse" + x1="220.9512" + y1="634.63232" + x2="278.14471" + y2="560.35498" + gradientTransform="matrix(1.1256295,0,0,1.1256295,-41.873391,-39.54673)" /><linearGradient + inkscape:collect="always" + xlink:href="#XMLID_4_" + id="linearGradient12343" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(-1.1256295,0,0,1.1256295,1028.9091,-39.54673)" + x1="429.84909" + y1="815.45459" + x2="480.12021" + y2="643.30768" /><radialGradient + inkscape:collect="always" + xlink:href="#radialGradient29545" + id="radialGradient12349" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.1256295,0,0,1.1256295,-41.873391,-39.54673)" + cx="375.21439" + cy="783.16357" + fx="375.21439" + fy="783.16357" + r="300.5752" /><linearGradient + inkscape:collect="always" + xlink:href="#linearGradient5498" + id="linearGradient17820" + gradientUnits="userSpaceOnUse" + x1="1508.4957" + y1="-363.42487" + x2="361.1441" + y2="630.52039" /><linearGradient + inkscape:collect="always" + xlink:href="#linearGradient5498" + id="linearGradient2486" + x1="370.62177" + y1="-309.6048" + x2="370.20648" + y2="458.38397" + gradientUnits="userSpaceOnUse" /><linearGradient + inkscape:collect="always" + xlink:href="#linearGradient5498" + id="linearGradient5490" + x1="247.47713" + y1="274.88394" + x2="130.31757" + y2="580.54669" + gradientUnits="userSpaceOnUse" /><linearGradient + inkscape:collect="always" + xlink:href="#linearGradient5498" + id="linearGradient6539" + x1="220.36789" + y1="756.76105" + x2="185.21225" + y2="491.06723" + gradientUnits="userSpaceOnUse" /><radialGradient + inkscape:collect="always" + xlink:href="#radialGradient29545" + id="radialGradient33274" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.1256295,0,0,1.1256295,-41.873391,-39.54673)" + cx="375.21439" + cy="783.16357" + fx="375.21439" + fy="783.16357" + r="300.5752" /><linearGradient + inkscape:collect="always" + xlink:href="#XMLID_4_" + id="linearGradient33276" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(-1.1256295,0,0,1.1256295,1028.9091,-39.54673)" + x1="429.84909" + y1="815.45459" + x2="480.12021" + y2="643.30768" /><linearGradient + inkscape:collect="always" + xlink:href="#XMLID_5_" + id="linearGradient33278" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.1256295,0,0,1.1256295,-41.873391,-39.54673)" + x1="220.9512" + y1="634.63232" + x2="278.14471" + y2="560.35498" /><linearGradient + inkscape:collect="always" + xlink:href="#XMLID_6_" + id="linearGradient33280" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.1256295,0,0,1.1256295,-41.873391,-39.54673)" + x1="237.20799" + y1="684.01898" + x2="328.18469" + y2="565.86761" /><linearGradient + inkscape:collect="always" + xlink:href="#XMLID_7_" + id="linearGradient33282" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.1256295,0,0,1.1256295,-41.873391,-39.54673)" + x1="264.5405" + y1="704.23578" + x2="335.09619" + y2="612.60522" /><linearGradient + inkscape:collect="always" + xlink:href="#XMLID_8_" + id="linearGradient33284" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.1256295,0,0,1.1256295,-41.873391,-39.54673)" + x1="319.186" + y1="713.54639" + x2="373.12509" + y2="626.00598" /><linearGradient + inkscape:collect="always" + xlink:href="#XMLID_9_" + id="linearGradient33286" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.1256295,0,0,1.1256295,-41.873391,-39.54673)" + x1="331.49609" + y1="790.51508" + x2="409.85471" + y2="688.75092" /><linearGradient + inkscape:collect="always" + xlink:href="#linearGradient5498" + id="linearGradient33288" + gradientUnits="userSpaceOnUse" + x1="1508.4957" + y1="-363.42487" + x2="361.1441" + y2="630.52039" /><linearGradient + inkscape:collect="always" + xlink:href="#linearGradient5498" + id="linearGradient33290" + gradientUnits="userSpaceOnUse" + x1="370.62177" + y1="-309.6048" + x2="370.20648" + y2="458.38397" /><linearGradient + inkscape:collect="always" + xlink:href="#linearGradient5498" + id="linearGradient33292" + gradientUnits="userSpaceOnUse" + x1="247.47713" + y1="274.88394" + x2="130.31757" + y2="580.54669" /><linearGradient + inkscape:collect="always" + xlink:href="#linearGradient5498" + id="linearGradient33294" + gradientUnits="userSpaceOnUse" + x1="220.36789" + y1="756.76105" + x2="185.21225" + y2="491.06723" /><linearGradient + inkscape:collect="always" + xlink:href="#linearGradient5498" + id="linearGradient33297" + gradientUnits="userSpaceOnUse" + x1="220.36789" + y1="756.76105" + x2="185.21225" + y2="491.06723" /><linearGradient + inkscape:collect="always" + xlink:href="#linearGradient5498" + id="linearGradient33300" + gradientUnits="userSpaceOnUse" + x1="247.47713" + y1="274.88394" + x2="130.31757" + y2="580.54669" /><linearGradient + inkscape:collect="always" + xlink:href="#linearGradient5498" + id="linearGradient33303" + gradientUnits="userSpaceOnUse" + x1="370.62177" + y1="-309.6048" + x2="370.20648" + y2="458.38397" /><linearGradient + inkscape:collect="always" + xlink:href="#linearGradient5498" + id="linearGradient33306" + gradientUnits="userSpaceOnUse" + x1="1508.4957" + y1="-363.42487" + x2="361.1441" + y2="630.52039" /><linearGradient + inkscape:collect="always" + xlink:href="#XMLID_9_" + id="linearGradient33309" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.1256295,0,0,1.1256295,-41.873391,-39.54673)" + x1="331.49609" + y1="790.51508" + x2="409.85471" + y2="688.75092" /><linearGradient + inkscape:collect="always" + xlink:href="#XMLID_8_" + id="linearGradient33312" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.1256295,0,0,1.1256295,-41.873391,-39.54673)" + x1="319.186" + y1="713.54639" + x2="373.12509" + y2="626.00598" /><linearGradient + inkscape:collect="always" + xlink:href="#XMLID_7_" + id="linearGradient33315" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.1256295,0,0,1.1256295,-41.873391,-39.54673)" + x1="264.5405" + y1="704.23578" + x2="335.09619" + y2="612.60522" /><linearGradient + inkscape:collect="always" + xlink:href="#XMLID_6_" + id="linearGradient33318" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.1256295,0,0,1.1256295,-41.873391,-39.54673)" + x1="237.20799" + y1="684.01898" + x2="328.18469" + y2="565.86761" /><linearGradient + inkscape:collect="always" + xlink:href="#XMLID_5_" + id="linearGradient33321" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.1256295,0,0,1.1256295,-41.873391,-39.54673)" + x1="220.9512" + y1="634.63232" + x2="278.14471" + y2="560.35498" /><linearGradient + inkscape:collect="always" + xlink:href="#XMLID_4_" + id="linearGradient33324" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(-1.1256295,0,0,1.1256295,1028.9091,-39.54673)" + x1="429.84909" + y1="815.45459" + x2="480.12021" + y2="643.30768" /><radialGradient + inkscape:collect="always" + xlink:href="#radialGradient29545" + id="radialGradient33328" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.1256295,0,0,1.1256295,-41.873391,-39.54673)" + cx="375.21439" + cy="783.16357" + fx="375.21439" + fy="783.16357" + r="300.5752" /><linearGradient + inkscape:collect="always" + xlink:href="#linearGradient5498" + id="linearGradient2295" + gradientUnits="userSpaceOnUse" + x1="220.36789" + y1="756.76105" + x2="185.21225" + y2="491.06723" /><linearGradient + inkscape:collect="always" + xlink:href="#linearGradient5498" + id="linearGradient2298" + gradientUnits="userSpaceOnUse" + x1="247.47713" + y1="274.88394" + x2="130.31757" + y2="580.54669" /><linearGradient + inkscape:collect="always" + xlink:href="#linearGradient5498" + id="linearGradient2301" + gradientUnits="userSpaceOnUse" + x1="370.62177" + y1="-309.6048" + x2="370.20648" + y2="458.38397" /><linearGradient + inkscape:collect="always" + xlink:href="#linearGradient5498" + id="linearGradient2304" + gradientUnits="userSpaceOnUse" + x1="1508.4957" + y1="-363.42487" + x2="361.1441" + y2="630.52039" /><linearGradient + inkscape:collect="always" + xlink:href="#XMLID_5_" + id="linearGradient2307" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.1256295,0,0,1.1256295,-41.873391,-39.54673)" + x1="331.49609" + y1="790.51508" + x2="409.85471" + y2="688.75092" /><linearGradient + inkscape:collect="always" + xlink:href="#XMLID_5_" + id="linearGradient2310" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.1256295,0,0,1.1256295,-41.873391,-39.54673)" + x1="319.186" + y1="713.54639" + x2="373.12509" + y2="626.00598" /><linearGradient + inkscape:collect="always" + xlink:href="#XMLID_5_" + id="linearGradient2313" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.1256295,0,0,1.1256295,-41.873391,-39.54673)" + x1="264.5405" + y1="704.23578" + x2="335.09619" + y2="612.60522" /><linearGradient + inkscape:collect="always" + xlink:href="#XMLID_5_" + id="linearGradient2316" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.1256295,0,0,1.1256295,-41.873391,-39.54673)" + x1="237.20799" + y1="684.01898" + x2="328.18469" + y2="565.86761" /><linearGradient + inkscape:collect="always" + xlink:href="#XMLID_5_" + id="linearGradient2319" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.1256295,0,0,1.1256295,-41.873391,-39.54673)" + x1="220.9512" + y1="634.63232" + x2="278.14471" + y2="560.35498" /><linearGradient + inkscape:collect="always" + xlink:href="#XMLID_4_" + id="linearGradient2322" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(-1.1256295,0,0,1.1256295,1028.9091,-39.54673)" + x1="429.84909" + y1="815.45459" + x2="480.12021" + y2="643.30768" /><radialGradient + inkscape:collect="always" + xlink:href="#radialGradient29545" + id="radialGradient2326" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.1256295,0,0,1.1256295,-41.873391,-39.54673)" + cx="375.21439" + cy="783.16357" + fx="375.21439" + fy="783.16357" + r="300.5752" /><radialGradient + inkscape:collect="always" + xlink:href="#XMLID_22_" + id="radialGradient5267" + cx="663.91144" + cy="649.22662" + fx="663.91144" + fy="649.22662" + r="427.12018" + gradientTransform="translate(0,-4.9999861e-6)" + gradientUnits="userSpaceOnUse" /></defs> + +<g + id="g2305" + inkscape:export-filename="/home/knome/Work/Amarok logo/oxygen-icon.png" + inkscape:export-xdpi="421.87378" + inkscape:export-ydpi="421.87378"><path + id="circle22" + d="M 840.16762,427.119 C 840.16762,655.12184 655.12184,840.16763 427.119,840.16763 C 199.11616,840.16763 14.070375,655.12184 14.070375,427.119 C 14.070375,199.11616 199.11616,14.070375 427.119,14.070375 C 655.12184,14.070375 840.16762,199.11616 840.16762,427.119 z " + style="fill:url(#radialGradient5267);stroke:#0057ae;stroke-width:28.14313698;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;fill-opacity:1.0" /><path + id="ellipse40" + d="M 699.57098,390.89499 C 699.57098,553.04278 570.4108,684.64099 411.267,684.64099 C 252.1232,684.64099 122.96301,553.04278 122.96301,390.89499 C 122.96301,228.7472 252.1232,97.148987 411.267,97.148987 C 570.4108,97.148987 699.57098,228.7472 699.57098,390.89499 z " + mask="url(#XMLID_23_)" + style="opacity:0.86000001;fill:#eeeeee" + transform="matrix(1.1256295,0,0,1.1256295,-41.873391,-39.54673)" /><path + id="path43" + d="M 654.69879,222.73733 C 637.51042,222.73733 604.13438,258.58412 591.62864,264.24379 C 579.11727,269.90345 480.08889,334.04295 470.70564,345.36228 C 461.3269,356.68048 448.81777,361.398 435.26745,360.45472 C 421.71824,359.51144 378.87904,359.12422 364.2875,366.67044 C 349.69259,374.21666 283.08009,435.91467 266.40051,443.46201 C 249.72318,451.00598 227.96364,454.87365 217.54031,455.8158 C 207.11698,456.7602 172.58492,491.56804 163.2028,498.17324 C 153.8218,504.7728 85.234943,531.21834 74.81274,536.87576 C 64.388285,542.53655 136.10214,632.11302 148.61126,633.99957 C 161.11813,635.88725 136.32389,645.13542 112.25793,645.13542 C 109.6082,645.13542 146.65154,742.275 272.95618,804.97144 C 382.14786,859.1705 470.12031,826.25709 513.14637,813.11199 C 543.56989,803.81767 549.0866,799.6731 550.13231,784.58066 C 551.17577,769.48934 553.06007,757.5633 555.14586,740.58543 C 557.23165,723.60981 564.52348,581.1749 591.62977,543.44605 C 618.72817,505.71608 696.19287,460.423 723.29352,455.70886 C 750.39643,450.99248 745.18589,441.56083 745.18589,433.07133 C 745.18589,424.58183 741.01543,405.71628 733.7191,402.88645 C 726.42165,400.05774 697.94997,417.05024 688.56898,418.9368 C 679.18686,420.82335 634.36542,423.65319 628.11029,414.22154 C 621.85629,404.78764 678.67695,308.57445 685.97328,297.25625 C 693.26736,285.93692 696.39548,270.84448 682.8474,254.80989 C 669.29145,238.77192 659.90933,222.73733 654.69879,222.73733 z " + style="opacity:0.22000002;fill:#323232" /><path + id="path58" + d="M 618.12146,172.83479 C 601.44751,172.83479 569.07553,211.25928 556.94237,217.3253 C 544.80921,223.39244 448.75361,292.14477 439.65402,304.27905 C 430.55444,316.41109 418.42015,321.46741 405.2773,320.4566 C 392.13445,319.44691 350.58296,319.03043 336.42817,327.1192 C 322.27113,335.2091 257.65887,401.34434 241.48132,409.43423 C 225.3049,417.52076 204.19935,421.6642 194.08782,422.67614 C 183.97854,423.68583 150.48318,460.99707 141.38359,468.07615 C 132.28401,475.15524 60.378793,506.26989 50.268389,512.33366 C 40.156859,518.4008 124.03202,603.36669 136.16518,605.38719 C 148.29834,607.4122 127.8085,653.4482 104.46407,653.4482 C 101.89313,653.4482 125.32536,729.73323 247.83775,796.93894 C 353.75161,855.03831 466.12658,829.3143 507.85929,815.22592 C 537.37105,805.2596 516.65834,799.75189 517.67141,783.57547 C 518.67997,767.3968 519.52982,746.12465 521.55258,727.9266 C 523.57646,709.72742 530.65104,557.05041 556.94124,516.60767 C 583.22807,476.16718 658.36384,427.61653 684.65291,422.56133 C 710.94312,417.50612 705.88566,407.39459 705.88566,398.29613 C 705.88566,389.19767 701.8424,368.97461 694.76332,365.94104 C 687.68648,362.90859 660.07029,381.12353 650.9707,383.14516 C 641.87111,385.16679 598.39367,388.20149 592.32541,378.09108 C 586.25939,367.97843 641.37358,264.84713 648.45154,252.71396 C 655.53063,240.57968 658.56195,224.40101 645.41685,207.21489 C 632.27737,190.02316 623.17666,172.83479 618.12146,172.83479 z " + style="fill:url(#radialGradient2326);fill-opacity:1" /><path + id="path60" + d="M 416.43229,363.42863 C 416.43229,363.42863 376.37564,357.26468 353.26309,380.37836 C 330.14941,403.49091 310.11546,454.33672 311.66095,469.74546 C 313.19968,485.15421 359.42591,417.35754 379.45423,412.73683 C 399.48706,408.11275 421.0575,374.21216 416.43229,363.42863 z " + style="fill:#ffffff" /><path + id="path116" + d="M 517.5791,576.1107 C 517.5791,576.1107 481.73456,698.13344 480.82843,700.01887 C 475.70569,710.66507 466.25715,741.5152 469.51022,751.47814 C 473.34074,763.21508 487.69814,780.27963 494.39902,788.8119 C 501.09989,797.34755 503.97137,801.61368 502.05667,804.8161 C 500.1431,808.01626 475.25431,808.01626 475.25431,808.01626 C 475.25431,808.01626 456.11073,779.21366 452.27909,768.54606 C 448.45194,757.88073 446.53612,735.47957 450.36664,728.0144 C 454.19491,720.54585 517.5791,576.1107 517.5791,576.1107 z " + style="opacity:0.18000004;fill:url(#linearGradient2322)" /><path + id="polygon123" + d="M 264.93381,598.84278 L 199.45032,655.53061 L 281.54923,606.66141 L 264.93381,598.84278 z " + style="opacity:0.2;fill:url(#linearGradient2319)" /><path + id="polygon130" + d="M 329.44139,579.29735 L 218.01983,723.9475 L 346.05568,589.06894 L 329.44139,579.29735 z " + style="opacity:0.2;fill:url(#linearGradient2316);fill-opacity:1.0" /><path + id="polygon137" + d="M 338.23818,629.14248 L 253.20588,764.01991 L 357.78474,637.93815 L 338.23818,629.14248 z " + style="opacity:0.20132015;fill:url(#linearGradient2313);fill-opacity:1.0" /><path + id="polygon144" + d="M 388.08331,623.27682 L 316.73416,790.41029 L 411.5403,628.16543 L 388.08331,623.27682 z " + style="opacity:0.2;fill:url(#linearGradient2310);fill-opacity:1.0" /><path + id="polygon151" + d="M 416.42779,667.25967 L 410.56213,819.73069 L 435.97322,669.21489 L 416.42779,667.25967 z " + style="opacity:0.2;fill:url(#linearGradient2307);fill-opacity:1.0" /><path + id="path2270" + d="M 420.91732,729.36642 C 351.30018,698.39383 288.50975,655.88934 234.26482,603.01627 C 228.17499,597.08044 226.8144,595.4965 226.8144,594.34262 C 226.8144,593.13125 228.55133,591.40592 239.05626,582.1824 C 260.44809,563.40004 265.15448,558.38692 268.80041,550.49987 C 272.52283,542.44735 280.21466,532.95491 295.21739,517.89884 C 328.33946,484.65899 388.46538,434.98491 416.82689,417.42901 C 421.75597,414.37788 424.58874,412.17246 428.48767,408.35063 C 440.85841,396.22453 460.01914,371.72167 499.93884,316.97838 C 542.43234,258.70557 556.73716,239.87049 570.88047,223.57002 C 579.4268,213.72016 585.7114,207.8731 588.1486,207.5041 C 589.18431,207.34729 590.90434,207.06941 591.9709,206.88659 C 593.03746,206.70377 594.40047,206.78803 594.99981,207.07385 C 595.59916,207.35966 596.47603,207.71632 596.94842,207.86643 C 597.85387,208.15415 615.10032,227.66836 621.45221,235.59225 C 629.06069,245.08374 629.74587,247.72667 625.93867,252.8978 C 624.51775,254.82777 623.14916,257.60325 622.0958,260.69106 C 620.28646,265.995 610.96485,284.8373 596.62206,312.18261 C 585.34543,333.68211 570.08225,364.07457 565.31216,374.52776 C 555.24252,396.59436 550.331,411.79539 551.61625,416.91622 C 552.03701,418.59268 554.5804,419.79199 561.41213,421.53535 C 577.39273,425.61339 592.16664,424.99594 605.9335,419.67469 C 608.01588,418.8698 612.90001,416.3756 616.78711,414.13203 C 628.65388,407.28274 637.59912,403.45373 648.76876,400.44235 C 654.10472,399.00376 655.37311,398.8526 662.48236,398.8081 C 669.94112,398.76141 670.43639,398.82084 672.82712,400.04901 C 674.99738,401.16392 675.39512,401.60914 675.75849,403.33033 C 676.57722,407.20845 676.72083,409.70195 676.15791,410.26491 C 675.85023,410.57255 675.59852,411.32821 675.59852,411.94417 C 675.59852,414.47076 669.52221,416.51234 652.58246,419.67732 C 634.81328,422.99725 633.95536,423.35495 621.58956,432.59958 C 593.90887,453.29357 550.84044,490.78155 535.5622,507.4802 C 528.63101,515.05579 518.00575,529.71705 503.68433,551.46683 C 471.5521,600.26572 449.29204,642.4077 443.34539,665.69842 C 442.16377,670.32635 438.83962,691.2303 435.82204,713.00897 C 433.03687,733.11041 432.91259,733.71553 431.57904,733.67009 C 430.99045,733.65005 426.19268,731.71337 420.91732,729.36642 z " + style="opacity:0.57999998;fill:url(#linearGradient2304);fill-opacity:1" /><path + id="path2476" + d="M 232.63545,457.72623 C 232.32648,456.61911 232.38894,446.89128 232.73423,442.33838 C 232.98128,439.08089 233.18774,438.00445 234.05625,435.44502 C 236.74093,427.53362 240.66616,423.12959 246.34405,421.65829 C 248.46188,421.1095 249.61118,420.54692 252.67377,418.55984 C 258.96449,414.47824 266.9936,407.70183 279.8434,395.62919 C 283.33012,392.35334 297.4753,378.44049 311.27713,364.71177 C 325.07896,350.98304 336.99177,339.3012 337.75003,338.75214 C 338.5083,338.20306 339.62503,337.62248 340.23165,337.46196 C 344.66331,336.28925 355.38062,335.72073 382.55689,335.21676 C 408.09255,334.74322 418.96719,334.25126 422.32093,333.41786 C 425.79448,332.5547 433.72051,327.86095 449.69823,317.2052 C 461.25088,309.50061 485.83784,292.50027 500.08576,282.36542 C 503.90907,279.64579 505.46448,278.68996 506.06672,278.68996 C 506.51435,278.68996 506.95729,278.56587 507.05101,278.41423 C 507.14472,278.26258 507.41122,278.13849 507.64324,278.13849 C 507.99016,278.13849 508.03868,278.42006 507.9166,279.72396 C 507.60339,283.0691 507.40571,283.75666 506.35404,285.15932 C 503.48868,288.98093 495.94139,295.14902 487.04407,300.94052 C 484.7598,302.4274 480.32374,305.63362 477.18606,308.06546 C 445.54219,332.59103 424.04481,347.10814 414.75017,350.22825 C 412.90728,350.8469 412.19408,350.93239 408.87631,350.93239 C 402.47717,350.93239 395.12723,352.3194 382.58568,355.89371 C 364.10099,361.16178 347.09763,368.07111 338.16364,373.94464 C 330.40581,379.04493 324.1723,388.83443 317.90944,405.75312 C 315.44537,412.40965 312.53953,422.24882 309.03897,435.78866 C 308.16672,439.16239 307.36897,442.05878 307.26619,442.22508 C 307.16342,442.39138 290.59105,446.05592 270.43873,450.36849 C 250.28639,454.68105 233.58513,458.27493 233.32479,458.35485 C 232.98198,458.4601 232.79187,458.28672 232.63545,457.72623 z " + style="fill:url(#linearGradient2301);fill-opacity:1" /><path + id="path5482" + d="M 137.72559,580.45986 C 137.34282,580.37465 136.58716,580.30043 136.04635,580.29491 C 133.6773,580.27075 126.95272,576.87395 119.71787,572.04689 C 111.87019,566.81093 105.56261,561.80559 98.411879,555.13967 C 96.972365,553.79776 94.464264,551.48807 92.838314,550.00703 C 88.471835,546.02972 81.735123,539.26156 79.754779,536.86241 C 76.134794,532.47692 74.197025,529.24921 73.930598,527.16116 C 73.525521,523.98649 73.539647,523.23809 74.056146,520.50727 C 74.173258,519.88813 75.704,518.55644 77.786876,517.26169 C 81.684875,514.83862 89.97768,509.21429 119.19592,489.17727 C 163.65173,458.69075 182.38099,446.29601 192.25667,440.8268 C 194.75874,439.44115 195.44804,439.28657 199.14319,439.28264 C 202.68422,439.27886 205.66402,439.59679 207.78132,440.20429 C 212.01481,441.41899 218.55894,443.9768 224.32907,446.6721 C 227.2094,448.01756 227.41617,448.14556 227.41617,448.58327 C 227.41617,449.00581 227.31681,449.08032 226.41575,449.33347 C 224.34431,449.91544 218.81508,451.7359 214.28011,453.32905 C 186.7276,463.00831 159.15855,476.3578 134.50683,491.95688 C 128.66425,495.65393 127.98593,496.15089 125.80375,498.33306 C 119.60523,504.53153 114.02491,514.21693 112.46913,521.47709 C 112.25189,522.49083 112.15177,523.69473 112.15082,525.30481 C 112.14929,527.87219 112.45637,529.2153 113.42141,530.86199 C 117.23617,537.37141 136.60237,543.51816 161.6507,546.16976 C 168.95857,546.94337 172.32439,547.12227 179.74358,547.13138 L 186.96406,547.14026 L 188.87793,546.47918 C 193.99516,544.7116 200.99731,540.71142 216.19398,530.8741 C 229.3623,522.34981 233.42675,519.86654 238.11641,517.4801 C 242.50747,515.24563 244.72611,514.58169 246.61672,514.93637 C 247.33648,515.0714 247.46896,515.47588 247.47685,517.56237 C 247.48827,520.57957 247.15877,521.72825 245.6302,523.9999 C 241.21384,530.56322 230.3452,539.48309 215.0564,549.09176 C 202.20767,557.16691 187.57399,564.6544 179.78048,567.14111 C 179.04248,567.37658 177.06305,567.88388 175.38173,568.26843 C 166.39674,570.32344 157.42239,573.5234 145.55502,578.90368 C 143.87492,579.6654 142.39182,580.22135 141.8143,580.30598 C 140.1115,580.55547 138.43805,580.61845 137.72559,580.45986 z " + style="opacity:0.7;fill:url(#linearGradient2298);fill-opacity:1" /><path + id="path6524" + d="M 168.63849,529.9315 C 168.4693,529.90931 166.27755,529.84827 163.76793,529.79586 C 159.24918,529.70149 156.41999,529.50691 152.38621,529.01309 C 143.03032,527.86773 136.79422,526.024 134.60723,523.75665 C 134.14337,523.27573 133.99029,522.70506 133.99765,521.4841 C 134.00721,519.89411 134.15346,519.274 134.8333,517.94054 C 137.71533,512.28764 146.00612,503.33611 155.76996,495.33536 C 157.91225,493.57991 161.46514,490.91334 163.30919,489.6769 L 164.6395,488.78491 L 194.83695,485.13213 L 225.03441,481.47934 L 227.0339,481.5935 C 228.89293,481.69963 230.48389,481.81725 234.5344,482.14805 C 236.40256,482.30062 236.54756,482.36826 236.3708,483.00474 C 236.31176,483.21731 236.26321,483.58448 236.26289,483.82069 C 236.26209,484.42647 235.31089,486.65231 233.98756,489.14512 C 226.84799,502.59401 212.42824,519.53378 202.83492,525.74201 C 200.0852,527.52146 198.15537,528.36324 195.67618,528.86463 C 193.14723,529.37607 189.91738,529.70056 186.3263,529.80398 C 182.79755,529.9056 169.18899,530.00371 168.63849,529.9315 z " + style="fill:url(#linearGradient2295);fill-opacity:1" /></g></svg> \ No newline at end of file diff --git a/amarok/src/images/amarok_icon_small.svg b/amarok/src/images/amarok_icon_small.svg new file mode 100644 index 00000000..6010c395 --- /dev/null +++ b/amarok/src/images/amarok_icon_small.svg @@ -0,0 +1,1133 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://web.resource.org/cc/" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="22" + height="22" + viewBox="0 0 854.238 854.238" + overflow="visible" + enable-background="new 0 0 854.238 854.238" + xml:space="preserve" + id="svg2" + sodipodi:version="0.32" + inkscape:version="0.45" + sodipodi:docname="amarok-icon-small_oxygen.svg" + sodipodi:docbase="/home/me" + inkscape:output_extension="org.inkscape.output.svg.inkscape" + version="1.0" + inkscape:export-filename="/home/me/amarok22.png" + inkscape:export-xdpi="12.643598" + inkscape:export-ydpi="12.643598" + sodipodi:modified="true"><sodipodi:namedview + inkscape:window-height="688" + inkscape:window-width="1022" + inkscape:pageshadow="2" + inkscape:pageopacity="0.0" + guidetolerance="10.0" + gridtolerance="10.0" + objecttolerance="10.0" + borderopacity="1.0" + bordercolor="#666666" + pagecolor="#ffffff" + id="base" + inkscape:zoom="3.7930793" + inkscape:cx="107.38014" + inkscape:cy="19.259016" + inkscape:window-x="0" + inkscape:window-y="0" + inkscape:current-layer="svg2" + width="22px" + height="22px" /> + <metadata + id="metadata7"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + </cc:Work> + </rdf:RDF> + </metadata> + <defs + id="defs217"><radialGradient + gradientTransform="matrix(1.1256295,0,0,1.1256295,-41.873391,-39.54673)" + id="radialGradient29545" + cx="375.21439" + cy="783.16357" + r="300.5752" + fx="375.21439" + fy="783.16357" + gradientUnits="userSpaceOnUse"> + <stop + offset="0.0112" + style="stop-color:#eeeeef;stop-opacity:0.94117647;" + id="stop29547" /> + <stop + offset="0.29210001" + style="stop-color:#6193cf;stop-opacity:1;" + id="stop29549" /> + <stop + offset="0.45750001" + style="stop-color:#2c72c7;stop-opacity:1;" + id="stop29551" /> + <stop + offset="0.6767" + style="stop-color:#0057ae;stop-opacity:1;" + id="stop29553" /> + <stop + offset="0.8653" + style="stop-color:#004d9a;stop-opacity:1;" + id="stop29555" /> + <stop + offset="1" + style="stop-color:#00438a;stop-opacity:1;" + id="stop29557" /> + </radialGradient><linearGradient + id="linearGradient5498"><stop + style="stop-color:#eeeeee;stop-opacity:1;" + offset="0" + id="stop5500" /><stop + style="stop-color:#eeeeee;stop-opacity:0;" + offset="1" + id="stop5502" /></linearGradient><radialGradient + gradientUnits="userSpaceOnUse" + fy="783.16357" + fx="375.21439" + r="300.5752" + cy="783.16357" + cx="375.21439" + id="XMLID_25_" + gradientTransform="matrix(1.1256295,0,0,1.1256295,-41.873391,-39.54673)"> + <stop + id="stop46" + style="stop-color:#D2E8EC" + offset="0.0112" /> + <stop + id="stop48" + style="stop-color:#60A8CE" + offset="0.2921" /> + <stop + id="stop50" + style="stop-color:#4680A8" + offset="0.4575" /> + <stop + id="stop52" + style="stop-color:#2F5C84" + offset="0.6767" /> + <stop + id="stop54" + style="stop-color:#234970" + offset="0.8653" /> + <stop + id="stop56" + style="stop-color:#1F426A" + offset="1" /> + </radialGradient><mask + id="XMLID_26_" + height="44.813" + width="93.126" + y="461.543" + x="155.705" + maskUnits="userSpaceOnUse"> + <g + id="g69"> + <linearGradient + y2="466.75449" + x2="200.0733" + y1="562.85791" + x1="211.7222" + gradientUnits="userSpaceOnUse" + id="XMLID_1_"> + <stop + id="stop72" + style="stop-color:#FFFFFF" + offset="0" /> + <stop + id="stop74" + style="stop-color:#000000" + offset="1" /> + </linearGradient> + <ellipse + style="fill:url(#XMLID_1_)" + id="ellipse76" + ry="59.682999" + rx="80.542" + cy="495.98801" + cx="203.617" + sodipodi:cx="203.617" + sodipodi:cy="495.98801" + sodipodi:rx="80.542" + sodipodi:ry="59.682999" /> + </g> + </mask><mask + id="XMLID_2_" + height="127.553" + width="156.907" + y="425.034" + x="101.57" + maskUnits="userSpaceOnUse"> + <g + id="g86"> + <linearGradient + y2="563.47321" + x2="186.92191" + y1="397.62061" + x1="206.09081" + gradientUnits="userSpaceOnUse" + id="XMLID_3_"> + <stop + id="stop89" + style="stop-color:#FFFFFF" + offset="0" /> + <stop + id="stop91" + style="stop-color:#CBCBCB" + offset="0.1111" /> + <stop + id="stop93" + style="stop-color:#969696" + offset="0.2396" /> + <stop + id="stop95" + style="stop-color:#686868" + offset="0.3698" /> + <stop + id="stop97" + style="stop-color:#424242" + offset="0.4991" /> + <stop + id="stop99" + style="stop-color:#252525" + offset="0.6273" /> + <stop + id="stop101" + style="stop-color:#111111" + offset="0.7542" /> + <stop + id="stop103" + style="stop-color:#040404" + offset="0.8792" /> + <stop + id="stop105" + style="stop-color:#000000" + offset="1" /> + </linearGradient> + <ellipse + style="fill:url(#XMLID_3_)" + id="ellipse107" + ry="123.604" + rx="160.94" + cy="495.62299" + cx="194.76401" + sodipodi:cx="194.76401" + sodipodi:cy="495.62299" + sodipodi:rx="160.94" + sodipodi:ry="123.604" /> + </g> + </mask><linearGradient + gradientTransform="matrix(-1.1256295,0,0,1.1256295,1028.9091,-39.54673)" + y2="643.30768" + x2="480.12021" + y1="815.45459" + x1="429.84909" + gradientUnits="userSpaceOnUse" + id="XMLID_4_"> + <stop + id="stop112" + style="stop-color:#FFFFFF" + offset="0" /> + <stop + id="stop114" + style="stop-color:#E5EAF2" + offset="1" /> + </linearGradient><linearGradient + y2="560.35498" + x2="278.14471" + y1="634.63232" + x1="220.9512" + gradientUnits="userSpaceOnUse" + id="XMLID_5_"> + <stop + id="stop119" + style="stop-color:#FFFFFF" + offset="0" /> + <stop + id="stop121" + style="stop-color:#193D6B" + offset="1" /> + </linearGradient><linearGradient + y2="565.86761" + x2="328.18469" + y1="684.01898" + x1="237.20799" + gradientUnits="userSpaceOnUse" + id="XMLID_6_"> + <stop + id="stop126" + style="stop-color:#FFFFFF" + offset="0" /> + <stop + id="stop128" + style="stop-color:#193D6B" + offset="1" /> + </linearGradient><linearGradient + y2="612.60522" + x2="335.09619" + y1="704.23578" + x1="264.5405" + gradientUnits="userSpaceOnUse" + id="XMLID_7_"> + <stop + id="stop133" + style="stop-color:#FFFFFF" + offset="0" /> + <stop + id="stop135" + style="stop-color:#193D6B" + offset="1" /> + </linearGradient><linearGradient + y2="626.00598" + x2="373.12509" + y1="713.54639" + x1="319.186" + gradientUnits="userSpaceOnUse" + id="XMLID_8_"> + <stop + id="stop140" + style="stop-color:#FFFFFF" + offset="0" /> + <stop + id="stop142" + style="stop-color:#193D6B" + offset="1" /> + </linearGradient><linearGradient + y2="688.75092" + x2="409.85471" + y1="790.51508" + x1="331.49609" + gradientUnits="userSpaceOnUse" + id="XMLID_9_"> + <stop + id="stop147" + style="stop-color:#FFFFFF" + offset="0" /> + <stop + id="stop149" + style="stop-color:#193D6B" + offset="1" /> + </linearGradient><mask + id="XMLID_10_" + height="527.73" + width="447.197" + y="217.631" + x="191.541" + maskUnits="userSpaceOnUse"> + <g + id="g159"> + <radialGradient + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.931,0,0,0.9299,28.7459,21.6629)" + fy="155.23289" + fx="657.2124" + r="606.95343" + cy="155.23289" + cx="657.2124" + id="XMLID_11_"> + <stop + id="stop162" + style="stop-color:#C9C9C8" + offset="0" /> + <stop + id="stop164" + style="stop-color:#C4C4C4" + offset="0.0881" /> + <stop + id="stop166" + style="stop-color:#B8B8B7" + offset="0.2192" /> + <stop + id="stop168" + style="stop-color:#A1A0A0" + offset="0.3769" /> + <stop + id="stop170" + style="stop-color:#7F7F7E" + offset="0.5556" /> + <stop + id="stop172" + style="stop-color:#4F4F4E" + offset="0.7517" /> + <stop + id="stop174" + style="stop-color:#121212" + offset="0.9595" /> + <stop + id="stop176" + style="stop-color:#000000" + offset="1" /> + </radialGradient> + <circle + style="fill:url(#XMLID_11_)" + id="circle178" + r="302.95401" + cy="444.22601" + cx="408.427" + sodipodi:cx="408.427" + sodipodi:cy="444.22601" + sodipodi:rx="302.95401" + sodipodi:ry="302.95401" /> + </g> + </mask><mask + id="XMLID_13_" + height="187.183" + width="252.506" + y="279.317" + x="243.061" + maskUnits="userSpaceOnUse"> + <g + style="filter:url(#Adobe_OpacityMaskFilter_4_)" + id="g193"> + <linearGradient + y2="417.81519" + x2="372.38071" + y1="266.93359" + x1="340.0488" + gradientUnits="userSpaceOnUse" + id="XMLID_14_"> + <stop + id="stop196" + style="stop-color:#FFFFFF" + offset="0" /> + <stop + id="stop198" + style="stop-color:#F8F8F8" + offset="0.0867" /> + <stop + id="stop200" + style="stop-color:#E4E4E4" + offset="0.2156" /> + <stop + id="stop202" + style="stop-color:#C2C2C2" + offset="0.3708" /> + <stop + id="stop204" + style="stop-color:#949494" + offset="0.5465" /> + <stop + id="stop206" + style="stop-color:#595959" + offset="0.7394" /> + <stop + id="stop208" + style="stop-color:#151515" + offset="0.9438" /> + <stop + id="stop210" + style="stop-color:#000000" + offset="0.9944" /> + </linearGradient> + <ellipse + style="fill:url(#XMLID_14_)" + id="ellipse212" + ry="183.32201" + rx="215.32401" + cy="374.28201" + cx="363.052" + sodipodi:cx="363.052" + sodipodi:cy="374.28201" + sodipodi:rx="215.32401" + sodipodi:ry="183.32201" /> + </g> + </mask><mask + id="XMLID_23_" + height="587.491" + width="576.609" + y="97.149" + x="122.963" + maskUnits="userSpaceOnUse"> + <g + style="filter:url(#Adobe_OpacityMaskFilter)" + id="g31"> + <radialGradient + gradientUnits="userSpaceOnUse" + fy="-20.899401" + fx="88.977501" + r="673.11609" + cy="-20.899401" + cx="88.977501" + id="XMLID_24_"> + <stop + id="stop34" + style="stop-color:#FFFFFF" + offset="0.309" /> + <stop + id="stop36" + style="stop-color:#000000" + offset="1" /> + </radialGradient> + <ellipse + style="fill:url(#XMLID_24_)" + id="ellipse38" + ry="349.10599" + rx="512.93402" + cy="286.172" + cx="295.89499" + sodipodi:cx="295.89499" + sodipodi:cy="286.172" + sodipodi:rx="512.93402" + sodipodi:ry="349.10599" /> + </g> + </mask><radialGradient + id="XMLID_22_" + cx="622.72803" + cy="621.17328" + r="510.71881" + fx="622.72803" + fy="621.17328" + gradientUnits="userSpaceOnUse"> + <stop + offset="0" + style="stop-color:#FFFFFF" + id="stop14" /> + <stop + offset="0.2814" + style="stop-color:#E6E6E6" + id="stop16" /> + <stop + offset="0.7515" + style="stop-color:#C0C0C0" + id="stop18" /> + <stop + offset="1" + style="stop-color:#B2B2B2" + id="stop20" /> + </radialGradient><linearGradient + inkscape:collect="always" + xlink:href="#XMLID_9_" + id="linearGradient12328" + gradientUnits="userSpaceOnUse" + x1="331.49609" + y1="790.51508" + x2="409.85471" + y2="688.75092" + gradientTransform="matrix(1.1256295,0,0,1.1256295,-41.873391,-39.54673)" /><linearGradient + inkscape:collect="always" + xlink:href="#XMLID_8_" + id="linearGradient12331" + gradientUnits="userSpaceOnUse" + x1="319.186" + y1="713.54639" + x2="373.12509" + y2="626.00598" + gradientTransform="matrix(1.1256295,0,0,1.1256295,-41.873391,-39.54673)" /><linearGradient + inkscape:collect="always" + xlink:href="#XMLID_7_" + id="linearGradient12334" + gradientUnits="userSpaceOnUse" + x1="264.5405" + y1="704.23578" + x2="335.09619" + y2="612.60522" + gradientTransform="matrix(1.1256295,0,0,1.1256295,-41.873391,-39.54673)" /><linearGradient + inkscape:collect="always" + xlink:href="#XMLID_6_" + id="linearGradient12337" + gradientUnits="userSpaceOnUse" + x1="237.20799" + y1="684.01898" + x2="328.18469" + y2="565.86761" + gradientTransform="matrix(1.1256295,0,0,1.1256295,-41.873391,-39.54673)" /><linearGradient + inkscape:collect="always" + xlink:href="#XMLID_5_" + id="linearGradient12340" + gradientUnits="userSpaceOnUse" + x1="220.9512" + y1="634.63232" + x2="278.14471" + y2="560.35498" + gradientTransform="matrix(1.1256295,0,0,1.1256295,-41.873391,-39.54673)" /><linearGradient + inkscape:collect="always" + xlink:href="#XMLID_4_" + id="linearGradient12343" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(-1.1256295,0,0,1.1256295,1028.9091,-39.54673)" + x1="429.84909" + y1="815.45459" + x2="480.12021" + y2="643.30768" /><radialGradient + inkscape:collect="always" + xlink:href="#radialGradient29545" + id="radialGradient12349" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.1256295,0,0,1.1256295,-41.873391,-39.54673)" + cx="375.21439" + cy="783.16357" + fx="375.21439" + fy="783.16357" + r="300.5752" /><linearGradient + inkscape:collect="always" + xlink:href="#linearGradient5498" + id="linearGradient17820" + gradientUnits="userSpaceOnUse" + x1="1508.4957" + y1="-363.42487" + x2="361.1441" + y2="630.52039" /><linearGradient + inkscape:collect="always" + xlink:href="#linearGradient5498" + id="linearGradient2486" + x1="370.62177" + y1="-309.6048" + x2="370.20648" + y2="458.38397" + gradientUnits="userSpaceOnUse" /><linearGradient + inkscape:collect="always" + xlink:href="#linearGradient5498" + id="linearGradient5490" + x1="247.47713" + y1="274.88394" + x2="130.31757" + y2="580.54669" + gradientUnits="userSpaceOnUse" /><linearGradient + inkscape:collect="always" + xlink:href="#linearGradient5498" + id="linearGradient6539" + x1="220.36789" + y1="756.76105" + x2="185.21225" + y2="491.06723" + gradientUnits="userSpaceOnUse" /><radialGradient + inkscape:collect="always" + xlink:href="#radialGradient29545" + id="radialGradient33274" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.1256295,0,0,1.1256295,-41.873391,-39.54673)" + cx="375.21439" + cy="783.16357" + fx="375.21439" + fy="783.16357" + r="300.5752" /><linearGradient + inkscape:collect="always" + xlink:href="#XMLID_4_" + id="linearGradient33276" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(-1.1256295,0,0,1.1256295,1028.9091,-39.54673)" + x1="429.84909" + y1="815.45459" + x2="480.12021" + y2="643.30768" /><linearGradient + inkscape:collect="always" + xlink:href="#XMLID_5_" + id="linearGradient33278" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.1256295,0,0,1.1256295,-41.873391,-39.54673)" + x1="220.9512" + y1="634.63232" + x2="278.14471" + y2="560.35498" /><linearGradient + inkscape:collect="always" + xlink:href="#XMLID_6_" + id="linearGradient33280" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.1256295,0,0,1.1256295,-41.873391,-39.54673)" + x1="237.20799" + y1="684.01898" + x2="328.18469" + y2="565.86761" /><linearGradient + inkscape:collect="always" + xlink:href="#XMLID_7_" + id="linearGradient33282" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.1256295,0,0,1.1256295,-41.873391,-39.54673)" + x1="264.5405" + y1="704.23578" + x2="335.09619" + y2="612.60522" /><linearGradient + inkscape:collect="always" + xlink:href="#XMLID_8_" + id="linearGradient33284" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.1256295,0,0,1.1256295,-41.873391,-39.54673)" + x1="319.186" + y1="713.54639" + x2="373.12509" + y2="626.00598" /><linearGradient + inkscape:collect="always" + xlink:href="#XMLID_9_" + id="linearGradient33286" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.1256295,0,0,1.1256295,-41.873391,-39.54673)" + x1="331.49609" + y1="790.51508" + x2="409.85471" + y2="688.75092" /><linearGradient + inkscape:collect="always" + xlink:href="#linearGradient5498" + id="linearGradient33288" + gradientUnits="userSpaceOnUse" + x1="1508.4957" + y1="-363.42487" + x2="361.1441" + y2="630.52039" /><linearGradient + inkscape:collect="always" + xlink:href="#linearGradient5498" + id="linearGradient33290" + gradientUnits="userSpaceOnUse" + x1="370.62177" + y1="-309.6048" + x2="370.20648" + y2="458.38397" /><linearGradient + inkscape:collect="always" + xlink:href="#linearGradient5498" + id="linearGradient33292" + gradientUnits="userSpaceOnUse" + x1="247.47713" + y1="274.88394" + x2="130.31757" + y2="580.54669" /><linearGradient + inkscape:collect="always" + xlink:href="#linearGradient5498" + id="linearGradient33294" + gradientUnits="userSpaceOnUse" + x1="220.36789" + y1="756.76105" + x2="185.21225" + y2="491.06723" /><linearGradient + inkscape:collect="always" + xlink:href="#linearGradient5498" + id="linearGradient33297" + gradientUnits="userSpaceOnUse" + x1="220.36789" + y1="756.76105" + x2="185.21225" + y2="491.06723" /><linearGradient + inkscape:collect="always" + xlink:href="#linearGradient5498" + id="linearGradient33300" + gradientUnits="userSpaceOnUse" + x1="247.47713" + y1="274.88394" + x2="130.31757" + y2="580.54669" /><linearGradient + inkscape:collect="always" + xlink:href="#linearGradient5498" + id="linearGradient33303" + gradientUnits="userSpaceOnUse" + x1="370.62177" + y1="-309.6048" + x2="370.20648" + y2="458.38397" /><linearGradient + inkscape:collect="always" + xlink:href="#linearGradient5498" + id="linearGradient33306" + gradientUnits="userSpaceOnUse" + x1="1508.4957" + y1="-363.42487" + x2="361.1441" + y2="630.52039" /><linearGradient + inkscape:collect="always" + xlink:href="#XMLID_9_" + id="linearGradient33309" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.1256295,0,0,1.1256295,-41.873391,-39.54673)" + x1="331.49609" + y1="790.51508" + x2="409.85471" + y2="688.75092" /><linearGradient + inkscape:collect="always" + xlink:href="#XMLID_8_" + id="linearGradient33312" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.1256295,0,0,1.1256295,-41.873391,-39.54673)" + x1="319.186" + y1="713.54639" + x2="373.12509" + y2="626.00598" /><linearGradient + inkscape:collect="always" + xlink:href="#XMLID_7_" + id="linearGradient33315" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.1256295,0,0,1.1256295,-41.873391,-39.54673)" + x1="264.5405" + y1="704.23578" + x2="335.09619" + y2="612.60522" /><linearGradient + inkscape:collect="always" + xlink:href="#XMLID_6_" + id="linearGradient33318" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.1256295,0,0,1.1256295,-41.873391,-39.54673)" + x1="237.20799" + y1="684.01898" + x2="328.18469" + y2="565.86761" /><linearGradient + inkscape:collect="always" + xlink:href="#XMLID_5_" + id="linearGradient33321" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.1256295,0,0,1.1256295,-41.873391,-39.54673)" + x1="220.9512" + y1="634.63232" + x2="278.14471" + y2="560.35498" /><linearGradient + inkscape:collect="always" + xlink:href="#XMLID_4_" + id="linearGradient33324" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(-1.1256295,0,0,1.1256295,1028.9091,-39.54673)" + x1="429.84909" + y1="815.45459" + x2="480.12021" + y2="643.30768" /><radialGradient + inkscape:collect="always" + xlink:href="#radialGradient29545" + id="radialGradient33328" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.1256295,0,0,1.1256295,-41.873391,-39.54673)" + cx="375.21439" + cy="783.16357" + fx="375.21439" + fy="783.16357" + r="300.5752" /><linearGradient + inkscape:collect="always" + xlink:href="#XMLID_4_" + id="linearGradient7344" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(-1.1256295,0,0,1.1256295,1058.6084,-9.8474173)" + x1="429.84909" + y1="815.45459" + x2="480.12021" + y2="643.30768" /><linearGradient + inkscape:collect="always" + xlink:href="#linearGradient5498" + id="linearGradient7346" + gradientUnits="userSpaceOnUse" + gradientTransform="translate(29.699313,29.699313)" + x1="1508.4957" + y1="-363.42487" + x2="361.1441" + y2="630.52039" /><linearGradient + inkscape:collect="always" + xlink:href="#linearGradient5498" + id="linearGradient7348" + gradientUnits="userSpaceOnUse" + gradientTransform="translate(29.699313,29.699313)" + x1="370.62177" + y1="-309.6048" + x2="370.20648" + y2="458.38397" /><linearGradient + inkscape:collect="always" + xlink:href="#linearGradient5498" + id="linearGradient7350" + gradientUnits="userSpaceOnUse" + gradientTransform="translate(29.699313,29.699313)" + x1="247.47713" + y1="274.88394" + x2="130.31757" + y2="580.54669" /><linearGradient + inkscape:collect="always" + xlink:href="#linearGradient5498" + id="linearGradient7352" + gradientUnits="userSpaceOnUse" + gradientTransform="translate(29.699313,29.699313)" + x1="220.36789" + y1="756.76105" + x2="185.21225" + y2="491.06723" /><linearGradient + inkscape:collect="always" + xlink:href="#linearGradient5498" + id="linearGradient7373" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.9343129,0,0,0.9343129,27.748451,27.748451)" + x1="220.36789" + y1="756.76105" + x2="185.21225" + y2="491.06723" /><linearGradient + inkscape:collect="always" + xlink:href="#linearGradient5498" + id="linearGradient7376" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.9343129,0,0,0.9343129,27.748451,27.748451)" + x1="247.47713" + y1="274.88394" + x2="130.31757" + y2="580.54669" /><linearGradient + inkscape:collect="always" + xlink:href="#linearGradient5498" + id="linearGradient7379" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.9343129,0,0,0.9343129,27.748451,27.748451)" + x1="370.62177" + y1="-309.6048" + x2="370.20648" + y2="458.38397" /><linearGradient + inkscape:collect="always" + xlink:href="#linearGradient5498" + id="linearGradient7382" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.9343129,0,0,0.9343129,27.748451,27.748451)" + x1="1508.4957" + y1="-363.42487" + x2="361.1441" + y2="630.52039" /><linearGradient + inkscape:collect="always" + xlink:href="#XMLID_4_" + id="linearGradient7385" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(-1.0516902,0,0,1.0516902,989.07148,-9.200569)" + x1="429.84909" + y1="815.45459" + x2="480.12021" + y2="643.30768" /><linearGradient + inkscape:collect="always" + xlink:href="#XMLID_4_" + id="linearGradient7431" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(-1.0516902,0,0,1.0516902,989.07148,-9.200569)" + x1="429.84909" + y1="815.45459" + x2="480.12021" + y2="643.30768" /><linearGradient + inkscape:collect="always" + xlink:href="#linearGradient5498" + id="linearGradient7433" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.9343129,0,0,0.9343129,27.748451,27.748451)" + x1="1508.4957" + y1="-363.42487" + x2="361.1441" + y2="630.52039" /><linearGradient + inkscape:collect="always" + xlink:href="#linearGradient5498" + id="linearGradient7435" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.9343129,0,0,0.9343129,27.748451,27.748451)" + x1="370.62177" + y1="-309.6048" + x2="370.20648" + y2="458.38397" /><linearGradient + inkscape:collect="always" + xlink:href="#linearGradient5498" + id="linearGradient7437" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.9343129,0,0,0.9343129,27.748451,27.748451)" + x1="247.47713" + y1="274.88394" + x2="130.31757" + y2="580.54669" /><linearGradient + inkscape:collect="always" + xlink:href="#linearGradient5498" + id="linearGradient7439" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.9343129,0,0,0.9343129,27.748451,27.748451)" + x1="220.36789" + y1="756.76105" + x2="185.21225" + y2="491.06723" /><linearGradient + inkscape:collect="always" + xlink:href="#XMLID_4_" + id="linearGradient7454" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(-1.0516902,0,0,1.0516902,989.07148,-9.200569)" + x1="429.84909" + y1="815.45459" + x2="480.12021" + y2="643.30768" /><linearGradient + inkscape:collect="always" + xlink:href="#linearGradient5498" + id="linearGradient7456" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.9343129,0,0,0.9343129,27.748451,27.748451)" + x1="1508.4957" + y1="-363.42487" + x2="361.1441" + y2="630.52039" /><linearGradient + inkscape:collect="always" + xlink:href="#linearGradient5498" + id="linearGradient7458" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.9343129,0,0,0.9343129,27.748451,27.748451)" + x1="370.62177" + y1="-309.6048" + x2="370.20648" + y2="458.38397" /><linearGradient + inkscape:collect="always" + xlink:href="#linearGradient5498" + id="linearGradient7460" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.9343129,0,0,0.9343129,27.748451,27.748451)" + x1="247.47713" + y1="274.88394" + x2="130.31757" + y2="580.54669" /><linearGradient + inkscape:collect="always" + xlink:href="#linearGradient5498" + id="linearGradient7462" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.9343129,0,0,0.9343129,27.748451,27.748451)" + x1="220.36789" + y1="756.76105" + x2="185.21225" + y2="491.06723" /><linearGradient + inkscape:collect="always" + xlink:href="#XMLID_4_" + id="linearGradient7475" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(-1.0516902,0,0,1.0516902,989.07148,-9.200569)" + x1="429.84909" + y1="815.45459" + x2="480.12021" + y2="643.30768" /><linearGradient + inkscape:collect="always" + xlink:href="#linearGradient5498" + id="linearGradient7477" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.9343129,0,0,0.9343129,27.748451,27.748451)" + x1="1508.4957" + y1="-363.42487" + x2="361.1441" + y2="630.52039" /><linearGradient + inkscape:collect="always" + xlink:href="#linearGradient5498" + id="linearGradient7479" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.9343129,0,0,0.9343129,27.748451,27.748451)" + x1="370.62177" + y1="-309.6048" + x2="370.20648" + y2="458.38397" /><linearGradient + inkscape:collect="always" + xlink:href="#linearGradient5498" + id="linearGradient7481" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.9343129,0,0,0.9343129,27.748451,27.748451)" + x1="247.47713" + y1="274.88394" + x2="130.31757" + y2="580.54669" /><linearGradient + inkscape:collect="always" + xlink:href="#linearGradient5498" + id="linearGradient7483" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.9343129,0,0,0.9343129,27.748451,27.748451)" + x1="220.36789" + y1="756.76105" + x2="185.21225" + y2="491.06723" /><linearGradient + inkscape:collect="always" + xlink:href="#XMLID_4_" + id="linearGradient7494" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(-1.0516902,0,0,1.0516902,989.07148,-9.200569)" + x1="429.84909" + y1="815.45459" + x2="480.12021" + y2="643.30768" /><linearGradient + inkscape:collect="always" + xlink:href="#linearGradient5498" + id="linearGradient7496" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.9343129,0,0,0.9343129,27.748451,27.748451)" + x1="1508.4957" + y1="-363.42487" + x2="361.1441" + y2="630.52039" /><linearGradient + inkscape:collect="always" + xlink:href="#linearGradient5498" + id="linearGradient7498" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.9343129,0,0,0.9343129,27.748451,27.748451)" + x1="370.62177" + y1="-309.6048" + x2="370.20648" + y2="458.38397" /><linearGradient + inkscape:collect="always" + xlink:href="#linearGradient5498" + id="linearGradient7500" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.9343129,0,0,0.9343129,27.748451,27.748451)" + x1="247.47713" + y1="274.88394" + x2="130.31757" + y2="580.54669" /><linearGradient + inkscape:collect="always" + xlink:href="#linearGradient5498" + id="linearGradient7502" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.9343129,0,0,0.9343129,27.748451,27.748451)" + x1="220.36789" + y1="756.76105" + x2="185.21225" + y2="491.06723" /><linearGradient + inkscape:collect="always" + xlink:href="#linearGradient5498" + id="linearGradient7505" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.8998301,0,0,0.8998301,53.458406,33.708279)" + x1="220.36789" + y1="756.76105" + x2="185.21225" + y2="491.06723" /><linearGradient + inkscape:collect="always" + xlink:href="#linearGradient5498" + id="linearGradient7508" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.8998301,0,0,0.8998301,53.458406,33.708279)" + x1="247.47713" + y1="274.88394" + x2="130.31757" + y2="580.54669" /><linearGradient + inkscape:collect="always" + xlink:href="#linearGradient5498" + id="linearGradient7511" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.8998301,0,0,0.8998301,53.458406,33.708279)" + x1="370.62177" + y1="-309.6048" + x2="370.20648" + y2="458.38397" /><linearGradient + inkscape:collect="always" + xlink:href="#linearGradient5498" + id="linearGradient7514" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.8998301,0,0,0.8998301,53.458406,33.708279)" + x1="1508.4957" + y1="-363.42487" + x2="361.1441" + y2="630.52039" /><linearGradient + inkscape:collect="always" + xlink:href="#XMLID_4_" + id="linearGradient7517" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(-1.0128754,0,0,1.0128754,979.30179,-1.8770598)" + x1="429.84909" + y1="815.45459" + x2="480.12021" + y2="643.30768" /></defs> + +<g + id="g8826" + transform="translate(-9.9999999e-7,-77.04248)"><path + id="circle22" + d="M 813.31393,504.16148 C 813.31393,717.34108 640.2986,890.35641 427.119,890.35641 C 213.9394,890.35641 40.924072,717.34108 40.924072,504.16148 C 40.924072,290.98188 213.9394,117.96655 427.119,117.96655 C 640.2986,117.96655 813.31393,290.98188 813.31393,504.16148 z " + style="fill:#dddddd;fill-opacity:1;stroke:#0057ae;stroke-width:81.84814453;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /><g + transform="matrix(1.0007211,0,0,1.0007211,0,77.042475)" + id="g7521"><path + style="opacity:0.22000002;fill:#323232" + d="M 642.57609,234.13404 C 627.10948,234.13404 597.07671,266.39006 585.82368,271.4828 C 574.56556,276.57553 485.45685,334.29019 477.01352,344.47566 C 468.57424,354.66011 457.31815,358.90508 445.12516,358.05629 C 432.93318,357.20749 394.38517,356.85907 381.25527,363.64938 C 368.12233,370.4397 308.1824,425.95743 293.17361,432.74876 C 278.16685,439.53704 258.58696,443.01729 249.20773,443.86506 C 239.8285,444.71487 208.75552,476.03601 200.3132,481.97956 C 191.87189,487.91805 130.15538,511.71454 120.77716,516.80526 C 111.39693,521.89901 175.92721,602.50261 187.1833,604.20019 C 198.43735,605.89878 176.12675,614.22056 154.47147,614.22056 C 152.08716,614.22056 185.41987,701.62968 299.0726,758.04583 C 397.32655,806.81577 476.48682,777.1993 515.20296,765.37094 C 542.57896,757.00763 547.54306,753.27822 548.48402,739.69759 C 549.42296,726.11797 551.11852,715.38655 552.99537,700.10935 C 554.87223,684.83418 561.43364,556.66695 585.82469,522.7174 C 610.20865,488.76684 679.91372,448.01076 704.29969,443.76883 C 728.68771,439.52489 723.99911,431.03801 723.99911,423.3989 C 723.99911,415.7598 720.2464,398.784 713.68095,396.23764 C 707.11449,393.69229 681.49481,408.98265 673.05351,410.68022 C 664.61119,412.3778 624.27951,414.92418 618.65097,406.4373 C 613.02342,397.94839 664.15237,311.37286 670.71782,301.18841 C 677.28126,291.00293 680.09603,277.4223 667.90506,262.99389 C 655.70701,248.56244 647.2647,234.13404 642.57609,234.13404 z " + id="path43" /><path + style="fill:#00316e;fill-opacity:1" + d="M 609.66271,189.23022 C 594.65899,189.23022 565.52971,223.80574 554.61192,229.26413 C 543.69414,234.72353 457.26042,296.58894 449.07233,307.50773 C 440.88426,318.4245 429.96546,322.97434 418.13913,322.06477 C 406.31279,321.15623 368.92351,320.78146 356.18661,328.05999 C 343.44767,335.33952 285.30761,394.85 270.75057,402.12953 C 256.19454,409.40603 237.20313,413.13442 228.10447,414.045 C 219.00783,414.95354 188.8677,448.52733 180.67962,454.89729 C 172.49154,461.26727 107.78906,489.26517 98.691417,494.72154 C 89.592758,500.18093 165.06615,576.6358 175.98393,578.45391 C 186.90172,580.27607 168.46434,621.70065 147.45832,621.70065 C 145.14491,621.70065 166.22994,690.34422 276.47028,750.81794 C 371.77476,803.0975 472.89314,779.95027 510.44549,767.27311 C 537.00106,758.30512 518.36314,753.34912 519.27473,738.79309 C 520.18227,724.23503 520.94698,705.09371 522.76712,688.71856 C 524.58827,672.34238 530.95419,534.95901 554.6109,498.56742 C 578.26459,462.17785 645.87402,418.49051 669.52971,413.94169 C 693.18644,409.39286 688.63559,400.2942 688.63559,392.10713 C 688.63559,383.92006 684.99734,365.72275 678.62736,362.99304 C 672.25941,360.26436 647.40954,376.65471 639.22145,378.47383 C 631.03337,380.29296 591.91105,383.02367 586.45065,373.92602 C 580.99226,364.82635 630.58567,272.0257 636.95463,261.10791 C 643.32461,250.18912 646.05228,235.63107 634.22393,220.16647 C 622.40062,204.69684 614.21153,189.23022 609.66271,189.23022 z " + id="path58" /><path + style="fill:#ffffff" + d="M 428.17672,360.73231 C 428.17672,360.73231 392.13254,355.1858 371.33517,375.98418 C 350.53679,396.78155 332.50963,442.53415 333.90031,456.39939 C 335.28491,470.26465 376.88066,409.25916 394.90275,405.10131 C 412.92889,400.94043 432.33862,370.43565 428.17672,360.73231 z " + id="path60" /></g></g></svg> \ No newline at end of file diff --git a/amarok/src/images/amarok_logo.svg b/amarok/src/images/amarok_logo.svg new file mode 100644 index 00000000..191ef0e2 --- /dev/null +++ b/amarok/src/images/amarok_logo.svg @@ -0,0 +1,2344 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Generator: Adobe Illustrator 10, SVG Export Plug-In . SVG Version: 3.0.0 Build 76) --> +<svg + xmlns:ns0="http://ns.adobe.com/SaveForWeb/1.0/" + xmlns:ns="http://ns.adobe.com/Variables/1.0/" + xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/" + xmlns:i="http://ns.adobe.com/AdobeIllustrator/10.0/" + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://web.resource.org/cc/" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + i:viewOrigin="0.5107 456.6963" + i:rulerOrigin="39 -87.8027" + i:pageBounds="-39 513 839.7402 87.8027" + width="670" + height="250" + viewBox="0 0 760.908 307.134" + overflow="visible" + enable-background="new 0 0 760.908 307.134" + xml:space="preserve" + id="svg2" + sodipodi:version="0.32" + inkscape:version="0.45" + sodipodi:docname="amarok_logo_oxygen.svg" + sodipodi:docbase="/home/knome/Work/Amarok logo" + version="1.0" + inkscape:export-filename="/home/me/amarok_logo2.png" + inkscape:export-xdpi="43.119404" + inkscape:export-ydpi="43.119404" + inkscape:output_extension="org.inkscape.output.svg.inkscape" + sodipodi:modified="TRUE"><defs + id="defs48"><mask + maskUnits="userSpaceOnUse" + x="243.061" + y="279.317" + width="252.506" + height="187.183" + id="XMLID_13_" + i:invertMask="false" + i:clipMask="true"> + <g + filter="url(#Adobe_OpacityMaskFilter_4_)" + id="g6864"> + <linearGradient + id="XMLID_14_" + gradientUnits="userSpaceOnUse" + x1="340.0488" + y1="266.93359" + x2="372.38071" + y2="417.81519"> + <stop + offset="0" + style="stop-color:#FFFFFF" + id="stop6867" /> + <stop + offset="0.0867" + style="stop-color:#F8F8F8" + id="stop6869" /> + <stop + offset="0.2156" + style="stop-color:#E4E4E4" + id="stop6871" /> + <stop + offset="0.3708" + style="stop-color:#C2C2C2" + id="stop6873" /> + <stop + offset="0.5465" + style="stop-color:#949494" + id="stop6875" /> + <stop + offset="0.7394" + style="stop-color:#595959" + id="stop6877" /> + <stop + offset="0.9438" + style="stop-color:#151515" + id="stop6879" /> + <stop + offset="0.9944" + style="stop-color:#000000" + id="stop6881" /> + <a:midPointStop + offset="0" + style="stop-color:#FFFFFF" /> + <a:midPointStop + offset="0.6215" + style="stop-color:#FFFFFF" /> + <a:midPointStop + offset="0.9944" + style="stop-color:#000000" /> + </linearGradient> + <ellipse + style="fill:url(#XMLID_14_)" + sodipodi:ry="183.32201" + sodipodi:rx="215.32401" + sodipodi:cy="374.28201" + sodipodi:cx="363.052" + i:knockout="Off" + cx="363.052" + cy="374.28201" + rx="215.32401" + ry="183.32201" + id="ellipse6883" /> + </g> + </mask><mask + maskUnits="userSpaceOnUse" + x="191.541" + y="217.631" + width="447.197" + height="527.73" + id="XMLID_10_" + i:invertMask="false" + i:clipMask="true"> + <g + filter="url(#Adobe_OpacityMaskFilter_3_)" + id="g6830"> + + <radialGradient + id="XMLID_11_" + cx="657.2124" + cy="155.23289" + r="606.95343" + fx="657.2124" + fy="155.23289" + gradientTransform="matrix(0.931,0,0,0.9299,28.7459,21.6629)" + gradientUnits="userSpaceOnUse"> + <stop + offset="0" + style="stop-color:#C9C9C8" + id="stop6833" /> + <stop + offset="0.0881" + style="stop-color:#C4C4C4" + id="stop6835" /> + <stop + offset="0.2192" + style="stop-color:#B8B8B7" + id="stop6837" /> + <stop + offset="0.3769" + style="stop-color:#A1A0A0" + id="stop6839" /> + <stop + offset="0.5556" + style="stop-color:#7F7F7E" + id="stop6841" /> + <stop + offset="0.7517" + style="stop-color:#4F4F4E" + id="stop6843" /> + <stop + offset="0.9595" + style="stop-color:#121212" + id="stop6845" /> + <stop + offset="1" + style="stop-color:#000000" + id="stop6847" /> + <a:midPointStop + offset="0" + style="stop-color:#C9C9C8" /> + <a:midPointStop + offset="0.6215" + style="stop-color:#C9C9C8" /> + <a:midPointStop + offset="1" + style="stop-color:#000000" /> + </radialGradient> + <circle + style="fill:url(#XMLID_11_)" + sodipodi:ry="302.95401" + sodipodi:rx="302.95401" + sodipodi:cy="444.22601" + sodipodi:cx="408.427" + i:knockout="Off" + cx="408.427" + cy="444.22601" + r="302.95401" + id="circle6849" /> + </g> + </mask><linearGradient + id="XMLID_12_" + gradientUnits="userSpaceOnUse" + x1="517.18988" + y1="239.939" + x2="410.27759" + y2="522.02393"> + <stop + offset="0" + style="stop-color:#FFFFFF" + id="stop6852" /> + <stop + offset="0.9944" + style="stop-color:#FFFFFF" + id="stop6854" /> + <a:midPointStop + offset="0" + style="stop-color:#FFFFFF" /> + <a:midPointStop + offset="0.6215" + style="stop-color:#FFFFFF" /> + <a:midPointStop + offset="0.9944" + style="stop-color:#FFFFFF" /> + </linearGradient><linearGradient + id="XMLID_9_" + gradientUnits="userSpaceOnUse" + x1="331.49609" + y1="790.51508" + x2="409.85471" + y2="688.75092"> + <stop + offset="0" + style="stop-color:#FFFFFF" + id="stop6818" /> + <stop + offset="1" + style="stop-color:#193D6B" + id="stop6820" /> + <a:midPointStop + offset="0" + style="stop-color:#FFFFFF" /> + <a:midPointStop + offset="0.5" + style="stop-color:#FFFFFF" /> + <a:midPointStop + offset="1" + style="stop-color:#193D6B" /> + </linearGradient><linearGradient + id="XMLID_8_" + gradientUnits="userSpaceOnUse" + x1="319.186" + y1="713.54639" + x2="373.12509" + y2="626.00598"> + <stop + offset="0" + style="stop-color:#FFFFFF" + id="stop6811" /> + <stop + offset="1" + style="stop-color:#193D6B" + id="stop6813" /> + <a:midPointStop + offset="0" + style="stop-color:#FFFFFF" /> + <a:midPointStop + offset="0.5" + style="stop-color:#FFFFFF" /> + <a:midPointStop + offset="1" + style="stop-color:#193D6B" /> + </linearGradient><linearGradient + id="XMLID_7_" + gradientUnits="userSpaceOnUse" + x1="264.5405" + y1="704.23578" + x2="335.09619" + y2="612.60522"> + <stop + offset="0" + style="stop-color:#FFFFFF" + id="stop6804" /> + <stop + offset="1" + style="stop-color:#193D6B" + id="stop6806" /> + <a:midPointStop + offset="0" + style="stop-color:#FFFFFF" /> + <a:midPointStop + offset="0.5" + style="stop-color:#FFFFFF" /> + <a:midPointStop + offset="1" + style="stop-color:#193D6B" /> + </linearGradient><linearGradient + id="XMLID_6_" + gradientUnits="userSpaceOnUse" + x1="237.20799" + y1="684.01898" + x2="328.18469" + y2="565.86761"> + <stop + offset="0" + style="stop-color:#FFFFFF" + id="stop6797" /> + <stop + offset="1" + style="stop-color:#193D6B" + id="stop6799" /> + <a:midPointStop + offset="0" + style="stop-color:#FFFFFF" /> + <a:midPointStop + offset="0.5" + style="stop-color:#FFFFFF" /> + <a:midPointStop + offset="1" + style="stop-color:#193D6B" /> + </linearGradient><linearGradient + id="XMLID_5_" + gradientUnits="userSpaceOnUse" + x1="220.9512" + y1="634.63232" + x2="278.14471" + y2="560.35498"> + <stop + offset="0" + style="stop-color:#FFFFFF" + id="stop6790" /> + <stop + offset="1" + style="stop-color:#193D6B" + id="stop6792" /> + <a:midPointStop + offset="0" + style="stop-color:#FFFFFF" /> + <a:midPointStop + offset="0.5" + style="stop-color:#FFFFFF" /> + <a:midPointStop + offset="1" + style="stop-color:#193D6B" /> + </linearGradient><linearGradient + id="XMLID_4_" + gradientUnits="userSpaceOnUse" + x1="429.84909" + y1="815.45459" + x2="480.12021" + y2="643.30768" + gradientTransform="matrix(-1,0,0,1,951.2744,0)"> + <stop + offset="0" + style="stop-color:#FFFFFF" + id="stop6783" /> + <stop + offset="1" + style="stop-color:#E5EAF2" + id="stop6785" /> + <a:midPointStop + offset="0" + style="stop-color:#FFFFFF" /> + <a:midPointStop + offset="0.5" + style="stop-color:#FFFFFF" /> + <a:midPointStop + offset="1" + style="stop-color:#E5EAF2" /> + </linearGradient><mask + maskUnits="userSpaceOnUse" + x="101.57" + y="425.034" + width="156.907" + height="127.553" + id="XMLID_2_" + i:invertMask="false" + i:clipMask="true"> + <g + filter="url(#Adobe_OpacityMaskFilter_2_)" + id="g6757"> + <linearGradient + id="XMLID_3_" + gradientUnits="userSpaceOnUse" + x1="206.09081" + y1="397.62061" + x2="186.92191" + y2="563.47321"> + <stop + offset="0" + style="stop-color:#FFFFFF" + id="stop6760" /> + <stop + offset="0.1111" + style="stop-color:#CBCBCB" + id="stop6762" /> + <stop + offset="0.2396" + style="stop-color:#969696" + id="stop6764" /> + <stop + offset="0.3698" + style="stop-color:#686868" + id="stop6766" /> + <stop + offset="0.4991" + style="stop-color:#424242" + id="stop6768" /> + <stop + offset="0.6273" + style="stop-color:#252525" + id="stop6770" /> + <stop + offset="0.7542" + style="stop-color:#111111" + id="stop6772" /> + <stop + offset="0.8792" + style="stop-color:#040404" + id="stop6774" /> + <stop + offset="1" + style="stop-color:#000000" + id="stop6776" /> + <a:midPointStop + offset="0" + style="stop-color:#FFFFFF" /> + <a:midPointStop + offset="0.2994" + style="stop-color:#FFFFFF" /> + <a:midPointStop + offset="1" + style="stop-color:#000000" /> + </linearGradient> + <ellipse + style="fill:url(#XMLID_3_)" + sodipodi:ry="123.604" + sodipodi:rx="160.94" + sodipodi:cy="495.62299" + sodipodi:cx="194.76401" + i:knockout="Off" + cx="194.76401" + cy="495.62299" + rx="160.94" + ry="123.604" + id="ellipse6778" /> + </g> + </mask><mask + maskUnits="userSpaceOnUse" + x="155.705" + y="461.543" + width="93.126" + height="44.813" + id="XMLID_26_" + i:invertMask="false" + i:clipMask="true"> + <g + filter="url(#Adobe_OpacityMaskFilter_1_)" + id="g6740"> + <linearGradient + id="XMLID_1_" + gradientUnits="userSpaceOnUse" + x1="211.7222" + y1="562.85791" + x2="200.0733" + y2="466.75449"> + <stop + offset="0" + style="stop-color:#FFFFFF" + id="stop6743" /> + <stop + offset="1" + style="stop-color:#000000" + id="stop6745" /> + <a:midPointStop + offset="0" + style="stop-color:#FFFFFF" /> + <a:midPointStop + offset="0.5" + style="stop-color:#FFFFFF" /> + <a:midPointStop + offset="1" + style="stop-color:#000000" /> + </linearGradient> + <ellipse + style="fill:url(#XMLID_1_)" + sodipodi:ry="59.682999" + sodipodi:rx="80.542" + sodipodi:cy="495.98801" + sodipodi:cx="203.617" + i:knockout="Off" + cx="203.617" + cy="495.98801" + rx="80.542" + ry="59.682999" + id="ellipse6747" /> + </g> + </mask><radialGradient + id="XMLID_25_" + cx="375.21439" + cy="783.16357" + r="300.5752" + fx="375.21439" + fy="783.16357" + gradientUnits="userSpaceOnUse"> + <stop + offset="0.0112" + style="stop-color:#D2E8EC" + id="stop6717" /> + <stop + offset="0.2921" + style="stop-color:#60A8CE" + id="stop6719" /> + <stop + offset="0.4575" + style="stop-color:#4680A8" + id="stop6721" /> + <stop + offset="0.6767" + style="stop-color:#2F5C84" + id="stop6723" /> + <stop + offset="0.8653" + style="stop-color:#234970" + id="stop6725" /> + <stop + offset="1" + style="stop-color:#1F426A" + id="stop6727" /> + <a:midPointStop + offset="0.0112" + style="stop-color:#D2E8EC" /> + <a:midPointStop + offset="0.5" + style="stop-color:#D2E8EC" /> + <a:midPointStop + offset="0.2921" + style="stop-color:#60A8CE" /> + <a:midPointStop + offset="0.3651" + style="stop-color:#60A8CE" /> + <a:midPointStop + offset="1" + style="stop-color:#1F426A" /> + </radialGradient><mask + maskUnits="userSpaceOnUse" + x="122.963" + y="97.149" + width="576.609" + height="587.491" + id="XMLID_23_" + i:invertMask="false" + i:clipMask="true"> + <g + filter="url(#Adobe_OpacityMaskFilter)" + id="g6702"> + + <radialGradient + id="XMLID_24_" + cx="88.977501" + cy="-20.899401" + r="673.11609" + fx="88.977501" + fy="-20.899401" + gradientUnits="userSpaceOnUse"> + <stop + offset="0.309" + style="stop-color:#FFFFFF" + id="stop6705" /> + <stop + offset="1" + style="stop-color:#000000" + id="stop6707" /> + <a:midPointStop + offset="0.309" + style="stop-color:#FFFFFF" /> + <a:midPointStop + offset="0.5" + style="stop-color:#FFFFFF" /> + <a:midPointStop + offset="1" + style="stop-color:#000000" /> + </radialGradient> + <ellipse + style="fill:url(#XMLID_24_)" + sodipodi:ry="349.10599" + sodipodi:rx="512.93402" + sodipodi:cy="286.172" + sodipodi:cx="295.89499" + i:knockout="Off" + cx="295.89499" + cy="286.172" + rx="512.93402" + ry="349.10599" + id="ellipse6709" /> + </g> + </mask><radialGradient + id="XMLID_22_" + cx="622.72803" + cy="621.17328" + r="510.71881" + fx="622.72803" + fy="621.17328" + gradientUnits="userSpaceOnUse"> + <stop + offset="0" + style="stop-color:#FFFFFF" + id="stop6685" /> + <stop + offset="0.2814" + style="stop-color:#E6E6E6" + id="stop6687" /> + <stop + offset="0.7515" + style="stop-color:#C0C0C0" + id="stop6689" /> + <stop + offset="1" + style="stop-color:#B2B2B2" + id="stop6691" /> + <a:midPointStop + offset="0" + style="stop-color:#FFFFFF" /> + <a:midPointStop + offset="0.4359" + style="stop-color:#FFFFFF" /> + <a:midPointStop + offset="1" + style="stop-color:#B2B2B2" /> + </radialGradient> + + + + + + + + + + + <radialGradient + inkscape:collect="always" + xlink:href="#XMLID_22_" + id="radialGradient9742" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(3.12237,0,0,3.12237,-1084.425,-172.2462)" + cx="622.72803" + cy="621.17328" + fx="622.72803" + fy="621.17328" + r="510.71881" /><radialGradient + inkscape:collect="always" + xlink:href="#XMLID_25_" + id="radialGradient9744" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(3.12237,0,0,3.12237,-1084.425,-172.2462)" + cx="375.21439" + cy="783.16357" + fx="375.21439" + fy="783.16357" + r="300.5752" /><linearGradient + inkscape:collect="always" + xlink:href="#XMLID_4_" + id="linearGradient9746" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(-3.12237,0,0,3.12237,1885.808,-172.2462)" + x1="429.84909" + y1="815.45459" + x2="480.12021" + y2="643.30768" /><linearGradient + inkscape:collect="always" + xlink:href="#XMLID_5_" + id="linearGradient9748" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(3.12237,0,0,3.12237,-1084.425,-172.2462)" + x1="220.9512" + y1="634.63232" + x2="278.14471" + y2="560.35498" /><linearGradient + inkscape:collect="always" + xlink:href="#XMLID_6_" + id="linearGradient9750" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(3.12237,0,0,3.12237,-1084.425,-172.2462)" + x1="237.20799" + y1="684.01898" + x2="328.18469" + y2="565.86761" /><linearGradient + inkscape:collect="always" + xlink:href="#XMLID_7_" + id="linearGradient9752" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(3.12237,0,0,3.12237,-1084.425,-172.2462)" + x1="264.5405" + y1="704.23578" + x2="335.09619" + y2="612.60522" /><linearGradient + inkscape:collect="always" + xlink:href="#XMLID_8_" + id="linearGradient9754" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(3.12237,0,0,3.12237,-1084.425,-172.2462)" + x1="319.186" + y1="713.54639" + x2="373.12509" + y2="626.00598" /><linearGradient + inkscape:collect="always" + xlink:href="#XMLID_9_" + id="linearGradient9756" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(3.12237,0,0,3.12237,-1084.425,-172.2462)" + x1="331.49609" + y1="790.51508" + x2="409.85471" + y2="688.75092" /><linearGradient + inkscape:collect="always" + xlink:href="#XMLID_12_" + id="linearGradient9758" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(3.12237,0,0,3.12237,-1084.425,-172.2462)" + x1="517.18988" + y1="239.939" + x2="410.27759" + y2="522.02393" /> + <foreignObject + id="foreignObject8" + height="1" + width="1" + y="0" + x="0" + requiredExtensions="http://ns.adobe.com/AdobeIllustrator/10.0/"> + <i:pgfRef + xlink:href="#adobe_illustrator_pgf"> + </i:pgfRef> + </foreignObject> + + <linearGradient + inkscape:collect="always" + xlink:href="#XMLID_12_" + id="linearGradient3423" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(3.12237,0,0,3.12237,-1084.425,-172.2462)" + x1="517.18988" + y1="239.939" + x2="410.27759" + y2="522.02393" /><radialGradient + inkscape:collect="always" + xlink:href="#XMLID_22_" + id="radialGradient3567" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(3.12237,0,0,3.12237,-1084.425,-172.2462)" + cx="622.72803" + cy="621.17328" + fx="622.72803" + fy="621.17328" + r="510.71881" /><radialGradient + inkscape:collect="always" + xlink:href="#XMLID_25_" + id="radialGradient3569" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(3.12237,0,0,3.12237,-1084.425,-172.2462)" + cx="375.21439" + cy="783.16357" + fx="375.21439" + fy="783.16357" + r="300.5752" /><linearGradient + inkscape:collect="always" + xlink:href="#XMLID_4_" + id="linearGradient3571" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(-3.12237,0,0,3.12237,1885.808,-172.2462)" + x1="429.84909" + y1="815.45459" + x2="480.12021" + y2="643.30768" /><linearGradient + inkscape:collect="always" + xlink:href="#XMLID_5_" + id="linearGradient3573" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(3.12237,0,0,3.12237,-1084.425,-172.2462)" + x1="220.9512" + y1="634.63232" + x2="278.14471" + y2="560.35498" /><linearGradient + inkscape:collect="always" + xlink:href="#XMLID_6_" + id="linearGradient3575" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(3.12237,0,0,3.12237,-1084.425,-172.2462)" + x1="237.20799" + y1="684.01898" + x2="328.18469" + y2="565.86761" /><linearGradient + inkscape:collect="always" + xlink:href="#XMLID_7_" + id="linearGradient3577" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(3.12237,0,0,3.12237,-1084.425,-172.2462)" + x1="264.5405" + y1="704.23578" + x2="335.09619" + y2="612.60522" /><linearGradient + inkscape:collect="always" + xlink:href="#XMLID_8_" + id="linearGradient3579" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(3.12237,0,0,3.12237,-1084.425,-172.2462)" + x1="319.186" + y1="713.54639" + x2="373.12509" + y2="626.00598" /><linearGradient + inkscape:collect="always" + xlink:href="#XMLID_9_" + id="linearGradient3581" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(3.12237,0,0,3.12237,-1084.425,-172.2462)" + x1="331.49609" + y1="790.51508" + x2="409.85471" + y2="688.75092" /><linearGradient + inkscape:collect="always" + xlink:href="#XMLID_12_" + id="linearGradient3583" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(3.12237,0,0,3.12237,-1084.425,-172.2462)" + x1="517.18988" + y1="239.939" + x2="410.27759" + y2="522.02393" /><radialGradient + gradientUnits="userSpaceOnUse" + fy="621.17328" + fx="622.72803" + r="510.71881" + cy="621.17328" + cx="622.72803" + id="radialGradient3930"> + <stop + id="stop14" + style="stop-color:#FFFFFF" + offset="0" /> + <stop + id="stop16" + style="stop-color:#E6E6E6" + offset="0.2814" /> + <stop + id="stop18" + style="stop-color:#C0C0C0" + offset="0.7515" /> + <stop + id="stop20" + style="stop-color:#B2B2B2" + offset="1" /> + </radialGradient><mask + maskUnits="userSpaceOnUse" + x="122.963" + y="97.149" + width="576.609" + height="587.491" + id="mask3921"> + <g + id="g3923" + style="filter:url(#Adobe_OpacityMaskFilter)"> + <radialGradient + id="radialGradient3925" + cx="88.977501" + cy="-20.899401" + r="673.11609" + fx="88.977501" + fy="-20.899401" + gradientUnits="userSpaceOnUse"> + <stop + offset="0.309" + style="stop-color:#FFFFFF" + id="stop34" /> + <stop + offset="1" + style="stop-color:#000000" + id="stop36" /> + </radialGradient> + <ellipse + cx="295.89499" + cy="286.172" + rx="512.93402" + ry="349.10599" + id="ellipse38" + style="fill:url(#XMLID_24_)" /> + </g> + </mask><mask + maskUnits="userSpaceOnUse" + x="243.061" + y="279.317" + width="252.506" + height="187.183" + id="mask3907"> + <g + id="g193" + style="filter:url(#Adobe_OpacityMaskFilter_4_)"> + <linearGradient + id="linearGradient3910" + gradientUnits="userSpaceOnUse" + x1="340.0488" + y1="266.93359" + x2="372.38071" + y2="417.81519"> + <stop + offset="0" + style="stop-color:#FFFFFF" + id="stop196" /> + <stop + offset="0.0867" + style="stop-color:#F8F8F8" + id="stop198" /> + <stop + offset="0.2156" + style="stop-color:#E4E4E4" + id="stop200" /> + <stop + offset="0.3708" + style="stop-color:#C2C2C2" + id="stop202" /> + <stop + offset="0.5465" + style="stop-color:#949494" + id="stop204" /> + <stop + offset="0.7394" + style="stop-color:#595959" + id="stop206" /> + <stop + offset="0.9438" + style="stop-color:#151515" + id="stop208" /> + <stop + offset="0.9944" + style="stop-color:#000000" + id="stop210" /> + </linearGradient> + <ellipse + cx="363.052" + cy="374.28201" + rx="215.32401" + ry="183.32201" + id="ellipse212" + style="fill:url(#XMLID_14_)" /> + </g> + </mask><mask + maskUnits="userSpaceOnUse" + x="191.541" + y="217.631" + width="447.197" + height="527.73" + id="mask3893"> + <g + id="g159"> + <radialGradient + id="radialGradient3896" + cx="657.2124" + cy="155.23289" + r="606.95343" + fx="657.2124" + fy="155.23289" + gradientTransform="matrix(0.931,0,0,0.9299,28.7459,21.6629)" + gradientUnits="userSpaceOnUse"> + <stop + offset="0" + style="stop-color:#C9C9C8" + id="stop162" /> + <stop + offset="0.0881" + style="stop-color:#C4C4C4" + id="stop164" /> + <stop + offset="0.2192" + style="stop-color:#B8B8B7" + id="stop166" /> + <stop + offset="0.3769" + style="stop-color:#A1A0A0" + id="stop168" /> + <stop + offset="0.5556" + style="stop-color:#7F7F7E" + id="stop170" /> + <stop + offset="0.7517" + style="stop-color:#4F4F4E" + id="stop172" /> + <stop + offset="0.9595" + style="stop-color:#121212" + id="stop174" /> + <stop + offset="1" + style="stop-color:#000000" + id="stop176" /> + </radialGradient> + <circle + cx="408.427" + cy="444.22601" + r="302.95401" + id="circle178" + style="fill:url(#XMLID_11_)" /> + </g> + </mask><linearGradient + id="linearGradient3889" + gradientUnits="userSpaceOnUse" + x1="331.49609" + y1="790.51508" + x2="409.85471" + y2="688.75092"> + <stop + offset="0" + style="stop-color:#FFFFFF" + id="stop147" /> + <stop + offset="1" + style="stop-color:#193D6B" + id="stop149" /> + </linearGradient><linearGradient + id="linearGradient3885" + gradientUnits="userSpaceOnUse" + x1="319.186" + y1="713.54639" + x2="373.12509" + y2="626.00598"> + <stop + offset="0" + style="stop-color:#FFFFFF" + id="stop140" /> + <stop + offset="1" + style="stop-color:#193D6B" + id="stop142" /> + </linearGradient><linearGradient + id="linearGradient3881" + gradientUnits="userSpaceOnUse" + x1="264.5405" + y1="704.23578" + x2="335.09619" + y2="612.60522"> + <stop + offset="0" + style="stop-color:#FFFFFF" + id="stop133" /> + <stop + offset="1" + style="stop-color:#193D6B" + id="stop135" /> + </linearGradient><linearGradient + id="linearGradient3877" + gradientUnits="userSpaceOnUse" + x1="237.20799" + y1="684.01898" + x2="328.18469" + y2="565.86761"> + <stop + offset="0" + style="stop-color:#FFFFFF" + id="stop126" /> + <stop + offset="1" + style="stop-color:#193D6B" + id="stop128" /> + </linearGradient><linearGradient + id="linearGradient3873" + gradientUnits="userSpaceOnUse" + x1="220.9512" + y1="634.63232" + x2="278.14471" + y2="560.35498"> + <stop + offset="0" + style="stop-color:#FFFFFF" + id="stop119" /> + <stop + offset="1" + style="stop-color:#193D6B" + id="stop121" /> + </linearGradient><linearGradient + id="linearGradient3869" + gradientUnits="userSpaceOnUse" + x1="429.84909" + y1="815.45459" + x2="480.12021" + y2="643.30768" + gradientTransform="matrix(-1.1256295,0,0,1.1256295,1028.9091,-39.54673)"> + <stop + offset="0" + style="stop-color:#FFFFFF" + id="stop112" /> + <stop + offset="1" + style="stop-color:#E5EAF2" + id="stop114" /> + </linearGradient><mask + maskUnits="userSpaceOnUse" + x="101.57" + y="425.034" + width="156.907" + height="127.553" + id="mask3854"> + <g + id="g86"> + <linearGradient + id="linearGradient3857" + gradientUnits="userSpaceOnUse" + x1="206.09081" + y1="397.62061" + x2="186.92191" + y2="563.47321"> + <stop + offset="0" + style="stop-color:#FFFFFF" + id="stop89" /> + <stop + offset="0.1111" + style="stop-color:#CBCBCB" + id="stop91" /> + <stop + offset="0.2396" + style="stop-color:#969696" + id="stop93" /> + <stop + offset="0.3698" + style="stop-color:#686868" + id="stop95" /> + <stop + offset="0.4991" + style="stop-color:#424242" + id="stop97" /> + <stop + offset="0.6273" + style="stop-color:#252525" + id="stop99" /> + <stop + offset="0.7542" + style="stop-color:#111111" + id="stop101" /> + <stop + offset="0.8792" + style="stop-color:#040404" + id="stop103" /> + <stop + offset="1" + style="stop-color:#000000" + id="stop105" /> + </linearGradient> + <ellipse + cx="194.76401" + cy="495.62299" + rx="160.94" + ry="123.604" + id="ellipse107" + style="fill:url(#XMLID_3_)" /> + </g> + </mask><mask + maskUnits="userSpaceOnUse" + x="155.705" + y="461.543" + width="93.126" + height="44.813" + id="mask3846"> + <g + id="g69"> + <linearGradient + id="linearGradient3849" + gradientUnits="userSpaceOnUse" + x1="211.7222" + y1="562.85791" + x2="200.0733" + y2="466.75449"> + <stop + offset="0" + style="stop-color:#FFFFFF" + id="stop72" /> + <stop + offset="1" + style="stop-color:#000000" + id="stop74" /> + </linearGradient> + <ellipse + cx="203.617" + cy="495.98801" + rx="80.542" + ry="59.682999" + id="ellipse76" + style="fill:url(#XMLID_1_)" /> + </g> + </mask><radialGradient + gradientTransform="matrix(1.1256295,0,0,1.1256295,-41.873391,-39.54673)" + id="radialGradient3838" + cx="375.21439" + cy="783.16357" + r="300.5752" + fx="375.21439" + fy="783.16357" + gradientUnits="userSpaceOnUse"> + <stop + offset="0.0112" + style="stop-color:#D2E8EC" + id="stop46" /> + <stop + offset="0.2921" + style="stop-color:#60A8CE" + id="stop48" /> + <stop + offset="0.4575" + style="stop-color:#4680A8" + id="stop50" /> + <stop + offset="0.6767" + style="stop-color:#2F5C84" + id="stop52" /> + <stop + offset="0.8653" + style="stop-color:#234970" + id="stop54" /> + <stop + offset="1" + style="stop-color:#1F426A" + id="stop56" /> + </radialGradient><linearGradient + id="linearGradient5498"><stop + id="stop5500" + offset="0" + style="stop-color:#eeeeee;stop-opacity:1;" /><stop + id="stop5502" + offset="1" + style="stop-color:#eeeeee;stop-opacity:0;" /></linearGradient><radialGradient + r="300.5752" + fy="783.16357" + fx="375.21439" + cy="783.16357" + cx="375.21439" + gradientTransform="matrix(1.1256295,0,0,1.1256295,-41.873391,-39.54673)" + gradientUnits="userSpaceOnUse" + id="radialGradient33328" + xlink:href="#radialGradient29545" + inkscape:collect="always" /><linearGradient + y2="643.30768" + x2="480.12021" + y1="815.45459" + x1="429.84909" + gradientTransform="matrix(-1.1256295,0,0,1.1256295,1028.9091,-39.54673)" + gradientUnits="userSpaceOnUse" + id="linearGradient33324" + xlink:href="#XMLID_4_" + inkscape:collect="always" /><linearGradient + y2="560.35498" + x2="278.14471" + y1="634.63232" + x1="220.9512" + gradientTransform="matrix(1.1256295,0,0,1.1256295,-41.873391,-39.54673)" + gradientUnits="userSpaceOnUse" + id="linearGradient33321" + xlink:href="#XMLID_5_" + inkscape:collect="always" /><linearGradient + y2="565.86761" + x2="328.18469" + y1="684.01898" + x1="237.20799" + gradientTransform="matrix(1.1256295,0,0,1.1256295,-41.873391,-39.54673)" + gradientUnits="userSpaceOnUse" + id="linearGradient33318" + xlink:href="#XMLID_6_" + inkscape:collect="always" /><linearGradient + y2="612.60522" + x2="335.09619" + y1="704.23578" + x1="264.5405" + gradientTransform="matrix(1.1256295,0,0,1.1256295,-41.873391,-39.54673)" + gradientUnits="userSpaceOnUse" + id="linearGradient33315" + xlink:href="#XMLID_7_" + inkscape:collect="always" /><linearGradient + y2="626.00598" + x2="373.12509" + y1="713.54639" + x1="319.186" + gradientTransform="matrix(1.1256295,0,0,1.1256295,-41.873391,-39.54673)" + gradientUnits="userSpaceOnUse" + id="linearGradient33312" + xlink:href="#XMLID_8_" + inkscape:collect="always" /><linearGradient + y2="688.75092" + x2="409.85471" + y1="790.51508" + x1="331.49609" + gradientTransform="matrix(1.1256295,0,0,1.1256295,-41.873391,-39.54673)" + gradientUnits="userSpaceOnUse" + id="linearGradient33309" + xlink:href="#XMLID_9_" + inkscape:collect="always" /><linearGradient + y2="630.52039" + x2="361.1441" + y1="-363.42487" + x1="1508.4957" + gradientUnits="userSpaceOnUse" + id="linearGradient33306" + xlink:href="#linearGradient5498" + inkscape:collect="always" /><linearGradient + y2="458.38397" + x2="370.20648" + y1="-309.6048" + x1="370.62177" + gradientUnits="userSpaceOnUse" + id="linearGradient33303" + xlink:href="#linearGradient5498" + inkscape:collect="always" /><linearGradient + y2="580.54669" + x2="130.31757" + y1="274.88394" + x1="247.47713" + gradientUnits="userSpaceOnUse" + id="linearGradient33300" + xlink:href="#linearGradient5498" + inkscape:collect="always" /><linearGradient + y2="491.06723" + x2="185.21225" + y1="756.76105" + x1="220.36789" + gradientUnits="userSpaceOnUse" + id="linearGradient33297" + xlink:href="#linearGradient5498" + inkscape:collect="always" /><linearGradient + y2="491.06723" + x2="185.21225" + y1="756.76105" + x1="220.36789" + gradientUnits="userSpaceOnUse" + id="linearGradient33294" + xlink:href="#linearGradient5498" + inkscape:collect="always" /><linearGradient + y2="580.54669" + x2="130.31757" + y1="274.88394" + x1="247.47713" + gradientUnits="userSpaceOnUse" + id="linearGradient33292" + xlink:href="#linearGradient5498" + inkscape:collect="always" /><linearGradient + y2="458.38397" + x2="370.20648" + y1="-309.6048" + x1="370.62177" + gradientUnits="userSpaceOnUse" + id="linearGradient33290" + xlink:href="#linearGradient5498" + inkscape:collect="always" /><linearGradient + y2="630.52039" + x2="361.1441" + y1="-363.42487" + x1="1508.4957" + gradientUnits="userSpaceOnUse" + id="linearGradient33288" + xlink:href="#linearGradient5498" + inkscape:collect="always" /><linearGradient + y2="688.75092" + x2="409.85471" + y1="790.51508" + x1="331.49609" + gradientTransform="matrix(1.1256295,0,0,1.1256295,-41.873391,-39.54673)" + gradientUnits="userSpaceOnUse" + id="linearGradient33286" + xlink:href="#XMLID_9_" + inkscape:collect="always" /><linearGradient + y2="626.00598" + x2="373.12509" + y1="713.54639" + x1="319.186" + gradientTransform="matrix(1.1256295,0,0,1.1256295,-41.873391,-39.54673)" + gradientUnits="userSpaceOnUse" + id="linearGradient33284" + xlink:href="#XMLID_8_" + inkscape:collect="always" /><linearGradient + y2="612.60522" + x2="335.09619" + y1="704.23578" + x1="264.5405" + gradientTransform="matrix(1.1256295,0,0,1.1256295,-41.873391,-39.54673)" + gradientUnits="userSpaceOnUse" + id="linearGradient33282" + xlink:href="#XMLID_7_" + inkscape:collect="always" /><linearGradient + y2="565.86761" + x2="328.18469" + y1="684.01898" + x1="237.20799" + gradientTransform="matrix(1.1256295,0,0,1.1256295,-41.873391,-39.54673)" + gradientUnits="userSpaceOnUse" + id="linearGradient33280" + xlink:href="#XMLID_6_" + inkscape:collect="always" /><linearGradient + y2="560.35498" + x2="278.14471" + y1="634.63232" + x1="220.9512" + gradientTransform="matrix(1.1256295,0,0,1.1256295,-41.873391,-39.54673)" + gradientUnits="userSpaceOnUse" + id="linearGradient33278" + xlink:href="#XMLID_5_" + inkscape:collect="always" /><linearGradient + y2="643.30768" + x2="480.12021" + y1="815.45459" + x1="429.84909" + gradientTransform="matrix(-1.1256295,0,0,1.1256295,1028.9091,-39.54673)" + gradientUnits="userSpaceOnUse" + id="linearGradient33276" + xlink:href="#XMLID_4_" + inkscape:collect="always" /><radialGradient + r="300.5752" + fy="783.16357" + fx="375.21439" + cy="783.16357" + cx="375.21439" + gradientTransform="matrix(1.1256295,0,0,1.1256295,-41.873391,-39.54673)" + gradientUnits="userSpaceOnUse" + id="radialGradient33274" + xlink:href="#radialGradient29545" + inkscape:collect="always" /><linearGradient + gradientUnits="userSpaceOnUse" + y2="491.06723" + x2="185.21225" + y1="756.76105" + x1="220.36789" + id="linearGradient2976" + xlink:href="#linearGradient5498" + inkscape:collect="always" /><linearGradient + gradientUnits="userSpaceOnUse" + y2="580.54669" + x2="130.31757" + y1="274.88394" + x1="247.47713" + id="linearGradient2974" + xlink:href="#linearGradient5498" + inkscape:collect="always" /><linearGradient + gradientUnits="userSpaceOnUse" + y2="458.38397" + x2="370.20648" + y1="-309.6048" + x1="370.62177" + id="linearGradient2972" + xlink:href="#linearGradient5498" + inkscape:collect="always" /><linearGradient + y2="630.52039" + x2="361.1441" + y1="-363.42487" + x1="1508.4957" + gradientUnits="userSpaceOnUse" + id="linearGradient2970" + xlink:href="#linearGradient5498" + inkscape:collect="always" /><radialGradient + r="300.5752" + fy="783.16357" + fx="375.21439" + cy="783.16357" + cx="375.21439" + gradientTransform="matrix(1.1256295,0,0,1.1256295,-41.873391,-39.54673)" + gradientUnits="userSpaceOnUse" + id="radialGradient2968" + xlink:href="#radialGradient29545" + inkscape:collect="always" /><linearGradient + y2="643.30768" + x2="480.12021" + y1="815.45459" + x1="429.84909" + gradientTransform="matrix(-1.1256295,0,0,1.1256295,1028.9091,-39.54673)" + gradientUnits="userSpaceOnUse" + id="linearGradient2966" + xlink:href="#XMLID_4_" + inkscape:collect="always" /><linearGradient + gradientTransform="matrix(1.1256295,0,0,1.1256295,-41.873391,-39.54673)" + y2="560.35498" + x2="278.14471" + y1="634.63232" + x1="220.9512" + gradientUnits="userSpaceOnUse" + id="linearGradient2964" + xlink:href="#XMLID_5_" + inkscape:collect="always" /><linearGradient + gradientTransform="matrix(1.1256295,0,0,1.1256295,-41.873391,-39.54673)" + y2="565.86761" + x2="328.18469" + y1="684.01898" + x1="237.20799" + gradientUnits="userSpaceOnUse" + id="linearGradient2962" + xlink:href="#XMLID_6_" + inkscape:collect="always" /><linearGradient + gradientTransform="matrix(1.1256295,0,0,1.1256295,-41.873391,-39.54673)" + y2="612.60522" + x2="335.09619" + y1="704.23578" + x1="264.5405" + gradientUnits="userSpaceOnUse" + id="linearGradient2960" + xlink:href="#XMLID_7_" + inkscape:collect="always" /><linearGradient + gradientTransform="matrix(1.1256295,0,0,1.1256295,-41.873391,-39.54673)" + y2="626.00598" + x2="373.12509" + y1="713.54639" + x1="319.186" + gradientUnits="userSpaceOnUse" + id="linearGradient2958" + xlink:href="#XMLID_8_" + inkscape:collect="always" /><linearGradient + gradientTransform="matrix(1.1256295,0,0,1.1256295,-41.873391,-39.54673)" + y2="688.75092" + x2="409.85471" + y1="790.51508" + x1="331.49609" + gradientUnits="userSpaceOnUse" + id="linearGradient2956" + xlink:href="#XMLID_9_" + inkscape:collect="always" /><radialGradient + gradientUnits="userSpaceOnUse" + fy="621.17328" + fx="622.72803" + r="510.71881" + cy="621.17328" + cx="622.72803" + id="radialGradient2946"> + <stop + id="stop2948" + style="stop-color:#FFFFFF" + offset="0" /> + <stop + id="stop2950" + style="stop-color:#E6E6E6" + offset="0.2814" /> + <stop + id="stop2952" + style="stop-color:#C0C0C0" + offset="0.7515" /> + <stop + id="stop2954" + style="stop-color:#B2B2B2" + offset="1" /> + </radialGradient><mask + maskUnits="userSpaceOnUse" + x="122.963" + y="97.149" + width="576.609" + height="587.491" + id="mask2934"> + <g + id="g2936" + style="filter:url(#Adobe_OpacityMaskFilter)"> + <radialGradient + id="radialGradient2938" + cx="88.977501" + cy="-20.899401" + r="673.11609" + fx="88.977501" + fy="-20.899401" + gradientUnits="userSpaceOnUse"> + <stop + offset="0.309" + style="stop-color:#FFFFFF" + id="stop2940" /> + <stop + offset="1" + style="stop-color:#000000" + id="stop2942" /> + </radialGradient> + <ellipse + cx="295.89499" + cy="286.172" + rx="512.93402" + ry="349.10599" + id="ellipse2944" + style="fill:url(#XMLID_24_)" /> + </g> + </mask><mask + maskUnits="userSpaceOnUse" + x="243.061" + y="279.317" + width="252.506" + height="187.183" + id="mask2910"> + <g + id="g2912" + style="filter:url(#Adobe_OpacityMaskFilter_4_)"> + <linearGradient + id="linearGradient2914" + gradientUnits="userSpaceOnUse" + x1="340.0488" + y1="266.93359" + x2="372.38071" + y2="417.81519"> + <stop + offset="0" + style="stop-color:#FFFFFF" + id="stop2916" /> + <stop + offset="0.0867" + style="stop-color:#F8F8F8" + id="stop2918" /> + <stop + offset="0.2156" + style="stop-color:#E4E4E4" + id="stop2920" /> + <stop + offset="0.3708" + style="stop-color:#C2C2C2" + id="stop2922" /> + <stop + offset="0.5465" + style="stop-color:#949494" + id="stop2924" /> + <stop + offset="0.7394" + style="stop-color:#595959" + id="stop2926" /> + <stop + offset="0.9438" + style="stop-color:#151515" + id="stop2928" /> + <stop + offset="0.9944" + style="stop-color:#000000" + id="stop2930" /> + </linearGradient> + <ellipse + cx="363.052" + cy="374.28201" + rx="215.32401" + ry="183.32201" + id="ellipse2932" + style="fill:url(#XMLID_14_)" /> + </g> + </mask><mask + maskUnits="userSpaceOnUse" + x="191.541" + y="217.631" + width="447.197" + height="527.73" + id="mask2886"> + <g + id="g2888"> + <radialGradient + id="radialGradient2890" + cx="657.2124" + cy="155.23289" + r="606.95343" + fx="657.2124" + fy="155.23289" + gradientTransform="matrix(0.931,0,0,0.9299,28.7459,21.6629)" + gradientUnits="userSpaceOnUse"> + <stop + offset="0" + style="stop-color:#C9C9C8" + id="stop2892" /> + <stop + offset="0.0881" + style="stop-color:#C4C4C4" + id="stop2894" /> + <stop + offset="0.2192" + style="stop-color:#B8B8B7" + id="stop2896" /> + <stop + offset="0.3769" + style="stop-color:#A1A0A0" + id="stop2898" /> + <stop + offset="0.5556" + style="stop-color:#7F7F7E" + id="stop2900" /> + <stop + offset="0.7517" + style="stop-color:#4F4F4E" + id="stop2902" /> + <stop + offset="0.9595" + style="stop-color:#121212" + id="stop2904" /> + <stop + offset="1" + style="stop-color:#000000" + id="stop2906" /> + </radialGradient> + <circle + cx="408.427" + cy="444.22601" + r="302.95401" + id="circle2908" + style="fill:url(#XMLID_11_)" /> + </g> + </mask><linearGradient + id="linearGradient2880" + gradientUnits="userSpaceOnUse" + x1="331.49609" + y1="790.51508" + x2="409.85471" + y2="688.75092"> + <stop + offset="0" + style="stop-color:#FFFFFF" + id="stop2882" /> + <stop + offset="1" + style="stop-color:#193D6B" + id="stop2884" /> + </linearGradient><linearGradient + id="linearGradient2874" + gradientUnits="userSpaceOnUse" + x1="319.186" + y1="713.54639" + x2="373.12509" + y2="626.00598"> + <stop + offset="0" + style="stop-color:#FFFFFF" + id="stop2876" /> + <stop + offset="1" + style="stop-color:#193D6B" + id="stop2878" /> + </linearGradient><linearGradient + id="linearGradient2868" + gradientUnits="userSpaceOnUse" + x1="264.5405" + y1="704.23578" + x2="335.09619" + y2="612.60522"> + <stop + offset="0" + style="stop-color:#FFFFFF" + id="stop2870" /> + <stop + offset="1" + style="stop-color:#193D6B" + id="stop2872" /> + </linearGradient><linearGradient + id="linearGradient2862" + gradientUnits="userSpaceOnUse" + x1="237.20799" + y1="684.01898" + x2="328.18469" + y2="565.86761"> + <stop + offset="0" + style="stop-color:#FFFFFF" + id="stop2864" /> + <stop + offset="1" + style="stop-color:#193D6B" + id="stop2866" /> + </linearGradient><linearGradient + id="linearGradient2856" + gradientUnits="userSpaceOnUse" + x1="220.9512" + y1="634.63232" + x2="278.14471" + y2="560.35498"> + <stop + offset="0" + style="stop-color:#FFFFFF" + id="stop2858" /> + <stop + offset="1" + style="stop-color:#193D6B" + id="stop2860" /> + </linearGradient><linearGradient + id="linearGradient2850" + gradientUnits="userSpaceOnUse" + x1="429.84909" + y1="815.45459" + x2="480.12021" + y2="643.30768" + gradientTransform="matrix(-1.1256295,0,0,1.1256295,1028.9091,-39.54673)"> + <stop + offset="0" + style="stop-color:#FFFFFF" + id="stop2852" /> + <stop + offset="1" + style="stop-color:#E5EAF2" + id="stop2854" /> + </linearGradient><mask + maskUnits="userSpaceOnUse" + x="101.57" + y="425.034" + width="156.907" + height="127.553" + id="mask2824"> + <g + id="g2826"> + <linearGradient + id="linearGradient2828" + gradientUnits="userSpaceOnUse" + x1="206.09081" + y1="397.62061" + x2="186.92191" + y2="563.47321"> + <stop + offset="0" + style="stop-color:#FFFFFF" + id="stop2830" /> + <stop + offset="0.1111" + style="stop-color:#CBCBCB" + id="stop2832" /> + <stop + offset="0.2396" + style="stop-color:#969696" + id="stop2834" /> + <stop + offset="0.3698" + style="stop-color:#686868" + id="stop2836" /> + <stop + offset="0.4991" + style="stop-color:#424242" + id="stop2838" /> + <stop + offset="0.6273" + style="stop-color:#252525" + id="stop2840" /> + <stop + offset="0.7542" + style="stop-color:#111111" + id="stop2842" /> + <stop + offset="0.8792" + style="stop-color:#040404" + id="stop2844" /> + <stop + offset="1" + style="stop-color:#000000" + id="stop2846" /> + </linearGradient> + <ellipse + cx="194.76401" + cy="495.62299" + rx="160.94" + ry="123.604" + id="ellipse2848" + style="fill:url(#XMLID_3_)" /> + </g> + </mask><mask + maskUnits="userSpaceOnUse" + x="155.705" + y="461.543" + width="93.126" + height="44.813" + id="mask2812"> + <g + id="g2814"> + <linearGradient + id="linearGradient2816" + gradientUnits="userSpaceOnUse" + x1="211.7222" + y1="562.85791" + x2="200.0733" + y2="466.75449"> + <stop + offset="0" + style="stop-color:#FFFFFF" + id="stop2818" /> + <stop + offset="1" + style="stop-color:#000000" + id="stop2820" /> + </linearGradient> + <ellipse + cx="203.617" + cy="495.98801" + rx="80.542" + ry="59.682999" + id="ellipse2822" + style="fill:url(#XMLID_1_)" /> + </g> + </mask><radialGradient + gradientTransform="matrix(1.1256295,0,0,1.1256295,-41.873391,-39.54673)" + id="radialGradient2798" + cx="375.21439" + cy="783.16357" + r="300.5752" + fx="375.21439" + fy="783.16357" + gradientUnits="userSpaceOnUse"> + <stop + offset="0.0112" + style="stop-color:#D2E8EC" + id="stop2800" /> + <stop + offset="0.2921" + style="stop-color:#60A8CE" + id="stop2802" /> + <stop + offset="0.4575" + style="stop-color:#4680A8" + id="stop2804" /> + <stop + offset="0.6767" + style="stop-color:#2F5C84" + id="stop2806" /> + <stop + offset="0.8653" + style="stop-color:#234970" + id="stop2808" /> + <stop + offset="1" + style="stop-color:#1F426A" + id="stop2810" /> + </radialGradient><linearGradient + id="linearGradient2792"><stop + id="stop2794" + offset="0" + style="stop-color:#eeeeee;stop-opacity:1;" /><stop + id="stop2796" + offset="1" + style="stop-color:#eeeeee;stop-opacity:0;" /></linearGradient><radialGradient + gradientUnits="userSpaceOnUse" + fy="783.16357" + fx="375.21439" + r="300.5752" + cy="783.16357" + cx="375.21439" + id="radialGradient29545" + gradientTransform="matrix(1.1256295,0,0,1.1256295,-41.873391,-39.54673)"> + <stop + id="stop29547" + style="stop-color:#eeeeef;stop-opacity:0.94117647;" + offset="0.0112" /> + <stop + id="stop29549" + style="stop-color:#6193cf;stop-opacity:1;" + offset="0.29210001" /> + <stop + id="stop29551" + style="stop-color:#2c72c7;stop-opacity:1;" + offset="0.45750001" /> + <stop + id="stop29553" + style="stop-color:#0057ae;stop-opacity:1;" + offset="0.6767" /> + <stop + id="stop29555" + style="stop-color:#004d9a;stop-opacity:1;" + offset="0.8653" /> + <stop + id="stop29557" + style="stop-color:#00438a;stop-opacity:1;" + offset="1" /> + </radialGradient><linearGradient + inkscape:collect="always" + xlink:href="#linearGradient5498" + id="linearGradient5118" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.245382,0,0,0.245382,401.46597,47.397214)" + x1="220.36789" + y1="756.76105" + x2="185.21225" + y2="491.06723" /><linearGradient + inkscape:collect="always" + xlink:href="#linearGradient5498" + id="linearGradient5121" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.245382,0,0,0.245382,401.46597,47.397214)" + x1="247.47713" + y1="274.88394" + x2="130.31757" + y2="580.54669" /><linearGradient + inkscape:collect="always" + xlink:href="#linearGradient5498" + id="linearGradient5124" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.245382,0,0,0.245382,401.46597,47.397214)" + x1="370.62177" + y1="-309.6048" + x2="370.20648" + y2="458.38397" /><linearGradient + inkscape:collect="always" + xlink:href="#linearGradient5498" + id="linearGradient5127" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.245382,0,0,0.245382,401.46597,47.397214)" + x1="1508.4957" + y1="-363.42487" + x2="361.1441" + y2="630.52039" /><linearGradient + inkscape:collect="always" + xlink:href="#XMLID_5_" + id="linearGradient5130" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.2762093,0,0,0.2762093,391.19099,37.693155)" + x1="331.49609" + y1="790.51508" + x2="409.85471" + y2="688.75092" /><linearGradient + inkscape:collect="always" + xlink:href="#XMLID_5_" + id="linearGradient5133" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.2762093,0,0,0.2762093,391.19099,37.693155)" + x1="319.186" + y1="713.54639" + x2="373.12509" + y2="626.00598" /><linearGradient + inkscape:collect="always" + xlink:href="#XMLID_5_" + id="linearGradient5136" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.2762093,0,0,0.2762093,391.19099,37.693155)" + x1="264.5405" + y1="704.23578" + x2="335.09619" + y2="612.60522" /><linearGradient + inkscape:collect="always" + xlink:href="#XMLID_5_" + id="linearGradient5139" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.2762093,0,0,0.2762093,391.19099,37.693155)" + x1="237.20799" + y1="684.01898" + x2="328.18469" + y2="565.86761" /><linearGradient + inkscape:collect="always" + xlink:href="#XMLID_5_" + id="linearGradient5142" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.2762093,0,0,0.2762093,391.19099,37.693155)" + x1="220.9512" + y1="634.63232" + x2="278.14471" + y2="560.35498" /><linearGradient + inkscape:collect="always" + xlink:href="#XMLID_4_" + id="linearGradient5145" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(-0.2762093,0,0,0.2762093,653.94178,37.693155)" + x1="429.84909" + y1="815.45459" + x2="480.12021" + y2="643.30768" /><radialGradient + inkscape:collect="always" + xlink:href="#radialGradient29545" + id="radialGradient5149" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.2762093,0,0,0.2762093,391.19099,37.693155)" + cx="375.21439" + cy="783.16357" + fx="375.21439" + fy="783.16357" + r="300.5752" /><radialGradient + inkscape:collect="always" + xlink:href="#XMLID_22_" + id="radialGradient5154" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.245382,0,0,0.245382,401.46597,47.397214)" + cx="663.91144" + cy="649.22662" + fx="663.91144" + fy="649.22662" + r="427.12018" /></defs><sodipodi:namedview + inkscape:window-height="593" + inkscape:window-width="773" + inkscape:pageshadow="2" + inkscape:pageopacity="0.0" + borderopacity="1.0" + bordercolor="#666666" + pagecolor="#ffffff" + id="base" + inkscape:zoom="0.90286865" + inkscape:cx="367.33414" + inkscape:cy="138.18589" + inkscape:window-x="1630" + inkscape:window-y="133" + inkscape:current-layer="svg2" + width="441px" + height="180px" + showguides="true" + inkscape:guide-bbox="true" /> + <metadata + id="metadata4"> + <ns:variableSets> + <ns:variableSet + varSetName="binding1" + locked="none"> + <ns:variables /> + <ns:sampleDataSets /> + </ns:variableSet> + </ns:variableSets> + <ns0:sfw> + <ns0:slices /> + <ns0:sliceSourceBounds + y="-7892" + x="-7791" + width="16383" + height="16383" + bottomLeftOrigin="true" /> + </ns0:sfw> + <rdf:RDF><cc:Work + rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata> + + <i:pgf> + + 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 + + </i:pgf> +<g + id="g6143"><g + inkscape:export-ydpi="42.189999" + inkscape:export-xdpi="42.189999" + inkscape:export-filename="/home/knome/Work/Amarok logo/oxygen_logo.png" + id="g25" + style="fill:#0057ae;fill-opacity:1" + transform="matrix(1.057339,0,0,1.057339,-27.56698,-5.6631554)"> + <g + i:knockout="Off" + id="g27" + style="fill:#0057ae;fill-opacity:1"> + <path + i:knockout="Off" + d="M 364.178,121.267 L 364.546,121.267 C 368.59,106.011 379.986,100.864 395.427,101.231 L 395.427,130.825 C 392.854,130.641 390.464,130.641 388.074,130.641 C 369.876,130.825 363.627,141.67 364.178,159.316 L 364.178,196.263 L 329.253,196.263 L 329.253,127.7 C 329.253,119.429 329.069,113.914 328.15,103.988 L 363.627,103.988 L 364.178,121.267 z " + id="path29" + style="fill:#0057ae;fill-opacity:1" /> + </g> + <g + i:knockout="Off" + id="g31" + style="fill:#0057ae;fill-opacity:1"> + <path + i:knockout="Off" + d="M 235.979,130.09 C 236.163,118.509 241.126,111.341 248.479,107.113 C 256.015,102.701 265.757,101.231 275.684,101.231 C 294.249,101.231 315.02,108.032 315.02,135.788 L 315.02,186.154 C 315.204,190.749 315.939,193.507 316.307,196.263 L 291.492,196.263 C 290.94,192.035 290.573,187.808 290.573,183.948 L 290.205,183.948 C 283.955,194.609 274.397,199.02 261.897,199.02 C 244.618,199.02 231.751,189.094 231.751,171.081 C 231.751,164.647 234.692,149.023 251.419,141.854 C 262.448,137.075 276.418,136.523 287.447,136.891 L 287.447,128.251 C 287.447,124.391 284.506,116.855 274.764,116.855 C 265.205,116.855 262.08,125.862 262.448,130.09 L 235.979,130.09 L 235.979,130.09 z M 258.22,167.404 C 258.22,175.125 263.918,180.639 271.638,180.639 C 286.895,180.639 287.446,163.728 287.446,152.883 C 277.888,152.7 258.22,152.516 258.22,167.404 z " + id="path33" + style="fill:#0057ae;fill-opacity:1" /> + </g> + <g + i:knockout="Off" + id="g35" + style="fill:#0057ae;fill-opacity:1"> + <path + i:knockout="Off" + d="M 98.489,117.223 L 98.857,117.223 C 104.371,105.643 115.768,101.231 125.143,101.231 C 142.605,101.231 152.899,107.113 157.678,118.509 C 164.295,107.113 174.773,101.231 188.375,101.231 C 209.697,101.231 222.748,114.649 222.748,131.193 L 222.748,196.263 L 205.286,196.263 L 205.286,141.67 C 205.286,128.987 203.448,114.649 183.044,114.649 C 177.713,114.649 165.949,117.59 162.641,128.986 C 160.251,137.442 160.803,146.632 160.803,148.838 L 160.803,196.262 L 143.34,196.262 L 143.34,141.67 C 143.34,125.678 139.664,114.649 122.936,114.649 C 115.768,114.649 105.474,117.406 100.143,129.722 C 98.121,134.501 98.856,146.633 98.856,148.839 L 98.856,196.263 L 81.395,196.263 L 81.395,103.988 L 98.49,103.988 L 98.49,117.223 L 98.489,117.223 z " + id="path37" + style="fill:#0057ae;fill-opacity:1" /> + </g> + <g + i:knockout="Off" + id="g39" + style="fill:#0057ae;fill-opacity:1"> + <path + i:knockout="Off" + d="M 60.292,131.193 C 59.189,116.12 51.469,111.341 38.418,111.341 C 26.837,111.341 17.279,114.833 15.809,127.333 L 4.044,127.333 C 6.801,108.4 20.955,101.231 38.601,101.231 C 59.005,101.231 71.688,110.606 71.32,131.744 L 71.32,175.492 C 71.136,182.661 71.871,190.013 72.239,196.263 L 60.843,196.263 L 60.475,182.66 L 60.107,182.66 L 59.556,183.947 C 55.512,191.667 43.197,199.019 30.697,199.019 C 13.603,199.021 0,188.543 0,170.529 C 0,158.03 7.537,148.471 18.565,143.876 C 30.88,138.545 46.873,140.383 60.291,139.648 L 60.291,131.193 L 60.292,131.193 z M 32.719,188.911 C 56.799,188.911 61.394,169.058 60.291,149.758 C 44.666,150.31 11.948,147.368 11.948,170.529 C 11.948,183.029 21.506,188.911 32.719,188.911 z " + id="path41" + style="fill:#0057ae;fill-opacity:1" /> + </g> + </g><g + inkscape:export-ydpi="42.189999" + inkscape:export-xdpi="42.189999" + inkscape:export-filename="/home/knome/Work/Amarok logo/oxygen_logo.png" + style="fill:#0057ae" + id="g4531" + transform="matrix(1.057339,0,0,1.057339,-37.592927,-9.1602333)"><g + transform="translate(20.36266,1.566356)" + style="fill:#0057ae;fill-opacity:1" + id="g21" + i:knockout="Off"> + <path + style="fill:#0057ae;fill-opacity:1" + id="path23" + d="M 603.85,105.816 L 639.597,105.816 L 639.597,145.393 L 660.919,105.723 L 700.38,105.723 L 672.534,151.862 L 700.621,197.992 L 661.044,197.992 L 639.583,158.54 L 639.597,197.992 L 603.85,197.992 L 603.85,105.816 z " + i:knockout="Off" /> + </g><path + style="fill:#0057ae;fill-opacity:1" + id="path43" + d="M 781.27063,199.55435 L 720.73663,199.55435 L 720.73663,173.86635 L 781.27063,173.86635 L 781.27063,199.55435 z " + i:knockout="Off" /><path + style="fill:#0057ae;fill-opacity:1" + id="path45" + d="M 781.26163,132.98035 L 720.72763,132.98035 L 720.72763,107.29235 L 781.26163,107.29235 L 781.26163,132.98035 z " + i:knockout="Off" /></g><path + inkscape:export-ydpi="42.189999" + inkscape:export-xdpi="42.189999" + inkscape:export-filename="/home/knome/Work/Amarok logo/oxygen_logo.png" + id="flowRoot7510" + d="M -55.413237,281.31195 C -55.737529,281.12367 -56.09319,280.98768 -56.48022,280.90398 C -56.856814,280.80985 -57.275239,280.76278 -57.735494,280.76276 C -59.36736,280.76278 -60.622634,281.29627 -61.501318,282.36324 C -62.369555,283.41977 -62.803671,284.94179 -62.803665,286.9293 L -62.803665,296.18695 L -65.706487,296.18695 L -65.706487,278.61311 L -62.803665,278.61311 L -62.803665,281.34333 C -62.196955,280.27636 -61.407179,279.48658 -60.434334,278.974 C -59.461506,278.45098 -58.279457,278.18947 -56.888184,278.18945 C -56.689444,278.18947 -56.469772,278.20516 -56.229165,278.23652 C -55.988584,278.25746 -55.721838,278.29407 -55.428928,278.34636 L -55.413237,281.31195 M -38.027686,286.67824 L -38.027686,288.09043 L -51.302213,288.09043 C -51.176691,290.07795 -50.580436,291.59474 -49.513447,292.6408 C -48.436011,293.6764 -46.940144,294.1942 -45.025841,294.1942 C -43.917028,294.1942 -42.844815,294.05821 -41.8092,293.78624 C -40.763154,293.51426 -39.727553,293.1063 -38.702396,292.56234 L -38.702396,295.29256 C -39.738014,295.73191 -40.799766,296.06665 -41.887655,296.29678 C -42.975573,296.52692 -44.079167,296.64198 -45.198441,296.64198 C -48.001896,296.64198 -50.224775,295.82606 -51.867087,294.1942 C -53.498946,292.56235 -54.314874,290.35516 -54.314872,287.57263 C -54.314874,284.69597 -53.540788,282.41555 -51.992614,280.73138 C -50.433987,279.03678 -48.336635,278.18947 -45.700551,278.18945 C -43.336464,278.18947 -41.469245,278.95309 -40.098889,280.48033 C -38.718105,281.99713 -38.027704,284.0631 -38.027686,286.67824 M -40.914817,285.83093 C -40.935754,284.25139 -41.38033,282.99089 -42.248546,282.04942 C -43.10633,281.10798 -44.246537,280.63725 -45.669169,280.63724 C -47.280114,280.63725 -48.571999,281.09229 -49.544829,282.00235 C -50.507212,282.91243 -51.061624,284.19386 -51.208068,285.84662 L -40.914817,285.83093 M -21.72481,281.28056 L -21.72481,271.77186 L -18.837679,271.77186 L -18.837679,296.18695 L -21.72481,296.18695 L -21.72481,293.55087 C -22.33154,294.59693 -23.100395,295.37625 -24.031377,295.88882 C -24.951923,296.39093 -26.060747,296.64198 -27.357854,296.64198 C -29.481367,296.64198 -31.212598,295.79467 -32.551552,294.10005 C -33.880053,292.40544 -34.544302,290.17733 -34.5443,287.41572 C -34.544302,284.65413 -33.880053,282.42602 -32.551552,280.73138 C -31.212598,279.03678 -29.481367,278.18947 -27.357854,278.18945 C -26.060747,278.18947 -24.951923,278.44575 -24.031377,278.95831 C -23.100395,279.46043 -22.33154,280.23452 -21.72481,281.28056 M -31.563023,287.41572 C -31.563028,289.53923 -31.128913,291.2077 -30.260676,292.42112 C -29.381991,293.6241 -28.179021,294.22558 -26.651762,294.22558 C -25.124523,294.22558 -23.921553,293.6241 -23.042848,292.42112 C -22.16417,291.2077 -21.724825,289.53923 -21.72481,287.41572 C -21.724825,285.29222 -22.16417,283.62899 -23.042848,282.426 C -23.921553,281.21258 -25.124523,280.60587 -26.651762,280.60585 C -28.179021,280.60587 -29.381991,281.21258 -30.260676,282.426 C -31.128913,283.62899 -31.563028,285.29222 -31.563023,287.41572 M -12.890817,278.61311 L -10.003686,278.61311 L -10.003686,296.18695 L -12.890817,296.18695 L -12.890817,278.61311 M -12.890817,271.77186 L -10.003686,271.77186 L -10.003686,275.42785 L -12.890817,275.42785 L -12.890817,271.77186 M 7.224956,279.13091 L 7.224956,281.86113 C 6.4090142,281.44272 5.5617048,281.1289 4.6830253,280.91967 C 3.8043224,280.71048 2.8942494,280.60587 1.9528035,280.60585 C 0.51969097,280.60587 -0.55775182,280.82554 -1.2795282,281.26487 C -1.9908553,281.70423 -2.3465161,282.36325 -2.3465114,283.24193 C -2.3465161,283.91142 -2.0902311,284.43968 -1.5776558,284.82671 C -1.0650914,285.20331 -0.03472134,285.5642 1.5134574,285.90939 L 2.501986,286.12906 C 4.552256,286.56842 6.0062807,287.19082 6.8640646,287.99628 C 7.7322813,288.7913 8.1663966,289.90535 8.1664118,291.33845 C 8.1663966,292.97031 7.5178388,294.2622 6.2207365,295.21411 C 4.9340683,296.16603 3.1609949,296.64198 0.90151113,296.64198 C -0.039951645,296.64198 -1.023249,296.54784 -2.0483837,296.35955 C -3.0630678,296.18172 -4.1352803,295.90974 -5.2650244,295.54362 L -5.2650244,292.56234 C -4.198044,293.11676 -3.1467527,293.53518 -2.1111475,293.81762 C -1.075552,294.0896 -0.050412255,294.22558 0.96427485,294.22558 C 2.3241461,294.22558 3.3702071,293.99545 4.1024609,293.53518 C 4.8346925,293.06446 5.2008138,292.40544 5.200826,291.55812 C 5.2008138,290.77358 4.9340683,290.1721 4.4005886,289.75367 C 3.8775467,289.33525 2.7216493,288.93251 0.93289299,288.54546 L -0.071326539,288.3101 C -1.8600977,287.93353 -3.151983,287.35819 -3.9469863,286.5841 C -4.7419957,285.79956 -5.1394989,284.72735 -5.139497,283.36746 C -5.1394989,281.71469 -4.5537047,280.4385 -3.3821128,279.53887 C -2.2105282,278.63927 -0.54729121,278.18947 1.607603,278.18945 C 2.6745766,278.18947 3.6787951,278.26792 4.6202616,278.42481 C 5.5617048,278.58174 6.4299354,278.81711 7.224956,279.13091 M 25.426437,279.28782 L 25.426437,281.98666 C 24.610494,281.53686 23.789336,281.20212 22.962961,280.98244 C 22.14702,280.75232 21.320632,280.63725 20.483794,280.63724 C 18.611334,280.63725 17.157309,281.23351 16.121715,282.426 C 15.086109,283.60806 14.568309,285.2713 14.568313,287.41572 C 14.568309,289.56015 15.086109,291.22862 16.121715,292.42112 C 17.157309,293.60318 18.611334,294.1942 20.483794,294.1942 C 21.320632,294.1942 22.14702,294.08436 22.962961,293.86469 C 23.789336,293.63456 24.610494,293.29459 25.426437,292.84478 L 25.426437,295.51224 C 24.620954,295.88882 23.784106,296.17126 22.915888,296.35955 C 22.058105,296.54784 21.142802,296.64198 20.169975,296.64198 C 17.523431,296.64198 15.420848,295.81037 13.862222,294.14713 C 12.303587,292.48389 11.524271,290.24009 11.524273,287.41572 C 11.524271,284.54952 12.308817,282.29526 13.877912,280.65293 C 15.45746,279.01063 17.617576,278.18947 20.358267,278.18945 C 21.247408,278.18947 22.115638,278.28361 22.962961,278.47189 C 23.810257,278.64974 24.631415,278.92171 25.426437,279.28782 M 37.288775,280.63724 C 35.740595,280.63725 34.516703,281.24397 33.617097,282.45738 C 32.717478,283.66037 32.267672,285.31314 32.267677,287.41572 C 32.267672,289.51831 32.712248,291.17632 33.601406,292.38974 C 34.501012,293.59272 35.730134,294.1942 37.288775,294.1942 C 38.826475,294.1942 40.045136,293.58749 40.944761,292.37405 C 41.84436,291.16062 42.294167,289.50785 42.294181,287.41572 C 42.294167,285.33406 41.84436,283.68652 40.944761,282.47307 C 40.045136,281.2492 38.826475,280.63725 37.288775,280.63724 M 37.288775,278.18945 C 39.799311,278.18947 41.771136,279.0054 43.204255,280.63724 C 44.637343,282.26911 45.353895,284.5286 45.353913,287.41572 C 45.353895,290.29239 44.637343,292.55189 43.204255,294.1942 C 41.771136,295.82606 39.799311,296.64198 37.288775,296.64198 C 34.767758,296.64198 32.790703,295.82606 31.357603,294.1942 C 29.934956,292.55189 29.223635,290.29239 29.223637,287.41572 C 29.223635,284.5286 29.934956,282.26911 31.357603,280.63724 C 32.790703,279.0054 34.767758,278.18947 37.288775,278.18945 M 48.052758,278.61311 L 51.11249,278.61311 L 56.604315,293.36258 L 62.096141,278.61311 L 65.155872,278.61311 L 58.565682,296.18695 L 54.642949,296.18695 L 48.052758,278.61311 M 84.173282,286.67824 L 84.173282,288.09043 L 70.898756,288.09043 C 71.024278,290.07795 71.620533,291.59474 72.687522,292.6408 C 73.764958,293.6764 75.260825,294.1942 77.175128,294.1942 C 78.283941,294.1942 79.356154,294.05821 80.391768,293.78624 C 81.437815,293.51426 82.473415,293.1063 83.498572,292.56234 L 83.498572,295.29256 C 82.462955,295.73191 81.401203,296.06665 80.313314,296.29678 C 79.225396,296.52692 78.121802,296.64198 77.002527,296.64198 C 74.199073,296.64198 71.976194,295.82606 70.333882,294.1942 C 68.702023,292.56235 67.886095,290.35516 67.886097,287.57263 C 67.886095,284.69597 68.66018,282.41555 70.208355,280.73138 C 71.766981,279.03678 73.864334,278.18947 76.500418,278.18945 C 78.864505,278.18947 80.731724,278.95309 82.10208,280.48033 C 83.482864,281.99713 84.173264,284.0631 84.173282,286.67824 M 81.286151,285.83093 C 81.265215,284.25139 80.820639,282.99089 79.952422,282.04942 C 79.094638,281.10798 77.954432,280.63725 76.531799,280.63724 C 74.920855,280.63725 73.62897,281.09229 72.65614,282.00235 C 71.693757,282.91243 71.139345,284.19386 70.992901,285.84662 L 81.286151,285.83093 M 99.095354,281.31195 C 98.771062,281.12367 98.415401,280.98768 98.028371,280.90398 C 97.651777,280.80985 97.233352,280.76278 96.773096,280.76276 C 95.14123,280.76278 93.885957,281.29627 93.007273,282.36324 C 92.139035,283.41977 91.70492,284.94179 91.704926,286.9293 L 91.704926,296.18695 L 88.802104,296.18695 L 88.802104,278.61311 L 91.704926,278.61311 L 91.704926,281.34333 C 92.311635,280.27636 93.101411,279.48658 94.074256,278.974 C 95.047085,278.45098 96.229134,278.18947 97.620406,278.18945 C 97.819146,278.18947 98.038819,278.20516 98.279426,278.23652 C 98.520007,278.25746 98.786753,278.29407 99.079663,278.34636 L 99.095354,281.31195 M 119.69755,297.8188 C 118.88161,299.91092 118.0866,301.27603 117.31253,301.91414 C 116.53843,302.55223 115.50283,302.87128 114.20572,302.87128 L 111.89915,302.87128 L 111.89915,300.45488 L 113.59377,300.45488 C 114.38878,300.45488 115.00595,300.26659 115.4453,299.89001 C 115.88464,299.51342 116.37106,298.62427 116.90456,297.22255 L 117.42236,295.90451 L 110.31437,278.61311 L 113.3741,278.61311 L 118.86593,292.35836 L 124.35775,278.61311 L 127.41748,278.61311 L 119.69755,297.8188 M 138.21285,280.63724 C 136.66467,280.63725 135.44078,281.24397 134.54117,282.45738 C 133.64155,283.66037 133.19174,285.31314 133.19175,287.41572 C 133.19174,289.51831 133.63632,291.17632 134.52548,292.38974 C 135.42508,293.59272 136.65421,294.1942 138.21285,294.1942 C 139.75055,294.1942 140.96921,293.58749 141.86883,292.37405 C 142.76843,291.16062 143.21824,289.50785 143.21825,287.41572 C 143.21824,285.33406 142.76843,283.68652 141.86883,282.47307 C 140.96921,281.2492 139.75055,280.63725 138.21285,280.63724 M 138.21285,278.18945 C 140.72338,278.18947 142.69521,279.0054 144.12833,280.63724 C 145.56142,282.26911 146.27797,284.5286 146.27799,287.41572 C 146.27797,290.29239 145.56142,292.55189 144.12833,294.1942 C 142.69521,295.82606 140.72338,296.64198 138.21285,296.64198 C 135.69183,296.64198 133.71478,295.82606 132.28168,294.1942 C 130.85903,292.55189 130.14771,290.29239 130.14771,287.41572 C 130.14771,284.5286 130.85903,282.26911 132.28168,280.63724 C 133.71478,279.0054 135.69183,278.18947 138.21285,278.18945 M 150.74989,289.25156 L 150.74989,278.61311 L 153.63702,278.61311 L 153.63702,289.14172 C 153.63702,290.80496 153.96129,292.05501 154.60986,292.89185 C 155.25841,293.71824 156.23125,294.13144 157.52837,294.13144 C 159.08699,294.13144 160.31612,293.63456 161.21574,292.6408 C 162.1258,291.64704 162.58084,290.29239 162.58085,288.57685 L 162.58085,278.61311 L 165.46798,278.61311 L 165.46798,296.18695 L 162.58085,296.18695 L 162.58085,293.48811 C 161.87998,294.55509 161.06405,295.3501 160.13307,295.87313 C 159.21252,296.3857 158.14031,296.64198 156.91643,296.64198 C 154.89752,296.64198 153.36504,296.01435 152.31898,294.75907 C 151.27292,293.5038 150.74989,291.66796 150.74989,289.25156 M 181.62964,281.31195 C 181.30535,281.12367 180.94969,280.98768 180.56266,280.90398 C 180.18606,280.80985 179.76764,280.76278 179.30738,280.76276 C 177.67552,280.76278 176.42024,281.29627 175.54156,282.36324 C 174.67332,283.41977 174.23921,284.94179 174.23921,286.9293 L 174.23921,296.18695 L 171.33639,296.18695 L 171.33639,278.61311 L 174.23921,278.61311 L 174.23921,281.34333 C 174.84592,280.27636 175.6357,279.48658 176.60854,278.974 C 177.58137,278.45098 178.76342,278.18947 180.15469,278.18945 C 180.35343,278.18947 180.57311,278.20516 180.81371,278.23652 C 181.05429,278.25746 181.32104,278.29407 181.61395,278.34636 L 181.62964,281.31195 M 208.60235,281.98666 C 209.32412,280.68955 210.18712,279.73241 211.19135,279.11522 C 212.19555,278.49806 213.3776,278.18947 214.7375,278.18945 C 216.56809,278.18947 217.98027,278.8328 218.97406,280.11943 C 219.96779,281.39565 220.46467,283.21579 220.46469,285.57988 L 220.46469,296.18695 L 217.56187,296.18695 L 217.56187,285.67402 C 217.56185,283.98988 217.26372,282.73983 216.66749,281.92389 C 216.07121,281.10798 215.16114,280.70001 213.93727,280.7 C 212.44138,280.70001 211.25933,281.19689 210.39112,282.19064 C 209.52287,283.18441 209.08875,284.53906 209.08877,286.25459 L 209.08877,296.18695 L 206.18595,296.18695 L 206.18595,285.67402 C 206.18593,283.97942 205.88781,282.72937 205.29156,281.92389 C 204.6953,281.10798 203.77476,280.70001 202.52996,280.7 C 201.055,280.70001 199.88342,281.20212 199.01519,282.20633 C 198.14695,283.2001 197.71284,284.54952 197.71285,286.25459 L 197.71285,296.18695 L 194.81002,296.18695 L 194.81002,278.61311 L 197.71285,278.61311 L 197.71285,281.34333 C 198.37186,280.2659 199.16163,279.47089 200.08218,278.95831 C 201.0027,278.44575 202.09583,278.18947 203.36158,278.18945 C 204.63776,278.18947 205.72044,278.51375 206.6096,279.16229 C 207.5092,279.81086 208.17345,280.75232 208.60235,281.98666 M 225.94082,289.25156 L 225.94082,278.61311 L 228.82795,278.61311 L 228.82795,289.14172 C 228.82795,290.80496 229.15223,292.05501 229.80079,292.89185 C 230.44934,293.71824 231.42218,294.13144 232.71931,294.13144 C 234.27793,294.13144 235.50705,293.63456 236.40667,292.6408 C 237.31673,291.64704 237.77177,290.29239 237.77179,288.57685 L 237.77179,278.61311 L 240.65892,278.61311 L 240.65892,296.18695 L 237.77179,296.18695 L 237.77179,293.48811 C 237.07091,294.55509 236.25498,295.3501 235.324,295.87313 C 234.40345,296.3857 233.33124,296.64198 232.10736,296.64198 C 230.08845,296.64198 228.55597,296.01435 227.50992,294.75907 C 226.46385,293.5038 225.94082,291.66796 225.94082,289.25156 M 257.8405,279.13091 L 257.8405,281.86113 C 257.02456,281.44272 256.17725,281.1289 255.29857,280.91967 C 254.41987,280.71048 253.50979,280.60587 252.56835,280.60585 C 251.13523,280.60587 250.05779,280.82554 249.33602,281.26487 C 248.62469,281.70423 248.26903,282.36325 248.26903,283.24193 C 248.26903,283.91142 248.52531,284.43968 249.03789,284.82671 C 249.55045,285.20331 250.58082,285.5642 252.129,285.90939 L 253.11753,286.12906 C 255.1678,286.56842 256.62182,287.19082 257.47961,287.99628 C 258.34782,288.7913 258.78194,289.90535 258.78196,291.33845 C 258.78194,292.97031 258.13338,294.2622 256.83628,295.21411 C 255.54961,296.16603 253.77654,296.64198 251.51705,296.64198 C 250.57559,296.64198 249.59229,296.54784 248.56716,296.35955 C 247.55248,296.18172 246.48026,295.90974 245.35052,295.54362 L 245.35052,292.56234 C 246.4175,293.11676 247.46879,293.53518 248.5044,293.81762 C 249.53999,294.0896 250.56513,294.22558 251.57982,294.22558 C 252.93969,294.22558 253.98575,293.99545 254.718,293.53518 C 255.45024,293.06446 255.81636,292.40544 255.81637,291.55812 C 255.81636,290.77358 255.54961,290.1721 255.01613,289.75367 C 254.49309,289.33525 253.33719,288.93251 251.54844,288.54546 L 250.54422,288.3101 C 248.75545,287.93353 247.46356,287.35819 246.66856,286.5841 C 245.87355,285.79956 245.47604,284.72735 245.47605,283.36746 C 245.47604,281.71469 246.06184,280.4385 247.23343,279.53887 C 248.40502,278.63927 250.06825,278.18947 252.22315,278.18945 C 253.29012,278.18947 254.29434,278.26792 255.2358,278.42481 C 256.17725,278.58174 257.04548,278.81711 257.8405,279.13091 M 263.39508,278.61311 L 266.28221,278.61311 L 266.28221,296.18695 L 263.39508,296.18695 L 263.39508,278.61311 M 263.39508,271.77186 L 266.28221,271.77186 L 266.28221,275.42785 L 263.39508,275.42785 L 263.39508,271.77186 M 284.95441,279.28782 L 284.95441,281.98666 C 284.13847,281.53686 283.31731,281.20212 282.49094,280.98244 C 281.675,280.75232 280.84861,280.63725 280.01177,280.63724 C 278.13931,280.63725 276.68529,281.23351 275.64969,282.426 C 274.61409,283.60806 274.09629,285.2713 274.09629,287.41572 C 274.09629,289.56015 274.61409,291.22862 275.64969,292.42112 C 276.68529,293.60318 278.13931,294.1942 280.01177,294.1942 C 280.84861,294.1942 281.675,294.08436 282.49094,293.86469 C 283.31731,293.63456 284.13847,293.29459 284.95441,292.84478 L 284.95441,295.51224 C 284.14893,295.88882 283.31208,296.17126 282.44387,296.35955 C 281.58608,296.54784 280.67078,296.64198 279.69795,296.64198 C 277.05141,296.64198 274.94883,295.81037 273.3902,294.14713 C 271.83156,292.48389 271.05225,290.24009 271.05225,287.41572 C 271.05225,284.54952 271.83679,282.29526 273.40589,280.65293 C 274.98544,279.01063 277.14555,278.18947 279.88624,278.18945 C 280.77538,278.18947 281.64362,278.28361 282.49094,278.47189 C 283.33823,278.64974 284.15939,278.92171 284.95441,279.28782" + style="font-size:32.13502502px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#0057ae;fill-opacity:1;font-family:DejaVu Sans" + transform="matrix(1.223374,0,0,1.223374,59.51252,-116.39885)" /><path + inkscape:export-ydpi="42.189999" + inkscape:export-xdpi="42.189999" + inkscape:export-filename="/home/knome/Work/Amarok logo/oxygen_logo.png" + style="fill:url(#radialGradient5154);fill-opacity:1;stroke:#0057ae;stroke-width:17.74128838;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="M 607.62801,152.20455 C 607.62801,208.15234 562.2211,253.55926 506.2733,253.55926 C 450.3255,253.55926 404.91859,208.15234 404.91859,152.20455 C 404.91859,96.256742 450.3255,50.849834 506.2733,50.849834 C 562.2211,50.849834 607.62801,96.256742 607.62801,152.20455 z " + id="path3013" /><path + inkscape:export-ydpi="42.189999" + inkscape:export-xdpi="42.189999" + inkscape:export-filename="/home/knome/Work/Amarok logo/oxygen_logo.png" + transform="matrix(0.2762093,0,0,0.2762093,391.19099,37.693155)" + style="opacity:0.86000001;fill:#eeeeee" + mask="url(#XMLID_23_)" + d="M 699.57098,390.89499 C 699.57098,553.04278 570.4108,684.64099 411.267,684.64099 C 252.1232,684.64099 122.96301,553.04278 122.96301,390.89499 C 122.96301,228.7472 252.1232,97.148987 411.267,97.148987 C 570.4108,97.148987 699.57098,228.7472 699.57098,390.89499 z " + id="path3015" /><path + inkscape:export-ydpi="42.189999" + inkscape:export-xdpi="42.189999" + inkscape:export-filename="/home/knome/Work/Amarok logo/oxygen_logo.png" + style="opacity:0.22000002;fill:#323232" + d="M 562.11729,102.05295 C 557.89957,102.05295 549.70969,110.84911 546.64101,112.2379 C 543.57094,113.62667 519.27116,129.36535 516.96868,132.14291 C 514.6673,134.9202 511.59779,136.07779 508.27278,135.84633 C 504.94805,135.61486 494.43608,135.51985 490.85558,137.37155 C 487.27425,139.22326 470.92874,154.36284 466.83587,156.21483 C 462.74355,158.06598 457.40415,159.01504 454.84645,159.24622 C 452.28875,159.47796 443.81521,168.01918 441.513,169.63998 C 439.21107,171.25939 422.38109,177.74865 419.82367,179.13688 C 417.2657,180.52594 434.86299,202.50639 437.9325,202.96932 C 441.00146,203.43252 434.9174,205.70186 429.01205,205.70186 C 428.36185,205.70186 437.45162,229.53816 468.44451,244.92274 C 495.23819,258.22222 516.82504,250.14586 527.38287,246.92029 C 534.84825,244.63963 536.20196,243.62263 536.45855,239.91921 C 536.7146,236.21607 537.17698,233.28964 537.68879,229.12357 C 538.20061,224.95806 539.98989,190.00709 546.64128,180.74911 C 553.29074,171.49085 572.29919,160.37675 578.9492,159.21998 C 585.59977,158.06267 584.3212,155.74831 584.3212,153.66514 C 584.3212,151.58197 583.29784,146.9527 581.50745,146.25831 C 579.71679,145.5642 572.73035,149.73385 570.42843,150.19678 C 568.12622,150.65971 557.12785,151.3541 555.59295,149.03974 C 554.05833,146.72483 568.0011,123.11584 569.79149,120.33856 C 571.58132,117.561 572.34891,113.85759 569.02445,109.92298 C 565.69806,105.98755 563.39586,102.05295 562.11729,102.05295 z " + id="path3017" /><path + inkscape:export-ydpi="42.189999" + inkscape:export-xdpi="42.189999" + inkscape:export-filename="/home/knome/Work/Amarok logo/oxygen_logo.png" + style="fill:url(#radialGradient5149);fill-opacity:1" + d="M 553.14187,89.807766 C 549.05038,89.807766 541.10688,99.236444 538.12962,100.72494 C 535.15236,102.2137 511.58204,119.08429 509.34917,122.06183 C 507.11629,125.03881 504.13876,126.27954 500.91374,126.03151 C 497.68872,125.78375 487.49273,125.68155 484.0194,127.66639 C 480.54551,129.65151 464.69082,145.87991 460.72115,147.86502 C 456.75174,149.84931 451.57282,150.86604 449.09163,151.11435 C 446.611,151.36211 438.39184,160.51761 436.15896,162.25469 C 433.92609,163.99177 416.28184,171.62675 413.80093,173.11469 C 411.31974,174.60346 431.9012,195.45256 434.87846,195.94835 C 437.85572,196.44526 432.82788,207.74166 427.09958,207.74166 C 426.46871,207.74166 432.21856,226.46064 462.2809,242.95171 C 488.27026,257.20825 515.84506,250.89604 526.08551,247.43901 C 533.32717,244.99345 528.24464,243.64196 528.49323,239.67256 C 528.74072,235.7026 528.94925,230.4828 529.4456,226.01732 C 529.94223,221.55157 531.6782,184.08738 538.12934,174.16346 C 544.57966,164.24008 563.01663,152.32663 569.46749,151.08617 C 575.91864,149.84571 574.67763,147.36453 574.67763,145.13193 C 574.67763,142.89933 573.68548,137.93696 571.9484,137.19257 C 570.21187,136.44846 563.43536,140.91808 561.20248,141.41415 C 558.96961,141.91023 548.30102,142.65488 546.81198,140.17397 C 545.32349,137.69251 558.84752,112.38594 560.58433,109.40868 C 562.32141,106.43114 563.06524,102.46119 559.83967,98.244025 C 556.61548,94.025481 554.38232,89.807766 553.14187,89.807766 z " + id="path3019" /><path + inkscape:export-ydpi="42.189999" + inkscape:export-xdpi="42.189999" + inkscape:export-filename="/home/knome/Work/Amarok logo/oxygen_logo.png" + style="fill:#ffffff" + d="M 503.65097,136.57607 C 503.65097,136.57607 493.82179,135.06355 488.15038,140.73523 C 482.4787,146.40664 477.56273,158.88328 477.94196,162.66431 C 478.31954,166.44534 489.66263,149.80925 494.57722,148.67542 C 499.49291,147.54075 504.78591,139.22215 503.65097,136.57607 z " + id="path3021" /><path + inkscape:export-ydpi="42.189999" + inkscape:export-xdpi="42.189999" + inkscape:export-filename="/home/knome/Work/Amarok logo/oxygen_logo.png" + style="opacity:0.18000004;fill:url(#linearGradient5145)" + d="M 528.47058,188.76443 C 528.47058,188.76443 519.67498,218.70662 519.45263,219.16927 C 518.1956,221.78165 515.8771,229.35172 516.67534,231.79645 C 517.61528,234.67648 521.13833,238.86382 522.7826,240.95748 C 524.42688,243.05198 525.13149,244.09881 524.66165,244.88463 C 524.1921,245.66989 518.08484,245.66989 518.08484,245.66989 C 518.08484,245.66989 513.38735,238.60225 512.44713,235.98461 C 511.50802,233.36753 511.03791,227.87069 511.97785,226.03887 C 512.91724,224.20622 528.47058,188.76443 528.47058,188.76443 z " + id="path3023" /><path + inkscape:export-ydpi="42.189999" + inkscape:export-xdpi="42.189999" + inkscape:export-filename="/home/knome/Work/Amarok logo/oxygen_logo.png" + style="opacity:0.2;fill:url(#linearGradient5142)" + d="M 466.47597,194.34247 L 450.40749,208.25265 L 470.55309,196.26103 L 466.47597,194.34247 z " + id="path3025" /><path + inkscape:export-ydpi="42.189999" + inkscape:export-xdpi="42.189999" + inkscape:export-filename="/home/knome/Work/Amarok logo/oxygen_logo.png" + style="opacity:0.2;fill:url(#linearGradient5139);fill-opacity:1" + d="M 482.30496,189.54638 L 454.96412,225.04092 L 486.38181,191.94415 L 482.30496,189.54638 z " + id="path3027" /><path + inkscape:export-ydpi="42.189999" + inkscape:export-xdpi="42.189999" + inkscape:export-filename="/home/knome/Work/Amarok logo/oxygen_logo.png" + style="opacity:0.20132015;fill:url(#linearGradient5136);fill-opacity:1" + d="M 484.46354,201.77748 L 463.59814,234.87397 L 489.25991,203.93578 L 484.46354,201.77748 z " + id="path3029" /><path + inkscape:export-ydpi="42.189999" + inkscape:export-xdpi="42.189999" + inkscape:export-filename="/home/knome/Work/Amarok logo/oxygen_logo.png" + style="opacity:0.2;fill:url(#linearGradient5133);fill-opacity:1" + d="M 496.69464,200.33815 L 479.18684,241.3497 L 502.45056,201.53773 L 496.69464,200.33815 z " + id="path3031" /><path + inkscape:export-ydpi="42.189999" + inkscape:export-xdpi="42.189999" + inkscape:export-filename="/home/knome/Work/Amarok logo/oxygen_logo.png" + style="opacity:0.2;fill:url(#linearGradient5130);fill-opacity:1" + d="M 503.64987,211.13075 L 502.21054,248.5444 L 508.44596,211.61053 L 503.64987,211.13075 z " + id="path3033" /><path + inkscape:export-ydpi="42.189999" + inkscape:export-xdpi="42.189999" + inkscape:export-filename="/home/knome/Work/Amarok logo/oxygen_logo.png" + style="opacity:0.57999998;fill:url(#linearGradient5127);fill-opacity:1" + d="M 504.75152,226.37063 C 487.66872,218.77051 472.26108,208.34067 458.95034,195.36657 C 457.45601,193.91003 457.12215,193.52136 457.12215,193.23822 C 457.12215,192.94097 457.54836,192.5176 460.12608,190.25432 C 465.37525,185.64546 466.53012,184.41533 467.42476,182.47999 C 468.33817,180.50405 470.22561,178.17477 473.90701,174.48029 C 482.03457,166.32382 496.78839,154.1347 503.7478,149.82679 C 504.9573,149.0781 505.65242,148.53693 506.60915,147.59912 C 509.6447,144.62359 514.3464,138.61104 524.14198,125.17801 C 534.56912,110.87891 538.07927,106.25712 541.54978,102.25728 C 543.6469,99.840302 545.18902,98.405539 545.78707,98.314991 C 546.04121,98.276513 546.46327,98.208329 546.72499,98.163469 C 546.98671,98.118604 547.32116,98.139281 547.46823,98.209418 C 547.6153,98.279551 547.83047,98.367066 547.94639,98.403902 C 548.16857,98.474501 552.40053,103.26294 553.95918,105.20732 C 555.82616,107.53636 555.99429,108.18489 555.06007,109.45379 C 554.7114,109.92737 554.37557,110.60842 554.1171,111.36612 C 553.67312,112.66761 551.38577,117.29117 547.8663,124.00122 C 545.09922,129.27681 541.35391,136.73457 540.18341,139.2996 C 537.71251,144.71434 536.50731,148.4444 536.82268,149.70097 C 536.92593,150.11234 537.55004,150.40662 539.22642,150.83442 C 543.14777,151.8351 546.77302,151.68358 550.15116,150.37784 C 550.66214,150.18034 551.86062,149.5683 552.81444,149.01778 C 555.72634,147.33708 557.92134,146.39751 560.66217,145.65857 C 561.97151,145.30557 562.28275,145.26848 564.02724,145.25756 C 565.85748,145.2461 565.97901,145.26068 566.56565,145.56205 C 567.0982,145.83563 567.1958,145.94488 567.28496,146.36723 C 567.48586,147.31885 567.5211,147.93071 567.38297,148.06885 C 567.30747,148.14434 567.24571,148.32977 567.24571,148.48091 C 567.24571,149.10089 565.75469,149.60186 561.59798,150.37849 C 557.23774,151.19314 557.02723,151.28091 553.99288,153.54938 C 547.20053,158.62731 536.63232,167.82619 532.88331,171.92374 C 531.18252,173.78265 528.57528,177.38026 525.06105,182.71727 C 517.17638,194.69164 511.71417,205.03252 510.25496,210.74765 C 509.96501,211.88326 509.14933,217.01271 508.40886,222.35681 C 507.72543,227.28934 507.69494,227.43782 507.36771,227.42667 C 507.22328,227.42176 506.046,226.94653 504.75152,226.37063 z " + id="path3035" /><path + inkscape:export-ydpi="42.189999" + inkscape:export-xdpi="42.189999" + inkscape:export-filename="/home/knome/Work/Amarok logo/oxygen_logo.png" + style="fill:url(#linearGradient5124);fill-opacity:1" + d="M 458.55053,159.71501 C 458.47471,159.44334 458.49004,157.05631 458.57477,155.9391 C 458.63539,155.13978 458.68605,154.87564 458.89917,154.2476 C 459.55794,152.30629 460.52112,151.22561 461.91437,150.86458 C 462.43405,150.72992 462.71607,150.59187 463.46757,150.10428 C 465.0112,149.10273 466.9814,147.43992 470.13451,144.47751 C 470.99009,143.67368 474.46106,140.25971 477.84778,136.89093 C 481.23451,133.52215 484.15769,130.65563 484.34376,130.5209 C 484.52982,130.38617 484.80385,130.2437 484.9527,130.20432 C 486.04015,129.91655 488.66999,129.77705 495.33856,129.65338 C 501.60455,129.53719 504.27299,129.41647 505.09594,129.21197 C 505.94828,129.00016 507.89319,127.8484 511.81383,125.23367 C 514.64865,123.3431 520.68185,119.17152 524.17803,116.68462 C 525.1162,116.01727 525.49787,115.78273 525.64565,115.78273 C 525.75549,115.78273 525.86418,115.75228 525.88717,115.71506 C 525.91017,115.67785 525.97556,115.6474 526.0325,115.6474 C 526.11763,115.6474 526.12953,115.71649 526.09958,116.03645 C 526.02272,116.85729 525.97421,117.026 525.71615,117.37019 C 525.01305,118.30794 523.16108,119.82148 520.97783,121.24261 C 520.41731,121.60746 519.32878,122.39421 518.55885,122.99094 C 510.79402,129.00908 505.51895,132.57132 503.23821,133.33694 C 502.786,133.48874 502.61099,133.50972 501.79687,133.50972 C 500.22664,133.50972 498.42309,133.85006 495.34562,134.72713 C 490.80981,136.01983 486.63749,137.71525 484.44525,139.15651 C 482.54162,140.40803 481.01203,142.8102 479.47523,146.96174 C 478.8706,148.59513 478.15755,151.00949 477.29858,154.33192 C 477.08455,155.15977 476.88879,155.8705 476.86357,155.91131 C 476.83835,155.95211 472.77179,156.85132 467.82677,157.90955 C 462.88175,158.96778 458.78356,159.84965 458.71968,159.86926 C 458.63556,159.89509 458.58891,159.85254 458.55053,159.71501 z " + id="path3037" /><path + inkscape:export-ydpi="42.189999" + inkscape:export-xdpi="42.189999" + inkscape:export-filename="/home/knome/Work/Amarok logo/oxygen_logo.png" + style="opacity:0.7;fill:url(#linearGradient5121);fill-opacity:1" + d="M 435.26135,189.83164 C 435.16743,189.81073 434.982,189.79251 434.8493,189.79116 C 434.26798,189.78523 432.61789,188.95172 430.84258,187.76724 C 428.9169,186.48243 427.36914,185.25421 425.61448,183.61852 C 425.26124,183.28923 424.6458,182.72248 424.24682,182.35906 C 423.17537,181.3831 421.5223,179.72231 421.03636,179.1336 C 420.14808,178.05748 419.67259,177.26546 419.60721,176.75309 C 419.50781,175.97409 419.51127,175.79044 419.63802,175.12035 C 419.66675,174.96842 420.04237,174.64165 420.55347,174.32394 C 421.50997,173.72936 423.54487,172.34925 430.7145,167.43253 C 441.62316,159.95169 446.21899,156.91024 448.6423,155.56819 C 449.25626,155.22818 449.4254,155.19025 450.33213,155.18928 C 451.20103,155.18835 451.93222,155.26637 452.45177,155.41544 C 453.49059,155.7135 455.0964,156.34115 456.51229,157.00252 C 457.21907,157.33268 457.26981,157.36408 457.26981,157.47149 C 457.26981,157.57517 457.24543,157.59346 457.02433,157.65557 C 456.51603,157.79838 455.15926,158.24509 454.04646,158.63602 C 447.28556,161.01113 440.52061,164.28686 434.47153,168.11459 C 433.03786,169.02178 432.87141,169.14373 432.33595,169.67919 C 430.81494,171.20019 429.44563,173.57681 429.06387,175.35832 C 429.01057,175.60708 428.986,175.90249 428.98577,176.29758 C 428.98539,176.92757 429.06074,177.25714 429.29755,177.66121 C 430.23362,179.2585 434.98574,180.76681 441.13215,181.41746 C 442.92537,181.60729 443.75128,181.65119 445.57182,181.65343 L 447.34359,181.6556 L 447.81322,181.49339 C 449.06889,181.05965 450.7871,180.07808 454.51609,177.66418 C 457.74735,175.57247 458.7447,174.96312 459.89546,174.37753 C 460.97295,173.82923 461.51736,173.66632 461.98128,173.75335 C 462.1579,173.78648 462.1904,173.88573 462.19234,174.39772 C 462.19514,175.13809 462.11429,175.41995 461.73921,175.97738 C 460.65551,177.5879 457.98854,179.77667 454.23694,182.13447 C 451.0841,184.11596 447.49325,185.95326 445.58087,186.56345 C 445.39978,186.62123 444.91406,186.74572 444.50149,186.84008 C 442.29674,187.34434 440.0946,188.12955 437.18256,189.44978 C 436.77029,189.63669 436.40636,189.77311 436.26465,189.79388 C 435.84681,189.8551 435.43618,189.87055 435.26135,189.83164 z " + id="path3039" /><path + inkscape:export-ydpi="42.189999" + inkscape:export-xdpi="42.189999" + inkscape:export-filename="/home/knome/Work/Amarok logo/oxygen_logo.png" + style="fill:url(#linearGradient5118);fill-opacity:1" + d="M 442.84682,177.43288 C 442.80531,177.42744 442.26749,177.41246 441.65168,177.3996 C 440.54285,177.37644 439.84862,177.3287 438.85881,177.20752 C 436.56304,176.92647 435.03281,176.47405 434.49616,175.91769 C 434.38234,175.79968 434.34478,175.65965 434.34658,175.36004 C 434.34893,174.96989 434.38482,174.81772 434.55164,174.49052 C 435.25884,173.1034 437.29324,170.90685 439.68912,168.94361 C 440.2148,168.51286 441.08661,167.85853 441.53911,167.55513 L 441.86554,167.33625 L 449.27545,166.43992 L 456.68537,165.5436 L 457.17601,165.5716 C 457.63218,165.59765 458.02257,165.62651 459.01649,165.70768 C 459.47491,165.74512 459.51049,165.76172 459.46712,165.9179 C 459.45263,165.97006 459.44072,166.06016 459.44064,166.11812 C 459.44044,166.26677 459.20703,166.81295 458.88231,167.42464 C 457.13039,170.72476 453.59204,174.88147 451.23801,176.40486 C 450.56328,176.8415 450.08974,177.04806 449.48139,177.17109 C 448.86083,177.29659 448.06828,177.37622 447.18709,177.40159 C 446.32121,177.42653 442.98191,177.4506 442.84682,177.43288 z " + id="path3041" /></g></svg> \ No newline at end of file diff --git a/amarok/src/images/amarok_rocks.jpg b/amarok/src/images/amarok_rocks.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e8b7db9fbb51f967c469229856e2343b91a64bc4 GIT binary patch literal 15945 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ëz<@*?ij3 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 literal 0 HcmV?d00001 diff --git a/amarok/src/images/amarok_rocks.xcf b/amarok/src/images/amarok_rocks.xcf new file mode 100644 index 0000000000000000000000000000000000000000..a370deb5a0add39ed7fd44dbeebbeb15842ecbda GIT binary patch literal 119195 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
    \n" + "\n" + "\n" + "\n" + "\n" + "
    \n" + "\n" + "\n" + "\n" + "
    \n" + ) + .args( QStringList() + << escapeHTML( currentTrack.title() ) + << escapeHTML( currentTrack.artist() ) + << escapeHTML( currentTrack.album() ) + << ( isCompilation ? "" : escapeHTMLAttr( currentTrack.artist() ) ) + << escapeHTMLAttr( currentTrack.album() ) + << escapeHTMLAttr( albumImage ) + << albumImageTitleAttr + << i18n( "Look up this track at musicbrainz.org" ) + << escapeHTMLAttr( currentTrack.artist() ) + << escapeHTMLAttr( currentTrack.album() ) + << escapeHTMLAttr( currentTrack.title() ) + << escapeHTML( m_musicBrainIconPath ) ) + : QString ( //no title + "%1 " + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "
    \n" + "\n" + "\n" + "\n" + "\n" + ) + .arg( escapeHTML( currentTrack.prettyTitle() ) ) + .arg( escapeHTMLAttr( currentTrack.artist() ) ) + .arg( escapeHTMLAttr( currentTrack.album() ) ) + .arg( escapeHTMLAttr( albumImage ) ) + .arg( albumImageTitleAttr ) + ) ); + + if ( !values.isEmpty() && values[2].toInt() ) + { + QDateTime firstPlay = QDateTime(); + firstPlay.setTime_t( values[0].toUInt() ); + QDateTime lastPlay = QDateTime(); + lastPlay.setTime_t( values[1].toUInt() ); + + const uint playtimes = values[2].toInt(); + const uint score = static_cast( values[3].toFloat() ); + const uint rating = values[4].toInt(); + + //SAFE = .arg( x, y ) + //UNSAFE = .arg( x ).arg( y ) + m_HTMLSource.append( QString( + "%1
    \n" + "
    %2
    \n" + "%3
    \n" + "%4\n" + ) + .arg( i18n( "Track played once", "Track played %n times", playtimes ), + statsHTML( score, rating, false ), + i18n( "Last played: %1" ).arg( Amarok::verboseTimeSince( lastPlay ) ), + i18n( "First played: %1" ).arg( Amarok::verboseTimeSince( firstPlay ) ) ) ); + } + else + m_HTMLSource.append( i18n( "Never played before" ) ); + + m_HTMLSource.append( + "
    \n" + "\n" ); + // + + if ( currentTrack.url().isLocalFile() && !CollectionDB::instance()->isFileInCollection( currentTrack.url().path() ) ) + { + m_HTMLSource.append( + "
    \n" + "
    \n" + "\n" + + i18n( "This file is not in your Collection!" ) + + "\n" + "
    \n" + "
    \n" + "

    \n" + + i18n( "If you would like to see contextual information about this track," + " you should add it to your Collection." ) + + "

    \n" + "
    \n" + "

    \n" + "
    \n" + "
    \n" + ); + } + + /* cue file code */ + if ( b->m_cuefile && (b->m_cuefile->count() > 0) ) { + m_HTMLSource.append( + "
    \n" + "
    \n" + "\n" + + i18n( "Cue File" ) + + "\n" + "
    \n" + "\n" ); + CueFile::Iterator it; + uint i = 0; + for ( it = b->m_cuefile->begin(); it != b->m_cuefile->end(); ++it ) { + m_HTMLSource.append( + "\n" + "\n" + "" + ); + } + m_HTMLSource.append( + "
    \n" + "\n" + "\n" + QString::number(it.data().getTrackNumber()) + " \n" + "\n" + escapeHTML( it.data().getTitle() ) + "\n" + "\n" + + i18n(" – ") + + "\n" + "\n" + escapeHTML( it.data().getArtist() ) + "\n" + " (" + MetaBundle::prettyTime( it.data().getLength()/1000, false ) + ")\n" + "\n" + "
    \n" + "
    \n" + ); + } +} + + +void CurrentTrackJob::showRelatedArtists( const QString &artist, const QStringList &relArtists ) +{ + // + m_HTMLSource.append( QString( + "\n" ); + + if ( !b->m_relatedOpen ) + m_HTMLSource.append( "\n" ); + // +} + +void CurrentTrackJob::showSuggestedSongs( const QStringList &relArtists ) +{ + QString token; + + QueryBuilder qb; + QStringList values; + qb.clear(); + qb.addReturnValue( QueryBuilder::tabSong, QueryBuilder::valURL ); + qb.addReturnValue( QueryBuilder::tabSong, QueryBuilder::valTitle ); + qb.addReturnValue( QueryBuilder::tabArtist, QueryBuilder::valName ); + qb.addReturnValue( QueryBuilder::tabStats, QueryBuilder::valScore ); + qb.addReturnValue( QueryBuilder::tabStats, QueryBuilder::valRating ); + qb.addMatches( QueryBuilder::tabArtist, relArtists ); + qb.sortByFavorite(); + qb.setLimit( 0, 10 ); + values = qb.run(); + + // + if ( !values.isEmpty() ) + { + m_HTMLSource.append( + "
    \n" + "
    \n" + "\n" + + i18n( "Suggested Songs" ) + + "\n" + "
    \n" + "\n" ); + + for ( uint i = 0; i < values.count(); i += 5 ) + m_HTMLSource.append( + "\n" + "\n" + "\n" + "\n" + "\n" ); + + m_HTMLSource.append( + "
    \n" + "\n" + "\n"+ escapeHTML( values[i + 2] ) + "\n" + "\n" + + i18n(" – ") + + "\n" + escapeHTML( values[i + 1] ) + "\n" + "\n" + "\n" + statsHTML( static_cast( values[i + 3].toFloat() ), values[i + 4].toInt() ) + "
    \n" + "
    \n" ); + + if ( !b->m_suggestionsOpen ) + m_HTMLSource.append( "\n" ); + } + //
    +} + +void +CurrentTrackJob::showSongsWithLabel( const QString &label ) +{ + QueryBuilder qb; + QStringList values; + qb.addReturnValue( QueryBuilder::tabSong, QueryBuilder::valURL ); + qb.addReturnValue( QueryBuilder::tabSong, QueryBuilder::valTitle ); + qb.addReturnValue( QueryBuilder::tabArtist, QueryBuilder::valName ); + qb.addReturnValue( QueryBuilder::tabStats, QueryBuilder::valScore ); + qb.addReturnValue( QueryBuilder::tabStats, QueryBuilder::valRating ); + qb.addMatch( QueryBuilder::tabLabels, QueryBuilder::valType, QString::number( CollectionDB::typeUser ) ); + qb.addMatch( QueryBuilder::tabLabels, QueryBuilder::valName, label ); + qb.sortByFavorite(); + qb.setOptions( QueryBuilder::optRandomize ); + qb.setLimit( 0, 30 ); + values = qb.run(); + + if ( !values.isEmpty() ) + { + m_HTMLSource.append( + "
    \n" + "
    \n" + "\n" + + i18n( "Songs with label %1" ).arg( label ) + + "\n" + "
    \n" + "\n" ); + + for ( uint i = 0; i < values.count(); i += 5 ) + m_HTMLSource.append( + "\n" + "\n" + "\n" + "\n" + "\n" ); + + m_HTMLSource.append( + "
    \n" + "\n" + "\n"+ escapeHTML( values[i + 2] ) + "\n" + "\n" + + i18n(" – ") + + "\n" + escapeHTML( values[i + 1] ) + "\n" + "\n" + "\n" + statsHTML( static_cast( values[i + 3].toFloat() ), values[i + 4].toInt() ) + "
    \n" + "
    \n" ); + } +} + +void +CurrentTrackJob::showUserLabels( const MetaBundle ¤tTrack ) +{ + QueryBuilder qb; + qb.addReturnValue( QueryBuilder::tabLabels, QueryBuilder::valName, true ); + qb.addMatch( QueryBuilder::tabSong, QueryBuilder::valURL, currentTrack.url().path() ); + qb.addMatch( QueryBuilder::tabLabels, QueryBuilder::valType, QString::number( CollectionDB::typeUser ) ); + qb.setLimit( 0, 10 ); + qb.sortBy( QueryBuilder::tabLabels, QueryBuilder::valName, false ); + qb.buildQuery(); + QStringList values = qb.run(); + + QString title; + if ( currentTrack.title().isEmpty() ) + title = currentTrack.veryNiceTitle(); + else + title = currentTrack.title(); + + m_HTMLSource.append( + "
    \n" + "
    \n" + "\n" + + i18n( " Labels for %1 " ).arg( escapeHTML( title ) ) + + "\n" + "
    \n" + "\n" ); + m_HTMLSource.append( "\n" ); + m_HTMLSource.append( "\n" ); + m_HTMLSource.append( + "
    \n" ); + if ( !values.isEmpty() ) + { + foreach( values ) + { + if( it != values.begin() ) + m_HTMLSource.append( ", \n" ); + m_HTMLSource.append( "" + escapeHTML( *it ) + "" ); + } + } + m_HTMLSource.append( "
    " + i18n( "Add labels to %1" ).arg( escapeHTML( title ) ) + "
    \n" + "
    \n" ); + + if ( !b->m_labelsOpen ) + m_HTMLSource.append( "\n" ); +} + +void CurrentTrackJob::showArtistsFaves( const QString &artist, uint artist_id ) +{ + QString artistName = artist.isEmpty() ? escapeHTML( i18n( "This Artist" ) ) : escapeHTML( artist ); + QueryBuilder qb; + QStringList values; + + qb.clear(); + qb.addReturnValue( QueryBuilder::tabSong, QueryBuilder::valTitle ); + qb.addReturnValue( QueryBuilder::tabSong, QueryBuilder::valURL ); + qb.addReturnValue( QueryBuilder::tabStats, QueryBuilder::valScore ); + qb.addReturnValue( QueryBuilder::tabStats, QueryBuilder::valRating ); + qb.addNumericFilter( QueryBuilder::tabStats, QueryBuilder::valPlayCounter, "0", QueryBuilder::modeGreater ); + qb.addMatch( QueryBuilder::tabSong, QueryBuilder::valArtistID, QString::number( artist_id ) ); + qb.sortByFavorite(); + qb.setLimit( 0, 10 ); + values = qb.run(); + usleep( 10000 ); + + if ( !values.isEmpty() ) + { + m_HTMLSource.append( + "
    \n" + "
    \n" + "\n" + + i18n( "Favorite Tracks by %1" ).arg( artistName ) + + "\n" + "
    \n" + "\n" ); + + for ( uint i = 0; i < values.count(); i += 4 ) + m_HTMLSource.append( + "\n" + "\n" + "\n" + "\n" + "\n" + ); + + m_HTMLSource.append( + "
    \n" + "\n" + "\n" + escapeHTML( values[i] ) + "\n" + "\n" + "\n" + statsHTML( static_cast( values[i + 2].toFloat() ), values[i + 3].toInt() ) + "
    \n" + "
    \n" ); + + if ( !b->m_favoritesOpen ) + m_HTMLSource.append( "\n" ); + + } +} + +void CurrentTrackJob::showArtistsAlbums( const QString &artist, uint artist_id, uint album_id ) +{ + DEBUG_BLOCK + QString artistName = artist.isEmpty() ? escapeHTML( i18n( "This Artist" ) ) : escapeHTML( artist ); + QueryBuilder qb; + QStringList values; + // + qb.clear(); + qb.addReturnValue( QueryBuilder::tabAlbum, QueryBuilder::valName ); + qb.addReturnValue( QueryBuilder::tabAlbum, QueryBuilder::valID ); + qb.addReturnFunctionValue( QueryBuilder::funcMax, QueryBuilder::tabYear, QueryBuilder::valName ); + qb.addMatch( QueryBuilder::tabSong, QueryBuilder::valArtistID, QString::number( artist_id ) ); + qb.groupBy( QueryBuilder::tabAlbum, QueryBuilder::valName ); + qb.groupBy( QueryBuilder::tabAlbum, QueryBuilder::valID ); + qb.sortByFunction( QueryBuilder::funcMax, QueryBuilder::tabYear, QueryBuilder::valName, true ); + qb.sortBy( QueryBuilder::tabAlbum, QueryBuilder::valName, true ); + qb.setOptions( QueryBuilder::optNoCompilations ); + values = qb.run(); + + if ( !values.isEmpty() ) + { + // write the script to toggle blocks visibility + m_HTMLSource.append( + "
    \n" + "
    \n" + "\n" + + i18n( "Albums by %1" ).arg( artistName ) + + "\n" + "
    \n" + "\n" ); + + uint vectorPlace = 0; + // find album of the current track (if it exists) + while ( vectorPlace < values.count() && values[ vectorPlace+1 ] != QString::number( album_id ) ) + vectorPlace += 3; + for ( uint i = 0; i < values.count(); i += 3 ) + { + qb.clear(); + qb.addReturnValue( QueryBuilder::tabSong, QueryBuilder::valTitle ); + qb.addReturnValue( QueryBuilder::tabSong, QueryBuilder::valURL ); + qb.addReturnValue( QueryBuilder::tabSong, QueryBuilder::valTrack ); + qb.addReturnValue( QueryBuilder::tabYear, QueryBuilder::valName ); + qb.addReturnValue( QueryBuilder::tabSong, QueryBuilder::valLength ); + qb.addReturnValue( QueryBuilder::tabSong, QueryBuilder::valDiscNumber ); + qb.addMatch( QueryBuilder::tabSong, QueryBuilder::valAlbumID, values[ i + 1 ] ); + qb.addMatch( QueryBuilder::tabSong, QueryBuilder::valArtistID, QString::number( artist_id ) ); + qb.sortBy( QueryBuilder::tabSong, QueryBuilder::valDiscNumber ); + qb.sortBy( QueryBuilder::tabSong, QueryBuilder::valTrack ); + qb.sortBy( QueryBuilder::tabSong, QueryBuilder::valTitle ); + qb.setOptions( QueryBuilder::optNoCompilations ); + QStringList albumValues = qb.run(); + usleep( 10000 ); + + QString albumYear; + if ( !albumValues.isEmpty() ) + { + albumYear = albumValues[ 3 ]; + for ( uint j = 0; j < albumValues.count(); j += qb.countReturnValues() ) + if ( albumValues[j + 3] != albumYear || albumYear == "0" ) + { + albumYear = QString::null; + break; + } + } + + uint i_albumLength = 0; + for ( uint j = 0; j < albumValues.count(); j += qb.countReturnValues() ) + i_albumLength += QString(albumValues[j + 4]).toInt(); + + QString albumLength = ( i_albumLength==0 ? i18n( "Unknown" ) : MetaBundle::prettyTime( i_albumLength, true ) ); + QString albumImage = ContextBrowser::getEncodedImage( CollectionDB::instance()->albumImage( artist, values[ i ], true, 50 ) ); + QString albumImageTitleAttr = albumImageTooltip( albumImage, 50 ); + + m_HTMLSource.append( QStringx ( + "\n" + "\n" + "\n" ); + } + m_HTMLSource.append( + "
    \n" + "
    \n" + "\n" + "\n" + "\n" + "\n" + "\n" + "
    \n" + "\n" + "\n" + "\n" + "\n" + "%6 " + "%9\n" + "
    \n" + "%10\n" + "%11\n" + "
    \n" + "
    \n" + "
    \n" ) + .args( QStringList() + << values[ i + 1 ] + << escapeHTMLAttr( artist ) // artist name + << escapeHTMLAttr( values[ i ].isEmpty() ? i18n( "Unknown" ) : values[ i ] ) // album.name + << albumImageTitleAttr + << escapeHTMLAttr( albumImage ) + << i18n( "Single", "%n Tracks", albumValues.count() / qb.countReturnValues() ) + << QString::number( artist_id ) + << values[ i + 1 ] //album.id + << escapeHTML( values[ i ].isEmpty() ? i18n( "Unknown" ) : values[ i ] ) + << albumYear + << albumLength + << ( i!=vectorPlace ? "none" : "block" ) /* shows it if it's the current track album */ + << values[ i + 1 ] ) ); + + QString discNumber; + if ( !albumValues.isEmpty() ) + for ( uint j = 0; j < albumValues.count(); j += qb.countReturnValues() ) + { + QString newDiscNumber = albumValues[ j + 5 ].stripWhiteSpace(); + if( discNumber != newDiscNumber && newDiscNumber.toInt() > 0) + { + discNumber = newDiscNumber; + m_HTMLSource.append( QStringx ( + "
    \n" + "\n" + "%4" + "\n" + "
    \n" ) + .args( QStringList() + << QString::number( artist_id ) + << values[ i + 1 ] //album.id + << escapeHTMLAttr( discNumber ) + << i18n( "Disc %1" ).arg( discNumber ) ) ); + } + QString track = albumValues[j + 2].stripWhiteSpace(); + if( track.length() > 0 ) { + if( track.length() == 1 ) + track.prepend( "0" ); + + track = "\n" + track + " \n"; + } + + QString length; + if( albumValues[j + 4] != "0" ) + length = "(" + MetaBundle::prettyTime( QString(albumValues[j + 4]).toInt(), true ) + ")\n"; + + bool current = false; + if( i==vectorPlace && albumValues[j + 2].toInt() == m_currentTrack.track() && discNumber.toInt() == m_currentTrack.discNumber() ) + current = true; + m_HTMLSource.append( + "\n" ); + } + + m_HTMLSource.append( + "
    \n" + "
    \n" + "
    \n" ); + } + //
    +} + +void CurrentTrackJob::showArtistsCompilations( const QString &artist, uint artist_id, uint album_id ) +{ + QString artistName = artist.isEmpty() ? escapeHTML( i18n( "This Artist" ) ) : escapeHTML( artist ); + QueryBuilder qb; + QStringList values; + // + qb.clear(); + qb.addReturnValue( QueryBuilder::tabAlbum, QueryBuilder::valName ); + qb.addReturnValue( QueryBuilder::tabAlbum, QueryBuilder::valID ); + qb.addMatch( QueryBuilder::tabSong, QueryBuilder::valArtistID, QString::number( artist_id ) ); + qb.sortBy( QueryBuilder::tabYear, QueryBuilder::valName, true ); + qb.sortBy( QueryBuilder::tabAlbum, QueryBuilder::valName, true ); + qb.setOptions( QueryBuilder::optRemoveDuplicates ); + qb.setOptions( QueryBuilder::optOnlyCompilations ); + values = qb.run(); + + if ( !values.isEmpty() ) + { + // write the script to toggle blocks visibility + m_HTMLSource.append( + "
    \n" + "
    \n" + "\n" + + i18n( "Compilations with %1" ).arg( artistName ) + + "\n" + "
    \n" + "\n" ); + + uint vectorPlace = 0; + // find album of the current track (if it exists) + while ( vectorPlace < values.count() && values[ vectorPlace+1 ] != QString::number( album_id ) ) + vectorPlace += 2; + for ( uint i = 0; i < values.count(); i += 2 ) + { + qb.clear(); + qb.addReturnValue( QueryBuilder::tabSong, QueryBuilder::valTitle ); + qb.addReturnValue( QueryBuilder::tabSong, QueryBuilder::valURL ); + qb.addReturnValue( QueryBuilder::tabSong, QueryBuilder::valTrack ); + qb.addReturnValue( QueryBuilder::tabYear, QueryBuilder::valName ); + qb.addReturnValue( QueryBuilder::tabSong, QueryBuilder::valLength ); + qb.addReturnValue( QueryBuilder::tabArtist, QueryBuilder::valName ); + qb.addReturnValue( QueryBuilder::tabSong, QueryBuilder::valDiscNumber ); + qb.addMatch( QueryBuilder::tabSong, QueryBuilder::valAlbumID, values[ i + 1 ] ); + qb.sortBy( QueryBuilder::tabSong, QueryBuilder::valDiscNumber ); + qb.sortBy( QueryBuilder::tabSong, QueryBuilder::valTrack ); + qb.setOptions( QueryBuilder::optOnlyCompilations ); + QStringList albumValues = qb.run(); + usleep( 10000 ); + + QString albumYear; + if ( !albumValues.isEmpty() ) + { + albumYear = albumValues[ 3 ]; + for ( uint j = 0; j < albumValues.count(); j += qb.countReturnValues() ) + if ( albumValues[j + 3] != albumYear || albumYear == "0" ) + { + albumYear = QString::null; + break; + } + } + + uint i_albumLength = 0; + for ( uint j = 0; j < albumValues.count(); j += qb.countReturnValues() ) + i_albumLength += QString(albumValues[j + 4]).toInt(); + + QString albumLength = ( i_albumLength==0 ? i18n( "Unknown" ) : MetaBundle::prettyTime( i_albumLength, true ) ); + QString albumImage = ContextBrowser::getEncodedImage( CollectionDB::instance()->albumImage( artist, values[ i ], true, 50 ) ); + QString albumImageTitleAttr = albumImageTooltip( albumImage, 50 ); + + m_HTMLSource.append( QStringx ( + "\n" + "\n" + "\n" ); + } + m_HTMLSource.append( + "
    \n" + "
    \n" + "\n" + "\n" + "\n" + "\n" + "\n" + "
    \n" + "\n" + "\n" + "\n" + "\n" + "%5 " + "%7\n" + "
    \n" + "%8\n" + "%9\n" + "
    \n" + "
    \n" + "
    \n" ) + .args( QStringList() + << values[ i + 1 ] + << escapeHTMLAttr( values[ i ].isEmpty() ? i18n( "Unknown" ) : values[ i ] ) // album.name + << albumImageTitleAttr + << escapeHTMLAttr( albumImage ) + << i18n( "Single", "%n Tracks", albumValues.count() / qb.countReturnValues() ) + << values[ i + 1 ] //album.id + << escapeHTML( values[ i ].isEmpty() ? i18n( "Unknown" ) : values[ i ] ) + << albumYear + << albumLength + << ( i!=vectorPlace ? "none" : "block" ) /* shows it if it's the current track album */ + << values[ i + 1 ] ) ); + + QString discNumber; + if ( !albumValues.isEmpty() ) + for ( uint j = 0; j < albumValues.count(); j += qb.countReturnValues() ) + { + QString newDiscNumber = albumValues[ j + 6 ].stripWhiteSpace(); + if( discNumber != newDiscNumber && newDiscNumber.toInt() > 0) + { + discNumber = newDiscNumber; + m_HTMLSource.append( QStringx ( + "
    \n" + "\n" + "%3" + "\n" + "
    \n" ) + .args( QStringList() + << values[ i + 1 ] //album.id + << escapeHTMLAttr( discNumber ) + << i18n( "Disc %1" ).arg( discNumber ) ) ); + } + + QString track = albumValues[j + 2].stripWhiteSpace(); + if( track.length() > 0 ) { + if( track.length() == 1 ) + track.prepend( "0" ); + + track = "\n" + track + " \n"; + } + + QString length; + if( albumValues[j + 4] != "0" ) + length = "(" + MetaBundle::prettyTime( QString(albumValues[j + 4]).toInt(), true ) + ")\n"; + + QString tracktitle_formated; + QString tracktitle; + tracktitle = escapeHTML( i18n("%1 - %2").arg( albumValues[j + 5], albumValues[j] ) ); + tracktitle_formated = "\n"; + if( i==vectorPlace && albumValues[j + 2].toInt() == m_currentTrack.track() && discNumber.toInt() == m_currentTrack.discNumber() ) + tracktitle_formated += "\n"; + if ( artist == albumValues[j + 5] ) + tracktitle_formated += "\n"; + tracktitle_formated += tracktitle; + if ( artist == albumValues[j + 5] ) + tracktitle_formated += "\n"; + if( i==vectorPlace && track.toInt() == m_currentTrack.track() && discNumber.toInt() == m_currentTrack.discNumber() ) + tracktitle_formated += "\n"; + tracktitle_formated += " "; + m_HTMLSource.append( + "\n" ); + } + + m_HTMLSource.append( + "
    \n" + "
    \n" + "
    \n" ); + } + //
    +} + +QString CurrentTrackJob::statsHTML( int score, int rating, bool statsbox ) //static +{ + if( !AmarokConfig::useScores() && !AmarokConfig::useRatings() ) + return ""; + + if ( rating < 0 ) + rating = 0; + if ( rating > 10 ) + rating = 10; + + QString table = QString( "%2
    \n" ) + .arg( statsbox ? "class='statsBox'" : "" ); + QString contents; + + if( AmarokConfig::useScores() ) + contents += QString( "
    \n" + QString::number( score ) + "\n" + "
    \n" + "
    \n" + "
    \n" + "
    \n"; + if( rating ) + { + bool half = rating%2; + contents += "\n"; + + QImageIO fullStarIO; + fullStarIO.setImage( StarManager::instance()->getStarImage( half ? rating/2 + 1 : rating/2 ) ); + fullStarIO.setFormat( "PNG" ); + QBuffer fullStarBuf; + fullStarBuf.open( IO_WriteOnly ); + fullStarIO.setIODevice( &fullStarBuf ); + fullStarIO.write(); + fullStarBuf.close(); + QCString fullStar = KCodecs::base64Encode( fullStarBuf.buffer(), true ); + + const QString img = "\n"; + for( int i = 0, n = rating / 2; i < n; ++i ) + contents += img.arg( "data:image/png;base64," + fullStar ); + if( rating % 2 ) + { + QImageIO halfStarIO; + halfStarIO.setImage( StarManager::instance()->getHalfStarImage( half ? rating/2 + 1 : rating/2 ) ); + halfStarIO.setFormat( "PNG" ); + QBuffer halfStarBuf; + halfStarBuf.open( IO_WriteOnly ); + halfStarIO.setIODevice( &halfStarBuf ); + halfStarIO.write(); + halfStarBuf.close(); + QCString halfStar = KCodecs::base64Encode( halfStarBuf.buffer(), true ); + contents += img.arg( "data:image/png;base64," + halfStar ); + } + contents += "\n"; + } + else + contents += i18n( "Not rated" ); + contents += "